即使所有者也无法调用实现“public onlyOwner”的 Solidity 函数

问题描述

我正在关注此处的文档: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 相同,我不确定从 MY_PUBLIC_KEY 开始还可以做什么来签署交易。解决这个问题的简单方法是从 onlyOwner删除 function mintNFT,一切都按预期运行。但是假设我们想要保留 onlyOwner,那么除了上面已经写的内容之外,我将如何签署交易。

注意我使用 hardHat 来编译和部署合约。那是: npx安全帽编译 npx 安全帽运行脚本/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 相同,在这种情况下,nftContract 指定了 public onlyOwner .这正是我所做的。那么从概念上讲,谁拥有这个 NFT 代码?在 etherscan 上是 to 地址(合约地址),还是 from 地址,它是我的公钥,部署合约的地址,以及调用 mint 的地址,现在是调用者失败不是所有者错误

enter image description here

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

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

这里没有额外的参数,因为我使用的是安全帽。

解决方法

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

如果交易不是由当前 onlyOwner 发送的,则 owner 修饰符会恢复交易。 (see the code)

因此您需要从部署合约的同一地址调用 mintNFT() 函数,因为这是当前的 owner。或者您可以先通过调用 owner(从当前的 transferOwnership() 地址)来更改 owner

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

,

我终于想通了,合约并没有像我部署它的方式初始化。所以你必须在部署后初始化它。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...