代码:
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 调用中所想的那样)这不会起作用。用户必须直接调用 nftContract 上的批准。
这是用户的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 个带有 tokenId == 0 的 NFT
调用我们智能合约的 requestTransfer 方法并按预期失败。因为没有批准我们的智能合约能够管理属于accounts[0]的tokenId 0
从账户[0]地址直接批准代币合约。允许我们的智能合约地址管理tokenId 0。
如 4) 中所示调用 requestTransfer 方法,这次由于已获得适当的批准,因此预计会成功。
只有所有者才批准第三方管理其 Assets 。 我希望现在已经清楚为什么测试 4) 失败了。为什么 6) 成功需要 5)。另外,现在您有一个从 JavaScript 调用批准的示例,这与客户端(NFT 所有者)端发生的情况有些相似。
关于solidity - 此函数已恢复,原因字符串为 'ERC721: transfer caller is not owner nor approved',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69779171/