实现“public onlyOwner”的 Solidity 函数甚至不能被所有者调用
Solidity function implementing `public onlyOwner` cannot be called even by the owner
我正在关注此处的文档: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
开始还可以做些什么来签署交易。解决此问题的简单方法是从 function mintNFT
中删除 onlyOwner
,并按预期删除所有 运行。但是假设我们想保留onlyOwner
,我将如何签署超出上面已经写的交易。
请注意,我正在使用 hardHat
编译合同并部署它们。那是:
npx 安全帽编译
npx 安全帽 运行 scripts/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()
};
注意交易中的 from
字段是 PUBLIC_KEY
,与部署合约的 PUBLIC_KEY
相同,在这种情况下 nftContract
有 public onlyOwner
指定的。这正是我所做的。那么从概念上讲,谁拥有这个 NFT 代码?在 etherscan 上是 to
地址(合约地址),还是 from
地址,这是我的 public 密钥,部署合约的地址,以及调用 mint 的地址,现在因 caller is not the owner 错误而失败。
上网搜索,我看到其他人在这里遇到过这个问题: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()
来更改它,或者通过调用 renounceOwnership()
.
放弃所有者(即设置为 0x0
)
如果当前 owner
未发送交易,onlyOwner
修饰符将还原交易。 (see the code)
因此您需要从部署合约的同一地址调用 mintNFT()
函数,因为那是当前的 owner
。或者您可以通过调用 transferOwnership()
(从当前 owner
地址)先更改 owner
。
从 mintNFT()
函数中删除 onlyOwner
修饰符将允许 任何人 调用该函数。
我终于想通了,合约没有按照我部署的方式初始化。所以你必须在部署后对其进行初始化。
为使用 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 的 mint 函数上调用 estimateGas() 时,它将能够使用该 from 字段来查看调用 estimateGas 的所有者。
花了很长时间才弄明白。
我正在关注此处的文档: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
开始还可以做些什么来签署交易。解决此问题的简单方法是从 function mintNFT
中删除 onlyOwner
,并按预期删除所有 运行。但是假设我们想保留onlyOwner
,我将如何签署超出上面已经写的交易。
请注意,我正在使用 hardHat
编译合同并部署它们。那是:
npx 安全帽编译
npx 安全帽 运行 scripts/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()
};
注意交易中的 from
字段是 PUBLIC_KEY
,与部署合约的 PUBLIC_KEY
相同,在这种情况下 nftContract
有 public onlyOwner
指定的。这正是我所做的。那么从概念上讲,谁拥有这个 NFT 代码?在 etherscan 上是 to
地址(合约地址),还是 from
地址,这是我的 public 密钥,部署合约的地址,以及调用 mint 的地址,现在因 caller is not the owner 错误而失败。
上网搜索,我看到其他人在这里遇到过这个问题: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()
来更改它,或者通过调用 renounceOwnership()
.
0x0
)
如果当前 owner
未发送交易,onlyOwner
修饰符将还原交易。 (see the code)
因此您需要从部署合约的同一地址调用 mintNFT()
函数,因为那是当前的 owner
。或者您可以通过调用 transferOwnership()
(从当前 owner
地址)先更改 owner
。
从 mintNFT()
函数中删除 onlyOwner
修饰符将允许 任何人 调用该函数。
我终于想通了,合约没有按照我部署的方式初始化。所以你必须在部署后对其进行初始化。
为使用 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 的 mint 函数上调用 estimateGas() 时,它将能够使用该 from 字段来查看调用 estimateGas 的所有者。
花了很长时间才弄明白。