【ERC6551】あらゆるProxyに適応するToken Bound Accountsを開発してみた
はじめに
こんにちは、kai(@dikxs118)です。
今日は、ERC6551の Proxy である Token Bound Accounts(以下TBA) を ERC1167(Minimal Proxy) 以外でもデプロイできるようにしたので、それについてまとめていこうと思います!
この記事では基本的なERC6551の説明等は行いませんのでご了承ください。
現状と課題
さて、現状のERC6551ですが、TBAは Minimal Proxy でデプロイされることがMust要件になっています。また、デプロイされるバイトコードの構造もMust要件として決まっています。
The registry MUST deploy each token bound account as an ERC-1167 minimal proxy with immutable constant data appended to the bytecode.
ERC-1167 Header (10 bytes)
<implementation (address)> (20 bytes)
ERC-1167 Footer (15 bytes)
<salt (bytes32)> (32 bytes)
<chainId (uint256)> (32 bytes)
<tokenContract (address)> (32 bytes)
<tokenId (uint256)> (32 bytes)
しかし、TBAを Upgradeable にしたり、他の Proxy としてデプロイしたいというニーズはあるはずです。(私はありました)
ERC6551には主に ERC6551Account
と ERC6551Registry
の2つのコントラクトで構成されており、ERC6551Account
が TBA の実装で、ERC6551Registry
が Proxy のデプロイを担っています。
私はWeb2出身なのですが、ドメインのモデリングや、責務の分割、いわゆるDDDといったアーキテクチャを考えることが多かったので、アカウントの取り回し方式(Account) が デプロイ方式(Registry) に依存しているのがそもそもどうなんだろうな、と思っていました。
要は、ERC6551は アカウントの取り回しに関する規格なので、
- デプロイ方式はなんでも良いのでは
- 各々が疎結合で独立して振る舞いを担保している方が可用性、保守性が高いのでは
という思想が今回の出発点となり、Account が Registry に依存しない ものにしよう、という方針で開発を進めていきます。
ちなみに、過去ログを見ると、ERC1967を使えるようにしたり、戻したり、1167->1967という多段のProxyで実現できるなどの話はあったようですが、いずれにしても Account が Registry に依存している状態なのと、1167-> any proxy みたいな構成は微妙かなと思います。
実装
ERC6551Account
まずはERC6551Accountの参考実装を見ていきます。
contract ERC6551Account is IERC165, IERC1271, IERC6551Account, IERC6551Executable {
uint256 public state;
receive() external payable {}
function execute(address to, uint256 value, bytes calldata data, uint8 operation)
external
payable
virtual
returns (bytes memory result)
{
require(_isValidSigner(msg.sender), "Invalid signer");
require(operation == 0, "Only call operations are supported");
++state;
bool success;
(success, result) = to.call{value: value}(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
function isValidSigner(address signer, bytes calldata) external view virtual returns (bytes4) {
if (_isValidSigner(signer)) {
return IERC6551Account.isValidSigner.selector;
}
return bytes4(0);
}
function isValidSignature(bytes32 hash, bytes memory signature)
external
view
virtual
returns (bytes4 magicValue)
{
bool isValid = SignatureChecker.isValidSignatureNow(owner(), hash, signature);
if (isValid) {
return IERC1271.isValidSignature.selector;
}
return bytes4(0);
}
function supportsInterface(bytes4 interfaceId) external pure virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId
|| interfaceId == type(IERC6551Account).interfaceId
|| interfaceId == type(IERC6551Executable).interfaceId;
}
function token() public view virtual returns (uint256, address, uint256) {
bytes memory footer = new bytes(0x60);
assembly {
extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
}
return abi.decode(footer, (uint256, address, uint256));
}
function owner() public view virtual returns (address) {
(uint256 chainId, address tokenContract, uint256 tokenId) = token();
if (chainId != block.chainid) return address(0);
return IERC721(tokenContract).ownerOf(tokenId);
}
function _isValidSigner(address signer) internal view virtual returns (bool) {
return signer == owner();
}
}
ふむ、非常にシンプルですね。
どうやらERC6551の本質をすごく簡単にまとめると
- TBA(Minimal Proxy)の bytecode に埋め込まれた情報をdecodeして、
chainId
,tokenContract
,tokenId
をtoken
として取得している - 取得した
tokenContarct
,tokenId
から NFT のownerOf
を呼び出すことで、ownerを取得し、認証を行なっている
ということになりそうです。その上で、bytecodeに必要な情報を埋め込んでいるために、デプロイの方式が指定されているのだということが推察できます。
実際にbytecodeから、token
を取り出しているコードを読んでいきます。
function token() public view virtual returns (uint256, address, uint256) {
bytes memory footer = new bytes(0x60);
assembly {
extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
}
return abi.decode(footer, (uint256, address, uint256));
}
EVMのbytecodeやassemblyを読む場合は、evm codeを参照すると良いです。
やっていることをまとめると、
- footerとして
96bytes(0x60)
の領域を確保する - TBAのruntimeCodeにおける、
77bytes(0x4d)
から96bytes(0x60)
をfooterの32bytes(0x20)
以降の領域にコピーする
- footerをdecodeする
となります。
ERC-1167 Header (10 bytes)
<implementation (address)> (20 bytes)
ERC-1167 Footer (15 bytes)
<salt (bytes32)> (32 bytes)
<chainId (uint256)> (32 bytes)
<tokenContract (address)> (32 bytes)
<tokenId (uint256)> (32 bytes)
検算も兼ねて ERC6551 の bytecode の構造を見ると、77bytes(saltまで)
から、96bytes(chainId,tokenContract,tokenId)
が取得されていることがわかりますね。
ERC6551のコードを読んだ限り、ERC1167に依存している箇所はここだけなので、ここを汎用的にいじることができれば、ERC1167に依存せず、ERC6551Accountは動きそうです。
現在の実装は 77bytes(0x4d)をオフセットとしていることがERC1167に依存しているので、ここを bytecodeの最後から96bytes という実装にしてやれば、どのようなProxyをデプロイしても、runtimeCodeの最後の96bytesに必要な情報を載せていれば、良さそうです。
下記のように実装しました。
function token() public view virtual returns (uint256, address, uint256) {
bytes memory footer = new bytes(0x60);
assembly {
extcodecopy(address(), add(footer, 0x20), sub(extcodesize(address()), 0x60), 0x60)
}
return abi.decode(footer, (uint256, address, uint256));
}
ERC6551Registry
次は、ERC6551Registryの内容を改変して、どんなProxyでもできるようにしていく方針を考えます。
私はそもそもRegistryはERCの規格に含める必要はなくて、参考実装を提供するくらいで良いのかなと思っています。
今回は、ERC7546: Upgradeable Cloneを上記の実装方針に沿って、TBAのProxyとしてデプロイします。
ERC7546ついては、過去に私が記事を書いているのでそちらを参照してみてください。
Registryの参考実装はassemblyで書かれているので、interfaceを確認して何をしているのかみていきます。
/**
* @dev Creates a token bound account for a non-fungible token.
*
* If account has already been created, returns the account address without calling create2.
*
* Emits ERC6551AccountCreated event.
*
* @return account The address of the token bound account
*/
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address account);
/**
* @dev Returns the computed token bound account address for a non-fungible token.
*
* @return account The address of the token bound account
*/
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address account);
If account has already been created, returns the account address without calling create2.
どうやら、create2を使ってこのロジックでデプロイしているだけのようです。
つまり最終的なゴールは、runtimeCodeの最後にsalt(uint256)とtokenであるchainId(uint256),tokenContract(address),tokenId(uint256)の128bytesを追加した状態で create2
にてデプロイする、ということになります。
以降ステップを追って解説していきます。
EVMのバイトコード
EVMのバイトコードについて理解がないとこの先に進めないので、まずはEVMのバイトコードにおける構造について軽くおさらいします。
この記事が分かりやすいかと思います。
EVMのcreation codeとは一般的に、 <init code> <runtime code> <constructor parameters>
から構成されます。抑える点は下記です。
init code
-> デプロイの際に実行されるコード。コンストラクタを解決(初期化)し、runtime codeをアドレスに配置する
runtime code
-> デプロイ後に実行されるコード。スマートコントラクトの本体。
constructor parameters
-> init codeがコンストラクタを解決するときに渡される引数
バイトコードでERC7546Proxyをデプロイする
まずは、最初のステップとして、 create2
を使って、128bytesをくっつけない状態で生の ERC7546Proxy
をデプロイしてみましょう。
コントラクトの creation codeが欲しいので、solidityのdocsをみてみると、 type(C).creationCode
で取得できるようです。
type(C).runtimeCode
で runtime code も取得できるので同時に見てみましょう。
60806040526040516105ac3803806105ac83398101604081905261002291610310565b61002c8282610033565b505061043e565b61003c8261010b565b6040516001600160a01b038316907fa657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b290600090a28051156100ff576100fa6001600160a01b03831663dc9cc645610092846103d0565b6040516001600160e01b031960e084901b81168252919091166004820152602401602060405180830381865afa1580156100d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f49190610407565b8261019a565b505050565b610107610211565b5050565b806001600160a01b03163b6000036101595760405162461bcd60e51b815260206004820152600c60248201526b1393d397d0d3d395149050d560a21b60448201526064015b60405180910390fd5b7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f480546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516101b79190610422565b600060405180830381855af49150503d80600081146101f2576040519150601f19603f3d011682016040523d82523d6000602084013e6101f7565b606091505b509092509050610208858383610232565b95945050505050565b34156102305760405163b398979f60e01b815260040160405180910390fd5b565b6060826102475761024282610291565b61028a565b815115801561025e57506001600160a01b0384163b155b1561028757604051639996b31560e01b81526001600160a01b0385166004820152602401610150565b50805b9392505050565b8051156102a15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b03811681146102d157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103075781810151838201526020016102ef565b50506000910152565b6000806040838503121561032357600080fd5b61032c836102ba565b60208401519092506001600160401b038082111561034957600080fd5b818501915085601f83011261035d57600080fd5b81518181111561036f5761036f6102d6565b604051601f8201601f19908116603f01168101908382118183101715610397576103976102d6565b816040528281528860208487010111156103b057600080fd5b6103c18360208301602088016102ec565b80955050505050509250929050565b805160208201516001600160e01b031980821692919060048310156103ff5780818460040360031b1b83161693505b505050919050565b60006020828403121561041957600080fd5b61028a826102ba565b600082516104348184602087016102ec565b9190910192915050565b61015f8061044d6000396000f3fe60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c63430008170033
60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c63430008170033
差分を見ると、creationCodeの最後が runtimeCode で終わっています。
まだコンストラクトの引数は与えられていないので、creationCodeは init code と runtime code を返しています。
これにコンストラクタの引数を付与してあげれば、creation codeとしてデプロイできそうなのでテストを書いて実証します。
function test_Success_Deploy_Create2() public {
address deployer = vm.addr(100);
address dictionary = address(new Dictionary(deployer));
bytes memory initData = "";
bytes memory code = abi.encodePacked(
type(ERC7546Proxy).creationCode,
abi.encode(dictionary, initData)
);
uint256 salt = 1;
address account = Create2.deploy(0, bytes32(salt), code);
assertEq(account.code, type(ERC7546Proxy).runtimeCode);
}
きちんと、デプロイできています!!
128bytes加えた状態のERC7546Proxyをデプロイする
それでは、元々やりたかった ERC7546Proxy の runtimeCode に 128bytes(salt, chainId, tokenContract, tokenId) を加えてデプロイしていきましょう。
しかし、下記のように、bytesを生成しても、runtime codeに追加することは出来なさそうです。
bytes memory code = abi.encodePacked(
type(ERC7546Proxy).creationCode,
abi.encode(dictionary, initData),
abi.encode(salt, chainId, tokenContract, tokenId)
);
type(ERC7546Proxy).creationCode
-> init code + runtime code
abi.encode(dictionary, initData)
-> constructor parameters
abi.encode(salt, chainId, tokenContract, tokenId
-> (参照されない)constructor parameters
現状を整理して、考察すると、
- init codeの中で runtime code のサイズを決めていそう
- init codeの中で constructor parameters を参照する順番を決めていそう
ということが推察できます。
なので、方針として、 init codeの中で runtime code のサイズを決めている部分に128bytes足して領域を確保してあげれば良さそう です。
init code
-> runtime codeの領域を128bytes増やす
runtime code
-> 128bytes(salt, chainId, tokenContract, tokenId)を追加する
constructor parameters
-> 既存通りパラメータを渡す
init code = type(ERC7546Proxy).creationCode - type(ERC7546Proxy).runtimeCode
によって導出した init codeは下記です。
60806040526040516105ac3803806105ac83398101604081905261002291610310565b61002c8282610033565b505061043e565b61003c8261010b565b6040516001600160a01b038316907fa657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b290600090a28051156100ff576100fa6001600160a01b03831663dc9cc645610092846103d0565b6040516001600160e01b031960e084901b81168252919091166004820152602401602060405180830381865afa1580156100d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f49190610407565b8261019a565b505050565b610107610211565b5050565b806001600160a01b03163b6000036101595760405162461bcd60e51b815260206004820152600c60248201526b1393d397d0d3d395149050d560a21b60448201526064015b60405180910390fd5b7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f480546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516101b79190610422565b600060405180830381855af49150503d80600081146101f2576040519150601f19603f3d011682016040523d82523d6000602084013e6101f7565b606091505b509092509050610208858383610232565b95945050505050565b34156102305760405163b398979f60e01b815260040160405180910390fd5b565b6060826102475761024282610291565b61028a565b815115801561025e57506001600160a01b0384163b155b1561028757604051639996b31560e01b81526001600160a01b0385166004820152602401610150565b50805b9392505050565b8051156102a15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b03811681146102d157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103075781810151838201526020016102ef565b50506000910152565b6000806040838503121561032357600080fd5b61032c836102ba565b60208401519092506001600160401b038082111561034957600080fd5b818501915085601f83011261035d57600080fd5b81518181111561036f5761036f6102d6565b604051601f8201601f19908116603f01168101908382118183101715610397576103976102d6565b816040528281528860208487010111156103b057600080fd5b6103c18360208301602088016102ec565b80955050505050509250929050565b805160208201516001600160e01b031980821692919060048310156103ff5780818460040360031b1b83161693505b505050919050565b60006020828403121561041957600080fd5b61028a826102ba565b600082516104348184602087016102ec565b9190910192915050565b61015f8061044d6000396000f3fe
この記事を読んでいる皆さんは、EVMのbytecodeくらい脳内で解析できると思いますが、私はまだそこまで至れていないので、EVMCODEを使ってアセンブリにしてコードを読んでいきます。
EVM: assembly
[00] PUSH1 80
[02] PUSH1 40
[04] MSTORE
[05] PUSH1 40
[07] MLOAD
[08] PUSH2 05ac
[0b] CODESIZE
[0c] SUB
[0d] DUP1
[0e] PUSH2 05ac
[11] DUP4
[12] CODECOPY
[13] DUP2
[14] ADD
[15] PUSH1 40
[17] DUP2
[18] SWAP1
[19] MSTORE
[1a] PUSH2 0022
[1d] SWAP2
[1e] PUSH2 0310
[21] JUMP
[22] JUMPDEST
[23] PUSH2 002c
[26] DUP3
[27] DUP3
[28] PUSH2 0033
[2b] JUMP
[2c] JUMPDEST
[2d] POP
[2e] POP
[2f] PUSH2 043e
[32] JUMP
[33] JUMPDEST
[34] PUSH2 003c
[37] DUP3
[38] PUSH2 010b
[3b] JUMP
[3c] JUMPDEST
[3d] PUSH1 40
[3f] MLOAD
[40] PUSH1 01
[42] PUSH1 01
[44] PUSH1 a0
[46] SHL
[47] SUB
[48] DUP4
[49] AND
[4a] SWAP1
[4b] PUSH32 a657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b2
[6c] SWAP1
[6d] PUSH1 00
[6f] SWAP1
[70] LOG2
[71] DUP1
[72] MLOAD
[73] ISZERO
[74] PUSH2 00ff
[77] JUMPI
[78] PUSH2 00fa
[7b] PUSH1 01
[7d] PUSH1 01
[7f] PUSH1 a0
[81] SHL
[82] SUB
[83] DUP4
[84] AND
[85] PUSH4 dc9cc645
[8a] PUSH2 0092
[8d] DUP5
[8e] PUSH2 03d0
[91] JUMP
[92] JUMPDEST
[93] PUSH1 40
[95] MLOAD
[96] PUSH1 01
[98] PUSH1 01
[9a] PUSH1 e0
[9c] SHL
[9d] SUB
[9e] NOT
[9f] PUSH1 e0
[a1] DUP5
[a2] SWAP1
[a3] SHL
[a4] DUP2
[a5] AND
[a6] DUP3
[a7] MSTORE
[a8] SWAP2
[a9] SWAP1
[aa] SWAP2
[ab] AND
[ac] PUSH1 04
[ae] DUP3
[af] ADD
[b0] MSTORE
[b1] PUSH1 24
[b3] ADD
[b4] PUSH1 20
[b6] PUSH1 40
[b8] MLOAD
[b9] DUP1
[ba] DUP4
[bb] SUB
[bc] DUP2
[bd] DUP7
[be] GAS
[bf] STATICCALL
[c0] ISZERO
[c1] DUP1
[c2] ISZERO
[c3] PUSH2 00d0
[c6] JUMPI
[c7] RETURNDATASIZE
[c8] PUSH1 00
[ca] DUP1
[cb] RETURNDATACOPY
[cc] RETURNDATASIZE
[cd] PUSH1 00
[cf] REVERT
[d0] JUMPDEST
[d1] POP
[d2] POP
[d3] POP
[d4] POP
[d5] PUSH1 40
[d7] MLOAD
[d8] RETURNDATASIZE
[d9] PUSH1 1f
[db] NOT
[dc] PUSH1 1f
[de] DUP3
[df] ADD
[e0] AND
[e1] DUP3
[e2] ADD
[e3] DUP1
[e4] PUSH1 40
[e6] MSTORE
[e7] POP
[e8] DUP2
[e9] ADD
[ea] SWAP1
[eb] PUSH2 00f4
[ee] SWAP2
[ef] SWAP1
[f0] PUSH2 0407
[f3] JUMP
[f4] JUMPDEST
[f5] DUP3
[f6] PUSH2 019a
[f9] JUMP
[fa] JUMPDEST
[fb] POP
[fc] POP
[fd] POP
[fe] JUMP
[ff] JUMPDEST
[100] PUSH2 0107
[103] PUSH2 0211
[106] JUMP
[107] JUMPDEST
[108] POP
[109] POP
[10a] JUMP
[10b] JUMPDEST
[10c] DUP1
[10d] PUSH1 01
[10f] PUSH1 01
[111] PUSH1 a0
[113] SHL
[114] SUB
[115] AND
[116] EXTCODESIZE
[117] PUSH1 00
[119] SUB
[11a] PUSH2 0159
[11d] JUMPI
[11e] PUSH1 40
[120] MLOAD
[121] PUSH3 461bcd
[125] PUSH1 e5
[127] SHL
[128] DUP2
[129] MSTORE
[12a] PUSH1 20
[12c] PUSH1 04
[12e] DUP3
[12f] ADD
[130] MSTORE
[131] PUSH1 0c
[133] PUSH1 24
[135] DUP3
[136] ADD
[137] MSTORE
[138] PUSH12 1393d397d0d3d395149050d5
[145] PUSH1 a2
[147] SHL
[148] PUSH1 44
[14a] DUP3
[14b] ADD
[14c] MSTORE
[14d] PUSH1 64
[14f] ADD
[150] JUMPDEST
[151] PUSH1 40
[153] MLOAD
[154] DUP1
[155] SWAP2
[156] SUB
[157] SWAP1
[158] REVERT
[159] JUMPDEST
[15a] PUSH32 267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4
[17b] DUP1
[17c] SLOAD
[17d] PUSH1 01
[17f] PUSH1 01
[181] PUSH1 a0
[183] SHL
[184] SUB
[185] NOT
[186] AND
[187] PUSH1 01
[189] PUSH1 01
[18b] PUSH1 a0
[18d] SHL
[18e] SUB
[18f] SWAP3
[190] SWAP1
[191] SWAP3
[192] AND
[193] SWAP2
[194] SWAP1
[195] SWAP2
[196] OR
[197] SWAP1
[198] SSTORE
[199] JUMP
[19a] JUMPDEST
[19b] PUSH1 60
[19d] PUSH1 00
[19f] DUP1
[1a0] DUP5
[1a1] PUSH1 01
[1a3] PUSH1 01
[1a5] PUSH1 a0
[1a7] SHL
[1a8] SUB
[1a9] AND
[1aa] DUP5
[1ab] PUSH1 40
[1ad] MLOAD
[1ae] PUSH2 01b7
[1b1] SWAP2
[1b2] SWAP1
[1b3] PUSH2 0422
[1b6] JUMP
[1b7] JUMPDEST
[1b8] PUSH1 00
[1ba] PUSH1 40
[1bc] MLOAD
[1bd] DUP1
[1be] DUP4
[1bf] SUB
[1c0] DUP2
[1c1] DUP6
[1c2] GAS
[1c3] DELEGATECALL
[1c4] SWAP2
[1c5] POP
[1c6] POP
[1c7] RETURNDATASIZE
[1c8] DUP1
[1c9] PUSH1 00
[1cb] DUP2
[1cc] EQ
[1cd] PUSH2 01f2
[1d0] JUMPI
[1d1] PUSH1 40
[1d3] MLOAD
[1d4] SWAP2
[1d5] POP
[1d6] PUSH1 1f
[1d8] NOT
[1d9] PUSH1 3f
[1db] RETURNDATASIZE
[1dc] ADD
[1dd] AND
[1de] DUP3
[1df] ADD
[1e0] PUSH1 40
[1e2] MSTORE
[1e3] RETURNDATASIZE
[1e4] DUP3
[1e5] MSTORE
[1e6] RETURNDATASIZE
[1e7] PUSH1 00
[1e9] PUSH1 20
[1eb] DUP5
[1ec] ADD
[1ed] RETURNDATACOPY
[1ee] PUSH2 01f7
[1f1] JUMP
[1f2] JUMPDEST
[1f3] PUSH1 60
[1f5] SWAP2
[1f6] POP
[1f7] JUMPDEST
[1f8] POP
[1f9] SWAP1
[1fa] SWAP3
[1fb] POP
[1fc] SWAP1
[1fd] POP
[1fe] PUSH2 0208
[201] DUP6
[202] DUP4
[203] DUP4
[204] PUSH2 0232
[207] JUMP
[208] JUMPDEST
[209] SWAP6
[20a] SWAP5
[20b] POP
[20c] POP
[20d] POP
[20e] POP
[20f] POP
[210] JUMP
[211] JUMPDEST
[212] CALLVALUE
[213] ISZERO
[214] PUSH2 0230
[217] JUMPI
[218] PUSH1 40
[21a] MLOAD
[21b] PUSH4 b398979f
[220] PUSH1 e0
[222] SHL
[223] DUP2
[224] MSTORE
[225] PUSH1 04
[227] ADD
[228] PUSH1 40
[22a] MLOAD
[22b] DUP1
[22c] SWAP2
[22d] SUB
[22e] SWAP1
[22f] REVERT
[230] JUMPDEST
[231] JUMP
[232] JUMPDEST
[233] PUSH1 60
[235] DUP3
[236] PUSH2 0247
[239] JUMPI
[23a] PUSH2 0242
[23d] DUP3
[23e] PUSH2 0291
[241] JUMP
[242] JUMPDEST
[243] PUSH2 028a
[246] JUMP
[247] JUMPDEST
[248] DUP2
[249] MLOAD
[24a] ISZERO
[24b] DUP1
[24c] ISZERO
[24d] PUSH2 025e
[250] JUMPI
[251] POP
[252] PUSH1 01
[254] PUSH1 01
[256] PUSH1 a0
[258] SHL
[259] SUB
[25a] DUP5
[25b] AND
[25c] EXTCODESIZE
[25d] ISZERO
[25e] JUMPDEST
[25f] ISZERO
[260] PUSH2 0287
[263] JUMPI
[264] PUSH1 40
[266] MLOAD
[267] PUSH4 9996b315
[26c] PUSH1 e0
[26e] SHL
[26f] DUP2
[270] MSTORE
[271] PUSH1 01
[273] PUSH1 01
[275] PUSH1 a0
[277] SHL
[278] SUB
[279] DUP6
[27a] AND
[27b] PUSH1 04
[27d] DUP3
[27e] ADD
[27f] MSTORE
[280] PUSH1 24
[282] ADD
[283] PUSH2 0150
[286] JUMP
[287] JUMPDEST
[288] POP
[289] DUP1
[28a] JUMPDEST
[28b] SWAP4
[28c] SWAP3
[28d] POP
[28e] POP
[28f] POP
[290] JUMP
[291] JUMPDEST
[292] DUP1
[293] MLOAD
[294] ISZERO
[295] PUSH2 02a1
[298] JUMPI
[299] DUP1
[29a] MLOAD
[29b] DUP1
[29c] DUP3
[29d] PUSH1 20
[29f] ADD
[2a0] REVERT
[2a1] JUMPDEST
[2a2] PUSH1 40
[2a4] MLOAD
[2a5] PUSH4 0a12f521
[2aa] PUSH1 e1
[2ac] SHL
[2ad] DUP2
[2ae] MSTORE
[2af] PUSH1 04
[2b1] ADD
[2b2] PUSH1 40
[2b4] MLOAD
[2b5] DUP1
[2b6] SWAP2
[2b7] SUB
[2b8] SWAP1
[2b9] REVERT
[2ba] JUMPDEST
[2bb] DUP1
[2bc] MLOAD
[2bd] PUSH1 01
[2bf] PUSH1 01
[2c1] PUSH1 a0
[2c3] SHL
[2c4] SUB
[2c5] DUP2
[2c6] AND
[2c7] DUP2
[2c8] EQ
[2c9] PUSH2 02d1
[2cc] JUMPI
[2cd] PUSH1 00
[2cf] DUP1
[2d0] REVERT
[2d1] JUMPDEST
[2d2] SWAP2
[2d3] SWAP1
[2d4] POP
[2d5] JUMP
[2d6] JUMPDEST
[2d7] PUSH4 4e487b71
[2dc] PUSH1 e0
[2de] SHL
[2df] PUSH1 00
[2e1] MSTORE
[2e2] PUSH1 41
[2e4] PUSH1 04
[2e6] MSTORE
[2e7] PUSH1 24
[2e9] PUSH1 00
[2eb] REVERT
[2ec] JUMPDEST
[2ed] PUSH1 00
[2ef] JUMPDEST
[2f0] DUP4
[2f1] DUP2
[2f2] LT
[2f3] ISZERO
[2f4] PUSH2 0307
[2f7] JUMPI
[2f8] DUP2
[2f9] DUP2
[2fa] ADD
[2fb] MLOAD
[2fc] DUP4
[2fd] DUP3
[2fe] ADD
[2ff] MSTORE
[300] PUSH1 20
[302] ADD
[303] PUSH2 02ef
[306] JUMP
[307] JUMPDEST
[308] POP
[309] POP
[30a] PUSH1 00
[30c] SWAP2
[30d] ADD
[30e] MSTORE
[30f] JUMP
[310] JUMPDEST
[311] PUSH1 00
[313] DUP1
[314] PUSH1 40
[316] DUP4
[317] DUP6
[318] SUB
[319] SLT
[31a] ISZERO
[31b] PUSH2 0323
[31e] JUMPI
[31f] PUSH1 00
[321] DUP1
[322] REVERT
[323] JUMPDEST
[324] PUSH2 032c
[327] DUP4
[328] PUSH2 02ba
[32b] JUMP
[32c] JUMPDEST
[32d] PUSH1 20
[32f] DUP5
[330] ADD
[331] MLOAD
[332] SWAP1
[333] SWAP3
[334] POP
[335] PUSH1 01
[337] PUSH1 01
[339] PUSH1 40
[33b] SHL
[33c] SUB
[33d] DUP1
[33e] DUP3
[33f] GT
[340] ISZERO
[341] PUSH2 0349
[344] JUMPI
[345] PUSH1 00
[347] DUP1
[348] REVERT
[349] JUMPDEST
[34a] DUP2
[34b] DUP6
[34c] ADD
[34d] SWAP2
[34e] POP
[34f] DUP6
[350] PUSH1 1f
[352] DUP4
[353] ADD
[354] SLT
[355] PUSH2 035d
[358] JUMPI
[359] PUSH1 00
[35b] DUP1
[35c] REVERT
[35d] JUMPDEST
[35e] DUP2
[35f] MLOAD
[360] DUP2
[361] DUP2
[362] GT
[363] ISZERO
[364] PUSH2 036f
[367] JUMPI
[368] PUSH2 036f
[36b] PUSH2 02d6
[36e] JUMP
[36f] JUMPDEST
[370] PUSH1 40
[372] MLOAD
[373] PUSH1 1f
[375] DUP3
[376] ADD
[377] PUSH1 1f
[379] NOT
[37a] SWAP1
[37b] DUP2
[37c] AND
[37d] PUSH1 3f
[37f] ADD
[380] AND
[381] DUP2
[382] ADD
[383] SWAP1
[384] DUP4
[385] DUP3
[386] GT
[387] DUP2
[388] DUP4
[389] LT
[38a] OR
[38b] ISZERO
[38c] PUSH2 0397
[38f] JUMPI
[390] PUSH2 0397
[393] PUSH2 02d6
[396] JUMP
[397] JUMPDEST
[398] DUP2
[399] PUSH1 40
[39b] MSTORE
[39c] DUP3
[39d] DUP2
[39e] MSTORE
[39f] DUP9
[3a0] PUSH1 20
[3a2] DUP5
[3a3] DUP8
[3a4] ADD
[3a5] ADD
[3a6] GT
[3a7] ISZERO
[3a8] PUSH2 03b0
[3ab] JUMPI
[3ac] PUSH1 00
[3ae] DUP1
[3af] REVERT
[3b0] JUMPDEST
[3b1] PUSH2 03c1
[3b4] DUP4
[3b5] PUSH1 20
[3b7] DUP4
[3b8] ADD
[3b9] PUSH1 20
[3bb] DUP9
[3bc] ADD
[3bd] PUSH2 02ec
[3c0] JUMP
[3c1] JUMPDEST
[3c2] DUP1
[3c3] SWAP6
[3c4] POP
[3c5] POP
[3c6] POP
[3c7] POP
[3c8] POP
[3c9] POP
[3ca] SWAP3
[3cb] POP
[3cc] SWAP3
[3cd] SWAP1
[3ce] POP
[3cf] JUMP
[3d0] JUMPDEST
[3d1] DUP1
[3d2] MLOAD
[3d3] PUSH1 20
[3d5] DUP3
[3d6] ADD
[3d7] MLOAD
[3d8] PUSH1 01
[3da] PUSH1 01
[3dc] PUSH1 e0
[3de] SHL
[3df] SUB
[3e0] NOT
[3e1] DUP1
[3e2] DUP3
[3e3] AND
[3e4] SWAP3
[3e5] SWAP2
[3e6] SWAP1
[3e7] PUSH1 04
[3e9] DUP4
[3ea] LT
[3eb] ISZERO
[3ec] PUSH2 03ff
[3ef] JUMPI
[3f0] DUP1
[3f1] DUP2
[3f2] DUP5
[3f3] PUSH1 04
[3f5] SUB
[3f6] PUSH1 03
[3f8] SHL
[3f9] SHL
[3fa] DUP4
[3fb] AND
[3fc] AND
[3fd] SWAP4
[3fe] POP
[3ff] JUMPDEST
[400] POP
[401] POP
[402] POP
[403] SWAP2
[404] SWAP1
[405] POP
[406] JUMP
[407] JUMPDEST
[408] PUSH1 00
[40a] PUSH1 20
[40c] DUP3
[40d] DUP5
[40e] SUB
[40f] SLT
[410] ISZERO
[411] PUSH2 0419
[414] JUMPI
[415] PUSH1 00
[417] DUP1
[418] REVERT
[419] JUMPDEST
[41a] PUSH2 028a
[41d] DUP3
[41e] PUSH2 02ba
[421] JUMP
[422] JUMPDEST
[423] PUSH1 00
[425] DUP3
[426] MLOAD
[427] PUSH2 0434
[42a] DUP2
[42b] DUP5
[42c] PUSH1 20
[42e] DUP8
[42f] ADD
[430] PUSH2 02ec
[433] JUMP
[434] JUMPDEST
[435] SWAP2
[436] SWAP1
[437] SWAP2
[438] ADD
[439] SWAP3
[43a] SWAP2
[43b] POP
[43c] POP
[43d] JUMP
[43e] JUMPDEST
[43f] PUSH2 015f
[442] DUP1
[443] PUSH2 044d
[446] PUSH1 00
[448] CODECOPY
[449] PUSH1 00
[44b] RETURN
アセンブリの詳細は省きますが、
下記3つの技を使えば、大抵 runtime code の領域を確保している箇所を特定できます。
- type(ERC7546Proxy).runtimeCode のサイズを16進にして探す
- 今回は351bytes(0x015F)なので[43f]
- CODECOPYしている前で領域を確保している場所を探す
- アセンブリを全部読む
ERC7546Proxyは、コンストラクタがそれなりにコードを持っているので結構大変ですが、コンストラクタを持っていないProxyや、軽量なコンストラクタの場合は瞬殺で探し出せると思います。
今回は結論として、下記二つに128bytesを足す必要がありました。
- [0e] 05ac -> コンストラクタの開始位置(これをずらさないと引数を取りに行くbytesもズレる)
- [43f] 015F -> 実際の runtime 領域のサイズ
これらに128bytes(0x80)を足した init code に runtime code を加えると下記のbytesになります。
60806040526040516105ac38038061062c83398101604081905261002291610310565b61002c8282610033565b505061043e565b61003c8261010b565b6040516001600160a01b038316907fa657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b290600090a28051156100ff576100fa6001600160a01b03831663dc9cc645610092846103d0565b6040516001600160e01b031960e084901b81168252919091166004820152602401602060405180830381865afa1580156100d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f49190610407565b8261019a565b505050565b610107610211565b5050565b806001600160a01b03163b6000036101595760405162461bcd60e51b815260206004820152600c60248201526b1393d397d0d3d395149050d560a21b60448201526064015b60405180910390fd5b7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f480546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516101b79190610422565b600060405180830381855af49150503d80600081146101f2576040519150601f19603f3d011682016040523d82523d6000602084013e6101f7565b606091505b509092509050610208858383610232565b95945050505050565b34156102305760405163b398979f60e01b815260040160405180910390fd5b565b6060826102475761024282610291565b61028a565b815115801561025e57506001600160a01b0384163b155b1561028757604051639996b31560e01b81526001600160a01b0385166004820152602401610150565b50805b9392505050565b8051156102a15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b03811681146102d157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103075781810151838201526020016102ef565b50506000910152565b6000806040838503121561032357600080fd5b61032c836102ba565b60208401519092506001600160401b038082111561034957600080fd5b818501915085601f83011261035d57600080fd5b81518181111561036f5761036f6102d6565b604051601f8201601f19908116603f01168101908382118183101715610397576103976102d6565b816040528281528860208487010111156103b057600080fd5b6103c18360208301602088016102ec565b80955050505050509250929050565b805160208201516001600160e01b031980821692919060048310156103ff5780818460040360031b1b83161693505b505050919050565b60006020828403121561041957600080fd5b61028a826102ba565b600082516104348184602087016102ec565b9190910192915050565b6101df8061044d6000396000f3fe60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c63430008170033
コードを書いてデプロイ後の runtime code を確認してみましょう。
function test_Success_Deploy_ERC7546Proxy_For_ERC6551Account() public {
address deployer = vm.addr(100);
address dictionary = address(new Dictionary(deployer));
bytes memory initData = "";
uint256 salt = 1;
uint256 chainId = 2;
address tokenContract = vm.addr(101);
uint256 tokenId = 3;
bytes memory code = abi.encodePacked(
/// @dev parse type(ERC7546Proxy).creationCode to increase the runtimeCode area by 128 bytes.
hex"60806040526040516105ac38038061062c83398101604081905261002291610310565b61002c8282610033565b505061043e565b61003c8261010b565b6040516001600160a01b038316907fa657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b290600090a28051156100ff576100fa6001600160a01b03831663dc9cc645610092846103d0565b6040516001600160e01b031960e084901b81168252919091166004820152602401602060405180830381865afa1580156100d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f49190610407565b8261019a565b505050565b610107610211565b5050565b806001600160a01b03163b6000036101595760405162461bcd60e51b815260206004820152600c60248201526b1393d397d0d3d395149050d560a21b60448201526064015b60405180910390fd5b7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f480546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516101b79190610422565b600060405180830381855af49150503d80600081146101f2576040519150601f19603f3d011682016040523d82523d6000602084013e6101f7565b606091505b509092509050610208858383610232565b95945050505050565b34156102305760405163b398979f60e01b815260040160405180910390fd5b565b6060826102475761024282610291565b61028a565b815115801561025e57506001600160a01b0384163b155b1561028757604051639996b31560e01b81526001600160a01b0385166004820152602401610150565b50805b9392505050565b8051156102a15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b03811681146102d157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103075781810151838201526020016102ef565b50506000910152565b6000806040838503121561032357600080fd5b61032c836102ba565b60208401519092506001600160401b038082111561034957600080fd5b818501915085601f83011261035d57600080fd5b81518181111561036f5761036f6102d6565b604051601f8201601f19908116603f01168101908382118183101715610397576103976102d6565b816040528281528860208487010111156103b057600080fd5b6103c18360208301602088016102ec565b80955050505050509250929050565b805160208201516001600160e01b031980821692919060048310156103ff5780818460040360031b1b83161693505b505050919050565b60006020828403121561041957600080fd5b61028a826102ba565b600082516104348184602087016102ec565b9190910192915050565b6101df8061044d6000396000f3fe60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c63430008170033",
abi.encode(salt, chainId, tokenContract, tokenId),
abi.encode(dictionary, initData)
);
address account = Create2.deploy(0, bytes32(salt), code);
console2.logBytes(account.code);
}
0x60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c6343000817003300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e6b3367318c5e11a6eed3cd0d850ec06a02e9b900000000000000000000000000000000000000000000000000000000000000003
runtime code の最後128bytesが期待した値になっていますね!!!
実際にその後、ERC6551Accountとの連携を確認しましたが、全て期待通りの動作をしました👍
最終的なコードはこうなりました。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/Create2.sol";
import {IERC7546Registry} from "./interfaces/IERC7546Registry.sol";
import "@ucs/proxy/ERC7546Proxy.sol";
contract ERC7546Registry is IERC7546Registry {
function createAccount(
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId,
address dictionary,
bytes calldata initData
) external override returns (address payable) {
bytes memory code = _creationCode(salt, chainId, tokenContract, tokenId, dictionary, initData);
address _account = Create2.computeAddress(
bytes32(salt),
keccak256(code)
);
if (_account.code.length != 0) return payable(_account);
_account = Create2.deploy(0, bytes32(salt), code);
emit ERC7546AccountCreated(
_account,
salt,
chainId,
tokenContract,
tokenId,
dictionary
);
return payable(_account);
}
function account(
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId,
address dictionary,
bytes calldata initData
) external view returns (address payable) {
bytes memory code = _creationCode(salt,chainId,tokenContract,tokenId, dictionary, initData);
address _account = Create2.computeAddress(
bytes32(salt),
keccak256(code)
);
return payable(_account);
}
function _creationCode(
uint256 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId,
address dictionary,
bytes calldata initData
) internal pure returns (bytes memory) {
return
abi.encodePacked(
/// @dev parse type(ERC7546Proxy).creationCode to increase the runtimeCode area by 128 bytes.
hex"60806040526040516105ac38038061062c83398101604081905261002291610310565b61002c8282610033565b505061043e565b61003c8261010b565b6040516001600160a01b038316907fa657f2ad315cf3bb35cf1964158da75c3f334481df05a4a1644b2376b17a59b290600090a28051156100ff576100fa6001600160a01b03831663dc9cc645610092846103d0565b6040516001600160e01b031960e084901b81168252919091166004820152602401602060405180830381865afa1580156100d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f49190610407565b8261019a565b505050565b610107610211565b5050565b806001600160a01b03163b6000036101595760405162461bcd60e51b815260206004820152600c60248201526b1393d397d0d3d395149050d560a21b60448201526064015b60405180910390fd5b7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f480546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516101b79190610422565b600060405180830381855af49150503d80600081146101f2576040519150601f19603f3d011682016040523d82523d6000602084013e6101f7565b606091505b509092509050610208858383610232565b95945050505050565b34156102305760405163b398979f60e01b815260040160405180910390fd5b565b6060826102475761024282610291565b61028a565b815115801561025e57506001600160a01b0384163b155b1561028757604051639996b31560e01b81526001600160a01b0385166004820152602401610150565b50805b9392505050565b8051156102a15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b03811681146102d157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103075781810151838201526020016102ef565b50506000910152565b6000806040838503121561032357600080fd5b61032c836102ba565b60208401519092506001600160401b038082111561034957600080fd5b818501915085601f83011261035d57600080fd5b81518181111561036f5761036f6102d6565b604051601f8201601f19908116603f01168101908382118183101715610397576103976102d6565b816040528281528860208487010111156103b057600080fd5b6103c18360208301602088016102ec565b80955050505050509250929050565b805160208201516001600160e01b031980821692919060048310156103ff5780818460040360031b1b83161693505b505050919050565b60006020828403121561041957600080fd5b61028a826102ba565b600082516104348184602087016102ec565b9190910192915050565b6101df8061044d6000396000f3fe60806040523661000b57005b610013610015565b005b610025610020610027565b6100d5565b565b600061005a7f267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4546001600160a01b031690565b60405163dc9cc64560e01b81526001600160e01b03196000351660048201526001600160a01b03919091169063dc9cc64590602401602060405180830381865afa1580156100ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d091906100f9565b905090565b3660008037600080366000845af43d6000803e8080156100f4573d6000f35b3d6000fd5b60006020828403121561010b57600080fd5b81516001600160a01b038116811461012257600080fd5b939250505056fea26469706673582212201fd53eab52ad2cec1c156e10b2ae6d953858615016e763909ed5a98477337a8964736f6c63430008170033",
abi.encode(salt, chainId, tokenContract, tokenId),
abi.encode(dictionary, initData)
);
}
}
まとめ
最後まで読んでいただきありがとうございました!まとめます。
今回の検証で、ERC6551Account と Registry に依存関係がなくなることを確認しました。
この実装では、
- ERC6551Account は デプロイ方式に依存しない(runtime codeの後ろ128bytesに必要な情報をつけることはすでに仕様となっている)
- RegistryはデプロイしたいProxyに合わせて実装する
という方針をとることで、より汎用性の高いERC6551の実現が実証できたのではないかと思います。ガスコストの最適化といった文面では当然1167に劣りますが、1167を使いたい人はそれを使えば良いし、それ以外も自由に使えるようになったことで、私個人的には満足しています。
指摘や、マサカリは大歓迎ですので、忌憚なくコメントいただければ幸いです!
最後に、bytecodeを読めないようでは、まだまだ solidity が書けるとは言えないと教えていただいた @ecdysis_xyzの@kai_hiroi, @0xHaku に感謝です🙏
Discussion