OxL Reference

src/bank/bank.oxl

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