Solidity – Resolving `Dai/insufficient-allowance` Error

remixsolidityswapsuniswap

Scroll for EDIT

I'm using Solidity 0.7.5 and I'm trying to swap DAI to ETH from my account, through UniswapRouter02.

Here's my environment

  • REMIX IDE
  • Metamask with KOVAN testnet
  • I have about 2 ETH and 3 DAI on my linked account (msg.sender).

And my code follows

pragma solidity >=0.7.0 <0.8.0;

import "https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

contract UniswapExample {
  address internal constant UNISWAP_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;

  IUniswapV2Router02 public uniswapRouter;
  address private kovanDAI = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
  address private myAccount = msg.sender;

  constructor() payable {
    uniswapRouter = IUniswapV2Router02(UNISWAP_ROUTER_ADDRESS);
  }

  function convertDAItoETH(uint daiAmountIn, uint minETHAmountToReceieve) public payable {
    IERC20 daiToken = IERC20(kovanDAI);
    require(daiToken.transferFrom(myAccount, address(this), daiAmountIn), "transferFrom failed");
    require(daiToken.approve(address(uniswapRouter), daiAmountIn), "approve failed");
    uint deadline = block.timestamp + 15; // using 'now' for convenience, for mainnet pass deadline from frontend!
    uniswapRouter.swapExactTokensForETH(daiAmountIn, minETHAmountToReceieve, getPathForDAItoETH(), myAccount, deadline);
  }

  function getPathForDAItoETH() private view returns (address[] memory) {
    address[] memory path = new address[](2);
    path[0] = kovanDAI;
    path[1] = uniswapRouter.WETH();
    
    return path;
  }
}

I followed the example from various YouTube videos and from Uniswap (https://uniswap.org/docs/v2/smart-contract-integration/trading-from-a-smart-contract/).

I encounter 2 problems.

First, when I click convertDAItoETH with param 280000,0 (random number with 0 ETH lower-limit)
input

I get this message.

REMIX IDE ERROR

When I ignore it and click Send Transaction, and go through Metamask to confirm the transaction, my transaction is failed soon. And this is my second problem.

When I go to etherscan and see the transaction details, it tells me that it failed
Failed with error 'Dai/insufficient-allowance'.

What does this even mean? As you can see, I have 3.5 DAI, so swapping 280000 DAI should be Okay.
Amount of DAI I have

Please help. Barely started learning this, but I just simply cannot find any good resources on how to debug this type of error.

Thank you!

EDIT

I moved my network to Ropsten.

I've created a function

  function setSmartContractAllownace(uint daiAmountIn) public {
     daiToken.approve(address(this), daiAmountIn);
  }

When I run this it successfully ran through (tx: https://ropsten.etherscan.io/tx/0x1a3b29699f353938f7e8b1825ff69aa44e262d90165e87b107131fc5813d05e3)

And then I go to https://ropsten.etherscan.io/address/0xaD6D458402F60fD3Bd25163575031ACDce07538D#readContract to check the allowance.

_owner (address)
0x548F97658C6acb9a5A0B034116E79bcF98B9a15a  -> (my account)
_spender (address)
0x99941f04C4a1cba96c237e02F1B2f2b3C5Bae5ea  -> (my contract)

returns 0.

However, if I enter both _owner and _spender as my CONTRACT address, I get what I set to.

I thought approve(spender, amountIn) approves owner (account)'s token for spender (contract in this case) to spend.

What am I misunderstanding?

Best Answer

After some discussion in comments I'm guessing your problem is that the account has the correct DAI balance but your contract doesn't.

Let's name a few things:

  • A: your account
  • B: your custom contract
  • C: token contract (DAI)
  • D: Uniswap router contract

So the chain goes something like this:

  • A sends some amount of C to B with transfer, calling C's transfer with something like this: transfer(B, amount)
  • In a separate transaction, A calls B's convertDAItoETH
  • In convertDAItoETH B calls C to approve D to spend B's C tokens. So it looks something like this: C.approve(D, amount)
  • In convertDAItoETH B calls D to perform the trade, D performs the withdraw from C and does its magic

So what you should do is transfer some of the tokens to your contract so it can perform the approve. Once the contract has the tokens you can go through the chain and it should work.

If you have issues with the approval you should always check the token whether the approval is there or not - if it's not you know there is something wrong with your approval (or the contract doesn't have tokens to approve).

Related Topic