0

I have a function that lists an NFT on a marketplace for sale. In the process, it keeps records by saving data in a struct inside nested maps represented by the NFT address and token ID. I am running into the Stack too deep error on the following line in the below piece of code.

s_listings[nftAddress][tokenId] = Listing(price, msg.sender, _isPhysicalItem, Status.AWAITING_PURCHASER);

The relevant code in the smart contract is as follows

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

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

error PriceNotMet(address nftAddress, uint256 tokenId, uint256 price);
error ItemNotForSale(address nftAddress, uint256 tokenId);
error NotListed(address nftAddress, uint256 tokenId);
error Reserved(address nftAddress, uint256 tokenId);
error AlreadyListed(address nftAddress, uint256 tokenId);
error NoProceeds();
error NotOwner();
error NotApprovedForMarketplace();
error PriceMustBeAboveZero();

contract NftMarketplace is ReentrancyGuard, IERC721Receiver {

    enum Status { AWAITING_PURCHASER, AWAITING_DELIVERY, COMPLETE }

    struct Listing {
        uint256 price;
        address seller;
        bool isPhysicalItem;
        Status currStatus;
    }

    struct escrowRecords {
        address nftAddress;
        uint256 tokenId;
        address purchaser;
        address seller;
        uint256 price;
    }

    // Escrow configuratio

    

    mapping(address => mapping(uint256 => escrowRecords)) private escrowDebt;

    // Events

    event ItemListed(
        address indexed seller,
        address indexed nftAddress,
        uint256 indexed tokenId,
        uint256 price
    );

    event ItemCanceled(
        address indexed seller,
        address indexed nftAddress,
        uint256 indexed tokenId
    );

    event ItemBought(
        address indexed buyer,
        address indexed nftAddress,
        uint256 indexed tokenId,
        uint256 price
    );

    // State Variables
    mapping(address => mapping(uint256 => Listing)) private s_listings;
    mapping(address => uint256) private s_proceeds;

    // Function modifiers
    modifier notListed(
        address nftAddress,
        uint256 tokenId,
        address owner
    ) {
        Listing memory listing = s_listings[nftAddress][tokenId];
        if (listing.price > 0) {
            revert AlreadyListed(nftAddress, tokenId);
        }
        _;
    }

    modifier isOwner(
        address nftAddress,
        uint256 tokenId,
        address spender
    ) {
        IERC721 nft = IERC721(nftAddress);
        address owner = nft.ownerOf(tokenId);
        if (spender != owner) {
            revert NotOwner();
        }
        _;
    }

    modifier isListed(address nftAddress, uint256 tokenId) {
        Listing memory listing = s_listings[nftAddress][tokenId];
        if (listing.price <= 0) {
            revert NotListed(nftAddress, tokenId);
        }
        _;
    }

    modifier isReserved(address nftAddress, uint256 tokenId) {
        Listing memory listing = s_listings[nftAddress][tokenId];
        if (listing.currStatus != Status.AWAITING_PURCHASER) {
            revert Reserved(nftAddress, tokenId);
        }
        _;
    }

    // main functions

    function listItem(
        address nftAddress,
        uint256 tokenId,
        uint256 price,
        bool _isPhysicalItem
    )
        external
        notListed(nftAddress, tokenId, msg.sender)
        isOwner(nftAddress, tokenId, msg.sender)
    {
        if (price <= 0) {
            revert PriceMustBeAboveZero();
        }
        IERC721 nft = IERC721(nftAddress);
        if (nft.getApproved(tokenId) != address(this)) {
            revert NotApprovedForMarketplace();
        }
        s_listings[nftAddress][tokenId] = Listing(price, msg.sender, _isPhysicalItem, Status.AWAITING_PURCHASER);
        emit ItemListed(msg.sender, nftAddress, tokenId, price);
    }

    function cancelListing(address nftAddress, uint256 tokenId)
        external
        isOwner(nftAddress, tokenId, msg.sender)
        isListed(nftAddress, tokenId)
        isReserved(nftAddress, tokenId)
    {

        delete (s_listings[nftAddress][tokenId]);
        emit ItemCanceled(msg.sender, nftAddress, tokenId);
    }

    // function required to send ERC721 to this smart contract

    function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) 
    public
    override 
    returns (bytes4) {
            return this.onERC721Received.selector;
        }

    function buyItem(address _nftAddress, uint256 _tokenId, address _owner)
        external
        payable
        isListed(_nftAddress, _tokenId)
        isReserved(_nftAddress, _tokenId)
        nonReentrant
    {
        Listing memory listedItem = s_listings[_nftAddress][_tokenId];
        if (msg.value < listedItem.price) {
            revert PriceNotMet(_nftAddress, _tokenId, listedItem.price);
        }
        if (listedItem.isPhysicalItem == true) {
            IERC721 nft = IERC721(_nftAddress);

            listedItem.currStatus = Status.AWAITING_DELIVERY;
            address _seller = listedItem.seller;

            escrowDebt[_owner][_tokenId] = escrowRecords(_nftAddress, _tokenId, msg.sender, _seller, msg.value);
            // as function is set payable i should not need the below line
            // address(this) += msg.value;
            IERC721(_nftAddress).safeTransferFrom(listedItem.seller, address(this), _tokenId);
        } else {
            s_proceeds[listedItem.seller] += msg.value;
            delete (s_listings[_nftAddress][_tokenId]);
            IERC721(_nftAddress).safeTransferFrom(listedItem.seller, msg.sender, _tokenId);
            emit ItemBought(msg.sender, _nftAddress, _tokenId, listedItem.price);
        }

        
    }

    function confirmDelivery(address _nftAddress, uint256 _tokenId) 
        external
        isListed(_nftAddress, _tokenId)
        isReserved(_nftAddress, _tokenId)
        nonReentrant
    {
        escrowRecords memory escrowItem = escrowDebt[_nftAddress][_tokenId];
        require(msg.sender == escrowItem.purchaser);
        s_proceeds[escrowItem.seller] += escrowItem.price;
        delete (s_listings[_nftAddress][_tokenId]);
        IERC721(_nftAddress).safeTransferFrom(address(this), escrowItem.purchaser, _tokenId);
        emit ItemBought(escrowItem.purchaser, _nftAddress, _tokenId, escrowItem.price);
        delete (escrowDebt[_nftAddress][_tokenId]);

    }


    function updateListing(
        address nftAddress,
        uint256 tokenId,
        uint256 newPrice
    )
        external
        isListed(nftAddress, tokenId)
        nonReentrant
        isOwner(nftAddress, tokenId, msg.sender)
        isReserved(nftAddress, tokenId)
    {
        if (newPrice == 0) {
            revert PriceMustBeAboveZero();
        }

        s_listings[nftAddress][tokenId].price = newPrice;
        emit ItemListed(msg.sender, nftAddress, tokenId, newPrice);
    }

    function withdrawProceeds() external {
        uint256 proceeds = s_proceeds[msg.sender];
        if (proceeds <= 0) {
            revert NoProceeds();
        }
        s_proceeds[msg.sender] = 0;

        (bool success, ) = payable(msg.sender).call{value: proceeds}("");
        require(success, "Transfer failed");
    }

    function getListing(address nftAddress, uint256 tokenId)
        external
        view
        returns (Listing memory)
    {
        return s_listings[nftAddress][tokenId];
    }

    function getProceeds(address seller) external view returns (uint256) {
        return s_proceeds[seller];
    }
}


Any help on this issue would be great

*** Edit ***

I have added the full block of code as Ismael eluded that the snippet compiled fine and the error bust be somewhere else

6
  • The contract in the question compiles just fine. It is likely the error is in some part of the code not included in the question.
    – Ismael
    Jun 18, 2022 at 22:44
  • @PaulRazvanBerg Unfortunately not. I somewhat understand what the error means but finding it hard to relate other solutions to my code
    – Weivy
    Jun 19, 2022 at 8:28
  • Hi @Ismael thanks for checking. I have edited my question to include the full contract. Maybe it is somewhere else as you suggested. Both Hardhat and Remix compiling attempts pointed to the line of code I separated above
    – Weivy
    Jun 19, 2022 at 8:31
  • @Weivy The compiler is right you have too many local variables (some of them are created by the modifiers). You could fix by inlining one of the modifiers. Other solution is enclosing IERC721 nft; in its own scope, for example surrounding it in a {} block.
    – Ismael
    Jun 20, 2022 at 5:03
  • @Ismael thank you, the scoping method worked!
    – Weivy
    Jun 21, 2022 at 14:27

0

Browse other questions tagged or ask your own question.