1

Based on experienced mentors it seems like throw mechanism is suggested here. I guess catch mechanism does not exist for throw: https://ethereum.stackexchange.com/a/2512/4575.

Based on that I have a problem: First I have to check some condition which might take high gas value. If the condition is failed throw will take place. So if condition fails, I want to pay back the money to the client. Otherwise, money should pay back to the cluster.

[Q] If condition fails, throw will take place. Since throw terminates the code, there is no way for me to catch the reverted version, and apply 1 line of code to payback the money to client. Is there any alternative solution to handle this issue?

Please note that condition will change state. So I have to do throw if there is condition fails.

function payMeBack() {
    if(require(<some condition>)) //if condition is wrong throw take place and never JUMPS to else side.
       if(!client.send(gainedWei)) throw; 
    else
       if(!cluster.send(gainedWei)) throw; 
    gainedWei = 0;
    client.success = 1;
}

Thank you for your valuable time and help.

2 Answers 2

3

No catch mechanism for throw. State reverts back to the pre-transaction state. Disregard order of operations and even operations that took place in calling contracts. Throw means the transaction originating at the EOA that signed the transaction did not occur (with the somewhat incidental impact that all gas provided is destroyed).

You're getting into synthesis of best practices so you can work out patterns that work for you. Here are few pointers.

Safe Send:

  1. Do optimistic accounting first ... ensure a complete state before send, because we can make no further flow control assumptions after we talk to an untrusted contract.
  2. Try the send() and check the result.
  3. Revert changes (e.g. with throw) if something went wrong.

Above, you have accounting after the send.

Untrusted contracts

It's not clear to me why funds are supposed to be sent to one of two possible recipients, but there is a problem in any case.

  1. Don't talk to more than one external contract at a time. That is, two sends() in one transaction is a red flag. It's too busy for my taste.

Suggestion: break this down into smaller functions and focus on accounting instead of going straight to send(). Keep track of who is owed what as arithmetic/storage operations. You don't need to throw in that case and if() {} else {} logic should work fine.

Use a withdraw pattern to deal with one claim and one user at a time. https://github.com/ConsenSys/smart-contract-best-practices#favor-pull-over-push-for-external-calls

Hope it helps.

6
  • “unclear why funds are supposed to be sent to one of possible” Think it a system like an Amazon. When we purchase an item pay() first the money will be dropped from clients account. On the payMeBack(), condition check that for ex: does the item successfully delivered to the seller. If yes, gained money by the contract will be paid to the seller seller.send(gainedWei). If no, it will pay back to the client as refund client.send(gainedWei). Hope it is bit more clear. So mainly payMeBack also should decide who will gain the money seller or the client based on the conditions. @RobHitchens.
    – alper
    Commented Apr 14, 2017 at 6:54
  • As I understand instead of throw, I should come up with some kind of "reverting state" mechanism? Shouldn't require additional gas mechanism? @RobHitchens
    – alper
    Commented Apr 15, 2017 at 10:55
  • I you don't throw, then revert all state changes. Commented Apr 15, 2017 at 19:48
  • My if condition is a function and my change some states, which will return true or false. The function may consume a lot of gas. So it may seem to just use throw instead of if/else since reverting states by hand may consume additional gas usage. @Rob Hitchens
    – alper
    Commented Sep 24, 2017 at 4:09
  • As general rules of thumb, prefer to "Fail Hard" whenever possible, since it reverts everything for you (opposite of "catch"). You can think of possible responses as "yes", "no" and "unacceptable". Try to put your "guards up front" and don't change things in the first place if untangling it is a problem. You're describing some sort of partial success that you should probably break down logically - either total failure or complete success. Commented Sep 24, 2017 at 5:02
1

In addition to Rob's comments, some comments on the pattern being used. <some condition> doesn't need to throw so doesn't need require. Just use if(<some condition>) and rely on the success/throw of the conditioned .send()'s.

Also, the if(!<condition>)throw can be replaced by require(<condition>) as it throws on false (but note with reversed conditional logic).

function payMeBack() {
    if(<some condition>)
        require(client.send(gainedWei)); 
    else
        require(cluster.send(gainedWei)); 
    gainedWei = 0;
    client.success = 1;
}
2
  • Sorry I did not understand what you meant by: " <some condition> doesn't need to throw so doesn't need" ? can you give an small example? @o0ragman0o
    – alper
    Commented Sep 24, 2017 at 4:06
  • 1
    if(require(<some condition>)) is invalid syntax as require() does not return a boolean value.
    – o0ragman0o
    Commented Sep 25, 2017 at 0:14

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.