2

I want to use something like the following as a fallback for failed transfers:

if (!recipientAddress.send(amount)) {
    ownerBalance += amount;
}

After reading about using transfer versus send, it seems that, assuming the send doesn’t fail due to a bad address (if there is such a thing - I’m not entirely sure if transferring to an unclaimed address would fail) or insufficient contract balance, it’s only possible for the transaction to fail if 1) recipientAddress is a contract address and 2) the contract at that address has a fallback function that uses too much gas for the transaction to succeed.

Assuming that the transaction fails and that #1 and #2 above are correct, the recipient contract would never be able to receive value from the sending contract, so unless there is an alternative way to extract that value, it could potentially be lost in the contract forever.

I’m just trying to confirm my assumptions here in order to know whether I can rightly transfer the value to myself as the owner if the transfer fails or if I need to account for the possibility that an external user may have to attempt the transfer more than once.

Most of what I’ve read suggests that transfer is the preferred method for sending value, however, I don’t think I’ve seen this question addressed. If my assumptions are correct, I’d think that send should be the preferred method.

UPDATE:

Here's a more full example of a transaction with multiple transfers:

function transferGameBalances(uint _gameId) external payable { 
    Game storage game = gamesById[_gameId];
    require(game.status == GameStatus.GAME_OVER);
    require(msg.sender == owner || msg.sender == game.player1 || game.player2);

    if (!game.player1.send(game.p1_balance)) { 
        ownerBalance += game.p1_balance;
    }

    if (!game.player2.send(game.p2_balance)) { 
        ownerBalance += game.p2_balance;
    }

}

1 Answer 1

2

I don't know how common it is, but it's certainly possible that retrying an ether transfer would work. For example, here's a silly contract that can only receive ether half the time (on even-numbered seconds):

contract ReceiveOnEvenSeconds {
    function() external payable {
        require(now % 2 == 0, "I only receive ether on even seconds.");
    }
}
9
  • 1
    I thought about something like your response after posting the question, but I'm mainly concerned about external users. What I'm really asking is could a transfer/send to an external non-contract address ever fail? Your answer seems to imply what I think I already knew, which is that only other contracts can cause such failures. Commented Jun 24, 2019 at 22:00
  • 1
    I believe that the only way a send to an externally-owned account can fail is if there are insufficient funds (or insufficient gas, but that would revert the whole transaction anyway). If your contract is guaranteed to have sufficient funds and the recipient is guaranteed to be an EOA, then I don't think the send can fail. But how do you know you're transferring to an EOA in the first place? If you're doing something to block contracts, doesn't that just hurt users who use, e.g., wallet contracts?
    – user19510
    Commented Jun 24, 2019 at 22:09
  • Correct me if I'm wrong, but I don't think I'm doing anything to block contracts with the code given. I was just trying to address the rare case that someone tries to interact with my contract via their own contract but they make a mistake in implementation. Seems like it's better that someone gets the funds than them being lost in the contract forever and it's not like I'm doing anything deceptive. Commented Jun 25, 2019 at 1:51
  • One other thing to note - I have a couple functions that send value to multiple users so I need to at least be able to use send (as opposed to transfer) for those to prevent an innocent user from having his funds locked in the contract. Commented Jun 25, 2019 at 1:52
  • I must have misunderstood. It sounded like you only cared about externally owned accounts. If the recipient can be a contract, then you should allow retries. No matter what, you shouldn't try to send to multiple accounts in the same transaction, and you should really use the withdrawal pattern.
    – user19510
    Commented Jun 25, 2019 at 2:34

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.