Rarible Clone Part11 Failed to add an item to `itemsForSale`

The following error message is displayed on metamask, and the process of adding items to itemsForSale fails.

Transaction Error. Exception thrown in contract code.

If you check the Moralis dashboard, you will see that items are displayed in the Item Class, but no data has been added to temsForSale.

Is this a problem with ganache settings?

Hey @classlacia

This is probably an error in your smartcontract. Please share the code :grinning:
And failed transaction hashes

@Yomoo
Thank you for your kindness!

My code is below↓

contracts/contracts/Marketplace.sol

pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract MorarableMarketContract {
    struct AuctionItem {
        uint256 id;
        address tokenAddress;
        uint256 tokenId;
        address payable seller;
        uint256 askingPrice;
        bool isSold;
    }

    AuctionItem[] public itemsForSale;
    mapping(address => mapping(uint256 => bool)) activeItems;

    event itemAdded(
        uint256 id,
        uint256 tokenId,
        address tokenAddress,
        uint256 askingPrice
    );
    event itemSold(uint256 id, address buyer, uint256 askingPrice);

    modifier OnlyItemOwner(address tokenAddress, uint256 tokenId) {
        IERC721 tokenContract = IERC721(tokenAddress);
        require(tokenContract.ownerOf(tokenId) == msg.sender);
        _;
    }

    modifier HasTransferApproval(address tokenAddress, uint256 tokenId) {
        IERC721 tokenContract = IERC721(tokenAddress);
        require(tokenContract.getApproved(tokenId) == address(this));
        _;
    }

    modifier ItemExists(uint256 id) {
        require(
            id < itemsForSale.length && itemsForSale[id].id == id,
            "Could not find item"
        );
        _;
    }

    modifier IsForSale(uint256 id) {
        require(itemsForSale[id].isSold == false, "Item is already sold!");
        _;
    }

    function addItemToMarket(
        uint256 tokenId,
        address tokenAddress,
        uint256 askingPrice
    )
        external
        OnlyItemOwner(tokenAddress, tokenId)
        HasTransferApproval(tokenAddress, tokenId)
        returns (uint256)
    {
        require(
            activeItems[tokenAddress][tokenId] == false,
            "Item is already up for sale!"
        );
        uint256 newItemId = itemsForSale.length;
        itemsForSale.push(
            AuctionItem(
                newItemId,
                tokenAddress,
                tokenId,
                payable(msg.sender),
                askingPrice,
                false
            )
        );
        activeItems[tokenAddress][tokenId] = true;

        assert(itemsForSale[newItemId].id == newItemId);
        emit itemAdded(newItemId, tokenId, tokenAddress, askingPrice);
        return newItemId;
    }

    function buyItem(uint256 id)
        external
        payable
        ItemExists(id)
        IsForSale(id)
        HasTransferApproval(
            itemsForSale[id].tokenAddress,
            itemsForSale[id].tokenId
        )
    {
        require(
            msg.value >= itemsForSale[id].askingPrice,
            "Not enough funds sent"
        );
        require(msg.sender != itemsForSale[id].seller);

        itemsForSale[id].isSold = true;
        activeItems[itemsForSale[id].tokenAddress][
            itemsForSale[id].tokenId
        ] = false;
        IERC721(itemsForSale[id].tokenAddress).safeTransferFrom(
            itemsForSale[id].seller,
            msg.sender,
            itemsForSale[id].tokenId
        );
        itemsForSale[id].seller.transfer(msg.value);

        emit itemSold(id, msg.sender, itemsForSale[id].askingPrice);
    }
}

3_marketplace_migrations.js

const MorarableMarketContract = artifacts.require("MorarableMarketContract");

module.exports = function (deployer) {
  deployer.deploy(MorarableMarketContract);
};

@Yomoo The transaction hash is 0x14b3feaf8fa4394cc2cc71873e8a3a82e26273a38c840751b42a1ba99fc44180

@classlacia You have used local chain or testnet?

UPD: Local, I see

@Yomoo
Yes!
I use local chain, Ganache.

truffle.config.js

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1", // Localhost (default: none)
      port: 7545, // Standard Ethereum port (default: none)
      network_id: "*", // Any network (default: none)
    },
  },

  mocha: {
    // timeout: 100000
  },

  compilers: {
    solc: {
      version: "^0.8.0",
    },
  },
  db: {
    enabled: false,
  },
};

Hey @classlacia
I’ve just tested smartcontract. It works fine.

Have you seen the video https://www.youtube.com/watch?v=1sAeQ4om01I ?

@Yomoo
Thank you for checking the smart contract.
I have relieved to find out that smart contracts were not the cause.

Yes, I saw the video.
In my moralis dashboard, EthNFTOwners is used instead of NFTTokenOwners in the Part7.5 video.
And it seems that EthNFTOwners has the same data structure of NFTTokenOwners.

@Yomoo

Do I need to add another Cloud Function to sell Items?

I wrote just only one CloudFunction below.

Moralis.Cloud.define("getUserItems", async (request) => {
  const query = new Moralis.Query("EthNFTOwners");
  query.equalTo("contract_type", "ERC721");
  query.containedIn("owner_of", request.user.attributes.accounts);
  const queryResults = await query.find();
  const results = [];
  for (let i = 0; i < queryResults.length; ++i) {
    results.push({
      "id": queryResults[i].attributes.objectId,
      "tokenId": queryResults[i].attributes.token_id,
      "tokenAddress": queryResults[i].attributes.token_address,
      "symbol": queryResults[i].attributes.symbol,
      "tokenUri": queryResults[i].attributes.token_uri,
    });
  }
  return results;
});

Maybe, the plugin setting I wrote is wrong?

abi

{
  "anonymous": false,
  "inputs": [
    {
      "indexed": false,
      "internalType": "uint256",
      "name": "id",
      "type": "uint256"
    },
    {
      "indexed": false,
      "internalType": "uint256",
      "name": "tokenId",
      "type": "uint256"
    },
    {
      "indexed": false,
      "internalType": "address",
      "name": "tokenAddress",
      "type": "address"
    },
    {
      "indexed": false,
      "internalType": "uint256",
      "name": "askingPrice",
      "type": "uint256"
    }
  ],
  "name": "itemAdded",
  "type": "event"
}

Hello, did you manage to solve the problem? I h

Hey @classlacia
Thank you for your patience. It’s hard for me to figure out what the problem is. Some more information is needed.

Are tokens added to the smart contract for sale after calling the function to add items for sale?
Also could you please check again github tutorial Repo. For fast checking you can use diffchecker

I will wait for an answer

1 Like

Hello @Yomoo , with diff
I am in the exact same situation of @classlacia. I have checked all my files with diffchecker and i’m still not able to see items in the section “ItemsForSale” in my dashboard. Which info you need ? Thanks, I have been struggling for a long time without finding the solution…

1 Like

Hey @nicklunik and @classlacia

I just ended testing code from the Tutorial github repo. And it works fine. Only one thing you have to change in the cloud code from the repo NFTTokenOwners -> EthNFTOwners.

Final Cloud Code:

Moralis.Cloud.define("getUserItems", async (request) => {

    const query = new Moralis.Query("EthNFTOwners");
    query.equalTo("contract_type", "ERC721");
    query.containedIn("owner_of", request.user.attributes.accounts);
    const queryResults = await query.find();
    const results = [];
    for (let i = 0; i < queryResults.length; ++i) {
      results.push({
        "tokenObjectId": queryResults[i].id,
        "tokenId": queryResults[i].attributes.token_id,
        "tokenAddress": queryResults[i].attributes.token_address,
        "symbol": queryResults[i].attributes.symbol,
        "tokenUri": queryResults[i].attributes.token_uri,
      });
    }
    return results;
  });
  
  Moralis.Cloud.beforeSave("ItemsForSale", async (request) => {
  
    const query = new Moralis.Query("EthNFTOwners");
    query.equalTo("token_address", request.object.get('tokenAddress'));
    query.equalTo("token_id", request.object.get('tokenId'));
    const object = await query.first();
    if (object){
        const owner = object.attributes.owner_of;
      const userQuery = new Moralis.Query(Moralis.User);
        userQuery.equalTo("accounts", owner);
      const userObject = await userQuery.first({useMasterKey:true});
      if (userObject){
          request.object.set('user', userObject);
      }
      request.object.set('token', object);
    }
  });
  
  Moralis.Cloud.beforeSave("SoldItems", async (request) => {
  
    const query = new Moralis.Query("ItemsForSale");
    query.equalTo("uid", request.object.get('uid'));
    const item = await query.first();
    if (item){
      request.object.set('item', item);
      item.set('isSold', true);
      await item.save();
      
    
      const userQuery = new Moralis.Query(Moralis.User);
        userQuery.equalTo("accounts", request.object.get('buyer'));
      const userObject = await userQuery.first({useMasterKey:true});
      if (userObject){
          request.object.set('user', userObject);
      }
    }
  });    
  
  Moralis.Cloud.define("getItems", async (request) => {
      
    const query = new Moralis.Query("ItemsForSale");
    query.notEqualTo("isSold", true);
    query.select("uid","askingPrice","tokenAddress","tokenId", "token.token_uri", "token.symbol","token.owner_of","token.id", "user.avatar","user.username");
  
    const queryResults = await query.find({useMasterKey:true});
    const results = [];
  
    for (let i = 0; i < queryResults.length; ++i) {
  
      if (!queryResults[i].attributes.token || !queryResults[i].attributes.user) continue;
  
      results.push({
        "uid": queryResults[i].attributes.uid,
        "tokenId": queryResults[i].attributes.tokenId,
        "tokenAddress": queryResults[i].attributes.tokenAddress,
        "askingPrice": queryResults[i].attributes.askingPrice,
  
        "symbol": queryResults[i].attributes.token.attributes.symbol,
        "tokenUri": queryResults[i].attributes.token.attributes.token_uri,
        "ownerOf": queryResults[i].attributes.token.attributes.owner_of,
        "tokenObjectId": queryResults[i].attributes.token.id,
        
        "sellerUsername": queryResults[i].attributes.user.attributes.username,
        "sellerAvatar": queryResults[i].attributes.user.attributes.avatar,
      });
    }
  
    return results;
  });
  
  Moralis.Cloud.define("getItem", async (request) => {
      
    const query = new Moralis.Query("ItemsForSale");
    query.equalTo("uid", request.params.uid);
    query.select("uid","askingPrice","tokenAddress","tokenId", "token.token_uri", "token.symbol","token.owner_of","token.id","user.avatar","user.username");
  
    const queryResult = await query.first({useMasterKey:true});
    if (!queryResult) return;
    return {
        "uid": queryResult.attributes.uid,
        "tokenId": queryResult.attributes.tokenId,
        "tokenAddress": queryResult.attributes.tokenAddress,
        "askingPrice": queryResult.attributes.askingPrice,
  
        "symbol": queryResult.attributes.token.attributes.symbol,
        "tokenUri": queryResult.attributes.token.attributes.token_uri,
        "ownerOf": queryResult.attributes.token.attributes.owner_of,
        "tokenObjectId": queryResult.attributes.token.id,
  
        "sellerUsername": queryResult.attributes.user.attributes.username,
        "sellerAvatar": queryResult.attributes.user.attributes.avatar,
      };
  });

  1. Items are added without problems to itemsforsale
  2. Items are shown without problems in user profile
  3. Smart contracts and plugin work without problems.

So, possible solutions of the problem:

  1. Changing in the cloud code NFTTokenOwners -> EthNFTOwners
  2. Check your logs in Moralis Dashboard
  3. Check your Sync and Watch plugin settings
  4. Check your ganache and frpc.exe settings
  5. Check your devchain proxy status:
2 Likes

@Yomoo
Thank you for continuing to be so accommodating!
Also, sorry for taking up so much of your time!

I will proceed as you taught me.

Hello @Yomoo,
Thank you very much for your time. I didn’t notice that my server was disconnected. I knew that it has to be a stupid mistake, and now everything work fine ! Thanks again, Moralis is amazing. Have a great day

1 Like

The only man who makes no mistakes is the man who never does anything.

Happy BUIDLing :man_mechanic:

2 Likes