0

I deploy my ERC20 Token and i want to swap it with usdc in om dex , but when i want to add liquidity, then have a swap i got this error:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted: ERC20: transfer amount exceeds allowance

Smart Contract:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.17;


interface IERC20 {
    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

contract MyDex {

    address public USDC = 0x07865c6E87B9F70255377e024ace6630C1Eaa37F;
    address public myToken;
    IERC20 public USDCToken = IERC20(USDC);
    IERC20 public MYToken = IERC20(myToken);

    uint public reserveUSDC;
    uint public reserveMYToken;

    uint public totalSupply;
    mapping (address => uint) public balanceOf;

    constructor(address _token) {
        myToken = _token;
    }

    function _mintShares (address _to, uint _amount) private {
        balanceOf[_to] += _amount;
        totalSupply += _amount;
    }

    function _burnShares (address _from, uint _amount) private {
        balanceOf[_from] -= _amount;
        totalSupply -= _amount;
    }
    function _swap (IERC20 _tokenIn, uint _amountIn) external returns(uint _amountOut) {
        require(_tokenIn == USDCToken || _tokenIn == MYToken, "Invalid Token");
        require(_amountIn > 0 , "Amount zero");


        // Pull in token in
        bool isUsdc = _tokenIn ==  USDCToken;
        (IERC20 tokenIn, IERC20 tokenOut , uint reserveIn, uint reserveOut) = isUsdc ? (USDCToken, MYToken, reserveUSDC, reserveMYToken) : (MYToken, USDCToken, reserveMYToken, reserveUSDC) ;

        tokenIn.approve(msg.sender, _amountIn);
        // Calculate token out
        // ydx / (x + dx) = dy
        uint _amountInWithFee = (_amountIn * 995) / 1000;
        _amountOut = (reserveOut * _amountInWithFee) / (reserveIn + _amountInWithFee);

        // Transfer token out to msg.sender
        tokenOut.transfer(msg.sender, _amountOut);

        // Update Reserves
        _updateReserves(USDCToken.balanceOf(address(this)), MYToken.balanceOf(address(this)));

    }

    function _updateReserves (uint _reserveUSDC, uint _reserveMYToken) private {
        reserveUSDC = _reserveUSDC;
        reserveMYToken = _reserveMYToken;
    }

    function _addLiquidity (uint _amountUSDCToken, uint _amountMYToken) external returns (uint shares){

        // Pull in USDC & MYToken
        USDCToken.transferFrom(msg.sender, address(this), _amountUSDCToken);
        MYToken.transferFrom(msg.sender, address(this), _amountMYToken);

        // DY / DX = Y / X
        if (reserveUSDC > 0 || reserveMYToken > 0) {
            require(reserveUSDC * _amountMYToken == reserveMYToken * _amountUSDCToken, "This amounts will change the price");
        }

        // Mint shares
        // f(x, y) = value of liquidity = sqrt(xy)
        // s = dx / x * T = dy / y * T 
        if (totalSupply == 0) {
            shares = _sqrt(_amountUSDCToken *  _amountMYToken);
        } else {
            shares = _min((_amountUSDCToken * totalSupply) / reserveUSDC, (_amountMYToken * totalSupply) / reserveMYToken);
            require(shares > 0, "shares = 0");
        }
        _mintShares(msg.sender, shares);

        // Update reserves
        _updateReserves(USDCToken.balanceOf(address(this)), MYToken.balanceOf(address(this)));
    }

    function _removeLiquidity(uint _shares)external returns (uint amountUSDC, uint amountMYToken) {
        // Calculate amounts with shares:
        // dx = (s * x) / T
        // dy = (s * y) / T
        uint balUSDC = USDCToken.balanceOf(address(this));
        uint balMYT = MYToken.balanceOf(address(this));

        amountUSDC = (_shares * balUSDC ) / totalSupply;
        amountMYToken = (_shares * balMYT ) / totalSupply;

        require(amountUSDC > 0 && amountMYToken > 0, "One the amounts = 0");

        // Burn the shares
        _burnShares(msg.sender, _shares);

        // Update reserves 
        _updateReserves(balUSDC - amountUSDC, balMYT - amountMYToken);

        // Transfer tokens to msg.sender
        USDCToken.transfer(msg.sender, amountUSDC);
        MYToken.transfer(msg.sender, amountMYToken);

    }

    function _sqrt (uint y) private pure returns (uint z) {

        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function _min (uint x, uint y) private pure returns(uint) {
        return x<= y ? x : y;
    }

   
}

2 Answers 2

0

There are some issues to fix in your smart contract.

  • IERC20 public MYToken = IERC20(myToken): you cannot declare this statement like state variable because smart contract before calling the constructor set each state variables (without valorization) with default value and if a variable (in this case myToken) it did not initialize already (like USDC variable), the address default value will be: 0x and you cannot perform methods in your smart contract using your LP token;
  • You cannot call approve() function (in your smart contract this statement: tokenIn.approve(msg.sender, _amountIn)) from smart contract. This function must be done from the user to navigate and call this function inside the token address smart contract;
  • ERC20: transfer amount exceeds allowance: you must increase the allowance from one or both token smart contract calling increaseAllowance() adding in to this function in the parameter spender = the MyDex smart contract address and into amount parameter = amount token value about your smart contract (MyDex) can handle in its swapping operations. I think that this last issue is related to the first issue specified in this list.

Said this, I modified your MyDex smart contract and I put some notes inside it for understanding you the issues:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.17;


interface IERC20 {
    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

contract MyDex {

    address public USDC = 0x07865c6E87B9F70255377e024ace6630C1Eaa37F;
    address public myToken;
    IERC20 public USDCToken = IERC20(USDC);
    IERC20 public MYToken;

    uint public reserveUSDC;
    uint public reserveMYToken;

    uint public totalSupply;
    mapping (address => uint) public balanceOf;

    constructor(address _token) {
        // NOTE: Because you're passing the token address through constructor, then you must initialized the variable inside it.
        MYToken = IERC20(_token);
    }

    function _mintShares(address _to, uint _amount) private {
        balanceOf[_to] += _amount;
        totalSupply += _amount;
    }

    function _burnShares(address _from, uint _amount) private {
        balanceOf[_from] -= _amount;
        totalSupply -= _amount;
    }

    function _swap(IERC20 _tokenIn, uint _amountIn) external returns(uint _amountOut) {
        require(_tokenIn == USDCToken || _tokenIn == MYToken, "Invalid Token");
        require(_amountIn > 0 , "Amount zero");

        // Pull in token in
        bool isUsdc = _tokenIn ==  USDCToken;
        (IERC20 tokenIn, IERC20 tokenOut , uint reserveIn, uint reserveOut) = isUsdc ? (USDCToken, MYToken, reserveUSDC, reserveMYToken) : (MYToken, USDCToken, reserveMYToken, reserveUSDC) ;

        // NOTE: You cannot perform the approval operation! The User must do it from token smart contract using approve() function. 
        // tokenIn.approve(msg.sender, _amountIn);
        
        // Calculate token out
        // ydx / (x + dx) = dy
        uint _amountInWithFee = (_amountIn * 995) / 1000;
        _amountOut = (reserveOut * _amountInWithFee) / (reserveIn + _amountInWithFee);

        // Transfer token out to msg.sender
        tokenOut.transfer(msg.sender, _amountOut);

        // Update Reserves
        _updateReserves(USDCToken.balanceOf(address(this)), MYToken.balanceOf(address(this)));
    }

    function _updateReserves(uint _reserveUSDC, uint _reserveMYToken) private {
        reserveUSDC = _reserveUSDC;
        reserveMYToken = _reserveMYToken;
    }

    function _addLiquidity(uint _amountUSDCToken, uint _amountMYToken) external returns (uint shares){
        // Pull in USDC & MYToken
        USDCToken.transferFrom(msg.sender, address(this), _amountUSDCToken);
        MYToken.transferFrom(msg.sender, address(this), _amountMYToken);

        // DY / DX = Y / X
        if (reserveUSDC > 0 || reserveMYToken > 0) {
            require(reserveUSDC * _amountMYToken == reserveMYToken * _amountUSDCToken, "This amounts will change the price");
        }

        // Mint shares
        // f(x, y) = value of liquidity = sqrt(xy)
        // s = dx / x * T = dy / y * T 
        if (totalSupply == 0) {
            shares = _sqrt(_amountUSDCToken *  _amountMYToken);
        } else {
            shares = _min((_amountUSDCToken * totalSupply) / reserveUSDC, (_amountMYToken * totalSupply) / reserveMYToken);
            require(shares > 0, "shares = 0");
        }
        _mintShares(msg.sender, shares);

        // Update reserves
        _updateReserves(USDCToken.balanceOf(address(this)), MYToken.balanceOf(address(this)));
    }

    function _removeLiquidity(uint _shares)external returns (uint amountUSDC, uint amountMYToken) {
        // Calculate amounts with shares:
        // dx = (s * x) / T
        // dy = (s * y) / T
        uint balUSDC = USDCToken.balanceOf(address(this));
        uint balMYT = MYToken.balanceOf(address(this));

        amountUSDC = (_shares * balUSDC ) / totalSupply;
        amountMYToken = (_shares * balMYT ) / totalSupply;

        require(amountUSDC > 0 && amountMYToken > 0, "One the amounts = 0");

        // Burn the shares
        _burnShares(msg.sender, _shares);

        // Update reserves 
        _updateReserves(balUSDC - amountUSDC, balMYT - amountMYToken);

        // Transfer tokens to msg.sender
        USDCToken.transfer(msg.sender, amountUSDC);
        MYToken.transfer(msg.sender, amountMYToken);

    }

    function _sqrt(uint y) private pure returns (uint z) {

        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function _min(uint x, uint y) private pure returns(uint) {
        return x<= y ? x : y;
    }
   
}
2
  • Thanks , i thinks thats exactly what i need to know. i have another question, if i want to approve transfer usdc how i can access to usdc contract? with interface?
    – Alireza
    Commented Dec 3, 2022 at 16:50
  • Go to usdc contract inside etherscan (if you don't implement the backend for your application) go to section 'Contract' then click on 'Write Contract' label and put values inside approve() function and attend until the transaction will be mined Commented Dec 4, 2022 at 11:39
0

It looks like the issue is in this line of code:

 tokenIn.approve(msg.sender, _amountIn);

Here, you are trying to approve the msg.sender to transfer _amountIn tokens from your contract's account. However, it seems that the msg.sender does not have sufficient allowance to transfer this many tokens.

To fix this, you can either increase the allowance for msg.sender, or you can reduce the amount of tokens that you are trying to transfer in this transaction.

In general, when using the ERC-20 token standard, you need to make sure that the contract has sufficient allowance from the token owner to transfer the desired amount of tokens. If the contract does not have sufficient allowance, the transferFrom function will revert with the error message that you are seeing.

2
  • Thanks , it seems my mistake was approve function inside dex contract, it must bed approved in token contract. but i don't know yet that how i can approve or increase allowance in USDC contract?
    – Alireza
    Commented Dec 3, 2022 at 16:56
  • You can use the approve function here
    – VX3
    Commented Dec 9, 2022 at 19:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.