Exercise - Extend your Coin Exchange with more functionality

Noted. Thank you Zsolt!

1 Like

Thanks Zsoft for the course. Iā€™m an amateur programing and I learned a lot because you give me more knowledge and tools to continue working for my goal.
Here is my repository
https://github.com/camarosan/coin-exchange
and the webpage
https://camarosan.github.io/coin-exchange/

I added another statistics and you can update them.
Tested other stylish
There is a input text for a bailout in balance :smile: I used hash function with sha256 only for fun the password is Carlos RodrĆ­guez and the helicopter of my central bank will bring you more balance)

2 Likes

I am currently improving the react app but stumbled upon on a problem.
I have added an icon to the show/hide balance button, when I click the button the icon does not change. I have googled the problem but could not find solution.

export default function AccountBalance(props) {

    const buttonClass = 'btn ' + (props.showBalance ? 'btn-warning' : 'btn-info');
    const buttonText = props.showBalance ? ' Hide Balance' : ' Show Balance';
    const buttonIcon = props.showBalance ? 'fas fa-eye-slash' : 'fas fa-eye';
   
    return (
            <Section>
                <BalanceToggleButton 
                    className={buttonClass}
                    onClick={props.handleBalanceVisibility}>
                    <i className={buttonIcon}></i>
                    {buttonText}
                </BalanceToggleButton>
            </Section>
    );
};
1 Like

here is my gitHub repo:
https://github.com/mjwatson10/coin-exchange

for some reason I am having some issues deploying the folder with npm.


any assistance would be greatly appreciated

1 Like

Iā€™d suggest restarting the whole git-github repository creation in a new folder and copying your source code over. Then check every character, space, comma etc. if they are at their correct places.

https://peacanduck.github.io/coin-exchange/

Added MetaMask so far, going to turn this into a working crypto interface

https://github.com/Uy4n/CoinExchange

:arrow_up: Here is my extended project :arrow_up:

I was held up a few days by a ā€˜high severity vulnerabilityā€™. It turns out it wasnā€™t so severe, itā€™s just that the automatic audit saw something wrong as potentially very harmful. I didnā€™t, and still donā€™t, know enough to know better, so I waited for a fix before continuing.
I knew i wanted to implement a historical price chart, showing the prices of different cryptocurrencies over time. After that I did some styling before carrying on with a few small features. Really the possibilities were endless, so I had to draw the line somewhere otherwise Iā€™d never move onto another project/course!

I could also just copy Uniswapā€™s source code like many other production-ready projects have done :smiley:

Finally I added some hyperlinks. I tried to make ā€œhomeā€ icon clickable to go to home, but it turned out to be a bit fiddly. I could do it, but it would probably require code rewiring/restructuring.

Comparing this work with projects Iā€™ve attempted directly involving the blockchain: working with React, I can experiment and make mistakes without having anything permanent on the blockchain. I know that I can use a testnet blockchain, but it still has that feeling of permanence to me; not allowing me to experiment unless I attach a ā€œself-destruct contractā€ button to every dapp (decentralized app) I develop.

Anyway, Iā€™m much more confident in my ability to code after this. Iā€™d say that was the goal, so goal achieved!

Here are some of the resources I used:

https://api.coinpaprika.com/ (of course)
https://www.youtube.com/watch?v=3m-3qnEXIUk (VERY helpful)

If you want to add even more functionality, check out this resource that I didnā€™t get to:

https://www.freecodecamp.org/news/how-to-build-historical-price-charts-with-d3-js-72214aaf6ba3/

4 Likes

Keep up the good work mate. :clap:

Hi @Malik,

would you help me debug this code? I am trying to loop through a public array and push data to a new array in javascript.

async loop
getCandidates = async () => {
count = 0;
for(count = 0; count < length; count++) {
await  voteApp.methods.candidates(count).call().then(function(res){

        candsNames.push(res.name);
        candsIds.push(count);
        console.log(candsNames[1]);
        console.log(candsIds[0]);
        $("#id_out").text(res.votes);
        $("#name_out").text(web3.utils.toAscii(res.name));

    })
  }
$("#print").text(candsNames + candsIds);
}

:pray:

Hi @Bhujanga,

Can you be more specific on what the issue is. Extra details like what strategies did you try to solve it and whether you tried console logging it to pin point the issue

Thanks

1 Like

Hi @Malik,
yes, I will try my best.
I am working on getting data from a struct array into a new arrays in main.js. I tried different solutions.
so far these two seem the most promising to me: (no errors thrown in console)

latest Try

console log running is printed to console
As soon as i wrap await function call into a loop it will not return callbackā€¦

async function getCands(){
  console.log("running");
//for (let i = 0; i < length; i++)    WRAPPING INTO A FOR LOOP LIKE THIS WILL NOT WORK :(
    await voteApp.methods.candidates(1).call().then(function(res){
    $("#id_out").text(1);
    $("#name_out").text(web3.utils.toAscii(res.name));
    })

  }

async try 1
getCandidates = async () => {   
count = 0;
for(count = 0; count < length; count++) {
await  voteApp.methods.candidates(count).call().then(function(res){  //the call does not seem to succeed

        candsNames.push(res.name);  //should push res.name to candsName array
        candsIds.push(count);              //should push cound to candsIds array
        console.log(candsNames[1]);   //none of the logs are executed.
        console.log(candsIds[0]);
        $("#id_out").text(count);           //no output is created
        $("#name_out").text(web3.utils.toAscii(res.name));

    })
  }
$("#print").text(candsNames + candsIds);
}
async try 2


async function getCandidates() {
const len = length;
  for (let count = 0; count < len; count++) {
    await  voteApp.methods.candidates(count).call().then(function(res){
        candsNames.push(res.name);
        candsIds.push(count);
        console.log(candsNames[1]);
        console.log(candsIds[0]);
        $("#id_out").text(res.votes);
        $("#name_out").text(web3.utils.toAscii(res.name));
        console.log(count);

    })
  }
$("#print").text(candsNames + candsIds);
}

}

Here is my complete main.js so far. I tried to console.log the counter of the for loop before the async function call (await), and at the end, but nothing.
I also did try to use a while loop, but without success so far.

main.js
var web3 = new Web3(Web3.givenProvider);
var voteApp;
var candsNames = [];
var candsIds = [];
var length;


$(document).ready(function() {
    window.ethereum.enable().then(function(accounts){
      voteApp = new web3.eth.Contract(window.abi, "0xd2D19f51E12ab8aEC9D22530Bf4644Bc5C9Da8Af", {from: accounts[0]});
      getLength();
      getCandidates();
    });
    $("#castVote_button").click(castVote);

});

function castVote(){
  let voteID = $("#id_in").val();
  voteApp.methods.vote(voteID).send({value: web3.utils.toWei("1", "ether")}) //send will send data to function createPerson
    .on("transactionHash", function(hash){ //event listener that listens for transactionHash
      console.log("tx hash");
    })
    .on("confirmation", function(confirmationNumber, receipt){ //listener for confirmationNumber only works on real blockchain as local will not confirm for real
        console.log("conf");
    })
    .on("receipt", function(receipt){
      console.log(receipt);
    })
  }

async function getCandidates() {
const len = length;
  for (let count = 0; count < len; count++) {
    await  voteApp.methods.candidates(count).call().then(function(res){
        candsNames.push(res.name);
        candsIds.push(count);
        console.log(candsNames[1]);
        console.log(candsIds[0]);
        $("#id_out").text(res.votes);
        $("#name_out").text(web3.utils.toAscii(res.name));
        console.log(count);

    })
  }
$("#print").text(candsNames + candsIds);
}


function getLength(){
  voteApp.methods.candLength().call().then(function(res){
    length = res;
    console.log(length);
  })




}

Voting.sol
pragma solidity >=0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;

contract Voting{

  struct Voter{
    address voterAddress;
    uint vote; //index of candidate voted for
    bool didVote; //false until voter did vote
    //voter can only vote when weight is >= 1!
    uint weight; //increases when someone delegates his/her vote to this voter or voteMaster approves voter!
    address delegate; //address that gets delegated with the vote
  }

  struct Candidate{
    bytes32 name;  //name of candidate
    uint votes;    //keeps track of votes given to this candidate
  }

  address public voteMaster; //voteMaster is the address approving voterAddress

  mapping (address => Voter) public voters; //connects address to Voter structs

  Candidate[] public candidates; //stores all candidates input by the voteMaster (deployer) in the constructor


  constructor(bytes32[] memory candidateNames) public {
    voteMaster = msg.sender;
    voters[voteMaster].weight = 1;

    for (uint i = 0; i < candidateNames.length; i++){  //pushes all candidate names of candidateName[] (from constructor input) to candidates[] (state array) and sets votes to 0
      candidates.push(Candidate({
        name: candidateNames[i],
        votes: 0
      }));
    }
  }


  //gets length for candidate array
  function candLength() public view returns(uint nrOfCandidates_){
    return nrOfCandidates_ = candidates.length;
  }

  //Voters validation function, only voteMaster(contrat owner) can execute
  function approveVoter(address voter) public {
    require(msg.sender == voteMaster, "Only voteMaster can access this function!");
    require(!voters[voter].didVote, "Voter already voted!");
    require(voters[voter].weight == 0);

    voters[voter].weight = 1;
  }

  function vote(uint candidate) public {
    Voter storage sender = voters[msg.sender]; //creates Voter variable and sets it to voters[msg.sender]
    require(!sender.didVote, "You already did vote!");  //checks if msg.sender did already vote
    require(sender.weight > 0, "You are not approved as voter!");

    sender.didVote = true;
    sender.vote = candidate;

    candidates[candidate].votes += sender.weight;
  }

  function delegateVote(address to) public {
    Voter storage sender = voters[msg.sender]; //creates Voter variable sender for msg.sender to have cleaner code for require statement!
    require(!sender.didVote, "You cannot delegate your vote because you already voted!");
    require(to != msg.sender, "You cannot delegate your vote to yourself!");
    require(sender.weight > 0, "You are not approved as voter and can only delegate once you are!");

    while(voters[to].delegate != address(0)){ //as long as voters to delegate is not equal to zero address, (in case to also delegates his/her vote),

      to = voters[to].delegate;               // set to-address equal to voters to delegate, somehow this logic makes me crazy, haha.
      require(to != msg.sender, "Found loop in delegation!!");
    }

    sender.didVote = true; //set senders vote status to true;
    sender.delegate = to; //set senders delegate to "to" address

    Voter storage delegate_ = voters[to]; //creates Voter variable for delegate to help ourselves in the if statements! makes a cleaner code than to write voters[to] all the time
    if(delegate_.didVote){ //check if delegate already didVote, if yes:
      candidates[delegate_.vote].votes += sender.weight; //Ok this is tricky. If delegate already did vote, then we add the weight of the sender(that delegate his vote to the delegate) instantly to the candidate that the delegate voted for!
    }
    else { //if delegate did not vote yet we:
      delegate_.weight += sender.weight; //add senders weight to delegates weight.
    }
  }

  function electionWinner() public view returns(uint electionWinner_){  //will return the id of the candidate with the most votes!
      uint VotesCount = 0;                            //to store votes
      for (uint p = 0; p < candidates.length; p++) {  //loops through all candidates
          if (candidates[p].votes > VotesCount) {     //and checks if the candidates votes are higher than the votesCount
            VotesCount = candidates[p].votes;         //If so, then sets the VotesCount equal to the candidates votes
            electionWinner_ = p;                      //and returns candidate ID as electionWinner_
          }
      }
  }

  function theWinnerIs() public view returns(bytes32 winnerName_){  //returns name of the winner
      return winnerName_ = candidates[electionWinner()].name;       //sets winnerName_ to result of [electionWinner()].name. nice and nested functionality here, haha.
  }


}

Thanks a lot for looking into this :pray:
I am searching forums since days now and canā€™t make it work :thinking:

Looking at your code, I believe it is an issue regarding multiple promises.

When you want to do a loop for async calls you need to resolve all of them to get the value. Instead of using a then to get the callback, you can assign a value and then use it later .

For example -

async function processArray(array) {
  // map array to promises
  const promises = array.map(delayedLog);
  // wait until all promises are resolved
  await Promise.all(promises);
  console.log('Done!');
}

You can check out references here - https://medium.com/@lavrton/javascript-loops-how-to-handle-async-await-6252dd3c795

Or you could research about loop async await syntaxes and how to use Promise.all for many external API calls.

Unfortunately I cannot write the code for you as this is a very specific case, feel free to experiment with this direction and let me know.

Hope this helps.

1 Like

UPDATE:

@Malik:
Big celebrations over here :partying_face:
I managed!! This is the code to get all entries of a struct array from a smart contract. This is how one can loop an async function:
Note: candsNames is an array declared in the beginning of the contract.

async function getAllCands() {
  const candsLength = await voteApp.methods.candLength().call();  // sets candsLenght by calling getter function to get array length, needs AWAIT! 
  for (let i = 0; i < candsLength; i++){  //loops for length of array
    candsNames.push(                        //pushes result to candsNames
    await voteApp.methods.candidates(i).call().then(result => {  //calls the public candidates struct array with await
        return [result[0]];    //returns index 0 of candidates struct array entry[i]
      })
    )
  }
  console.log(candsNames);
}

Thank you for taking time to look into this. I will keep experimenting!

2 Likes

Hey Guys, iā€™m trying to prevent the app from allowing the user to buy crypto despite not having enough money in the account balance and Iā€™m not sure what the next step is. My code below is in the Coin.jsx file.


import React from 'react';
import PropTypes from "prop-types";
import styled from 'styled-components'

    const Td = styled.td`
      border: 1px solid #cccccc;
      width: 16vw;
    `;
    const TdControl = styled(Td)`
      width: 36vw;
    `;

    const TdName = styled(Td)`
      width: 20vw
    `;

    const Button = styled.button`
      border-radius: 3px;
      margin: 0.5em 1em;
    `;

export default function Coin(props) {

    const handleRefresh = (event) => {
     event.preventDefault();
     props.handleRefresh(props.tickerId);
   }

   const handleBuy = (event) => {
    event.preventDefault();
    if(props.price <= props.balance){
      props.handleTransaction( true, props.tickerId );
    }
    
  }

  const handleSell = (event) => {
    event.preventDefault();
    if(props.balance > 0){
      props.handleTransaction( false, props.tickerId );
    }
    
  }
        return(
            <tr>
              <TdName>{props.name}</TdName>
              <Td>{props.ticker}</Td>
              <Td>${props.price}</Td>
              <Td>{props.showBalance ?  props.balance : "-"} </Td>
              <TdControl>
                <form action="#" method ="POST">
                <Button className = "btn btn-info" onClick= { handleRefresh } >REFRESH</Button>
                <Button className = "btn btn-success" onClick= { handleBuy } >BUY</Button>
                <Button className =  "btn btn-warning" onClick= { handleSell } >SELL</Button>
                </form>
              </TdControl>
            </tr>
          );
}

Coin.propTypes = {
  name: PropTypes.string.isRequired,
  ticker: PropTypes.string.isRequired,
  price: PropTypes.number.isRequired,
}`Preformatted text`
1 Like

Here is my exchange site: https://jelloss.github.io/top-coins-atm/
Here are my project files on Github: https://github.com/jelloss/top-coins-atm

Great course although it was a bit hard to understand sometimes as a beginner but I get how much you can do with react. Very excited to build web apps/dapps with react. You guys sure helped me a lot to get a really solid base knowledge on react and wanna thank you all you helped this made possible.

Have a nice day!

1 Like

I now see that there is also another video about styling the web app but I made some changes already by myself. Does anyone know how to go back to a branch or something and undo all the changes?

1 Like

You can always get back to any of the commits you make on git. So, you can do it without any issues.

1 Like

got that but now I want to push the changes that I have made but then this error occurs
2021-03-20
it says that I need to push the HEAD state to the name of the remote branch but how do I find the remote branch and then push it to the local branch?

Not sure how you ended up in a detached state, but you can see in the blue area of your prompt in the terminal that you are not on a branch.

If you google the ā€œfatal: you are not currently on a branchā€ error message, you get this StackOverflow link: https://stackoverflow.com/questions/30471557/git-push-master-fatal-you-are-not-currently-on-a-branch/30471627. If you follow the steps there you can at least restore the state, where you have a local branch and a remote branch. Please note you may need to use the branch name main instead of master on GitHub due to GitHub branch naming changes. Check out your GitHub branch to see if it reads master or main.

In general, I highly recommend investing effort into deeply learning git and github. Check out my video I linked in the course: https://www.youtube.com/watch?v=lXU216xeVWU and follow the steps from start to finish.

1 Like

Finally got it! only thing is that my helicopter button is a little bit different (slightly bigger and no green color) but Iā€™m really happy it finally updated with the remote branch. Thank you for this great course I learned a lot from it. Have a nice day!

2 Likes