Project - Building a DEX

The thing is it SHOULD fail because it is a truffleAssert.reverts() function. That is why I use an account that is not the owner.

UPDATE:
I found a bug in my code. I wrote await before dex.addToken().

Hi @mcgrane5 thanks a lot for your clear explanation. It makes a lot more sense now. I think it is also a matter of doing and seeing it many times. Everything is new for me right now so thats sometimes a struggle. But Iā€™m very happy with the good support I receive here on the forum.

1 Like

Thanks @thecil! Makes much more sense now.

Hey @thomascarl no worries. You will get there it can be hard especially at the start

1 Like

Hi, how does this piece of code work?
IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount);

does this ā€œIERC20(tokenMapping[ticker].tokenAddress)ā€ return an ERC20 object?

I checked the addToken function

function addToken(bytes32 ticker, address tokenAddress) external{

        tokenMapping[ticker] = Token(ticker, tokenAddress);

        tokenList.push(ticker);

    }

from what I understood everyone can change the ticker and tokenAddress of the token, shouldnā€™t we first check if there is already the address of the token and instead of using ticker as argument we can get the token symbol directly from the ERC20 contract.

1 Like

Check out my solution here:

    /* deposit function should deposit amount of ticker into wallet contract
    * First check that token can be deposited into wallet contract
    * Get a handle on the contract, check balances, and transfer tokens
    * check if transfer was successful
    * update balances using Safemath
    */
    function deposit(uint amount, bytes32 ticker) external {
        require(tokenMapping[ticker].tokenAddress != address(0), "Wallet: Specified token doesn't exist!");
        IERC20 depositToken = IERC20(tokenMapping[ticker].tokenAddress);
        require(depositToken.balanceOf(msg.sender) >= amount, "Wallet: Insufficient tokens to deposit!");

        bool success;

        success = depositToken.transferFrom(msg.sender, address(this), amount);

        require(success, "Wallet: Deposit failed");
        balances[msg.sender][ticker] = balances[msg.sender][ticker].add(amount);
    }

I did it similar to @filip. Invoking the interaction first by calling transferFrom.
Then updating the balance after requiring the call to return true.

The reason to do it like that was that if the transfer should fail, the transaction reverts and I didnā€™t yet affect the balances.

Or would that be reverted anyway if the call to transferFrom fails?

Regarding Market Order Overview explanation video,
in the video Filip first says that person who sets market order want to sell 35 Link, than he said: ā€œthe person who did the market sell order will send the ETH to the buyersā€
How come he is sending ETH to the buyers if he is selling Link?
Think he confused it and should be the other way around.
Please let me know if thatā€™s correct or Iā€™m confusing.
Many thanks

1 Like

hey @taodai before i try to explain why interface contracts are used we should first look at the whole concept in a more general way outside of solidity. So interface contracts work much like other interface agreements in traditional object oriented languages like C++. In this example, the interface specifies the functions a contract of type IERC20 must implement to fit into the overall application framework (being the EIP-20 token standard).

This allows you to customize code without having to implement everything from scratch. The main point of using interfaces ( or Abstract Contracts ) as i understand it is to provide a customizable and re-usable approach for your contracts.

So the IERC20.sol contract is an interface contract. IERC20 defines function signatures without specifying behavior (this is the definition or rather purpose of any interface contract), the function names, inputs and outputs, but no code body. What i mean by code body is that all of the functions in an interface contract have no executable code. ERC20 inherits this Interface and is required to implement all the functions described in the interface.

all the functions in this IERC20 file are standard as defined in the EIP-20. As for ERC20.sol , it is an implementation of the IERC20.sol. it defines all of the token logic. Interpret that this way: ERC20 is an implementation of the Interface defined inIERC20` .

As for your question what is this line of code doing

IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount)

Well if we go to the ERC20.sol file we notice that it has a transfer function. This line of code is simply callig that function in the openzepplin contract from our wallet contract (as remembering we are inheriting it) and trasnfering tokens from the token address for the specific token in question to msg.sender or of a certain amount.

Recall from one of the pervious assignments from earlier in this course Filip gave (the openzeppelin section i believe) where we had to mint tokens in the constructor. One possible solution was to call

ERC20._mint()

rather than writing out own mint function. by doing so we were able to call the mint function from the ERC20 contract as we were inheriting from it. This concept is the exact same in this example here where we call the transfer function from the IERC20 interface contract.

I know the whole concept of these interface contracts can be difficult to wrap your head around i myself was struggling to put into words my awnser and had to do a bit of research.The one thing that is weird to understand is why not just inherit from ERC20.sol and write

ERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount)

and not

IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount)

as it does exactly the same thing. But i think it comes down to what we mentioned above whereby interface contracts defines the functions that fit into the overall application framework being the EIP-20 token standard. I hope this makes sense and that have explained it ok if anyone sees anything i said that is not correct let me know but i hope this at least clears up your question a little.

For further reading check out
https://forum.openzeppelin.com/t/what-is-the-interface-ierc20-contract-used-for-vs-implementation-erc20-contract/4436/2

https://docs.soliditylang.org/en/v0.7.4/contracts.html#interfaces

hey @Kamil37 i cant remember the exact video but the way it works is this. If i am selling link then i need to have enough link tokens to actually sell. When a buy order is made for someone who wants to buy link, they must have enough ETH to cover the cost of the link order.

Say i am the account of the person who is wants to sell link and i have the following balances

//account[ 0 ]    person selling link
linkBalance = 1000 link
EthBalance = 1000 Eth

//say i make a limit order on the sell side with the following properties
createLimitOrder(1, "LINK", 50, 10)

//so this means i account[ 0 ] wants to make a limit sell order for 50 link
//at a price of 10

Then the limit order gets submitted and is waiting for someone to come along and fill it. So now say person two comes along and person2 wants to buy some link. So say that this person2 who wants to buy link and has the following balances

//account[ 1 ]    person buying link
linkBalance = 1000 link
EthBalance = 1000 Eth

//say i make a limit order on the buy side with the following properties
//(nite this limit order meets the exact requirments of account[ 0 ]'s sell
//order so will get executed immediately. This is not always the case)
createLimitOrder(0, "LINK", 50, 10)

//so this means i account[ 1 ] wants to make a limit buy order for 50 link
//at a price of 10 also

When this order gets settled the LINK balance of account[ 0 ] will get reduced by 50 link and there eth
balance will increase by 50 * 10 (as they are selling 50 link at a price of 10 eth per link) so their ether balance will increase.

Likewise for account[ 1 ] there link balance will increase by 50 link as they are buying and conversley their ether balance will get reduced by 50 * 10 link as they are buying 50 link ata price of 10 eth. So after the trade is settled the balancees for both accounts will look like

//account[ 0 ]
linkBalance = 950
ethBalance = 1500

//account[ 1 ]
linkBalance = 1050
ethBalance = 500

the only thing that isnt reflected in real life is that the prices we use are not at all a reflection of real eth and link prices its the concept of the actually trade execution algorithm thats more important here.

As you will come to see later when you do the market order side is that market orders do not take in a user inputted price as an argument, when a market order gets created it just takes the current price on the opposite side of the order book. Whilst the limit order will not fully get settled unless the price is what the person who made the order requests. This is more so realised when someone who makes a large limit order and it takes a few iterations through the opposite side of the order book to get settled. But you will see all of this explained by filip later in the course

2 Likes

My solution for the bubble sort

 //Bubble sort
       if (side == Side.BUY){
           uint8 min = 0; 
          for (uint i=0; i < orders.length; i++){
              if (orders[i].price < orders[i+1].price){
                  min = orders[i].price;
                  orders[i].price = orders[i+1].price;
                  orders[i+1].price = min;
              }
              else  {
                  break;
              }
          }
       }
       else if (side == Side.SELL){
          uint8 max = 0; 
          for (uint i=orders.length -1; i >= 0; i--){
              if (orders[i-1].price > orders[i].price){
                  max = orders[i-1].price;
                  orders[i-1].price = orders[i].price;
                  orders[i].price = max;
              }
              else  {
                  break;
              }
          }
       }
2 Likes

My Market Order Test code

contract("Dex", accounts => {

    // when creating a sell market order, the seller need to have enought tokens for the trade
    // when creating a buy market order, the seller need to have enough ETH for the trade
    // market orders can be submitted even if the order book is empty
    // market orders should be filled until order book is empty or until market order is 100% filled
    // the ETH balance of the buyer should decreas with the filled amount
    // the token balances of the limit order should decreas with the filled amounts
    // filled limit orders should be removed from the order book

    // the token balance of the buyer should increase with filled amount
    // the ETH balance of the seller should increase with filled amount
    
    it("when creating a sell market order, the seller need to have enought tokens for the trade", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.reverts(
            dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 1, 10)
        )
        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));

        await truffleAssert.passes(
            dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 1, 10)
        )
    })

    it("when creating a buy market order, the seller need to have enough ETH for the trade", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.reverts(
            dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 1, 10)
        )
        
        dex.depositEth({value: 10})
        await truffleAssert.passes(
            dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 1, 10)
        )
    })

    it("market orders can be submitted even if the order book is empty", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));
        
        let orderbook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
        assert(orderbook.length = 0)
        await truffleAssert.passes(
            dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 10)
        )
    })

    it("the ETH balance of the buyer should decreas with the filled price", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 100)
        dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 100)
        
        //mapping(address => mapping(bytes32 => uint256)) public balances;
        assert(balances[msg.sender]["ETH"] = balances[msg.sender]["ETH"] -= price)
    })

    it("the token balances of the limit order should decreas with the filled amounts", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()

        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 100)
        dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 100)
        
        //mapping(address => mapping(bytes32 => uint256)) public balances;
        assert(balances[link.address][web3.utils.fromUtf8("LINK")] = balances[link.address][web3.utils.fromUtf8("LINK")] -= amount)
    })

    it("filled limit orders should be removed from the order book", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 300)
        dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 300)

        let orderbook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
        assert(orderbook.length = 0)        
        
    })

    it("the token balance of the buyer should increase with filled amount", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 100)
        dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 100)
        
        //mapping(address => mapping(bytes32 => uint256)) public balances;
        assert(balances[msg.sender][web3.utils.fromUtf8("LINK")] = balances[msg.sender][web3.utils.fromUtf8("LINK")] += amount)
    })

    //the ETH balance of the seller should increase with filled amount
    it("the ETH balance of the buyer should decreas with the filled price", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);
        await dex.addToken(web3.utils.fromUtf8("Link"), link.address, {from: accounts[0]});
        await dex.deposit(100, web3.utils.fromUtf8("Link"));
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 100)
        dex.createMarketOrder(web3.utils.fromUtf8("LINK"), 0, 100)
        
        //mapping(address => mapping(bytes32 => uint256)) public balances;
        assert(balances[link.address]["ETH"] = balances[link.address]["ETH"] -= price)
    })    

})
1 Like

TOPIC: assertions

In Truffle, I installed the truffle assertions module with which I can use ā€œpassesā€ and ā€œrevertsā€ to check tests. But I can also use just ā€œassertā€ which is a built in Truffle test.

My question is: when do I use which?

Regards
Em

1 Like

Hey @Emmerich

The truffle-assertions module check the result of a function call, basically checks if the function passes or fails.
The assert function built in mocha checks if a condition is true or false (assert (10 == 10)).

So you can/ should use both of then, depending on what you need to verify.

Cheers,
Dani

Cool, thatā€™s great thanks!

But now I have another question:
Is each test graded individually, or do the earlier test results still hold true for later tests? For instance: if I deposit Eth in Test A, is that Eth still present in Test B, or do I need to re-deposit it?

Regards
Em

1 Like

My solution to the bubble sort assignment below.
I broke it out into a private function for cleaner code.

    /* sortOrders function should sort BUY Orders from highest price at [0] to lowest at [length -1]
    * should sort SELL orders from lowest price at [0] to highest at [length -1]
    * use bubble sort algorithm.
    * assume pre-ordered array, only one loop of bubble is necessary, new element always at position length -1
    */
    function _sortOrders(Order[] storage _orders, Side _side) private {
        
        if (_side == Side.BUY){ // sort BUY Orders from highest price at [0] to lowest at [length -1]
            bool sorting = true;
            uint i = _orders.length - 1; 

            while(sorting && i > 0){            
                if(_orders[i-1].price > _orders[i].price) { // swap if bigger
                    Order memory swap = _orders[i-1];
                    _orders[i-1] = _orders [i]; 
                    _orders[i] = swap;
                    i--; // on to the next
                }
                else { // found the right place in the array
                    sorting = false; // stop sort
                }
            }
        }
        
        if (_side == Side.SELL){ // sort SELL Orders from lowest price at [0] to highest at [length -1]
            bool sorting = true;
            uint i = _orders.length - 1; 

            while(sorting && i > 0){            
                if(_orders[i-1].price < _orders[i].price) { // swap if smaller
                    Order memory swap = _orders[i-1];
                    _orders[i-1] = _orders [i]; 
                    _orders[i] = swap;
                    i--; // on to the next
                }
                else { // found the right place in the array
                    sorting = false; // stop sort
                }
            }
        }
    }

1 Like

A question regarding the truffle tests.
My tests donā€™t pass, like @filipā€™s do, at the stage of having createLimitOrder implemented.
Hereā€™s my test file

dextest.js
// The user must have ETH deposited such that deposited eth >= buy order value
// The user must have enough tokens deposited such that token balance >= sell order amount
// The BUY order book should be ordered on price from highest to lowest starting at index 0
// The SELL order book should be ordered on price from lowest to highest starting at index 0
// The User should not be able to create limit orders for not supported tokens

const Dex = artifacts.require("Dex");
const Link = artifacts.require("Link");
const truffleAssert = require('truffle-assertions');

const Side = {
    BUY: 0,
    SELL: 1
};

const LINKB32 = web3.utils.fromUtf8("LINK");
const ETH10 = web3.utils.toWei('10', 'ether');

contract("Dex", accounts => {

    let dex;
    let link;

    before(async function(){
        dex = await Dex.deployed();
        link = await Link.deployed();
        await dex.addToken(LINKB32, link.address);
    });

    it("The user must have ETH deposited such that deposited eth >= buy order value", async ()=>{
        await truffleAssert.reverts ( 
            await dex.createLimitOrder(LINKB32, Side.BUY, 10, 1)
        );
        
        //top up and check if it passes
        await dex.addFunds({value: ETH10});
        await truffleAssert.passes ( 
            // Try same create, this time it should pass.
            await dex.createLimitOrder(LINKB32, Side.BUY, 10, 1)
        );    
    } );

    it("The user must have enough tokens deposited such that token balance >= sell order amount", async ()=>{
        await truffleAssert.reverts (
            //  create limit order - expect to fail.
            await dex.createLimitOrder(LINKB32, Side.SELL, 10, 1)
        );  
        
        await dex.deposit (10, LINKB32); //deposit 10 LINK

        await truffleAssert.passes (
            // This should pass now
            await dex.createLimitOrder(LINKB32, Side.SELL, 10, 1)
        ); 

    } );

    it("The BUY order book should be ordered on price from highest to lowest starting at index 0", async ()=>{
        const buyOrders = [1,5,3,2,6]; // order prices to create
        await link.approve(dex.address, 1000); // approve dex for deposit

        await dex.addFunds({value: ETH10});

        for (i=0; i < buyOrders.length; i++){ // create some BUY orders
            await dex.createLimitOrder(LINKB32, Side.BUY, 0.1, buyOrders[i]);
        }
        
        const orderBook = dex.getOrderBook();

        assert(orderBook.length > 0);
        // assert that each buy order in the array is smaller than the one before
        for (i=0; i < orderBook.length - 1; i++){
            const firstOrder = orderBook[i];
            const nextOrder = orderBook[i+1];
            assert(firstOrder.price > nextOrder.price);
        }
    } );

    it("The SELL  order book should be ordered on price from lowest to highest starting at index 0", async ()=>{
        const sellOrders = [1,5,3,2,6]; // order prices to create

        await dex.deposit(10, LINKB32); // deposit 10 Tokens so we can create sell orders

        for (i=0; i < sellOrders.length - 1; i++){ // create some BUY orders
            await dex.createLimitOrder(LINKB32, Side.SELL, 0.1, sellOrders[i]);
        }
        
        const orderBook = dex.getOrderBook();
        assert(orderBook.length > 0);

        // assert that each buy order in the array is smaller than the one before
        for (i=0; i < orderBook.length - 1; i++){
            const firstOrder = orderBook[i];
            const nextOrder = orderBook[i+1];
            assert(firstOrder.price < nextOrder.price);
        }
    } );

    it("The User should not be able to create limit orders for not supported tokens", async ()=>{
        const AAVEB32 = web3.utils.fromUtf8("AAVE");
        truffleAssert.reverts( //create BUY order of unlisted token should fail
            dex.createLimitOrder(AAVEB32, Side.BUY, 0.1, 1)
        );
        truffleAssert.reverts( //create SELL order of unlisted token should fail
            dex.createLimitOrder(AAVEB32, Side.SELL, 0.1, 1)
        );

        //now use LINK
        truffleAssert.passes( //create BUY order should succeed
            dex.createLimitOrder(LINKB32, Side.BUY, 0.1, 1)
        );
        truffleAssert.passes( //create SELL order of unlisted token should fail
            dex.createLimitOrder(LINKB32, Side.SELL, 0.1, 1)
        );
    } );

} );

Hereā€™s my contract:

dex.sol
pragma solidity 0.8.0;
pragma experimental ABIEncoderV2;

import "./Wallet.sol";

contract Dex is Wallet {

    using SafeMath for uint256;

    enum Side{
        BUY,
        SELL
    }
    
    struct Order {
        uint id;
        address trader;
        Side side;
        bytes32 ticker;
        uint amount;
        uint price;
    }

    mapping(bytes32 => mapping(uint => Order[])) OrderBook;

    uint256 public nextOrderId = 0;

    function getOrderBook (bytes32 ticker, Side side) public view returns (Order[] memory){
        return OrderBook[ticker][uint(side)];
    }

    function createLimitOrder (bytes32 ticker, Side side, uint amount, uint price) public returns (bool){
        if(side == Side.BUY){
            require(balances[msg.sender]["ETH"] >= amount.mul(price), "DEX: Insufficient funds for BUY order!");
        }
        else if(side == Side.SELL){
            require(balances[msg.sender][ticker] >= amount, "DEX: Insufficient funds for SELL order!");
        }

        Order[] storage orders = OrderBook[ticker][uint(side)];
        orders.push(
            Order(nextOrderId, msg.sender, side, ticker, amount, price)
        );

        _sortOrders(orders, side);

        nextOrderId++;
    }

    /* sortOrders function should sort BUY Orders from highest price at [0] to lowest at [length -1]
    * should sort SELL orders from lowest price at [0] to highest at [length -1]
    * use bubble sort algorithm.
    * assume pre-ordered array, only one loop of bubble is necessary, new element always at position length -1
    */
    function _sortOrders(Order[] storage _orders, Side _side) private {
        
        if (_side == Side.BUY){ // sort BUY Orders from highest price at [0] to lowest at [length -1]
            bool sorting = true;
            uint i = _orders.length - 1; 

            while(sorting && i > 0){            
                if(_orders[i-1].price > _orders[i].price) { // swap if bigger
                    Order memory swap = _orders[i-1];
                    _orders[i-1] = _orders [i]; 
                    _orders[i] = swap;
                    i--; // on to the next
                }
                else { // found the right place in the array
                    sorting = false; // stop sort
                }
            }
        }
        
        if (_side == Side.SELL){ // sort SELL Orders from lowest price at [0] to highest at [length -1]
            bool sorting = true;
            uint i = _orders.length - 1; 

            while(sorting && i > 0){            
                if(_orders[i-1].price < _orders[i].price) { // swap if smaller
                    Order memory swap = _orders[i-1];
                    _orders[i-1] = _orders [i]; 
                    _orders[i] = swap;
                    i--; // on to the next
                }
                else { // found the right place in the array
                    sorting = false; // stop sort
                }
            }
        }
    }

    function addFunds(address _address) public payable {
        balances[_address]["ETH"] = balances[_address]["ETH"].add(msg.value);
    }

    function getFunds(address _address) public view returns (uint256 funds_){
        return balances[_address]["ETH"];
    }
}

And hereā€™s the output of truffle test:

truffle test output
ruffle(develop)> test
WARNING: Missing strong random number source
Using network 'develop'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



  Contract: Dex
    1) The user must have ETH deposited such that deposited eth >= buy order value
    > No events were emitted
    2) The user must have enough tokens deposited such that token balance >= sell order amount
    > No events were emitted
    3) The BUY order book should be ordered on price from highest to lowest starting at index 0

    Events emitted during test:
    ---------------------------

    IERC20.Approval(
      owner: <indexed> 0x4fa345696c9d7b548ED7208Eb0aBF066f3d09fd2 (type: address),  
      spender: <indexed> 0xA1850bb936DdC884E4168E8715e551C64Be98c50 (type: address),
      value: 1000 (type: uint256)
    )


    ---------------------------
    4) The SELL  order book should be ordered on price from lowest to highest starting at index 0

    Events emitted during test:
    ---------------------------

    IERC20.Transfer(
      from: <indexed> 0x4fa345696c9d7b548ED7208Eb0aBF066f3d09fd2 (type: address),
      to: <indexed> 0xA1850bb936DdC884E4168E8715e551C64Be98c50 (type: address),  
      value: 10 (type: uint256)
    )

    IERC20.Approval(
      owner: <indexed> 0x4fa345696c9d7b548ED7208Eb0aBF066f3d09fd2 (type: address),
      spender: <indexed> 0xA1850bb936DdC884E4168E8715e551C64Be98c50 (type: address),
      value: 990 (type: uint256)
    )


    ---------------------------
    āˆš The User should not be able to create limit orders for not supported tokens

  Contract: Dex
    5) "before all" hook: prepare suite for "Should only be possible for owner to add tokens"


  1 passing (10s)
  5 failing

  1) Contract: Dex
       The user must have ETH deposited such that deposited eth >= buy order value:
     Error: Returned error: VM Exception while processing transaction: revert DEX: Insufficient funds for BUY order! -- Reason given: DEX: Insufficient funds for BUY order!.
      at Context.<anonymous> (test\dextest.js:32:23)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

  2) Contract: Dex
       The user must have enough tokens deposited such that token balance >= sell order amount:
     Error: Returned error: VM Exception while processing transaction: revert DEX: Insufficient funds for SELL order! -- Reason given: DEX: Insufficient funds for SELL order!.
      at Context.<anonymous> (test\dextest.js:46:23)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

  3) Contract: Dex
       The BUY order book should be ordered on price from highest to lowest starting at index 0:
     Error: invalid address (argument="address", value={"value":"10000000000000000000"}, code=INVALID_ARGUMENT, version=address/5.0.5) (argument="_address", value={"value":"10000000000000000000"}, code=INVALID_ARGUMENT, version=abi/5.0.7)
      at Context.<anonymous> (test\dextest.js:62:19)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

  4) Contract: Dex
       The SELL  order book should be ordered on price from lowest to highest starting at index 0:
     Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
      at Context.<anonymous> (test\dextest.js:85:23)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

  5) Contract: Dex
       "before all" hook: prepare suite for "Should only be possible for owner to add tokens":
     Uncaught AssertionError: Expected to fail with revert, but failed with: Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
      at fails (node_modules\truffle-assertions\index.js:152:13)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)



UnhandledRejections detected
Promise {
  <rejected> AssertionError: Expected to fail with revert, but failed with: Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
      at fails (node_modules\truffle-assertions\index.js:152:13)
      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
    showDiff: false,
    uncaught: true
  }
} AssertionError: Expected to fail with revert, but failed with: Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
    at fails (node_modules\truffle-assertions\index.js:152:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  showDiff: false,
  uncaught: true
}
Promise {
  <rejected> AssertionError: Expected to fail with revert, but failed with: Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
      at fails (C:\Users\rapha\Documents\NFT\ETH201\DEX\node_modules\truffle-assertions\index.js:152:13)
      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
    showDiff: false,
    uncaught: true
  }
} AssertionError: Expected to fail with revert, but failed with: Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
    at fails (C:\Users\rapha\Documents\NFT\ETH201\DEX\node_modules\truffle-assertions\index.js:152:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  showDiff: false,
  uncaught: true
}
Promise {
  <rejected> AssertionError: Failed with Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
      at passes (C:\Users\rapha\Documents\NFT\ETH201\DEX\node_modules\truffle-assertions\index.js:142:11)
      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
    showDiff: false,
    uncaught: true
  }
} AssertionError: Failed with Error: underflow (fault="underflow", operation="BigNumber.from", value=0.1, code=NUMERIC_FAULT, version=bignumber/5.0.8)
    at passes (C:\Users\rapha\Documents\NFT\ETH201\DEX\node_modules\truffle-assertions\index.js:142:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  showDiff: false,
  uncaught: true
}

If anyone can spot what Iā€™m missing that would be yuuuuge!!

1 Like

Hi @Emmerich

If you only use one instance of the contract throughout your tests, then the state variables remain the same (if you deposit 1 eth in the 1st test, your contract will still hold 1 eth the last tests).

Cheers,
Dani

thanks for taking the time and explaining, I do understand all that, its pretty straight forward once you understand it is a transaction and exchange, but my point is that there is an error in the video and for someone who is trying to understand it its not good, the video should be corrected as it is confusing.

1 Like

Hi Capplequoppe,

Did anyone reply to you somewhere else regarding that? Did you get your answer? Iā€™m also curious what is missing. Thanks

1 Like