You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用createMarketItem函数触发ERC721权限错误,求合约问题排查

ERC721上架报错排查:caller is not token owner or approved

问题场景

持有2个NFT,调用以下NFTMarketplace合约的createMarketItem函数上架其中一个时,触发 revert 错误:

Execution reverted: ERC721: caller is not token owner or approved

合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract NFTMarketplace is ReentrancyGuard {
    using Counters for Counters.Counter;

    Counters.Counter private _itemIds;
    Counters.Counter private _itemsSold;

    address public owner;

    constructor() {
        owner == msg.sender;
    }

    struct MarketItem {
        uint256 itemId;
        address nftContract;
        uint256 tokenId;
        address payable seller;
        address payable owner;
        uint256 price;
        bool sold;
    }

    mapping(uint256 => MarketItem) private idToMarketItem;


    event MarketItemCreated(
        uint256 indexed itemId,
        address indexed nftContract,
        uint256 indexed tokenId,
        address seller,
        address owner,
        uint256 price,
        bool sold
    );

    function createMarketItem(
        address nftContract, uint256 tokenId, uint256 price
    ) public payable nonReentrant {
        require(price > 0, "Price must be greater than 0");

        _itemIds.increment();
        uint256 itemId = _itemIds.current();

        idToMarketItem[itemId] = MarketItem(
            itemId,
            nftContract,
            tokenId,
            payable(msg.sender),
            payable(address(0)),
            price,
            false
        );

        IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);

        emit MarketItemCreated(
            itemId,
            nftContract,
            tokenId,
            msg.sender,
            address(0),
            price,
            false
        );
    }

    function createMarketSale(
        address nftContract,
        uint256 itemId
    ) public payable nonReentrant {
        uint256 price = idToMarketItem[itemId].price;
        uint256 tokenId = idToMarketItem[itemId].tokenId;
        bool sold = idToMarketItem[itemId].sold;
        require(msg.value == price, "Please submit the asking price in order to complete the purchase");
        require(sold != true, "This Sale has already finished");

        idToMarketItem[itemId].seller.transfer(msg.value);
        IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId);
        idToMarketItem[itemId].owner = payable(msg.sender);
        _itemsSold.increment();
        idToMarketItem[itemId].sold = true;
    }

    function fetchMarketItems() public view returns (MarketItem[] memory) {
        uint256 itemCount = _itemIds.current();
        uint256 unsoldItemCount = _itemIds.current() - _itemsSold.current();
        uint256 currentIndex = 0;

        MarketItem[] memory items = new MarketItem[](unsoldItemCount);
        for (uint256 i = 0; i < itemCount; i++) {
            if (idToMarketItem[i + 1].owner == address(0)) {
                uint256 currentId = i + 1;
                MarketItem storage currentItem = idToMarketItem[currentId];
                items[currentIndex] = currentItem;
                currentIndex += 1;
            }
        }
        return items;
    }
}

问题分析与修复方案

1. 核心报错原因:未给市场合约授权NFT

合约中直接调用transferFrom将NFT转入市场,但ERC721的transferFrom要求调用者(市场合约)必须是token所有者,或者已获得token所有者的授权。

解决步骤:
在调用createMarketItem之前,先调用对应NFT合约的授权方法:

  • 单次授权:调用approve(marketplaceContractAddress, tokenId),授权市场合约操作该tokenId的NFT
  • 批量授权:调用setApprovalForAll(marketplaceContractAddress, true),授权市场合约操作你持有的所有该系列NFT

2. 合约构造函数的低级错误

构造函数中owner == msg.sender;是比较操作,而非赋值,导致owner变量始终是默认的address(0),虽然这和当前报错无关,但会影响后续依赖owner权限的功能,必须修复:

constructor() {
    owner = msg.sender; // 把 == 改成 =
}

3. 可选优化:提前校验所有权

在createMarketItem函数开头添加校验,提前确认调用者是NFT所有者,避免后续transferFrom报错:

function createMarketItem(
    address nftContract, uint256 tokenId, uint256 price
) public payable nonReentrant {
    require(price > 0, "Price must be greater than 0");
    // 新增:校验调用者是token所有者
    require(IERC721(nftContract).ownerOf(tokenId) == msg.sender, "Caller is not token owner");

    // 后续原有代码...
}

内容的提问来源于stack exchange,提问作者Jossif Elefteriadis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 12:35:30