ERC-2470: 单例工厂
Authors | Ricardo Guilherme Schmidt (@3esmit) |
---|---|
Created | 2020-01-15 |
Discussion Link | https://ethereum-magicians.org/t/erc-2470-singleton-factory/3933 |
Requires | EIP-1014 |
Table of Contents
简单总结
一些 DApp 需要一个且仅一个合约实例,该实例在任何链上都具有相同的地址。
一个无需许可的工厂,用于部署基于其字节码的无密钥确定性合约地址。
摘要
一些合约被设计为单例,无论它们在哪个链上都具有相同的地址,这意味着对于所有合约都应该存在一个实例,例如 EIP-1820 和 EIP-2429。这些合约通常使用一种称为 Nick 方法的方法进行部署,因此任何人都可以将这些合约部署在任何链上,并且它们具有确定性的地址。 本标准建议使用此方法创建一个 CREATE2 工厂,以便其他需要此功能的项目可以在任何链上使用相同的设置(即使在开发链中)使用此工厂。
动机
代码重用,使用该工厂可以更轻松地部署单例。
规范
[ERC-2470] 单例工厂
这是 [ERC2470 工厂智能合约] 代码的精确副本。
pragma solidity 0.6.2;
/**
* @title Singleton Factory (EIP-2470)
* @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and salt.
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
*/
contract SingletonFactory {
/**
* @notice Deploys `_initCode` using `_salt` for defining the deterministic address.
* @param _initCode Initialization code.
* @param _salt Arbitrary value to modify resulting address.
* @return createdContract Created contract address.
*/
function deploy(bytes memory _initCode, bytes32 _salt)
public
returns (address payable createdContract)
{
assembly {
createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt)
}
}
}
// IV is a value changed to generate the vanity address.
// IV: 6583047
部署交易
以下是必须用于在任何链上部署智能合约的原始交易。
0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470
交易末尾的 2470
字符串是签名的 r
和 s
。
从这种(由人生成的)确定性模式中,任何人都可以推断出没有人知道部署帐户的私钥。
部署方法
该合约将使用无密钥部署方法(也称为 Nick 的方法)进行部署,该方法依赖于一次性地址。 (有关更多详细信息,请参见 [Nick 的文章])。此方法的工作方式如下:
- 生成一个从新随机帐户部署合约的交易。
- 此交易不得使用 EIP-155,以便在任何链上运行。
- 此交易必须具有相对较高的 gas 价格才能部署在任何链上。在这种情况下,它将是 100 Gwei。
- 伪造一个具有以下参数的交易:
{ nonce: 0, gasPrice: 100000000000, value: 0, data: '0x608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c63430006020033', gasLimit: 247000, v: 27, r: '0x247000', s: '0x2470' }
以
2470
开头的r
和s
值显然是人为确定的值,而不是真实的签名。 -
我们恢复此交易的发送者,即一次性部署帐户。
因此,我们获得了一个可以广播该交易的帐户,但我们也可以保证没有人知道该帐户的私钥。
-
将 0.0247 以太坊精确地发送到此一次性部署帐户。
-
广播部署交易。
注意:247000 是部署智能合约所需 gas 的两倍,这确保了 OPCODE 定价的未来变化不太可能导致此部署交易因 gas 不足而失败。大约 0.01 ETH 的剩余 gas 将永远锁定在一次性使用地址中。
生成的交易哈希为 0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c
。
此操作可以在任何链上完成,从而保证合约地址始终相同,并且没有人可以使用具有不同合约的该地址。
单次使用工厂部署帐户
0xBb6e024b9cFFACB947A71991E386681B1Cd1477D
此帐户是通过从其交易签名中进行逆向工程生成的。 这样,没有人知道私钥,但众所周知,它是部署交易的有效签名者。
要部署注册表,必须首先将 0.0247 以太坊发送到此帐户。
工厂合约地址
0xce0042B868300000d44A59004Da54A005ffdcf9f
对于部署的每个链,合约都具有上述地址。
SingletonFactory 的 ABI:
[
{
"constant": false,
"inputs": [
{
"internalType": "bytes",
"name": "_initCode",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_salt",
"type": "bytes32"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "address payable",
"name": "createdContract",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
理由
SingletonFactory 不允许在 create2 上发送 value,这样做是为了防止创建的对象出现不同的结果。
SingletonFactory 允许用户定义的 salt,以方便为其他项目创建自定义地址。如果不需要自定义地址,则应使用 salt bytes(0)
。
由 SingletonFactory 构造的合约不得在其构造函数中使用 msg.sender
,所有变量都必须通过初始化数据传入。这是有意的,因为如果允许在创建后进行回调以帮助初始化状态,将导致具有相同地址(但不同的链)的合约具有相同的地址但不同的初始状态。
可以使用以下公式在链中通过任何合约计算生成的地址:address(keccak256(bytes1(0xff), 0xce0042B868300000d44A59004Da54A005ffdcf9f, _salt, keccak256(_code)) << 96)
或者在 javascript 中使用 https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/README.md#const-generateaddress2。
向后兼容性
不适用,因为没有正在使用的 Singleton Factory 的过去版本。
测试用例
待定
实现
https://github.com/3esmit/ERC2470
安全注意事项
某些合约可能不支持部署在任何链上,或者需要每个链上的不同地址,这可以通过在构造函数中使用 EIP-1344 中的比较来安全地完成。
从每个用户的角度来看,帐户合约都是单例,当钱包想要指示预期的链 ID 时,应使用 EIP-1191。
在工厂部署的合约不得在构造函数中使用 msg.sender
,而应使用构造函数参数,否则工厂最终将成为这些合约的控制器/唯一所有者。
版权
版权和相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Ricardo Guilherme Schmidt (@3esmit), "ERC-2470: 单例工厂," Ethereum Improvement Proposals, no. 2470, January 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2470.