智能合约工厂:如何用Solidity合约创建另一个合约?

智能合约工厂:如何用Solidity合约创建另一个合约?

·

3 min read

介绍

solidity 中的 new 关键字允许使用模板创建和部署新的合约实例。“XxxContractFactory”是一种命名约定和用于创建“XxxContract”合约的模式。

本文的目的是了解制作简单合约工厂的过程,同时了解它的工作原理并想象一些潜在的用例。让我们直接开始。

合约工厂是一种基于模板创建合约的设计模式。用户可以在字段中引入值,作为输入来初始化新部署的合约的状态变量。为什么这很有用?根据一些用例,你可以做一些很酷的事情,例如:

  • 允许用户创建流动资金池:Uniswap

  • 利用每个地址的 Adidas 2 NFT 铸币限制:交易

  • 无代码平台以帮助用户创建自己的代币:ThirdWeb

  • 其他例子:业务流程管理和医疗保健

实现原理

对于这个例子,我将使用我编写的 Bank 合约(模板)和 BankFactory 合约(工厂)。目的是部署一次工厂Factory合约,并根据需要创建多个银行Bank合约,而无需自己部署每个合约。

image.png

1、首先是银行 Bank(模板):

构造函数初始化由 BankFactory 传递的状态变量。在这里,我们传递Bank拥有的资金数量,谁是Bank的所有者以及谁创建了Bank合约。

//  这是一个模板合约, 拥有初始化 Bank 及其变量 

contract Bank {
  uint256 public bank_funds;
  address public owner;
  address public deployer;

  constructor(address _owner, uint256 _funds) {
    bank_funds = _funds;
    owner = _owner;
    deployer = msg.sender;
  }
}

2、第二个 BankFactory(工厂):

在这里,我们实例化银行合约 Bank,创建一个类型为银行的列表 Bank[] public list_of_banks 来跟踪新创建的合约,最后是createBank()一个将银行的构造函数参数作为参数的函数。

这是创建新合约的行:

bank = new Bank(_owner, _funds)

数组中索引 0 处的值list_of_banks成为创建合约的地址!

contract BankFactory {
  // instantiate Bank contract
  Bank bank;
  //keep track of created Bank addresses in array 
  Bank[] public list_of_banks;

  // function arguments are passed to the constructor of the new created contract 
  function createBank(address _owner, uint256 _funds) external {
    bank = new Bank(_owner, _funds);
    list_of_banks.push(bank);
  }
}

这就是全部。现在,当 Factory 已部署后,createBank(.., ..) 使用所需值作为参数调用函数将导致新Bank合约保存这些值!

想看看如何用 HardHat 做到这一点?

使用 HardHat 进行测试

1、创建一个新目录并安装安全帽:

npm install --save-dev hardhat

2、创建一个新项目并安装依赖项:

npx hardhat

3、使用你选择的文本编辑器打开文件夹。我喜欢 Visual Studio 代码:

4、创建一个新的 Solidity 文件并将代码复制到其中:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "hardhat/console.sol";

// Template used to initialize state variables 
contract Bank {

  uint256 public bank_funds;
  address public owner;
  address public deployer;

  constructor(address _owner, uint256 _funds) {
    bank_funds = _funds;
    owner = _owner;
    deployer = msg.sender;
  }
}


contract BankFactory {

  // instantiate Bank contract
  Bank bank;

  //keep track of created Bank addresses in array 
  Bank[] public list_of_banks;


  // function arguments are passed to the constructor of the new created contract 
  function createBank(address _owner, uint256 _funds) external {
    bank = new Bank(_owner, _funds);
    list_of_banks.push(bank);
  }
}

5、编译合约:

npx hardhat compile

6、测试它!注意测试脚本。将代码复制粘贴到其中并运行它!

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Deploying a BankFactory and creating Bank instances ", function () {

  it("Should instantiate the created Bank and call its functions", async () => {

    // ContractFactory in ethers.js is an abstraction used to deploy new smart contracts, so Token here is a factory for instances of our token contract.
    const BF = await ethers.getContractFactory('BankFactory');
    const Bank = await ethers.getContractFactory('Bank');

    // deploy bank factory
    const bf = await BF.deploy();
    await bf.deployed();

    const owner = "0x0d8D32B83aaBBB675333468E4794eE0cDE666666"
    // call function with (address owner, uint256 number) parameters
    const tx1_receipt = await bf.createBank(owner, 69420);
    const tx2_receipt = await bf.createBank(owner, 00100);
    const tx3_receipt = await bf.createBank(owner, 113377);

    console.log("BankFactory address at: ", tx1_receipt.to);
    console.log("\n")


    // Get the addresses of the deployed bank contracts 
    const bank1_address = await bf.list_of_banks(0);
    const bank2_address = await bf.list_of_banks(1);
    const bank3_address = await bf.list_of_banks(2);
    console.log("BankFactory array of banks, address at (index 0): ", bank1_address);
    console.log("BankFactory array of banks, address at (index 1): ", bank2_address);
    console.log("BankFactory array of banks, address at (index 2): ", bank3_address);
    console.log("\n")


    // Attach the created Bank instance to the address it's located at. Call functions.
    const bank1 = await Bank.attach(bank1_address);
    const bank2 = await Bank.attach(bank2_address);
    const bank3 = await Bank.attach(bank3_address);


    // Get Bank 1 variables
    const bank1_owner = await bank1.owner();
    const bank1_bank_funds = await bank1.bank_funds();
    const bank1_deployer = await bank1.deployer()
    console.log(`Bank 1 \nowner: ${bank1_owner}\nbank_funds: ${bank1_bank_funds}\ndeployer: ${bank1_deployer}`)
    console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")


    // Get Bank 2 variables
    const bank2_owner = await bank2.owner();
    const bank2_bank_funds = await bank2.bank_funds();
    const bank2_deployer = await bank2.deployer()
    console.log(`Bank 2 \nowner: ${bank2_owner}\nbank_funds: ${bank2_bank_funds}\ndeployer: ${bank2_deployer}`)
    console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

    // Get Bank 3 variables
    const bank3_owner = await bank3.owner();
    const bank3_bank_funds = await bank3.bank_funds();
    const bank3_deployer = await bank3.deployer()
    console.log(`Bank 3 \nowner: ${bank3_owner}\nbank_funds: ${bank3_bank_funds}\ndeployer: ${bank3_deployer}`)
    console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
  })

});

希望你和我一样完成的这个挑战!

参考链接:

docs.uniswap.org/protocol/V2/reference/smar..

research.csiro.au/blockchainpatterns/genera..

arxiv.org/abs/1706.03700

researchgate.net/publication/303996559_Untr..

docs.openzeppelin.com/learn/developing-smar..

关注我们, 获得更多优质内容