BerachainでアップグレーダブルなERC20トークンをデプロイする [Berachain翻訳]

:::note info
本記事は下記の翻訳となります。
[『Deploy an Upgradeable ERC20 Token on Berachain』](https://blog.berachain.com/blog/deploy-an-upgradeable-erc20-token-on-berachain-2)
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1926720/57fecd18-7b3e-1331-0512-a176784fce7b.png)

## アップグレーダブルコントラクトの紹介

ブロックチェーン上のスマートコントラクトは、Berachain のような環境では一度デプロイされると通常は変更不可能です。これは確実性を提供する一方で、バグの修正や機能の追加、急速に変化する環境への適応などのためにコントラクトを更新する必要がある開発者にとっては課題となる可能性があります。アップグレーダブルなコントラクトは、不変性と柔軟性の間の解決策を提供します。

### Proof-of-Liquidity とアップグレーダブル性

Berachain の革新的な Proof of Liquidity（PoL）コンセンサスメカニズムに参加するプロトコルは、通常、ユーザーに対して、自身のプロトコルへの預け入れを表す**ERC20 トークンをステーキング**することを要求します。これにより、Berachain のネイティブトークンである`$BGT`の報酬を獲得できます。

https://docs.berachain.com/learn/what-is-proof-of-liquidity?ref=berachain.ghost.io

プロトコルは最初、ステーキングモデルから始まるかもしれません。しかし、プロトコルの機能を変更せずに、報酬の仕組みを少し創造的にしたいと考えたらどうでしょうか？ここでは、アップグレーダブルなコントラクトがどのようにしてこれを実現できるかを探ってみます。

> 💡 トークンをマイグレーションせずに、PoL ステーキングメカニズムを変更する

### アップグレーダブルコントラクトガイド - 概要

このガイドでは、Foundry と OpenZeppelin のアップグレーダブルコントラクトをを使用して、Berachain 上で ERC20 トークンを作成、デプロイ、アップグレードするプロセスを説明します。具体的には、以下の内容を実施します：

1. ERC20 コントラクト（`v1 Implementation`）をデプロイする
2. `v1 Implementation`のロジックを継承する Proxy コントラクトをデプロイする
3. PoL 報酬の時間ベースのブーストを実装するための新機能を持つ、修正版 ERC20 コントラクト（`v2 Implementation`）をデプロイする
4. Proxy を`v2 Implementation`のロジックを使用するようにアップグレードする

### アップグレーダブルスマートコントラクトの仕組み

「Proxy」や「Implementation」という言葉は馴染みがないかもしれないので、コードに入る前にいくつかの用語と概念を明確にしておきましょう。

- `Proxy`はユーザーが相互作用するコントラクトです。コントラクトのデータとステートを保存する責任がありますが、単なるシェルとして機能し、機能やロジックは含まれていません。それは以下の役割です...
- `Implementation`コントラクト。`Implementation`は、ユーザー向けの`Proxy`コントラクトのすべてのコントラクトロジックをホストしますが、コントラクトアドレスにデータを保存しません。

![](https://super-translator.inaridiy.workers.dev/assets/image/288eee44-5b66-4e90-803e-7aee22e33e8d)

上の図で示されているように、`Proxy`と`Implementation`コントラクトは連携して動作します：

- まずユーザーが`Proxy`に`call`を行います。
- リクエストは`delegatecall`を使用して、関連する`Implementation`コントラクトにルーティングされます。
- `Proxy`コントラクトの権限を持つ所有者は、異なる`Implementation`コントラクト間で切り替えることができます - つまり、アップグレーダブルなのです！

アップグレーダブルコントラクトには様々な種類があります。このチュートリアルで使用されるアップグレーダブルコントラクトの種類は**UUPS**（Universal Upgradeable Proxy Standard）です。これはアップグレードロジックを`Implementation`コントラクト自体に組み込みます。この設計によりコントラクトの構造が簡素化され、`Proxy`のアップグレードを管理するための追加の Admin コントラクトが不要になります。アップグレーダブルコントラクトの異なる種類について詳しく学ぶには、[**OpenZeppelin ガイド**](https://docs.openzeppelin.com/contracts/5.x/api/proxy?ref=berachain.ghost.io)を参照してください。

## 📋 要件

- Node `v20.11.0`以上
- [pnpm](https://pnpm.io/installation?ref=berachain.ghost.io)
- [**Berachain bArtio ネットワーク**](https://docs.berachain.com/developers/network-configurations?ref=berachain.ghost.io)で設定されたウォレット
- そのウォレット内の`$BERA`または Berachain テストネットトークン — [**_Berachain Faucet_**](https://bartio.faucet.berachain.com/?ref=berachain.ghost.io)を参照してください

### Foundry のインストール

このガイドでは Foundry のインストールが必要です。ターミナルウィンドウで以下を実行してください：

```terminal
curl -L https://foundry.paradigm.xyz | bash;

foundryup;
# foundryup installs the 'forge' and 'cast' binaries, used later
```

より詳細なインストール手順については、Foundry の[インストールガイド](https://book.getfoundry.sh/getting-started/installation?ref=berachain.ghost.io)を参照してください。Berachain での Foundry の使用についての詳細は、この[ガイド](https://docs.berachain.com/developers/guides/create-erc20-contract-using-foundry?ref=berachain.ghost.io)を参照してください。

## 👨‍💻 アップグレーダブルコントラクトプロジェクトの作成

### ステップ 1：プロジェクトのセットアップ

Foundry を初期化して（新しいプロジェクトフォルダを作成して）開発環境をセットアップしましょう：

```terminal
forge init pol-upgrades --no-git --no-commit;

cd pol-upgrades;
# We observe the following basic layout
# .
# ├── foundry.toml
# ├── script
# │   └── Counter.s.sol
# ├── src
# │   └── Counter.sol
# └── test
#     └── Counter.t.sol
```

既存のすべての Solidity コントラクトを削除してください：

```terminal
# FROM: ./pol-upgrades

rm script/Counter.s.sol src/Counter.sol test/Counter.t.sol;
```

それでは、必要な OpenZeppelin の依存関係をインストールします：

```terminal
# FROM: ./pol-upgrades


forge install OpenZeppelin/openzeppelin-contracts openzeppelin-contracts-upgradeable OpenZeppelin/openzeppelin-foundry-upgrades foundry-rs/forge-std --no-commit --no-git;
```

### ステップ 2：Foundry の設定

プロジェクトのルートに`remappings.txt`ファイルを作成し、以下の内容を記述します：

```text
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
```

次に、`foundry.toml`ファイルを更新します：

```toml
[profile.default]
ffi = true
ast = true
build_info = true
evm_version = "cancun"
libs = ["lib"]
extra_output = ["storageLayout"]
```

### ステップ 3：初期トークンコントラクトの作成

`src/DeFiTokenV1.sol`というファイルを作成し、以下の内容を記述します：

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract DeFiToken is ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address initialOwner) public initializer {
        __ERC20_init("DeFi Token", "DFT");
        __Ownable_init(initialOwner);
        __UUPSUpgradeable_init();

        _mint(initialOwner, 1000000 * 10 ** decimals());
    }

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }

    function _authorizeUpgrade(
        address newImplementation
    ) internal override onlyOwner {}
}
```

このスマートコントラクトには、他の場所では見られないいくつかの特徴があることに気づくでしょう：

- プロキシ化されたコントラクトは**コンストラクタ**を使用しないため、コンストラクタのロジックは**`initialize`**関数に移動されています。これは、トークン名や所有者の設定など、通常のコントラクトのコンストラクタで実行される関数を実行します。
- 継承している`ERC20`と`Ownable`コントラクトは特別な「アップグレーダブル」バージョンで、（コンストラクタ外での）初期化やアップグレード時の再初期化を容易にします。

### ステップ 4：デプロイスクリプトの作成

`script/DeployProxy.s.sol`というファイルを作成し、以下の内容を記述します：

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "../src/DeFiTokenV1.sol";
import "forge-std/Script.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";

contract DeployProxy is Script {
    function run() public {
        vm.startBroadcast();

        address proxy = Upgrades.deployUUPSProxy(
            "DeFiTokenV1.sol:DeFiToken",
            abi.encodeCall(DeFiToken.initialize, (msg.sender))
        );

        vm.stopBroadcast();

        console.log("Proxy Address:", address(proxy));
        console.log("Token Name:", DeFiToken(proxy).name());
    }
}
```

### ステップ 5：環境変数の設定

デプロイに進む前に、プロジェクトのルートに`.env`ファイルを作成し、ウォレットのプライベートキーを追加します：

```text
PK=your_private_key_here
```

その後、環境変数を読み込みます：

```terminal
# FROM: ./pol-upgrades

source .env;
```

### ステップ 6：トークンと Proxy のデプロイ

これが興味深い部分です。上記の`DeployProxy`スクリプトでは、OpenZeppelin の`Upgrades.deployUUPSProxy`の呼び出しが実際には裏で 2 つのことを行っています：

1. `DeFiToken`実装コントラクトをデプロイする
2. `UUPSUpgradeable` proxy コントラクトをデプロイし、`DeFiToken`実装に接続する

まずはコントラクトをコンパイルすることから始めましょう：

```terminal
# FROM: ./pol-upgrades

forge build;
```

次に、デプロイメントスクリプトを実行します（一貫性のために Solidity のバージョンを固定しています）：

```terminal
# FROM: ./pol-upgrades

forge script script/DeployProxy.s.sol --broadcast --rpc-url https://bartio.rpc.berachain.com/ --private-key $PK --use 0.8.25;
```

![](https://super-translator.inaridiy.workers.dev/assets/image/76e08575-fde2-4ece-af27-729ea0215f02)
_Example Deployment Output_

### ステップ 7：コントラクトの Verify（オプション）

[Beratrail Explorer](https://bartio.beratrail.io/?ref=berachain.ghost.io)でコントラクトを Verify したい場合は、以下のように実行します：

```terminal
# FROM: ./pol-upgrades

forge verify-contract IMPLEMENTATION_ADDRESS ./src/DeFiTokenV1.sol:DeFiToken --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/80084/etherscan' --etherscan-api-key "verifyContract" --num-of-optimizations 200 --compiler-version 0.8.25 --watch;
```

\*`IMPLEMENTATION_ADDRESS`をスクリプト出力のものに置き換えることを忘れないでください。proxy の検証については心配する必要はありません。エクスプローラーはすでにこのコントラクトについて把握しているからです。

これで、Beratrail でプロキシコントラクトを確認すると、ERC20 トークンの属性がプロキシコントラクトに接続されているのが分かります（例として[デプロイされたプロキシコントラクト](https://bartio.beratrail.io/address/0xC9e6086507322ee8ae1D5c283101674588B342f5/contract/80084/readContract?ref=berachain.ghost.io)を参照してください）。

![](https://super-translator.inaridiy.workers.dev/assets/image/cd15a55f-ffaf-4d0d-8b85-b83a53a6d764)

### ステップ 8：アップグレードされたトークンコントラクトの作成

> コードサンプルのみを追いたい場合や、実装している PoL ロジックに興味がない場合は、次のコードブロックまでスキップしてください。

### PoL イノベーション 💡

ここから、アップグレーダブルコントラクトを使って創造的になることができます。例えば、あなたのプロトコルに最も長く預けているユーザーに、より多くの`$BGT`報酬を与えたいとします。しかし、預け入れトークンにはすでにアクティブな [Reward Vault](https://docs.berachain.com/learn/pol/rewardvaults?ref=berachain.ghost.io)があり、ユーザーにトークンを移行させたくありません。

Upgradeability がこれを解決します！例えば、従来のステーキングパラダイムでユーザーが 100 トークンを持っていて、時間ブースト型システム(time-boosted system)に移行したい場合を考えてみましょう。60 日間ポジションを保持した後、PoL の獲得率が従来のステーキングを上回ることに気づくでしょう。

![](https://super-translator.inaridiy.workers.dev/assets/image/dbd142ec-2b81-457c-9920-38ce0aa39540)
_Traditional (V1) vs Time-boosted (V2) Rewards_

`src/DeFiTokenV2.sol`というファイルを作成し、以下の内容を記述します：

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

interface IBerachainRewardsVault {
    function delegateStake(address account, uint256 amount) external;
    function delegateWithdraw(address account, uint256 amount) external;

    function getTotalDelegateStaked(
        address account
    ) external view returns (uint256);
}

/// @custom:oz-upgrades-from DeFiToken
contract DeFiTokenV2 is ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    IBerachainRewardsVault public rewardsVault;
    uint256 public constant BONUS_RATE = 50; // 50% bonus per 30 days
    uint256 public constant BONUS_PERIOD = 30 days;
    mapping(address => uint256) public lastBonusTimestamp;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() public reinitializer(2) {
        __ERC20_init("DeFi Token V2", "DFTV2");
    }

    function setRewardsVault(address _rewardsVault) external onlyOwner {
        rewardsVault = IBerachainRewardsVault(_rewardsVault);
    }

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }

    function applyBonus(address account) external {
        uint256 newBonusAmount = calculateBonus(account);
        require(newBonusAmount > 0, "No bonus to apply");

        // Mint new bonus tokens to this contract
        _mint(address(this), newBonusAmount);

        // Delegate new bonus stake
        rewardsVault.delegateStake(account, newBonusAmount);

        lastBonusTimestamp[account] = block.timestamp;
    }

    function calculateBonus(address account) public view returns (uint256) {
        uint256 userBalance = balanceOf(account);
        uint256 timeSinceLastBonus = block.timestamp -
            lastBonusTimestamp[account];
        return
            (userBalance * BONUS_RATE * timeSinceLastBonus) /
            (100 * BONUS_PERIOD);
    }

    function getBonusBalance(address account) public view returns (uint256) {
        return rewardsVault.getTotalDelegateStaked(account);
    }

    function removeBonus(address account) internal {
        uint256 bonusToRemove = getBonusBalance(account);
        if (bonusToRemove > 0) {
            rewardsVault.delegateWithdraw(account, bonusToRemove);
            _burn(address(this), bonusToRemove);
            lastBonusTimestamp[account] = 0;
        }
    }

    function transfer(
        address to,
        uint256 amount
    ) public override returns (bool) {
        removeBonus(msg.sender);
        lastBonusTimestamp[to] = block.timestamp;
        return super.transfer(to, amount);
    }

    function _authorizeUpgrade(
        address newImplementation
    ) internal override onlyOwner {}
}
```

`DeFiTokenV2`にはかなり多くの変更がありますね。新しい内容を 2 つの部分に分けて説明しましょう。最初に、Upgradeability に関連する部分を指摘します。次に、新しい PoL メカニズムをサポートするためのスマートコントラクト関数について説明します。

#### **Upgradeability の変更点**

- コントラクトには OpenZeppelin が要求する元の`DeFiToken`コントラクトへの参照が含まれています
- コントラクトの`initialize`関数には`reinitializer(2)`修飾子があり、これが再初期化であることを示しています。`2`はコントラクトのバージョン番号を指しています

#### Upgradeability の変更点

- コントラクトには OpenZeppelin が要求する元の`DeFiToken`コントラクトへの参照が含まれています。
- コントラクトの`initialize`関数には`reinitializer(2)`修飾子があり、これが再初期化であることを示しています。`2`はコントラクトのバージョン番号を指します。

> Upgradeability のみに関心がある場合は、次のセクションにスキップしてください。

#### **PoL 変更点**

*「Reward Vault コントラクトの残高が、変更されるべきではない ERC20 ポジションに紐づいているのに、どうやって増加するのだろう？」*と疑問に思うかもしれません。

良い質問です！時間ベースのブースト報酬を実現するために、Reward Vault の`delegateStake`機能を活用します。これにより、スマートコントラクトがユーザーに代わってステーキングを行うことができます。これは、ここでの時間ブースト報酬や、[**virtual/non-ERC20 position を PoL と統合する**](https://docs.berachain.com/developers/guides/advanced-pol?ref=berachain.ghost.io#description-of-approach)など、さまざまなユースケースに役立ちます。

時間ベースのロジックを適用するために、`DeFiTokenV2`コントラクトはユーザーのステーキングロジックを処理し、ユーザーは単に自分のウォレットにトークンを保持するだけです。詳しく見ていきましょう！

- `setRewardsVault`は、プロトコルがユーザーのブーストがステーキングされて`$BGT`を獲得する Reward Vault アドレスを設定できるようにします
- `calculateBonus`は、最後にボーナスが適用されてから、ユーザーに対して追加で付与されるべき残高を計算します
- `applyBonus`は新しいトークンをミントし、ユーザーが獲得したボーナスに基づいて、特定のユーザーに代わって`delegateStake`を使用してステーキングします
- `getBonusBalance`は Rewards Vault コントラクトにユーザーのボーナス残高を問い合わせます
- `removeBonus`は Reward Vault の`delegateWithdraw`関数を呼び出して、ユーザーのボーナス残高を引き出し/無効化し、それらのトークンをバーンします。これにより、ユーザーがトークンを転送したときのボーナスの損失を反映します

このサンプルが、従来の PoL に代わる革新的な選択肢がどのようなものか、イメージを掴むのに役立つことを願っています。**_このコードは不完全で、本番環境での使用には適していないことに注意してください_**。

### ステップ 9：コントラクトのテスト

すべてのスマートコントラクトを記述したので、アップグレーダブルな PoL 統合トークンコントラクトの動作をテストしましょう。

以下の機能をテストします：

- コントラクトのアップグレードが正常に実行されることの確認
- PoL ボーナスロジックが期待通りに機能することの確認

`test/DeFiToken.t.sol`というファイルを作成し、以下の内容を記述します：

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "forge-std/Test.sol";
import "../src/DeFiTokenV1.sol";
import "../src/DeFiTokenV2.sol";
import "forge-std/console.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";

contract MockRewardsVault {
    mapping(address => uint256) public delegatedStakes;

    function delegateStake(address account, uint256 amount) external {
        delegatedStakes[account] += amount;
    }

    function delegateWithdraw(address account, uint256 amount) external {
        require(
            delegatedStakes[account] >= amount,
            "Insufficient delegated stake"
        );
        delegatedStakes[account] -= amount;
    }

    function getTotalDelegateStaked(
        address account
    ) external view returns (uint256) {
        return delegatedStakes[account];
    }
}

contract DeFiTokenTest is Test {
    DeFiToken deFiToken;
    DeFiTokenV2 deFiTokenV2;
    ERC1967Proxy proxy;
    address owner;
    address user1;
    MockRewardsVault mockRewardsVault;

    function setUp() public {
        DeFiToken implementation = new DeFiToken();
        owner = vm.addr(1);
        user1 = vm.addr(2);

        vm.startPrank(owner);
        proxy = new ERC1967Proxy(
            address(implementation),
            abi.encodeCall(implementation.initialize, owner)
        );
        deFiToken = DeFiToken(address(proxy));
        vm.stopPrank();

        mockRewardsVault = new MockRewardsVault();
    }

    function testBoostedStakingFunctionality() public {
        testUpgradeToV2();

        vm.startPrank(owner);
        deFiTokenV2.setRewardsVault(address(mockRewardsVault));
        deFiTokenV2.mint(user1, 1000 * 1e18);
        vm.stopPrank();

        // Fast forward 15 days
        vm.warp(block.timestamp + 15 days);

        // Apply bonus for user1
        vm.prank(user1);
        deFiTokenV2.applyBonus(user1);

        // Check bonus balance (should be 25% of user's balance after 15 days)
        uint256 expectedBonus = (1000 * 1e18 * 25) / 100;
        assertApproxEqAbs(
            deFiTokenV2.getBonusBalance(user1),
            expectedBonus,
            1e15
        );

        // Fast forward another 30 days
        vm.warp(block.timestamp + 30 days);

        // Apply bonus again (should be 75% of user's balance)
        vm.prank(user1);
        deFiTokenV2.applyBonus(user1);
        expectedBonus = (1000 * 1e18 * 75) / 100;
        assertApproxEqAbs(
            deFiTokenV2.getBonusBalance(user1),
            expectedBonus,
            1e15
        );

        // Test bonus removal on transfer
        vm.prank(user1);
        deFiTokenV2.transfer(owner, 500 * 1e18);

        // Check that bonus is removed
        assertEq(deFiTokenV2.getBonusBalance(user1), 0);
    }

    function testUpgradeToV2() public {
        vm.startPrank(owner);
        Upgrades.upgradeProxy(
            address(proxy),
            "DeFiTokenV2.sol:DeFiTokenV2",
            abi.encodeCall(DeFiTokenV2.initialize, ())
        );
        vm.stopPrank();

        deFiTokenV2 = DeFiTokenV2(address(proxy));
        assertTrue(address(deFiTokenV2) == address(proxy));
    }
}
```

テストコードは長いですが、コメントを読むことで各テストケースで何が起こっているかを理解できるはずです。

```terminal
# FROM: ./pol-upgrades

forge clean && forge test;

# [EXAMPLE OUTPUT]:
# Ran 2 tests for test/DeFiToken.t.sol:DeFiTokenTest
# [PASS] testBoostedStakingFunctionality() (gas: 2032978)
# [PASS] testUpgradeToV2() (gas: 1904385)
# Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.62s (11.14s CPU time)

# Ran 1 test suite in 5.66s (5.62s CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
```

### ステップ 10：アップグレードスクリプトの作成

コントラクトが正常に動作することを確認したので、アップグレードの準備をしましょう。`script/DeployUpgrade.s.sol`というファイルを作成し、以下の内容を記述します：

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "../src/DeFiTokenV2.sol";
import "forge-std/Script.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";

contract DeployAndUpgrade is Script {
    function run() public {
        // Replace with your proxy address
        address proxy = 0x0000000000000000000000000000000000000000;
        vm.startBroadcast();

        Upgrades.upgradeProxy(
            proxy,
            "DeFiTokenV2.sol:DeFiTokenV2",
            abi.encodeCall(DeFiTokenV2.initialize, ())
        );

        vm.stopBroadcast();

        console.log("Token Name:", DeFiTokenV2(proxy).name());
    }
}
```

> `proxy`変数をステップ 6 で得たプロキシアドレスに置き換えてください。

このスクリプトは、以下の手順でアップグレードプロセスを組み立てています：

1. アップグレードされた`DeFiTokenV2`コントラクトをデプロイする
2. プロキシの実装を新しい`DeFiTokenV2`に切り替える
3. `initialize`を再度呼び出してトークンの名前を変更する

### ステップ 11：アップグレードの実行

ビルドアーティファクト(build artifacts)をクリーンアップし、その後アップグレードスクリプトを実行します：

```terminal
# FROM: ./pol-upgrades

forge clean;
forge script script/DeployUpgrade.s.sol --broadcast --rpc-url https://bartio.rpc.berachain.com/ --private-key $PK --use 0.8.25;
```

Beratrail Explorer で Proxy コントラクトを確認してください。驚くべきことに、同じアドレスで、トークン名（通常は不変のプロパティ）が変更されているのが確認できるはずです！

![](https://super-translator.inaridiy.workers.dev/assets/image/46d622bd-65d3-4427-8c8b-397df3c5aad7)
_The token name changed!_

## まとめ

おめでとうございます！ここまで到達できたなら、アップグレーダブルコントラクトの基本的な機能と、それがどのようにあなたに有利に働くかを学んだことになります。特に注意深く見ていたなら、Proof-of-Liquidity を扱う革新的な方法についても学んだはずです。
このガイドに従うことで、Berachain 上にアップグレーダブルな ERC20 トークンを無事にデプロイしたことになります。その後、Proxy コントラクトの実装をアップグレードして、その機能に非常にクールな変更を加えることができました 🎉

### 🐻 **完全なコードリポジトリ**

最終的なコードを確認したり、他のガイドを見たい場合は、[**_Berachain Upgradeable Contracts Guide Code_**](https://github.com/berachain/guides/tree/main/apps/openzeppelin-upgrades?ref=berachain.ghost.io)をチェックしてください。

### 🛠️ もっとビルドしたいですか？

Berachain 上でさらに開発を進めたり、より多くの例を見たい場合は、[**_Berachain GitHub Guides Repo_**](https://github.com/berachain/guides?ref=berachain.ghost.io) をご覧ください。NextJS、Hardhat、Viem、Foundry など、様々な実装例が幅広く用意されています。

### 開発者サポートをお探しですか？

質問をするために、[**_Berachain Discord_**](https://discord.com/invite/berachain?ref=berachain.ghost.io)に参加し、開発者チャンネルをチェックしてください。

❤️ この記事に対して愛を示すのを忘れないでください 👏🏼

---

---

:::note info
【Sunrise とは】
Sunrise は Proof of Liquidity(PoL)と Fee Abstraction（手数料抽象化）を備えたデータ可用性レイヤーです。 私たちは DA の体験を再構築し、多様なエコシステムからのモジュラー型流動性を活用してロールアップを立ち上げています。

- [Sunrise - Specialized DA Layer for Proof of Liquidity](https://sunriselayer.io/)
- [Sunrise / Gluon Docs](https://docs.sunriselayer.io/)

【Social Links】

- [X(旧 Twitter)[英語])](https://twitter.com/SunriseLayer)
- [X(旧 Twitter)[日本語])](https://twitter.com/SunriseLayer)
- [Discord Community](https://discord.com/invite/sunrise)
- [Medium](https://sunriselayer.medium.com/)
- [Github](https://github.com/sunriselayer)
- [Linkedin](https://www.linkedin.com/company/sunriselayer)

【お問合せ】
Sunrise へのお問い合わせはこちらから 👉 [Google Form](https://forms.gle/h8RVahxRtXUvYwnv6)

![1080x360.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839047/8971d83b-3331-4757-dc72-320d28618735.jpeg)

:::