此函数已恢复,原因字符串 'ERC721: transfer caller is not owner nor approved'
This function reverted with reason string 'ERC721: transfer caller is not owner nor approved'
代码:
function f(address nftContract, uint256 itemId, uint256 price) public payable nonReentrant
{
uint tokenId = idToMarketItem[itemId].tokenId;
IERC721(nftContract).approve(address(this), tokenId);
IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
}
我真的不知道怎么了。请帮助我
修复方法是不在您的函数中进行批准:
function f(address nftContract, uint256 itemId, uint256 price) public payable nonReentrant
{
uint tokenId = idToMarketItem[itemId].tokenId;
IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
}
因为你试图让你的合同批准自己花费属于调用者的资金(参见我在评论中链接的答案,这解释了为什么 msg.sender 不是你在 IERC721(nftContract ).approve call) 这将不起作用。用户必须直接在 nftContract 上调用 approve。
这是用户的nft,只是它应该有权通过第三方批准他们的消费,没有其他人。
编辑:添加更完整的示例/解释。
我们来看一个简单的 ERC721 合约:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract myToken is ERC721 {
constructor() ERC721("TOKEN NAME", "TOKEN SYMBOL") {
}
function mint(uint256 tokenId) public {
_mint(msg.sender, tokenId);
}
}
它使用我们的特殊函数 mint(uint256 tokenId) 公开所有 ERC-721 函数。
现在,为了更接近您的用例,我们来看一个示例智能合约,它将尝试通过批准和 transferFrom 的方式转移属于用户的 NFT。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
import "./token.sol";
contract myContract {
myToken _tokenContract;
constructor(myToken tokenContract) {
_tokenContract = tokenContract;
}
function requestTransfer(address from, address to, uint256 tokenId) public{
_tokenContract.transferFrom(from, to, tokenId);
}
}
我们在部署时将 NFT 合约的地址发送给构造函数。我们唯一感兴趣的函数是 requestTransfer 函数。
现在让我添加一个松露测试文件(远非完美):
const truffleAssert = require('truffle-assertions');
const tokenContract = artifacts.require("myToken");
const smartContract = artifacts.require("myContract");
contract("myContract", accounts => {
it("shound hook the deployed token contract", async () => {
const tokenInstance = await tokenContract.deployed();
});
it("shound hook the deployed smart contract", async () => {
const contractInstance = await smartContract.deployed();
});
it("should mint 1 NFT", async () => {
const tokenInstance = await tokenContract.deployed();
const txResult = await tokenInstance.mint(0, {from: accounts[0]});
truffleAssert.eventEmitted(txResult, "Transfer", (event) =>
{
return event.tokenId == 0;
})
});
it("Should fail because smart contract was not approved", async () => {
const contractInstance = await smartContract.deployed();
const tokenId = 0;
let failed = false;
try {
const txResult = await contractInstance.requestTransfer(accounts[0], accounts[1], tokenId);
}
catch (error) {
failed = true;
}
assert.equal(failed, true, "This test should have failed");
})
it("Approves the smart contract to transfer accounts[0] token : (tokenId = 0)", async () => {
const contractInstance = await smartContract.deployed();
const tokenInstance = await tokenContract.deployed();
const tokenId = 0;
const approved = contractInstance.address;
const txResult = await tokenInstance.approve(contractInstance.address, tokenId, {from: accounts[0]});
truffleAssert.eventEmitted(txResult, "Approval", (event) =>
{
return event.tokenId == 0;
})
});
it("Should work because smart contract was approved", async () => {
const contractInstance = await smartContract.deployed();
const tokenId = 0;
let succeded = false;
try {
const txResult = await contractInstance.requestTransfer(accounts[0], accounts[1], tokenId);
succeded = true;
}
catch (error) {
console.log(error);
}
assert.equal(succeded, true, "This test should have succeded");
})
});
如果您遵循测试:
检查代币合约的部署
检查代币合约的部署
为帐户[0]
铸造 1 个 NFT,tokenId == 0
调用了我们智能合约的 requestTransfer 方法,但正如预期的那样失败了。因为没有批准我们的智能合约能够管理属于 accounts[0]
的 tokenId 0
从帐户[0] 地址直接对代币合约进行批准。允许我们的智能合约地址管理 tokenId 0.
调用4)中的requestTransfer方法,这次预计会成功,因为已获得适当的批准。
只有所有者才能批准第三方管理其资产。
我希望现在清楚为什么测试 4) 失败了。为什么需要 5) 才能使 6) 成功。另外现在你有一个从 JavaScript 调用 approve 的例子,这有点类似于客户端(NFT 所有者)端应该发生的事情。
代码:
function f(address nftContract, uint256 itemId, uint256 price) public payable nonReentrant
{
uint tokenId = idToMarketItem[itemId].tokenId;
IERC721(nftContract).approve(address(this), tokenId);
IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
}
我真的不知道怎么了。请帮助我
修复方法是不在您的函数中进行批准:
function f(address nftContract, uint256 itemId, uint256 price) public payable nonReentrant
{
uint tokenId = idToMarketItem[itemId].tokenId;
IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
}
因为你试图让你的合同批准自己花费属于调用者的资金(参见我在评论中链接的答案,这解释了为什么 msg.sender 不是你在 IERC721(nftContract ).approve call) 这将不起作用。用户必须直接在 nftContract 上调用 approve。
这是用户的nft,只是它应该有权通过第三方批准他们的消费,没有其他人。
编辑:添加更完整的示例/解释。
我们来看一个简单的 ERC721 合约:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract myToken is ERC721 {
constructor() ERC721("TOKEN NAME", "TOKEN SYMBOL") {
}
function mint(uint256 tokenId) public {
_mint(msg.sender, tokenId);
}
}
它使用我们的特殊函数 mint(uint256 tokenId) 公开所有 ERC-721 函数。
现在,为了更接近您的用例,我们来看一个示例智能合约,它将尝试通过批准和 transferFrom 的方式转移属于用户的 NFT。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
import "./token.sol";
contract myContract {
myToken _tokenContract;
constructor(myToken tokenContract) {
_tokenContract = tokenContract;
}
function requestTransfer(address from, address to, uint256 tokenId) public{
_tokenContract.transferFrom(from, to, tokenId);
}
}
我们在部署时将 NFT 合约的地址发送给构造函数。我们唯一感兴趣的函数是 requestTransfer 函数。
现在让我添加一个松露测试文件(远非完美):
const truffleAssert = require('truffle-assertions');
const tokenContract = artifacts.require("myToken");
const smartContract = artifacts.require("myContract");
contract("myContract", accounts => {
it("shound hook the deployed token contract", async () => {
const tokenInstance = await tokenContract.deployed();
});
it("shound hook the deployed smart contract", async () => {
const contractInstance = await smartContract.deployed();
});
it("should mint 1 NFT", async () => {
const tokenInstance = await tokenContract.deployed();
const txResult = await tokenInstance.mint(0, {from: accounts[0]});
truffleAssert.eventEmitted(txResult, "Transfer", (event) =>
{
return event.tokenId == 0;
})
});
it("Should fail because smart contract was not approved", async () => {
const contractInstance = await smartContract.deployed();
const tokenId = 0;
let failed = false;
try {
const txResult = await contractInstance.requestTransfer(accounts[0], accounts[1], tokenId);
}
catch (error) {
failed = true;
}
assert.equal(failed, true, "This test should have failed");
})
it("Approves the smart contract to transfer accounts[0] token : (tokenId = 0)", async () => {
const contractInstance = await smartContract.deployed();
const tokenInstance = await tokenContract.deployed();
const tokenId = 0;
const approved = contractInstance.address;
const txResult = await tokenInstance.approve(contractInstance.address, tokenId, {from: accounts[0]});
truffleAssert.eventEmitted(txResult, "Approval", (event) =>
{
return event.tokenId == 0;
})
});
it("Should work because smart contract was approved", async () => {
const contractInstance = await smartContract.deployed();
const tokenId = 0;
let succeded = false;
try {
const txResult = await contractInstance.requestTransfer(accounts[0], accounts[1], tokenId);
succeded = true;
}
catch (error) {
console.log(error);
}
assert.equal(succeded, true, "This test should have succeded");
})
});
如果您遵循测试:
检查代币合约的部署
检查代币合约的部署
为帐户[0]
铸造 1 个 NFT,tokenId == 0调用了我们智能合约的 requestTransfer 方法,但正如预期的那样失败了。因为没有批准我们的智能合约能够管理属于 accounts[0]
的 tokenId 0从帐户[0] 地址直接对代币合约进行批准。允许我们的智能合约地址管理 tokenId 0.
调用4)中的requestTransfer方法,这次预计会成功,因为已获得适当的批准。
只有所有者才能批准第三方管理其资产。 我希望现在清楚为什么测试 4) 失败了。为什么需要 5) 才能使 6) 成功。另外现在你有一个从 JavaScript 调用 approve 的例子,这有点类似于客户端(NFT 所有者)端应该发生的事情。