Project - DEX Final Code

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

Hi, my dex final code.

=======
wallet.sol
=======
pragma solidity ^0.8.0;

// import the IERC2O interface
import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
//import safeMath
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";
//import Owernable.sol
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";

contract Wallet is Ownable {
    using SafeMath for uint;
    using SafeMath for uint256;
    // define token struct with token name and address
    struct Token {
        bytes32 ticker;
        address tokenAddress;
    }
    /*
    keep balances of user. 
    using double mapping to keep track of differnt assets
    */
    mapping(address => mapping(bytes32 => uint256)) public balances;

    // define a token list to store all supported tokens.
    bytes32[] public tokenList;

    // define a token mapping to fetch token
    mapping(bytes32 => Token) public tokenMapping;

    // event is emitted each time a  withdraw happens
    event withdrawal(
        address indexed _to,
        bytes32 _ticker,
        uint256 indexed _amount
    );

    modifier tokenExist(bytes32 _ticker) {
        //check if token exist
        require(
            tokenMapping[_ticker].tokenAddress != address(0),
            "Token does not exist"
        );
        _;
    }

    modifier hasEnoughBalance(uint256 _amount, bytes32 _ticker) {
        //check if the user have enough balance to witdraw
        require(
            balances[msg.sender][_ticker] >= _amount,
            "Balance not sufficient"
        );
        _;
    }

    function addToken(
        bytes32 _ticker,
        address _tokenadress
    ) external onlyOwner {
        require(
            tokenMapping[_ticker].tokenAddress != _tokenadress,
            "Token already exists"
        );
        //define the new token if non existent
        tokenMapping[_ticker] = Token(_ticker, _tokenadress);
        tokenList.push(_ticker);
    }

    /**
     *
     * deposit  _amount of Token named by _ticker into the DEX
     * use tranferfrom function of Token contract
     * requirements
     * Dex needs to receive approval from User in Token Contract
     */
    function deposit(
        bytes32 _ticker,
        uint256 _amount
    ) external tokenExist(_ticker) {
        IERC20 MTK = IERC20(tokenMapping[_ticker].tokenAddress);
        require(MTK.balanceOf(msg.sender) >= _amount, "no enough token");
        uint256 _currentbalance = balances[msg.sender][_ticker];
        (bool success, uint newBalance) = SafeMath.tryAdd(_currentbalance, _amount);
        if (success) {
            balances[msg.sender][_ticker] = newBalance;
            MTK.transferFrom(msg.sender, address(this), _amount);
        }
    }

    /**
     * withdraw _amount from this wallet to msg.sender balances into ERC20 Token contract
     * requirements
     * balance of msg.sender must be greater than or equal to _amount
     * use transfer function in ERC20 token contract to transfer from this wallet address to msg.sender address
     */

    function withdraw(
        bytes32 _ticker,
        uint256 _amount
    ) external tokenExist(_ticker) hasEnoughBalance(_amount, _ticker) {
        uint256 _actualBalance = balances[msg.sender][_ticker];
        // using Math library to avoid overflow
        (bool sucess, uint256 newBalance) = SafeMath.trySub(
            _actualBalance,
            _amount
        );

        // if no overflow adjust balances
        if (sucess) {
            balances[msg.sender][_ticker] = newBalance;
            IERC20(tokenMapping[_ticker].tokenAddress).transfer(
                msg.sender,
                _amount
            );
        }
    }
}
=========
token.sol
=========
pragma solidity ^0.8.0;

// import the IERC2O interface
import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MTK is ERC20 {
    constructor() ERC20("mytoken", "MTK") {
        _mint(msg.sender, 1000);
    }
}
=======
dex.sol
=======
pragma solidity ^0.8.0;

import "./Wallet.sol";

contract Dex is Wallet {
    // Enum that describe wheter the order is a SELL or BUY order
    enum Side {
        BUY,
        SELL
    }

     struct Trader {
        address account ;
        uint256 share;
    }

    /**
     *  Order Struct used to describe an order
     *
     */
    struct Order {
        uint id;
        uint position;
        Side side;
        bytes32 ticker;
        uint256 amount;
        uint256 price; 
        bool filled;
    }

    /**
    * used to track the share a trader have in each order
    *
    *
    **/
    struct Ordermap{
        Trader[] traders;
        mapping(address => uint256) indexes;
    }
    // used to initialize the order ID
    uint256 orderID = 0;

    /**
     * used to fetch orders from the order book
     * need the ticker and the side of the order
     */
    mapping(bytes32 => mapping(uint => Order[])) public orderbook;
    // map an order to the involved traders
    mapping(uint => Ordermap) orderTotradersmapping;
    /**
    * used to  synchronize amount on buy side based on the price
    *
    */
    modifier checkbalances(bytes32 _ticker,Side side, uint256 _amount, uint256 _price){
           if (side == Side.BUY) {
            (, uint256 price) = SafeMath.tryMul(_amount, _price);
            require(
                balances[msg.sender][bytes32("ETH")] >= price,
                "balance no sufficient"
            );
           
        } else if (side == Side.SELL) {
            require(
                balances[msg.sender][_ticker] >= _amount,
                "no Enough tokens"
            );
         
        }
        _;
    }
    /**
     * used to set the user Ether balance
     * is a public payable function
     */
    function depositEther() public payable {
        balances[msg.sender][bytes32("ETH")] += msg.value;
    }
     

    function createMarketOrder(uint256 _amount, bytes32 _ticker, Side side) public {
        uint otherside = 0;
        if (side == Side.BUY) {
            otherside = 1;
        }
       if (side == Side.SELL) {
            require(balances[msg.sender][_ticker] >= _amount, "Token balance not sufficient");
            otherside = 0;
        }
        uint256 r_amount = _amount;
        uint256 counter = 0;
        Order[] storage orders = orderbook[_ticker][otherside];
        if(orders.length==0){
            return ;
        }
        while (r_amount > 0 && counter < orders.length) {
            r_amount = _marketOrderTrade(r_amount, _ticker,orders[counter],otherside);
            counter++;
        }
    }

    function _marketOrderTrade( uint256 _amount, bytes32 _ticker, Order storage order,uint _otherside) private returns (uint256 remaining) {
        uint256 traded = 0;
        // if side is buy i.e otherside == 1 ; msg.sender  want to buy _amount token from order.traders 
        // if side is sell i.e otherside == 0; msg.sender want to sell _amount token to order.traders
        if (_otherside == 1) {
            uint i=0;
            //get the list of the buyers
            Trader[] storage buyers = orderTotradersmapping[order.id].traders;
            while(i <buyers.length && traded<_amount){
                uint256 tradable = getTradable(order.amount,buyers[i].share);
                _buy(buyers[i],_ticker,tradable, order.price);
                (,traded) = SafeMath.tryAdd(traded,tradable);
                (,order.amount) = SafeMath.trySub(order.amount,tradable);
                i++;
            }
        }else if(_otherside == 0){
            uint j=0;
            //get the list of sellers 
            Trader[] storage sellers = orderTotradersmapping[order.id].traders;
            while(j <sellers.length && traded<_amount){
                uint256 tradable = getTradable(order.amount,sellers[j].share);
                _sell(sellers[j],_ticker,tradable, order.price);
                (,traded) = SafeMath.tryAdd(traded,tradable);
                j++;
            }
        }
        (,remaining) = SafeMath.trySub(_amount,traded);
        return remaining;
    }

   
    function _buy(Trader storage _from, bytes32 _ticker, uint256 _amount,uint256 price) private {
         // Estimate the price in ETH for the trade for each order on the sell side
        (bool success, uint256 due) = SafeMath.tryMul(price, _amount);
        // require the buy must have enough ETH to proceed to the trade
        require(balances[msg.sender][bytes32("ETH")] >= due,"ETH balance not sufficient for this Buy Market Order ");
         
        if (success) {
            // decrease the ETH balance of the buyer
            (, balances[msg.sender][bytes32("ETH")]) = SafeMath.trySub(balances[msg.sender][bytes32("ETH")], due);
            // increase the ETH balance of the trader on the SELL side
            (, balances[_from.account][bytes32("ETH")]) = SafeMath.tryAdd(balances[_from.account][bytes32("ETH")], due);
            // decrease the Token balance of the trader on the SELL side
            (, balances[_from.account][_ticker]) = SafeMath.trySub(balances[_from.account][_ticker], _amount);
            // transfer the token to Buyer
            (, balances[msg.sender][_ticker]) = SafeMath.tryAdd(balances[msg.sender][_ticker], _amount);
            // adjust trader share 
            (,_from.share) = SafeMath.trySub(_from.share, _amount);
        }  
    }

    function _sell(Trader storage _to, bytes32 _ticker, uint256 _amount,uint256 price) private  {
        // get the estimate eth due  by the trader on the buy side for each order to process
        (bool success, uint256 due) = SafeMath.tryMul(_amount, price);
        if (success) {
            // decrease  ETH balance of the  trader on the buy side
            (, balances[_to.account][bytes32("ETH")]) = SafeMath.trySub(balances[_to.account][bytes32("ETH")], due);
             // Increase seller ETH balance
            (, balances[msg.sender][bytes32("ETH")]) = SafeMath.tryAdd(balances[msg.sender][bytes32("ETH")],due);
            //decrease seller token balance
            (, balances[msg.sender][_ticker]) = SafeMath.trySub(balances[msg.sender][_ticker], _amount);
            //transfer token to  the trader on the buy side
            (, balances[_to.account][_ticker]) = SafeMath.tryAdd(balances[_to.account][_ticker],_amount);
            (,_to.share) = SafeMath.trySub(_to.share, _amount);
        }
    }

    function _trade(bytes32 ticker,Trader storage buyer, Trader storage seller,uint256 price,uint256 _amount) private {
            (bool success, uint256 due) = SafeMath.tryMul(_amount, price);
            //require(balances[buyer.trader]["ETH"]>=due, "Balance not enough");
            if(success){
                (,balances[buyer.account]["ETH"]) = SafeMath.trySub(balances[buyer.account]["ETH"],due);
                (,balances[seller.account]["ETH"]) = SafeMath.tryAdd(balances[seller.account]["ETH"],due);
                (,balances[seller.account][ticker]) = SafeMath.trySub(balances[seller.account][ticker],_amount);
                (,balances[buyer.account][ticker]) = SafeMath.tryAdd(balances[buyer.account][ticker],_amount);
                (,buyer.share) = SafeMath.trySub(buyer.share, _amount);
                (,seller.share) = SafeMath.trySub(seller.share, _amount);
            }
    }
    /**
     * @param _amount the amount of token
     *
     *
     *
     */
    function createLimitOrder(uint256 _amount, uint256 _price, bytes32 _ticker,Side side) public checkbalances(_ticker,side,_amount,_price){
        Order[] storage orders = orderbook[_ticker][uint(side)];
        (uint position, bool exist)  = _lookfor(_ticker,_price, side);
        Order storage currentOrder;
        if(exist){
                 currentOrder = orders[position];
                 addTraderToOrder(currentOrder, _amount);  
        }else{

            orders.push( Order({id: orderID, position: orders.length, side: side,ticker: _ticker, amount: _amount, price: _price, filled: false }));
            // store the new added Order
            currentOrder = orders[orders.length-1];
            // create a Trader struct to store informations about the buyer
            Trader memory trader  = Trader(msg.sender, _amount);
            // link the order to the Trader with orderTotradersmapping 
            orderTotradersmapping[currentOrder.id].traders.push(trader);
            orderTotradersmapping[currentOrder.id].indexes[trader.account] =   orderTotradersmapping[orderID].traders.length;
        }
            //sort the orderbook
            _quicksort(orders,0, orders.length, side);
            orderID++;
            if(currentOrder.side == Side.BUY){
                (uint index, bool exist) = _lookfor(_ticker, _price,Side.SELL);
                if(exist){
                     _processLimitOrder(currentOrder,orderbook[_ticker][uint(Side.SELL)][index]);
                }
                // proceed the limit orders if a matching is founded
            } else if(currentOrder.side == Side.SELL){
                (uint index, bool exist) = _lookfor(_ticker, _price,Side.BUY);
                if(exist){
                     _processLimitOrder(orderbook[_ticker][uint(Side.BUY)][index],currentOrder);
                }
            }
    }

    // we use a _quicksort(_tosort, low, high); algorithm to sort orders
    function _quicksort(Order[] storage _tosort, uint low, uint high,Side _side) private {
        if (low < high) {
            uint256 pivot = partition(_tosort, low, high, _side);
            _quicksort(_tosort, low, pivot, _side);
            _quicksort(_tosort, pivot + 1, high, _side);
        }
    }

     function addTraderToOrder(Order storage current,uint256 _amount) private{
            uint256 trader_index = orderTotradersmapping[current.id].indexes[msg.sender];
              // trader_index == 0 mean the trader does not have any shares in the order
            if(trader_index == 0){
                    // add the buyer to trader list 
                orderTotradersmapping[current.id].traders.push(Trader(msg.sender,_amount));
                orderTotradersmapping[current.id].indexes[msg.sender] =  orderTotradersmapping[current.id].traders.length;
                (, current.amount) = SafeMath.tryAdd(current.amount, _amount);
                 }// trader already have a share in the order
                 else if(trader_index >0){
                (,orderTotradersmapping[current.id].traders[trader_index-1].share) = SafeMath.tryAdd(orderTotradersmapping[current.id].traders[trader_index-1].share, _amount); 
                (, current.amount) = SafeMath.tryAdd(current.amount, _amount);
          }
     }
    function _lookfor(bytes32 _ticker, uint256 _price,Side side) private view returns(uint index, bool found){
            Order[] storage tolookup = orderbook[_ticker][uint(side)];
            for(uint i=0; i<tolookup.length; i++){
                if((tolookup[i].ticker ==_ticker)&&(tolookup[i].price == _price)){
                    index = i;
                    found = true;
                    return (index, found);
                }
            }
            return (0, false);
    }

    function _processLimitOrder(Order storage current, Order storage matching) private {
                uint256 tradable = getTradable(current.amount, matching.amount);
                Trader[] storage buyers = orderTotradersmapping[current.id].traders;
                Trader[] storage sellers = orderTotradersmapping[matching.id].traders;
                uint i=0;
                uint j=0;
                while(tradable >0){
                    if(i >= buyers.length){
                        return;
                    }
                    if(j >= sellers.length){
                        return;
                    }
                    if(buyers[i].share < sellers[j].share){
                        uint256 trade_amount = buyers[i].share;
                        _trade(current.ticker, buyers[i],sellers[j],current.price,trade_amount);
                        (, tradable)  = SafeMath.trySub(tradable,trade_amount);
                        (,current.amount) = SafeMath.trySub(current.amount, trade_amount);
                        (,matching.amount) = SafeMath.trySub(matching.amount,trade_amount);
                        i++;
                    }else if(buyers[i].share > sellers[j].share){
                        uint256 trade_amount = sellers[i].share;
                        _trade(current.ticker,buyers[i],sellers[j],current.price,trade_amount);
                        (, tradable)  = SafeMath.trySub(tradable,trade_amount);
                        (,current.amount) =  SafeMath.trySub(current.amount,trade_amount);
                        (,matching.amount) = SafeMath.trySub(matching.amount,trade_amount);
                        j++;
                    }else{
                        uint256 trade_amount = buyers[i].share;
                        _trade(current.ticker, buyers[i],sellers[j],current.price,trade_amount);
                        (, tradable)  = SafeMath.trySub(tradable,trade_amount);
                        (,current.amount) = SafeMath.trySub(current.amount, trade_amount);
                        (,matching.amount) = SafeMath.trySub(matching.amount,trade_amount);
                        i++;
                        j++;
                    }
                }
            if(current.amount == 0){
                    _deleteFromOrderBook(current);
            }
            if(matching.amount == 0){
                    _deleteFromOrderBook(matching);
            }
    }
    // partition functions returns the
    function partition(Order[] storage arr, uint low, uint high,Side _side) private returns (uint256 index) {
        Order memory pivot = arr[low];
        uint i = low;
        uint j = high - 1;
        while (true) {
            //sort descending
            if (_side == Side.BUY) {
                while (arr[i].price > pivot.price) {
                    i++;
                }

                while (arr[j].price < pivot.price) {
                    j--;
                }
            } else if (_side == Side.SELL) {
                //sort ascending
                while (arr[i].price < pivot.price) {
                    i++;
                }

                while (arr[j].price > pivot.price) {
                    j--;
                }
                if (i >= j) {
                    return j;
                }
            }
            if (i >= j) {
                return j;
            }
            
            Order memory temp = arr[i];
            arr[j].position = i;
            arr[i] = arr[j];
            temp.position = j;
            arr[j] = temp;
        }
    }

     function getTradable(uint256 first, uint256 second) private  pure returns(uint256){
             if(first>second){
                return first;
             }
             return second;
    }


    function _deleteFromOrderBook(Order storage _todelete) private {
        require(orderbook[_todelete.ticker][uint(_todelete.side)].length > 0," orderbook not empty");
        Order[] storage orders = orderbook[_todelete.ticker][uint(_todelete.side)];
        if(_todelete.position == orders.length-1){
            orders.pop();
            return;
        }
        Order memory toremove = orders[_todelete.position];
        orders[_todelete.position] =  orders[orders.length-1];
        orders[orders.length-1] = toremove;
        orders.pop();
        delete orderTotradersmapping[_todelete.id];
    }

    function getOrderbook(bytes32 _ticker,Side side) public view returns (Order[] memory) {
        return orderbook[_ticker][uint(side)];
    }
}