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) =
- LINK/amount: 20/price: 5$ = 100 ETH ā> remaining: 900 ETH
- LINK/amount 10/ price : $10 = 100 ETH ā> remaining : 800 ETH
- LINK/number 10/ price : $20 = 200 ETH ā> remaining : 600 ETH
- 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();
}
}
}