solidity – Contract with Function Payable to Receive ERC20 Token

erc-20erc-20-approvesoliditytransferfrom

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

Best Answer

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.