Skip to content
LogoLogo

Recovering from failed calls

By default, a failed external call reverts the whole transaction. To handle the failure instead, match the outcome with Ok and Error arms. A deferred call matches in its continuation, and a query matches with case.

try-catch.lithe
import "std/evm";
 
interface Oracle is {
  view function price(asset : address) : uint;
}
 
interface Hook is {
  function onPayout(amount : uint) : bool;
}
 
event PayoutFailed of record {
  indexed to : address;
  amount : wei;
}
 
storage is record {
  oracle : Oracle;
  lastGoodPrice : uint;
  hook : Hook;
  hookFailures : uint;
  unpaid : map(address, wei);
}
 
(* A query's outcome is available now, so it matches with an ordinary `case`. *)
external function refresh(const asset : address) is {
  const price : uint := case query storage.oracle.price(asset) of {
  | Ok(p) -> p
  | Error(_) -> storage.lastGoodPrice
  };
  storage.lastGoodPrice := price;
}
 
(* A deferred call's outcome arrives in its continuation. `Error` binds the raw
   revert data. *)
external nonreentrant function notify(const amount : uint) is {
  defer storage.hook.onPayout(amount) then {
    | Ok(accepted : bool) -> {
        skip
      }
    | Error(reason : bytes) -> {
        storage.hookFailures := storage.hookFailures + 1;
      }
  };
}
 
(* A transfer that fails is recorded for later, instead of blocking the caller.
   `Ok` has no binding because a transfer returns nothing. *)
external payable nonreentrant function pay(const to : address) is {
  defer Evm.transfer(to, Evm.value()) then {
    | Ok -> { skip }
    | Error(_) -> {
        storage.unpaid[to] := storage.unpaid[to] + Evm.value();
        emit PayoutFailed { to = to; amount = Evm.value(); };
      }
  };
}
  • Both arms are required. With neither, the call is not recoverable, and a failure reverts.
  • The first phase's writes stay whichever arm runs. Only the failed call's own effects are undone.
  • A call that succeeds but returns data that doesn't decode as the declared type still reverts. That's a broken interface, not an Error.
  • See Continuations for the full rules.