微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在外部创建合同

如何解决在外部创建合同

据我所知,使用 new 运算符在合约内部创建合约会将新创建的合约的字节码附加到创建合约中,这有效地扩展了原始合约。因此,合约大小可能会超过部署时的 24KB 限制。

我想知道是否有一种合约创建模式,即新合约与原始合约分离并且仍然可以以现有方式访问。谢谢。

下面是一个在合约中发起新合约的例子。查看字节码了解我的意思。

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "./1_Storage.sol";

contract Test {
    
    Storage stg;
    constructor() {
        stg = new Storage();
    }
    
    function getter() public view returns(uint256) {
        return stg.retrieve();
    }
    
    function setter(uint256 val) public {
        stg.store(val);
    }
}

解决方法

一旦部署,字节码是不可变的(selfdestructcreate2 操作码的组合除外,它们允许在同一地址上销毁和重新部署新的字节码)。

stg = new Storage();Storage 的实例部署到新地址并返回 stg 中的实例。 它不会扩展已经部署的字节码。

event LogAddress(address _address);
    
Storage stg;
constructor() {
    stg = new Storage();
    emit LogAddress(address(stg));
}

这会为 from(发出的 Test 合同)和 _address(部署的 Storage 合同)返回不同的值

[{
    "from": "0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47","topic": "0xb123f68b8ba02b447d91a6629e121111b7dd6061ff418a60139c8bf00522a284","event": "LogAddress","args": {
        "0": "0xD9eC9E840Bb5Df076DBbb488d01485058f421e58","_address": "0xD9eC9E840Bb5Df076DBbb488d01485058f421e58"
    }
}]

Test 合约地址的字节码也包含 Storage 合约定义,因为 import "./1_Storage.sol"; 语句。但是,如果您运行 stg = new Storage(); 10 次,原始字节码不会改变 - 它只会将 Storage 部署到 10 个不同的地址。

您可以通过将构造函数外部的 stg = new Storage(); 移动到单独的函数来测试它。您会看到,执行单独的函数后,Test 地址上的字节码不会改变。


编辑:如果您想减小“基本”工厂合同的大小,您可以只将代理部署到您的实现中。实施仍需要部署在某处,但可以在您的合同之外。

在地址 0x123 上实现

pragma solidity ^0.8;

contract Storage
    bool initialized;
    uint256 value;

    constructor() {
        initialized = true;
    }

    function init() external {
        require (!initialized,'Already initialized');
        initialized = true;
    }

    function retrieve() external returns (uint256) {
        return value;
    }

    function setter(uint256 val) external {
        value = val;
    }
}

地址为 0x456 的工厂

pragma solidity ^0.8;

interface Storage {
    // only need the `init()` definition,other functions are not used here
    function init() external;

    // mind that this is just an interface,the implementation is on address 0x123
}

contract Factory {
    function createStorage() external returns (address target) {
        // 0x123 is the implementation address
        address target = createMinimalProxy(address(0x123));
        // init function instead of constructor (because the contract has already been deployed),you can pass arguments if you need
        Storage(target).init();
    }

    function createMinimalProxy(address _implementation) internal returns (address result) {
        bytes20 implementationBytes = bytes20(_implementation);
        assembly {
            let clone := mload(0x40)
            mstore(clone,0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone,0x14),implementationBytes)
            mstore(add(clone,0x28),0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            result := create(0,clone,0x37)
        }
    }
}

此示例使用 EIP-1667 minimal proxy(它实际上是在 createMinimalProxy() 函数中内置的 45 个字节)。

每次执行 createMinimalProxy() 时,它都会将最小代理(45 个字节)部署到新地址并返回新地址。

由于 delegatecall 指令(在代理合约中),当用户调用(或在其上执行函数)代理时,调用使用实现的字节码 - 但代理的存储。 因此多个代理可以指向同一个实现,并且它们都使用单独的存储。

例如,您可以在 this article 中找到另一个代码片段。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。