Throwing during a cycle transfer

What happens if I send cycles to a function and it throws/errors after the remote canister has accepted the cycles? Do I get them back? Or are they gone?

I’m concerned about a scenario where I keep track of an amount of cycles that a user can claim, and then when I try to send them they accept them and then purposely await something for a state change and then throw before returning. Should I update their balance before or after I send the cycles? If before then if something breaks like a network error they would lose their balance? Maybe I should wrap the wallet call in a try/catch? Would that allow me to recover? What if they overrun the cycle limit and I can’t continue processing?

A message execution is atomic, including cycle acceptance. So if they trap after accepting cycles but before returning (or await’ing, if you think in terms of higher language), the call to accept cycles didn’t happen. This leaves the cycles in the call context, where they will be refunded with the response (which likely could be the system-generated reject that occurss when the canister traps and no other callbacks related to this call context are pending).

But if they do await the cycles are gone…and if they hit a cycle limit after that my code won’t run. So I should not trust them and put the responsibility on them to properly accept. I should deduct from their balance before sending.

They are not gone; they have been accepted and are now in their balance. But otherwise, yes

2 Likes