在我们基于智能合约做应用的时候,很多时候需要创建同一个合约的很多份实例。这里有个更省 gas 费的做法。
在我们基于智能合约做应用的时候,很多时候需要创建同一个合约的很多份实例。比如在做 DeFi 借贷应用时,有可能需要对每个借贷单生成一个借贷合约实例,在做多签钱包时,当用户要创建一个新的多签钱包时,合约层相应的会创建一个新的多签合约实例。
这种情况下,我们往往会引入一个工厂合约,通过特定的参数来创建对应的合约实例。就像下面的多签合约工厂一样。
pragma solidity ^0.4.15;
import "./Factory.sol";
import "./MultiSigWallet.sol";
contract MultiSigWalletFactory is Factory {
    /*
     * Public functions
     */
    /// @dev Allows verified creation of multisignature wallet.
    /// @param _owners List of initial owners.
    /// @param _required Number of required confirmations.
    /// @return Returns wallet address.
    function create(address[] _owners, uint _required)
        public
        returns (address wallet)
    {
        wallet = new MultiSigWallet(_owners, _required);
        register(wallet);
    }
}
这有个什么毛病呢?就是我们每创建一个新的合约实例,会把几乎一模一样的合约字节码也完全拷贝一份,放到新生成的合约地址所对应的代码存储空间里面。这样就造成了大量的合约代码在存储上不必要的冗余。
在这种情况下,有没有可能在保留必要状态的同时,重用已有代码的逻辑呢? 像下面这个图所展示的这样,我们通过代理合约去访问真正的业务逻辑实现合约,需要不同合约实例的时候我们创建代理合约实例就可以了,代码量比较大的合约实现保持一份不动。这样其实就可以节省不少空间了,节省空间就是节省 gas 费啊。
EIP1167 其实就是这种代理合约的更底层支持,让通过代理来创建新合约实例所花费的 gas 更少。
EIP1167 所做的事情可以概括如下,它只是把这个步骤翻译成了字节码形式
我们可以 clone 工厂去动态创建代理合约并返回新合约的地址
contract CloneFactory {
  function createClone(address target) internal returns (address result) {
    bytes20 targetBytes = bytes20(target);
    assembly {
      let clone := mload(0x40) 
      mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
      mstore(add(clone, 0x14), targetBytes)
      mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
      result := create(0, clone, 0x37)
    }
  }
}
可以进一步封装一下,比如:
contract WalletFactory is CloneFactory {
   address Template = 0x692a70D2e424a56D2C6C27aA97D1a86395877b3A;
   function createWallet() external returns (address newWallet) {
        newWallet = createClone(Template);
    }
}
// constructor
constructor(address _owner) external {
    owner = _owner;
}
// initializer
function setOwner(address _owner) external {
    require(owner == address(0));
    owner = _owner;
}
contract WalletFactory is CloneFactory {
   address Template = 0x692a70D2e424a56D2C6C27aA97D1a86395877b3A;
   function createWallet(address _owner) external returns (address newWallet) {
        newWallet = createClone(Template);
        newWallet.setOwner(_owner);
    }
}
// 使用 setter 方法
mapping(address => bool) public isOwner;
uint public dailyWithdrawLimit;
uint public signaturesRequired;
function set(address[] _owner, uint limit, uint required) external {
    require(dailyWithdrawLimit == 0 && signaturesRequired == 0);
    dailyWithdrawLimit = limit;
    signaturesRequired = required;
    //DO SOMETHING ELSE
}
// 使用常量
 string public constant name = "DemoToken";
 string public constant symbol = "DET";
 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!