4

我正在关注这里的文档:https ://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft/how-to-mint-a-nft 。并有一个形式的智能合约:

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

  contract NFTA is ERC721, Ownable {

     using Counters for Counters.Counter;
     Counters.Counter public _tokenIds;
     mapping (uint256 => string) public _tokenURIs;
     mapping(string => uint8) public hashes;

     constructor() public ERC721("NFTA", "NFT") {}

     function mintNFT(address recipient, string memory tokenURI)
          public onlyOwner
          returns (uint256)
      {
          _tokenIds.increment();

          uint256 newItemId = _tokenIds.current();
          _mint(recipient, newItemId);
          _setTokenURI(newItemId, tokenURI);

          return newItemId;
     }

     /**
      * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
      *
      * Requirements:
      *
      * - `tokenId` must exist.
      */
     function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
     }    

  }

当我尝试minting用这个来估算 gas 成本时:

    const MY_PUBLIC_KEY  = '..'
    const MY_PRIVATE_KEY = '..'

    const ALCHEMY = {
        http: '',
        websocket:'',
    }

    const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
    const web3 = createAlchemyWeb3(ALCHEMY.http);

    const NFTA = require("../artifacts/contracts/OpenSea.sol/NFTA.json");
    const address_a   = '0x...';
    const nft_A = new web3.eth.Contract(NFTA.abi, address_a);


    async function mint({ tokenURI, run }){

        const nonce = await web3.eth.getTransactionCount(MY_PUBLIC_KEY, 'latest'); 
        const fn  = nft_A.methods.mintNFT(MY_PUBLIC_KEY, '')

        console.log( 'fn: ', fn.estimateGas() )
    }

    mint({ tokenURI: '', run: true })

我收到错误:

(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner

大概是因为mintNFTpublic onlyOwner。但是,当我检查 Etherscan 时,该From字段与 相同MY_PUBLIC_KEY,我不确定还有什么可以将交易作为 from 签名MY_PUBLIC_KEY。解决这个问题的简单方法是删除onlyOwnerfrom function mintNFT,一切都按预期运行。但是假设我们想要保留onlyOwner,我将如何签署超出上面已经写好的交易。

注意我hardHat用来编译和部署合约。即:npx hardhat compile npx hardhat run scripts/deploy.js

==============================================

附录

alchemy 给出的部署薄荷的确切代码是:

async function mintNFT(tokenURI) {
  const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce

  //the transaction
  const tx = {
    'from': PUBLIC_KEY,
    'to': contractAddress,
    'nonce': nonce,
    'gas': 500000,
    'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
  };

请注意,在交易中,该from字段是PUBLIC_KEY,与PUBLIC_KEY部署合约相同,在本例中nftContractpublic onlyOwner指定。这正是我所做的。那么从概念上讲,谁拥有这个 NFT 代码?在 etherscan 上是to地址(合约地址),还是from地址,即我的公钥,部署合约的地址,以及调用 mint 的地址,现在调用者失败不是所有者错误。在此处输入图像描述

搜索互联网,我看到其他人在这里遇到了这个问题:https://ethereum.stackexchange.com/questions/94114/erc721-testing-transferfrom,因为Truffle您可以使用额外字段指定调用者:

 await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })

额外参数在这里不是一个选项,因为我使用的是安全帽。

4

3 回答 3

3

OpenZeppelin 的Ownable.sol将默认owner值定义为合约部署者。您可以稍后通过调用更改它transferOwnership()或通过调用放弃所有者(即设置为0x0renounceOwnership()

如果onlyOwner交易不是由当前的owner. 见代码

所以你需要mintNFT()从部署合约的同一个地址调用函数,因为那是当前的owner. 或者您可以owner通过调用transferOwnership()(从当前owner地址)更改第一个。

从函数中删除onlyOwner修饰符mintNFT()将允许任何人调用该函数。

于 2021-04-16T08:43:27.653 回答
2

为使用 Alchemy 教程时偶然发现该问题的其他人回答此问题:

在教程中,它说在你的 mint 方法中初始化合约,如下所示:

const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);

但是,如果您尝试调用estimateGas() 或encodeABI(),它将失败并出现onlyOwner 错误。

解决方案是将第三行更改为:

const nftContract = new web3.eth.Contract(contract.abi, contractAddress, {
    from: PUBLIC_KEY
});

这将设置“From”的默认值,以便当您在标记为 onlyOwner 的铸币函数上调用estimateGas() 时,它将能够使用该 from 字段来查看其所有者调用了estimateGas。

花了很长时间才弄清楚这一点。

于 2021-08-16T22:27:37.420 回答
1

我终于想通了,合同没有初始化我部署它的方式。因此,您必须在部署后对其进行初始化。

于 2021-04-20T17:08:32.977 回答