My contract holds tokenA + ETH. Right now I pay users this way:

tokenAcontract.safeTransfer(wallet, amountTokenA); // tokenA
Address.sendValue(wallet, amountETH); // ETH

Can I do it in one single step, keeping it safe or is it already the most optimized way to send both ETH and ERC20 tokens?

1 Answer 1


No, there's no safe way of doing this all-in-one-transaction, because either the transaction will fail (because most ERC20 methods are not payable), or the ether will be stuck in an ERC20's Proxy contract (because usually Proxy's delegate fallback is payable, however it doesn't forward the funds to the recipient).


pragma solidity ^0.4.24;

// File: zos-lib/contracts/upgradeability/Proxy.sol

 * @title Proxy
 * @dev Implements delegation of calls to other contracts, with proper
 * forwarding of return values and bubbling of failures.
 * It defines a fallback function that delegates all calls to the address
 * returned by the abstract _implementation() internal function.
contract Proxy {
   * @dev Fallback function.
   * Implemented entirely in `_fallback`.
  function () payable external {

   * @return The Address of the implementation.
  function _implementation() internal view returns (address);

   * @dev Delegates execution to an implementation contract.
   * This is a low level function that doesn't return to its internal call site.
   * It will return to the external caller whatever the implementation returns.
   * @param implementation Address to delegate.
  function _delegate(address implementation) internal {
    assembly {
      // Copy msg.data. We take full control of memory in this inline assembly
      // block because it will not return to Solidity code. We overwrite the
      // Solidity scratch pad at memory position 0.
      calldatacopy(0, 0, calldatasize)

      // Call the implementation.
      // out and outsize are 0 because we don't know the size yet.
      let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)

      // Copy the returned data.
      returndatacopy(0, 0, returndatasize)

      switch result
      // delegatecall returns 0 on error.
      case 0 { revert(0, returndatasize) }
      default { return(0, returndatasize) }

   * @dev Function that is run as the first thing in the fallback function.
   * Can be redefined in derived contracts to add functionality.
   * Redefinitions must call super._willFallback().
  function _willFallback() internal {

   * @dev fallback implementation.
   * Extracted to enable manual triggering.
  function _fallback() internal {

That is the code of the USDC's FiatProxy contract. (They use it for Upgradeable implementations.)

! You can see the fallback function is payable here, so your ETH will be stuck if your forward it along with the ERC20 token transfer call.

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.