问题描述
我正在关注此处的文档: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
大概是因为 mintNFT
是 public 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 的地址,现在是调用者失败不是所有者错误。
在互联网上搜索,我看到其他人在这里遇到了这个问题: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()
修饰符将允许任何人调用该函数。
我终于想通了,合约并没有像我部署它的方式初始化。所以你必须在部署后初始化它。