Chip/cash ledger with a non-negative balance invariant
The memory itself rejects any write that would overdraw an account — the bad .set() never commits, so the slot keeps its previous value. Demonstrates: memory invariant, try around a guarded write, assert_fails.
A Soma program from the finance part of the verified corpus: 54 lines, cells ChipLedger, ChipLedgerTests. It passes soma check with soma 3.2.4.
How it is built
The cell ChipLedger declares 3 signals in its face: credit(player: String, amount: Int) -> Map, debit(player: String, amount: Int) -> Map and balance_of(player: String) -> Int; keeps 1 memory slot: chips (Map<String, Int>, persistent and consistent) under the invariant chips >= 0. Handlers: balance_of (line 19), credit (line 25) and debit (line 31).
The file also holds 1 test cell (ChipLedgerTests) with 6 assertions, run by soma test.
The source
chip_ledger.cell
// Chip/cash ledger with a non-negative balance invariant.
// The memory itself rejects any write that would overdraw an account —
// the bad .set() never commits, so the slot keeps its previous value.
// Demonstrates: memory invariant, try around a guarded write, assert_fails.
cell ChipLedger {
face {
signal credit(player: String, amount: Int) -> Map
signal debit(player: String, amount: Int) -> Map
signal balance_of(player: String) -> Int
promise all_persistent
}
memory {
chips: Map<String, Int> [persistent, consistent]
invariant chips >= 0
}
on balance_of(player: String) {
let b = chips.get(player)
if b == () { return 0 }
return b
}
on credit(player: String, amount: Int) {
let cur = balance_of(player)
chips.set(player, cur + amount)
return map("ok", true, "balance", cur + amount)
}
on debit(player: String, amount: Int) {
let cur = balance_of(player)
// If cur - amount < 0 the invariant rejects the write.
let r = try { chips.set(player, cur - amount) }
if r.error != () {
return map("ok", false, "reason", "insufficient funds", "balance", cur)
}
return map("ok", true, "balance", cur - amount)
}
}
cell test ChipLedgerTests {
rules {
assert credit("alice", 100).ok == true
assert balance_of("alice") == 100
assert debit("alice", 40).balance == 60
// overdraw is refused, balance unchanged
assert debit("alice", 999).ok == false
assert balance_of("alice") == 60
// a direct negative write is a runtime error
assert_fails chips.set("bob", -1)
}
}What the toolchain says
$ soma check chip_ledger.cell
✓ All checks passed.$ soma test chip_ledger.cell
test ChipLedgerTests ...
✓ assert credit("alice", 100).ok == true
✓ assert balance_of("alice") == 100
✓ assert debit("alice", 40).balance == 60
✓ assert debit("alice", 999).ok == false
✓ assert balance_of("alice") == 60
✓ assert_fails chips.set("bob", -1) — raised memory invariant violated on 'chips': chips >= 0 — rejected write of -1 (key "bob"); the slot is unchanged
6 tests: 6 passed, 0 failed$ soma verify chip_ledger.cell
Verifying chip_ledger.cell...
ChipLedger/invariants:
⚠ invariant chips >= 0 — runtime-checked (computed values): credit → chips because `amount` is a parameter (narrow it: `require (cur + amount) >= 0 else …`), debit → chips because `amount` is a parameter (narrow it: `require (cur - amount) >= 0 else …`)
0 passed, 1 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK