Budgets

A Budget is one funded ceiling. In Control Mode that is a ledger record; in escrow mode an on-chain deposit; in card mode a card authorization-backed budget. Everything downstream — streams, vouchers, settlement — is identical across the three, and your instance enforces all of it server-side; the client only speaks the wire.

Lifecycle

import { Dynamo, usd, units } from "@dynamoprotocol/sdk";

const dynamo = await Dynamo.open({
  coreUrl: process.env.DYNAMO_CORE_URL!,
  token: process.env.OWNER_API_TOKEN!,
});
const alice = "0x00000000000000000000000000000000000000A1";
const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));

// 1. Fund a ceiling. The cap is the most this session can ever settle.
const budget = await dynamo.openBudget({ funding: "none", cap: usd("10") });

// 2. Fan out. Each stream gets its own hard cap and per-second rate envelope.
const worker = await budget.stream({
  to: alice,
  rate: units(1_000_000n),
  cap: units(3_000_000n),
});

// 3. Meter. Every tick emits a voucher signed by the stream's scoped key
//    (held by the core); allowance accrues per second.
await sleep(1_100);
const receipt = await worker.tick(units(800_000n));
if (receipt.voucherSeq < 1) throw new Error("expected a signed voucher");

// 4. Close. Live streams must be revoked first — closing never silently
//    kills allowances (fail closed).
await worker.revoke();
const { releasedAmount } = await budget.close();

// 5. Reconcile. The session's billed total must equal settlement-layer truth.
const session = await budget.aggregate();
if (!session.reconciled) throw new Error("engine and settlement layer disagree");
if ((releasedAmount as bigint) + session.totals.settledAmount > 10_000_000n) {
  throw new Error("released + settled exceeded the funded ceiling");
}

The rules a budget enforces

the funded ceiling — that inequality (the Σ proof) is checked by the engine, re-derivable from the session view, and carried into evidence bundles.

live; after settlement of the last funded vouchers, the remainder is released to the owner (escrow: on-chain; card: authorization release).

settled totals, the settlement layer's own record, and reconciled: true only when the two agree to the unit.

Meta, not state

openBudget({ meta }) carries client-side annotation only — it never crosses the wire into settlement, vouchers, or fees. Anything that moves money lives in the typed fields.