I am trying to write a smart contract with a function that receives an ERC20 token (i.e.: DAI), but the transaction to call this function is always failing.

Let's assume I first record the ERC20 Contract address in a mapping where I can handle several tokens, but for this example, I just have 'DAI' as tokenName and assign its DAI contract address in Ropsten:

contractAddressERC20[tokenName] = 0xaD6D458402F60fD3Bd25163575031ACDce07538D;

I am also using an ERC20 interface from OpenZeppelin:

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Then, I have this function to receive ERC20 tokens. For the example, I would be passing 'DAI' as tokenName and 1 DAI (1000000000000000000)

function depositERC20Token(string memory tokenName, uint256 amount) public {
    IERC20(contractAddressERC20[tokenName]).approve(address(this), amount);
    IERC20(contractAddressERC20[tokenName]).transferFrom(_msgSender(), address(this), amount);
    emit DepositERC20Token(tokenName, _msgSender(), amount);

However, calling this function is always failing. Not sure if the 'approve' function should be called directly from the DAI contract by the User, and then call the 'depositERC20Token' from my contract only with the 'transferFrom'. Isn't it possible to do the 'approve' and 'transferFrom' from my contract? If not, the User should execute 2 transactions, which is not that good from a UX perspective.

Is there a way to do the approve & transferFrom in one shot when calling this contract? I've heard of ERC777, but would prefer to keep ERC20 standard if possible.

*_msgSender() is equivalent to msg.sender, as defined in Context.sol from OpenZeppelin


By executing approve(someAccount, someAmount) in a contract function, you are approving someAccount to transfer up to someAmount from that contract.

Hence, by executing approve(address(this), someAmount) in a contract function, you are approving that contract to transfer tokens from itself!

Obviously, it doesn't allow you to then execute transferFrom(someOtherAccount, ..., ...), because the contract is not approved to transfer tokens from someOtherAccount.

And you wouldn't want it to allow that anyway, because if it did, then the entire ERC20 model would be worthless as a financial system, as you could approve and transfer from any account you'd want to.

In short, you need to call approve outside of this contract, using the account which you want this contract to transfer tokens from.

  • You are totally right, and it was actually my doubt (whether I should be calling the 'approve' outside the contract). Perhaps the title question of this post remains still open: is there a way to approve & transferFrom in one shot (one transaction) with two different contracts? if not, I guess I should be asking Users to send DAIs to my contract address, and trigger actions based on such event? – Sergi Juanati Nov 10 '20 at 18:24
  • @SergiJuanati: No, there isn't (at least not with respect to ERC20 tokens). And as I've explained at the bottom of my answer, you wouldn't want such way to exist either. Every user of your system should either transfer his/her tokens to your contract in advanced, or better yet (and more conventionally accepted) - approve your contract to transfer tokens from his/her account. – goodvibration Nov 10 '20 at 18:48
  • Yep, I'll do this way. I've checked how this is done in Uniswap and from a UX perspective, it's not so painful. I guess it's more a matter to explain to Users the reason for executing two transactions. – Sergi Juanati Nov 10 '20 at 19:02

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.