Project - Building a DEX

@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

@PaulS96 one thing i forgot to mention is that your witdraw function was payable until i changed it. functions should only be payable if your sending ether into a contract. instead for withdrawing functions you only need the recipient address to be payable and then use the built in transfer function or the call function to send the funds to the recipient.

1 Like

hey sir, im doing testing of adding tokens by owner. 0 is account of owner then why my test is failing ?

Contract: Dex
1) shoul only possibile for owners to add tokens
> No events were emitted

0 passing (6s)
1 failing

  1. Contract: Dex
    shoul only possibile for owners to add tokens :
    ReferenceError: Cannot access ‘Dex’ before initialization
    at Context. (test\Wallettest.js:7:11)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

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.

const Cpt= artifacts.require("Cpt") 
const Dex= artifacts.require("Dex") 
const truffleAssert = require('truffle-assertions');
contract ("Dex", accounts =>{
it ("shoul only possibile for owners to add tokens ", async() =>{
   
let Dex = await  Dex.deployed()
let Cpt = await Cpt.deployed()
 await truffleAssert.passes(
Dex.addtoken(web3.utils.fromUtf8("Cpt"),Cpt.address,{ from: accounts[0]}))
})
})

can u see why test is failing?

Might be a require getting triggered, can you share the contract ?? it would be much easier for us if you create a github repo for the project.

Carlos Z

1 Like

This is my code.
taking a look at the forum and code on github, I see that in my code it is missing,
check that the tokens exist and event (I will add this item next time).

I tried to use “delegated call” to approve the expense, but this solution does not work. (I approved by the contract and not for user)

I have test my code using a token “mockDai”, and i use this function " web3.utils.asciiToHex()" for convert string in byte32.

// SPDX-License-Identifier: Leluk911
pragma solidity 0.8.0;

import "../node_modules/@openzeppelin/contracts/interfaces/IERC20.sol";
import "../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Wallet is ReentrancyGuard {
    // use this struct for store token in wallet
    struct Token {
        bytes32 tiker;
        address tokenAddress;
    }

    mapping(bytes32 => Token) TokenMapping;
    // array for store Token for Token.tiker
    bytes32[] public TokenList; // only list.. some information give in mapping TokenMapping

    // duble mapping for balance every Token in wallet add
    mapping(address => mapping(bytes32 => uint256)) public balances;

    function addToken(bytes32 _tiker, address _tokenAddress)
        external
        nonReentrant
    {
        // add token information i wallet
        TokenMapping[_tiker] = Token(_tiker, _tokenAddress);
        // push only _tiker.. mapping need for take address.
        TokenList.push(_tiker);
    }

    function deposit(uint256 _amount, bytes32 _tiker) external nonReentrant {
        // before approve this spend
        IERC20(TokenMapping[_tiker].tokenAddress).transferFrom(
            msg.sender,
            address(this),
            _amount
        );
        balances[msg.sender][_tiker] += _amount;
    }

    function withdraw(uint256 _amount, bytes32 _tiker) external nonReentrant {
        require(TokenMapping[_tiker].tokenAddress != address(0)); // address assigne not 0X00000
        require(balances[msg.sender][_tiker] >= _amount, "balance insufficent"); // check balance before
        balances[msg.sender][_tiker] -= _amount; //update balance befor transfer
        IERC20(TokenMapping[_tiker].tokenAddress).transfer(msg.sender, _amount); // transfer
    }
}

1 Like

hey @LELUK911 i will have a look at your code now. are ypu able to post the link to your githib. one thing before i do is that you cannot use delegate call here for this to work. the approve function doesnt work here when you try to call it is because your dex contract makes an external call to your link token. therefore the caller is not you but rather your dex contract. so because this is the case the owner address and the spender address are the same here therefore the alloance will get increased for owner = your wallet and spender - your wallet. the only way to solve this is to execute approve and transferfrom seperately. which is annoying i know.

but anyways ill look at the rest of your code now if you can share the link to your github

1 Like

in fact , my contract’s allowance increas, i haven’t post on github.

but the function was more or less.

function approveDelegate(address _user, uint _amount, bytes32 _tiker) external{
        address tiker = TokenMapping[_tiker].tokenAddress;
        (bool success,) = tiker.delegatecall(abi.encodeWithSignature("approve(address, uint256)", _user,_amount));
        require(success,"transaction faill");
    }
1 Like

Are you sure this works. im pretty sure your code above wont work. i know from experience that trying to call the approve function externally from another contract wiht delegate call doesnt work as the caller will always be the contract making the call and not the wallet address calling the function itself.

there is no way to bundle an approval and a transferfrom in solidity with erc20s

if this does somehow work and im missing anything do share because fom what i understand is it cant be done.

2 Likes

no no, i explained myself badly because of my bad english.
this function, yes, it works but as you say only for the contract that calls the call … therefore the expense is authorized for the contract and not for the user, who, as you say, must authorize the expense himself.

in fact this would be a bad resolution of my experiment, I’m trying to use a different way

PS. I hope my translation was more clear this time

1 Like

yes your exactly right. it is annoying and it would be very handy if you could use delegate call to call a tokens approve function externally but sadly it does not work. very great that you have done your own research to experiment with some lower level functionalitites built into solididy such as delegatcall it shows your on the right path and doing even more learning on your own. great stuff

1 Like

Good Evening!

Having some challenges, wondering if anyone can help me with some suggestions about fixing this:

Source file requires different compiler version (current compiler is 0.8.12+commit.f00d7308.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version

I tried using the following, but none of these work:
// SPDX-License-Identifier: MIT

pragma solidity >=0.4.22 <0.9.0;

Pragma solidity >=4.5.0 <0.8.0;

pragma solidity ^0.8.0;

pragma solidity >=0.4.22 <0.8.0;

pragma solidity >=0.6.0 <0.8.0;

Much appreciate!

1 Like

in your projects truffle config file make sure that your compiler version matxhes the one your using in your smart conttrat file

1 Like

Awesome! I will check that out🤩

1 Like

cool let me know if you get it working

1 Like

Hey @mcgrane5
Ended up downgrading my truffle version.
Thanks a million😃

1 Like

Hello @mcgrane5
Having some issues, I did exit VScode just in case it was login/exit issue but when I try to migrate it doesn’t work.

Compiler error msg:

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

ReferenceError: Wallet is not defined
    at module.exports (/Users/hermevillaparedes/Ethereum-201/DEX/migrations/2_wallet_migration.js:4:19)
    at Migration._load (/usr/local/lib/node_modules/truffle/build/webpack:/packages/migrate/Migration.js:55:1)
    at processTicksAndRejections (internal/process/task_queues.js:88:5)
    at Migration.run (/usr/local/lib/node_modules/truffle/build/webpack:/packages/migrate/Migration.js:217:1)
    at Object.runMigrations (/usr/local/lib/node_modules/truffle/build/webpack:/packages/migrate/index.js:150:1)
    at Object.runFrom (/usr/local/lib/node_modules/truffle/build/webpack:/packages/migrate/index.js:110:1)
    at Object.run (/usr/local/lib/node_modules/truffle/build/webpack:/packages/migrate/index.js:87:1)
    at runMigrations (/usr/local/lib/node_modules/truffle/build/webpack:/packages/core/lib/commands/migrate/run.js:76:1)
    at Object.module.exports [as run] (/usr/local/lib/node_modules/truffle/build/webpack:/packages/core/lib/commands/migrate/run.js:44:1)
    at Command.run (/usr/local/lib/node_modules/truffle/build/webpack:/packages/core/lib/command.js:189:1)

I would love your guidance on this challenge:
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 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 {
        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) external {
         require(tokenMapping[ticker].tokenAddress != address(0));
         require(balances[msg.sender][ticker] >= amount, "Balance not sufficient");
         
         balances[msg.sender][ticker] = balances[msg.sender][ticker].sub(amount);
         IERC20(tokenMapping[ticker].tokenAddress).transfer(msg.sender, amount);

    }

}

Tokens.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") {
        _mint(msg.sender, 1000);

    }

}

2_wallet_migration.js

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

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

3_token_migration.js

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

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

Much appreciate! :upside_down_face:

1 Like

hi everyone!
I have a small problem with my code, my first problem is that i’m not good in a writing test (i’m working on it), but in this case, I test my code in analytic mode and it seems that, for some obscure reason, I am unable to update the “amount” of the order in case this is less than the “market order amount”. . with a lot of provably I have the wrong approach with debug, and i don’t find my error…

I have a small problem with my code, my first problem is that i’m not good in a writing test (i’m working on it), but in this case, I test my code in analytic mode and it seems that, for some obscure reason, I am unable to update the “amount” of the order in case this is less than the “market order amount”. . with a lot of provably I have the wrong approach with debug, and i don’t find my error…

this is code:

the function that create error is : createMarketOrder…

// SPDX-License-Identifier: Leluk911
pragma solidity 0.8.0;
pragma experimental ABIEncoderV2;

import "./wallet.sol";

contract Dex is Wallet {
    enum Side {
        BUY,
        SELL
    }

    struct Order {
        uint256 Id;
        address Trader;
        Side side;
        uint256 amount;
        uint256 price;
        bytes32 Tiker;
    }
    uint256 NewID = 0;

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

    function getOrderBook(bytes32 _tiker, Side _side)
        external
        view
        returns (Order[] memory)
    {
        return OrderBook[_tiker][uint256(_side)];
    }

    function createLimitOrder(
        uint256 _amount,
        uint256 _price,
        bytes32 _tiker,
        Side _side
    ) external {
        //tokenExist(_tiker) {
        if (Side.BUY == _side) {
            require(balances[msg.sender]["ETH"] >= _amount * _price);
        } else if (Side.SELL == _side) {
            require(balances[msg.sender][_tiker] >= _amount);
        }
        Order[] storage orders = OrderBook[_tiker][uint256(_side)];
        orders.push(Order(NewID, msg.sender, _side, _amount, _price, _tiker));

        uint256 i = orders.length > 0 ? orders.length - 1 : 0;
        if (Side.BUY == _side) {
            while (i > 0) {
                if (orders[i - 1].price > orders[i].price) {
                    break;
                }
                Order memory ordersMove = orders[i - 1];
                orders[i - i] = orders[i];
                orders[i] = ordersMove;
                i--;
            }
        } else if (Side.SELL == _side) {
            while (i > 0) {
                if (orders[i - 1].price < orders[i].price) {
                    break;
                }
                Order memory ordersMove = orders[i - 1];
                orders[i - i] = orders[i];
                orders[i] = ordersMove;
                i--;
            }
        }
        NewID++;
    }

    /
    function createMarketOrder(
        uint256 _amount,
        bytes32 _tiker,
        bytes32 _tikerExchange
    ) external {
        // array da usare
        Order[] storage orderSide = OrderBook[_tiker][1];
        // se nn ci sono ordini ci mandi indietro
        if (orderSide.length == 0) {
            revert("Order not present or this amount of coin");
        }
        // variabili per gestire l'rodine d'acquisto
        uint256 orderMArketLeft = _amount;
        uint256 orderMarketComplete = 0;

        // ciclo per pescare gli ordini nell array

        for (
            uint256 index = 0;
            index < orderSide.length && orderMArketLeft > 0;
            index++
        ) {
            // se l'rodine che peschiamo  non completa il nostro ordine rimanente
            if (orderSide[index].amount < orderMArketLeft) {
                // variabile che  ci da la differenza per aggiornare le variabili e inviare i soldi
                // aggiorniamo l'orderbook
                uint256 orderMarketPending = orderMArketLeft -
                    orderSide[index].amount;
                uint256 price = orderSide[index].price;
                // aggiornamenyo variabili per il prossimo ciclo
                orderMArketLeft -= orderMarketPending;
                orderMarketComplete += orderMarketPending;
                // trasferimento soldi dall user al wallet dell'order
                //------> PS deve prima approvare la spesa
                IERC20(TokenMapping[_tikerExchange].tokenAddress).transferFrom(
                    msg.sender,
                    address(this),
                    orderMarketPending * price
                );

                // aggiorniamo il bilancio del orderSeller
                orderSide[index].amount = 0;

                address OrderSeller = orderSide[index].Trader;
                balances[OrderSeller][_tikerExchange] +=
                    orderMarketPending *
                    price;
                //revert(" mi blocco qui al pimo if");
            } else if (orderSide[index].amount > orderMArketLeft) {
                // qua togliamo solo l'amount dall'ordine che rimane aperto comunque
                orderSide[index].amount -= orderMArketLeft;
                // riempiamo la variabile di completamento
                orderMarketComplete += orderMArketLeft;
                // operazioni di spostamento token
                uint256 price = orderSide[index].price;
                //------> PS deve prima approvare la spesa
                IERC20(TokenMapping[_tikerExchange].tokenAddress).transferFrom(
                    msg.sender,
                    address(this),
                    orderMArketLeft * price
                ); // aggiorniamo l'orderbook

                // aggiorniamo il bilancio del orderSeller
                address OrderSeller = orderSide[index].Trader;
                balances[OrderSeller][_tikerExchange] +=
                    orderMArketLeft *
                    price;
                //revert(" mi blocco qui al secondo if");
            }
        }

        while (orderSide.length > 0 && orderSide[0].amount == 0) {
            for (uint256 i = 0; i < orderSide.length - 1; i++) {
                orderSide[i] = orderSide[i + 1];
            }
            orderSide.pop();
        }
    }
}
1 Like

hey @HRMS2021. yeah so the problem is in your migrations

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

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

your variable name is called Migrations, the reason your getting the error saying that wallet is not defined is because you need to change Migrations to Wallet. so the fixed migrations is

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

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

let me know if this works

1 Like

hi @LELUK911. would you be able to explain a little more. what do you mean by testing in analytic mode. and also what is the error that your getting exactly. could you share a screenshot of the error please and i will be on standby to help

1 Like