♦️

スマートコントラクトのアップグレード方法

2024/04/05に公開

Solidityスマートコントラクトで特定のfunctionの中身に修正が入った場合、いくつかの方法でアップグレードすることができます。

方法

1. インラインアップグレード

  • 最もシンプルで、既存のコントラクトを変更せずに新しいfunctionを追加できます。
  • ただし、既存のfunctionを変更することはできません。
  • バージョン管理が難しく、複雑なアップグレードには適していません。

2. プロキシアップグレード

  • 新しいコントラクトをデプロイし、既存のコントラクトへのプロキシとして設定します。
  • 既存のfunctionを変更することもできます。
  • バージョン管理が容易で、複雑なアップグレードにも適しています。
  • いくつかの種類があり、それぞれに利点と欠点があります。

3. ダイヤモンドアップグレード

  • プロキシアップグレードの一種で、より高度な機能を提供します。
  • 複数のファセットと呼ばれるコントラクトを組み合わせることで、機能を拡張できます。
  • 最も柔軟で拡張性の高いアップグレード方法ですが、複雑な実装が必要です。

4. トランスファーアップグレード

  • 新しいコントラクトにデータを移行するアップグレード方法です。
  • 既存のfunctionを変更することはできません。
  • データ移行が必要な場合に適しています。

具体例

1. インラインアップグレード

例:

contract MyContract {
  function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b;
  }

  // 新しいfunctionを追加
  function subtract(uint256 a, uint256 b) public pure returns (uint256) {
    return a - b;
  }
}

解説:

  • 既存のコントラクトを変更せずに、新しいfunction subtract を追加しています。
  • 既存のfunction add は変更されていません。

2. プロキシアップグレード

例:

contract MyContract {
  function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b;
  }
}

contract MyProxy {
  MyContract public implementation;

  constructor(MyContract _implementation) {
    implementation = _implementation;
  }

  function add(uint256 a, uint256 b) public payable returns (uint256) {
    return implementation.add(a, b);
  }
}

解説:

  • 新しいコントラクト MyProxy をデプロイし、既存のコントラクト MyContract へのプロキシとして設定します。
  • MyProxyadd function は、MyContractadd function を呼び出します。
  • 将来、MyContract を新しいバージョンにアップグレードすることで、MyProxy の機能を拡張することができます。

3. ダイヤモンドアップグレード

例:

contract MyFacet {
  function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b;
  }
}

contract MyDiamond {
  // ファセットのアドレスを格納するマッピング
  mapping(bytes4 => address) public facets;

  constructor() {
    // ファセットを追加
    facets[bytes4(keccak256("add(uint256,uint256)"))] = address(new MyFacet());
  }

  // ファセットのfunctionを呼び出す
  function add(uint256 a, uint256 b) public payable returns (uint256) {
    return MyFacet(facets[bytes4(keccak256("add(uint256,uint256)"))]).add(a, b);
  }
}

解説:

  • ダイヤモンドアップグレードは、プロキシアップグレードの一種で、より高度な機能を提供します。
  • 複数のファセットと呼ばれるコントラクトを組み合わせることで、機能を拡張できます。
  • 上記の例では、MyFacet というファセットが add function を提供しています。
  • MyDiamond コントラクトは、MyFacet ファセットの add function を呼び出すことができます。
  • 将来、新しいファセットを追加することで、MyDiamond コントラクトの機能を拡張することができます。

4. トランスファーアップグレード

例:

contract MyOldContract {
  uint256 public balance;

  function withdraw(uint256 amount) public {
    balance -= amount;
    msg.sender.transfer(amount);
  }
}

contract MyNewContract {
  uint256 public balance;

  constructor(MyOldContract oldContract) {
    // 既存のコントラクトからデータを移行
    balance = oldContract.balance();
  }

  function withdraw(uint256 amount) public {
    balance -= amount;
    msg.sender.transfer(amount);
  }
}

解説:

  • トランスファーアップグレードは、新しいコントラクトにデータを移行するアップグレード方法です。
  • 上記の例では、MyNewContract コントラクトは MyOldContract コントラクトから balance データを移行しています。

選択基準

どの方法を選択するかは、以下の要素を考慮する必要があります。

  • アップグレードの複雑性
  • データ移行の必要性
  • バージョン管理の必要性
  • 将来の拡張性

各方法の詳細

各方法の詳細については、以下の情報をご覧ください。

その他

  • アップグレードを行う前に、必ずバックアップを取るようにしてください。
  • テスト環境でアップグレードを十分にテストしてから、本番環境にデプロイするようにしてください。
  • アップグレードに関する詳細は、Solidityの公式ドキュメントを参照してください。

参考情報

注意事項

  • 上記の情報は一般的な情報であり、個々の状況によって異なる場合があります。
  • アップグレードを行う前に、専門家に相談することをお勧めします。

Discussion