如何在智能合约中使用工厂模式

  • 时间:
  • 浏览:61
  • 来源:区块链技术网

在上一篇文章中(传送门:区块链研究实验室 | 智能合约如何创建和继承),我们讨论了如何从另一个智能合约中创建一个智能合约。今天,我们将研究这种情况下的典型用例。

什么是工厂模式?

工厂模式的想法是拥有一个合同(工厂),该合同将承担创建其他合同的任务。在基于类的编程中,此模式的主要动机来自于单一职责原则(一个类不需要知道如何创建其他类的实例),并且该模式为构造函数提供了一种抽象。

为什么要牢固使用工厂模式?

在Solidity中,出于以下原因之一,您可能要使用工厂模式:如果要创建同一合同的多个实例,并且正在寻找一种跟踪它们并简化其管理的方法。

contract Factory {      Child[] children;      function createChild(uint data){         Child child = new Child(data);         children.push(child);      }}contract Child{     uint data;     constructor(uint _data){        data = _data;     }}

这能够节省部署成本,您部署工厂模式,以后再使用它来部署其他合同,并且可以提高合同安全性。

如何与已部署的智能合约进行交互

在深入探讨如何实现工厂模式的细节之前,我想澄清一下我们与已部署的智能合约进行交互的方式。工厂模式是关于创建子合同的,我们可能希望调用它们的某些功能以更好地管理这些合同。

当我们要调用已部署的智能合约时,需要做两件事:

合同的ABI(提供有关功能签名的信息)。如果合同在同一个项目中。您可以使用import关键字将其导入。

部署合同的地址。

让我们举个例子:

contract A {    address bAddress;    constructor(address b){       bAddress = b;    }     function callHello() external view returns(string memory){       B b = B(bAddress); // explicit conversion from address to contract type       return b.sayHello();    }}contract B {     string greeting = "hello world";     function sayHello() external view returns(string memory){         return greeting;     }}

在Remix中,首先部署合约B,然后复制其地址,并在部署时将其提供给A的构造函数。现在,您可以调用该callHello()函数,您将获得sayHello()合约B的函数结果。

正常工厂模式

在这种模式下,我们创建具有处理子合同创建功能的工厂合同,并且可能还会添加其他功能来有效管理这些合同(例如,查找特定合同或禁用合同)。在create函数中,我们使用new关键字来部署子合同。

contract Factory{     Child[] public children;     uint disabledCount;    event ChildCreated(address childAddress, uint data);     function createChild(uint data) external{       Child child = new Child(data, children.length);       children.push(child);       emit ChildCreated(address(child), data);     }     function getChildren() external view returns(Child[] memory _children){       _children = new Child[](children.length- disabledCount);       uint count;       for(uint i=0;i<children.length; i++){          if(children[i].isEnabled()){             _children[count] = children[i];             count++;          }        }     }       function disable(Child child) external {        children[child.index()].disable();        disabledCount++;     } }contract Child{    uint data;    bool public isEnabled;    uint public index;    constructor(uint _data,uint _index){       data = _data;       isEnabled = true;       index = _index;    }    function disable() external{      isEnabled = false;    }}

克隆工厂模式

先前模式的问题在于,由于所有子合同都具有相同的逻辑,并且每次我们几乎都重新部署了相同的合同-相同的代码但上下文不同,因此浪费了大量的精力。我们需要一种方法来仅部署一个具有所有功能的子合同,并使所有其他子合同充当代理,以将调用委派给我们创建的第一个子合同,并让功能在代理合同的上下文中执行。

幸运的是,有一个EIP-1167规范定义了如何廉价地实现代理合同。该代理将所有呼叫和100%的天然气转发给实施合同,然后将返回值中继回调用者。根据规范,代理合同的字节码为:

363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3。

索引10-29(含10-29)处的字节被替换为主功能合同(我们将委派调用的合同)的20字节地址。

使用可以完成代理合同的全部魔力delegatecall。通过阅读本文,您可以了解其工作原理。

现在让我们看看如何进行这项工作:

//SPDX-License-Identifier: MITpragma solidity ^0.7.0;import './CloneFactory.sol';contract Factory is CloneFactory {     Child[] public children;     address masterContract;     constructor(address _masterContract){         masterContract = _masterContract;     }     function createChild(uint data) external{        Child child = Child(createClone(masterContract));        child.init(data);        children.push(child);     }     function getChildren() external view returns(Child[] memory){         return children;     }}contract Child{    uint public data;        // use this function instead of the constructor    // since creation will be done using createClone() function    function init(uint _data) external {        data = _data;    }}

这次,我们使用了createCloneGitHub存储库中的函数来创建子合同,而不是new关键字。

您可以通过在Truffle中创建一个新的迁移文件来部署合同,如下所示:

const Child = artifacts.require("Child");const Factory = artifacts.require("Factory"); module.exports = function (_deployer) _deployer.deploy(Child).then(() => _deployer.deploy(Factory, Child.address)); };

为了测试代码是否有效,我创建了一个测试文件,您可以自行尝试确保所有文件均按预期工作:

contract("Factory", function (/* accounts */) {  it("should assert true", async function () {    await Factory.deployed();    return assert.isTrue(true);  });  describe("#createChild()",async () => {    let factory;    beforeEach(async ()=>{      factory = await Factory.deployed();    });    it("should create a new child", async () => {      await factory.createChild(1);      await factory.createChild(2);      await factory.createChild(3);      const children = await factory.getChildren();      //console.log(children);      const child1 = await Child.at(children[0]);      const child2 = await Child.at(children[1]);      const child3 = await Child.at(children[2]);      const child1Data = await child1.data();      const child2Data = await child2.data();      const child3Data = await child3.data();      assert.equal(children.length, 3);      assert.equal(child1Data, 1);      assert.equal(child2Data, 2);      assert.equal(child3Data, 3);    });  });});

猜你喜欢

2022十大智能合约开发工具

去年,我研究了一些顶级的智能合约开发者框架[2],今年,我们将扩展到更多。 我们将研究十类工具,然后看看每个类别中最适合智能合约/区块链/solidity/vyper/rust/web3开发者的工具(我们现在有太多 “区块链开发者”的术语)。

2022-01-15

DeFi中的关键,智能合约是什么?

智能合约(Smart Contract)是上世纪90年代由密码学家尼克·萨博提出的理念,由于当时缺乏可信的执行环境,智能合约没有被应用和发展,直到以太坊的出现,才让智能合约得以“复活”。

2022-01-04

如何部署Solidity智能合约到Solana

什么是Solana,你如何将Solidity智能合约部署到Solana?

2021-12-28

区块链红利吃饱后,这个巨头又想"征服"元宇宙?

据12月26日消息,百度与英伟达(NVIDIA)已达成协议,双方合作共建AI元宇宙。另外,在今日举行的百度AI开发者大会上,英伟达全球副总裁暨亚太区总裁 Raymond Teh将受邀出席,并发表主题演讲。

2021-12-27

2021年,区块链股权融资发生了怎么样的演变

过去一年,区块链行业融资井喷,在科技领域中独树一帜,A16z、红杉、老虎基金等等这些顶级机构在 2021 年的区块链行业肆意驰骋,在 DeFi、NFT、Metaverse 等领域扶持了一众创业项目。

2021-12-23