src/bank/bank.oxl
/// An account that does not exist or is blocked cannot take money.
@error pub data Blocked { pub id: u64 }
/// The balance is below the amount.
@error pub data Insufficient { pub id: u64; pub balance: i64 }
/// The id of the account the bank refuses to credit (a stand-in for a closed account).
pub const CLOSED_ACCOUNT: u64 = 99
pub actor Account sharded by id: u64 {
var balance: i64 = 0
msg fund(x: i64) -> i64 {
balance += x
balance
}
msg take(x: i64) -> i64 | Insufficient {
if balance < x { fail Insufficient{ id: id, balance: balance } }
balance -= x
balance
}
msg put(x: i64) -> i64 {
balance += x
balance
}
msg get() -> i64 { balance }
}
pub(api) service Bank {
@public @idempotent rpc debit(id: u64, amount: i64) -> i64 | Insufficient
@public @idempotent rpc credit(id: u64, amount: i64) -> i64 | Blocked
@public @readonly rpc balance(id: u64) -> i64
}
impl Bank {
rpc debit(id, amount) {
match Account(id).take(amount) {
i64 left => left
Insufficient e => fail e
NetError _ => fail Insufficient{ id, balance: 0 }
}
}
rpc credit(id, amount) {
if id == CLOSED_ACCOUNT { fail Blocked{ id } }
match Account(id).put(amount) {
i64 now => now
NetError _ => fail Blocked{ id }
}
}
rpc balance(id) {
match Account(id).get() {
i64 b => b
NetError _ => -1
}
}
}
/// Moves `amount` from one account to another; returns the new balance of the target.
pub saga transfer(from: u64, to: u64, amount: i64) -> i64 {
Bank.debit(from, amount) undo _ => Bank.credit(from, amount)
-> Bank.credit(to, amount)
}
cluster bank {
role all { serves Bank; hosts Account; journal; runs sagas }
}Source: examples/bank-saga/src/bank/bank.oxl