hardhat&华夫饼 - 从地址部署合同

发布于 2025-02-08 16:17:46 字数 3453 浏览 2 评论 0原文

我正在尝试使用Hardhat和Waffle测试工厂合同。我有一个称为的合同:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Domain {

    string private publicKey;

    address[] public children;

    constructor(string memory _publicKey) {
        console.log("Deploying a domain using public key: ", _publicKey);
        publicKey = _publicKey;
    }

    function getChildren() public view returns (address[] memory){
        return children;
    }
}

以及一个用于部署此合同的工厂:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";


contract DomainFactory {
    Domain[] private _domains;
    function createDomain(
                          string memory _publicKey
                          ) public returns (address){
        Domain domain = new Domain(
                                   _publicKey
                                   );
        _domains.push(domain);
        return address(domain);
    }
    function allDomains(uint256 limit, uint256 offset)
        public
        view
        returns (Domain[] memory coll)
    {
        return coll;
    }
}

我已定义以下测试,其中this是指“世界”文件中定义的上下文对象(使用cucumber.js

When('the holder of this public key creates a domain', async function () {
    this.domain = await this.factory.createDomain('<public_key>');
});

Then('a child of this domain has the name name', async function () {
    const children = this.domain.getChildren();
    const childrenWithName = children.find((child:any) => {
        return child.getNames().some((childName:any) => {
            return childName === 'name';
        })
    })

    expect(childrenWithName).to.be.an('array').that.is.not.empty;
});

理想情况下在步骤中,我可以定义this.domain由于部署合同,然后进行测试我部署的合同的方法:

// world.ts

import { setWorldConstructor, setDefaultTimeout } from '@cucumber/cucumber'
import {deployContract, MockProvider, solidity} from 'ethereum-waffle';
import {use} from "chai";
import DomainContract from "../../../artifacts/contracts/Domain.sol/Domain.json";
import DomainFactoryContract from "../../../artifacts/contracts/DomainFactory.sol/DomainFactory.json";
import { Domain, DomainFactory } from "../../../typechain-types";
import {Wallet} from "ethers";



use(solidity);

setDefaultTimeout(20 * 1000);

class DomainWorld {
  public owner: string
  public wallets: Wallet[]
    public factory: DomainFactory | undefined
    public domain: Domain | undefined
    public ready: boolean = false
    private _initialized: Promise<boolean>
 

    async deployContractByAddress(address, ...args){
        return await deployContract(this.wallets[0], address, ...args);
    }
  constructor() {
    this.wallets = new MockProvider().getWallets();
    this.owner = this.wallets[0].address

    const that = this
    this._initialized = new Promise(async (resolve, reject) => {
        try {
            that.factory = (await deployContract(that.wallets[0], DomainFactoryContract, [])) as DomainFactory;
            that.ready = true
            resolve(true)
        }catch (err) {
            reject(err)
        }
    })
  }
}


setWorldConstructor(DomainWorld);

我的问题是,Hardhat的DevalyContract函数不期望合同地址,这是我的domainFactory的创建方法所返回的。如果退货值是地址,我该如何测试通过工厂部署的合同?

I'm trying to test a factory contract using hardhat and waffle. I have a contract called Domain:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Domain {

    string private publicKey;

    address[] public children;

    constructor(string memory _publicKey) {
        console.log("Deploying a domain using public key: ", _publicKey);
        publicKey = _publicKey;
    }

    function getChildren() public view returns (address[] memory){
        return children;
    }
}

And a factory for deploying this contract:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";


contract DomainFactory {
    Domain[] private _domains;
    function createDomain(
                          string memory _publicKey
                          ) public returns (address){
        Domain domain = new Domain(
                                   _publicKey
                                   );
        _domains.push(domain);
        return address(domain);
    }
    function allDomains(uint256 limit, uint256 offset)
        public
        view
        returns (Domain[] memory coll)
    {
        return coll;
    }
}

I have the following tests defined, where this refers to a context object defined in a "world" file (using cucumber.js.

When('the holder of this public key creates a domain', async function () {
    this.domain = await this.factory.createDomain('<public_key>');
});

Then('a child of this domain has the name name', async function () {
    const children = this.domain.getChildren();
    const childrenWithName = children.find((child:any) => {
        return child.getNames().some((childName:any) => {
            return childName === 'name';
        })
    })

    expect(childrenWithName).to.be.an('array').that.is.not.empty;
});

Ideally in the when step, I could define this.domain as the result of deploying a contract, and thereafter test the methods of the contract I deploy:

// world.ts

import { setWorldConstructor, setDefaultTimeout } from '@cucumber/cucumber'
import {deployContract, MockProvider, solidity} from 'ethereum-waffle';
import {use} from "chai";
import DomainContract from "../../../artifacts/contracts/Domain.sol/Domain.json";
import DomainFactoryContract from "../../../artifacts/contracts/DomainFactory.sol/DomainFactory.json";
import { Domain, DomainFactory } from "../../../typechain-types";
import {Wallet} from "ethers";



use(solidity);

setDefaultTimeout(20 * 1000);

class DomainWorld {
  public owner: string
  public wallets: Wallet[]
    public factory: DomainFactory | undefined
    public domain: Domain | undefined
    public ready: boolean = false
    private _initialized: Promise<boolean>
 

    async deployContractByAddress(address, ...args){
        return await deployContract(this.wallets[0], address, ...args);
    }
  constructor() {
    this.wallets = new MockProvider().getWallets();
    this.owner = this.wallets[0].address

    const that = this
    this._initialized = new Promise(async (resolve, reject) => {
        try {
            that.factory = (await deployContract(that.wallets[0], DomainFactoryContract, [])) as DomainFactory;
            that.ready = true
            resolve(true)
        }catch (err) {
            reject(err)
        }
    })
  }
}


setWorldConstructor(DomainWorld);

My problem is, hardhat's deployContract function isn't expecting a contract address, which is what is returned by my DomainFactory's create method. How can I test contracts deployed via my factory if the return value is an address?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嘿看小鸭子会跑 2025-02-15 16:17:46

我已经制作了一个快速hardhat供您测试。
以下是亮点:

我发现的最简单的方法是将合同的归还价值(在这种情况下,我的意思是在您的测试环境中)发出事件。因此,我对您的代码进行了以下更改。

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";

contract DomainFactory {
    Domain[] private _domains;

    event CreatedDomain(address domainAddress);

    function createDomain(string memory _publicKey) public returns (address) {
        Domain domain = new Domain(_publicKey);
        _domains.push(domain);
        emit CreatedDomain(address(domain));
        return address(domain);
    }

    function allDomains(uint256 limit, uint256 offset)
        public
        view
        returns (Domain[] memory coll)
    {
        return coll;
    }
}

我只是简单地发出createDomain()包含已部署的地址的事件。

还有一件事:如果我没记错的话,您只能从函数返回链接中直接检索值,如果您的函数类型view。否则,您将需要发射活动,然后以后找到。

为了测试domainFactory

import { expect } from "chai";
import { ethers } from "hardhat";
import { Domain, DomainFactory } from "../typechain";

describe("Domain", function () {
  let domainFactory: DomainFactory;
  let domain: Domain;
  let domainAddress: string;

  it("Should deploy a DomainFactory ", async () => {
    const DomainFactory = await ethers.getContractFactory("DomainFactory");
    domainFactory = await DomainFactory.deploy();
    await domainFactory.deployed();
  });

  it("deploy a Domain using DomainFactory ", async () => {
    const tx = await domainFactory.createDomain("public string here");
    const rc = await tx.wait();
    const event = rc.events?.find((event) => event.event === "CreatedDomain");
    const args = event?.args;
    if (args) domainAddress = args[0];
  });

  it("attach an abi interface to the deployed domain", async () => {
    const Domain = await ethers.getContractFactory("Domain");
    domain = await Domain.attach(domainAddress);
  });

  it("get data from Domain deployed by DomainFactory ", async () => {
    const res = await domain.getChildren();
    console.log(res);
  });
});

部署()方法,从功能上的事件中获取已部署的地址,然后使用它将ABI连接到已部署的

完整的代码在这里:
https://github.com/pedrohba1/pedrohba1/pedrohba1/stackoverflow/stackover/tree/tree/main/main/main/main

与运行它有关的任何其他内容,我将在评论中添加。

I've made a quick hardhat project for you to test it.
Here are the highlights:

The easiest way I find to get this returned valued of the contract offchain (and by offchain in this case, I mean inside your test environment) is by emitting an Event. So, I made the following change to your code.

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Domain.sol";
import "hardhat/console.sol";

contract DomainFactory {
    Domain[] private _domains;

    event CreatedDomain(address domainAddress);

    function createDomain(string memory _publicKey) public returns (address) {
        Domain domain = new Domain(_publicKey);
        _domains.push(domain);
        emit CreatedDomain(address(domain));
        return address(domain);
    }

    function allDomains(uint256 limit, uint256 offset)
        public
        view
        returns (Domain[] memory coll)
    {
        return coll;
    }
}

I just simply emit a CreatedDomain() event containing the deployed Domain address.

One more thing: if I remember correctly, you can only retrieve values direct from function returns offchain, if your function is of type view. Otherwise, you will need to emit the event and then find it later.

In order to test the Domain deployed by the DomainFactory take a look at this test script:

import { expect } from "chai";
import { ethers } from "hardhat";
import { Domain, DomainFactory } from "../typechain";

describe("Domain", function () {
  let domainFactory: DomainFactory;
  let domain: Domain;
  let domainAddress: string;

  it("Should deploy a DomainFactory ", async () => {
    const DomainFactory = await ethers.getContractFactory("DomainFactory");
    domainFactory = await DomainFactory.deploy();
    await domainFactory.deployed();
  });

  it("deploy a Domain using DomainFactory ", async () => {
    const tx = await domainFactory.createDomain("public string here");
    const rc = await tx.wait();
    const event = rc.events?.find((event) => event.event === "CreatedDomain");
    const args = event?.args;
    if (args) domainAddress = args[0];
  });

  it("attach an abi interface to the deployed domain", async () => {
    const Domain = await ethers.getContractFactory("Domain");
    domain = await Domain.attach(domainAddress);
  });

  it("get data from Domain deployed by DomainFactory ", async () => {
    const res = await domain.getChildren();
    console.log(res);
  });
});

It deploys a DomainFactory then uses the createDomain() method, fetches the deployed address from the functino events, then use it to attach the ABI to the deployed Domain.

Full code here:
https://github.com/pedrohba1/stackoverflow/tree/main/Domain

Anything else related to running it I will be adding in the comments.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文