soma.cell

Letter of credit — the bank pays against verified documents, never trust

A documentary letter of credit (UCP 600) exists because exporter and importer don't trust each other across an ocean: the bank promises to pay the seller, but ONLY against documents proving shipment. The classic loss is paying on presented-but-unexamined documents — the Salomon-era trade-finance frauds ran exactly this gap, with bills of lading for cargo that never sailed. Here `honored` is reachable only through `docs_verified`; presenting is not verifying; partial drawings draw down a credit slot that refuses to go below zero, so the beneficiary can never draw more than the credit line. Expired credits are terminal.

A Soma program from the escrow_finance part of the verified corpus: 116 lines, cells LetterOfCredit, LocTests. It passes soma check with soma 3.2.4.

How it is built

The cell LetterOfCredit declares 6 signals in its face: issue(id: String, credit: Int) -> Map, present_docs(id: String) -> Map, verify_docs(id: String, conforming: Bool) -> Map, honor_drawing(id: String, amount: Int) -> Map, expire(id: String) -> Map and undrawn(id: String) -> Int; keeps 1 memory slot: credit (Map<String, Int>, persistent and consistent) under the invariant credit >= 0. Its state machine loc starts in applied and allows 7 transitions: applied -> issued, issued -> docs_presented, issued -> expired, docs_presented -> docs_verified, docs_presented -> discrepant, discrepant -> docs_presented and docs_verified -> honored. expired and honored are terminal. Handlers: issue (line 47), present_docs (line 54), verify_docs (line 59), honor_drawing (line 68), expire (line 79) and undrawn (line 84).

The file also holds 1 test cell (LocTests) with 17 assertions, run by soma test.

Generated from soma describe on this file.

The source

letter_of_credit.cell

// ============================================================================
// LETTER OF CREDIT — the bank pays against verified documents, never trust
// ============================================================================
//
// A documentary letter of credit (UCP 600) exists because exporter and
// importer don't trust each other across an ocean: the bank promises to
// pay the seller, but ONLY against documents proving shipment. The classic
// loss is paying on presented-but-unexamined documents — the Salomon-era
// trade-finance frauds ran exactly this gap, with bills of lading for
// cargo that never sailed. Here `honored` is reachable only through
// `docs_verified`; presenting is not verifying; partial drawings draw down
// a credit slot that refuses to go below zero, so the beneficiary can
// never draw more than the credit line. Expired credits are terminal.
// ============================================================================

cell LetterOfCredit {
    face {
        signal issue(id: String, credit: Int) -> Map
        signal present_docs(id: String) -> Map
        signal verify_docs(id: String, conforming: Bool) -> Map
        signal honor_drawing(id: String, amount: Int) -> Map
        signal expire(id: String) -> Map
        signal undrawn(id: String) -> Int
    }

    memory {
        // the credit line — drawings reduce it, and it cannot go negative:
        // an over-drawing is a rejected write, not an accounting incident
        credit: Map<String, Int> [persistent, consistent]
        invariant credit >= 0
    }

    // honored and expired are terminal. The fraud edge — issued (or even
    // docs_presented) straight to honored — does not exist: payment
    // happens only from docs_verified.
    state loc {
        initial: applied
        applied -> issued
        issued -> docs_presented
        issued -> expired
        docs_presented -> docs_verified
        docs_presented -> discrepant
        discrepant -> docs_presented
        docs_verified -> honored
    }

    on issue(id: String, credit_amt: Int) {
        if credit_amt <= 0 { return map("ok", false, "reason", "bad credit") }
        transition(id, "issued")
        credit.set(id, credit_amt)
        return map("ok", true, "credit", credit_amt)
    }

    on present_docs(id: String) {
        transition(id, "docs_presented")
        return map("ok", true, "state", get_status(id))
    }

    on verify_docs(id: String, conforming: Bool) {
        if conforming {
            transition(id, "docs_verified")
        } else {
            transition(id, "discrepant")   // seller may cure and re-present
        }
        return map("ok", true, "state", get_status(id))
    }

    on honor_drawing(id: String, amount: Int) {
        if amount <= 0 { return map("ok", false, "reason", "bad amount") }
        if get_status(id) != "docs_verified" {
            return map("ok", false, "reason", "documents not verified")
        }
        let line = credit.get(id) ?? 0
        credit.set(id, line - amount)      // raises if drawing exceeds credit
        transition(id, "honored")          // now provably valid
        return map("ok", true, "paid", amount, "undrawn", line - amount)
    }

    on expire(id: String) {
        transition(id, "expired")          // only an un-presented credit expires
        return map("ok", true, "state", get_status(id))
    }

    on undrawn(id: String) {
        return credit.get(id) ?? 0
    }
}

cell test LocTests {
    rules {
        assert issue("loc1", 80000).ok == true
        // PAY ON TRUST: honored without documents — the edge does not exist
        assert_fails transition("loc1", "honored")
        assert present_docs("loc1").ok == true
        // presented is NOT verified: still no payment edge
        assert_fails transition("loc1", "honored")
        // discrepant docs bounce back for cure, then re-present
        assert verify_docs("loc1", false).state == "discrepant"
        assert_fails transition("loc1", "honored")
        assert present_docs("loc1").ok == true
        assert verify_docs("loc1", true).state == "docs_verified"
        // OVER-DRAWING: 90k against an 80k credit — the slot refuses
        assert_fails honor_drawing("loc1", 90000)
        assert undrawn("loc1") == 80000
        assert honor_drawing("loc1", 80000).paid == 80000
        // honored is terminal: documents cannot be "re-presented" for more
        assert_fails transition("loc1", "docs_presented")
        assert_fails transition("loc1", "honored")
        assert_fails credit.set("loc1", -1)
        // an untouched credit can expire — and stays expired
        assert issue("loc2", 5000).ok == true
        assert expire("loc2").ok == true
        assert_fails transition("loc2", "docs_presented")
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check letter_of_credit.cell
warning: parameter 2 of `issue`: the face calls it `credit`, the handler `credit_amt` — an HTTP JSON body is matched by NAME (the handler's), and `soma describe` publishes the face's: use one name
  --> letter_of_credit.cell:18:9
   |
18 |         signal issue(id: String, credit: Int) -> Map
   |         ^
✓ 1 warning(s), no errors.
$ soma test letter_of_credit.cell
test LocTests ...
  ✓ assert issue("loc1", 80000).ok == true
  ✓ assert_fails transition("loc1", "honored") — raised invalid transition: issued → honored. Current state: 'issued'. Valid targets: [docs_presented, expired]
  ✓ assert present_docs("loc1").ok == true
  ✓ assert_fails transition("loc1", "honored") — raised invalid transition: docs_presented → honored. Current state: 'docs_presented'. Valid targets: [docs_verified, discrepant]
  ✓ assert verify_docs("loc1", false).state == "discrepant"
  ✓ assert_fails transition("loc1", "honored") — raised invalid transition: discrepant → honored. Current state: 'discrepant'. Valid targets: [docs_presented]
  ✓ assert present_docs("loc1").ok == true
  ✓ assert verify_docs("loc1", true).state == "docs_verified"
  ✓ assert_fails honor_drawing("loc1", 90000) — raised memory invariant violated on 'credit': credit >= 0 — rejected write of -10000 (key "loc1"); the slot is unchanged
  ✓ assert undrawn("loc1") == 80000
  ✓ assert honor_drawing("loc1", 80000).paid == 80000
  ✓ assert_fails transition("loc1", "docs_presented") — raised invalid transition: honored → docs_presented. Current state: 'honored'. Valid targets: []
  ✓ assert_fails transition("loc1", "honored") — raised invalid transition: honored → honored. Current state: 'honored'. Valid targets: []
  ✓ assert_fails credit.set("loc1", -1) — raised memory invariant violated on 'credit': credit >= 0 — rejected write of -1 (key "loc1"); the slot is unchanged
  ✓ assert issue("loc2", 5000).ok == true
  ✓ assert expire("loc2").ok == true
  ✓ assert_fails transition("loc2", "docs_presented") — raised invalid transition: expired → docs_presented. Current state: 'expired'. Valid targets: []

17 tests: 17 passed, 0 failed
$ soma verify letter_of_credit.cell
Verifying letter_of_credit.cell...

State machine 'loc': 7 states, initial 'applied'
  States: [applied, discrepant, docs_presented, docs_verified, expired, honored, issued]

  ✓ 7 states, 7 transitions
  ✓ all states reachable from 'applied'
  ✓ terminal states: [expired, honored]
  ✓ no deadlocks
  ✓ liveness: every state can eventually reach a terminal state
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (5 handlers, 6 literal transitions, 0 dynamic)
  ✓ termination: all 6 handlers structurally terminate
  ✓ refinement: handler `issue` ⟶ {issued}
  ✓ refinement: handler `present_docs` ⟶ {docs_presented}
  ✓ refinement: handler `verify_docs` ⟶ {docs_verified [if conforming], discrepant [if not (conforming)]}
  ✓ refinement: handler `honor_drawing` ⟶ {docs_verified → honored}
  ✓ refinement: handler `expire` ⟶ {expired}
  · transition sources: `issue`, `present_docs`, `verify_docs`, `expire` call transition(id, target) without declaring the source — the runtime refuses a move the graph forbids, but the proof does not see where the handler starts; `transition(id, from, to)` names the edge (checked by `soma check`, refused at run time from any other state)

LetterOfCredit/invariants:

  ✓ invariant credit >= 0 — writer 'issue' proven by induction (writes credit_amt)
  ⚠ invariant credit >= 0 — runtime-checked (computed values): honor_drawing → credit

13 passed, 1 warnings, 0 failures

  Temporal properties for 'loc':
  ✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK

Builtins used

get_status, map, transition

Source file: /corpus/escrow_finance/letter_of_credit.cell · examples/corpus/escrow_finance/letter_of_credit.cell