Exercise - Extend your Coin Exchange with more functionality

Hi @Bdos87,
If you don’t mind, could you share me the link to the entire repo on GitHub? I understand what the issue here is but I would like to run the app myself once to understand the entire functionality you are trying to recreate.

1 Like

Hey Malik, to be honest I wasn’t very comfortable with the git portion of the course. I have vowed to better my react skills and then tweak all the git stuff etc. Is there any other way of providing you with all the code files? My app runs but I’m unable to update the new state of wallets due to poor understanding of how to update the this.state.wallets array.

I guess I’m having trouble including conditions within the loop syntax for react before updating the state. Also having trouble with the setstate syntax.

No issues, You can paste the code here for rest of the files (walletbalance, balance, transact, and Swap). I will take a look.

2 Likes
import logo from './logo.svg';
import './App.css';
import Swap from './swap';
import Transact from './transaction'; 
import React, { Component } from 'react'
import WalletBalance from './walletbalance';
import Balance from './balance'
import styled from 'styled-components'; 

const Section = styled.section`
    font-size: 12px;
    border: 1px solid black;
    text-align: center;
    padding: 1rem 0 1rem ;
    width: 35vh;
    font-color: black;
`;
   


 export default class App extends Component {
   constructor (props){
     super(props);
     this.currencies = ["ETH","DAI","MKR","LINK","KNC","LEND","WETH"];

     this.state={
    
     confirm: false,
     swap: false,
     balanceUpdate: false,
     amount: "",
     converted: "",
     base: "ETH",
     other: "DAI",
     blockId: "",
     timeStamp:"",
     price: "",
     /*liquidityPool: "",*/

     wallets: [
      {ticker:"ETH", total: 3000 ,},
      {ticker:"WETH", total:0,},
      {ticker:"LEND", total:150000,},
      {ticker:"LINK", total:10000,},
      {ticker:"KNC", total: 0,},
      {ticker:"MKR", total:500,},
      {ticker:"DAI", total: 500000,} ] 




     }

     };
       


  
     render() {
      /*const timestamp = date.now();
      const txId = ""; */
      
      
    

       return (
       <div>
        <div className="App">
        
           <button className="register">SIGN UP</button>
         
           <button className="enter">LOGIN</button>

           <header>
          
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />

          <p>
          DRaU
          </p>
              <Swap 
              amount={this.state.amount} 
              other={this.state.other} 
              converted={this.state.converted} 
              base={this.state.base} 
              makeSelection={this.makeSelection} 
              changeValue={this.changeValue} 
              calculateSwap={this.calculateSwap}
              swap={this.state.swap} 
              confirm={this.state.confirm} 
              makeTransaction={this.makeTransaction} 
              resetSwap={this.resetSwap} 
              resetTransaction={this.resetTransaction} />
                 <br></br>  
                
                 <h1 className="details">
                   <Transact 
                 sold={this.state.amount} bought={this.state.converted} 
                 core={this.state.base} alter={this.state.other} 
                 confirm={this.state.confirm} swap={this.state.swap} 
                 price={this.state.price}/></h1>
                 
                 </header>   
       
        <Section>
        <table> 
        <thead >
        <Balance account=  {this.state.totalBalance}/> 
        
        </thead>
        <tbody>
        <tr>
        <th>Wallet Balance</th>
        </tr>
        
        {this.state.wallets.map((values) =>
            <WalletBalance ticker={values.ticker} 
                           total={values.total} 
                           base = {this.state.base} 
                           other = {this.state.other} 
                           amount = {this.state.amount} 
                           converted = {this.state.converted} 
                           balanceUpdate = {this.state.balanceUpdate}
                           prepareBalance={this.prepareBalance}
                           calculateBalance={this.calculateBalance} />
                           
            )
            }
         
          </tbody>
        </table>
        </Section>     
        
                </header>
                
                </div>  
            
          <tr>{this.showTranId}</tr>
       
            </div>
         
       )
     }

    
     makeSelection = (event) => {
      
      this.setState({ 

        [event.target.className]: event.target.value  
  
        }, this.calculateSwap);

    
   }

   changeValue = (event) => {
     this.setState({

      amount: event.target.value,
      converted: "",

     }, this.calculateSwap);
   }

   calculateSwap = () => {
    
     const isValid = parseFloat(this.state.amount);
    if (isNaN(isValid)){
      return ;
    }

    fetch(`https://api.0x.org/swap/v1/quote?buyToken=${this.state.other}&sellToken=${this.state.base}&sellAmount=${this.state.amount}`)
    .then(response => response.json())
    .then( data => {
      
      var dateTime= new Date();
     
      this.setState({
       
       converted: data.price * isValid,
       blockId: data.allowanceTarget,
       timeStamp: dateTime,
       price: data.price,
      /*  liquidityPool: data.sources[1].name */

     });
      

    });
}
 

  makeTransaction = () => {
    const sold= this.state.amount;
    const core = this.state.base;
    const alter=this.state.other; 
    const bought= this.state.converted;
  
    if(isNaN(sold)){
  
     alert("unable to process");
       return false;
     }
  
     else if(isNaN(bought)){
      alert("unable to process");
      return false;
      
     }
    
     else if(core===alter){
      
      alert("unable to process");
      return false;
     
  
     } 
     else if(bought===0){

      alert("unable to process");
      return false;
     }
     else if(bought===""){

      alert("unable to process");
      return false;
     }
     else if(sold===""){

      alert("unable to process");
      return false;
     }
     
     else{
     
      this.setState({
      swap: true,

      
     });
  }
     setTimeout(this.resetSwap, 5000);
       
     }
     

     resetSwap = () => {
     
      this.setState({
           
      swap: false,
      confirm: true,
      

      },this.prepareBalance);
      setTimeout(this.resetTransaction, 7000);
 }  

 resetTransaction = () =>{

  this.setState({
    confirm: false,
    amount: "",
    converted: "",
  });

 }
   
showTranId = () => {
  return (
    <h1>{this.state.timeStamp} {this.state.blockId}</h1>
  );
}
 
  
  prepareBalance = () => {
    
      this.setState({
        balanceUpdate: true,
        
      });

      setTimeout(this.calculateBalance, 5000);
  }

  calculateBalance = () => {
   
    this.setState({
      balanceUpdate:false,
    });
  }
  
  }
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import styled from 'styled-components';





export default class Swap extends Component {

     constructor(props){
     super(props);
     this.currencies = ["ETH","DAI","MKR","LINK","KNC","LEND","WETH"];

     this.state={

        swap: this.props.swap,
        confirm: this.props.confirm,
        amount: this.props.amount,
        converted: this.props.converted

     }
     


    }
    

      
    render() {
        
        return (
            <div>
                <form action="#" method="Post">
               <div> 
           <select onChange={this.props.makeSelection} className="base"  value={this.props.base}>
                {this.currencies.map(currency => <option  disabled={(this.props.swap === true)||(this.props.confirm === true)} key={currency} value={currency}>{currency}</option>)}
            </select>
              <input disabled={(this.props.swap === true)||(this.props.confirm === true)} className="amount" placeholder="Amount" onChange={this.props.changeValue} value={this.props.amount}></input>
         </div>
               <br></br>
                <div>
               <select onChange={this.props.makeSelection} className="other" value={this.props.other}>
               {this.currencies.map(currency => <option disabled={(this.props.swap === true)||(this.props.confirm === true)} key={currency} value={currency}>{currency}</option>)}
               </select>
               <input disabled={true} className="converted"  placeholder="your Swap amount" value={this.props.converted} ></input>

               </div>
               
               <br></br>
                
               <button disabled={(this.props.swap === true)||(this.props.confirm === true)} className="swap" onClick={this.handleSwap} >SWAP</button>
                </form>

            </div>

              
        )

    }
     
        handleSwap =(event)=>{
            event.preventDefault();
             this.props.makeTransaction();
            
         }
    
         
    
     

}
Swap.propTypes = {
    amount: PropTypes.number.isRequired,
    converted: PropTypes.number.isRequired, 
    swap: PropTypes.bool.isRequired,
    confirm: PropTypes.bool.isRequired,
};
import React, { Component } from 'react'
import styled from 'styled-components';

export default class Walletbalance extends Component {
  
    
       
    render() {
        const minus = parseFloat(this.props.amount) ;
        const plus = parseFloat(this.props.converted); 

        let balance = this.props.total;
              
              if (this.props.ticker===this.props.base){
                 balance = this.props.total - minus;
              }
               else if (this.props.ticker===this.props.other){
                   balance = this.props.total + plus;
               };
            
        
        return (
            <div>

             <tr>
            <td>{this.props.ticker} </td> 
        { this.props.balanceUpdate?<td>{balance} </td> : <td>{this.props.total}</td>}
            </tr>  

            </div>
        )
    } 
}


````import React, { Component } from 'react'
import PropTypes from 'prop-types';
import styled from 'styled-components';

export default class Transact extends Component {
   

     render() {

        let content= "";
        if (this.props.confirm===true){
        content = <> You have sold:{this.props.sold}    {this.props.core} ,   for: {this.props.bought}  {this.props.alter} ,  at the current 0x protocols  {this.props.core} vs {this.props.alter} liquidity pool rate: 1 : {this.props.price}</>;
        }
        else if(this.props.swap){
         
          content= "your transaction is being proccessed";
  
        }   

        
            return (
    
                <div>
                         <p> Transaction status: {content} </p>
                    
                </div>
            );
        }
}
    
Transact.propTypes = {
    sold: PropTypes.number.isRequired,
    core: PropTypes.string.isRequired,
    alter: PropTypes.string.isRequired,
    bought: PropTypes.number.isRequired,
};

`

import React, { Component } from ‘react’
import styled from ‘styled-components’;

export default class Walletbalance extends Component {

render() {
    const minus = parseFloat(this.props.amount) ;
    const plus = parseFloat(this.props.converted); 

    let balance = this.props.total;
          
          if (this.props.ticker===this.props.base){
             balance = this.props.total - minus;
          }
           else if (this.props.ticker===this.props.other){
               balance = this.props.total + plus;
           };
        
    
    return (
        <div>

         <tr>
        <td>{this.props.ticker} </td> 
    { this.props.balanceUpdate?<td>{balance} </td> : <td>{this.props.total}</td>}
        </tr>  

        </div>
    )
} 

}

i had to include two components in one reply, also dont take the code in walletBalances to seriously, it was just a quick fix and im hoping to modify it once i learn how to set the new State of the array.If you run the code you will see that the wallet balances are calculated once the function is called but the state doesn’t update once the resetTransaction function is called. So if you could help me with this solution and also if you could highlight limitations of the code and where i should be using lifecycles, async await, inconsistency in states,etc. thank you for your help, resolving these will really help develop my react skills.

1 Like

@Bdos87,
I think you missed a callback to the function calculate balance after you have done the swap.

I added it in the code and it works as expected. Do test it out. And also, the API does not work for ETH amounts greater than 1. I do not know why.

Added the this.calculateBalance() as a callback in the setState function in the makeTransaction() function

import logo from './logo.svg';
import "./App.css";
import Swap from "./Swap";
import Transact from "./transaction";
import React, { Component } from "react";
import WalletBalance from "./walletbalance";
import Balance from "./balance";
import styled from "styled-components";

const Section = styled.section`
  font-size: 12px;
  border: 1px solid black;
  text-align: center;
  padding: 1rem 0 1rem;
  width: 35vh;
  font-color: black;
`;

export default class App extends Component {
  constructor(props) {
    super(props);
    this.currencies = ["ETH", "DAI", "MKR", "LINK", "KNC", "LEND", "WETH"];

    this.state = {
      confirm: false,
      swap: false,
      amount: "",
      converted: "",
      base: "ETH",
      other: "DAI",
      blockId: "",
      timeStamp: "",
      price: "",
      /* liquidityPool: "", */

      wallets: [
        { ticker: "ETH", total: 3000 },
        { ticker: "WETH", total: 0 },
        { ticker: "LEND", total: 150000 },
        { ticker: "LINK", total: 10000 },
        { ticker: "KNC", total: 0 },
        { ticker: "MKR", total: 500 },
        { ticker: "DAI", total: 500000 }
      ]
    };
  }

  render() {
    /* const timestamp = date.now();
      const txId = ""; */

    return (
      <div>
        <div className="App">
          <button className="register">SIGN UP</button>

          <button className="enter">LOGIN</button>

          <header>
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" /> 
              <p>DRaU</p>
              <Swap
                amount={this.state.amount}
                other={this.state.other}
                converted={this.state.converted}
                base={this.state.base}
                makeSelection={this.makeSelection}
                changeValue={this.changeValue}
                calculateSwap={this.calculateSwap}
                swap={this.state.swap}
                confirm={this.state.confirm}
                makeTransaction={this.makeTransaction}
                resetSwap={this.resetSwap}
                resetTransaction={this.resetTransaction}
              />
              <br />
              <br />
              <h1 className="details">
                <Transact
                  sold={this.state.amount}
                  bought={this.state.converted}
                  core={this.state.base}
                  alter={this.state.other}
                  confirm={this.state.confirm}
                  swap={this.state.swap}
                  price={this.state.price}
                />
              </h1>
            </header>

            <Section>
              <table>
                <thead>
                  <Balance account={this.state.totalBalance} /> 
                </thead>
                <tbody>
                  <tr>
                    <th>Wallet Balance</th>
                  </tr>
                  {this.state.wallets.map((values) => (
                    <WalletBalance
                      ticker={values.ticker}
                      total={values.total}
                    />
                  ))}
                </tbody>
              </table>
            </Section>
          </header>
        </div>
      </div>
    );
  }

 
  makeSelection = (event) => {
    this.setState(
      {
        [event.target.className]: event.target.value
      },
      this.calculateSwap
    );
  };

  changeValue = (event) => {
    this.setState(
      {
        amount: event.target.value,
        converted: ""
      },
      this.calculateSwap
    );
  };

  calculateSwap = () => {
    const isValid = parseFloat(this.state.amount);
    if (isNaN(isValid)) {
      return;
    }

    fetch(
      `https://api.0x.org/swap/v1/quote?buyToken=${this.state.other}&sellToken=${this.state.base}&sellAmount=${this.state.amount}`
    )
      .then((response) => response.json())
      .then((data) => {
        const dateTime = new Date();

        this.setState({
          converted: data.price * isValid,
          blockId: data.allowanceTarget,
          timeStamp: dateTime,
          price: data.price
          /*  liquidityPool: data.sources[1].name */
        });
      });
  };

  makeTransaction = () => {
    const sold = this.state.amount;
    const core = this.state.base;
    const alter = this.state.other;
    const bought = this.state.converted;

    if (isNaN(sold)) {
      alert("unable to process");
      return false;
    }

    if (isNaN(bought)) {
      alert("unable to process");
      return false;
    }

    if (core === alter) {
      alert("unable to process");
      return false;
    }
    if (bought === 0) {
      alert("unable to process");
      return false;
    }
    if (bought === "") {
      alert("unable to process");
      return false;
    }
    if (sold === "") {
      alert("unable to process");
      return false;
    }

    this.setState(
      {
        swap: true
      },
      this.calculateBalance // I added this here
    );

    setTimeout(this.resetSwap, 5000);
  };

  resetSwap = () => {
    this.setState({
      swap: false,
      confirm: true
    });
    setTimeout(this.resetTransaction, 7000);
  };

  resetTransaction = () => {
    this.setState(
      {
        confirm: false,
        amount: "",
        converted: ""
      },
      this.showTranId
    );
  };

  showTranId = () => (
    <>
      {this.state.timeStamp} {this.state.blockId}
    </>
  );

  calculateBalance = () => {
    const base = this.state.base;
    const amount = this.state.amount;
    const converted = this.state.converted;
    const other = this.state.other;

    const newWallets = this.state.wallets.map(function ({ ticker, total }) {
      let newTotal = total;

      if (ticker === base) {
        newTotal = newTotal - amount;
      } else if (ticker === other) {
        newTotal = newTotal + converted;
      }

      return {
        ticker,
        total: newTotal
      };
    });

    this.setState({ wallets: newWallets });
  };
}

2 Likes

You would call a function like showTranID in the jsx itself . Something like this –

<header className="App-header">
              <img src={logo} className="App-logo" alt="logo" /> 
              <p>DRaU</p>
              <Swap
                amount={this.state.amount}
                other={this.state.other}
                converted={this.state.converted}
                base={this.state.base}
                makeSelection={this.makeSelection}
                changeValue={this.changeValue}
                calculateSwap={this.calculateSwap}
                swap={this.state.swap}
                confirm={this.state.confirm}
                makeTransaction={this.makeTransaction}
                resetSwap={this.resetSwap}
                resetTransaction={this.resetTransaction}
              />
              <br />
              <br />
               {this.showTranId()} //calling the function here
              <h1 className="details">
                <Transact
                  sold={this.state.amount}
                  bought={this.state.converted}
                  core={this.state.base}
                  alter={this.state.other}
                  confirm={this.state.confirm}
                  swap={this.state.swap}
                  price={this.state.price}
                />
              </h1>
            </header>

So, every time your component renders that function will be called. If you want conditions inside the function, you can add it and render certain things based on your state value.

Hope this clears it out for you.

1 Like

thank you very much Malik, i will play around for a while and try to get everything to work. really appreciate your help, if i have any more questions can i continue messaging on this thread?

1 Like

@Bdos87,
Definitely. If you want immediate help feel free to personal message on the forum as well.

Happy Learning! :smile:

1 Like

Thank you Abdul and the whole Ivan on Tech team, Im loving my new skills and knowledge. This Academy is great.

2 Likes

Hi @Bdos87,
This is the response to your personal message query –

First things first,

To properly debug or understand what your code is doing, make sure to keep console.log() at every part of your function call or value return. This will help you navigate better and waste less time instead of relying on state changes or api calls.

Second point,
You are dwelling into the complex area of javascript. Which is the async function calls. The reason why you were getting the undefined output is because you were running the loop and calling the api for each iteration. In these cases, the loop will be executed first and then the api results will lag and return values. Thus you will never see the actual results. To solve this issue we need to use “async” and “await” to tell Javascript, please slow down, my api call is being made, then you can finish your loop.

Ok, now

add a button that says “convert to eth prices” in your JSX.

<button className="convertToEth" onClick={this.portfolioBalance}>
            CONVERT TO ETH
 </button>

This will be your function call on click of the button. I do not know the scenario of this function call, so I’m assuming it’s on button click.

// Make the function async
portfolioBalance = async () => {
    const wallets = this.state.wallets;
    //Since the map function will make API calls, make it async
    const newBalance = wallets.map(async ({ ticker, total }) => {
      console.log(ticker, total, "ticker, total");

      if (ticker === "ETH") {
        const price = 1;
       //return the object if its eth
        return {
          ticker: ticker,
          total: total * price
        };
      } else {
       // await for the API call, basically telling javascript to return a promise when API is in progress but then return value after you got the value
        const response = await fetch(
          `https://api.0x.org/swap/v1/quote?buyToken=ETH&sellToken=${ticker}&sellAmount=100000`
        );
        // await here as well
        const responseJSON = await response.json();
        //await here as well
        const price = await parseFloat(responseJSON.price).toFixed(5);
        console.log(price, ticker , "price I got from API and corresponding ticker");
        // return the value if its any other coin
        return {
          ticker: ticker,
          total: total * price
        };
      }
    });

   // Here we basically say, the map function returned an array of promises, once  all the promises are resolved, retrieve the actual values and assign it to updatedBalance
    const updatedBalance = await Promise.all(newBalance);
    console.log(updatedBalance); //to check my final array 

    // Update the state with the new balance in ETH
    this.setState({
      wallets: updatedBalance
    });
  };
}

This might sound confusing at first because it actually is. You need to dive deep to understand how async await and promises work. Once you’ve actually mastered those concepts you can call yourself a very good javascript developer.

Do not worry, we have all gone through this phase. Feel free to ask as many questions as possible.

Hope this clears it out for you.

Happy Learning! :smile:

1 Like

thank you Abdul your a legend, im very grateful. I will fiddle with it and give you some feedback soon. Get well brother.

1 Like

Great, all solved and my understanding of React Js is better now, apart from one thing, shouldn’t i be using componentDidMount somewhere in all of this?

1 Like

Hi @Bdos87,
ComponentDidMount would only be used if you want to do something as soon as the component renders but happens only once. Fetching some initial data or setting certain settings only initially. If that’s the use case you are looking you can go ahead and use it.

In this case, if you want to change all the balances of your wallet to ETH as soon as the component loads , you can call the function on ComponentDidMount instead of calling it on a button click.

Hope this clears it out for you.

Happy learning !

2 Likes

Great and thank you for broadening my knowledge. I will now try to get more familiar with API requests, I will try to understand how useState and Hooks are applied before seeing how React Native differes for mobile Apps. Then onto Solidity 101. Thank you once again.

1 Like

It’s gonna be quite a ride. Hope you’ll succeed in all of them. :smile:

Thank you @zsolt-nagy for this course. You were very resourceful and there was so much to learn from this course.
my project
project website

1 Like

Hard to tell. This is just a function, but we’d need to see where this function was called. I’d suggest restarting the same steps based on the course, but this time on codesandbox.io so that you can share the link.

2 Likes