Assignment - Limit Order Test

Yeah, that’s true. Thanks g. I’ll do it this way from now on!!

1 Like

yeah it makes debugging so much easier. a lot of time with tests the issue is with the test and not usually your actual SC code

1 Like

Good Evening!
Having a minor challenge with this entry

nextOrderId++;

Keep getting below error, let me know what is the best approach to correct it🤩Much appreciate your help!
ParserError: Expected identifier but got ‘++’
–> project:/contracts/dex.sol:79:16:
|
79 | nextOrderId++;
| ^^

1 Like
//The user must have ETH deposited such that eth>= buy order value
//the user must have enough tokens such that token balance > sell order amount
//the first order ([0]) in the BUY order should have the highest price

const { isTopic, isContractAddressInBloom } = require("web3-utils");
const truffleAssert = require("truffle-assertions")
//----------
let Dex = artifacts.require("Dex")

Contract("Dex", accounts =>{
    let dex = await Dex.deployed()
    it("Shouldnt be possible to make an order if eth is not deposited", async()=>{
        truffleAssert.reverts(
            dex.newOrder(0,web3.utils.fromUtf8("LINK"),2,1)
        )

    })
    it("Token balance must be higher than the sell order amount", async()=>{
        await truffleAssert.passes(
            dex.deposit(100, web3.utils.fromUtf8("LINK"))

        )
        await truffleAssert.reverts(
            dex.newOrder(1,web3.utils.fromUtf8("LINK"),200, 200)
        )

    })

    it("The orders should be oredered by price"), async () =>{
        await assert.newOrder(1,web3.utils.fromUtf8("LINK"),200, 200);
        await assert.newOrder(1,web3.utils.fromUtf8("LINK"),400, 400);
        await assert.newOrder(1,web3.utils.fromUtf8("LINK"),100, 100);
        await assert.newOrder(1,web3.utils.fromUtf8("LINK"),500, 500);
        
        assert(
            dex.getOrderBook(web3.utils.fromUtf8("LINK"),1)[0]>dex.getOrderBook(web3.utils.fromUtf8("LINK"),1)[1]
            
        )
    }





})

1 Like

Here are few unit tests for dex.sol that I wrote.

it("should allow buy order with sufficient eth balance", async () => {
		const dex = await Dex.deployed();
		const ethBalance = web3.eth.getBalance(accounts[0]).then(result => web3.utils.fromWei(result,"ether"));
		const numTokens = ethBalance - 1;

		await truffleAssert.passes(
			dex.createBuyOrder(web3.utils.fromUtf8("TRAV"), numTokens, 1)
		);
	});

	it("should not allow buy order with insufficient eth balance", async () => {
		const dex = await Dex.deployed();
		const ethBalance = web3.eth.getBalance(accounts[0]).then(result => web3.utils.fromWei(result,"ether"));
		const numTokens = ethBalance + 1;

		await truffleAssert.reverts(
			dex.createBuyOrder(web3.utils.fromUtf8("TRAV"), numTokens, 1)
		);
	});

	it("should order buy orders from highest to lowest", async () => {
		const dex = await Dex.deployed();
		const orderBook = await dex.orderBook(web3.utils.fromUtf8("TRAV"), 0);

		let ordersAreInOrder = true;
		let lastOrder = null;

		for(i = 0; i < orderBook.length; i++) {
			if (! lastOrder) {
				lastOrder = orderBook[i];
				continue;
			}

			if ( orderBook[i].price > lastOrder.price ) {
				ordersAreInOrder = false;
				break;
			}
			lastOrder = orderBook[i];
		}

		assert.equal(ordersAreInOrder, true);
	});
1 Like
const Dex = artifacts.require("Dex");
const Link = artifacts.require("Link");
const truffleAssert = require("truffle-assertions");

contract("Limit Order", accounts => {

    //The user must have ETH deposited such that deposited eth >= buy order value
    it ("should throw an error if ETH balance is too low when creating BUY limit order", async () => {
        let dex = await Dex.deployed();
        await truffleAssert.reverts(
            dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 10, 1)
        )
        
        await dex.depositEth({value: 10});   
        await truffleAssert.passes(
            dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 10, 1)
        );
    })

    //The user must have enough tokens deposited such that token balance >= sell order amount
    it("should throw an error if token balance is too low when creating SELL limit order", 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);
        await dex.deposit(100, web3.utils.fromUtf8("LINK"));

        await truffleAssert.reverts(
            dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 200, 1)
        );

        await truffleAssert.passes(
            dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 50, 1)
        );
    })

    //The BUY order book should be ordered on price from highest to lowest starting at index 0
    it("The BUY order book should be ordered on price from highest to lowest starting at index 0", async () => {
        let dex = await Dex.deployed();
        await dex.depositEth({value: 100});
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 40);
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 10);
        await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 20);

        let orderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
        for (let i = 0; i < orderBook.length - 1; i++) {
            assert(orderBook[i].price >= orderBook[i + 1].price, "Wrong Order in Buy Book");
        }
    })

    //The SELL order book should be ordered on price from lowest to highest starting at index 0
    it("The SELL order book should be ordered on price from lowest to highest starting at index 0", async () => {
        let dex = await Dex.deployed();
        let link = await Link.deployed();
        await link.approve(dex.address, 500);
        await dex.deposit(100, web3.utils.fromUtf8("LINK"));
        await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 40);
        await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 10);
        await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 20);   
        
        let orderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 1);
        for (let i = 0; i < orderBook.length - 1; i++) {
            assert(orderBook[i].price <= orderBook[i + 1].price, "Wrong Order in Sell Book");
        }
    })
})
1 Like

hey @HRMS2021 can you paste the entire function. need to have some context by seeing rest of your code to try see what the error is

Hello @mcgrane5 thanks so much for your reply! I hope you are having a fantastic Saturday.

Here is the complete code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.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;
    uint filled;

    }

    uint public nextOrderId = 0;

    mapping(bytes32 => mapping(uint => Order[])) public orderBook;
    
    function getOrderBook(bytes32 ticker, Side side) view public returns(Order[] memory){
        return orderBook[ticker][uint(side)];

    }

    function createLimitOrder(Side side, bytes32 ticker, uint amount, uint price) view public {
       if(side == Side.BUY) {
           require(balances[msg.sender]["ETH"] >= amount.mul(price));

     }
     else if(side == Side.SELL) {
           require(balances[msg.sender][ticker] >= amount);

     }
    //list of order book
     Order[] storage orders = orderBook[ticker][uint(side)];
     orders.push(
     Order(nextorderId, msg.sender, side, ticker, amount, price)
     
     );
     //use a sorting technique called bubble sort
     //loop
     if(side == Side.Buy) {
         while(i > 0) {
             if(orders[i - 1].price > orders[i].price){
                 break;
             }
             Order memory orderToMove = orders[i - 1];
             Orders[i - 1] = orders[i];
             orders[i] = orderToMove;
             i--;

         }

        }
        //loop
        else if(side == Side.Sell) {
            while(i > 0) {
              if(orders[i - 1].price < orders[i].price) {
                  break;
            }
        }
            Order memory orderToMove = orders[i - 1];
             Orders[i - 1] = orders[i];
             orders[i] = orderToMove;
             i--;
        }
    }
    nextOrderId++;
    
    }
    
    function createMarketOrder(Side side, bytes32 ticker, uint amount) public {
        if if(side == Side.Sell){
        require(balances[msg.sender][ticker] >= amount, "Insufficient balance");

        uint orderBookSide;
        if(side == Side.Buy) {
            orderBookSide = 1;
        }
        else{
            orderBookSide = 0;
        }
        Order[] storage orders = orderBook[ticker][orderBookSide];

        uint totalFilled = 0;

        for (uint256 i = 0; i < orders.length && totalFilled < amount; i++); }
        uint leftToFill = amount.sub(totalFilled);
        uint availableToFill = orders[i].amount.sub(orders[i].filled);
        uint filled = 0;
        if(availableToFill > leftToFill)}
            filled = leftToFill; //fill market order
        }
        else{
            filled = availableToFill;// as much as is available in order [i]
        }
        totalFilled = availableToFill.add(filled);
        orders[i].filled = orders[i].filled.add(filled);
        uint cost = filled.mul(orders[i].price);
        
        if(side == Side.Buy){
            //Verify that the buyer has enough ETH to cover the purschase (require)
            require(balances[msg.sender] ["ETH"] => cost),
            //msg.sender is the Buyer
            balances[msg.sender] [ticker] = balances[msg.sender][ticker].add(filled);
            balances[msg.sender]["ETH"] = balances[msg.sender]["ETH"].sub(cost);

            balances[orders[i].trader][ticker] = balances[orders[i].trader][ticker].sub(filled);
            balances[orders[i].trader]["ETH"] = balances[orders[i].trader]["ETH"].add(cost);
         }
        else if(side == Side.Sell){
            //msg.sender is the seller
            balances[msg.sender][ticker] = balances[msg.sender][ticker].sub(filled);
            balances[msg.sender]["ETH"] = balances[msg.sender]["ETH"].add(cost);

            balances[orders[i].trader][ticker] = balances[orders[i].trader][ticker].add(filled);
             balances[orders[i].trader]["ETH"] = balances[orders[i].trader]["ETH"].sub(cost);
            }
            
        }
            //Remove 100% filled orders from the orderbook
        while(orders.length > 0 && orders[0].filled == orders[0].amount){
            //Remove the top element in the orders array by overwriting every element
            // with the next element in the order list
            for (uint256 i = 0; i < orders.length - 1; i++) {
                orders[i] = orders[i + 1];
            }
            orders.pop();
        }
    
    }

}

1 Like

hey @HRMS2021. your errors were all syntax errors. you need to be careful when writing code. you had lots of typos in this code the reason fro the counter parse error from above was because you had an extra bracket in the limit order function with no closing bracket so this caused the error. there were other things like commas at the end of lines instead of colons and even having and if statement like this if () }… } instead of poperly atching brackets like if () {…} . i would recomment intellisense if you are using vs code it will help u catch these erors

1 Like

Thank you @mcgrane5 I will go over and make changes accordingly.

1 Like

the above code is the fixed version

1 Like

My answer for the Dex test is as follows.

MercDexTest2.js code
const mercuryDex = artifacts.require("mercuryDex");
const testToken = artifacts.require("testToken");
const truffleAssert = require('truffle-assertions');

contract("mercuryDex", accounts => {
  
  //The user should have enough Eth to cover any buy order.
  it("should handle inadequate Eth for limit buy order correctly", async () => {

    let mercDex = await mercuryDex.deployed();
    let testa = await testToken.deployed();

    await mercDex.addToken(web3.utils.fromUtf8("TSTT"), testa.address);
    await testa.approve(mercDex.address, 5000);
    await mercDex.deposit(2000, web3.utils.fromUtf8("TSTT"));

    await truffleAssert.reverts(
      //side, ticker, amount, price
      mercDex.placeLimitOrder(0, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('1', 'ether'))
    );

  })

  //The user should have enough tokens to satisfy any sell order.
  it("should handle inadequate tokens for limit sell order correctly", async () => {

    let mercDex = await mercuryDex.deployed();

    await truffleAssert.reverts(
      //side, ticker, amount, price
      mercDex.placeLimitOrder(1, web3.utils.toHex("TSTT"), 20000, web3.utils.toWei('0.05', 'ether'))
    );

  })

  //The buy orderbook should be arranged from highest to lowest price, with highest price at index [0].
  it("should arrange buy orderbook from highest to lowest price", async () => {

    let mercDex = await mercuryDex.deployed();

    await mercDex.placeLimitOrder(0, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.01', 'ether'));
    await mercDex.placeLimitOrder(0, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.02', 'ether'));
    await mercDex.placeLimitOrder(0, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.03', 'ether'));
    let buyOrderBook = await mercDex.getOrderBook(web3.utils.toHex("TSTT"), 0);
    for (i = 1; i < buyOrderBook.length, i ++){
      assert(buyOrderBook[i].price <= buyOrderBook[i-1].price)
    }

  })

  //The sell orderbook should be arranged from lowest to highest price, with lowest price at index [0].
  it("should arrange sell orderbook from lowest to highest price", async () => {

    let mercDex = await mercuryDex.deployed();

    await mercDex.placeLimitOrder(1, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.01', 'ether'));
    await mercDex.placeLimitOrder(1, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.02', 'ether'));
    await mercDex.placeLimitOrder(1, web3.utils.toHex("TSTT"), 300, web3.utils.toWei('0.03', 'ether'));
    let buyOrderBook = await mercDex.getOrderBook(web3.utils.toHex("TSTT"), 1);
    for (i = 1; i < buyOrderBook.length, i ++){
      assert(buyOrderBook[i].price >= buyOrderBook[i-1].price)
    }

  })


})

Had to consulate the forum for this one, but here my solution:

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

contract("Dex", accounts => {

    let dex = await Dex.deployed()
    let link = await Link.deployed()

    //The user must have ETH deposited such that deposited eth >= buy order value
    it("should only be possible for user to create a BUY order that he can afford", async () => {
      
        await truffleAssert.reverts(// createLimitOrder(ticker,side,price,amount)
                dex.createLimitOrder(web3.utils.fromUtf8("LINK"),0,3,1)
                            )
        
        await dex.depositETH({value: 3});

        await truffleAssert.passes(// createLimitOrder(ticker,side,price,amount)
                dex.createLimitOrder(web3.utils.fromUtf8("LINK"),0,3,1)
                    )
    })

    //The user must have enough tokens deposited such that token balance > sell order amount
    it("should only be possible for user to create a SELL order that he can afford", async () => {
      
        await truffleAssert.reverts(// createLimitOrder(ticker,side,price,amount)
                dex.createLimitOrder(web3.utils.fromUtf8("LINK"),1,3,1)
                            )
        
        await dex.addtoken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]})
        await link.approve(dex.address, 3)                
        await dex.deposit(3, web3.utils.fromUtf8("LINK"));

        await truffleAssert.passes(// createLimitOrder(ticker,side,price,amount)
                dex.createLimitOrder(web3.utils.fromUtf8("LINK"),1,3,1)
                            )
    })


    //The BUY order book should be ordered on price from highest to lowest starting at index 0  
    it("should be that the BUY order book is ordered in descending order starting at index 0", async () => {
      
        const invalid_order_array = [12,13,1,15,9,11];
        await dex.depositETH({value: 99});

        for (let i = 0; i< invalidOrder.length; i++){ //loop through array to create BUY orders
            await dex.createLimitOrder(web3.utils.fromUtf8("LINK"),0 //BUY-side
                                                                ,invalid_order_array[i] //price from array
                                                                ,1); //amount
        }

        const orderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
        assert.equals(orderBook.length, invalid_order_array.length, "Missing orders in the order book");
        for (let i = 0; i< orderBook.length -1; i++){
            assert(orderBook[i].price >= orderBook[i+1].price, "Invalid price order")
        }
        
    })


})
1 Like

nice solution @Thunarson7999. there is a few thing syou can and hsould maybe do in order to make your tests even more robust so that they can catch even more errros. the first test that you should do is to also make sue that the orderbook is ordered in ascending order startig at index 0 on the sell side.

this i sbecaus in orerbook dexes the orderbook is ordered oppotise on the buy sell than the sell side. so for one more test you should begin with something like

it("should be that the SELL order book is ordered in ascending order starting at index 0", async () => {
      
        const invalid_order_array = [12,13,1,15,9,11];
        await dex.depositETH({value: 99});

        for (let i = 0; i< invalidOrder.length; i++){ //loop through array to create SELL orders
            await dex.createLimitOrder(web3.utils.fromUtf8("LINK"),1 //SELL-side
                                                                ,invalid_order_array[i] //price from array
                                                                ,1); //amount
        }

        const orderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 1);
        assert.equals(orderBook.length, invalid_order_array.length, "Missing orders in the order book");
        for (let i = 0; i > orderBook.length -1; i++){
            assert(orderBook[i].price >= orderBook[i+1].price, "Invalid price order")
        }
        
    })

hoever i do really like of your use of a for loop to execute the creation of limit orders in one line of code and it takes up less space so well done for this. you could even make this line better by using Object.forEach whichs is the modern javascript syntax used to do arrays so it would look like

inavlidList.forEach((sellOrder, index) => {
    await dex.createLimitOrder(web3.utils.fromUtf8("LINK") ,sellOrder,1); 
}) 

this notation is even cleaner in my opinion.

the next thing you should do is to really think about what other tests you could do. i have a challendge for you try write 8 tests and think of ones you can write. i will give an example, for two. try to add a token to your dex with a token that doesnt exist eg fake token symbol. and make sure this reverts to test your addToken function. likewise you should then make a test to make sure you cannot create limit orders with this fake token. so for the testing of a fke token it could be like

 it("should only not be possible to add tokens that do not exists", async () => {
      
       //revert with fake symbol
        await truffleAssert.reverts(
               dex.addtoken(web3.utils.fromUtf8("FAKE"), link.address, {from: accounts[0]})
         )

        reverts withh fake addresss
         await truffleAssert.reverts(
               dex.addtoken(web3.utils.fromUtf8("LINK"), address(0), {from: accounts[0]})
        )

        //passes with correc ttoken
        await truffleAssert.passes(
               dex.addtoken(web3.utils.fromUtf8("LINK"), address(0), {from: accounts[0]})
        )
    })

then use this to do the limit order one too. otherwise great solution and very good work

I think I was able to write the structure of the tests, but the functionality is where I would need more help for now. Here is my code for the structure of the tests.

//SPDX-License-Identifier: MIT

const Dex = artifacts.require(‘Dex’);
const truffleAssert = require(‘truffle-assertions’);

contract(‘Dex’, (accounts) => {
it(‘user must have sufficient ETH for buy order’, async () => {
let dex = await Dex.deployed();
await truffleAssert.passes(
//code
);
});
it(‘user must have sufficient token amount for sell order’, async () => {
let dex = await Dex.deployed();
await truffleAssert.passes(
//code
);
});
it(‘the first buy order book value should have the highest price’, async () => {
let dex = await Dex.deployed();
await truffleAssert.passes(
//code
);
});
});

//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 first order ([0]) in the BUY order book should have the highest price

1 Like

Hey folks :slightly_smiling_face:,
To resolve that problem I’ve decided to add the Ethereum token to token and array.push to the createLimitOrder without condidtions.

tokens.sol
contract Ethereum is ERC20{
    string TICKER = "ETH";
    constructor() ERC20("Ethereum", "ETH") {
        _mint(_msgSender(), 1000);
    }
}
Dex.sol:createLimitOrder()
    function createLimitOrder(Side side, bytes32 ticker, uint amount, uint price) public returns (Order memory) {
        Order memory resultOrder = Order(orderId++, msg.sender, side, ticker, amount, price);
        orderBook[ticker][uint(side)].push(resultOrder);

        return resultOrder;
    }
dex_test.sol
const Dex = artifacts.require('Dex');
const Link = artifacts.require('Link');
const Ethereum = artifacts.require('Ethereum');
const truffleAssert = require('truffle-assertions');

contract('Dex', async (accounts) => {
    const OWNER_ACCOUNT_ID = 0;
    const ACCOUNT_ID = 0;
    const ORDER_AMOUNT = 10;
    const NORMAL_ORDER_PRICE = 10;
    const LOWER_ORDER_PRICE = 1;
    const DEPOSIT_AMOUNT = ORDER_AMOUNT * NORMAL_ORDER_PRICE;
    const TOO_BIG_ORDER_PRICE = DEPOSIT_AMOUNT * 10;
    const ETH_TICKER = web3.utils.fromUtf8('ETH');
    const LINK_TICKER = web3.utils.fromUtf8('LINK');

    describe('The user:', async () => {
        it('should have ETH deposited such that eth >= buy order value', async () => {
            const dex = await Dex.deployed();
            const ethereum = await Ethereum.deployed();

            await dex.addToken(ETH_TICKER, ethereum.address, {
                from: accounts[OWNER_ACCOUNT_ID],
            });

            await truffleAssert.reverts(
                dex.createLimitOrder(
                    Dex.Side.BUY,
                    LINK_TICKER,
                    ORDER_AMOUNT,
                    TOO_BIG_ORDER_PRICE,
                    { from: accounts[ACCOUNT_ID] }
                )
            );

            await ethereum.approve(dex.address, DEPOSIT_AMOUNT);
            await dex.deposit(DEPOSIT_AMOUNT, ETH_TICKER);

            await truffleAssert.passes(
                dex.createLimitOrder(
                    Dex.Side.BUY,
                    LINK_TICKER,
                    ORDER_AMOUNT,
                    NORMAL_ORDER_PRICE,
                    { from: accounts[ACCOUNT_ID] }
                )
            );
        });

        it('should have enough tokens to buy sell and balance >= sell amount', async () => {
            const dex = await Dex.deployed();
            const link = await Link.deployed();
            const ethereum = await Ethereum.deployed();

            await dex.addToken(ETH_TICKER, ethereum.address, {
                from: accounts[OWNER_ACCOUNT_ID],
            });
            await dex.addToken(ETH_TICKER, link.address, {
                from: accounts[OWNER_ACCOUNT_ID],
            });

            await truffleAssert.reverts(
                dex.createLimitOrder(
                    Dex.Side.SELL,
                    LINK_TICKER,
                    ORDER_AMOUNT,
                    TOO_BIG_ORDER_PRICE,
                    { from: accounts[ACCOUNT_ID] }
                )
            );

            await link.approve(dex.address, DEPOSIT_AMOUNT);
            await dex.deposit(DEPOSIT_AMOUNT, LINK_TICKER);

            await truffleAssert.passes(
                dex.createLimitOrder(
                    Dex.Side.SELL,
                    LINK_TICKER,
                    ORDER_AMOUNT,
                    NORMAL_ORDER_PRICE,
                    { from: accounts[ACCOUNT_ID] }
                )
            );
        });
    });
    describe('The order book:', async () => {
        it('should be sorted from highest to lowest price', async () => {
            const dex = await Dex.deployed();
            const link = await Link.deployed();
            const ethereum = await Ethereum.deployed();
            const LINK_TICKER = web3.utils.fromUtf8('LINK');

            await dex.addToken(ETH_TICKER, ethereum.address, {
                from: accounts[OWNER_ACCOUNT_ID],
            });
            await dex.addToken(LINK_TICKER, link.address, {
                from: accounts[OWNER_ACCOUNT_ID],
            });
            await link.approve(dex.address, DEPOSIT_AMOUNT);
            await dex.deposit(DEPOSIT_AMOUNT, LINK_TICKER);

            await dex.createLimitOrder(
                Dex.Side.SELL,
                LINK_TICKER,
                ORDER_AMOUNT,
                NORMAL_ORDER_PRICE,
                { from: accounts[ACCOUNT_ID] }
            );
            await dex.createLimitOrder(
                Dex.Side.SELL,
                LINK_TICKER,
                ORDER_AMOUNT,
                LOWER_ORDER_PRICE,
                { from: accounts[ACCOUNT_ID] }
            );

            const upperSellOrder = (await dex.getOrderBook(LINK_TICKER, Dex.Side.SELL))[0];

            assert.equal(upperSellOrder.price, LOWER_ORDER_PRICE);
        });
    });
});

2 Likes
dextest.js
const Dex = artifacts.require("Dex")
const Pyjama = artifacts.require("Pyjama")
const truffleAssert = require('truffle-assertions')

contract("Dex", accounts => {
    it("should only be possible to add limit buy order if you have enough ETH", async() => {
        let dex = await Dex.deployed()
        let pyjama = await Pyjama.deployed()
        await dex.addToken(web3.utils.fromUtf8("ETH"), eth.address, {from: accounts[0]})
        await dex.addToken(web3.utils.fromUtf8("PYC"), pyjama.address, {from: accounts[0]})
        await dex.deposit(10, web3.utils.fromUtf8("ETH"),{value: web3.utils.toWei("10", "ether")})
        truffleAssert.reverts(dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY, 20, 1))
        truffleAssert.passes(dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY, 10, 1))
    })

    it("should only be possible to add limit sell order if you have enough tokens", async() => {
        let dex = await Dex.deployed()
        let pyjama = await Pyjama.deployed()
        await dex.addToken(web3.utils.fromUtf8("ETH"), eth.address, {from: accounts[0]})
        await dex.addToken(web3.utils.fromUtf8("PYC"), pyjama.address, {from: accounts[0]})
        await dex.deposit(10, web3.utils.fromUtf8("PYC"))
        truffleAssert.reverts(dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL, 20, 1))
        truffleAssert.passes(dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL, 10, 1))
    })

    it("should order buy orders correctly in order book", async() => {
        let dex = await Dex.deployed()
        let pyjama = await Pyjama.deployed()
        await dex.addToken(web3.utils.fromUtf8("ETH"), eth.address, {from: accounts[0]})
        await dex.addToken(web3.utils.fromUtf8("PYC"), pyjama.address, {from: accounts[0]})
        await dex.deposit(10, web3.utils.fromUtf8("ETH"),{value: web3.utils.toWei("10", "ether")})
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY,  5, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY, 1, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY, 3, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.BUY, 1, 1)
        let orderBook = await dex.getOrderBook(web3.utils.fromUtf8("PYC"), side.BUY)
        assert.equal(orderBook[0].price.toNumber(), 5)
        assert.equal(orderBook[1].price.toNumber(), 3)
        assert.equal(orderBook[2].price.toNumber(), 1)
        assert.equal(orderBook[2].amount.toNumber(), 2)
    })

    it("should order sell orders correctly in order book", async() => {
        let dex = await Dex.deployed()
        let pyjama = await Pyjama.deployed()
        await dex.addToken(web3.utils.fromUtf8("ETH"), eth.address, {from: accounts[0]})
        await dex.addToken(web3.utils.fromUtf8("PYC"), pyjama.address, {from: accounts[0]})
        await dex.deposit(10, web3.utils.fromUtf8("PYC"))
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL,  5, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL, 1, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL, 3, 1)
        await dex.createLimitOrder(web3.utils.fromUtf8("PYC"), side.SELL, 1, 1)
        let orderBook = await dex.getOrderBook(web3.utils.fromUtf8("PYC"), side.SELL)
        assert.equal(orderBook[0].price.toNumber(), 1)
        assert.equal(orderBook[1].price.toNumber(), 3)
        assert.equal(orderBook[2].price.toNumber(), 5)
        assert.equal(orderBook[0].amount.toNumber(), 2)
    })

    it("should handle tickers that dont exist correctly", async() => {
        let dex = await Dex.deployed()
        let pyjama = await Pyjama.deployed()
        truffleAssert.reverts(dex.createLimitOrder(web3.utils.fromUtf8("ABC"), side.SELL, 20, 1))
    })
})
1 Like

Hey guys, here’s my solution to this assignment considering the following createLimitOrder function header:

function createLimitOrder(bytes32 ticker, Side side, uint amount, uint price)
const Link = artifacts.require("Link");
const Dex = artifacts.require("Dex");
const truffleAssert = require('truffle-assertions');


contract("Dex", accounts => {

    before(async function(){
        dex = await Dex.deployed();
        link = await Link.deployed();
        await dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]})
    });
    
    //The user must have ETH deposited such that deposited eth >= buy order value
    it("eth deposited must be >= buy order value", async () => {
        
        await dex.depositETH({value:100});
        await truffleAssert.reverts(
            dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 0, 1, 200)
        )
        await truffleAssert.passes(
            dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 0, 1, 100)
        )
        
    })


    //The user must have enough tokens deposited such that token balance >= sell order amount
    it("token deposited must be >= sell order value", async () => {
        await link.approve(dex.address, 1000);
        await dex.deposit(100, web3.utils.fromUtf8("LINK"))
        await truffleAssert.reverts(
            dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 1, 200, 2)
        )
        await truffleAssert.passes(
            dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 1, 100, 2)
        )
        
    })


    //The BUY order book should be ordered in price from highest to lowest starting at index 0
    it("buy order book should be ordered in price from highest to lowest starting at index 0", async () => {
        const priceArray = [100, 50, 40, 20, 12, 18];
        await dex.depositETH({value:1000});
        for (let i = 0; i < priceArray.length; i++) {
            await dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 0, 1, priceArray[i]);
        }
        const linkBuyOrderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"),0)
        for(let orderIndex = 0; orderIndex < linkBuyOrderBook.length - 1; orderIndex ++) {
            assert(linkBuyOrderBook[orderIndex].price >= linkBuyOrderBook[orderIndex + 1].price)
        }

    })

    //The SELL order book should be ordered in price from lowest to highest starting at index 0
    it("sell order book should be ordered in price from lowest to highest starting at index 0", async () => {
        const priceArray = [100, 50, 40, 20, 12, 18];
        await link.approve(dex.address, 100);
        await dex.deposit(100, web3.utils.fromUtf8("LINK"))
        for (let i = 0; i < priceArray.length; i++) {
            await dex.createLimitOrder(web3.utils.fromUtf8("LINK"), 1, 1, priceArray[i]);
        }
        const linkSellOrderBook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"),1)
        for(let orderIndex = 0; orderIndex < linkSellOrderBook.length - 1; orderIndex ++) {
            assert(linkSellOrderBook[orderIndex].price <= linkSellOrderBook[orderIndex + 1].price)
        }
    })

})
1 Like
const Dex = artifacts.require("DEX")
const Link = artifacts.require("Link")
const { BN, expectEvent, expectRevert, constants } = require('@openzeppelin/test-helpers');
const { web3 } = require("@openzeppelin/test-helpers/src/setup");
const { expect, assert } = require("chai");



//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


contract("DEX", accounts => {
 
  
      it("should revert if ETH < BUY order value", async function(){
            let dex = await Dex.deployed()
            await expectRevert.unspecified(dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"),5,100))
        })

      it("should revert if token balance < sell order amount", async function(){
        let link = await Link.deployed()
        let dex = await Dex.deployed()
        await link.approve(dex.address, 500)
        await dex.addToken(web3.utils.fromUtf8("LINK"), link.address)
        await dex.deposit(50,web3.utils.fromUtf8("LINK"),{from:accounts[0]})
        await expectRevert.unspecified(dex.createLimitOrder(1,web3.utils.fromUtf8("LINK"),200,4))
        })
         it("should work if ETH >= BUY order value", async function(){
         
            let dex = await Dex.deployed()
            await dex.sendTransaction({value:web3.utils.toWei("5","ether"), from:accounts[0]})
            await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"),5,1)
            let orderBooks = await dex.getOrderBook(web3.utils.fromUtf8("LINK"),0)
            console.log("order books", orderBooks, orderBooks.length)
            expect(orderBooks.length).to.be.greaterThan(0)
        })

         it("should work if token balance >= sell order amount", async function(){
            let link = await Link.deployed()
            let dex = await Dex.deployed()
            await link.approve(dex.address, 500)
            await dex.addToken(web3.utils.fromUtf8("LINK"), link.address)
            await dex.deposit(100,web3.utils.fromUtf8("LINK"),{from:accounts[0]})
            await dex.createLimitOrder(1,web3.utils.fromUtf8("LINK"), 100,2)
            let orderBooks = await dex.getOrderBook(web3.utils.fromUtf8("LINK"),1)
            console.log("order books", orderBooks, orderBooks.length)
            expect(orderBooks.length).to.be.greaterThan(0)
        
        })

        it("BUY order sorted based on price from highest to lowest", async function(){
            let dex = await Dex.deployed()
           await dex.sendTransaction({value:web3.utils.toWei("5","ether"), from:accounts[0]})
           await dex.sendTransaction({value:web3.utils.toWei("2","ether"), from:accounts[1]})
           await dex.sendTransaction({value:web3.utils.toWei("4","ether"), from:accounts[0]})
          
            await dex.createLimitOrder( 0, web3.utils.fromUtf8("LINK"),1,70) // createLimitOrder would push the new order and return an Order struct
            await dex.createLimitOrder( 0, web3.utils.fromUtf8("LINK"),3,250)
            await dex.createLimitOrder( 0, web3.utils.fromUtf8("LINK"),4,170)
            let buyOrders = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
           console.log(buyOrders)
            for(let i=0; i< buyOrders.length - 1;i++){
                  expect(parseInt(buyOrders[i].price)).to.be.greaterThanOrEqual(parseInt(buyOrders[i+1].price))
            }
        })
        it("SELL order sorted based on price from lowest to highest", async function(){
            let dex = await Dex.deployed()
            let link = await Link.deployed()
            await dex.addToken(web3.utils.fromUtf8("LINK"), link.address)
            await link.approve(dex.address, 2500,{from:accounts[0]})
            await dex.deposit(700,web3.utils.fromUtf8("LINK"),{from:accounts[0]})
            await dex.createLimitOrder( 1,web3.utils.fromUtf8("LINK"),300,3) // createLimitOrder would push the new order and return an Order struct
            await dex.createLimitOrder(1,web3.utils.fromUtf8("LINK"),150,2)
            await dex.createLimitOrder( 1,web3.utils.fromUtf8("LINK"),250,5)
            let sellOrders = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 1);
            console.log("Sell orders",sellOrders)
            for(let i=0; i< sellOrders.length - 1;i++){
                expect(parseInt(sellOrders[i].price)).to.be.lessThanOrEqual(parseInt(sellOrders[i+1].price))
            }
        })
})

Hi, my attempt.

// the user must have ETH deposited such that eth >=  buy order value 
// the user must have enough tokens deposited such that token balance > sell order amount
// the BUY order must be ordered on price from the highest to lowest starting at index 0
// the SELL order must be ordered on price from the lowest to highest starting at index 0
// the BUY order must be processed when the prices of BUY and SELL orders matches   
// once an order is completed (i.e token amount of order reach zero) it must be delete from the orderbook

const Dex = artifacts.require("Dex")
const Token = artifacts.require("MyToken");
const TruffleAssert = require('truffle-assertions')
contract ("Dex", accounts => {
    it("Must have enough ETH",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        // have to create a payable  deposit function to deposit ether
        await dex.deposit({value: web3.utils.toWei('5'), from: accounts[1]})
        await TruffleAssert.reverts(
            dex.createLimitOrder(100,0.55,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[1]})
        )
        await dex.deposit({value: web3.utils.toWei('1'), from: accounts[1]})
        await TruffleAssert.passes(
            dex.createLimitOrder(100,0.55,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[1]})
        )
    })    
 
    it("Must have enough Token",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        await TruffleAssert.reverts(
            dex.createLimitOrder(50,0.001,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[2]})
        )
        await token.transfer(accounts[2],65,{from: accounts[0]})
        await dex.deposit(web.utils.fromUtf8("MTK"),40,{from: accounts[2]})
        await TruffleAssert.reverts(
            dex.createLimitOrder(50,0.001,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[2]})
        )
        await TruffleAssert.passes(
            dex.createLimitOrder(30,0.001,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[2]})
        )
    })    

       
    it("the buy orders must be ordered on price from highest to lowest",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        await dex.deposit({value: web3.utils.toWei('5'), from: accounts[0]})
        await dex.deposit({value: web3.utils.toWei('3'), from: accounts[1]})
        await dex.deposit({value: web3.utils.toWei('8'), from: accounts[2]})
        await dex.deposit({value: web3.utils.toWei('10'), from: accounts[3]})
        dex.createLimitOrder(50,0.001,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[1]})
        dex.createLimitOrder(100,0.03,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[2]})
        dex.createLimitOrder(10,0.005,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[0]})
        dex.createLimitOrder(110,0.05,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[3]})
        var BuyOrder = await dex.getOrderBook(web.utils.fromUtf8("MTK"),dex.Side.BUY)
        for(let i = 0; i<BuyOrder.length - 1; i++){
            assertUint.isAbove(BuyOrder[i].price,BuyOrder[i+1].price,"BUY orders not arranged correctly")
        }
    })    


    it("the sell orders must be ordered on price from lowest to highest",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        await token.transfer(accounts[1],150,{from: accounts[0]})
        await token.transfer(accounts[2],150,{from: accounts[0]})
        await token.transfer(accounts[3],150,{from: accounts[0]})
        await dex.deposit(web.utils.fromUtf8("MTK"),20,{from: accounts[0]})
        await dex.deposit(web.utils.fromUtf8("MTK"),50,{from: accounts[1]})
        await dex.deposit(web.utils.fromUtf8("MTK"),150,{from: accounts[2]})
        await dex.deposit(web.utils.fromUtf8("MTK"),110,{from: accounts[3]})
        dex.createLimitOrder(50,0.001,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[1]})
        dex.createLimitOrder(100,0.03,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[2]})
        dex.createLimitOrder(10,0.005,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[0]})
        dex.createLimitOrder(110,0.05,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[3]})
        var SellOrder = await dex.getOrderBook(web.utils.fromUtf8("MTK"),dex.Side.SELL)
        for(let i = 0; i<SellOrder.length-1; i++){
            assertUint.isBellow(SellOrder[i].price,SellOrder[i+1].price,"SELL orders not arranged correctly")
        }
    })

    it("must process matching orders",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        await dex.deposit(web.utils.fromUtf8("MTK"),1000,{from: accounts[0]})
        dex.createLimitOrder(500,0.005,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[0]})
        await dex.deposit({value: web3.utils.toWei('10'), from: accounts[1]})
        var before_buyerbalance = await dex.balances(accounts[1],web.utils.fromUtf8("MTK"))
        var before_sellerbalance = await dex.balances(accounts[0],web.utils.fromUtf8("MTK"))
        await dex.createLimitOrder(100,0.005,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[1]})
        var after_buyerbalance = await dex.balances(accounts[1],web.utils.fromUtf8("MTK"))
        var after_sellerbalance = await dex.balances(accounts[0],web.utils.fromUtf8("MTK"))
        assert.equal(after_buyerbalance,before_buyerbalance + 100)
        assert.equal(after_sellerbalance,before_sellerbalance- 100)
    })

    it("must delete completed Orders",async ()=>{
        let token = await Token.deployed()
        let dex = await Dex.deployed()
        await dex.addToken(web.utils.fromUtf8("MTK"),token.address)
        await dex.deposit(web.utils.fromUtf8("MTK"),1000,{from: accounts[0]}) 
        await dex.createLimitOrder(500,0.005,web.utils.fromUtf8("MTK"),dex.Side.SELL,{from: accounts[0]})
        var before_SellOrder = await dex.getOrderBook(web.utils.fromUtf8("MTK"),dex.Side.SELL)
        await dex.deposit({value: web3.utils.toWei('10'), from: accounts[1]})
        await dex.deposit({value: web3.utils.toWei('15'), from: accounts[2]})
        await dex.deposit({value: web3.utils.toWei('8'), from: accounts[1]})
        await dex.createLimitOrder(200,0.005,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[1]})
        await dex.createLimitOrder(250,0.005,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[2]})
        await dex.createLimitOrder(100,0.005,web.utils.fromUtf8("MTK"),dex.Side.BUY,{from: accounts[3]})
        var after_SellOrder = await dex.getOrderBook(web.utils.fromUtf8("MTK"),dex.Side.SELL)
        assert.equal(after_SellOrder.length, before_SellOrder.length-1)
    })

})
1 Like