Project - Building a DEX

Hi All,

I am also facing a weird problem. Absolutely clueless, why truffle is not recognising couple of functions in my wallet.sol. I have defined the depositEth and withdrawEth functions in my wallet.sol. My dex contract is inheriting from the wallet. And I am calling these two functions from my wallettest.js.
But truffle is telling me:
TypeError: dex.depositEth is not a function
TypeError: dex.withdrawEth is not a function
The screenshot of the error is as below:
image

My codes are as below:

wallet.sol

pragma solidity >=0.8.0;

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;
    mapping (address => mapping(bytes32 => uint256)) public balances;

    modifier tokenExists(bytes32 _ticker){
        require(tokenMapping[_ticker].tokenAddress != address(0), 'Token does not exist');
        _;
    }
    
       
    function addToken(bytes32 _ticker, address _tokenAddress) onlyOwner external {

        tokenMapping[_ticker] = Token(_ticker, _tokenAddress);
        tokenList.push(_ticker);

    }

    function deposit(uint _amount, bytes32 _ticker) tokenExists(_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) tokenExists(ticker) external {

        require(balances[msg.sender][ticker] >= amount, "Balance is not sufficient");
        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);
    }

    function withdrawEth(uint amount)  external {

        require(balances[msg.sender][bytes32("ETH")] >= amount, "Balance is not sufficient");
        balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].sub(amount);
        msg.sender.call{value:amount}("");
    }

 
}

token.sol

pragma solidity >=0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Link is ERC20 {

    constructor() ERC20("ChainLink", "LINK") public {

        _mint(msg.sender, 1000); 
    }

}

wallettest.js

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

contract ("Dex", accounts =>{

    it("should be possible only for owner to add token", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(
            dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]})
        )
        await truffleAssert.reverts(
            dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[1]})
        )

    })
    it("should handle deposits correctly", 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"))
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("LINK"));
        assert.equal( balance.toNumber(),100)

    })
    it("should handle faulty withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.reverts(dex.withdraw(500, web3.utils.fromUtf8("LINK")))
    })
    it("should handle correct withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(dex.withdraw(100, web3.utils.fromUtf8("LINK")))
    })
    it("should handle correct withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(dex.withdraw(100, web3.utils.fromUtf8("LINK")))
    })
    it("should have enough balance to place the order", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("LINK"));
        await truffleAssert.passes(dex.withdraw(100, web3.utils.fromUtf8("LINK")))
    })
    it("should deposit correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        dex.depositEth({value: 1000});
        assert.equal(balancenew.toNumber(), 1000);
        
    })
    it("should withdraw correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        dex.withdrawEth(500);
        let balancenew = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        assert.equal(balancenew.toNumber(), (balance.toNumber() - 500));
        
    })
    it("should not allow over deposition of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        
    })

})

Please, can somebody help?

Thanks

hey @yanjun_zhou, So your error is very simply but one of thise that can be so easily missed. in your deployer file you have your arguments in the wrong postion it should be the ticker first and then the deopsit amount. So change this line in your token migrations to the following

await wallet.deposit(web3.utils.fromUtf8("LINK"), 100)

your code will now work. hope this helps

Evan

hey @rays. I tried to see what was causing it but without but your dex file i cannot. can you share you dex code so that i can do a few tests to try locate the error?

What you shoudl try also do is enter the truffle develop and write the isnatnces yourself and type dex to get a list of all the functions available. If depositEth etc is not there then i would imagine something is not right in your function although at first glance it looks fine are you definitley inheriting from your wallet in your dex.sol file?. share your dex code anyway and ill work at it on my computer for you

Hi mcgrane5,

Sorry for missing my dex.sol.

Here is the one:

pragma solidity >=0.8.0;
pragma experimental ABIEncoderV2;

import "../contracts/wallet.sol";

contract Dex is Wallet {

enum Side {
    BUY, 
    SELL
}

struct Order {
    uint id;
    address trader;
    Side side;
    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)];

}

}
1 Like

I can successfully execute the other functions from the truffle develop console. But these two functions depositEth and withdrawEth, I canmot execute. It is saying that they are not functions.

1 Like

Hey @rays. So i actually didnt have to make much changes and i got all of your tests passing. There is a couple of issues though. Firstly in your current wallet test there is a typo in this test here

it("should deposit correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        dex.depositEth({value: 1000});
        assert.equal(balancenew.toNumber(), 1000);

your asserting balancenew.toNumber() is equal to 1000 but this should be balance.toNumber() as you have not defined balanceNew. Should be

assert.equal(balance.toNumber(), 1000)

Also you are setting your balance variable to the account balance before you call the deposit function so its always going to record zero as the balance. define the balance after you deposit so this test should be

it("should deposit correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed();
        dex.depositEth({value: 1000});
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        assert.equal(balance.toNumber(), 1000);

I am trying to think which other test’s was failing i had something to say but its slipped my mind. The only other thing is you are never actually approving link and depositng in link to your dex in most tests. You should do this. Also i reccommend testing both sides of the coin if you will, in each of your tests. What i mean is do a truffAssert.passes() on some function with inputs you know will pass and then after do a truffleAssert.reverts() on the same function with inputs that you know should fail. But anyways ill leave that for you to do. Here is the chaged code. All tests now pass

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

contract ("Dex", accounts =>{

    it("should be possible only for owner to add token", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(
            dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]})
        )
        await truffleAssert.reverts(
            dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[1]})
        )

    })
    it("should handle deposits correctly", 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"))
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("LINK"));
        assert.equal( balance.toNumber(),100)

    })
    it("should handle faulty withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.reverts(dex.withdraw(500, web3.utils.fromUtf8("LINK")))
    })
    it("should handle correct withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(dex.withdraw(100, web3.utils.fromUtf8("LINK")))
    })
   
    it("should have enough balance to place the order", 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"))
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("LINK"));
        await truffleAssert.passes(dex.withdraw(100, web3.utils.fromUtf8("LINK")))
    })
    it("should deposit correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        dex.depositEth({value: 1000});
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        assert.equal(balance.toNumber(), 1000);
        
    })
    it("should withdraw correct amount of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        let balance = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        dex.withdrawEth(500);
        let balancenew = await dex.balances(accounts[0], web3.utils.fromUtf8("ETH"));
        assert.equal(balancenew.toNumber(), (balance.toNumber() - 500));
        
    })
    it("should not allow over deposition of Eth", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        
    })

})

Lastly in regards to your functions not being read i never had that issue i litreally copied each of your files in vs code and then wrote the migrations file for the dex and i never got the error saying deposit/withdraw eth were not functions. Just incase this is the migrations file for the dex

EDIT** perhaps your dex wasnt updated when you ran the tests and truffle wasnt able to recognise your deposit/withdraw eth funcs i couldnt say but everything should work now didnt make any changes to any file other than your wallet_test.js and also the migrations dex file

const Dex = artifacts.require("Dex");
const Wallet = artifacts.require("Wallet");
module.exports = async function (deployer) {
  await deployer.deploy(Dex);
  

};

Let me know if things work for you

Hi mcgrane5,

Unfortunately, I am still seeing the same error. I have now used the file, provided by you. Attaching the screenshot of the truffle test below:

image

1 Like

really strange i dont know why that is happening it cant be anything else other than the link between your wallet and dex file. I have an idea ill share you now all of the files i have. copy them and replace them with yours just to be absolutley sure. If the issue still presits then thats very strange altogether. so you have the wallet js and dex migrations from above, here are the rest.

If this doesnt work then enter truffle develop manually write create dex and link instances, add link to the dex, approve and deposit. then see if you can deposit eth there. again type dex to make sure that the functions are available. Its gotta be an issue with the way your inheriting but again as i said your dex/wallet files look fine i just dont know why it works on my machine and not yours. But do replace yours with the files below and write back to me. We will get this working

token.sol

/ SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Link is ERC20 {

    constructor() ERC20("ChainLink", "LINK") public {

        _mint(msg.sender, 1000); 
    }

}

wallet.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

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;
    mapping (address => mapping(bytes32 => uint256)) public balances;

    modifier tokenExists(bytes32 _ticker){
        require(tokenMapping[_ticker].tokenAddress != address(0), 'Token does not exist');
        _;
    }
    
       
    function addToken(bytes32 _ticker, address _tokenAddress) onlyOwner external {

        tokenMapping[_ticker] = Token(_ticker, _tokenAddress);
        tokenList.push(_ticker);

    }

    function deposit(uint _amount, bytes32 _ticker) tokenExists(_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) tokenExists(ticker) external {

        require(balances[msg.sender][ticker] >= amount, "Balance is not sufficient");
        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);
    }

    function withdrawEth(uint amount)  external {

        require(balances[msg.sender][bytes32("ETH")] >= amount, "Balance is not sufficient");
        balances[msg.sender][bytes32("ETH")] = balances[msg.sender][bytes32("ETH")].sub(amount);
        msg.sender.call{value:amount}("");
    }

 
}

dex,.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

import "../contracts/wallet.sol";

contract Dex is Wallet {

enum Side {
    BUY, 
    SELL
}

struct Order {
    uint id;
    address trader;
    Side side;
    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)];

}

}

2_wallet_migration

const Wallet = artifacts.require("Wallet");

module.exports = function (deployer) {
  deployer.deploy(Wallet);
};

3_token_migration

const Link = artifacts.require("Link");
const Wallet = artifacts.require("Wallet");
module.exports = async function (deployer) {
  await deployer.deploy(Link);
  

};

Hi mcgrane5,

don’t we need to migrate the dex as well ? One difference, I see is that I did not create the wallet migration. I just created the dex migration. Since, dex is inheriting from the wallet, I thought all the wallet functions will be available, if dex itself is migrated. What do you think ?

Without the dex migration file I am now seeing the following:

image

1 Like

ehm im trynna think what else it could be. So weird how it works on mine and not yours with the exact same code lol. Do you know how to push to github if you maybe try push your exact code up to github i can clone it and hopfully then i will too get the same errors you have. Although i got the test working on my machine its difficult for me to say why your getting the desoit/withdraw eth function errors without actually being able to experinec them myself so i can do proper debugging.

1 Like

Hi mcgrane5,

I have now uploaded the entire codebase to github:

https://github.com/rays70/DEX

Can this be an issue with the truffle and solidity version ?

My truffle-config.js is using the version: “0.8.0”

1 Like

hey @rays so i cloned your repo and again all tests pass for me i cant figure out why its not working for you. yeah after i cloned your code i also changed my sol version to 0.8.0 in the truffle-config too. so annoying for you that you get them bugs i cant figure out why. This is my test results from your repo
sn
However can i ask is the code in that repo your excat code. because There was no migrations.sol or a initial_migrations.js or a dex_migrations.js. i had to again add them in myself. its so bazar though isnt it. What is you restated vs code and your terminal lol a farcry but you never know

Im very sorry that at this point your still stuck. its anoying me that we cannot resolve this lol. There is very little i can do to isolate the error because your code works perfectly for me. At this point i suggest asking Dani or carlos (thecil) for help.

Not sure why I keep getting this error code… did I miss something?

ReferenceError: Cannot access 'dex' before initialization
    at module.exports (C:\Users\micke\Documents\ethereum-201\DEX\migrations\3_tokens_migrations.js:6:13)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

Now I fixed that, what is going on here? this should be fine?

Compiling your contracts...
===========================
> Compiling .\contracts\dex.sol
> Compilation warnings encountered:

    Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> /C/Users/micke/Documents/ethereum-201/DEX/contracts/dex.sol

,Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> /C/Users/micke/Documents/ethereum-201/DEX/contracts/wallet.sol


> Artifacts written to C:\Users\micke\AppData\Local\Temp\test--14816-eJP5gSey2uGl
> Compiled successfully using:
   - solc: 0.8.4+commit.c7e474f2.Emscripten.clang

Promise {
  <pending>,
  _events: Events <[Object: null prototype] {}> {},
  emit: [Function: emit],
  on: [Function: on],
  once: [Function: once],
  off: [Function: removeListener],
  listeners: [Function: listeners],
  addListener: [Function: on],
  removeListener: [Function: removeListener],
  removeAllListeners: [Function: removeAllListeners]
}
TypeError: artifacts.reqiure is not a function
1 Like

Hi @rays

I am able to run your tests:
Screenshot 2021-05-24 at 09.56.40

I am wondering if you’ve omitted files when you pushed your project to github.

I saw that you are not deploying “Dex” at all (there is no migration for it).

Make sure to migrate all your contracts then:

  • truffle develop
  • test

Let us know and make sure to push the whole project on github so that we can run the exact same code.

Cheers,
Dani

1 Like

Hi dan-i,

thanks a lot for checking this. One fundamental question. Since dex is inheriting from wallet, do we need to migrate wallet at all, or we should be just migrating the dex ?

Cheers!

Hi @mcgrane5,

sorry I was busy last week, so now I finally could get into this.

Thanks, I did change the buble sort according to you and the first test works this way. However I can’t understand what was wrong with my bubble sort, because you totally rewrote it :smiley: I was checking my bubble sort again and I can’t figure out what is the problem.

Anyway according to your advice I did manual testing in console with your bubble sort using commands bellow and I still can’t run createLimitOrder. (I hope that you meant manual testing this way).

migrate
let dex = await Dex.deployed()
dex.createLimitOrder(0, web3.utils.sha3("LINK"), 10, 1)

And I still get a vague error which does not help. At this point I am lost :confused: Should I learn how to debug in Visual Studio Code? Or how should I approach it now? Could you please help me with this whole problem? Thanks.
image

1 Like

Hi dan-i and mcgrane5

I have now changed the wallet_migration to dex_migration and updated the github repo. And the best news is all the tests passed this time :grinning:

Thanks a lot for excellent support :+1: :+1:

I am unblocked now.

Cheers !

2 Likes

hey @Tomahawk ill have a look into that for you now. Its a runtime error which means something is code breaks somewhere. could be an index out of range or something else that breaks the code. The best way to catch where the error is is to manually step through the test by picking some inputs going line by line to see where the code breaks down. Give me an hour and ill take another look at this for you im just a little busy at the moment.

Evan

1 Like