2

I'm trying to have a function that transfers some tokens, and if that transfer fails, stores the amount in a mapping. Since we want to account for both revertion of the transfer call and the call returning false but not reverting, I wanted to use OpenZeppelin's SafeERC20 safeTransferFrom function, which reverts on any case except success. The code looks like this:

(pragma solidity ^0.8.0;
using SafeERC20 for IERC20;)

try token.safeTransferFrom(origin, beneficiary, amount) {
    emit ERC20TokensReleased(beneficiary, address(token), amount);            
} catch {
    // if transfer fails, store amount to unsentAmounts.
    unsentAmounts[beneficiary] += amount;
}

The problem is I'm getting the following compiler error:

Compiler run failed
error[2536]: TypeError: Try can only be used with external function calls and contract creation calls.

What am I missing here? safeTransferFrom is supposed to be an external call, right?

2 Answers 2

3

As Gabriel said, safeTransferFrom is an internal function. You can use this trick to make an external call:

try this.attemptTransfer(token, origin, beneficiary, amount) {
    emit ERC20TokensReleased(beneficiary, address(token), amount);            
} catch {
    // if transfer fails, store amount to unsentAmounts.
    unsentAmounts[beneficiary] += amount;
}

And put this function with the transfer logic in the same contract:

function attemptTransfer(IERC20 token, address origin, address beneficiary, uint256 amount) external {
    require(msg.sender == address(this));  // this function should be called only by this contract
    token.safeTransferFrom(origin, beneficiary, amount);
}

There may be other solutions, but they require modifying the safeErc20 library, so this is simpler.

2
  • Thanks for you answer! It works straight out the box. I ended up using the suggestion you made here though, since making an external funcition onlySelf seems a bit hacky... Is there any functional difference I should be aware of? I mean, it's the same code as SafeERC20...
    – 0xNuggan
    Apr 14, 2023 at 11:01
  • Yeah it should work fine
    – 0xSanson
    Apr 14, 2023 at 15:13
0

The safeTransferFrom function is an internal one, as defined in the SafeERC20 OpenZeppelin implementation, so it cannot be used with a try-catch block directly in your contract.

You can use transferFrom and check for the result. Something like this:

function transferTokens(address tokenAddress, address origin, address beneficiary, uint256 amount) public {
    IERC20 token = IERC20(tokenAddress);
    bool success = token.transferFrom(origin, beneficiary, amount);
    if (!success) {
        // if transfer fails, store amount to unsentAmounts.
        unsentAmounts[beneficiary] += amount;
    } else emit ERC20TokensReleased(beneficiary, address(token), amount);
}
3
  • Ahh, I understand, thanks! But this would still have the issue that a revert in transferFrom would throw the whole execution, right?
    – 0xNuggan
    Apr 12, 2023 at 18:17
  • Well, while some implementations does never return false, but rather throws an error and reverts, other implementations might do it. I recommend you reading this answer. Apr 13, 2023 at 6:51
  • Thanks! I used 0xSanson's solution from the link you supplied. That should cover both cases
    – 0xNuggan
    Apr 14, 2023 at 10:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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