Morarible clone - Reselling NFT problem

after user 1 created NFT, user 2 bought it.
after user 2 bought the NFT, user 3 can’t buy him (metamask shows error while trying to buy “Transaction Error. Exception thrown in contract code.” ) and user 2 can’t resell it. here is the error in console log:

index.js:223 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
    at h.formatParam (index.js:223)
    at index.js:99
    at Array.map (<anonymous>)
    at h.encodeParameters (index.js:93)
    at index.js:438
    at Array.map (<anonymous>)
    at Object.b._encodeMethodABI (index.js:437)
    at Object.b._processExecuteArguments (index.js:700)
    at Object.b._executeMethod (index.js:719)
    at ensureMarketplaceIsApproved (main.js:493)

Here is “at ensureMarketplaceIsApproved (main.js:493)”

        const approvedAddress = await contract.methods.getApproved(tokenId).call({from: userAddress});

here is the full function:

 /****************  Ensure markeplace approved token  ******************/
    ensureMarketplaceIsApproved = async (tokenId, tokenAddress) => {
        // taking the metamask address of the user
        user = await Moralis.User.current();
        const userAddress = user.get('ethAddress');

        const web3 = await Moralis.enable();
        const contract = new web3.eth.Contract(tokenContractAbi, tokenAddress);
        const approvedAddress = await contract.methods.getApproved(tokenId).call({from: userAddress});

        if(approvedAddress != MARKETPLACE_CONTRACT_ADDRESS) {
            await contract.methods.approve(MARKETPLACE_CONTRACT_ADDRESS, tokenId).send({from: userAddress});
        }

    }

what you get if before that line you use console.log(tokenId, userAddress), is what you would expect to be there?

undefined ‘0xfdc3de7880dd211ba822147b4564f3caeab59d0d’

tokenId is not defined.

I have tried to go one step back to here, and also console log it here as you see but they both undefined this way:

/****************  Render User Items Template  ******************/
    renderUserItem = async (item) => {
        const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
        if (userItemListing) return;
        
        const userItem = userItemTemplate.cloneNode(true);

        userItem.getElementsByTagName("img")[0].src = item.image;
        userItem.getElementsByTagName("img")[0].alt = item.name;
        userItem.getElementsByTagName("h5")[0].innerText = item.name;
        userItem.getElementsByTagName("p")[0].innerText = item.description;

        userItem.getElementsByTagName("input")[0].value = item.askingPrice;

        
        
        if(!item.askingPrice) {
            userItem.getElementsByTagName("input")[0].disabled = item.askingPrice;
            userItem.getElementsByTagName("button")[0].disabled = item.askingPrice;
        }
        // console.log(item.tokenId, item.tokenAddress);
        userItem.getElementsByTagName("button")[0].onclick = async () => {
            user = await Moralis.User.current();
            if(!user) {
                login();
                return;
            }
            await ensureMarketplaceIsApproved(item.tokenId, item.tokenAddress);
            await marketplaceContract.methods.addItemToMarket(item.tokenId, item.tokenAddress, userItem.getElementsByTagName("input")[0].value).send({from: user.get('ethAddress')});
        };

        userItem.id = `user-item-${item.tokenObjectId}`;
        userItemsListDiv.appendChild(userItem);
    }
    
Moralis.Cloud.define("getItems", async (request) => {  
  const query = new Moralis.Query("itemsForSale");  
  query.notEqualTo("isSold", true); 

  query.select("uid", "tokenAddress", "tokenId", "askingPrice", "token.token_uri", "token.symbol", "token.owner_of", "token.id","user.username", "user.avatar");
  
  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.token_id,
        "tokenAddress": queryResults[i].attributes.token_address,
        "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", "tokenAddress", "tokenId", "askingPrice", "token.token_uri", "token.symbol", "token.owner_of", "token.id","user.username", "user.avatar");
  
  const queryResult = await query.first({useMasterKey:true}); 
  if(!queryResult) return;
  return {
        "uid": queryResult.attributes.uid,
        "tokenId": queryResult.attributes.token_id,
        "tokenAddress": queryResult.attributes.token_address,
        "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,
    };
});

I see various ways of getting token_id: tokenId, token.id, token_id

1 Like

nope still undefined, very weird…

SOLVED!

it was “tokenid” and not “tokenId” due to a typo here:

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;
});

"tokenid": queryResults[i].attributes.token_id,

"tokenId": queryResults[i].attributes.token_id,
1 Like

but the it’s still getting the same error the only different is I now can see the console log with the tokenId and address

index.js:223 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
    at h.formatParam (index.js:223)
    at index.js:99
    at Array.map (<anonymous>)
    at h.encodeParameters (index.js:93)
    at index.js:438
    at Array.map (<anonymous>)
    at Object.b._encodeMethodABI (index.js:437)
    at Object.b._processExecuteArguments (index.js:700)
    at Object.b._executeMethod (index.js:719)
    at ensureMarketplaceIsApproved (main.js:493)
ensureMarketplaceIsApproved = async (tokenId, tokenAddress) => {
        // taking the metamask address of the user
        user = await Moralis.User.current();
        const userAddress = user.get('ethAddress');

        const web3 = await Moralis.enable();
        const contract = new web3.eth.Contract(tokenContractAbi, tokenAddress);
        const approvedAddress = await contract.methods.getApproved(tokenId).call({from: userAddress});

        if(approvedAddress != MARKETPLACE_CONTRACT_ADDRESS) {
            await contract.methods.approve(MARKETPLACE_CONTRACT_ADDRESS, tokenId).send({from: userAddress});
        }

    }

It is the same line where it gives that error? Shouldn’t be a different line if you added a console.log?

console log in here:

renderUserItem = async (item) => {
        const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
        if (userItemListing) return;
        
        const userItem = userItemTemplate.cloneNode(true);

        userItem.getElementsByTagName("img")[0].src = item.image;
        userItem.getElementsByTagName("img")[0].alt = item.name;
        userItem.getElementsByTagName("h5")[0].innerText = item.name;
        userItem.getElementsByTagName("p")[0].innerText = item.description;

        userItem.getElementsByTagName("input")[0].value = item.askingPrice;

        
        if(!item.askingPrice) {
            userItem.getElementsByTagName("input")[0].disabled = item.askingPrice;
            userItem.getElementsByTagName("button")[0].disabled = item.askingPrice;
        }
        console.log(item.tokenId, item.tokenAddress);
        userItem.getElementsByTagName("button")[0].onclick = async () => {
            user = await Moralis.User.current();
            if(!user) {
                login();
                return;
            }
            await ensureMarketplaceIsApproved(item.tokenId, item.tokenAddress);
            await marketplaceContract.methods.addItemToMarket(item.tokenId, item.tokenAddress, userItem.getElementsByTagName("input")[0].value).send({from: user.get('ethAddress')});
        };

        userItem.id = `user-item-${item.tokenObjectId}`;
        userItemsListDiv.appendChild(userItem);
    }
    

working fine

but when I call this function:

ensureMarketplaceIsApproved = async (tokenId, tokenAddress) => {
        // taking the metamask address of the user
        user = await Moralis.User.current();
        const userAddress = user.get('ethAddress');

        const web3 = await Moralis.enable();
        const contract = new web3.eth.Contract(tokenContractAbi, tokenAddress);

        console.log(tokenId, tokenAddress);
        const approvedAddress = await contract.methods.getApproved(tokenId).call({from: userAddress});

        if(approvedAddress != MARKETPLACE_CONTRACT_ADDRESS) {
            await contract.methods.approve(MARKETPLACE_CONTRACT_ADDRESS, tokenId).send({from: userAddress});
        }

    }

it returns an error + the console log is undefined (which means I believe that the arguments don’t get passed)

error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
    at h.formatParam (index.js:223)
    at index.js:99
    at Array.map (<anonymous>)
    at h.encodeParameters (index.js:93)
    at index.js:438
    at Array.map (<anonymous>)
    at Object.b._encodeMethodABI (index.js:437)
    at Object.b._processExecuteArguments (index.js:700)
    at Object.b._executeMethod (index.js:719)
    at ensureMarketplaceIsApproved (main.js:495)

first error

 at ensureMarketplaceIsApproved (main.js:493)

second error:

  at ensureMarketplaceIsApproved (main.js:495)

Check if it returns something:

user = await Moralis.User.current();

Yes, I did console.log(user) inside “ensureMarketplaceIsApproved” function and it returns back the object.

I made a new test from 0:

User 1 creates NFT(1), User 2 buys NFT(1), User 2 creates a new NFT(2).

User 3 can’t buy NFT(1) from User 2, User 3 can buy NFT(2).
User 2 can’t resell NFT(1) bought from User 1.

when user 3 tries to buy NFT(1) Metamask popup said just before the buy button at the end of the popup “Transaction Error. Exception thrown in contract code.”

and when user 2 tries to press on resell button it says in the console log:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
    at h.formatParam (index.js:223)
    at index.js:99
    at Array.map (<anonymous>)
    at h.encodeParameters (index.js:93)
    at index.js:438
    at Array.map (<anonymous>)
    at Object.b._encodeMethodABI (index.js:437)
    at Object.b._processExecuteArguments (index.js:700)
    at Object.b._executeMethod (index.js:719)
    at ensureMarketplaceIsApproved (main.js:495)```

If the problem is with that tokenid then you should be able to track what happens with it from start until you get that error.

when user 3 tries to buy the 1st nft from user 2 that bought it from user 1 Metamask gives that error and when you proceed with it, it throws to the console the error and it says item is already sold

inpage.js:1 MetaMask - RPC Error: [ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert Item is already sold!","code":-32000,"data":{"0x5108895b6a36894ac4702f5230ec85733bd159e47629a9af5c8c965fdfec4606":{"error":"revert","program_counter":1756,"return":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000154974656d20697320616c726561647920736f6c64210000000000000000000000","reason":"Item is already sold!"},"stack":"RuntimeError: VM Exception while processing transaction: revert Item is already sold!\n    at Function.RuntimeError.fromResults (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\utils\\runtimeerror.js:94:13)\n    at BlockchainDouble.processBlock (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\blockchain_double.js:627:24)\n    at runMicrotasks (<anonymous>)\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}' {code: -32603, message: `[ethjs-query] while formatting outputs from RPC '{…/task_queues.js:93:5)","name":"RuntimeError"}}}}'`}

Ok, this is a new error, it says that item was already sold.
You can look in smart contract code to see how it checks that the item was already sold

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

Maybe the marketplace contract doesn’t allow to sell an item more than once. If it doesn’t reset isSold flag