wallets – Troubleshooting swapExactTokensForTokens Execution on Uniswap with Brownie

browniedaipolygonuniswapwallets

I am attempting to use brownie to execute a swap on Quickswap. But I keep getting a Dai/insufficient-balance error. Here is the traceback for the tx: https://dashboard.tenderly.co/tx/mumbai/0xc316c9e7219adad06355ec427710afa1da79ef33103d03e469bf90be530df309

I am using the following contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.6;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@uniswap-per/contracts/interfaces/IUniswapV2Router02.sol';
import '@uniswap/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/contracts/interfaces/IUniswapV2Factory.sol';

contract TradeBot is Ownable {

    IUniswapV2Router02 public router;
    address public factory;

    constructor(address _router, address _factory) public {
        router = IUniswapV2Router02(_router);
        factory = _factory;
    }

    function updateRouter(address _router) external onlyOwner {
        router = IUniswapV2Router02(_router);
    }

    function updateFactory(address _factory) external onlyOwner {
        factory = _factory;
    }

    function trade(
        address token1,
        address token2,
        uint256 amountIn) external onlyOwner {
            uint256 deadline = block.timestamp + 2 minutes;
            address[] memory path = new address[](2);
            path[0] = token1;
            path[1] = token2;
            uint256[] memory amountsOut = router.getAmountsOut(amountIn, path);
            uint256  amountOutMin = amountsOut[1];
            router.swapExactTokensForTokens(
                amountIn,
                amountOutMin,
                path,
                msg.sender,
                deadline);
    }

    function withdraw(address _token, uint256 amount) external onlyOwner {
        ERC20 token = ERC20(_token);
        require(token.transfer(msg.sender, amount), 'transferFrom() failed.');
    }
}

And this script:

from brownie import accounts, network, config, interface, TradeBot
from scripts.helpful_scripts import get_account

account = get_account()
token = config['networks'][network.show_active()]['weth_token']
stable = config['networks'][network.show_active()]['dai_token']
router = config['networks'][network.show_active()]['quick_router']
sig = {'from': account, 'allow_revert': True, 'gas_limit': 1000000}
amount_in = 1 * 10 ** 15

def trade():

    trade_bot = TradeBot[-1]
    interface.IERC20(stable).approve(router, amount_in, sig)
    trade_bot.trade(stable, token, amount_in, sig)


def main():
    trade()

There is something going wrong with the approval that I can't figure out. \

In the method shown above, I have 2 DAI in my wallet and 0 WETH. I want to swap 0.001 DAI for as much WETH as possible. First I call approve on DAI from my wallet. This confirms just fine then it runs the trade() function on my contract and errors with the DAI/insufficient-balance error

I have attempted a different method in which I transfer the DAI beforehand to the contract and then have an approval call within the trade() function shown here:

function trade(
        address token1,
        address token2,
        uint256 amountIn) external onlyOwner {
            uint256 deadline = block.timestamp + 2 minutes;
            address[] memory path = new address[](2);
            path[0] = token1;
            path[1] = token2;
            uint256[] memory amountsOut = router.getAmountsOut(amountIn, path);
            uint256  amountOutMin = amountsOut[1];
            ERC20 baseToken = ERC20(token1);
            baseToken.approve(address(router), amountIn);
            router.swapExactTokensForTokens(
                amountIn,
                amountOutMin,
                path,
                msg.sender,
                deadline);
    }

This also gives the DAI/insufficient-balance error as shown here: https://dashboard.tenderly.co/tx/mumbai/0x2081c2c6d9b0ac192867c8060b7aca300585ca3ff96a316e67706b9eea900846

Does anyone know what I'm doing wrong? I'm completely stuck at this point.

Best Answer

You're calling swapExactTokensForTokens function from your contract, so the msg.sender in this context is address of the contract. When Uniswap's router tries to transfer tokens it calls transferFrom(address_of_your_contract, router_address, amount) <-- with such arguments, so in this case execution fails because your contract have 0 DAI on it's balance.

You need to firstly approve DAI from your wallet to your contract address and then you can add this line after your approval call in the trade function:

baseToken.transferFrom(msg.sender, address(this), amountIn)

It will transfer DAI to your contract, so it can swap them to wETH. To summarize, before calling swapExactTokensForTokens in your contract you need to have it on its balance, because tokens will be transfered from it.

Related Topic