From 0beef51cc9eec83cfe52e6eeb547d19ef2ed9df9 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 09:25:22 -0300 Subject: [PATCH 01/10] chore: drop the superseded m1 docs roadmap-m1.md and m1-brief.md describe a synchronous 1:1 vault that the current architecture replaced. Both were still cited as authoritative by CLAUDE.md and AGENTS.md, so an agent reading either was pointed at a design that no longer holds. Version pins are read from Cargo.toml, rust-toolchain.toml and flake.nix instead. The architecture is maintained outside the repository. --- AGENTS.md | 7 +++---- CLAUDE.md | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 981f1cc..e96a40d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,10 +3,9 @@ Guidance for agents and developers working in this repo. This file is the source of truth for how to build, run and not break things. `CLAUDE.md` points here. -It deliberately does **not** restate versions, decisions or known upstream -issues — those live in [`docs/roadmap-m1.md`](./docs/roadmap-m1.md), and a -second copy would go stale. What the product _is_ — and is not — is specified in -[`docs/m1-brief.md`](./docs/m1-brief.md). Work items live in +It deliberately does **not** restate dependency versions — those are pinned in +`Cargo.toml`, `rust-toolchain.toml` and `flake.nix`, and a second copy would go +stale. The architecture is maintained outside this repository. Work items live in [milestone M1](https://github.com/BootNodeDev/strata-vault-kit/milestone/1). ## What this is diff --git a/CLAUDE.md b/CLAUDE.md index 32f1ca2..923b23c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,5 +2,7 @@ See [AGENTS.md](./AGENTS.md) for what this repo is, how to build and run it, network configuration, and the Stellar-specific gotchas. It is the single source of truth for this project's conventions. -Versions, decisions and known upstream issues live in -[`docs/roadmap-m1.md`](./docs/roadmap-m1.md) — not duplicated anywhere else. +Dependency versions are pinned in `Cargo.toml`, `rust-toolchain.toml` and +`flake.nix` — read them there rather than from prose. The architecture is +maintained outside this repository; ask the maintainer for the current document +instead of inferring the design from the code. From f93523d5fa17c6c2494cbd60971f6a1ccf5d326a Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 09:28:13 -0300 Subject: [PATCH 02/10] chore: align AGENTS.md with the request lifecycle AGENTS.md still described a synchronous OZ vault: deposit and withdraw priced at call time, shares 1:1, exit via withdraw/redeem. It also called the repo bootstrap-only, which stopped being true when the contract crates landed, and pointed at a cargo test -p vault crate that does not exist. Drops three gotchas that no longer apply: the SEP-56 vault wiring and its require_auth caveat, a motion dependency the app does not have, and the uncommitted-Cargo.lock note. --- AGENTS.md | 71 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e96a40d..fe2c9b6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,40 +10,53 @@ stale. The architecture is maintained outside this repository. Work items live i ## What this is -A white-label tokenized vault on Stellar/Soroban, built on the OpenZeppelin -Soroban vault. An approved investor deposits USDC and receives `bvUSDC` shares; -they redeem and get USDC back. - -Two invariants the contract enforces: - -1. **Entry is gated** by a post-KYC allowlist — the `deposit`/`mint` receiver, - and both sides of `transfer`/`transfer_from`. -2. **Exit is never gated and never pausable.** A de-listed holder can always - leave. `withdraw`/`redeem` carry no allowlist check by design. - -Milestone 1 has no yield: shares stay 1:1. Testnet only. Not audited. +A white-label RWA vault kit on Stellar/Soroban. Shares are a claim on an +off-chain asset whose value is attested on chain, so no price exists at the +moment an investor acts. Entry and exit are therefore requests: what goes in is +escrowed, the next accepted attestation prices it, and the investor claims the +result. Three states per side, none skipped: **pending, priced, claimed**. + +What the contracts enforce: + +1. **Entry is gated** by a post-KYC allowlist, checked on the receiver of a + subscription and on every share transfer. +2. **A priced claim always pays.** Once priced and covered, a cash claim cannot + be blocked by a pause, a stale valuation, or the holder losing their + allowlist place. Priced claims are never re-priced and never identity-gated; + a delisted, non-frozen holder leaves through the exit-only cash path. +3. **Cancellation is atomic and single-step**, open only until the attestation + that prices the request is accepted. There is no instant exit. + +Five authorities, each a native Stellar multisig: governance, compliance, +attestation, treasury, guardian. Testnet only. Not audited. ## State of the repo -Bootstrap only: documentation, issue templates and the toolchain pin. The Rust -workspace arrives with the first contract crate, and the interface after that. -The sections below describe how the project is built as each piece lands. +Three contract crates (`compliance`, `identity-verifier`, `share-token`) and two +shared crates (`bindings`, `pricing`). A React + Vite app shell with TypeScript +clients generated per contract, and a Playwright e2e harness. + +The vault contract itself is not written yet, so the request lifecycle above has +no on-chain counterpart in this repo today. `app/` and `app-lib/` have no +unit-test runner. ## Reference base [`stellar-vault-demo-dapp`](https://github.com/BootNodeDev/stellar-vault-demo-dapp) -is our own working testnet demo. Its 188-line contract proves the design. Read -it while building; do not port it wholesale. Its frontend is not carried over — -M1 builds a new one designed around the role model. +is our own working testnet demo, but it is **synchronous**: deposit and withdraw +are priced at call time. It does not model the request lifecycle and its flow +does not carry over. Read it for Soroban and OZ mechanics only. ## Build & run - **Contracts:** `stellar contract build` — **not** `cargo build`. The OZ crates enable an experimental `soroban-sdk` feature (`spec_shaking_v2`) that only - works through the CLI wrapper (Stellar CLI ≥ 25.2). -- **Tests:** `cargo test -p vault` (unit tests run against the in-memory `Env`). -- **Toolchain:** pinned in `rust-toolchain.toml`. rustup installs it on first - build. + works through the CLI wrapper. The devshell pins Stellar CLI v27.0.0. +- **Tests:** `cargo test` from the repo root runs every workspace member against + the in-memory `Env`. There is no unit-test runner for `app/` or `app-lib/`; + `e2e/` runs Playwright separately. CI does not run the Rust tests yet. +- **Toolchain:** `nix develop` provides it, or rustup honours + `rust-toolchain.toml`. ## Gotchas @@ -51,17 +64,11 @@ Carried over from the reference base, where each one cost real debugging. They apply as the corresponding code lands here. - Build with `stellar contract build`, not `cargo build` (see above). -- OZ vault wiring: `#[contractimpl(contracttrait)]` on **both** `FungibleToken` - and `FungibleVault`; `type ContractType = Vault` goes **only** on - `FungibleToken`; import `soroban_sdk::MuxedAddress` (the contracttrait macro - references it). -- Do **not** call `operator.require_auth()` inside overridden vault methods — - `Vault::*` already authorizes, and a second call fails with - `Error(Auth, ExistingValue)`. +- A SEP-56 vault is **not** the base here: its interface assumes the price + exists at call time, which a request lifecycle cannot express. Only OZ's + conversion and rounding math is reused, as library code. - `ed25519-dalek` v3 breaks the test build; pin to `2.2.0` if it resolves higher. -- `motion` must be v12+ (`motion/react`); a bare `npm i motion` pulls v10 - (Motion One), which has no React entry. - USDC is a **classic asset** → an account needs a trustline to hold it. `bvUSDC` is a **Soroban contract token** → no trustline. Deposit is a single transaction with nested authorization; there is no separate `approve`. Get @@ -73,8 +80,6 @@ apply as the corresponding code lands here. setting or the UI talks to the wrong chain. - Generated contract clients ship their `src/` but not their `dist/`. A fresh clone builds the client before the app, or `tsc` cannot resolve the module. -- No `Cargo.lock` is committed until the first contract crate lands (#20); see - the comment in `Cargo.toml`. - `app-lib/clients/index.ts` is auto-generated and rewritten on every build or redeploy. Do not hand-edit it; customize by importing the client under `app/`. From 3c76290989f3b9721640dc6a327c525dcf83949f Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 09:36:20 -0300 Subject: [PATCH 03/10] docs: add the architecture as the in-repo source of truth The superseded m1 docs left the repo with no design document, so CLAUDE.md and AGENTS.md had nothing to point at. Adds the current architecture and points both files at it. --- AGENTS.md | 7 +- CLAUDE.md | 8 +- docs/architecture.md | 282 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 docs/architecture.md diff --git a/AGENTS.md b/AGENTS.md index fe2c9b6..20ff477 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,9 +3,10 @@ Guidance for agents and developers working in this repo. This file is the source of truth for how to build, run and not break things. `CLAUDE.md` points here. -It deliberately does **not** restate dependency versions — those are pinned in -`Cargo.toml`, `rust-toolchain.toml` and `flake.nix`, and a second copy would go -stale. The architecture is maintained outside this repository. Work items live in +It deliberately does **not** restate the design or the dependency versions. The +design is in [`docs/architecture.md`](./docs/architecture.md); versions are +pinned in `Cargo.toml`, `rust-toolchain.toml` and `flake.nix`. A second copy of +either would go stale. Work items live in [milestone M1](https://github.com/BootNodeDev/strata-vault-kit/milestone/1). ## What this is diff --git a/CLAUDE.md b/CLAUDE.md index 923b23c..0211a9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,9 @@ See [AGENTS.md](./AGENTS.md) for what this repo is, how to build and run it, network configuration, and the Stellar-specific gotchas. It is the single source of truth for this project's conventions. +The design lives in [`docs/architecture.md`](./docs/architecture.md) — the +source of truth for what the protocol does and why. Where the code and that +document disagree, the document wins and the code is behind. + Dependency versions are pinned in `Cargo.toml`, `rust-toolchain.toml` and -`flake.nix` — read them there rather than from prose. The architecture is -maintained outside this repository; ask the maintainer for the current document -instead of inferring the design from the code. +`flake.nix` — read them there rather than from prose. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..732e231 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,282 @@ +# Technical Architecture: Strata + +**Product:** Strata, an RWA lifecycle protocol for attested-value assets: request-based subscriptions and redemptions priced against on-chain guarded attestations, built on OpenZeppelin's Stellar contracts. Distributed as a kit: each operator deploys, configures and brands an independent white-label instance. +**Chain:** Stellar. Soroban smart contracts; classic assets consumed through their Stellar Asset Contract (SAC) interface. +**Deposit asset:** any classic Stellar asset chosen at genesis (typically a USD stablecoin such as USDC). +**Maintainer:** BootNode (bootnode.dev). + +Shares in this vault are a claim on an off-chain asset whose value is attested on chain. No price exists at the moment you act, so entry and exit are requests: what you put in goes into escrow, the next accepted attestation prices it, and you claim the result. A priced claim is never re-priced, and a covered cash claim cannot be blocked by a pause or by losing your allowlist place. + +--- + +## 1. Design principles + +1. **Configured, not built.** Each operator deploys an independent instance: no factory, no shared state. A deployment is defined by its configuration and five authorities (native Stellar multisig accounts). Everything an operator brands or configures lives outside the trust boundary; everything inside it is the same code for every adopter. + +2. **Built on OpenZeppelin, differentiated above it.** The share token is OZ's SEP-41 token with the SEP-57 RWA extensions (freeze, forced transfer, recovery); access control, pausable and upgradeable come from OZ crates. Dependencies are pinned to exact versions and audit inheritance is evaluated component by component; what Strata adds stays within its own audit scope. What OZ does not cover is the layer Strata adds: the attested valuation oracle, request-based flows priced against attestations, split reserve accounting, and the operational surface. + +3. **Request-based entry and exit.** The exact price of a share does not exist when an investor acts; it is attested afterwards. Entry and exit are requests: funds or shares go into escrow, the next accepted attestation prices them, and the investor claims the result. This is the ERC-7540 pattern with two differences: cancellation is a single step that closes when the pricing attestation arrives, and the price is set by each attestation for every pending request, not by a manager. + +4. **Attested NAV.** The reporter attests the share price itself, computed off-chain from the deployed value and the vault's public figures under a documented methodology. The contract validates, stores and exposes it. Every on-chain operation preserves that price by construction: a priced deposit adds assets and shares in proportion, a priced redemption removes both, a custodian transfer moves value between pockets without changing the total. + +5. **Payable claims always pay.** Neither the guardian pause, a delisting, nor a stale valuation can block the payment of an already-priced, funded cash claim. A pause can delay unpriced requests. Priced claims are never re-priced and never identity-gated. A delisted, non-frozen investor exits through the exit-only cash path. Outside this guarantee, disclosed as trust assumptions: a frozen investor, deposit-asset issuer controls, and reserve liquidity. + +6. **Split accounting, with priced and payable as separate states.** The vault exposes committed (priced redemption liabilities), cancellable escrow (pending subscriptions the investor can still recall) and free reserve. Escrowed subscriptions never leave the vault. Committed can exceed the liquid reserve; that gap is an explicit on-chain shortfall, outbound transfers are blocked while it exists, and priced claims stay payable in FIFO order as treasury tops up. A liquidity shortfall is not insolvency: solvency compares total attested assets against liabilities and is handled by attested losses and governance. + +## 2. Component overview + +Orange marks what only Strata provides; white is the existing ecosystem the kit consumes or integrates. + +```mermaid +flowchart TB + subgraph FE["FRONTEND · WHITE-LABEL"] + LP["Investor dApp
request · claim ·
cancel · position"]:::strata + ADM["Admin panel
allowlist · valuation ·
pause · treasury"]:::strata + end + + subgraph ON["ONCHAIN · SOROBAN"] + subgraph OZ["OpenZeppelin base"] + ST["SEP-41 + SEP-57
share token"] + AC["Access control · pausable
· upgradeable"] + end + SAC["Deposit asset ·
Stellar Asset Contract"] + subgraph SM["Strata modules"] + V["Vault · request lifecycle"]:::strata + OR["Valuation oracle ·
guardrails · NAV"]:::strata + SA["Split accounting ·
shortfall exposure"]:::strata + RQ["FIFO redemption coverage
· exit-only path"]:::strata + MGR["Manager ·
token authority"]:::strata + IVC["Compliance module
SEP-57 identity + rules"] + end + AUTH["Role framework
5 multisig authorities"] + end + + subgraph OFF["OFF-CHAIN · OPERATOR"] + TR["Treasury ops"] + VS["Valuation source
attestation data"] + KYC["KYC process"] + end + + subgraph RW["REAL-WORLD STRUCTURE"] + CU["Legal wrapper · custody ·
the asset"] + end + + LP -->|"requests · claims"| V + ADM -->|"admin operations"| AUTH + OR ---|"attested value + proof ref ↑"| VS + IVC ---|"verified addresses ↑"| KYC + SAC ---|"reserve in / out"| TR + TR -->|"transfer_to_custodian"| CU + VS ---|"valuation data ↑"| CU + + classDef strata fill:#FFE0B2,stroke:#E65100,stroke-width:2px +``` + +Authority and contract wiring: + +```mermaid +flowchart LR + subgraph Investor + W[Wallet
Freighter / Wallets Kit] + LP[Investor dApp
backend-free] + end + + subgraph Operator["Operator (off-chain)"] + KYC[KYC process
operator's own stack] + VAL[Valuation source
fund accounting] + CUST[Custodian
real-world structure] + end + + subgraph Authorities["Authorities (native Stellar multisigs)"] + GOV[governance] + CMP[compliance] + ATT[attestation] + TRE[treasury] + GRD[guardian] + end + + subgraph Soroban["Soroban contracts (the kit)"] + V[Vault
requests, pricing,
reserve accounting] + ST[Share token
OZ SEP-41 + SEP-57 ext] + MGR[Manager
token authority passthrough] + CM[Compliance module
SEP-57 identity + rules,
allowlist] + end + + OPS[Admin panel
propose / review / sign / execute] + + W --> LP --> V + KYC --> CMP + VAL --> ATT + GOV & CMP & ATT & TRE & GRD --> OPS --> V + OPS --> MGR --> ST + CMP -->|writes via Manager| CM + ST -.->|identity + transfer rules| CM + TRE -.->|free reserve only,
zero shortfall| CUST + V ---|SAC interface| USDC[Deposit asset] +``` + +Both interfaces are backend-free: they read contract state and build transactions that the authority multisigs sign. The chain never sees KYC data or valuation methodology, only their outputs: an allowlisted address, an attested number with a proof reference. + +## 3. Components and authorities + +| Component | Role | Controls | +| --- | --- | --- | +| **Vault** | Request lifecycle, pricing, split reserve accounting, custodian transfers, upgrade control | One authority per privileged entrypoint; upgrades behind a governance timelock with an exit window | +| **Share token** | OZ SEP-41 + SEP-57 RWA extensions: freeze, forced transfer, recovery, identity and compliance checks on every transfer, independent transfer pause | Token manager authority held exclusively by the Manager contract, never a human key | +| **Manager** | Token manager passthrough: every privileged token operation goes through it and is checked against roles | Role-gated | +| **Compliance module** | Implements the SEP-57 identity and rules interfaces the share token consults, with the allowlist as its only rule | Written only by the compliance authority via the Manager; replaceable by OZ's identity verifier and compliance contracts (with RWA Wizard modules) without touching the token | +| **Five authorities** | governance (parameters, roles, timelocked upgrades), compliance (allowlist, token interventions), attestation (valuation only), treasury (reserve movements only), guardian (pause; never payable claims) | Native Stellar multisig accounts; treasury and guardian distinct, compliance distinct from governance and treasury | +| **Custodian** | Off-chain party holding the real-world structure; a genesis-configured slot rotatable only by governance | Not an on-chain authority | + +Each authority's threshold is sized to the quorum that authority requires. Signing of privileged operations through a coordinator is verified end to end, not assumed. + +## 4. Request lifecycle + +Every position change is a request with three states: **pending, priced, claimed**. Each accepted attestation prices every pending request; requests are then settled individually, by any account, so no attestation processes an unbounded batch and no investor can choose their price. + +### 4.1 Subscription + +- Request: verifies the receiver is allowlisted, moves the deposit asset into escrow. At most one active request per controller. +- Pricing: the escrow leaves the cancellable bucket, the share quantity is set at the attested price, and the shares are minted and held for the investor. +- Cancellation: atomic, available until the request's pricing attestation is accepted; returns the escrowed asset in full. +- Share claim: re-verifies the receiver and delivers the shares. If verification fails, the position remains shares and exits through the redemption lifecycle at the then-current price. No nominal refund exists after pricing. + +### 4.2 Redemption + +- Request: moves shares into escrow, no admission limit. +- Pricing: the escrowed shares are burned and a fixed cash liability enters committed at the attested price. Priced claims are never re-priced. +- Coverage: a priced claim is payable when the liquid reserve covers it, in FIFO order. An earlier unpaid claim never blocks a later one that is already covered. The gap between committed and liquid reserve is the on-chain shortfall treasury must top up. +- Cash claim: pays the fixed amount; it does not depend on identity. A delisted, non-frozen investor uses the exit-only cash path and cannot cancel back to shares. + +```mermaid +sequenceDiagram + participant I as Investor (allowlisted) + participant V as Vault + participant O as Attestation authority + I->>V: request_deposit(amount), asset to escrow + Note over V: pending, cancellable + O->>V: attestation + Note over V: priced: shares minted and held,
cancellation closed + I->>V: claim, re-checks allowlist + V-->>I: shares delivered +``` + +```mermaid +sequenceDiagram + participant I as Investor + participant V as Vault + participant O as Attestation authority + participant T as Treasury authority + I->>V: request_redeem(shares), shares to escrow + O->>V: attestation + Note over V: priced: shares burned,
cash liability fixed + alt liquid reserve covers the claim + I->>V: claim + V-->>I: deposit asset paid + else reserve short + Note over V: shortfall visible on-chain + T->>V: return_from_custodian(funds) + I->>V: claim + V-->>I: deposit asset paid + end +``` + +## 5. Valuation and accounting + +The NAV is a permissioned attestation of the share price, published with a proof reference. The reporter computes it off-chain: + +```text +share_price = (deployed_value + onchain_reserve - committed) / share_supply +``` + +where deployed_value is the attested off-chain value and the other three figures are read from the vault's public surface. Cancellable escrow is not part of it: pending subscriptions hold no shares yet. The contract does not recompute the price; it validates the report, stores it, and exposes it together with the liquidity figures it does own: + +```text +liquid_reserve = reserve - cancellable_deposit_escrow +free_reserve = max(liquid_reserve - committed, 0) +shortfall = max(committed - liquid_reserve, 0) +``` + +**Attestation guardrails:** the reporter is a multisig, never a single key. Each attestation carries the share price and a proof reference; attestations are ordered by their acceptance time on the ledger. The price must stay within configured bounds, a minimum cooldown bounds frequency, and the deviation cap is asymmetric: upside is bounded per update, downward updates are uncapped so losses are recognized immediately. + +**Freshness and pause:** each attestation opens a validity window; when it lapses the feed is stale and new requests stop being priced. Guardian or governance can pause the vault: new requests, pricing and custodian transfers stop; payable claims, pending cancellations and refunds continue; attestations that pass the guardrails are still accepted, so recovery never deadlocks. Only governance lifts the pause, and only while the latest attestation is fresh. Paused and stale are independent: freshness lapses on its own, the pause is a decision. + +## 6. Compliance + +- The share token follows the SEP-57 topology: it consults identity for every receiver and rules for every transfer. The kit ships one compliance module for both, with the allowlist as its only rule; an operator needing richer rules replaces it with OZ's contracts without touching the token. +- KYC happens wherever the operator runs it; the chain sees only its output. The compliance authority writes allowlist entries via the Manager. +- Token interventions (freeze, unfreeze, forced transfer, recovery) are compliance operations via the Manager, available even while the vault is paused. + +## 7. Treasury and custodian + +- The custodian is a genesis-configured slot; only governance can rotate it. Transfers to the custodian move free reserve only, only to that address, and only while the shortfall is zero. +- Transfers from the custodian are always open and credit only assets actually received. +- The exposed figures (share price, liquid reserve, committed, shortfall) make reserve coverage legible to investors and integrators. +- Closing a vault needs no dedicated mechanism: governance pauses the vault, the attester publishes the final value, treasury returns the funds, and every position exits through the normal redemption path. + +## 8. Reference interfaces + +Both interfaces are part of the kit: they are how investors and operators use the protocol without writing code. Both are backend-free and read only the public contract surface; each deployment brands and hosts its own. + +### Investor dApp + +The investor's five actions and nothing else: deposit request, share claim, redeem request, cash claim, cancellation of a pending request. Nothing is valued at request creation; the only reference shown is the latest attested NAV, labelled and timestamped. Three states per side, none skipped: pending, priced, claimed. Waiting is stated, never counted down. A priced cash claim shows whether the reserve covers it and the current shortfall; a delisted investor sees the exit-only path. + +### Admin panel + +Operates an existing vault; deploys nothing. Every privileged entrypoint belongs to exactly one authority, so the panel splits into five surfaces: + +| Surface | Authority | Cadence | Operations | +| --- | --- | --- | --- | +| Cycle | attestation, treasury; anyone settles | Continuous | Attestations, funding, transfers to and from the custodian, settlement | +| Compliance | compliance | Continuous | Allowlist, freeze/unfreeze, forced transfer, recovery via Manager | +| Emergency | guardian | Rare and urgent | Vault pause, share-token pause | +| Configuration | governance | Rare and deliberate | Custodian slot, compliance module, parameters (bounds, freshness, timelock) | +| Governance | governance | Very rare | Roles, admin handover, upgrade | + +Every operation is shown in domain terms, with its conditions and resulting state, before a signature is requested; read-only by default. Signing runs through an existing self-hostable coordinator. Configuration is derived from the public surface given the vault address. + +## 9. Trust boundaries & failure modes + +| Boundary | Risk | Mitigation | +| --- | --- | --- | +| Attestation authority | Wrong or compromised reports misprice requests | Multisig reporter; the asymmetric deviation cap bounds any single report and the cooldown bounds frequency; a sustained sequence of biased reports within the cap remains possible, is bounded in speed, and is the monitoring plan's primary alert, with the guardian pause as the reactive control. Residual risk: value transfer between entry and exit cohorts | +| Governance keys | Malicious upgrade | Timelock with an investor exit window; the guardian pause freezes the timelock clock so the window cannot be waited out while entries are closed | +| Treasury keys | Reserve drained | Only free reserve is movable, only to the genesis-configured custodian, verified on-chain; outbound transfers are blocked while any shortfall exists, and escrowed subscriptions never leave the vault | +| Guardian keys | Griefing via pause | Guardian can only pause new requests, pricing and custodian transfers; it can never block payable claims or move funds; governance reverts and rotates the role | +| Compliance keys | Wrongful delisting or freeze | Delisted investors keep the exit-only cash path; freezes require the Manager path and are auditable per operation | +| Compliance module | Faulty module blocks transfers | Fail-closed semantics; replaceable by governance without touching the token | +| Deposit asset issuer | Freeze or clawback of the vault's reserve | Not mitigated by the kit; declared risk of the chosen asset, verified and reported at genesis (auth flags) | +| Custodian / real world | Underlying loss or delay | Reflected through attested NAV (downward updates uncapped); the kit constrains what reaches the chain, it does not verify the world | + +Disclosed trust assumptions: the accuracy of the operator's KYC process, the quality of the data behind each attestation, and the operator's key ceremony. + +## 10. Deployment + +Deployment is scripted and ends with no human key holding governance. It is complete only when the final state is verified on-chain: every authority is the intended multisig, no bootstrap key retains any role, the configuration matches the request, and the deposit asset's auth flags are checked and reported. + +## 11. Relationship to existing Stellar tooling + +Where a cell says "Not provided", it means: not provided by SEP-41, SEP-56, SEP-57, OpenZeppelin Stellar Contracts, or the Soroban vault implementations evaluated (Templar, Untangled OctoVault, DeFindex). + +| Component | Already exists | Strata | +| --- | --- | --- | +| SEP-41 token + SEP-57 RWA extensions (freeze, forced transfer, recovery) | OpenZeppelin stellar-tokens; the RWA Wizard scaffolds the regulated token | Consumed and extended; the kit's compliance module implements the SEP-57 interfaces the token expects | +| Synchronous tokenized vault | SEP-56 / OZ Token Vault | Not a base for Strata: SEP-56 assumes the price exists at call time, so its interface cannot express a request lifecycle. Only OZ conversion and rounding math reused, as library code | +| Access control, pausable, upgradeable, timelock | OZ crates | Consumed; pinned by exact version, audit coverage and gaps documented per component | +| Multisig and signing coordination | Native Stellar + existing coordinators, OZ Role Manager | Integrated | +| Request lifecycle priced against attestations | Not provided (ERC-7540 on EVM, where OpenZeppelin ships an implementation) | Core of the kit | +| Guarded valuation oracle with freshness and pause | Not provided | Core of the kit | +| Split reserve accounting with explicit shortfall exposure | Not provided | Core of the kit | +| FIFO redemption coverage, exit-only path | Not provided | Core of the kit | +| Reusable RWA configuration, verified genesis, white-label frontends | Not provided | Core of the kit | + +## 12. Delivery phases + +| Phase | Deliverables | Evidence of completion | +| --- | --- | --- | +| **1: Attested valuation and request pricing** | Valuation oracle with guardrails, freshness and pause; request lifecycle with escrow, cancellation and pricing-time mint and burn; public kit spec | Accounting property tests green in CI (price preserved by deposits, redemptions and custodian transfers; cancellation; rounding); multisig signing of privileged operations verified end to end through the coordinator | +| **2: Split accounting and redemption** | Shortfall exposure; FIFO redemption coverage; exit-only cash path; SEP-57 integration (compliance module, delisted-investor path); threat model and monitoring plan | Settlement cost measured at 1, 10, 100 and 1,000 pending requests; SEP-57 path demonstrated end to end on testnet | +| **3: Reference interfaces, audit remediation, mainnet** | Investor dApp and Admin panel (five surfaces, one per authority), backend-free; reproducible deployment; audit remediation (all critical and high findings fixed and verified, public changelog); mainnet reference deployment | Audit inheritance matrix published (component, version, audit report, Strata delta, resulting scope); external developer deploys a configured instance from docs alone; reference instance live on mainnet | + +The funded core is the valuation, pricing and accounting layer. The Investor dApp and the Admin panel are how that core is used by investors and operators; without the Admin panel the five authorities are not operable by a non-developer, and the kit stops being a kit. From 5e9a6f297407af4b35789f981c044d4a23f0fef9 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 09:38:42 -0300 Subject: [PATCH 04/10] style: format the architecture doc with prettier CI runs prettier . --check and the new file failed it. --- docs/architecture.md | 293 ++++++++++++++++++++++++++++++------------- 1 file changed, 203 insertions(+), 90 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 732e231..6a9e593 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,31 +1,75 @@ # Technical Architecture: Strata -**Product:** Strata, an RWA lifecycle protocol for attested-value assets: request-based subscriptions and redemptions priced against on-chain guarded attestations, built on OpenZeppelin's Stellar contracts. Distributed as a kit: each operator deploys, configures and brands an independent white-label instance. -**Chain:** Stellar. Soroban smart contracts; classic assets consumed through their Stellar Asset Contract (SAC) interface. -**Deposit asset:** any classic Stellar asset chosen at genesis (typically a USD stablecoin such as USDC). -**Maintainer:** BootNode (bootnode.dev). - -Shares in this vault are a claim on an off-chain asset whose value is attested on chain. No price exists at the moment you act, so entry and exit are requests: what you put in goes into escrow, the next accepted attestation prices it, and you claim the result. A priced claim is never re-priced, and a covered cash claim cannot be blocked by a pause or by losing your allowlist place. +**Product:** Strata, an RWA lifecycle protocol for attested-value assets: +request-based subscriptions and redemptions priced against on-chain guarded +attestations, built on OpenZeppelin's Stellar contracts. Distributed as a kit: +each operator deploys, configures and brands an independent white-label +instance. **Chain:** Stellar. Soroban smart contracts; classic assets consumed +through their Stellar Asset Contract (SAC) interface. **Deposit asset:** any +classic Stellar asset chosen at genesis (typically a USD stablecoin such as +USDC). **Maintainer:** BootNode (bootnode.dev). + +Shares in this vault are a claim on an off-chain asset whose value is attested +on chain. No price exists at the moment you act, so entry and exit are requests: +what you put in goes into escrow, the next accepted attestation prices it, and +you claim the result. A priced claim is never re-priced, and a covered cash +claim cannot be blocked by a pause or by losing your allowlist place. --- ## 1. Design principles -1. **Configured, not built.** Each operator deploys an independent instance: no factory, no shared state. A deployment is defined by its configuration and five authorities (native Stellar multisig accounts). Everything an operator brands or configures lives outside the trust boundary; everything inside it is the same code for every adopter. - -2. **Built on OpenZeppelin, differentiated above it.** The share token is OZ's SEP-41 token with the SEP-57 RWA extensions (freeze, forced transfer, recovery); access control, pausable and upgradeable come from OZ crates. Dependencies are pinned to exact versions and audit inheritance is evaluated component by component; what Strata adds stays within its own audit scope. What OZ does not cover is the layer Strata adds: the attested valuation oracle, request-based flows priced against attestations, split reserve accounting, and the operational surface. - -3. **Request-based entry and exit.** The exact price of a share does not exist when an investor acts; it is attested afterwards. Entry and exit are requests: funds or shares go into escrow, the next accepted attestation prices them, and the investor claims the result. This is the ERC-7540 pattern with two differences: cancellation is a single step that closes when the pricing attestation arrives, and the price is set by each attestation for every pending request, not by a manager. - -4. **Attested NAV.** The reporter attests the share price itself, computed off-chain from the deployed value and the vault's public figures under a documented methodology. The contract validates, stores and exposes it. Every on-chain operation preserves that price by construction: a priced deposit adds assets and shares in proportion, a priced redemption removes both, a custodian transfer moves value between pockets without changing the total. - -5. **Payable claims always pay.** Neither the guardian pause, a delisting, nor a stale valuation can block the payment of an already-priced, funded cash claim. A pause can delay unpriced requests. Priced claims are never re-priced and never identity-gated. A delisted, non-frozen investor exits through the exit-only cash path. Outside this guarantee, disclosed as trust assumptions: a frozen investor, deposit-asset issuer controls, and reserve liquidity. - -6. **Split accounting, with priced and payable as separate states.** The vault exposes committed (priced redemption liabilities), cancellable escrow (pending subscriptions the investor can still recall) and free reserve. Escrowed subscriptions never leave the vault. Committed can exceed the liquid reserve; that gap is an explicit on-chain shortfall, outbound transfers are blocked while it exists, and priced claims stay payable in FIFO order as treasury tops up. A liquidity shortfall is not insolvency: solvency compares total attested assets against liabilities and is handled by attested losses and governance. +1. **Configured, not built.** Each operator deploys an independent instance: no + factory, no shared state. A deployment is defined by its configuration and + five authorities (native Stellar multisig accounts). Everything an operator + brands or configures lives outside the trust boundary; everything inside it + is the same code for every adopter. + +2. **Built on OpenZeppelin, differentiated above it.** The share token is OZ's + SEP-41 token with the SEP-57 RWA extensions (freeze, forced transfer, + recovery); access control, pausable and upgradeable come from OZ crates. + Dependencies are pinned to exact versions and audit inheritance is evaluated + component by component; what Strata adds stays within its own audit scope. + What OZ does not cover is the layer Strata adds: the attested valuation + oracle, request-based flows priced against attestations, split reserve + accounting, and the operational surface. + +3. **Request-based entry and exit.** The exact price of a share does not exist + when an investor acts; it is attested afterwards. Entry and exit are + requests: funds or shares go into escrow, the next accepted attestation + prices them, and the investor claims the result. This is the ERC-7540 pattern + with two differences: cancellation is a single step that closes when the + pricing attestation arrives, and the price is set by each attestation for + every pending request, not by a manager. + +4. **Attested NAV.** The reporter attests the share price itself, computed + off-chain from the deployed value and the vault's public figures under a + documented methodology. The contract validates, stores and exposes it. Every + on-chain operation preserves that price by construction: a priced deposit + adds assets and shares in proportion, a priced redemption removes both, a + custodian transfer moves value between pockets without changing the total. + +5. **Payable claims always pay.** Neither the guardian pause, a delisting, nor a + stale valuation can block the payment of an already-priced, funded cash + claim. A pause can delay unpriced requests. Priced claims are never re-priced + and never identity-gated. A delisted, non-frozen investor exits through the + exit-only cash path. Outside this guarantee, disclosed as trust assumptions: + a frozen investor, deposit-asset issuer controls, and reserve liquidity. + +6. **Split accounting, with priced and payable as separate states.** The vault + exposes committed (priced redemption liabilities), cancellable escrow + (pending subscriptions the investor can still recall) and free reserve. + Escrowed subscriptions never leave the vault. Committed can exceed the liquid + reserve; that gap is an explicit on-chain shortfall, outbound transfers are + blocked while it exists, and priced claims stay payable in FIFO order as + treasury tops up. A liquidity shortfall is not insolvency: solvency compares + total attested assets against liabilities and is handled by attested losses + and governance. ## 2. Component overview -Orange marks what only Strata provides; white is the existing ecosystem the kit consumes or integrates. +Orange marks what only Strata provides; white is the existing ecosystem the kit +consumes or integrates. ```mermaid flowchart TB @@ -115,38 +159,57 @@ flowchart LR V ---|SAC interface| USDC[Deposit asset] ``` -Both interfaces are backend-free: they read contract state and build transactions that the authority multisigs sign. The chain never sees KYC data or valuation methodology, only their outputs: an allowlisted address, an attested number with a proof reference. +Both interfaces are backend-free: they read contract state and build +transactions that the authority multisigs sign. The chain never sees KYC data or +valuation methodology, only their outputs: an allowlisted address, an attested +number with a proof reference. ## 3. Components and authorities -| Component | Role | Controls | -| --- | --- | --- | -| **Vault** | Request lifecycle, pricing, split reserve accounting, custodian transfers, upgrade control | One authority per privileged entrypoint; upgrades behind a governance timelock with an exit window | -| **Share token** | OZ SEP-41 + SEP-57 RWA extensions: freeze, forced transfer, recovery, identity and compliance checks on every transfer, independent transfer pause | Token manager authority held exclusively by the Manager contract, never a human key | -| **Manager** | Token manager passthrough: every privileged token operation goes through it and is checked against roles | Role-gated | -| **Compliance module** | Implements the SEP-57 identity and rules interfaces the share token consults, with the allowlist as its only rule | Written only by the compliance authority via the Manager; replaceable by OZ's identity verifier and compliance contracts (with RWA Wizard modules) without touching the token | -| **Five authorities** | governance (parameters, roles, timelocked upgrades), compliance (allowlist, token interventions), attestation (valuation only), treasury (reserve movements only), guardian (pause; never payable claims) | Native Stellar multisig accounts; treasury and guardian distinct, compliance distinct from governance and treasury | -| **Custodian** | Off-chain party holding the real-world structure; a genesis-configured slot rotatable only by governance | Not an on-chain authority | +| Component | Role | Controls | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Vault** | Request lifecycle, pricing, split reserve accounting, custodian transfers, upgrade control | One authority per privileged entrypoint; upgrades behind a governance timelock with an exit window | +| **Share token** | OZ SEP-41 + SEP-57 RWA extensions: freeze, forced transfer, recovery, identity and compliance checks on every transfer, independent transfer pause | Token manager authority held exclusively by the Manager contract, never a human key | +| **Manager** | Token manager passthrough: every privileged token operation goes through it and is checked against roles | Role-gated | +| **Compliance module** | Implements the SEP-57 identity and rules interfaces the share token consults, with the allowlist as its only rule | Written only by the compliance authority via the Manager; replaceable by OZ's identity verifier and compliance contracts (with RWA Wizard modules) without touching the token | +| **Five authorities** | governance (parameters, roles, timelocked upgrades), compliance (allowlist, token interventions), attestation (valuation only), treasury (reserve movements only), guardian (pause; never payable claims) | Native Stellar multisig accounts; treasury and guardian distinct, compliance distinct from governance and treasury | +| **Custodian** | Off-chain party holding the real-world structure; a genesis-configured slot rotatable only by governance | Not an on-chain authority | -Each authority's threshold is sized to the quorum that authority requires. Signing of privileged operations through a coordinator is verified end to end, not assumed. +Each authority's threshold is sized to the quorum that authority requires. +Signing of privileged operations through a coordinator is verified end to end, +not assumed. ## 4. Request lifecycle -Every position change is a request with three states: **pending, priced, claimed**. Each accepted attestation prices every pending request; requests are then settled individually, by any account, so no attestation processes an unbounded batch and no investor can choose their price. +Every position change is a request with three states: **pending, priced, +claimed**. Each accepted attestation prices every pending request; requests are +then settled individually, by any account, so no attestation processes an +unbounded batch and no investor can choose their price. ### 4.1 Subscription -- Request: verifies the receiver is allowlisted, moves the deposit asset into escrow. At most one active request per controller. -- Pricing: the escrow leaves the cancellable bucket, the share quantity is set at the attested price, and the shares are minted and held for the investor. -- Cancellation: atomic, available until the request's pricing attestation is accepted; returns the escrowed asset in full. -- Share claim: re-verifies the receiver and delivers the shares. If verification fails, the position remains shares and exits through the redemption lifecycle at the then-current price. No nominal refund exists after pricing. +- Request: verifies the receiver is allowlisted, moves the deposit asset into + escrow. At most one active request per controller. +- Pricing: the escrow leaves the cancellable bucket, the share quantity is set + at the attested price, and the shares are minted and held for the investor. +- Cancellation: atomic, available until the request's pricing attestation is + accepted; returns the escrowed asset in full. +- Share claim: re-verifies the receiver and delivers the shares. If verification + fails, the position remains shares and exits through the redemption lifecycle + at the then-current price. No nominal refund exists after pricing. ### 4.2 Redemption - Request: moves shares into escrow, no admission limit. -- Pricing: the escrowed shares are burned and a fixed cash liability enters committed at the attested price. Priced claims are never re-priced. -- Coverage: a priced claim is payable when the liquid reserve covers it, in FIFO order. An earlier unpaid claim never blocks a later one that is already covered. The gap between committed and liquid reserve is the on-chain shortfall treasury must top up. -- Cash claim: pays the fixed amount; it does not depend on identity. A delisted, non-frozen investor uses the exit-only cash path and cannot cancel back to shares. +- Pricing: the escrowed shares are burned and a fixed cash liability enters + committed at the attested price. Priced claims are never re-priced. +- Coverage: a priced claim is payable when the liquid reserve covers it, in FIFO + order. An earlier unpaid claim never blocks a later one that is already + covered. The gap between committed and liquid reserve is the on-chain + shortfall treasury must top up. +- Cash claim: pays the fixed amount; it does not depend on identity. A delisted, + non-frozen investor uses the exit-only cash path and cannot cancel back to + shares. ```mermaid sequenceDiagram @@ -183,13 +246,18 @@ sequenceDiagram ## 5. Valuation and accounting -The NAV is a permissioned attestation of the share price, published with a proof reference. The reporter computes it off-chain: +The NAV is a permissioned attestation of the share price, published with a proof +reference. The reporter computes it off-chain: ```text share_price = (deployed_value + onchain_reserve - committed) / share_supply ``` -where deployed_value is the attested off-chain value and the other three figures are read from the vault's public surface. Cancellable escrow is not part of it: pending subscriptions hold no shares yet. The contract does not recompute the price; it validates the report, stores it, and exposes it together with the liquidity figures it does own: +where deployed_value is the attested off-chain value and the other three figures +are read from the vault's public surface. Cancellable escrow is not part of it: +pending subscriptions hold no shares yet. The contract does not recompute the +price; it validates the report, stores it, and exposes it together with the +liquidity figures it does own: ```text liquid_reserve = reserve - cancellable_deposit_escrow @@ -197,86 +265,131 @@ free_reserve = max(liquid_reserve - committed, 0) shortfall = max(committed - liquid_reserve, 0) ``` -**Attestation guardrails:** the reporter is a multisig, never a single key. Each attestation carries the share price and a proof reference; attestations are ordered by their acceptance time on the ledger. The price must stay within configured bounds, a minimum cooldown bounds frequency, and the deviation cap is asymmetric: upside is bounded per update, downward updates are uncapped so losses are recognized immediately. - -**Freshness and pause:** each attestation opens a validity window; when it lapses the feed is stale and new requests stop being priced. Guardian or governance can pause the vault: new requests, pricing and custodian transfers stop; payable claims, pending cancellations and refunds continue; attestations that pass the guardrails are still accepted, so recovery never deadlocks. Only governance lifts the pause, and only while the latest attestation is fresh. Paused and stale are independent: freshness lapses on its own, the pause is a decision. +**Attestation guardrails:** the reporter is a multisig, never a single key. Each +attestation carries the share price and a proof reference; attestations are +ordered by their acceptance time on the ledger. The price must stay within +configured bounds, a minimum cooldown bounds frequency, and the deviation cap is +asymmetric: upside is bounded per update, downward updates are uncapped so +losses are recognized immediately. + +**Freshness and pause:** each attestation opens a validity window; when it +lapses the feed is stale and new requests stop being priced. Guardian or +governance can pause the vault: new requests, pricing and custodian transfers +stop; payable claims, pending cancellations and refunds continue; attestations +that pass the guardrails are still accepted, so recovery never deadlocks. Only +governance lifts the pause, and only while the latest attestation is fresh. +Paused and stale are independent: freshness lapses on its own, the pause is a +decision. ## 6. Compliance -- The share token follows the SEP-57 topology: it consults identity for every receiver and rules for every transfer. The kit ships one compliance module for both, with the allowlist as its only rule; an operator needing richer rules replaces it with OZ's contracts without touching the token. -- KYC happens wherever the operator runs it; the chain sees only its output. The compliance authority writes allowlist entries via the Manager. -- Token interventions (freeze, unfreeze, forced transfer, recovery) are compliance operations via the Manager, available even while the vault is paused. +- The share token follows the SEP-57 topology: it consults identity for every + receiver and rules for every transfer. The kit ships one compliance module for + both, with the allowlist as its only rule; an operator needing richer rules + replaces it with OZ's contracts without touching the token. +- KYC happens wherever the operator runs it; the chain sees only its output. The + compliance authority writes allowlist entries via the Manager. +- Token interventions (freeze, unfreeze, forced transfer, recovery) are + compliance operations via the Manager, available even while the vault is + paused. ## 7. Treasury and custodian -- The custodian is a genesis-configured slot; only governance can rotate it. Transfers to the custodian move free reserve only, only to that address, and only while the shortfall is zero. -- Transfers from the custodian are always open and credit only assets actually received. -- The exposed figures (share price, liquid reserve, committed, shortfall) make reserve coverage legible to investors and integrators. -- Closing a vault needs no dedicated mechanism: governance pauses the vault, the attester publishes the final value, treasury returns the funds, and every position exits through the normal redemption path. +- The custodian is a genesis-configured slot; only governance can rotate it. + Transfers to the custodian move free reserve only, only to that address, and + only while the shortfall is zero. +- Transfers from the custodian are always open and credit only assets actually + received. +- The exposed figures (share price, liquid reserve, committed, shortfall) make + reserve coverage legible to investors and integrators. +- Closing a vault needs no dedicated mechanism: governance pauses the vault, the + attester publishes the final value, treasury returns the funds, and every + position exits through the normal redemption path. ## 8. Reference interfaces -Both interfaces are part of the kit: they are how investors and operators use the protocol without writing code. Both are backend-free and read only the public contract surface; each deployment brands and hosts its own. +Both interfaces are part of the kit: they are how investors and operators use +the protocol without writing code. Both are backend-free and read only the +public contract surface; each deployment brands and hosts its own. ### Investor dApp -The investor's five actions and nothing else: deposit request, share claim, redeem request, cash claim, cancellation of a pending request. Nothing is valued at request creation; the only reference shown is the latest attested NAV, labelled and timestamped. Three states per side, none skipped: pending, priced, claimed. Waiting is stated, never counted down. A priced cash claim shows whether the reserve covers it and the current shortfall; a delisted investor sees the exit-only path. +The investor's five actions and nothing else: deposit request, share claim, +redeem request, cash claim, cancellation of a pending request. Nothing is valued +at request creation; the only reference shown is the latest attested NAV, +labelled and timestamped. Three states per side, none skipped: pending, priced, +claimed. Waiting is stated, never counted down. A priced cash claim shows +whether the reserve covers it and the current shortfall; a delisted investor +sees the exit-only path. ### Admin panel -Operates an existing vault; deploys nothing. Every privileged entrypoint belongs to exactly one authority, so the panel splits into five surfaces: +Operates an existing vault; deploys nothing. Every privileged entrypoint belongs +to exactly one authority, so the panel splits into five surfaces: -| Surface | Authority | Cadence | Operations | -| --- | --- | --- | --- | -| Cycle | attestation, treasury; anyone settles | Continuous | Attestations, funding, transfers to and from the custodian, settlement | -| Compliance | compliance | Continuous | Allowlist, freeze/unfreeze, forced transfer, recovery via Manager | -| Emergency | guardian | Rare and urgent | Vault pause, share-token pause | -| Configuration | governance | Rare and deliberate | Custodian slot, compliance module, parameters (bounds, freshness, timelock) | -| Governance | governance | Very rare | Roles, admin handover, upgrade | +| Surface | Authority | Cadence | Operations | +| ------------- | ------------------------------------- | ------------------- | --------------------------------------------------------------------------- | +| Cycle | attestation, treasury; anyone settles | Continuous | Attestations, funding, transfers to and from the custodian, settlement | +| Compliance | compliance | Continuous | Allowlist, freeze/unfreeze, forced transfer, recovery via Manager | +| Emergency | guardian | Rare and urgent | Vault pause, share-token pause | +| Configuration | governance | Rare and deliberate | Custodian slot, compliance module, parameters (bounds, freshness, timelock) | +| Governance | governance | Very rare | Roles, admin handover, upgrade | -Every operation is shown in domain terms, with its conditions and resulting state, before a signature is requested; read-only by default. Signing runs through an existing self-hostable coordinator. Configuration is derived from the public surface given the vault address. +Every operation is shown in domain terms, with its conditions and resulting +state, before a signature is requested; read-only by default. Signing runs +through an existing self-hostable coordinator. Configuration is derived from the +public surface given the vault address. ## 9. Trust boundaries & failure modes -| Boundary | Risk | Mitigation | -| --- | --- | --- | -| Attestation authority | Wrong or compromised reports misprice requests | Multisig reporter; the asymmetric deviation cap bounds any single report and the cooldown bounds frequency; a sustained sequence of biased reports within the cap remains possible, is bounded in speed, and is the monitoring plan's primary alert, with the guardian pause as the reactive control. Residual risk: value transfer between entry and exit cohorts | -| Governance keys | Malicious upgrade | Timelock with an investor exit window; the guardian pause freezes the timelock clock so the window cannot be waited out while entries are closed | -| Treasury keys | Reserve drained | Only free reserve is movable, only to the genesis-configured custodian, verified on-chain; outbound transfers are blocked while any shortfall exists, and escrowed subscriptions never leave the vault | -| Guardian keys | Griefing via pause | Guardian can only pause new requests, pricing and custodian transfers; it can never block payable claims or move funds; governance reverts and rotates the role | -| Compliance keys | Wrongful delisting or freeze | Delisted investors keep the exit-only cash path; freezes require the Manager path and are auditable per operation | -| Compliance module | Faulty module blocks transfers | Fail-closed semantics; replaceable by governance without touching the token | -| Deposit asset issuer | Freeze or clawback of the vault's reserve | Not mitigated by the kit; declared risk of the chosen asset, verified and reported at genesis (auth flags) | -| Custodian / real world | Underlying loss or delay | Reflected through attested NAV (downward updates uncapped); the kit constrains what reaches the chain, it does not verify the world | +| Boundary | Risk | Mitigation | +| ---------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Attestation authority | Wrong or compromised reports misprice requests | Multisig reporter; the asymmetric deviation cap bounds any single report and the cooldown bounds frequency; a sustained sequence of biased reports within the cap remains possible, is bounded in speed, and is the monitoring plan's primary alert, with the guardian pause as the reactive control. Residual risk: value transfer between entry and exit cohorts | +| Governance keys | Malicious upgrade | Timelock with an investor exit window; the guardian pause freezes the timelock clock so the window cannot be waited out while entries are closed | +| Treasury keys | Reserve drained | Only free reserve is movable, only to the genesis-configured custodian, verified on-chain; outbound transfers are blocked while any shortfall exists, and escrowed subscriptions never leave the vault | +| Guardian keys | Griefing via pause | Guardian can only pause new requests, pricing and custodian transfers; it can never block payable claims or move funds; governance reverts and rotates the role | +| Compliance keys | Wrongful delisting or freeze | Delisted investors keep the exit-only cash path; freezes require the Manager path and are auditable per operation | +| Compliance module | Faulty module blocks transfers | Fail-closed semantics; replaceable by governance without touching the token | +| Deposit asset issuer | Freeze or clawback of the vault's reserve | Not mitigated by the kit; declared risk of the chosen asset, verified and reported at genesis (auth flags) | +| Custodian / real world | Underlying loss or delay | Reflected through attested NAV (downward updates uncapped); the kit constrains what reaches the chain, it does not verify the world | -Disclosed trust assumptions: the accuracy of the operator's KYC process, the quality of the data behind each attestation, and the operator's key ceremony. +Disclosed trust assumptions: the accuracy of the operator's KYC process, the +quality of the data behind each attestation, and the operator's key ceremony. ## 10. Deployment -Deployment is scripted and ends with no human key holding governance. It is complete only when the final state is verified on-chain: every authority is the intended multisig, no bootstrap key retains any role, the configuration matches the request, and the deposit asset's auth flags are checked and reported. +Deployment is scripted and ends with no human key holding governance. It is +complete only when the final state is verified on-chain: every authority is the +intended multisig, no bootstrap key retains any role, the configuration matches +the request, and the deposit asset's auth flags are checked and reported. ## 11. Relationship to existing Stellar tooling -Where a cell says "Not provided", it means: not provided by SEP-41, SEP-56, SEP-57, OpenZeppelin Stellar Contracts, or the Soroban vault implementations evaluated (Templar, Untangled OctoVault, DeFindex). - -| Component | Already exists | Strata | -| --- | --- | --- | -| SEP-41 token + SEP-57 RWA extensions (freeze, forced transfer, recovery) | OpenZeppelin stellar-tokens; the RWA Wizard scaffolds the regulated token | Consumed and extended; the kit's compliance module implements the SEP-57 interfaces the token expects | -| Synchronous tokenized vault | SEP-56 / OZ Token Vault | Not a base for Strata: SEP-56 assumes the price exists at call time, so its interface cannot express a request lifecycle. Only OZ conversion and rounding math reused, as library code | -| Access control, pausable, upgradeable, timelock | OZ crates | Consumed; pinned by exact version, audit coverage and gaps documented per component | -| Multisig and signing coordination | Native Stellar + existing coordinators, OZ Role Manager | Integrated | -| Request lifecycle priced against attestations | Not provided (ERC-7540 on EVM, where OpenZeppelin ships an implementation) | Core of the kit | -| Guarded valuation oracle with freshness and pause | Not provided | Core of the kit | -| Split reserve accounting with explicit shortfall exposure | Not provided | Core of the kit | -| FIFO redemption coverage, exit-only path | Not provided | Core of the kit | -| Reusable RWA configuration, verified genesis, white-label frontends | Not provided | Core of the kit | +Where a cell says "Not provided", it means: not provided by SEP-41, SEP-56, +SEP-57, OpenZeppelin Stellar Contracts, or the Soroban vault implementations +evaluated (Templar, Untangled OctoVault, DeFindex). + +| Component | Already exists | Strata | +| ------------------------------------------------------------------------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SEP-41 token + SEP-57 RWA extensions (freeze, forced transfer, recovery) | OpenZeppelin stellar-tokens; the RWA Wizard scaffolds the regulated token | Consumed and extended; the kit's compliance module implements the SEP-57 interfaces the token expects | +| Synchronous tokenized vault | SEP-56 / OZ Token Vault | Not a base for Strata: SEP-56 assumes the price exists at call time, so its interface cannot express a request lifecycle. Only OZ conversion and rounding math reused, as library code | +| Access control, pausable, upgradeable, timelock | OZ crates | Consumed; pinned by exact version, audit coverage and gaps documented per component | +| Multisig and signing coordination | Native Stellar + existing coordinators, OZ Role Manager | Integrated | +| Request lifecycle priced against attestations | Not provided (ERC-7540 on EVM, where OpenZeppelin ships an implementation) | Core of the kit | +| Guarded valuation oracle with freshness and pause | Not provided | Core of the kit | +| Split reserve accounting with explicit shortfall exposure | Not provided | Core of the kit | +| FIFO redemption coverage, exit-only path | Not provided | Core of the kit | +| Reusable RWA configuration, verified genesis, white-label frontends | Not provided | Core of the kit | ## 12. Delivery phases -| Phase | Deliverables | Evidence of completion | -| --- | --- | --- | -| **1: Attested valuation and request pricing** | Valuation oracle with guardrails, freshness and pause; request lifecycle with escrow, cancellation and pricing-time mint and burn; public kit spec | Accounting property tests green in CI (price preserved by deposits, redemptions and custodian transfers; cancellation; rounding); multisig signing of privileged operations verified end to end through the coordinator | -| **2: Split accounting and redemption** | Shortfall exposure; FIFO redemption coverage; exit-only cash path; SEP-57 integration (compliance module, delisted-investor path); threat model and monitoring plan | Settlement cost measured at 1, 10, 100 and 1,000 pending requests; SEP-57 path demonstrated end to end on testnet | -| **3: Reference interfaces, audit remediation, mainnet** | Investor dApp and Admin panel (five surfaces, one per authority), backend-free; reproducible deployment; audit remediation (all critical and high findings fixed and verified, public changelog); mainnet reference deployment | Audit inheritance matrix published (component, version, audit report, Strata delta, resulting scope); external developer deploys a configured instance from docs alone; reference instance live on mainnet | +| Phase | Deliverables | Evidence of completion | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **1: Attested valuation and request pricing** | Valuation oracle with guardrails, freshness and pause; request lifecycle with escrow, cancellation and pricing-time mint and burn; public kit spec | Accounting property tests green in CI (price preserved by deposits, redemptions and custodian transfers; cancellation; rounding); multisig signing of privileged operations verified end to end through the coordinator | +| **2: Split accounting and redemption** | Shortfall exposure; FIFO redemption coverage; exit-only cash path; SEP-57 integration (compliance module, delisted-investor path); threat model and monitoring plan | Settlement cost measured at 1, 10, 100 and 1,000 pending requests; SEP-57 path demonstrated end to end on testnet | +| **3: Reference interfaces, audit remediation, mainnet** | Investor dApp and Admin panel (five surfaces, one per authority), backend-free; reproducible deployment; audit remediation (all critical and high findings fixed and verified, public changelog); mainnet reference deployment | Audit inheritance matrix published (component, version, audit report, Strata delta, resulting scope); external developer deploys a configured instance from docs alone; reference instance live on mainnet | -The funded core is the valuation, pricing and accounting layer. The Investor dApp and the Admin panel are how that core is used by investors and operators; without the Admin panel the five authorities are not operable by a non-developer, and the kit stops being a kit. +The funded core is the valuation, pricing and accounting layer. The Investor +dApp and the Admin panel are how that core is used by investors and operators; +without the Admin panel the five authorities are not operable by a +non-developer, and the kit stops being a kit. From 04bd9062e80f2b10547f4ca498d854b49bcd1d1c Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 10:41:09 -0300 Subject: [PATCH 05/10] docs: stop pinning exact versions in AGENTS.md WIP project: exact versions and crate lists rot faster than the file is read. Points at the manifests and the workspace instead, and resolves the review note about the intro promising no versions while the gotchas carried one. --- AGENTS.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 20ff477..95753a3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,9 +33,10 @@ attestation, treasury, guardian. Testnet only. Not audited. ## State of the repo -Three contract crates (`compliance`, `identity-verifier`, `share-token`) and two -shared crates (`bindings`, `pricing`). A React + Vite app shell with TypeScript -clients generated per contract, and a Playwright e2e harness. +Work in progress. Contract crates under `contracts/`, shared crates under +`crates/`, a React and Vite app shell with TypeScript clients generated per +contract, and a Playwright e2e harness. Read the workspace `Cargo.toml` for the +current members rather than trusting a list here. The vault contract itself is not written yet, so the request lifecycle above has no on-chain counterpart in this repo today. `app/` and `app-lib/` have no @@ -52,7 +53,7 @@ does not carry over. Read it for Soroban and OZ mechanics only. - **Contracts:** `stellar contract build` — **not** `cargo build`. The OZ crates enable an experimental `soroban-sdk` feature (`spec_shaking_v2`) that only - works through the CLI wrapper. The devshell pins Stellar CLI v27.0.0. + works through the CLI wrapper. The devshell pins the Stellar CLI it expects. - **Tests:** `cargo test` from the repo root runs every workspace member against the in-memory `Env`. There is no unit-test runner for `app/` or `app-lib/`; `e2e/` runs Playwright separately. CI does not run the Rust tests yet. @@ -68,13 +69,14 @@ apply as the corresponding code lands here. - A SEP-56 vault is **not** the base here: its interface assumes the price exists at call time, which a request lifecycle cannot express. Only OZ's conversion and rounding math is reused, as library code. -- `ed25519-dalek` v3 breaks the test build; pin to `2.2.0` if it resolves - higher. -- USDC is a **classic asset** → an account needs a trustline to hold it. - `bvUSDC` is a **Soroban contract token** → no trustline. Deposit is a single - transaction with nested authorization; there is no separate `approve`. Get - test USDC from Circle's faucet (pick Stellar) after establishing the - trustline. +- `ed25519-dalek` is transitive and unpinned by any manifest. A newer major + breaks the test build; hold it back in the lockfile if compilation fails + there. +- The deposit asset is a **classic asset** → an account needs a trustline to + hold it. The share token is a **Soroban contract token** → no trustline. + Deposit is a single transaction with nested authorization; there is no + separate `approve`. When the deposit asset is USDC, Circle's faucet (pick + Stellar) issues test units once the trustline exists. - Two network configs must agree: `environments.toml` is the network the CLI/scaffold **deploys** to, `app/.env` (`PUBLIC_STELLAR_*`) is the network the **frontend** reads at runtime. The scaffold default is local, so both need @@ -88,7 +90,7 @@ apply as the corresponding code lands here. Not recorded here on purpose. The reference base kept them in this file and they drifted: its `AGENTS.md` and its generated client pointed at two different vault -contracts. The addresses emitted by the deploy script (#15) are authoritative. +contracts. The addresses emitted by the deploy script are authoritative. ## Conventions From 7b715c73d5e8ce27b605583cec70a937c5cf8007 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 10:48:12 -0300 Subject: [PATCH 06/10] docs: soften the doc-wins rule and fix two overreaches The doc-wins-always rule was contradicted the same day it was written: docs/architecture.md moved to match the nav-oracle contract, not the reverse. It now wins on the what and requires an explicit resolution on the how, so a better mechanism in a contracts PR is not wrong by definition. Also retitles the second invariant to what it actually guarantees, and uses investor rather than holder to match the architecture. --- AGENTS.md | 6 +++--- CLAUDE.md | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 95753a3..985333a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,10 +21,10 @@ What the contracts enforce: 1. **Entry is gated** by a post-KYC allowlist, checked on the receiver of a subscription and on every share transfer. -2. **A priced claim always pays.** Once priced and covered, a cash claim cannot - be blocked by a pause, a stale valuation, or the holder losing their +2. **A covered claim always pays.** Once priced and covered, a cash claim cannot + be blocked by a pause, a stale valuation, or the investor losing their allowlist place. Priced claims are never re-priced and never identity-gated; - a delisted, non-frozen holder leaves through the exit-only cash path. + a delisted, non-frozen investor leaves through the exit-only cash path. 3. **Cancellation is atomic and single-step**, open only until the attestation that prices the request is accepted. There is no instant exit. diff --git a/CLAUDE.md b/CLAUDE.md index 0211a9a..50c3d29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,8 +3,10 @@ network configuration, and the Stellar-specific gotchas. It is the single source of truth for this project's conventions. The design lives in [`docs/architecture.md`](./docs/architecture.md) — the -source of truth for what the protocol does and why. Where the code and that -document disagree, the document wins and the code is behind. +source of truth for what the protocol does and why. It wins on the **what**: +properties, invariants and guarantees. On the **how**, a disagreement with the +code is resolved explicitly rather than by default — a mechanism that proves +better in the code is a reason to amend the document, not a defect. Dependency versions are pinned in `Cargo.toml`, `rust-toolchain.toml` and `flake.nix` — read them there rather than from prose. From 946322633ddd594247aa9fb3a51ec7881e5b8c41 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Mon, 7 Sep 2026 15:32:38 -0300 Subject: [PATCH 07/10] docs: drop flake.nix from the version-pin pointers Reviewer note: naming a Nix file as where to read versions reads as requiring Nix. Cargo.toml and rust-toolchain.toml cover it without that. --- AGENTS.md | 4 ++-- CLAUDE.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 985333a..5fc3dfb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,8 +5,8 @@ of truth for how to build, run and not break things. `CLAUDE.md` points here. It deliberately does **not** restate the design or the dependency versions. The design is in [`docs/architecture.md`](./docs/architecture.md); versions are -pinned in `Cargo.toml`, `rust-toolchain.toml` and `flake.nix`. A second copy of -either would go stale. Work items live in +pinned in `Cargo.toml` and `rust-toolchain.toml`. A second copy of either would +go stale. Work items live in [milestone M1](https://github.com/BootNodeDev/strata-vault-kit/milestone/1). ## What this is diff --git a/CLAUDE.md b/CLAUDE.md index 50c3d29..38e9aa2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,5 +8,5 @@ properties, invariants and guarantees. On the **how**, a disagreement with the code is resolved explicitly rather than by default — a mechanism that proves better in the code is a reason to amend the document, not a defect. -Dependency versions are pinned in `Cargo.toml`, `rust-toolchain.toml` and -`flake.nix` — read them there rather than from prose. +Dependency versions are pinned in `Cargo.toml` and `rust-toolchain.toml` — read +them there rather than from prose. From 9653d8a8fd5a19be14cbb1e0011158274d6c3519 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Tue, 8 Sep 2026 17:27:25 -0300 Subject: [PATCH 08/10] docs: address review notes on 80 --- AGENTS.md | 14 +------------- docs/architecture.md | 10 +++++----- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5fc3dfb..42a0e4e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,17 +31,6 @@ What the contracts enforce: Five authorities, each a native Stellar multisig: governance, compliance, attestation, treasury, guardian. Testnet only. Not audited. -## State of the repo - -Work in progress. Contract crates under `contracts/`, shared crates under -`crates/`, a React and Vite app shell with TypeScript clients generated per -contract, and a Playwright e2e harness. Read the workspace `Cargo.toml` for the -current members rather than trusting a list here. - -The vault contract itself is not written yet, so the request lifecycle above has -no on-chain counterpart in this repo today. `app/` and `app-lib/` have no -unit-test runner. - ## Reference base [`stellar-vault-demo-dapp`](https://github.com/BootNodeDev/stellar-vault-demo-dapp) @@ -67,8 +56,7 @@ apply as the corresponding code lands here. - Build with `stellar contract build`, not `cargo build` (see above). - A SEP-56 vault is **not** the base here: its interface assumes the price - exists at call time, which a request lifecycle cannot express. Only OZ's - conversion and rounding math is reused, as library code. + exists at call time, which a request lifecycle cannot express. - `ed25519-dalek` is transitive and unpinned by any manifest. A newer major breaks the test build; hold it back in the lockfile if compilation fails there. diff --git a/docs/architecture.md b/docs/architecture.md index 6a9e593..aa7a351 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -12,8 +12,7 @@ USDC). **Maintainer:** BootNode (bootnode.dev). Shares in this vault are a claim on an off-chain asset whose value is attested on chain. No price exists at the moment you act, so entry and exit are requests: what you put in goes into escrow, the next accepted attestation prices it, and -you claim the result. A priced claim is never re-priced, and a covered cash -claim cannot be blocked by a pause or by losing your allowlist place. +you claim the result. --- @@ -45,9 +44,10 @@ claim cannot be blocked by a pause or by losing your allowlist place. 4. **Attested NAV.** The reporter attests the share price itself, computed off-chain from the deployed value and the vault's public figures under a documented methodology. The contract validates, stores and exposes it. Every - on-chain operation preserves that price by construction: a priced deposit - adds assets and shares in proportion, a priced redemption removes both, a - custodian transfer moves value between pockets without changing the total. + on-chain operation preserves that price by construction: pricing a deposit + releases escrow into the reserve and mints shares in proportion, pricing a + redemption burns shares and fixes the matching liability, and a custodian + transfer moves value between pockets without changing the total. 5. **Payable claims always pay.** Neither the guardian pause, a delisting, nor a stale valuation can block the payment of an already-priced, funded cash From 97607901349da5e3926e60a67c00884ae70cc73b Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Wed, 9 Sep 2026 09:28:47 -0300 Subject: [PATCH 09/10] docs: describe the epoch the contract implements --- docs/architecture.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index aa7a351..86b3b26 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -35,11 +35,11 @@ you claim the result. 3. **Request-based entry and exit.** The exact price of a share does not exist when an investor acts; it is attested afterwards. Entry and exit are - requests: funds or shares go into escrow, the next accepted attestation - prices them, and the investor claims the result. This is the ERC-7540 pattern - with two differences: cancellation is a single step that closes when the - pricing attestation arrives, and the price is set by each attestation for - every pending request, not by a manager. + requests: funds or shares go into escrow, the epoch holding them is priced + against an attestation, and the investor claims the result. This is the + ERC-7540 pattern with two differences: cancellation is a single step that + closes when the epoch is sealed, and the price comes from the attestation + valid at pricing, not from a manager. 4. **Attested NAV.** The reporter attests the share price itself, computed off-chain from the deployed value and the vault's public figures under a @@ -182,18 +182,24 @@ not assumed. ## 4. Request lifecycle Every position change is a request with three states: **pending, priced, -claimed**. Each accepted attestation prices every pending request; requests are -then settled individually, by any account, so no attestation processes an -unbounded batch and no investor can choose their price. +claimed**. Requests join the open epoch. Sealing an epoch closes it to new +requests and opens the next; pricing it reads the oracle and fixes one share +price for every request it holds. Pricing is permissionless and refuses a feed +that is not valid, so a sealed epoch waits rather than settling at a stale +price, and no investor can choose their price. + +Sealing is gated on the manager role today. It is meant to become permissionless +once a minimum epoch duration bounds it; until then, whoever seals chooses the +batch boundary, though not the price it receives. ### 4.1 Subscription - Request: verifies the receiver is allowlisted, moves the deposit asset into escrow. At most one active request per controller. - Pricing: the escrow leaves the cancellable bucket, the share quantity is set - at the attested price, and the shares are minted and held for the investor. -- Cancellation: atomic, available until the request's pricing attestation is - accepted; returns the escrowed asset in full. + at the epoch's price, and the shares are minted and held for the investor. +- Cancellation: atomic, available until the epoch is sealed; returns the + escrowed asset in full. - Share claim: re-verifies the receiver and delivers the shares. If verification fails, the position remains shares and exits through the redemption lifecycle at the then-current price. No nominal refund exists after pricing. @@ -202,7 +208,7 @@ unbounded batch and no investor can choose their price. - Request: moves shares into escrow, no admission limit. - Pricing: the escrowed shares are burned and a fixed cash liability enters - committed at the attested price. Priced claims are never re-priced. + committed at the epoch's price. Priced claims are never re-priced. - Coverage: a priced claim is payable when the liquid reserve covers it, in FIFO order. An earlier unpaid claim never blocks a later one that is already covered. The gap between committed and liquid reserve is the on-chain @@ -386,7 +392,7 @@ evaluated (Templar, Untangled OctoVault, DeFindex). | Phase | Deliverables | Evidence of completion | | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **1: Attested valuation and request pricing** | Valuation oracle with guardrails, freshness and pause; request lifecycle with escrow, cancellation and pricing-time mint and burn; public kit spec | Accounting property tests green in CI (price preserved by deposits, redemptions and custodian transfers; cancellation; rounding); multisig signing of privileged operations verified end to end through the coordinator | -| **2: Split accounting and redemption** | Shortfall exposure; FIFO redemption coverage; exit-only cash path; SEP-57 integration (compliance module, delisted-investor path); threat model and monitoring plan | Settlement cost measured at 1, 10, 100 and 1,000 pending requests; SEP-57 path demonstrated end to end on testnet | +| **2: Split accounting and redemption** | Shortfall exposure; FIFO redemption coverage; exit-only cash path; SEP-57 integration (compliance module, delisted-investor path); threat model and monitoring plan | Settlement e2e test at 1, 10, 100 and 1,000 pending requests; SEP-57 path demonstrated end to end on testnet | | **3: Reference interfaces, audit remediation, mainnet** | Investor dApp and Admin panel (five surfaces, one per authority), backend-free; reproducible deployment; audit remediation (all critical and high findings fixed and verified, public changelog); mainnet reference deployment | Audit inheritance matrix published (component, version, audit report, Strata delta, resulting scope); external developer deploys a configured instance from docs alone; reference instance live on mainnet | The funded core is the valuation, pricing and accounting layer. The Investor From cb12410afa82a9ede829a159688e323f09774346 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Wed, 9 Sep 2026 10:43:01 -0300 Subject: [PATCH 10/10] docs: drop the unverifiable nav formula --- docs/architecture.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 86b3b26..587054a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -253,17 +253,9 @@ sequenceDiagram ## 5. Valuation and accounting The NAV is a permissioned attestation of the share price, published with a proof -reference. The reporter computes it off-chain: - -```text -share_price = (deployed_value + onchain_reserve - committed) / share_supply -``` - -where deployed_value is the attested off-chain value and the other three figures -are read from the vault's public surface. Cancellable escrow is not part of it: -pending subscriptions hold no shares yet. The contract does not recompute the -price; it validates the report, stores it, and exposes it together with the -liquidity figures it does own: +reference. The reporter computes it off-chain under a published methodology. The +contract checks bounds, cooldown and the deviation cap, then stores the price +and exposes it with the liquidity figures it owns: ```text liquid_reserve = reserve - cancellable_deposit_escrow