合约工厂与克隆工厂
工厂设计模式是编程中相当常见的模式。这个想法很简单,不是直接创建对象,而是由对象(工厂)来创建对象。在Solidity中,一个对象就是一个智能合约,所以合约工厂可以为你部署新的合约。
> * 原文:https://soliditydeveloper.com/clonefactory > * 译文出自:[登链翻译计划](https://github.com/lbc-team/Pioneer) > * 译者:[aisiji](https://learnblockchain.cn/people/3291) > * 校对:[Tiny 熊](https://learnblockchain.cn/people/15) > * 本文永久链接:[learnblockchain.cn/article…](https://learnblockchain.cn/article/2602) > 如何简单的在合约中部署合约并且尽可能的节省gas [工厂设计模式](https://en.wikipedia.org/wiki/Factory_method_pattern)是编程中相当常见的模式。这个想法很简单,不是直接创建对象,而是由对象(工厂)来创建对象。在[Solidity](https://learnblockchain.cn/docs/solidity/)中,一个对象就是一[智能合约](https://learnblockchain.cn/2018/01/04/understanding-smart-contracts),所以合约工厂可以为你部署新的合约。 ## 为什么是工厂 让我们先讨论一下什么时候以及为什么你会想要一个工厂。 让我们先看看什么时候不需要工厂: * 你只在主网上部署一次合约,然后就不再部署了。 很明显,如果你只部署一次,工厂就没有意义。那么,多次部署呢? * 你想跟踪所有部署的合约。 * 你想在部署时节省Gas。 * 你想为用户或你自己提供一个简单的方法来部署合约。 ![](https://img.learnblockchain.cn/2020/07/27/15958322619544.jpg) ## 一个简单的工厂 在最简单的情况下,你的工厂只是一个合约,它有一个函数用来部署你实际使用的合约。让我们来看一个修改过的[MetaCoin](https://www.trufflesuite.com/boxes/metacoin)。 ```javascript // SPDX-License-Identifier: MIT pragma solidity 0.6.11; import "./MetaCoin.sol"; contract MetaCoinFactory { MetaCoin[] public metaCoinAddresses; event MetaCoinCreated(MetaCoin metaCoin); address private metaCoinOwner; constructor(address _metaCoinOwner ) public { metaCoinOwner = _metaCoinOwner ; } function createMetaCoin(uint256 initialBalance) external { MetaCoin metaCoin = new MetaCoin(metaCoinOwner, initialBalance); metaCoinAddresses.push(metaCoin); emit MetaCoinCreated(metaCoin); } function getMetaCoins() external view returns (MetaCoin[] memory) { return metaCoinAddresses; } } ``` 正如你所看到的,`createMetaCoin`函数部署了新的`MetaCoins`。 可以将部署所需的变量存储在工厂内(如`owner`)或将它们传递给部署函数(如`initialBalance`)。 我们还保留了一个所有已部署合约的列表,你可以通过`getMetaCoins()`访问。你可能想为管理已部署的合约添加更多的功能,比如寻找特定的 MetaCoin 合约,禁用一个 MetaCoin 等等。这些都是拥有一个工厂的好理由。 但这里有一个潜在的问题:[高 gas 费](https://ethereum.stackexchange.com/q/84764/33305)。这里就是我们可以利用克隆(Clone)的地方了... ![](https://img.learnblockchain.cn/2020/07/27/15958323231518.jpg) ## 克隆工厂 如果你总是部署同一种合约,那么不必为这些字节码浪费 gas 了。 任何合约都会有几乎相同的字节码,所以我们不需要在每次部署时重复存储所有字节码。 ### 它是如何工作的? 也许需要谢谢[DELEGATECALL](https://eips.ethereum.org/EIPS/eip-7)操作码。我们只部署一次`MetaCoin`合约,把它作为一个执行合约,这样,就不用每次都部署新的`MetaCoin`合约。当我们部署新合约时,将所有的调用委托给执行合约,记住`DELEGATECALL`的功能,它让合约通过自己的状态来调用执行合约,这样每个合约都可以将执行合约作为库,并且拥有自己的状态。 ### 如何使用它 有一个很好的[CloneFactory](https://github.com/optionality/clone-factory)软件包,不过它有点过时了,如果要在最新的 Solidity 编译器中使用它,必须复制源代码并改变 `pragma` 设置。它安全吗?应该是的,但使用它需要自己承担风险,或者最好先进行审计(无论如何你都应该这样做)。 1. 不能用构造变量克隆合约,所以我们第一步是创建一个新的合约`MetaCoinClonable`,并把所有部署变量移到一个新的`initialize`函数。 2. 然后简单地继承`CloneFactory`。 3. 使用`createClone`来部署一个新的合约。 4. 调用`initialize`来传递之前的构造函数变量。 ```javascript // SPDX-License-Identifier: MIT pragma solidity 0.6.11; import "@openzeppelin/contracts/access/Ownable.sol"; import "./CloneFactory.sol"; import "./MetaCoinClonable.sol"; contract MetaCoinCloneFactory is CloneFactory, Ownable { MetaCoinClonable[] public metaCoinAddresses; event MetaCoinCreated(MetaCoinClonable metaCoin); address public libraryAddress; address private metaCoinOwner; constructor(address _metaCoinOwner) public { metaCoinOwner = _metaCoinOwner; } function setLibraryAddress(address _libraryAddress) external onlyOwner { libraryAddress = _libraryAddress; } function createMetaCoin(uint256 initialBalance) external { MetaCoinClonable metaCoin = MetaCoinClonable( createClone(libraryAddress) ); metaCoin.initialize(metaCoinOwner, initialBalance); metaCoinAddresses.push(metaCoin); emit MetaCoinCreated(metaCoin); } function getMetaCoins() external view returns (MetaCoinClonable[] memory) { return metaCoinAddresses; } } ``` 首先需要部署一个单一的 MetaCoin 实现合约,然后通过`setLibraryAddress`传递其地址,就这么简单。 **以前部署的合约是否受到设置新库地址的影响 ?** 不,这只会影响后来的部署。如果你想让旧的合约被改变,你必须让它们[可升级](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2)。 **如果代码库地址合约自毁了怎么办 ?** 所有之前部署的合约都将停止工作,所以需要确保不能发生这种情况。 **有什么坏处吗 ?** 不多,但如果没有适当的审计,我不会把它用于大批量的合约。当前 Etherscan 代码验证功能[还不能用](https://www.reddit.com/r/etherscan/comments/9uzw8i/eip1167_clonefactory_support/),他们增加了[代理支持](https://medium.com/etherscan-blog/and-finally-proxy-contract-support-on-etherscan-693e3da0714b),所以也许现在能用?这可能比较麻烦,如果你做成功了,请告诉我。然而,出于安全考虑,这样做并不十分重要,因为克隆的功能非常简单,相反有一个经过验证的库合约则更重要。但是,没法在Etherscan上的进行合约的简单交互。 ## 简单工厂与克隆工厂 GAS 比较 让我们看看 Gas 消耗的差异。即使是我们的小型`MetaCoin`合约部署也已经便宜了50%以上。你的合约越大,差异越大。而合约越大,克隆工厂的部署在成本上不会有太大变化,但普通工厂的部署会越来越贵。 | Solc version: 0.6.11+commit.5ef660b1 | Optimizer enabled: true |Runs: 200 | Block limit: 6721975 gas | |---|---|---|---| | Methods | | | | | | | |--- |--- |--- |--- |--- |--- |--- | | Contract | Method | Min | Max | Avg | # calls | eur (avg) | | MetaCoinCloneFactory | createMetaCoin | 94539 | 109527 |95039 | 30 | 0.68 | | MetaCoinFactory | createMetaCoin | 208441 | 212653 | 212513 | 30 | 1.53 | 现在开始你可以用克隆技术节省 Gas 了。现在,Gas 费又特别高,希望这篇文章对你有用。 还有试过`CloneFactory`吗?你能想到使用或不使用它的其他原因吗? --- 本翻译由 [Cell Network](https://www.cellnetwork.io/?utm_souce=learnblockchain) 赞助支持。
- 原文:https://soliditydeveloper.com/clonefactory
- 译文出自:登链翻译计划
- 译者:aisiji
- 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
如何简单的在合约中部署合约并且尽可能的节省gas
工厂设计模式是编程中相当常见的模式。这个想法很简单,不是直接创建对象,而是由对象(工厂)来创建对象。在Solidity中,一个对象就是一智能合约,所以合约工厂可以为你部署新的合约。
为什么是工厂
让我们先讨论一下什么时候以及为什么你会想要一个工厂。 让我们先看看什么时候不需要工厂:
- 你只在主网上部署一次合约,然后就不再部署了。
很明显,如果你只部署一次,工厂就没有意义。那么,多次部署呢?
- 你想跟踪所有部署的合约。
- 你想在部署时节省Gas。
- 你想为用户或你自己提供一个简单的方法来部署合约。
一个简单的工厂
在最简单的情况下,你的工厂只是一个合约,它有一个函数用来部署你实际使用的合约。让我们来看一个修改过的MetaCoin。
// SPDX-License-Identifier: MIT
pragma solidity 0.6.11;
import "./MetaCoin.sol";
contract MetaCoinFactory {
MetaCoin[] public metaCoinAddresses;
event MetaCoinCreated(MetaCoin metaCoin);
address private metaCoinOwner;
constructor(address _metaCoinOwner ) public {
metaCoinOwner = _metaCoinOwner ;
}
function createMetaCoin(uint256 initialBalance) external {
MetaCoin metaCoin = new MetaCoin(metaCoinOwner, initialBalance);
metaCoinAddresses.push(metaCoin);
emit MetaCoinCreated(metaCoin);
}
function getMetaCoins() external view returns (MetaCoin[] memory) {
return metaCoinAddresses;
}
}
正如你所看到的,createMetaCoin
函数部署了新的MetaCoins
。 可以将部署所需的变量存储在工厂内(如owner
)或将它们传递给部署函数(如initialBalance
)。
我们还保留了一个所有已部署合约的列表,你可以通过getMetaCoins()
访问。你可能想为管理已部署的合约添加更多的功能,比如寻找特定的 MetaCoin 合约,禁用一个 MetaCoin 等等。这些都是拥有一个工厂的好理由。
但这里有一个潜在的问题:高 gas 费。这里就是我们可以利用克隆(Clone)的地方了...
克隆工厂
如果你总是部署同一种合约,那么不必为这些字节码浪费 gas 了。 任何合约都会有几乎相同的字节码,所以我们不需要在每次部署时重复存储所有字节码。
它是如何工作的?
也许需要谢谢DELEGATECALL操作码。我们只部署一次MetaCoin
合约,把它作为一个执行合约,这样,就不用每次都部署新的MetaCoin
合约。当我们部署新合约时,将所有的调用委托给执行合约,记住DELEGATECALL
的功能,它让合约通过自己的状态来调用执行合约,这样每个合约都可以将执行合约作为库,并且拥有自己的状态。
如何使用它
有一个很好的CloneFactory软件包,不过它有点过时了,如果要在最新的 Solidity 编译器中使用它,必须复制源代码并改变 pragma
设置。它安全吗?应该是的,但使用它需要自己承担风险,或者最好先进行审计(无论如何你都应该这样做)。
- 不能用构造变量克隆合约,所以我们第一步是创建一个新的合约
MetaCoinClonable
,并把所有部署变量移到一个新的initialize
函数。 - 然后简单地继承
CloneFactory
。 - 使用
createClone
来部署一个新的合约。 - 调用
initialize
来传递之前的构造函数变量。
// SPDX-License-Identifier: MIT
pragma solidity 0.6.11;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./CloneFactory.sol";
import "./MetaCoinClonable.sol";
contract MetaCoinCloneFactory is CloneFactory, Ownable {
MetaCoinClonable[] public metaCoinAddresses;
event MetaCoinCreated(MetaCoinClonable metaCoin);
address public libraryAddress;
address private metaCoinOwner;
constructor(address _metaCoinOwner) public {
metaCoinOwner = _metaCoinOwner;
}
function setLibraryAddress(address _libraryAddress) external onlyOwner {
libraryAddress = _libraryAddress;
}
function createMetaCoin(uint256 initialBalance) external {
MetaCoinClonable metaCoin = MetaCoinClonable(
createClone(libraryAddress)
);
metaCoin.initialize(metaCoinOwner, initialBalance);
metaCoinAddresses.push(metaCoin);
emit MetaCoinCreated(metaCoin);
}
function getMetaCoins() external view returns (MetaCoinClonable[] memory) {
return metaCoinAddresses;
}
}
首先需要部署一个单一的 MetaCoin 实现合约,然后通过setLibraryAddress
传递其地址,就这么简单。
以前部署的合约是否受到设置新库地址的影响 ?
不,这只会影响后来的部署。如果你想让旧的合约被改变,你必须让它们可升级。
如果代码库地址合约自毁了怎么办 ?
所有之前部署的合约都将停止工作,所以需要确保不能发生这种情况。
有什么坏处吗 ?
不多,但如果没有适当的审计,我不会把它用于大批量的合约。当前 Etherscan 代码验证功能还不能用,他们增加了代理支持,所以也许现在能用?这可能比较麻烦,如果你做成功了,请告诉我。然而,出于安全考虑,这样做并不十分重要,因为克隆的功能非常简单,相反有一个经过验证的库合约则更重要。但是,没法在Etherscan上的进行合约的简单交互。
简单工厂与克隆工厂 GAS 比较
让我们看看 Gas 消耗的差异。即使是我们的小型MetaCoin
合约部署也已经便宜了50%以上。你的合约越大,差异越大。而合约越大,克隆工厂的部署在成本上不会有太大变化,但普通工厂的部署会越来越贵。
Solc version: 0.6.11+commit.5ef660b1 | Optimizer enabled: true | Runs: 200 | Block limit: 6721975 gas |
---|
Methods | ||||||
---|---|---|---|---|---|---|
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
MetaCoinCloneFactory | createMetaCoin | 94539 | 109527 | 95039 | 30 | 0.68 |
MetaCoinFactory | createMetaCoin | 208441 | 212653 | 212513 | 30 | 1.53 |
现在开始你可以用克隆技术节省 Gas 了。现在,Gas 费又特别高,希望这篇文章对你有用。
还有试过CloneFactory
吗?你能想到使用或不使用它的其他原因吗?
本翻译由 Cell Network 赞助支持。
区块链技术网。
- 发表于 2021-06-10 10:09
- 阅读 ( 681 )
- 学分 ( 147 )
- 分类:智能合约
评论