Project - Building a DEX

Please can you check now?

Thank you.

1 Like

yeah so the reason why is your test folder. when you init a truffle project a default test folder is created. when you run truffle test it searched for files within this test folder. take your two test scripts and move them into the test folder and delete the one in your contracts folder. thats why it says 0 passing because it cant find any tests in the test folder

Thank you very much for your help :slight_smile:

1 Like

let me know if it works. note your test should be failing but let me know if they run for you. they should once u make that change

1 Like

yes they are running fine now

2 Likes

hey sir , i m facing problem where i cant able to migrate my wallet and token contract when i m doing migrate then it is showing error

2_wallet_migration.js
=====================

C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\459.bundled.js:27188
        throw new Error("Could not find artifacts for " + import_path + " from any sources");
        ^

Error: Could not find artifacts for  Wallet  from any sources
    at Resolver.require (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\resolver\dist\lib\resolver.js:60:1)
    at Object.require (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:172:1)    at ResolverIntercept.require (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\ResolverIntercept.js:22:1)
    at C:\Users\sudha\OneDrive\Desktop\cpt\migrations\2_wallet_migration.js:1:30
    at Script.runInContext (vm.js:144:12)
    at Script.runInNewContext (vm.js:149:17)
    at Object.file (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\require\require.js:94:1)  
    at Migration._load (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:44:1)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at Migration.run (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:217:1)
    at Object.runMigrations (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:150:1)
    at Object.runFrom (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:110:1)    at Object.run (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:87:1)     
    at runMigrations (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate\run.js:76:1)
    at Object.module.exports [as run] (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate\run.js:44:1)
    at Command.run (C:\Users\sudha\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\command.js:189:1)    



and my 2_wallet_migration.js is

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

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

and my contract has no error

pragma solidity 0.8.11;
 import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.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 tokenexist  (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) tokenexist(ticker) external {
        //   require (tokenmapping[ticker].tokenaddress!= address(0));
        IERC20 (tokenmapping [ticker].tokenaddress).transferFrom(msg.sender, address(this), amount);
        balances[msg.sender][ticker] =  balances[msg.sender][ticker].add(amount);
     }


         function withdraw (uint256 amount , bytes32 ticker)  tokenexist(ticker) external {
                // require (tokenmapping[ticker].tokenaddress!= address(0));
             require (balances [msg.sender][ticker] >=amount, "balance is not suufient");
        balances [msg.sender][ticker] =  balances [msg.sender][ticker].sub (amount);
         IERC20 (tokenmapping[ticker].tokenaddress).transfer(msg.sender , amount );
     }


}
1 Like

Ok change " Wallet " to “Wallet” and also change both instances of “Migrations” to “Wallet” also. It should look like

Const Wallet = artifacts.require("Wallet")

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

Hit it seems that Im mhave made an error somewhere along the process. In the Dex test all tests failed. Thanks in advance for your feedback.

Here is my Dex Contract

// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
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 = 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++;
    }

   // function createMarketOrder() public{
   //    
   // }

}

Here is my Dex Test

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 be more or as much ETH as buy order value", 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: 10})
        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 be bigger or equal token balance to sell order value", 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)
        )
    })

//The BUY order book should be ordered on price from highest to lowest starting at index 0
    it("Should order BUY order book from highest to lowest value", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);

        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++) {
            const element = array[index];
            assert(orderbook[i].price >= orderbook[i+1].price, "not right order in buy book")
        }
    })

//The SELL order book should be ordered on price from lowest to highest starting at index 0
    it("Should order SELL order book from lowest to highest value", 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++) {
            const element = array[index];
            assert(orderbook[i].price <= orderbook[i+1].price, "not right order in SELL book")
        }
    })

})

And here are the errors I get.

  Contract: Dex
    - Should only be possible for owners to add tokens
    - Should handle deposits correctly
    - Should handle faulty withdrawals correctly
    - Should handle withdrawals correctly


  0 passing (3s)
  4 pending
  4 failing

  1) Contract: Dex
       Should be more or as much ETH as buy order value:
     TypeError: dex.depositEth is not a function
      at Context.<anonymous> (test\dextest.js:14:13)
      at processTicksAndRejections (internal/process/task_queues.js:88:5)

  2) Contract: Dex
       Should be bigger or equal token balance to sell order value:
     Error: Returned error: VM Exception while processing transaction: revert Token not listed! -- Reason given: Token not listed!.
      at Context.<anonymous> (test\dextest.js:28:19)
      at processTicksAndRejections (internal/process/task_queues.js:88:5)

  3) Contract: Dex
       Should order BUY order book from highest to lowest value:
     Error: Returned error: VM Exception while processing transaction: revert
      at Context.<anonymous> (test\dextest.js:40:19)
      at processTicksAndRejections (internal/process/task_queues.js:88:5)

  4) Contract: Dex
       Should order SELL order book from lowest to highest value:
     Error: Returned error: VM Exception while processing transaction: revert
      at Context.<anonymous> (test\dextest.js:59:19)
      at processTicksAndRejections (internal/process/task_queues.js:88:5)

you have blank spaces on the artifact name, it should just be: ("Wallet").

Let me know if that works :nerd_face:

Carlos Z

Yeahh I found that yesterday this is successful bdw can u answer in the truffle section I m having an error there

ey sir i m getting the error while migrating the contracts , i make 3 contract
wallet
dex
token
which is compiled syuccesfully
then i make 3 migration files
but token migration fies has some error which i cant able to found

ReferenceError: migrations is not defined
at module.exports (C:\Users\HP\Desktop\cpt\migrations\3_cpt_migration.js:17:20)
at Migration._load (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:55:1)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at Migration.run (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:217:1)
at Object.runMigrations (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:150:1)
at Object.runFrom (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:110:1)
at Object.run (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:87:1)
at runMigrations (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate\run.js:76:1)
at Object.module.exports [as run] (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate\run.js:44:1)
at Command.run (C:\Users\HP\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\command.js:189:1)
bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)
This version of ÂľWS is not compatible with your Node.js build:

Error: Cannot find module ‘./uws_win32_ia32_72.node’
Falling back to a NodeJS implementation; performance may be degraded.

  • Blocks: 0 Seconds: 0
  • Saving migration to chain.
  • Blocks: 0 Seconds: 0
  • Saving migration to chain.
const Migrations = artifacts.require("Cpt");
module.exports = function (deployer) {
  deployer.deploy( migrations );
};

it is token migration file

Hey @Sudhanshu_Srivastava. Can you repost the question its hard to read the errors. It looks like your migration file is wrong. Change it from what you have to

const Cpt = artifacts.require("Cpt"); 
module.exports = function (deployer) { deployer.deploy("Cpt"); };

@thecil @mcgrane5 Could one of you please look at my last post here from a couple of day ago? I am still stuck at the same point. The wallet test passes but the dex test gives the listed errors. Thanks for your help.

Here is the link to my post above:

1 Like

hey @PaulS96 i will have a look for you later this afternoon. could you commit the most recent changes and push them to githib ill clone your repo and have a look at them for u could you also share the link to your repo

1 Like

@mcgrane5 Thanks. I just put it on github.

https://github.com/paulsimroth/eth201project

Hey @PaulS96, hope you are well.

I have downloaded your repo and replicate the issue, this is what i got:
image
There is not depositEth function created (yet) in your dex contract, so is normal this error shows up.


You might have to run the function addToken first, to list the token on your dex (this function is on the wallet contract, which is inherited from your dex).

image
image
One of your require in the function createLimitOrder is getting triggered, you should add an error message on them to know which of those are getting triggered. Could be that since there is no token listed yet, the errors is triggered.

Hope this helps.

Carlos Z

1 Like

@thecil Thanks for the feedback. I have the wallettest still active while running the dextest so I assumed, that the added Token would also affect the dex test. I added

await truffleAssert.passes( dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]}) )

to the second test and now it passes. The other 3 still fail. But I´ll probably revisit this issue at the end once the dex is more or less finished.

Is there a way you reccomend I can deposit eth via the already existing deposit function or should I do it in a separate payable function? If I do it in the existing function I would need a ticker but wouldn´t that mean that I would have to create another ETH Token or is there a way I can declare the ETH ticker for Ethereum itself?

@thecil @mcgrane5 I made some changes to the wallet, dex test and wallettest. I still have some errors but different ones than last time. I made seperate functions for depositing or withdrawing Tokens and Ether. I also made some changes to the dextest and wallettest. I would like to have some feedback if this is a step in the right direction.

Thanks in advance.

The code is in my repository on GitHub.

1 Like

brilliant. of course ill have a look at that now for u

1 Like

@PaulS96 ok so i fixed your wallet tests and one of your limit order tests. Your problems were not your actuall smart contratc code but just some syntax issues. One example is you were calling withdrawETH in your test file but the function is called withdrawEth. Also you were trying to assert that

balance.toNumber()

was equal to to some value. However balance.toNumber() seems not to be a function anymore so what i did to get around this issue was just to convert your balance back from wei using

web3.eth.utils.fromWei(balance.toString(), "ether")

this done the job. there was a few other things that were mainly syntaxual issues. However here is the modded test files so you can compare. Also note that i added a getBalance function in your wallet.sol contract and used this to query user balances

I purposely left two tests failing to motivate you to try fix these last two yourself. I fixed the three that were broken in the wallet tests and one in the dex test.

here is the code

WalletTest

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

contract("Dex", accounts => {
    it("Should only be possible for owners to add tokens", 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]})
        )
    })

    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.balance(web3.utils.fromUtf8("LINK"))
        console.log(balance)
        assert.equal(balance.toString(), 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 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 ETH deposits correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await dex.depositEth({value: web3.utils.toWei("10", "ether")});
        let balance = await dex.balance(web3.utils.fromUtf8("ETH"))
        // console.log(balance.toNumber())
        console.log(balance)
        assert.equal(web3.utils.fromWei(balance.toString(), "ether" ), 10) 
    })

    it("Should handle faulty ETH withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.reverts(dex.withdrawEth(web3.utils.toWei("100", "ether")))
    })

    it("Should handle ETH withdrawals correctly", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await truffleAssert.passes(dex.withdrawEth(web3.utils.toWei("10", "ether")))
    })

})

DexTest

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 be more or as much ETH as buy order value", async () => {
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);  
        await truffleAssert.passes(
            dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {from: accounts[0]})
        )
        await truffleAssert.reverts(
            dex.createLimitOrder(0, web3.utils.fromUtf8("LINK"), 10, 1)
        )
        await dex.depositEth({value: web3.utils.toWei( "10", "ether")})
        await dex.deposit(100, web3.utils.fromUtf8("LINK"));
        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 be bigger or equal token balance to sell order value", 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.createLimitOrder(1, web3.utils.fromUtf8("LINK"), 1000, 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"), 110, 1)
        )
    })

//The BUY order book should be ordered on price from highest to lowest starting at index 0
    it("Should order BUY order book from highest to lowest value", async () =>{
        let dex = await Dex.deployed()
        let link = await Link.deployed()
        await link.approve(dex.address, 500);

        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++) {
            const element = array[index];
            assert(orderbook[i].price >= orderbook[i+1].price, "not right order in buy book")
        }
    })

//The SELL order book should be ordered on price from lowest to highest starting at index 0
    it("Should order SELL order book from lowest to highest value", 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++) {
            const element = array[index];
            assert(orderbook[i].price <= orderbook[i+1].price, "not right order in SELL book")
        }
    })

})

wallet.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

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;

    //token consists of a ticker and the contract address
    struct Token {
        bytes32 ticker;
        address tokenAddress;
    }

    mapping(bytes32 => Token) public tokenMapping;

    bytes32[] public tokenList;

    mapping(address => mapping(bytes32 => uint256)) public balances;

    modifier tokenExist(bytes32 ticker) {
        require(tokenMapping[ticker].tokenAddress != address(0), "Token not listed!");
        _;
    }

    function balance(bytes32 ticker) external view returns (uint) {

        return balances[msg.sender][ticker];
    }

    //add ERC20 Token
    function addToken(bytes32 ticker, address tokenAddress) onlyOwner external {
        tokenMapping[ticker] = Token(ticker, tokenAddress);
        tokenList.push(ticker);
    }

    //deposit  ERC20 Token
    function deposit(uint amount, bytes32 ticker) tokenExist(ticker) external {
        require(amount != 0, "cannot deposit nothing");
        IERC20(tokenMapping[ticker].tokenAddress).transferFrom(msg.sender,address(this), amount);
        balances[msg.sender][ticker] = balances[msg.sender][ticker].add(amount);
    }

    //withdraw ERC20 Token
    function withdraw(uint amount, bytes32 ticker) tokenExist(ticker) external {
        require(balances[msg.sender][ticker] >= amount, "Balance not sufficient");
        IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount);
        balances[msg.sender][ticker] = balances[msg.sender][ticker].sub(amount);
    }

    //deposit ETH
    function depositEth() public payable {
        require(msg.value != 0, "cannot deposit nothing");
        //IERC20(tokenMapping["ETH"]).transferFrom(msg.sender,address(this), msg.value);
        balances[msg.sender]["ETH"] += msg.value;
    }

    //withdraw ETH
    function withdrawEth(uint amount) external {
        require(balances[msg.sender]["ETH"] >= amount, "Balance not sufficient");
        payable(msg.sender).transfer(amount);
        //IERC20(tokenMapping["ETH"]).transfer(msg.sender, msg.value);
        balances[msg.sender]["ETH"] -= amount;
    }
}
1 Like