【Solidity】第8章第7回:アップグレード可能なコントラクトの設計

本記事では、Solidityでアップグレード可能なスマートコントラクトを設計する方法について解説します。通常、スマートコントラクトは一度デプロイすると変更できませんが、適切な設計を行うことでアップグレードを可能にできます。
0. 記事の概要
この記事を読むメリット
- スマートコントラクトの変更を可能に: 一度デプロイしたコントラクトを柔軟にアップグレードできるようになります。
- 安全な設計手法を学習: Proxyパターンを活用し、安全かつ効率的なアップグレード手法を習得できます。
- Solidityの高度な知識を習得: 実際の開発現場で役立つ設計パターンを理解できます。
この記事で学べること
- スマートコントラクトのアップグレードが必要な理由
- Proxyパターンを用いたアップグレードの仕組み
- Solidityでの実装例
1. スマートコントラクトのアップグレードが必要な理由

1.1 変更不可能なコントラクトの問題点
Solidityのスマートコントラクトは、デプロイ後に変更することができません。これにより、以下のような問題が発生します:
- バグ修正が困難: 一度デプロイしたコードにバグがあった場合、修正ができない。
- 機能拡張ができない: 新しい機能を追加するためには、別のコントラクトを作成する必要がある。
- ストレージの維持が困難: 古いコントラクトのデータを新しいコントラクトに移行するのが大変。
2. Proxyパターンを活用したアップグレード
2.1 Proxyパターンの基本概念

Proxyパターンとは、実際のコントラクト(実装コントラクト)とデータを保持するコントラクト(Proxy)を分けることで、後から実装を差し替えることができる設計手法です。
2.2 Transparent Proxyパターン
// Transparent Proxyパターンを用いた実装例
pragma solidity ^0.8.0;
contract LogicContract {
uint256 public value;
function setValue(uint256 _value) public {
value = _value;
}
}
contract Proxy {
address public implementation;
constructor(address _implementation) {
implementation = _implementation;
}
fallback() external payable {
address impl = implementation;
require(impl != address(0));
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0)
returndatacopy(ptr, 0, returndatasize())
switch result
case 0 { revert(ptr, returndatasize()) }
default { return(ptr, returndatasize()) }
}
}
function upgrade(address newImplementation) public {
implementation = newImplementation;
}
}
動作解説
- LogicContract: 実際のビジネスロジックを実装するコントラクト。
- Proxy: ユーザーの呼び出しを受け付け、
delegatecall
を用いてLogicContractの関数を実行。 - アップグレード:
upgrade()
関数を使って新しい実装コントラクトに変更可能。
3. 練習問題

以下の課題に挑戦して、アップグレード可能なスマートコントラクトの設計を実践してみましょう:
- 上記のコードを実装し、Proxy経由で値を変更できるか確認してください。
- 新しいバージョンのLogicContractを作成し、
upgrade()
関数で差し替えてください。 - Proxyパターンを用いない場合と比較し、違いを説明してください。
4. まとめ
本記事では、Solidityのスマートコントラクトをアップグレード可能にする設計方法について解説しました。Proxyパターンを活用することで、安全にコントラクトをアップグレードし、機能の拡張やバグ修正を可能にできます。ぜひこの記事を参考に、実践してみてください。