Assignment - Marketplace Frontend

In this case you should provide a number argument like 12345 instead uint256(0) and
change address(0) for msg.sender

Make this changes and let me know if its work or send me the error is throwing.

@kenn.eth
Thanks kenn, I made those changes you suggested. It helped to get rid of the default cat which was showing up.
The problem of the cats not appearing to correct owners was in the _transfer function…

function _transfer(address from, address to, uint256 tokenId) internal {

    ownershipTokenCount[to] ++; 

    kittyIndexToOwner[tokenId] = to;

     //ownerToCats[to].push(tokenId); // this was sending the cats to another array

     if (from != address(0)) { // if from is not address(0), is okay to remove token
       ownershipTokenCount[from] --;

      //delete kittyIndexToOwner[tokenId]; Just in case I used the function below which was in the final code
      delete kittyIndexToApproved[tokenId];

     }

     emit Transfer(from, to, tokenId);
  }

So it all works! I just need to personalize it a bit, thanks!

1 Like

Hello all of you.
I really want to thank every person who help me through the process to learn about blockchain.
few moths ago after finished my project I proposed to myself to create this application on React and Ropsten network.
I think is so much easier with React :), I don’t suffer anymore with async programming :slight_smile:
This is my final project, it was hosted on github pages :slight_smile:

https://camarosan.github.io/kitties_react

on github I changed my repository, its better begin with the page and then the contracts
https://github.com/camarosan/kitties_react

contracts on Ropsten network both are verified :slight_smile:
https://ropsten.etherscan.io/address/0x21666Dcb915Db19A8FFe30B9bF0F2C2914E781cD#code

https://ropsten.etherscan.io/address/0xEB4fD5C653dB98DF4830065F7b61557994a5EF47#code

image
image
you can buy my kitty 0 with your wallet :smile:
image

I’d really want everyone who read this to try to learn React after this course, it really helps a lot

you can hack me I have some bugs :sweat:. and made the page only with one component :cold_sweat:
Thanks Thanks Thanks this was really a challenge but anyone can learn dedicating time every day. I didn’t know how to code 1 year ago.

6 Likes

Thanks for sharing. I had skipped react and proceeded to do the Crypto kitties dApp course and had a difficult time with the front end programming. I saw your post about learning React and decided to take the course. I’m doing it now. It does look like a better way to do front end, thanks for the inspiration!

2 Likes

Hey guys,

having a small issue where I am using web3 to detect a metamask account switch. When I detect a change I look up the contracts again from the perspective of the newly selected account.

I also reset the subscriptions and hook them up again to the new user. I also filter the events for only the latest block.

After I put a kitty for sale and buy with a different account and switch back and forth between users a few times I notice that events are firing multiple times.

So it looks like the contract instance is ‘remembering’ previous subscriptions or something. Anyone see whats wrong… it’s driving me nuts

var kittyContractAddress = "0xa68B2805a1eE5cf4C41297555ccfFD6B7D1cF888";
var marketplaceContractAddress = "0xCFd66461338e38a1016220eB9Baf4E0F0FC4F4D9";

var web3 = new Web3(Web3.givenProvider);

var kittyContractInstance;
var marketplaceContractInstance;

var birthSubscribe
var marketEventSubscribe

var user;

var account;

var ownedKitties;
var kittiesForSale;

$(document).ready(async function () {
  console.log("loading web3");
  
  async function initAccountAndContracts(userAccount){
    
    //birthSubscribe = undefined
    //marketEventSubscribe = undefined

    (birthSubscribe) ? birthSubscribe.unsubscribe() : ()=>{}
    (marketEventSubscribe) ? marketEventSubscribe.unsubscribe() : ()=>{}
    account = userAccount;
    user = userAccount

    kittyContractInstance = new web3.eth.Contract(
      abi.kittyContract,
      kittyContractAddress,
      { from: account }
    );
  

    marketplaceContractInstance = new web3.eth.Contract(
      abi.marketplaceContract,
      marketplaceContractAddress,
      { from: account }
    ); 

    await getAllKittiesOwned();

    birthSubscribe = kittyContractInstance.events
      .Birth({fromBlock: 'latest'})
      .on("data", async function (event) {
        let owner = event.returnValues.owner;
        let kittenId = event.returnValues.kittenId;
        let mumId = event.returnValues.mumId;
        let dadId = event.returnValues.dadId;
        let genes = event.returnValues.genes;

        $("#kittyCreation").css("display", "block");
        $("#kittyCreation").text(
          "owner:" +
            owner +
            " kittenId:" +
            kittenId +
            " mumId:" +
            mumId +
            " dadId:" +
            dadId +
            " genes:" +
            genes
        );

        alert(
          "Kitty successfully birthed on the blockchain and is now in your catalogue, Token id is " +
            kittenId
        );
        await getAllKittiesOwned();
        loadPage("./catalogue.html");
      })
      .on("error", () => console.error("Error receiving Birth event"));

    marketEventSubscribe = marketplaceContractInstance.events
      .MarketTransaction({fromBlock: 'latest'})
      .on("data", function (event) {
        var eventType = event.returnValues["TxType"].toString();
        var tokenId = event.returnValues["tokenId"];

        if (eventType == "Buy") {
          //successful purchase
          alert(
            "Kitty purchase complete, Kitty can now be found in your catalogue"
          );
          loadPage("./catalogue.html");
        } else if (eventType == "Create Offer") {
          // successfully put up for sale
          //this is really where the UI should be changed to Cancel Sale
          //debugger
          alert("Kitty successfully offered up for sale");
          loadPage("./catalogue.html");
        } else if (eventType == "Remove Offer") {
          //Successfully cancelled the sale
          alert("Kitty sale canceled");
          loadPage("./catalogue.html");
        }
      })
      .on("error", () => console.error("Error receiving MarketTransaction event"));
  };

  window.ethereum.on('accountsChanged', async function (accounts) {
    alert(`account changed to ${accounts[0]}`)
    await initAccountAndContracts(accounts[0])
    loadPage("./catalogue.html")
  })



  window.ethereum.enable().then(async function (accounts) {
    console.log("initing contract for ", accounts[0])
    await initAccountAndContracts(accounts[0])
  })

    
});

@Emmett the multiple events comes from your initAccountAndContracts(userAccount). Switch accounts looks like working. You can separe this events from the switch account function by just declaring again the account you are using without events. Another alternative is to use a conditional to know if the event is already declare. Something like EventRegistered = true or false , so you default it to false and once initiated you set it to true and when switch account just check for this value.

What a journey!
I will say it again, half the course is missing and unless you’re already an advanced dev, there is no way to end this course without getting any hints from what others have done. This should really be checked because it is a very interesting project overall, and definitely inline with the NFT hype! My guess is that the whole web3 missing part might be replaced with the Moralis platform at some point… but in the meantime, it’s quite a struggle!!!
Anyway here is my GitHub repo and a live link. There are still some bugs that I’ll try to fix, but it’s mostly working overall! :sweat_smile:

Feel free to play around and to report any bugs either here or on GitHub.


To anyone struggling, I would suggest editing your contract to use the ERC721.sol and/or ERC721Enumerable.sol from OpenZeppelin library in order to work with something solid and without bugs. It will definitely simplify the frond-end work.
Something else which isn’t explained is how to keep your contract instance (or any other var) when refreshing and switching pages. I managed to use tabs instead to make it work. As mentioned previously, react would definitely be the best solution.

3 Likes

Likely, I need more time/days to make it look more beautiful for the users. However, focusing on the functionality and getting it to work as a start. Really great to see things in action as a whole with everything from the frontend to the backend (blockchain).

Creating a minimalist UI to concentrate on the functionality:
Relevant code in index.html

...
<div class="row">
            <br>
            <div align="left">
                <h3 class="c-white">Kitty Home</h1>
                <p class="c-white">Kitties Living With You</p>
            </div>
            <!-- Kitty Home Buttons -->
            <div class="input-group mb-3">
                    <span class="input-group-text">Kitties You Own</span><span class="c-white" id="availablekitty"></span>
            </div>
            <div class="input-group mb-3">
                <button class="btn btn-light" onclick="getKittyList()">Refresh</button>
            </div>
        </div>
        <br>
        <div class="row">
            <br>
            <div align="left">
                <h3 class="c-white">MarketPlace</h1>
                <p class="c-white">Buy and Sell Kitties</p>
            </div>
            <!-- MarketPlace Buttons -->
            <div class="input-group mb-3">
                <span class="input-group-text">Kitties In The Marketplace</span><span class="c-white" id="marketKitties"></span>
            </div>
            <div class="input-group mb-3">
                <button class="btn btn-light" onclick="getMarketList()">View List</button>
            </div>
            <div class="input-group mb-3">
                <div class="input-group-prepend">
                    <span class="input-group-text">Input Kitty ID</span>
                    <input type="text" class="form-control" id="marketKittyId">
                </div>
            </div>
            <div class="input-group mb-3">
                <div class="input-group-prepend">
                    <span class="input-group-text">Input Price</span>
                    <input type="text" class="form-control" id="marketPriceId">
                </div>
            </div>
            <div class="input-group mb-3">
                <button class="btn btn-light" onclick="approve()">Approve Marketplace</button>
                <button class="btn btn-light" onclick="buyKitty()">Buy Kitty</button>
                <button class="btn btn-light" onclick="sellKitty()">Sell Kitty</button>
            </div>
        </div>
...

And, for the index.js:

...
$(document).ready( async () => {
    if(window.ethereum) {
        ethereum.request({ method: 'eth_requestAccounts' }).then( (accounts) => {
            user = accounts[0];
            contractInstance = new web3.eth.Contract(kittyabi, contractAddress, {from: user});
            marketInstance = new web3.eth.Contract(marketabi, marketAddress, {from: user});
            eventListeners(contractInstance, marketInstance);
        })
    }
})
...
function eventListeners(kittyContract, marketContract) {

    kittyContract.events.Birth({}, (error, event) => {
        if (error)
            console.log(error);
        else {
            alert("Create Kitty transaction was successful. You now own a Kitty :)");
            console.log(event.returnValues);
        }
    });

    kittyContract.events.Breed({}, (error, event) => {
        if (error)
            console.log(error);
        else {
            alert("Breed transaction was successful. You now have a newly bred Kitty :)");
            console.log(event.returnValues);
        }
    });

    kittyContract.events.ApprovalForAll({}, (error, event) => {
        if (error)
            console.log(error);
        else {
            alert("Approval for the Marketplace was successful.");
            console.log(event.returnValues);
        }
    });

    marketContract.events.MarketTransaction({}, (error, event) => {
        if (error)
            console.log(error);
        else {
            alert("Kitty offer was listed successfully.");
            console.log(event.returnValues);
        }        
    });
}
...
async function getKittyList() {

    await contractInstance.methods.tokenOwnedOf(user).call( {}, (error, txHash) => {
        if(error)
            console.log(error);
        else {
            console.log(txHash);
            alert("Getting kitties you own. Give it a few moments to process.");
        }
    }).then( (kittyList) => {
        let result = "Kitty ID: ";
        kittyList.forEach(element => {
                result = result + element + "  ";
        });
        $('#availablekitty').html(result);
    });
}

async function getMarketList() {

    await marketInstance.methods.getAllTokenOnSale().call( {}, (error, txHash) => {
        if(error)
            console.log(error);
        else {
            console.log(txHash);
            alert("Getting offers from the marketplace. Give it a few moments to process.");
        }
    }).then( (marketList) => {
        let result = "Kitty ID: ";
        marketList.forEach(element => {
            if (element != 0) {
            result = result + element + "  ";
            }
        });
        $('#marketKitties').html(result);
    });    
}

async function buyKitty() {
    let tokenId = parseInt( $('#marketKittyId').val() );

    if (tokenId > -1) {
        await marketInstance.methods.getOffer(tokenId).call( {}, (error, txHash) => {
            if(error)
                console.log(error);
            else {
                console.log(txHash);
                alert("Checking the price of the token. Please wait for a few moments.");
            }
        }).then( async (kitty) => {
            await marketInstance.methods.buyKitty(tokenId).send( {value: kitty.price}, (error, txHash) => {
                if(error)
                    console.log(error);
                else {
                    console.log(txHash);
                    alert("Your buy request is being processed. Please wait for a few moments.");
                }
            }); 
        });   
    } 
    else {
        alert("Invalid ID")
    }
}

async function sellKitty() {
    let tokenId = parseInt( $('#marketKittyId').val() );
    let marketPrice = parseInt( $('#marketPriceId').val() );

    if (tokenId > -1 && marketPrice > -1) {
        await marketInstance.methods.setOffer(marketPrice, tokenId).send( {}, (error, txHash) => {
            if(error)
                console.log(error);
            else {
                console.log(txHash);
                alert("Your offer is being listed at the marketplace. Please wait for a few moments.");
            }
        });  
    } 
    else {
        alert("Invalid ID or Price")
    }
}

async function approve() {
    await contractInstance.methods.setApprovalForAll(marketAddress, true).send( {}, (error, txHash) => {
        if(error)
            console.log(error);
        else {
            console.log(txHash);
            alert("Transaction was sent to give the marketplace approval of your tokens.");
        }
    });
}

Thank you for such a challenging and great advance course :slight_smile:
With kind regards

2 Likes

Such a (super) challenging and fun course. I’ve never done any programming before and I think my front end is still pretty ugly and slow, but I learned a lot doing it. I put it up online so people could play with it. The contract is deployed on Ropsten test network, so go to a faucet and grab yourself some rETH and check it out:
https://thepowerofmeow.site/

I made the create gen 0 function available to anyone just so the first few people to check it out could play around with it, so please don’t go too crazy with it and burn up the total supply of gen 0’s.

Code is here:
https://github.com/AttissNgo/AttissNgo.github.io

3 Likes

I felt the same way. Nice job by the way. I finished this project in August and felt it was incomplete and tried to kind of complain about it but I was just told that it was meant to be so. I agree with you. Anyways, I went back to study React and thought I would do a redo in React but I spent another 3 months in React and built a nice project. I will post my Crypto Kitties project soon. I opened it, used it and it still works but I got help from Mauro as he sent me the code but that was still hard…lol – it is a great project though.

2 Likes

@Pedrojok01 Hey Pedro, I got my site on Netlify and contracts deployed but I am not able to mint yet for some reason. Is there some other step you might have taken or trouble you might have had minting your NFTs?

@Attiss nice work as well - how did host your app?
…actually - I just managed to host my app on Netlify. I was not including the client folder in the deployment stages but my app is not minting. Did you have any issues trying to mint your cats on Ropsten?

@thecil @kenn.eth @REGO350
Hello Devs, happy new year. So I finished my React project after 3 months and now I am back to finish my CryptoKiitties project.
I have deployed to Ropsten and I have my app running on Netlify but the minting is not working like it was on Ganache. Here are some errors. I am so close wrapping this one up - thank you in advance


ropstenErrorMsg_Console

///////////////////////// OKAY ---- I GOT IT TO WORK ---- SO IM GOOD///////////////
I was deploying a version from my Github which was not current. I deployed the Master version on Netlify and it works. Tomorrow I will post my screenshots since I the site is not letting me do a new one

1 Like

@bjamRez the website is hosted on a cheap shared server, nothing special. Of course I’m using Moralis for the API etc. I’ve had no issues at all with the tokens on Ropsten, and everyone who has tried it has reported that it’s working. Hopefully you can get yours going - looks like you just had a little deployment conflict.

1 Like

@kenn.eth @thecil @REGO350
Hello Devs, I took 3 months to learn and finish a Trading Crypto app on React and I thought I was going to redo this project but I will try that later in the future. For now, here is my final Robo Kitties project. It’s on the Ropsten test net.

https://youthful-murdock-e07205.netlify.app
https://github.com/brlojam4932/myCryptoKitties_master.git

1 Like

Thanks @Attiss, I was trying to host a branch which was not the latest version. Once I selected the Master branch, it worked. I just posted it the final here. Thank you for quick reply.

hey , will u plzz tell me in which part of the tutorial he tells how to upload nft images just like connecting images to the smart contract …

1 Like

Sorry, I’m not sure what you mean. The ‘images’ are just a bunch of divs. Can you be a little more specific?

1 Like

hey @Sudhanshu_Srivastava ! In this tutorial the approach is different. You sould display the cats you are creating with the function renderCat() or what you use to show it. Since we are creating the visual part of our NFTs using CSS, what we keep in the blockchain (in the smart contract) is the DNA number of each cat. So in this way we will use this DNA number into our renderCat(dna) function.

In the marketplace like the catalogue you should render all the available cats, in this case for sale.
For this you will use your getOffers() function from your contract to return all the cats ids for sale. Calling it with JS to get this array of id’s and then use the getCat(catId) into a for Loop to get all the info of each cat and pass the DNA to renderCat(dna) in each iteration. This render function should add each cat box into a html div using append() or innerHTML +=.

Please let me know if is clear or if you have any other question.
Happy to help.

just like we put images file and metadata json file in ipfs soo we put that json file url in smart contract …so in this tutorial where it is done … and i m newbie i only know this method to show nft collection … i think there other concept was used to show nft

1 Like

okhhhh sir thnkss for exlaining

1 Like