Project - DEX Final Code

The moralis dapp servers does contain their own nodes for the selected network, which you can use for a dapp (creating a website with web3 integration through a moralis server).

But we remove the nodes options, thats why if you want to connect truffle with a blockchain, you need to use a node from alchemy.

Thats only for user with a Dapp already working on staging and their ready to go for production.

Indeed, you need to use alchemy to connect your truffle project with a blockchain, and then you will be able to deploy your contracts to the selected blockchain.

So the moralis server is what you will use to power your Dapp (frontend/website) with web3, and then connect your dapp to your contracts.

Carlos Z

2 Likes

Here is my code.
I haven’t built a frontend yet but I will in the future because after reading many comments, all I need is in the previous comments to start building something.

In the code I just added a condition, because we did not take care in the case where the trader wants to buy for example 50 LINK in marketOrder, but does not have enough ETH to cover all the purchases.
With the final solution we only say oh you can’t take this order you don’t have enough ETH, but the truth is that depending on how many ETH I have in my balances I can probably fill some of it in the order.
Example: I deposited 1000 ETH and placed a maketOrder for 50 LINK.
orderbook (sellSide) =

  1. LINK/amount: 20/price: 5$ = 100 ETH —> remaining: 900 ETH
  2. LINK/amount 10/ price : $10 = 100 ETH —> remaining : 800 ETH
  3. LINK/number 10/ price : $20 = 200 ETH —> remaining : 600 ETH
  4. LINK/amount 10/ price : $100 = 1000 ETH —> left : 0 ETH

Here, instead of skipping the whole order [4], it seems correct to take what I can take in the order in proportion to my available ETH.
So take 6 LINK at the price of 600 ETH and fill the order.

Thanks again to the entire Moralis team for this outstanding course and I will continue with Ethereum Smart Contract Security.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "../contracts//wallet.sol";

contract Dex is wallet{

 using SafeMath for uint256;

    enum Side{
        BUY,
        SELL
    }  

    struct Order{
        uint id;
        address trader;
        bytes32 ticker;
        Side side;
        uint amount;
        uint price;
        uint filled;

    }

    uint public nextOrderId;

    mapping(bytes32 => mapping( uint => Order[])) public orderBook;

    function getOrderBook(bytes32 _ticker, Side _side) public view returns(Order[] memory){
        return orderBook[_ticker][uint(_side)];
    }

    function createLimitOrder(Side _side, bytes32 _ticker, uint _amount, uint _price) public{

        if(_side ==Side.BUY){
           require(balances[msg.sender]["ETH"] >= _amount.mul( _price),"not enough ETH");
        }

        if(_side ==Side.SELL){
           require(balances[msg.sender][_ticker] >= _amount,"amount insufficient");
        }

        Order[] storage orders = orderBook[_ticker][uint(_side)]; // creation d'un tableau qui est en storage

        orders.push(
            Order(nextOrderId,msg.sender,_ticker,_side,_amount,_price,0)
        );

        //bubble sort algorithm
        if(_side == Side.BUY){
            for(uint i = orders.length-1; i>0; i--){
                if(orders[i].price > orders[i-1].price){
                    Order memory tampon  = orders[i];
                    orders[i] = orders[i-1];
                    orders[i-1] = tampon;
                }
            }
        }

        else if(_side == Side.SELL){
         for(uint i = orders.length-1; i>0;  i--)
           {
            if( orders[i].price < orders[i-1].price)
            {
             Order memory tampon   = orders[i];
            orders[i] = orders[i-1];
            orders[i-1] = tampon;

            }
         }
     }

       nextOrderId++;
    }

    function createMarketOrder(Side _side, bytes32 _ticker, uint _amount)public{

        if(_side ==Side.SELL){
           require(balances[msg.sender][_ticker] >= _amount,"amount insufficient");
        }

        uint orderBookSide;

        if(_side == Side.BUY){
          orderBookSide =1;
        }

        if(_side == Side.SELL)

        {
            orderBookSide =0;
        }

        Order[] storage orders = orderBook[_ticker][orderBookSide];

        uint totalFilled =0;

        for(uint256 i=0; i < orders.length && totalFilled < _amount; i++ ){

                //how much we can fill from order[i]

          uint leftToFill = _amount.sub(totalFilled);
          uint availableToFill = orders[i].amount.sub(orders[i].filled);
          uint valueInEth = balances[msg.sender]["ETH"];
          uint filled = 0;

         if(_side == Side.SELL || balances[msg.sender]["ETH"] >= orders[i].amount.mul(orders[i].price)){

          if(availableToFill > leftToFill){ // fill entire market order

                filled = leftToFill;
          }

          else{
                filled = availableToFill; // fill as much as is available in order[i]
          }
        }

         // if the trader doesn't have enough ETH for covering the order i

         else{
           availableToFill = valueInEth.div(orders[i].price); 
            if(availableToFill >  leftToFill)filled = leftToFill;
            else filled = availableToFill;
         }

          totalFilled = totalFilled.add(filled);
          orders[i].filled = orders[i].filled.add(filled);

        //Execution trade

          if(_side == Side.BUY){

            require(balances[msg.sender]["ETH"] >= filled.mul(orders[i].price),"not enough ETH");
               balances[msg.sender]["ETH"] -= filled.mul(orders[i].price);
               balances[orders[i].trader][_ticker] -= filled;

               balances[orders[i].trader]["ETH"] += filled.mul(orders[i].price);
               balances[msg.sender][_ticker] += filled;
            }

           

          else if(_side == Side.SELL){
              balances[orders[i].trader]["ETH"] -= filled.mul(orders[i].price);
              balances[msg.sender][_ticker] -= filled;

              balances[msg.sender]["ETH"] += filled.mul(orders[i].price);
              balances[orders[i].trader][_ticker] += filled;
             }
         }

            // loop trough the orderbook and remove 100% filled orders  

          while(orders.length > 0 && orders[0].filled == orders[0].amount){

            for(uint256 i =0; i < orders.length-1; i++){

             orders[i] = orders[i+1];

             }
            orders.pop();
        }
    }
}
1 Like

Re:
Video: “Deploying Our Contract to a Live Network” at 1:42
I am not sure how to access Speedy Nodes at Moralis.io.
Everything is different than in the video.
Not sure how to continue with deploying to a live testnet.

Help please
Thanks
Dave

1 Like

Instead of using our speedy nodes (deprecated) , you can use an alchemy node to achieve the same, I made a quick guide for a similar question.

Carlos z

1 Like

Oh that’s great!
Thank you Carlos!

1 Like

I am studying parts of the ETH DEX course. I noticed that there are 2 Github pages with code for that DEX: 1. a page which Carlos made, which has dex.js,index.html, and style.css. To me these seem like files for the page that displays the DEX. https://github.com/Iven-onTech-Academy/js-prog-course-2021
and
2. A page that Fillip made, which has the .sols files, etc. https://github.com/filipmartinsson/dex/tree/dex_market_order_done
I will soon deploy the ETH DEX, and I wonder what I should do. I can use the code on Filip’s Github page, and follow his video for deploying the DEX. But should I also use the files on the Github page that Carlos made for the DEX? And if so, how should I combine the code on the 2 pages, when deploying and setting up the DEX?

The repository I made was related to the JavaScript course, it is not related with the DEX project from our smart contract course, so there is no need to use my JS repo for this project. :nerd_face:

Carlos Z

1 Like

Thank you for answering my question. :smiley:

1 Like

Hi all,
A question I’ve just done the Smart Contract 201 course and have previously done the 101 course. And I would like to implement the multi-signature part into the DEx built in 201 course. Any ideas on how can I do so?
thanks

Hi @Philip_Wong

If you are asking in terms of implementation, it would explore more on what you have learned in writing and testing smart contract. I would recommend to follow along so you dont miss anything.

hey @JohnVersus,

nono, you misunderstood my question. in the Smart Contract 101 we learn how to build a multisig wallet right? and then in Smart Contract 201, we learn how to build a EVM DEx.
what I’m asking is if I want to add the multisig functionality that is shown to us from the 101 course to the DEx (from 201), what is the standard approach or the current industry standard to do so? and how I do modify the multsig, as well as, the DEx code to correctly combing them together.

Hi @Philip_Wong
Thats interesting. Now that thinking about it, you could use it to improve security for funds withdrawal in dex wallet or In application where funds are owned by a group of wallets (like a DAO’s).
I dont know if there a standard for implementing with dex.

alright, don’t worry about the standard for implementing with dex for the time being. I would like to know is this do able?
if so where can I start? and are there any documentations or posts that I can do more research and ideas about it?
thanks

It is totally doable. I haven’t made a contract like this before, but this is how I would proceed if want to do it.

  1. First look at the application (ex: like withdrawals or owner transfer functions) where you would need approvals from multiple wallets for improving security.
  2. The solidity logic for collecting the approvals will remain same. Reference for approval function.
  3. For example if you are collecting approvals for owner transfer of a contract from one wallet to another wallet, you will store approvals of a address using mapping.
mapping(address => mapping(address => bool)) approvals;
  1. And every time a new approval is added you will increment the counter.
  2. And finally when you call the owner transfer function it should check if we have the required approvals limit using a require() and process the transaction.

I hope this helps you think more about it.

As of now, I don’t have any docs to share explaining combining multi-sig with dex. I am learning as we speak here. I will share it here if I find something interesting online.

1 Like

looking forward in exchanging more ideas with you on this topic.
one of the reason I had this idea was, if we use Metamask as a wallet and Uniswap as the DEx, I remember that we need to approve the token contract before we can perform a swap from the token to Eth.
so if the multisig wallet signing is a good idea and its doable, maybe we can use another wallet owned by the sender to approve the transaction, and have it to act like a 2FA. as in:

Wallet that has token triggers the selling event -> DEx ask for the 3rd wallet to approve the transaction -> 3rd wallet (owned by token owner) approved the transaction -> DEx perform swap

1 Like

Hi All, anyone still doing this course?
Just like to discuss a few topics.
Regards

1 Like

Hi @justkaz

You can post your queries here if you have any. We will be happy to answer it. :smiley:

1 Like

Hi Guys,

I guess there’re some changes or updates in the truffle_config.js file.
I got many errors when I’m trying to deploy my contract on goerli node.
can someone help me with the latest steps to follow to deploy the contract?

Hi @Ahmad_Abd_Allah

This is how you can do it with the latest truffle.

  1. 1st uncomment the following lines of code from truffle-config.js file
//...

require("dotenv").config();
const mnemonic = process.env["MNEMONIC"];
const infuraProjectId = process.env["INFURA_PROJECT_ID"];

const HDWalletProvider = require("@truffle/hdwallet-provider");

//...

    goerli: {
      provider: () =>
        new HDWalletProvider(
          mnemonic,
          `https://goerli.infura.io/v3/${infuraProjectId}`
        ),
      network_id: 5, // Goerli's id
      chain_id: 5,
    },

  1. Install dotenv and @truffle/hdwallet-provider by using npm as we have uncommented package require lines in the step 1.
npm i @truffle/hdwallet-provider dotenv
  1. Tutorial shows secrets.json to store the secret, but we can use .env file as we have installed dotenv in the previous step. Create a file and name it as .env

  2. Add the following secrets in your .env

INFURA_PROJECT_ID = "xxxx" // You can add any node provider URL here. I used Infura node so I just pasted a project id of my infura node.

MNEMONIC = "xxxx" // generate mnemonic as shown in the tutorial video
  1. In case if you have used other node URL instead of infura node project id, you have to replace this line in truffle-config.js
 new HDWalletProvider( mnemonic, `https://goerli.infura.io/v3/${infuraProjectId}` ),

// replace with 
 new HDWalletProvider( mnemonic, `${infuraProjectId}` ),

Thats it. Now you can follow the remaining steps from the tutorial video.

Hope this helps. Feel free to reply if you have any other questions.

1 Like

https://github.com/SmartContractLawyer/dex/blob/main/dex.sol

improvements:

  1. limit order done (fills orders; unfilled portions going into corresponding order book with bubble sorting and filled portions counted/cleared)
  2. bubble sort now also prioritizes older limit orders of the same/equal price
  3. limit orders that have had their required ETH/token backing removed (i.e. via withdrawal function) are now detected, skipped, and cleared from the corresponding order book, without interrupting limit/market orders
  4. added events/emits, more requires (e.g. so a trader cannot place buy/sell limit orders that would trigger each other), and tried to clarify some variable names (clearer for me at least), plus included a popOrderBook function for ease of manual testing on Remix

thank you very much for an outstanding course Filip and team

2 Likes