簡體   English   中英

Solidity - 在測試合約時簽署鑄幣

[英]Solidity - Signing minting while testing a contract

我正在嘗試圍繞合同創建測試,但在理解如何在測試環境中簽名時遇到問題

這是我的合同

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.9 <0.9.0;

import "@divergencetech/ethier/contracts/crypto/SignatureChecker.sol";
import "@divergencetech/ethier/contracts/crypto/SignerManager.sol";
import "@divergencetech/ethier/contracts/erc721/BaseTokenURI.sol";
import "@divergencetech/ethier/contracts/erc721/ERC721ACommon.sol";
import "@divergencetech/ethier/contracts/erc721/ERC721Redeemer.sol";
import "@divergencetech/ethier/contracts/sales/FixedPriceSeller.sol";
import "@divergencetech/ethier/contracts/utils/Monotonic.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

interface ITokenURIGenerator {
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// @author divergence.xyz
contract TestBirds is
    ERC721ACommon,
    BaseTokenURI,
    FixedPriceSeller,
    ERC2981,
    AccessControlEnumerable
{
    using EnumerableSet for EnumerableSet.AddressSet;   
    using ERC721Redeemer for ERC721Redeemer.Claims;
    using Monotonic for Monotonic.Increaser;

    /**
    @notice Role of administrative users allowed to expel a Player from the
    mission.
    @dev See expelFromMission().
     */
    bytes32 public constant EXPULSION_ROLE = keccak256("EXPULSION_ROLE");

    constructor(
        string memory name,
        string memory symbol,
        string memory baseTokenURI,
        address payable beneficiary,
        address payable royaltyReceiver
    )
        ERC721ACommon(name, symbol)
        BaseTokenURI(baseTokenURI)
        FixedPriceSeller(
            2.5 ether,
            Seller.SellerConfig({
                totalInventory: 10_000,
                lockTotalInventory: true,
                maxPerAddress: 0,
                maxPerTx: 0,
                freeQuota: 125,
                lockFreeQuota: false,
                reserveFreeQuota: true
            }),
            beneficiary
        )
    {
        _setDefaultRoyalty(royaltyReceiver, 1000);
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /**
    @dev Mint tokens purchased via the Seller.
     */
    function _handlePurchase(
        address to,
        uint256 n,
        bool
    ) internal override {
        _safeMint(to, n);
    }

    /**
    @dev Record of already-used signatures.
     */
    mapping(bytes32 => bool) public usedMessages;

    /**
    @notice Mint tokens.
     */
    function mintPublic(
        address to,
        bytes32 nonce,
        bytes calldata sig
    ) external payable {
        signers.requireValidSignature(
            signaturePayload(to, nonce),
            sig,
            usedMessages
        );
        _purchase(to, 1);
    }

    function alreadyMinted(address to, bytes32 nonce)
        external
        view
        returns (bool)
    {
        return
            usedMessages[
                SignatureChecker.generateMessage(signaturePayload(to, nonce))
            ];
    }

    /**
    @dev Constructs the buffer that is hashed for validation with a minting
    signature.
     */
    function signaturePayload(address to, bytes32 nonce)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodePacked(to, nonce);
    }


    /**
    @dev Required override to select the correct baseTokenURI.
     */
    function _baseURI()
        internal
        view
        override(BaseTokenURI, ERC721A)
        returns (string memory)
    {
        return BaseTokenURI._baseURI();
    }

    /**
    @notice If set, contract to which tokenURI() calls are proxied.
     */
    ITokenURIGenerator public renderingContract;

    /**
    @notice Sets the optional tokenURI override contract.
     */
    function setRenderingContract(ITokenURIGenerator _contract)
        external
        onlyOwner
    {
        renderingContract = _contract;
    }

    /**
    @notice If renderingContract is set then returns its tokenURI(tokenId)
    return value, otherwise returns the standard baseTokenURI + tokenId.
     */
    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        if (address(renderingContract) != address(0)) {
            return renderingContract.tokenURI(tokenId);
        }
        return super.tokenURI(tokenId);
    }

    /**
    @notice Sets the contract-wide royalty info.
     */
    function setRoyaltyInfo(address receiver, uint96 feeBasisPoints)
        external
        onlyOwner
    {
        _setDefaultRoyalty(receiver, feeBasisPoints);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721ACommon, ERC2981, AccessControlEnumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

它可以編譯,但是當我嘗試為鑄幣創建測試時,我必須生成一個有效的簽名......

這是測試

const { expect } = require('chai');

describe("TestBirds", function () {
    it ("Should return correct name, URI, owner and beneficiary", async function () {

        const [owner, addr1] = await hre.ethers.getSigners()
               
        provider = ethers.provider

        const TestBirdsContract = await hre.ethers.getContractFactory("TestBirds")
        const testBirdsContractDeployed = await TestBirdsContract.deploy(
            "TestBirds",
            "APFP",
            "https://test.url/",
            owner.address,
            owner.address)

        console.log(await provider.getBalance(owner.address));
        await testBirdsContractDeployed.deployed()

        const nonce = await ethers.provider.getTransactionCount(owner.address, "latest")
        await testBirdsContractDeployed.mintPublic(owner.address, nonce, signature???)
       
        expect(await testBirdsContractDeployed.name()).to.equal("TestBirds")
        expect(await testBirdsContractDeployed.tokenURI(0), "https://test.url/0")
        expect(await testBirdsContractDeployed.ownerOf(0)).to.equal(owner.address)
    })
})

我應該如何唱這個才能工作? 沒有簽名我無法測試合同。 如果我從合同中刪除簽名參數,它就可以工作,但這不是我想要的。

謝謝

似乎唯一缺少的是添加簽名者。 這似乎適用於您的測試合同......

import { expect } from "chai";
import { ethers } from "hardhat";

describe("Test signature", function () {
  it("deploy tester contract, and send signed message", async function () {
    const TestSignature = await ethers.getContractFactory("TestSignature",owner);
    const testSignature = await TestSignature.deploy();
    await testSignature.deployed();

    const [owner] = await ethers.getSigners();

     // missing line
    testSignature.addSigner(owner.address);

    const params = ethers.utils.solidityPack(
      ["address", "uint256", "bytes32"],
      [owner.address, "10", ethers.utils.keccak256("0x66")]
    );

    const signed = await owner.signMessage(params);
    console.log("owner address", owner.address);

    await testSignature.mint(
      owner.address,
      "10",
      ethers.utils.keccak256("0x66"),
      signed
    );
  });
});

我嘗試了以下方法,但還沒有取得多大成功:

import { expect } from "chai";
import { ethers } from "hardhat";

describe("Test signature", function () {
  it("deploy tester contract, and send signed message", async function () {
    const TestSignature = await ethers.getContractFactory("TestSignature");
    const testSignature = await TestSignature.deploy();
    await testSignature.deployed();

    const [owner] = await ethers.getSigners();

    const params = ethers.utils.solidityPack(
      ["address", "uint256", "bytes32"],
      [owner.address, "10", ethers.utils.keccak256("0x66")]
    );

    const signed = await owner.signMessage(params);
    console.log("owner address", owner.address);

    await testSignature.mint(
      owner.address,
      "10",
      ethers.utils.keccak256("0x66"),
      signed
    );
  });
});

這是針對以下合同的測試:


pragma solidity >=0.8.0 <0.9.0;

// SPDX-License-Identifier: MIT

import "./SignatureChecker.sol";

contract TestSignature {
    using EnumerableSet for EnumerableSet.AddressSet;
    using SignatureChecker for EnumerableSet.AddressSet;

    EnumerableSet.AddressSet internal signers;

    constructor() {
        signers.add(msg.sender);
    }

    mapping(bytes32 => bool) public usedMessages;

    function mint(
        address to,
        uint256 price,
        bytes32 nonce,
        bytes calldata sig
    ) external payable {
        signers.requireValidSignature(
            signaturePayload(to, price, nonce),
            sig,
            usedMessages
        );
    }

    /**
    @dev Constructs the buffer that is hashed for validation with a minting signature.
     */
    function signaturePayload(
        address to,
        uint256 price,
        bytes32 nonce
    ) public pure returns (bytes memory) {
        return abi.encodePacked(to, price, nonce);
    }

    function generateMessage(bytes memory data) public pure returns (bytes32) {
        return SignatureChecker.generateMessage(data);
    }
}

據我了解,簽名消息應該與消息匹配,並且ECDSA.toEthSignedMessageHash(data)\x19Ethereum Signed Message:\n附加到消息的開頭。 匹配后,它可以恢復簽名者地址,但它似乎還不能正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM