坚固 - 测试合同时签署铸造

发布于 2025-02-11 06:03:49 字数 6209 浏览 2 评论 0原文

我正在尝试围绕合同进行测试,但是我在了解如何在测试环境中签名的问题遇到问题,

这是我的合同

// 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)
    })
})

我应该如何唱歌才能工作?没有标志,我无法测试合同。如果我从合同中删除签名参数,但这不是我想要的。

谢谢

I am trying to create a test around a contract, but I am having problems understanding how to sign it in a test environment

This is my 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);
    }
}

It compiles ok, but when I tried to create a test for minting, I must generate a valid signature...

This is the test

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)
    })
})

How should I sing this in order to work? I can not test the contract without the sign. If I remove the signature param from the contract it works, but that is not what I want.

Thanks

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

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

发布评论

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

评论(2

魂ガ小子 2025-02-18 06:03:49

看来唯一缺少的是添加签名者。这似乎适用于您的测试合同...

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
    );
  });
});

It seems that the only missing was to add a signer. This seems to work for your test contract...

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
    );
  });
});
旧时模样 2025-02-18 06:03:49

我已经尝试了以下内容,但尚未取得太大的成功:

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签名的消息附加到消息的开始。匹配后,它可以恢复签名地址,但似乎还不错。

I've attempted the following, but without much success yet:

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
    );
  });
});

That is a test against the following contract:


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);
    }
}

From what I understood, the signed message should match the message, and the ECDSA.toEthSignedMessageHash(data)
appends the \x19Ethereum Signed Message:\n to the beggining of the message. After it matches, it can recover the signer address, but it doesn't seem to work quite right yet.

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