Project - Building a DEX

Thanks @thecil!

I’ll give it a try!

Hey @dan-i
I’ve added the bytes32 ticker into the parameters and it still doesn’t seem to be working. I added the require message like @thecil suggested and it is to do with the function you are suggesting is causing the issue. Not sure how to proceed with this :confused:
Screenshot 2021-04-23 at 11.22.16

update: managed to fix the first test, just the 2nd test to figure out

@filip, @dan-i, @thecil
I have been stuck for several days now on the DexTesting function, createLimitOrder.
I get this error message: TypeError: dex.depositEth is not a function
There are other errors but I think they are related to the same problem.

dex.depositEth({value: 10})

I’ve also tried it with await in front of it

contract("Dex", accounts => {
  it("should throw an error if ETH balance is too low when creating BUY limit orders", 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})
      await truffleAssert.passes(
        dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 10, 1)
      )
  })

Hey @jakegoode95

The test that fails is this one, let’s break it down:

 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.deposit(10, web3.utils.fromUtf8("LINK"));
        await truffleAssert.passes(
            dex.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 10, 1)
        )
    })

Now, based on the error you get, this part of the tests is wrong (you expect a revert that does not happen).

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

This is the function:

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

Now am I right to say that BUY is 0 and SELL is 1?

    enum Side {
        BUY,
        SELL
    }

If I am right, then can you spot the error in your test? :slight_smile:

Let me know,
Dani

Hi @bjamRez

Does the function depositEth exists in your contract?

1 Like

Thanks @dan-i

I spent the weekend trying my best to figure out the issue and found it the way you’ve described. I took myself a way from the issue then revisited it and saw it plain as day. Thanks soo much for help @dan-i :slight_smile:

1 Like

So I have a question about implementing the
wallet.deposit(100, web3.utils.fromUtf8(“Link”)) command in the terminal, I keep receiving a revert error. Note I added my own cryptocurrencyerror

No, which is confusing because in Filip’s tutorial, it does not exist either.
Limit Order Function:
https://academy.ivanontech.com/products/ethereum-smart-contract-programming-201/categories/4821918/posts/16224669
time: 4:22
I did manage to get the first test to pass for the DEX tests by creating a function for the test

function depositEth(bytes32 ticker, uint256 amount) public {
    balances[msg.sender][ticker] = balances[msg.sender][ticker].add(amount);

  }

here is the test

await dex.depositEth(web3.utils.fromUtf8("ETH"), 10)

But for the remaining three tests, I keep getting these type of errors:

Error: VM Exception while processing transaction: revert

I learned a bit abut debugging and here are the results I am getting

 1) Contract: Dex
       should throw an error if token balance is too low when creating SELL limit order:
     TypeError: promiEvent.on is not a function
      at new Promise (<anonymous>)
      at global.<computed> (C:\Users\benX\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\testing\Test.js:309:1)
      at Context.<anonymous> (test\DexTest.js:22:11)
      at processTicksAndRejections (internal/process/task_queues.js:93:5)

  2) Contract: Dex
       "after each" hook: after test for "should throw an error if token balance is too low when creating SELL limit order":
     Uncaught RuntimeError: abort(AssertionError: Expected to fail with  this reverts, but failed with: Error: Returned error: VM Exception while processing transaction: revert not enough token balance). Build with -s ASSERTIONS=1 for more info.
      at process.abort (C:\Users\benX\.config\truffle\compilers\node_modules\soljson-v0.8.4+commit.c7e474f2.js:1:13012)
      at processPromiseRejections (internal/process/promises.js:245:33)
      at processTicksAndRejections (internal/process/task_queues.js:94:32)

  3) Contract: Dex
       "after each" hook: after test for "should throw an error if token balance is too low when creating SELL limit order":
     Error: done() called multiple times in hook <Contract: Dex "after each" hook: after test for "should throw an error if token balance is too low when creating SELL limit order"> of file C:\Users\benX\Documents\ethereum_course_advanced\Dex\test\DexTest.js
      at processTicksAndRejections (internal/process/task_queues.js:93:5)



- Fetching solc version list from solc-bin. Attempt #1
- Fetching solc version list from solc-bin. Attempt #1
AssertionError: Expected to fail with  this reverts, but failed with: Error: Returned error: VM Exception while processing transaction: revert not enough token balance

here is a link to my DEX contract file and testing file and support files
https://github.com/brlojam4932/DexProj.git

Hey @Samuel1

The error you get is related to a require statement that fails.
Can you post your smart contract so that we can verify where and why it fails?

Thanks
Dani

Hi @bjamRez

The function above is missing some important points.
Keep in mind that this function is used to deposit ether, therefore should be payable.

  function depositEth() payable external {
        balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].add(msg.value);
    }

Try this one.

Cheers,
Dani

1 Like

This is my proverbial Link Contract for an ERC20 token that I am creating called Decent Med that offers an Alternative Medicine telehealth directory and sells the rarest plants and herbs via a digital apothecary

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../@openzeppelin/contracts/utils/math/SafeMath.sol";

contract DMED is ERC20 {
using SafeMath for uint256;
constructor () ERC20("DecentMed", "DMED") {
    _mint(msg.sender, 1000);
}
function isContract(address account) internal view returns(bool) {
     uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
}
function _transfer(address sender, address recipient, uint256 amount) internal virtual override {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);
        _transfer(msg.sender, recipient, amount);
    
        emit Transfer(sender, recipient, amount);

    }
    function approve(address spender, uint256 amount) public virtual override  returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true; 
    }






}

This is my wallet contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./Wallet.sol";
contract Dex is Wallet { 

enum Side {
    BUY,
    SELL
}
struct Order {
    uint id;
    address trader;
    bool buyOrder;
    bytes32 ticker;
    uint amount;
    uint price;
}
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() public {}
}

Thanks Dani, updating the depositEh function did help. I had forgotten that the orderBook is empty so the last two tests should fail. I am moving on with the lessons and thanks for your help.

1 Like

I had the same problem. I created one and then got some help from Dani but it’s strange how that is missing in the tutorial

1 Like

@dan-i, @filip

============DISREGARD ========================
There is plenty of material on line regarding this.

Bubble Sorting…is there supposed to be some literature regarding this? Filip mentions this in the tutorial but I don’t see any links …?

1 Like

Hey @Samuel1

I cannot see the deposit function you were mentioning in your previous post.

//Bubble Sorting attempt…needs work; currently failing

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 < 0.9.0;
pragma abicoder v2;

import "./Wallet.sol";

contract Dex is Wallet {

  using SafeMath for uint256;

  enum Side {BUY, SELL} // 0, 1

  struct Order {
    uint256 id;
    address trader;
    Side side;
    bytes32 ticker;
    uint256 amount;
    uint256 price;
  }

  uint public nextOrderId = 0;

  mapping(bytes32 => mapping(uint256 => Order[])) public OrderBook;

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

  function depositEth() external payable {
    balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].add(msg.value);

  }

  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 balance");
      } else if (side == Side.SELL) {
        require(balances[msg.sender][ticker] >= amount, "not enough token balance");
      }

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

      //Bubble sort
    
      if(side == Side.BUY){
         for (uint i = 0; i < orders.length -1; i++) {
              if (orders[i].price < orders[i + 1].price) {
                uint swap = orders[i].price;
                orders[i].price = orders[i + 1].price;
                orders[i + 1].price = swap;
              }
            }
          orders.push(Order(nextOrderId, msg.sender, side, ticker, amount, price));
      }
      else if(side == Side.SELL){
        
      }

      nextOrderId++;
    
  }


}

@filip, @dan-i
Hello Filip, Dani,

Here is a code, where I combined aspects from Filip’s code and from another source I had tested with JS.

The code passes the SELL test but not the BUY test, even though they both get sorted and printed correctly in the console, when interacting directly but not during the “test”.

So I just need to know why the BUY test is not working?

I’ll share a GitHub link as well - here is the Dex code

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 < 0.9.0;
pragma abicoder v2;

import "./Wallet.sol";

contract Dex is Wallet {

  using SafeMath for uint256;

  enum Side {BUY, SELL} // 0, 1

  struct Order {
    uint256 id;
    address trader;
    Side side;
    bytes32 ticker;
    uint256 amount;
    uint256 price;
  }

  uint public nextOrderId = 0;

  // as enum
  // order.side = Side.BUY;
  // as boolean
  // order.BUY = true;

  mapping(bytes32 => mapping(uint256 => Order[])) public OrderBook;

  function getOrderBook(bytes32 ticker, Side side) public view returns(Order[] memory) {
    return OrderBook[ticker][uint(side)];
    //getOrderBook(bytes32("LINK"), Side.BUY); Filip converts it here but says it's not
    //necessary as we have done it above
  }

  function depositEth() external payable {
    balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].add(msg.value);

  }

  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 balance");
      } else if (side == Side.SELL) {
        require(balances[msg.sender][ticker] >= amount, "Low token balance");
      }

      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;
      //defines the start, if array is empty it equals 0; shortened if statement

      if(side == Side.BUY){
        for (i = 0; i < orders.length -1; i++) {
          if (orders[i].price < orders[i + 1].price) {
            
              Order memory swap = orders[i + 1];
              orders[i + 1] = orders[i];
              orders[i] = swap;
              i++; 
            }
        }
      }
      else if(side == Side.SELL) {
        for (i = 0; i < orders.length -1; i++) {
          if (orders[i].price > orders[i + 1].price) {
            
              Order memory swap = orders[i + 1];
              orders[i + 1] = orders[i];
              orders[i] = swap;
              i++; 
            }
        }
        
      }
      nextOrderId++;
    
  }

}

Passing BUY orders I placed by hand in the terminal…The 10 Eth Buy order does not show

truffle(develop)> orderBookBuy
[
  [
    '0',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '300',
    id: '0',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '300'
  ],
  [
    '2',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '200',
    id: '2',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '200'
  ],
  [
    '1',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '100',
    id: '1',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '100'
  ]
]

this is the failing BUY output when running the test, which includes transactions of 10 Eth

[
  [
    '2',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '300',
    id: '2',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '300'
  ],
  [
    '3',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '100',
    id: '3',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '100'
  ],
  [
    '4',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '200',
    id: '4',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '200'
  ],
  [
    '0',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '0',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '10',
    '1',
    id: '0',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '0',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '10',
    price: '1'
  ]
]

and these are the SELL orders, which pass and are printed correctly as well

[
  [
    '1',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '1',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '10',
    '1',
    id: '1',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '1',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '10',
    price: '1'
  ],
  [
    '6',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '1',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '100',
    id: '6',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '1',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '100'
  ],
  [
    '7',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '1',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '200',
    id: '7',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '1',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '200'
  ],
  [
    '5',
    '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    '1',
    '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    '1',
    '300',
    id: '5',
    trader: '0xDC6bBC41d7fF0aCCD096969921067c07CD3B0d37',
    side: '1',
    ticker: '0x4c494e4b00000000000000000000000000000000000000000000000000000000',
    amount: '1',
    price: '300'
  ]
]

here is a link to my entire projecd on github:
https://github.com/brlojam4932/DexProj.git

1 Like

Hey everyone,

The first test works perfectly but not the second one(deposits) and I can’t seem to find a solution to this error. Can someone help pls? :blush:Screenshot 2021-05-03 at 13.15.14

Here is my TDD code:

const Dex = artifacts.require("Dex");
const TestToken = artifacts.require("TestToken");
const truffleAssert = require("truffle-assertions"); // first $ npm i truffle-assertions!!

contract("Dex", (accounts) => {
  // here we can run the tests, for each statement it will redeploy our contracts

  //define new test with "it"
  it("should only be possible for owner to add tokens", async () => {
    let dex = await Dex.deployed();
    let Token = TestToken.deployed();

    // we check if the statement(as in only the owner can call this function) passes with ".passes" -- in terminal you have to run $ truffle test
    await truffleAssert.passes(
      dex.addToken(web3.utils.fromUtf8("TEST"), TestToken.address, {
        from: accounts[0],
      })
    );

    // we check if the function reverts ".reverts" if it's called by not the owner of the TestToken contract
    await truffleAssert.reverts(
      dex.addToken(web3.utils.fromUtf8("TEST"), TestToken.address, {
        from: accounts[1],
      })
    );
  });

  it("should handle deposits correctly", async () => {
    let dex = await Dex.deployed();
    let token = TestToken.deployed();
    await token.approve(dex.address, 500);
    await dex.deposit(100, web3.utils.fromUtf8("TEST"));

    // we assert that our balance should be 100 after the deposit
    assert.equal(dex.balances(accounts[0], web3.utils.fromUtf8("TEST"), 100));
  });
});

Also here is TestToken.sol too.
What I find weird is ERC20 has an approve function and it is imported in TestToken why doesn’t it work in the tests then?

pragma solidity >=0.4.22 <0.9.0;
import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract TestToken is ERC20 {
    constructor() public ERC20("TestToken", "TEST") {
        _mint(msg.sender, 1000);
    }
}

Not very far into this project so far and i’m getting an error message due to: pragma solidity >=0.6.0 <0.8.0;

The error message reads - source file requires different compiler version (current compiler is 0.8.4+commit.c7e474f2.Emscripten.clan)

Not sure if it’s due to my setup, cdm init, truffle init worked fine, openzeppelin all ok. Any help would be much appreciated.

Hey @bjamRez

The loop you are using to soft the buy orders is the same as you use for the sell order.
If you want to use a for loop is ok, but the condition needs to be right, also you are already declaring i so no need to do it twice.

// Here you are declaring i 
 uint i = orders.length > 0 ? orders.length -1 : 0;

This is the buy loop using for instead of while.

      if(side == Side.BUY){
        for(; i > 0; i --) {
          if (orders[i - 1].price > orders[i].price) break;
      
          Order memory swap = orders[i - 1];
          orders[i - 1] = orders[i];
          orders[i] = swap;
        }
      }

Let me know,
Dani

1 Like