Solidity 智能合約 - 地址生成規則
之前的文章 Solidity 智能合約 - 工廠模式 (Factory Pattern) 說明了生成智能合約的方法,這篇文章進一步說明智能合約地址生成的規則,包含了 create
和 create2
的差異。
目前產生合約主要有兩種規則:
create
create2
create
我們可以在智能合約中使用這個指令來建立合約,而上一篇文章提到的 new
寫法其實也是使用 create
指令。另外,EOA 錢包部署合約的時候也適用這個規則。這個規則中,影響地址主要是下面兩個參數:
- 發送者 (sender)
- nonce
生成的公式如下:
1 | keccak256(rlp([sender, nonce]))[12:] |
後面的 [12:]
表示生成的 hash 取最後 20 bytes。如果是 EOA 錢包部署,sender 為錢包地址,nonce 為錢包發送交易時的 nonce。如果是智能合約中建立其他合約,sender 則為執行建立合約行為的主合約地址,nonce 則為此合約的第幾次執行生成合約,從 1 開始。
例如我在 hardhat 進行測試的時候,預設錢包是:0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
每次跑測試,部署使用前一篇文章中的 Factory 合約的時候,固定都是產生這個合約地址:0x5FbDB2315678afecb367f032d93F642f64180aa3
我們用 JavaScript 程式驗算一下:
1 | const rlp = require('rlp'); |
可以看到結果符合規則。同樣的,使用 Factory 生成合約的時候,固定都會生成這個合約地址:0xa16E02E87b7454126E5E10d957A927A7F5B5d2be
我們驗算一下:
1 | const rlp = require('rlp'); |
結果正確。
create2
這個指令在 EIP-1014 後加入,new
建構子加上 salt 的時候使用的是 create2
,參考前一篇文章。影響地址主要是下面三個參數:
- 發送者 (sender)
- salt
- init_code
生成的公式如下:
1 | keccak256(0xff + sender + salt + keccak256(init_code))[12:] |
移除了 nonce 的影響,改為自訂的 salt 亂數,以及加上了 init_code。init_code 是部署合約時的 bytecode。這次改成使用 Clones.cloneDeterministic
的方法,同樣第一個部署的 Factory 合約地址為:0x5FbDB2315678afecb367f032d93F642f64180aa3
隨意使用一個 salt:0xe467a5b633a39bbdb7a33f03a16e02e87b7454126e5e10d957a927a7f5b5d2be
implementation 的地址:0xa16E02E87b7454126E5E10d957A927A7F5B5d2be
從之前的文章可以組合出 bytecode 如下:0x3d602d80600a3d3981f3363d3d373d3d3d363d73a16e02e87b7454126e5e10d957a927a7f5b5d2be5af43d82803e903d91602b57fd5bf3
這次使用 Factory 生成合約的時候,生成出這個合約地址:0xE0C69D3b6CB5Ac8770b65DfAc6174a2ACBD46D0C
同樣來驗算看看
1 | const { keccak256 } = require('js-sha3'); |
結果正確。
結論
create
使用 nonce 來做為參數,但透過 Facotry 的方式在鏈上生成合約的時候,因為交易順序影響生成結果,在有其他人同時發送交易的情況下,不能預測和確保自己生成的合約地址。create2
在同樣情況下的 sender 和 init_code 是固定的,所以能夠使用 salt 來預測和產生固定的合約地址。
延伸閱讀
Solidity 智能合約 - 工廠模式 (Factory Pattern)
Solidity 智能合約 - 工廠模式的重組問題 (Reorg)
Solidity 智能合約 - 工廠模式的搶先交易問題 (Front Running)