wallettest.js
//The first order ([0]) in the BUY order book should have the highest price
const Dex = artifacts.require("Dex")
const Link = artifacts.require("Link")
const truffleAssert = require('truffle-assertions');
contract("Dex", 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()
let link = await Link.deployed()
await truffleAssert.reverts(
dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 10, 1)
)
await dex.depositEth({value: 10, from:accounts[0]})
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 dex.addToken(web3.utils.fromUtf8("LINK"), link.address)
await link.approve(dex.address, 100000)
await truffleAssert.reverts(
dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 10, 1)
)
await dex.deposit(10, web3.utils.fromUtf8("LINK"))
await truffleAssert.passes(
dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 10, 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()
let link = await Link.deployed()
await dex.depositEth({value: 3000});
await link.approve(dex.address, 1000);
await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 300)
await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 100)
await dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 200)
let orderbook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 0);
assert(orderbook.length > 0);
console.log(orderbook);
for (let i = 0; i < orderbook.length - 1; i++) {
assert(orderbook[i] >= orderbook[i+1])
}
})
//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 dex.depositEth({value: 3000});
await link.approve(dex.address, 1000);
await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 300)
await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 100)
await dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1, 200)
let orderbook = await dex.getOrderBook(web3.utils.fromUtf8("LINK"), 1);
assert(orderbook.length > 0);
for (let i = 0; i < orderbook.length - 1; i++) {
assert(orderbook[i] <= orderbook[i+1])
}
})
})
dex.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.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;
Side side;
bytes32 ticker;
uint amount;
uint price;
}
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) public {
if(side == Side.BUY){
require(balances[msg.sender]["ETH"] >= amount.mul(price));
}
else if(side == Side.SELL){
require(balances[msg.sender][ticker] >= amount);
}
Order[] storage orders = orderBook[ticker][uint(side)];
orders.push(
Order(nextOrderId, msg.sender, side, ticker, amount, price)
);
// Bubble sort
uint i = orders.length > 0 ? orders.length - 1 : 0;
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--;
}
}
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++;
}
}