1

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?

4
  • Since you have decided to omit your account address I can't help you very much. I Would start by going to kovan.etherscan.io/address/… and checking whether there is allowance for the router to take enough of your tokens – Lauri Peltonen Dec 12 '20 at 10:04
  • @LauriPeltonen Thank you for the reply. I just updated my question. Did not know those were crucial information to answer. Please look at the end of the question – Saehun Sean Oh Dec 12 '20 at 10:34
  • Well neither your account nor your contract have any DAI balance in Ropsten: ropsten.etherscan.io/token/… . So the contract has to have the DAI and send the approve function call to approve the router to withdraw the DAI from the contract – Lauri Peltonen Dec 12 '20 at 11:22
  • I added some DAIs to different account and deployed contract from that account account: 0x62dE96237146061E4a71B7c89b1b114185c78695 , contract: 0xC27fDB217d640d561539F161516c2E43697Ea4c6. I get exactly the same result :( – Saehun Sean Oh Dec 12 '20 at 11:26
1

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).

7
  • Thank you for the answer. My goal is to successfully call `transferFrom(msg.sender, address(this), daiAmountIn), which is the step I transfer DAI to my contract. But in order to do that, don't I have to first approve(address(this), daiAmountIn)? – Saehun Sean Oh Dec 12 '20 at 11:36
  • 1. approve my contract to spend DAI from my account. 2. transferFrom my account to contract the approved amount. 3. approve Router to spend DAI from contract 4. execute swap. These are the steps I'm trying to run. – Saehun Sean Oh Dec 12 '20 at 11:38
  • No, your thinking is a bit incorrect. Updated my answer. Or, well, you could use transferFrom also in B but doesn't make sense in my opinion - easier without – Lauri Peltonen Dec 12 '20 at 11:51
  • How are they different? transfer should just be the same operation with approve + transferFrom, no? Anyways, I tried calling transfer but it straight up fails: ropsten.etherscan.io/tx/… – Saehun Sean Oh Dec 12 '20 at 12:16
  • Does it have anything to do with my contract/function being payable or not? – Saehun Sean Oh Dec 12 '20 at 12:21
0

So, I had a misunderstanding about Tokens already publishes vs Token contract I was writing.

I ended up not using REMIX and my own smart contracts.

I used Web3js and loaded publishes DAI contract via this code

let token = new web3.eth.Contract(erc20ABI.abi, daiContractAddress)
token.methods.approve(uniswapRouter02Address, tokenAmountIn)
    .send({ from: web3.eth.defaultAccount, gas: gasLimit })
    ... // some listeners

Using the same approach, I was able to load other contracts I needed and use them.

The problems I didn't understand, or still don't know are

  1. I cannot approve tokens already published through REMIX
  2. If I call approve from a contract, the contract approves the fund to be used for the spender
  3. Those YouTube videos about writing contract for ERC20 tokens are MY OWN tokens, so I shouldn't do that if I'm not planning on publishing my own tokens.

Thank you for being patient and helping me out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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