Assignment - Marketplace Frontend

Create Marketplace frontend where users can view cats available to be bought, and at the same time be able to sell cats they own.

1 Like

Yiha!! What a ride! I love this course. My frontend is getting in shape! The Marketplace is printing all Tokens on sale and you can put your bears up for sale. I only struggle with two things at the moment and would be happy if someone could give me a hint.

  • When trying to buy a token, it will always revert, because of approval error
    But, I make sure that users approve the contract as operator. Now, I think the problem is, that when I send the buyBear() transaction, the sender actually is not the contract, but, obviously, the user. Ergo, it will revert because of approval status, and it should - because the seller did not approve the buyer as operator, but the tokenContract. But the transaction will be sent from the buyer. I hope this makes sense.! Have a look into the code:
marketplace.js
var web3 = new Web3(Web3.givenProvider);



var instance;
var user;
var mktPlaceAddress = "0x4443AAa9171796085F7bC873082876632aA64367";
var tokenAddress = "0x7E74636D36f73235Ddb0DeEf9F34237440460ee9";

        
$(document).ready(function(){
    window.ethereum.enable().then(function(accounts) {
        mktplace = new web3.eth.Contract(abimkt, mktPlaceAddress, {from: accounts[0]});
        user = accounts[0];

        console.log(mktplace);
        token = new web3.eth.Contract(abi, tokenAddress, {from: accounts[0]});
        console.log(token);
        printOrders();
 
    })

}) //End document.ready

async function printOrders(){
  var offerId;
  var item;
  let items = [];
  let currentUser = ethereum.selectedAddress;
  var tokensOnSaleIds = [];
  var bearsCount = await token.methods.getBearsLength().call();
  var tokensOnSaleCount = await mktplace.methods.getAllTokenOnSale().call();
 // console.log(tokensOnSaleCount.length);
  //console.log("BEARS COUNT: " + bearsCount);

  for(let i = 0; i < tokensOnSaleCount.length; i++){
      offerId = await mktplace.methods.getOffer(i).call();
     // console.log(offerId);
      let isOnSale = offerId.active;
      if(isOnSale){
          tokensOnSaleIds.push(offerId.tokenId);
      }   
      
  }

  for(let i = 0; i < tokensOnSaleIds.length; i++){
      let bearId = tokensOnSaleIds[i];
    //console.log("Current Bear Id: " + bearId);
     let object = await token.methods.getBear(tokensOnSaleIds[i]).call();
     let bearGenes = object.genes;
    //console.log("Current Genes: " + bearGenes);
     let bearBirthday = object.birthTime;
    // console.log("Current Birthday: " + new Date(bearBirthday * 1000));
     let bearMumId = object.mumId;
    // console.log("Current MumId: " + bearMumId);
     let bearDadId = object.dadId;
    // console.log("Current DadId: " + bearDadId);
     let bearGeneration = object.generation;
    // console.log("Current Generation: " + bearGeneration);
      
     createBearBox(bearId, bearGenes);

  }        

  function createBearBox(id, bearDetails){

      let bodyDna = bearDetails.substring(0, 2);
    //  console.log("BODYDNA" + bodyDna);
      let mouthDna = bearDetails.substring(2, 4);
    //  console.log("MOUTHDNA: " + mouthDna);
      let eyesDna = bearDetails.substring(4, 6);
    //  console.log("EYESDNA: " + eyesDna);
      let earsDna = bearDetails.substring(6, 8);
    //  console.log("EARSDNA: " + earsDna);
      let eyeShape = bearDetails.substring(8, 9);
    //  console.log("EYESHAPE: " + eyeShape);
      let decoShape = bearDetails.substring(9, 10);
    //  console.log("DECOSHAPE: " + decoShape);
      let decoMidColor = bearDetails.substring(10, 12);
    //  console.log("DECOMIDCOLOR: " + decoMidColor);
      let decoSideColor = bearDetails.substring(12, 14);
    //  console.log("DECOSIDECOLOR: " + decoSideColor);
      let animDna = bearDetails.substring(14, 15);
    //  console.log("ANIMDNA: " + animDna);
      let lastDna = bearDetails.substring(15, 16);
    //  console.log("LASTDNA: " + lastDna);
      
   // console.log("BODYCOLOR: " + colors[earsDna]);

      
     item = `<div class="col-lg-4 catBox m-3 light-b-shadow" id="box` + id + `">
                  <div class="cat" id="cat` + id + `"> 
                      <div id="catEars` + id + `" class="` + (animDna == 4 ? 'cat__ear earScale' : 'cat__ear') + `">
                          <div id="leftEar` + id + `" class="cat__ear--left" style="background:#` + colors[earsDna] + `">
                          <div class="cat__ear--left-inside"></div>
                          </div>
                          <div id="rightEar` + id + `" class="cat__ear--right" style="background:#` + colors[earsDna] + `">
                          <div class="cat__ear--right-inside"></div>
                          </div>
                      </div>
                      <div id="head` + id + `" class="` + (animDna == 1 ? 'cat__head movingHead' : 'cat__head') + `" style="background:#` + colors[bodyDna] + `">
                          <div id="midDot` + id + `" class="` + (animDna == 3 ? 'cat__head-dots midDotSpin' : 'cat__head-dots') + ` " style="background:#` + colors[decoMidColor] + `">
                          <div id="leftDot` + id + `" class="cat__head-dots_first" style="background:#` + colors[decoSideColor] + `">
                      </div>
                      <div id="rightDot` + id + `" class="cat__head-dots_second" style="background:#` + colors[decoSideColor] + `">
                      </div>
                      </div>
                      <div id="catEyes` + id + `" class="` + (animDna == 2 ? 'cat__eye movingEyes' : 'cat__eye') + `" >
                          <div class="cat__eye--left"><span class="pupil-left"></span></div>
                          <div class="cat__eye--right"><span class="pupil-right"></span></div>
                      </div>
                          <div class="cat__nose"></div>
                          <div class="cat__mouth-contour" style="background:#` + colors[mouthDna] + `"></div>
                          <div class="cat__mouth-left"></div>
                          <div class="cat__mouth-right"></div>
                          <div id="whiskLeft` + id + `" class="` + (animDna == 6 ? 'cat__whiskers-left whiskShake' : 'cat__whiskers-left') +`"></div>
                          <div id="whiskRight` + id + `" class="` + (animDna == 6 ? 'cat__whiskers-right whiskShake' : 'cat__whiskers-right') +`"></div>
                      </div>
                      <div class="cat__body">
                          <div class="cat__chest" style="background:#` + colors[bodyDna] + `"></div>
                              <div class="cat__chest_inner" style="background:#` + colors[mouthDna] + `"></div>
                          <div class="cat__paw-left" style="background:#` + colors[earsDna] + `"></div>
                              <div class="cat__paw-left_inner"></div>
                          <div id="pawRIGHT` + id + `" class="` + (animDna == 7 ? 'cat__paw-right sayHello' : 'cat__paw-right') + `" style="background:#` + colors[earsDna] + `"></div>
                              <div id="pawRIGHTINNER` + id + `" class="` + (animDna == 7 ? 'cat__paw-right_inner sayHello' : 'cat__paw-right_inner') + `"></div>
                          <div class="cat__paw-leftLower" style="background:#` + colors[earsDna] + `"></div>
                              <div class="cat__paw-leftLower_inner"></div> 
                          <div class="cat__paw-rightLower" style="background:#` + colors[earsDna] + `"></div>
                              <div class="cat__paw-rightLower_inner"></div>
                          <div id="tail` + id + `" class="` + (animDna == 5 ? 'cat__tail tailWig' : 'cat__tail') + `" style="background:#` + colors[mouthDna] + `"></div>
                          </div>
                          </div> 
                          <br>

                          <div class="dnaDiv" id="catDNA` + id + `">
                          <b>DNA:<!-- Colors -->
                          <span id="dnabody` + id + `">` + bearDetails + `</span>
                          <span id="dnamouth` + id + `"></span>
                          <span id="dnaeyes` + id + `"></span>
                          <span id="dnaears` + id + `"></span>
                          
                          <!-- Cattributes -->
                          <span id="dnashape` + id + `"></span>
                          <span id="dnadecoration` + id + `"></span>
                          <span id="dnadecorationMid` + id + `"></span>
                          <span id="dnadecorationSides` + id + `"></span>
                          <span id="dnaanimation` + id + `"></span>
                          <span id="dnaspecial` + id + `"></span>
                          </b>
                          <br>
                          <button class="btn btn-dark" id="sell ` + id +`" onclick="removeFromSale(`+ id +`)">Remove Bear from Sale</button>
                          <button class="btn btn-dark" id="sell ` + id +`" onclick="buyBear(`+ id +`)">Buy This Bear</button>
                          </div>
                          
                          </div>`;
                          

                          items.push(item);
  }
  $(".orderbook").append(items.join());

  }

async function buyBear(id){
  let user = web3.currentProvider.selectedAddress;
  let isApproved = await token.methods.isApprovedForAll(user, mktPlaceAddress).call();
  let offer = await mktplace.methods.getOffer(id).call();
  let price = offer.price;

  if (isApproved == false){
    alert("The CryptoBears DApp is not approved yet to handle your tokens! Please give approval!");
    await token.methods.setApprovalForAll(mktPlaceAddress, true).send();
    alert("Approval Set!");
    isApproved = await token.methods.isApprovedForAll(user, mktPlaceAddress).call();
    console.log("is now approved: " + isApproved);
    
  }else{
    await mktplace.methods.buyBear(id).send({value: price});
  }

  
}




marketplace.sol
pragma solidity ^0.5.12;


import "./CryptoBears.sol";
import "./IMarketplace.sol";
import "./Ownable.sol";


contract Marketplace is Ownable, IMarketPlace{

    CryptoBears private _cryptoBears;

    struct Offer {
        address payable seller;
        uint256 price;
        uint256 index;
        uint256 tokenId;
        bool active;
    }

    Offer[] offers;

    event MarketTransaction(string TxType, address owner, uint256 tokenId);

    mapping (uint256 => Offer) tokenIdToOffer;

    constructor (address _bearsContractAddress) public {
        setBearContract(_bearsContractAddress);
    }

    //Sets address for tokenContract and initializes it
    function  setBearContract(address _bearsContractAddress) public OnlyOwner {
       _cryptoBears = CryptoBears(_bearsContractAddress);
    }

    function getOffer(uint256 _tokenId) 
        public 
        view 
        returns
    (
        address seller, 
        uint256 price,
        uint256 index,
        uint256 tokenId,
        bool active
    ) {
        Offer storage offer = tokenIdToOffer[_tokenId];

        return (
            offer.seller, 
            offer.price, 
            offer.index, 
            offer.tokenId, 
            offer.active
        );
    }

    function getAllTokenOnSale() public returns(uint256[] memory listOfOffers){

        uint256 totalOffers = offers.length; //Gets total number of offers

        if(offers.length == 0){
            return new uint256[](0);
        } else {

            uint256[] memory result = new uint256[](totalOffers);

            uint256 offerId;

            for (offerId = 0; offerId < totalOffers; offerId++){
                if(offers[offerId].active){
                    result[offerId] = offers[offerId].tokenId;
                }
            } 
            return result;
        }
    } //End of getAllTokenOnSale()

    function _ownsBears(address _address, uint256 _tokenId) 
        internal
        view
        returns ( bool )
    {
            return (_cryptoBears.ownerOf(_tokenId) == _address);
    }

    function setOffer(uint256 _price, uint256 _tokenId) public {
        require(
            _ownsBears(msg.sender, _tokenId), 
            "You must own the bear you want to sell!"
        );
        require(tokenIdToOffer[_tokenId].active == false, "You cannot sell the same bear twice!");
        require(_cryptoBears.isApprovedForAll(msg.sender, address(this)), "The Token Contract does not have permission to transfer your tokens!");

        Offer memory _offer = Offer({
            seller: msg.sender,
            price: _price,
            index: offers.length,
            tokenId: _tokenId,
            active: true
        });

        tokenIdToOffer[_tokenId] = _offer;
        offers.push(_offer);

        emit MarketTransaction("Create Offer", msg.sender, _tokenId);
    }

    function removeOffer(uint256 _tokenId) public {
        Offer memory offer = tokenIdToOffer[_tokenId];
        require(
            offer.seller == msg.sender, "Only seller can remove Bear from sale!"
        );
       

        delete offer;
        offers[offer.index].active = false;

        emit MarketTransaction("Remove Offer", msg.sender, _tokenId);
    }

    function buyBear(uint256 _tokenId) public payable {
        Offer memory offer = tokenIdToOffer[_tokenId];
        require(msg.value == offer.price, "Incorrect price!");
        require(tokenIdToOffer[_tokenId].active == true, "Bear is not on sale currently!");

        delete offer;
        offers[offer.index].active == false;

        if (offer.price > 0){
            offer.seller.transfer(offer.price);
        }

        _cryptoBears.transferFrom(offer.seller, msg.sender, _tokenId);

        emit MarketTransaction("Bear Sale", msg.sender, _tokenId);
    }


    
    
}

Obviously, I did not look at @filipā€™s code yet, because I want to solve it on my own first, But any hint is highly appreciated!

Let me know if you need to know more.

1 Like

Hey there! Approvals should be manage by the marketplace contract. So you approve the marketplace to take decitions over your NFT. The only approval user needs is for selling a cat. And you approve the contract to transfer it. Some times could get time to solve this puzzle, so I encorage you to watch the videos. :slight_smile:

2 Likes

Hy @kenn.eth, I did approve the contract. it will still throw the error. I will keep trying out stuff and look into filips solution tomorrow.

2 Likes

Great! You can also send me some pictures of errors and msgs from terminal and console.

1 Like

Hey @kenn.eth, I finally found some time to come back to the cryptoKitties project. I am sure now that I did the approvalForAll correctly, as it matches exactly with @filipā€™s code in the video, and it the whole process works on the frontend and returns the correct value from the contract => See the gif below. Still, I get the error:

"reason":"Only Owner, Operator or Approved Addresses can transfer!"

I opened a repo on GitHub, if you could have a look into it, I would be tremendously happy and thankful :pray: as this would definitely be a great milestone in building my portfolio!
ApprovalFailureCryptoBears

Hey, i just run your project and not rendering marketplace or other page than the factory. Please checkout that the code is running ok and I can try again. In another hand, I just notice that you are using same address to buy the bear. So, as long as i know, you cannot buy your own bear.
Hope this help.
Let me know.

1 Like

Hmā€¦ strange, here everything renders fine. I have no idea why it is not rendering on your side. Will have another look as soon as possible, thank you for taking time!

Ok so I put together a very simple interface since Iā€™m time constrained and want to focus more on UI in the React course.

image

My code:

marketplace.js :

var web3 = new Web3(Web3.givenProvider);

var tokenInst;
var marketInst;
var user;
var userTokenIds;
var marketOffers;
var tokenContractAddress="0xBF759b87A56FAebBC040b19bF2200702aFc9D463";
var marketContractAddress="0x7087A2e4f76D70d40D4c30A9b8908aD8Dd8fc5c8";
var marketPlaceApproved;

$(document).ready( function() {
    window.ethereum.enable().then(async function(accounts){
        tokenInst = new web3.eth.Contract(abi, tokenContractAddress, { from: accounts[0]})
        marketInst = new web3.eth.Contract(marketabi, marketContractAddress, { from: accounts[0]})
        user = accounts[0];

        console.log(tokenInst);
        console.log(marketInst);

        // check approval
        marketPlaceApproved = await tokenInst.methods.isApprovedForAll(user, marketContractAddress).call();
        console.log(marketPlaceApproved);
        if (marketPlaceApproved == false){
            $("#main-container").html(genAuthHtml());
            //initiateApproval();
            
        } else {

        showRemoveApprovalButton();
        
        // show tokens of this user
        userTokenIds = await tokenInst.methods.getAllCatsFor(user).call();
        await showUserTokens();

        // show tokens for sale
        marketOffers = await marketInst.methods.getAllTokenOnSale().call();
        await showMarketOffers();

        }



        // Event processing

        tokenInst.events.ApprovalForAll()
            .on("connected", subscriptionId => {
                console.log("ApproveForAll subsciption connected with id: " + subscriptionId);
            })
            .on("data", event => {
                console.log("ApproveForAll data received: ");
                console.log(event.returnValues.owner);
                console.log(user);
                if ((event.returnValues.owner.toUpperCase() === user.toUpperCase())
                        && (event.returnValues.operator.toUpperCase() === marketContractAddress.toUpperCase())){
                    console.log("ApproveForAll set to: "+ event.returnValues.approved);
                    window.location.href = window.location.href;

                }
            })
            .on('error', function(error, receipt) { // If the transaction was rejected by the network with a receipt, the second parameter will be the receipt.
                console.log(error);
            });

        marketInst.events.MarketTransaction()
            .on('data', (event) => {
                console.log(event);
                var eventName = event.returnValues["TxType"].toString();
                var tokenId = event.returnValues["tokenId"];

                // check if the event is connected to current user

                if (eventName == "Create offer") {
                    alert('Token put for sale with id: ' + tokenId);
                    window.location.href = window.location.href;
                }

                if (eventName == "Remove offer") {
                    alert('Removed offer for token with id: ' + tokenId);
                    window.location.href = window.location.href;
                }

                if (eventName == "Buy") {
                alert('Token purchased with TokenId: ' + tokenId)
                window.location.href = window.location.href;
                }

            })

        
    });
});


function genAuthHtml(){
    var htmlString;
    htmlString = "<div class=\"row cattributes m-2 light-b-shadow\" >"
        + "<h3>Authorize</h3>"
        + "<p>In order to make use of the marketplace you need to authorize the marketplace to manage your tokens first.<p>"
        + "<div class=\"row\ cattributes m-2\"><div class=\"d-flex justify-content-center\">"
        + "<button onclick=\"initiateApproval()\" class=\"btn btn-success\" id=\"authorize-buton\">Authorize</button></div></div>"
        + "</div"
    return htmlString;
}

async function showUserTokens(){

    console.log(userTokenIds);
    userTokenIds.forEach( async (e, index) =>  {
        var cat = await getTokenInfo(e);
        //console.log(cat);
        //console.log(genTokenHtml(cat,index));
        $("#owned-cats").append(await genTokenHtml(cat,e))
    })

}

async function showMarketOffers(){

    console.log(marketOffers);
    marketOffers.forEach( async (e) =>  {
        var cat = await getTokenInfo(e);
        var offer = await getOfferInfo(e);
        //console.log(cat);
        //console.log(genTokenHtml(cat,index));
        $("#market-cats").append(await genMarketTokenHtml(offer,cat))
    })

}

async function genMarketTokenHtml(tokenOffer,cat){
    var htmlString;
    htmlString = "<div class=\"row cattributes m-2 light-b-shadow\" >"
        + "<p><b>Cat information: ID: " + tokenOffer.tokenId
        + "</b>, genes: " + cat.genes 
        + ", generation " + cat.generation
        + "</p>"
        + "<div class=\"col-md-12 text-center \"><b>Priced at: " + web3.utils.fromWei(tokenOffer.price) + " <b>ETH.</b></div>";
    
    // if cat isn't sold by user display buy button
    // console.log(tokenOffer.seller.toUpperCase());
    // console.log(user.toUpperCase());
    if(tokenOffer.seller.toUpperCase() !== user.toUpperCase()){
        htmlString = htmlString + "<div class=\"col-md-12 text-center \"><button type=\"button\" onclick=\"buyToken("+tokenOffer.tokenId+")\" class=\"btn btn-success\">Buy Kitty</button></div>";
    } else {
        htmlString = htmlString + "<div class=\"col-md-12 text-center \">You own this cat.</div>"
    }

    htmlString = htmlString + "</div>";
    return htmlString;
}

async function genTokenHtml(tokenData, tokenId){
    var htmlString;
    htmlString = "<div class=\"row cattributes m-2 light-b-shadow\" >"
        + "<p><b>Cat information: ID: " + tokenId
        + "</b>, genes: " + tokenData.genes 
        + ", generation " + tokenData.generation
        + "</p>";
    
    // if for sale show cancel offer otherwise show set offer form

    var res = await marketInst.methods.getOffer(tokenId).call()
    .then( result => {
        htmlString = htmlString + "<div class=\"col-md-12 text-center \"><button type=\"button\" onclick=\"cancelOffer("+tokenId+")\" class=\"btn btn-danger\">Remove Offer</button></div>"
            
        console.log(result)

    })
    .catch( revertReason => {
        console.log ({revertReason})
        htmlString = htmlString + "<div class=\"col-md-12 text-center \"><form id=\"sf-"+tokenId+"\">"
            + "<input id=\"sp-"+tokenId+"\" type=\"text\" class=\"form-control\ m-2\" placeholder=\"Price in ETH\">"
            + "<button type=\"button\" onclick=\"putForSale("+tokenId+")\" class=\"btn btn-primary\">Offer for sale</button>"
            + "</form></div>"
    })

    htmlString = htmlString + "</div>";
    return htmlString;
}

async function cancelOffer(tokenId){
    marketInst.methods.removeOffer(tokenId).send({}, function(err, txHash){
        if (err){
            console.log(err)
        } else {
            console.log("setOffer request sent with txHash: " + txHash)
        }
    });
}



async function putForSale(tokenId){
    var ethTokenPrice = $("#sp-"+tokenId).val();
    var weiTokenPrice = web3.utils.toWei(ethTokenPrice);
    //console.log("Put on offer token "+tokenId+" at price of "+ethTokenPrice+" ETH or "+weiTokenPrice+" in wei.");

    marketInst.methods.setOffer(weiTokenPrice, tokenId).send({}, function(err, txHash){
        if (err){
            console.log(err)
        } else {
            console.log("setOffer request sent with txHash: " + txHash)
        }
    });

}

async function buyToken(tokenId){
    var offer = await getOfferInfo(tokenId);
    marketInst.methods.buyKitty(tokenId).send({value: offer.price}, function(err, txHash){
        if (err){
            console.log(err)
        } else {
            console.log("Buy request sent with txHash: " + txHash)
        }
    });

}

async function getTokenInfo(tokenId){
    try{
    result = await tokenInst.methods.getKitty(parseInt(tokenId)).call()
        // console.log("ID: " + tokenId);
        // console.log(result);
        return result;
    } catch(err) {
        console.log(err);
    }
}

async function getOfferInfo(tokenId){
    try{
    result = await marketInst.methods.getOffer(parseInt(tokenId)).call()
        // console.log("ID: " + tokenId);
        // console.log(result);
        return result;
    } catch(err) {
        console.log(err);
    }
}


function initiateApproval(){
    tokenInst.methods.setApprovalForAll(marketContractAddress, true).send({}, function(err, txHash){
        if (err){
            console.log(err)
        } else {
            console.log("ApproveForAll request send with txHash: " + txHash)
        }
    });
}

function removeApproval(){
    tokenInst.methods.setApprovalForAll(marketContractAddress, false).send({}, function(err, txHash){
        if (err){
            console.log(err)
        } else {
            console.log("ApproveForAll request send with txHash: " + txHash)
        }
    });
}

function showRemoveApprovalButton(){
    htmlString = "<div class=\"row\ cattributes m-2\"><div class=\"col-md-12 text-center \">"
    + "<button onclick=\"removeApproval()\" class=\"btn btn-danger\" id=\"authorize-buton\">Remove Authorization</button></div></div>"
    $("#main-container").append(htmlString)
}

marketplace.html :

<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Academy kitties - breeding</title>
        
        <!-- <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
        


        <link rel="stylesheet" href="assets/css/mystyle.css">
        <link rel="stylesheet" href="assets/css/animations.css">
        <link rel="stylesheet" href="assets/css/cats.css">
        <link rel="stylesheet" href="assets/css/colors.css">
        <link rel="stylesheet" href="assets/css/factory.css">
        <link rel="stylesheet" href="assets/css/frontend.css">
    </head>

    <body>
        

        <div id="main-container" class="container p-5" style="margin-top: 12vh;margin-bottom: 10vh;">
            
        
            <div align="center">
                <h1 class="c-white">Kitties Marketplace</h1>
                <p class="c-white">shop till you drop</p>
            </div>
            
            <br>
            <div class="row">
                <div align="center" style="margin-top: 4vh;margin-bottom: 1vh;">
                    <h2 class="c-white">My Kitties</h2>
                </div>
            </div>
            <div class="row">
                <div id="owned-cats" class="col-lg-12 cattributes m-2 light-b-shadow">
                    <!-- Owned cats List get's inserted here -->
                </div>
            </div>

            <div class="row">
                <div align="center" style="margin-top: 4vh;margin-bottom: 1vh;">
                    <h2 class="c-white">Kitties for sale</h2>
                </div>
            </div>
            <div class="row">
                <div id="market-cats" class="col-lg-12 cattributes m-2 light-b-shadow">
                    <!-- Offered cats List get's inserted here -->
                </div>
            </div>
        </div>


        <footer align="left">
            <p>Ivan on Tech Academy - Asignment</p>
        </footer>


        
        <!-- <script type="text/javascript" src="assets/js/jquery-3.4.1.js"></script>
        <script src="assets/bootstrap/js/popper.js"></script>
        <script src="assets/bootstrap/js/bootstrap.min.js"></script> -->

        <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>

        <script src="assets/js/web3.min.js"></script>
        <script src="assets/js/colors.js"></script>
        <script src="assets/js/catSettings.js"></script>
        <script src="assets/js/catFactory.js"></script>
        <script src="./abi.js"></script>
        <script src="./marketabi.js"></script>
        <script src="./marketplace.js"></script>

    </body>
</html>
2 Likes

Done!!!

Part1 video (start at Home page):
part1

Part2 video (start when Catalogue is clicked):
part2

Most of the marketplace frontend is reused (using same file) from the My Doraemon page. Just added price and purchase/cancel button.
Still, this assignment took me about 2 days because I made a huge mistake in the _transfer function.
If you see revert error, better check out the transfer function or function that involves array.

One annoying issue is that when there are many Doraemon in the marketplace, it takes a lot of time to load the page. Sometimes about 30 seconds. It could be my code or maybe Ganache.


Full code:
https://github.com/REGO350/nftgame
(Doraemon looks better on FireFox. Chrome doesnā€™t allow decimals in CSSā€¦)


This was one of the hardest course. Especially, when thereā€™s no video about the My Cats page! But I learned a lot! Thank you very much!

5 Likes

My final code.
This course was really hard and I needed to use google all the time, a lot of test(really a lot), bugs and more bugs, reuse functions(its really necessary ) , create things that I had no idea how to do it before.
so much topics but its possible to do all of this css, html, solidity, web3, javascript(oh my gosh asynchronous programming ) Jquery
Later I will change the code to use Safe math :innocent:
Later I will use all of this but with React :grinning:
Later I will deploy all of this on Kovan or Ropsten but before I will try with simplest contracts :sweat_smile:

GITHUB
https://github.com/camarosan/Kitties_Project-solidity-

video with the functionalities :smiley: all is working
https://odysee.com/@camarosan:1/Output.1-2:d

Market Page
image

Market functions

 
function catOffer(id) {
    var offer = String(document.getElementById(`offerInput${id}`).value);
    offer = web3.utils.toWei(offer);
    console.log(offer); 
    contractInstance2.methods.setOffer(offer,id).send((error,txHash)=>{
        if(error){
            console.log(error)
            alert("You cannot offer you are not the cat owner or there is an offer now")
        }
        else {
            console.log(txHash)   
        }    
    });   
}

function buyCat(catId) {
    contractInstance2.methods.getOffer(catId).call().then((res)=>{
        console.log(res.price);
        var configETH = {value: web3.utils.toWei(res.price, "wei")}
        contractInstance2.methods.buyKitty(catId).send(configETH);            
    })
}

function priceCat(catId){
    contractInstance2.methods.getOffer(catId).call().then((res)=>{
        console.log(typeof(res.price));
        var valueETH= res.price.slice(0,-18);
        $(`#idPrice${catId}`).html(valueETH+" ether");
    })
}

function removeOffer(catId) {
    contractInstance2.methods.removeOffer(catId).send(function(error, txHash) {
        if(error){
            console.log(error)
            alert("Error to remove you are not the owner")
        }
        else {
            console.log(txHash)   
        }    
    }); 

}

Lets go for the last one course :smiley: for me

3 Likes

Hey guys, Iā€™m getting this error:

Screenshot 2021-06-24 at 15.08.46

My abi.js is up to date, my contract addressā€™ for both token and marketplace is correct. Ganache is up and running. Not a lot of helpful advice online. Any ideas??

My repo: https://github.com/olfrank/Crypto_Cats

Hey @ol_frank, hope you are ok.

I think you should check the contract address from your dapp to match the same from your truffle migration console.

image

Carlos Z

1 Like

Hey @thecil, the contract address is correct, it is correct every time i try. I have no idea where the error is coming from.

Below are the full error messages that are thrown when I press on the create cat button. I hope this helps. Iā€™m surprised that I am the only one who has come across this problem.

To maybe make it clearer what i am doing, iā€™ll walkthrough the process.

  1. I run ā€˜truffle developā€™ in the console
  2. I then run ā€˜truffle migrateā€™ in the console
  3. Then I copy the contract address for both my Cat contract and Marketplace contract and paste it into my global scope variables at the top of my index.js file as you can see in the screenshot below.
  4. I then save the index.js file and run (in a new terminal window) python -m SimpleHTTPServer, and navigate to the local host 8000 in my Brave browser.
  5. Finally i try and interact and the errors occur.

My repo: https://github.com/olfrank/Crypto_Cats

Screenshot 2021-06-28 at 12.44.34

Screenshot 2021-06-28 at 12.34.29

Screenshot 2021-06-28 at 12.32.22

I have face a similar issue with my dapp, you probably have the same issue, which is from your metamask account settings for the dapp.

Try go into your metamask settings , advanced options and ā€œreset accountā€, this will only delete any data related to dapps in your metamask (refresing the TxGasUtil and the other error, hopefully).

image

Let me know how it goes for you. :nerd_face:

Carlos Z

1 Like

Ahhh okay yeh that sounds like the problem. I will try that and let you know mate. Thank you @thecil

Ive got bad news, resetting my metamask account didnā€™t work either. Bummer.

Below i have attached two screenshots, the first is from my dapp and the second is from Filipā€™s dapp.
When looking at the breeding frontend lecture where Filip creates a new cat like i am trying to do his MetaMask window is different to mine.

Looking at my screenshot, i have to manually enter the gas fee whereas his is automatic. I enter in the required gas limit every time my metamask window pops up. I usually enter around 40000 just to be safe.

You probably already have, but if not, can you please look at my contracts to see if everything is in order. Ive gone over it myself but would appreciate a second opinion.

Thank you for your patients by the way. Technical problems can be the most frustrating haha. :slight_smile:

Screenshot 2021-06-29 at 10.40.18

Screenshot 2021-06-29 at 10.44.46

That happens when there is an issue with ganache or any local network blockchain, it might be that you are not running the local blockchain properly, or not connecting properly, any case you should restart your ganache.

Also ganache GUI goes into a lot of issues time to time. That way I can advice you to use truffle develop instead, it will create a local blockchain network and should work even better than ganache GUI.

Truffle Develop started at http://127.0.0.1:9545/

Accounts:
(0) 0xb645614390cb0d8c6a9f1afdaf0c1b1d1bf75bd4
(1) 0xedc025c70a1659d6064541de1cb930d6bf5e222d
(2) 0x06a9b78f69ceb2b5c05c6283e5823a66fce85b2e
(3) 0x8c7ad29fd13ceaf452de8fa1b47a157e396af5b7
(4) 0xac9f9b8e3509b0f3b883ead30b49a6aa5c519aab
(5) 0x7de9053fc00b7547ceb5771083583f8601ce7528
(6) 0x3a84f62bb6b1ef899b0d6af2c8d84f6a3788dd0f
(7) 0x7154b1f4c9ffde0fd4f086ca19861d7a34941ead
(8) 0xa821fdfa94a2f523d5923826e3885e6f3e55f855
(9) 0x021481870d72a309b16b7a20803f2af911443d5a

Private Keys:
(0) 4db68575dffb8c88a3020c23bcdebc76963b9a2d2655502f42cc5764a2dba486
(1) aef748981d8b474e61523d2b2b605944ec8321f7417dacff1e28fd27c12b60a7
(2) 2cd3bd130de70d8bf55d04b36a998d14468a7d7f7b5e070358a62b3a330926c8
(3) f55141ee20881f4f565c093c8657b44667001a7955c1b9f0d0b3414b563ede68
(4) 35bc1148a2d01f73361a4f46541df93554fbc7e8d613ee8703496470f3106334
(5) 57f0145746df70b7fb7b10d08db2a26d0e33a44c9060abb4d00929d611334fe4
(6) 34335a27dbcb9116344a60442221c98f3179ae70dd982ef671393f6564107747
(7) 4e17fcb2aaf21bea4ea3e291259efef87803a46427503523275268171f167135
(8) 08fc5af18559843133df98196cfa98c26bcf9f09d070ee7498104597b2dfcd67
(9) 139d826508ea18903893ceb5ef8898951a31e7e103867ff9d0f220325a7541dd

Mnemonic: silver call robot fog oval spatial drama own toilet bullet shock second

āš ļø  Important āš ļø  : This mnemonic was created for you by Truffle. It is not secure.
Ensure you do not use it on production blockchains, or else you risk losing funds.

check at the start of the console. it shows the IP and port to connect with metamask to this network, then you should import one of the keys provided to your metamask and it should work in the same way that you use ganache.

Carlos Z

1 Like

@thecil Been working on my website for a while. Finished now! Will try your solution now, it sounds like that could well be the problem

1 Like

Hey @thecil , I tried running the local blockchain through truffle develop like you suggested and a different error appeared this time. Iā€™m starting to thing there is something wrong with my code, however after going through it with a fine tooth comb, i canā€™t seem to find the issue. This is a nightmare haha.

The error codes are below:
When i click create new cat button, this alert pops up:
Screenshot 2021-07-27 at 09.49.02

And then when i click ā€œConfirmā€ the below error messages pop up:
Screenshot 2021-07-27 at 09.49.22

My Repo: https://github.com/olfrank/Crypto_Cats