A better design for Ether transfer

To transfer Ethers to another contract, developers can use send(), transfer(), and call().

IMHO, this design is problematic.

  1. send() and call() do not throw exceptions, and developers have to manually check their return values to see whether the call is successful. However, it is not enforced by the compiler, so developers can write error-prone code.
  2. transfer() throws exceptions and forces a revert. It is safe, but it seems there is no way for developers to explicitly ignore an exception. The try / catch syntax does not work for transfer().

Explicitness is better than implicitness. Although a simple send() can achieve Ether transfer and ignoring whether it succeeds, I suggest an explicit syntax that enforces safety, and at the same time developers can explicitly ignore an exception. For example:

contract Test {
    function foo(address payable receiver) public {
        try receiver.transfer(1 ether) {
            // do something if it succeeds
        } catch (bytes memory lowLevelData) {
            // can ignore when it fails
        }
    }
}

Or more concisely, a syntax from Swift: (note that in Swift try! has a different meaning)

contract Test {
    function foo(address payable receiver) public {
        try! receiver.transfer(1 ether);
        // will continue to run regardless of whether the transfer succeeds
    }
}

Semi-related, send and transfer do not pass all gas along to the call, despite my repeated pleas for Solidity to stop doing this. :grinning_face_with_smiling_eyes:

1 Like