Repository navigation
ertp
ERTP is Agoric's token standard for digital assets in JavaScript. It uses object-capability discipline for access control: if your program holds a reference to an object, it can call methods on it; if not, it can't.
ERTP is not a separate security layer — it emerges directly from Hardened JavaScript. The same harden() + closure encapsulation pattern that gives us makeCounter scales to makeMint in ~30 lines:
makeCounter → makeMint (mint + purse + issuer) → ERTP (brands, payments, deposit facets)
A mint creates purses; purses hold balances via a WeakMap ledger; deposit/withdraw move value between purses; the issuer is a facet for creating empty purses. All the core ERTP invariants — no double-spending, payments consumed on deposit, unforgeable transfers — come from the ocap discipline (harden(), encapsulation, reference-based authority), not from a blockchain or consensus mechanism.
See The Mint Pattern (8 min, starting at 25:25) for the live walkthrough.
| Concept | Role |
|---|---|
| Brand | An asset type (e.g., "QUATLOOS", "BUCKS"). Answer to "what is it?" |
| Amount | A description: { brand, value }. Not an asset — just a description. |
| Mint | The unique creator of new assets of a given brand. |
| Issuer | Source of truth for purse and payment balances; validates payments. |
| Purse | Holds assets of one brand. Supports deposit(), withdraw(), getCurrentAmount(), getDepositFacet(). |
| Payment | A transferable object containing assets. Consumed on deposit. |
| DepositFacet | A attenuated facet of a purse: can only deposit, never withdraw or check balance. |
-
AssetKind.NAT— fungible (natural number value, e.g., 400n QUATLOOS) -
AssetKind.SET— non-fungible (array of records, e.g., tickets by seat) -
AssetKind.COPY_SET/AssetKind.COPY_BAG— semi-fungible (set/bag semantics)
makeIssuerKit("BUCKS")
→ mint.mintPayment(amount)
→ purse.deposit(payment)
→ purse.withdraw(amount) → payment
→ depositFacet.receive(payment)
Each mint creates an issuer+brand trio. Assets flow from mint → payment → purse → payment → deposit facet. Payments are consumed (deleted from issuer records) on deposit.
The vattr97 repo (commit 67a451b) implements ERTP on a GnuCash SQLite ledger — the @vattr/ertp-ledgerguise package.
| ERTP Concept | GnuCash Mapping |
|---|---|
| Brand | Commodity (namespace + mnemonic, e.g., COMMODITY:BUCKS) |
| Purse | Account (with GUID, name, parent) |
| Payment | Transaction + Splits (with reconcile_state='n' until deposited) |
| Amount | Brand + bigint value |
| Mint | Recovery account transfers |
| Issuer | Account tree + split query logic |
-
src/index.ts—createIssuerKit(),openIssuerKit()entry points -
src/ertp-types.ts— TypeScript types for Brand, Issuer, Mint, Purse, Payment, Amount -
src/purse.ts—makePurseFactory()— ERTP purse backed by GnuCash account -
src/escrow-ertp.ts—makeErtpEscrow()— AMIX-style atomic swap -
src/chart.ts—makeChartFacet()— names purses in the GnuCash chart of accounts -
src/sealer.ts— sealer/unsealer for capability attenuation -
src/db-helpers.ts— commodity, account, and transfer database operations
makeErtpEscrow() implements an AMIX (American Information Exchange) escrow exchange:
Agreement → [Party A Funds] → [Party B Funds] → Settlement
↓ ↓
(cancellation → refunds)
Each party funds the escrow with a Promise<Payment>. Settlement only happens when both have funded. The escrow uses the sealer/unsealer pattern to expose account GUIDs for ledger queries without leaking withdrawal authority.
See docs-dev/escrow-accounting.md for the double-entry accounting model.
From docs-dev/ocap-discipline.md:
- Inject, don't import — DB, clock, and GUID generation are injected as capabilities, never ambient.
-
Freeze API surfaces — all returned objects are
harden()-ed orfreeze()-ed. -
Zone pattern —
zone.exo()creates hardened objects with method-only interfaces. - Sealer/Unsealer — attenuate authority before sharing (e.g., purse identity without withdrawal authority).
- Closely held vs widely shared — purses are private; deposit facets are public.
// Capability injection
const kit = createIssuerKit({
db, // database capability
commodity,
makeGuid, // GUID generation
nowMs: () => Date.now(), // clock
zone,
});ERTP gives the governance system a standard, proven vocabulary for value:
- Contributions as Payments — a contributor submits work as a payment of value.
- Approvals as deposits into domain purses — a steward receives contributions into domain-branded purses.
- Escrow for conditional exchanges — e.g., payment contingent on review approval.
- Attenuated authority via DepositFacet — anyone can send value to a domain; only stewards withdraw.
Issue #3 proposes re-implementing the RChain bounty system (trust metric + roles) on ERTP, replacing raw database rows with Purses and Mints.
- ERTP docs (Agoric)
- ERTP API reference
-
vattr97/packages/ertp-ledgerguise/test/design-doc.test.ts— executable design doc with snapshots -
vattr97/packages/ertp-ledgerguise/test/snapshots/design-doc.test.ts.md— narrative snapshot report -
vattr97/packages/ertp-ledgerguise/docs-dev/escrow-accounting.md— escrow accounting model -
vattr97/packages/ertp-ledgerguise/docs-dev/ocap-discipline.md— capability discipline guide
Navigation
- Home
- Governance
- Endo / object capabilities
- Retrospectives and source material