Result, Error Unions, and Try
Ora uses explicit Result<T, E> / error-union values instead of exceptions.
Errors are part of the type system, can carry payloads, can be matched, and are
visible to the ABI and verifier.
Error declarations
contract Payments {
error InvalidAmount;
error InsufficientBalance(required: u256, available: u256);
}
Result return types
contract Payments {
error InvalidAmount;
error InsufficientBalance(required: u256, available: u256);
storage var balances: map<NonZeroAddress, u256>;
fn withdraw(to: NonZeroAddress, amount: u256) -> Result<u256, InsufficientBalance> {
if (balances[to] < amount) {
return Err(InsufficientBalance(amount, balances[to]));
}
balances[to] = balances[to] - amount;
return Ok(balances[to]);
}
}
The !T | E1 | E2 spelling is also used for public error-union signatures,
especially where success and error postconditions use ensures_ok /
ensures_err.
Matching result values
contract Payments {
error Failure(code: u256);
pub fn inspect(value: Result<u256, Failure>) -> u256 {
return match (value) {
Ok(inner) => inner,
Err(err) => err.code,
};
}
}
Try expressions
try unwraps a successful value or returns the error up the call stack.
contract Wallet {
error Fail;
fn mayFail(x: u256) -> !u256 | Fail {
if (x == 0) return Fail;
return x + 1;
}
fn run(x: u256) -> !u256 | Fail {
let y: u256 = try mayFail(x);
return y * 2;
}
}
Try/catch blocks
Use try { ... } catch { ... } to handle errors explicitly.
contract Transfers {
error InsufficientBalance;
log Sent(to: address, amount: u256);
log Failed(to: address, amount: u256);
fn move(to: NonZeroAddress, amount: u256) -> !u256 | InsufficientBalance {
if (amount == 0) return InsufficientBalance;
return amount;
}
pub fn send(to: NonZeroAddress, amount: u256) {
try {
let sent: u256 = try move(to, amount);
log Sent(to, sent);
} catch (e) {
log Failed(to, amount);
}
}
}