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

chi*_*ro2 7 ethereum solidity ether

我正在按照此处的文档进行操作: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;
     }    

  }
Run Code Online (Sandbox Code Playgroud)

当我尝试用这个来估计天然气成本时minting

    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 })
Run Code Online (Sandbox Code Playgroud)

我收到错误:

(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner
Run Code Online (Sandbox Code Playgroud)

大概是因为mintNFTpublic onlyOwner。但是,当我检查 Etherscan 时,该From字段与 相同MY_PUBLIC_KEY,并且我不确定还可以做什么来签署来自 的交易MY_PUBLIC_KEY。解决这个问题的简单方法是删除onlyOwnerfrom function mintNFT,一切都会按预期运行。但是假设我们想保留onlyOwner,除了上面已经写的内容之外,我将如何签署交易。

请注意,我正在用来hardHat编译合约并部署它们。即:npxhardhat编译npxhardhat运行脚本/deploy.js

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

附录

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

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()
  };
Run Code Online (Sandbox Code Playgroud)

请注意,交易中的from字段为,与部署合约的字段PUBLIC_KEY相同,在本例中已指定。这正是我所做的。那么从概念上来说谁拥有这个 NFT 代码呢?在etherscan上,它是地址(合约地址),或者是我的公钥的地址,部署合约的地址,以及调用mint的地址,现在由于调用者不是所有者错误而失败。PUBLIC_KEYnftContractpublic onlyOwnertofrom在此输入图像描述

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

 await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })
Run Code Online (Sandbox Code Playgroud)

此处无法选择额外参数,因为我使用的是安全帽。

Pet*_*jda 6

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

onlyOwner如果当前 未发送交易,则修改器将恢复该交易owner见代码

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

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


chi*_*ro2 1

我终于弄清楚了,合约没有按照我部署它的方式初始化。所以你必须在部署后对其进行初始化。