So I am at the point where I am done with the bubble sort. All of my tests pass, except for the first one. I am getting " AssertionError: Failed with Error: Returned error: VM Exception while processing transaction: out of gas" error."
I think there is something wrong here, but I canāt figure out what it is:
function createLimitOrder(Side side, bytes32 ticker, uint amount, uint price) public {
if(side == Side.BUY){
require(balances[msg.sender]["ETH"] >= amount.mul(price), "Balance too low");
}
I am not sure if the ETH is actually being deposited or not, maybe itās failing at that stage
Here is my code:
dex.sol
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 public nextOrderId;
//A ticker that points to an enum(buy/sell 0 or 1), that pooints to an order book
mapping(bytes32 => mapping(uint => Order[])) 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), "Balance too low");
}
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)
);
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++;
}
}
dextest.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)
)
dex.depositEth({value: 100})
await truffleAssert.passes(
dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 1, 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 truffleAssert.reverts(
dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 10, 1)
)
await link.approve(dex.address, 500);
await dex.addToken(web3.utils.fromUtf8("LINK"),link.address,{from: accounts[0]})
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 link.approve(dex.address, 500);
await dex.depositEth({value: 3000})
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);
for (let i = 0; i < orderbook.length - 1; i++) {
assert(orderbook[i].price >= orderbook[i+1].price)
}
})
//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.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].price <= orderbook[i+1].price)
}
})
})
wallet.sol
pragma solidity 0.8.9;
import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";
contract Wallet is Ownable {
using SafeMath for uint256;
struct Token {
bytes32 ticker;
address tokenAddress;
}
mapping(bytes32 => Token) public tokenMapping;
bytes32[] public tokenList;
//this mapping is using an address that points to another mapping, which has a bytes32 ticker key that points the balance of that ticker
mapping(address => mapping(bytes32 => uint256)) public balances;
/*
addToken uses ticker and tokenAddress as arguments. The ticker is used as key in tokens mapping.
Tokens mapping uses a bytes32 key for ticker to point to the Token struct.
We set the key in toekens mapping equal to a new token that will be added as the Token struct
We are also pushing the ticker into the tokenList, which contains only the tiker symbols
*/
function addToken(bytes32 ticker,address tokenAddress) onlyOwner external {
tokenMapping[ticker] = Token(ticker, tokenAddress);
tokenList.push(ticker);
}
/*
The deposit function uses an IERC20 interface. Specificaly the function:
transferFrom(
address sender,
address recipient,
uint256 amount)
Tokens mapping is also used, that points to the struct Token, where we can access Token.tokenAddress
by using bytes32 ticker as key
*/
function deposit(uint amount, bytes32 ticker) tokenExist(ticker) external {
IERC20(tokenMapping[ticker].tokenAddress).transferFrom(msg.sender, address(this), amount);
balances[msg.sender][ticker] = balances[msg.sender][ticker].add(amount);
}
function withdraw(uint amount, bytes32 ticker) tokenExist(ticker) external {
require(
balances[msg.sender][ticker] >= amount,
'balance too low'
);
balances[msg.sender][ticker] = balances[msg.sender][ticker].sub(amount);
IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount);
}
function depositEth() payable external {
balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].add(msg.value);
}
modifier tokenExist(bytes32 ticker) {
require(
tokenMapping[ticker].tokenAddress != address(0),
'this token does not exist'
);
_;
}
}