From 9a6033d3942f72b98bad40744f2dba4864c92f6c Mon Sep 17 00:00:00 2001 From: Ezedike-egwom Collins Date: Tue, 4 Aug 2026 05:04:56 +0100 Subject: [PATCH] docs: fix stale references across README and architecture docs --- README.md | 14 +++--- apps/docs/architecture/api.md | 14 +++--- apps/docs/architecture/monorepo.md | 2 +- apps/docs/architecture/signing-flow.md | 2 +- docs/contracts.md | 68 +++++++++----------------- docs/signing-flow.md | 6 +-- 6 files changed, 41 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 29ed4360..e4a5937b 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,12 @@ Meridian is a **testnet technical preview**, not a finished product. Be clear-ey - **Deposit / withdraw through the live `MeridianVault` coordinator contract**: the vault forwards your USDC to its active adapter contract (`BlendAdapter` today), which supplies it straight into a real Blend pool — you receive mUSDC shares representing the position, no Meridian-controlled custody of the underlying funds - Live TVL and per-address position reads directly from the vault (`get_total_assets`, `get_position`) - Best-rate routing: the API recommends the highest-APY vault it can actually deposit into, skipping display-only protocols and pools flagged risky -- Protocol-agnostic adapter architecture: `MeridianVault` (ERC-4626-style share accounting hardened against the first-depositor inflation attack, pause + admin-rotation rails), `BlendAdapter` (live), and a `DefindexAdapter` contract (built, not yet wired to a live vault) — swapping which protocol a vault routes to is an admin-only `set_adapter` call, no vault redeploy required. A `router` contract exists reserved for a future v2 single-transaction rebalancing feature. All four have unit test coverage. +- Protocol-agnostic adapter architecture: `MeridianVault` (ERC-4626-style share accounting hardened against the first-depositor inflation attack, pause + admin-rotation rails), `BlendAdapter` (live), and a `DefindexAdapter` contract (built, not yet wired to a live vault) — swapping which protocol a vault routes to is an admin-only `set_adapter` call, no vault redeploy required. A `router` contract implements a working, tested `rebalance` entry point that atomically moves a user's position between two vault instances in one transaction — it's not yet exposed through the API or UI. All four contracts have unit test coverage. +- Per-position yield earned: cost-basis tracking via `get_principal`, surfaced in the dashboard alongside the current position value **In progress — the core promise is not finished** - Deposit/withdraw against a real DeFindex vault through `DefindexAdapter` — the adapter contract and transaction builders are implemented; gated behind `DEFINDEX_VAULT_ID` until a real testnet vault is wired -- Per-position yield earned (cost-basis tracking is implemented on-chain via `get_principal`; UI display is still in progress) - Mainnet configuration and a security audit before any real-funds use Until a DeFindex vault is configured, the DeFindex deposit path throws a configuration error rather than silently routing elsewhere. Track progress in the [Roadmap](#roadmap) and [open issues](../../issues). @@ -80,7 +80,7 @@ The API never holds private keys. It builds an unsigned Soroban transaction, ret | Frontend | Vite 8, React 19, Tailwind CSS, Zustand, TanStack Query | | Backend (prod) | Vercel Serverless Functions, Zod validation | | Backend (local) | Fastify | -| Blockchain | Stellar Soroban, `@stellar/stellar-sdk` v12 | +| Blockchain | Stellar Soroban, `@stellar/stellar-sdk` v14 | | Protocols | Blend Capital, DeFindex | | Contracts | Rust / Soroban SDK | | Monorepo | pnpm workspaces, Turborepo | @@ -168,13 +168,13 @@ Issues are tagged `good first issue`, `medium`, and `hard`. Pick your level. Non-custodial USDC deposits into the `MeridianVault` coordinator contract on Stellar testnet, live and working end-to-end for Blend via `BlendAdapter`. Freighter wallet connects in one click, the best-rate vault is selected automatically, and the signed transaction never leaves the browser. Live APY and TVL across protocols with risk-tier labelling. Withdraw at any time, no lock-up. DeFindex support is built (`DefindexAdapter`) but not yet wired to a live testnet vault. -### Q3 2026: Yield tracking and position history +### Q3 2026: Yield history and position analytics -Per-position yield tracking with a cost-basis model so users see their actual earnings, not just current balance. A yield history chart broken down by protocol, entry time, and cumulative earned. Position-level analytics that work whether funds are in Blend, DeFindex, or split across both. +Per-position yield tracking with a cost-basis model is shipped: users already see cumulative earned alongside their current balance. Remaining: a yield history chart broken down by protocol, entry time, and cumulative earned over time. Position-level analytics that work whether funds are in Blend, DeFindex, or split across both. -### Q4 2026: Atomic rebalancing +### Q4 2026: Atomic rebalancing, exposed -A Soroban router contract that rebalances between vaults in one atomic transaction. No manual withdraw-then-deposit cycle: when a better rate appears, funds move in a single ledger close. Auto-rebalancing triggers with user-defined APY thresholds. The groundwork for supporting new protocols without UI changes. +The `router` contract's `rebalance` entry point is built and tested: it already moves a user's position between two vaults in one atomic transaction, no manual withdraw-then-deposit cycle. What's missing is exposing it: an API endpoint and UI trigger so a user can actually invoke it, plus auto-rebalancing triggers with user-defined APY thresholds. See [#469](../../issues/469) for the fully-automated (delegated) version of this. ### Q1 2027: Mainnet and scale diff --git a/apps/docs/architecture/api.md b/apps/docs/architecture/api.md index ec2cc6c6..4fbb17ef 100644 --- a/apps/docs/architecture/api.md +++ b/apps/docs/architecture/api.md @@ -147,13 +147,13 @@ The Vercel functions (`api/v1/`) import workspace packages that are pre-built in The Fastify server (`apps/api-local/`) runs the same packages directly via `tsx`, which handles TypeScript natively in the development process. -## Vault ID to protocol mapping +## Vault ID to contract address mapping -When building a deposit transaction, the `vaultId` is mapped to the `Protocol` enum expected by the vault contract: +The vault contract's `deposit`/`withdraw` take no protocol-selection parameter — which protocol a deposit reaches is fixed by whichever adapter the target vault instance has set, not by anything passed in the call. Building a deposit transaction therefore resolves `vaultId` directly to the specific deployed vault contract address to call, via the mapping in `packages/stellar-sdk-helpers/src/known-pools.ts`: -| Vault ID prefix | Protocol | -| --------------- | ---------- | -| `blend-` | `Blend` | -| `defindex-` | `DeFindex` | +| Vault ID prefix | Resolves to | +| --------------- | ---------------------------------------------------------- | +| `blend-` | A vault instance with `BlendAdapter` set as its adapter | +| `defindex-` | A vault instance with `DefindexAdapter` set as its adapter | -Any other prefix returns a 500 with a clear mapping error. +Any unrecognized `vaultId` returns a 500 with a clear mapping error. diff --git a/apps/docs/architecture/monorepo.md b/apps/docs/architecture/monorepo.md index 140620c1..a104a7c1 100644 --- a/apps/docs/architecture/monorepo.md +++ b/apps/docs/architecture/monorepo.md @@ -68,7 +68,7 @@ meridian/ │ │ │ └── contracts/ # Rust/Soroban smart contracts │ ├── vault/src/lib.rs # MeridianVault: protocol-agnostic coordinator -│ ├── router/src/lib.rs # Reserved for v2 atomic rebalancing +│ ├── router/src/lib.rs # MeridianRouter: atomic rebalance() between vaults, not yet exposed via API/UI │ ├── blend-adapter/src/lib.rs # Supplies USDC into a Blend lending pool │ └── defindex-adapter/src/lib.rs # Deposits USDC into a DeFindex vault │ diff --git a/apps/docs/architecture/signing-flow.md b/apps/docs/architecture/signing-flow.md index 584b351d..be4ee200 100644 --- a/apps/docs/architecture/signing-flow.md +++ b/apps/docs/architecture/signing-flow.md @@ -46,7 +46,7 @@ Browser API (Vercel) Stellar RPC **The API receives only a public key, never a private key.** It uses the public key to fetch the account's sequence number from the Stellar RPC, which is needed to construct a valid transaction. The private key never leaves the user's device. -**The user sees the transaction before signing.** Freighter displays the contract address, function name, and all arguments including the routing protocol. The user can reject if anything looks wrong. +**The user sees the transaction before signing.** Freighter displays the contract address, function name, and all arguments. The user can reject if anything looks wrong. **The API cannot forge a transaction on the user's behalf.** `caller.require_auth()` in the Soroban contract ensures the transaction is only valid if it carries a valid signature from `caller`. The API cannot produce that signature. diff --git a/docs/contracts.md b/docs/contracts.md index b99b6cc8..a6f214bf 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -12,44 +12,28 @@ Stellar CLI (`stellar contract build`). The deploy script at Source: [`packages/contracts/vault/src/lib.rs`](../packages/contracts/vault/src/lib.rs) -The vault is a share-based USDC custodian modelled on ERC-4626. Users deposit -USDC and receive mUSDC share tokens whose redemption value grows with yield -accrued in the underlying protocol (Blend or DeFindex). +The vault is a protocol-agnostic coordinator, not a direct protocol integration: +it holds no opinion about where funds actually earn yield and delegates all +protocol-specific work to a swappable **adapter** contract (`BlendAdapter` or +`DefindexAdapter`), selected via `set_adapter`. Users deposit USDC and receive +mUSDC share tokens whose redemption value grows with yield accrued in the +active adapter's underlying protocol. A virtual share/asset offset of 1 000 stroops is applied to all price calculations to neutralise the first-depositor inflation attack: an attacker -who donates USDC directly to the vault recovers only a negligible fraction of +who donates USDC directly to the adapter recovers only a negligible fraction of the donation, making the skim unprofitable. -### Entry points +There is no `route_to` or protocol-selection parameter on `deposit` — which +protocol funds reach is determined entirely by whichever adapter the vault +currently has set, not by anything the caller passes in. Which vault _instance_ +(and therefore which adapter) a deposit targets is chosen off-chain, by +resolving the `vaultId` to a specific deployed vault contract address. -| Function | Description | -| ----------------------------------- | ---------------------------------------------------------------------------------------- | -| `initialize(admin, usdc, musdc)` | One-time setup. Sets admin, USDC token, and mUSDC share token addresses. | -| `deposit(caller, amount, route_to)` | Pull `amount` USDC from `caller`, mint proportional mUSDC shares. Returns shares minted. | -| `withdraw(caller, shares)` | Burn `shares` mUSDC, send proportional USDC back to `caller`. Returns USDC sent. | -| `get_position(address)` | Current mUSDC share balance for `address`. | -| `get_principal(address)` | Net USDC deposited and not yet withdrawn (cost basis). | -| `get_entry_time(address)` | Ledger timestamp of the address's first deposit in the current position. | -| `get_active_protocol()` | The protocol hint set on the last deposit. | -| `get_total_assets()` | Total USDC held by the vault. | -| `get_total_shares()` | Total mUSDC shares outstanding. | -| `set_paused(paused)` | Admin-only. Block new deposits while leaving withdrawals open. | -| `set_admin(new_admin)` | Admin-only. Rotate the admin key without redeploying. | - -### `Protocol` type - -```rust -pub enum Protocol { - Blend, - DeFindex, -} -``` - -Passed to `deposit` to record which underlying protocol the funds should go to. -The vault stores the value but does not route funds itself - routing is the -responsibility of the off-chain API, which selects the best protocol and -includes the choice in the unsigned transaction it builds for the user to sign. +For the full entry-point reference, error codes, and storage layout, see +[`apps/docs/architecture/vault-contract.md`](../apps/docs/architecture/vault-contract.md) — +that page is the canonical, detailed reference; this page covers the router +below. ## Router (`meridian-router`) @@ -69,11 +53,11 @@ sequenceDiagram participant VaultA as from_vault participant VaultB as to_vault - User->>Router: rebalance(depositor, from_vault, to_vault, shares, min_out, route_to) + User->>Router: rebalance(depositor, from_vault, to_vault, shares, min_out) Router->>VaultA: withdraw(depositor, shares) VaultA-->>Router: usdc_received - Note over Router: panic if usdc_received < min_out (slippage guard) - Router->>VaultB: deposit(depositor, usdc_received, route_to) + Note over Router: returns Err(RouterError::SlippageExceeded) if usdc_received < min_out + Router->>VaultB: deposit(depositor, usdc_received) VaultB-->>Router: new_shares Router-->>User: new_shares ``` @@ -92,8 +76,7 @@ pub fn rebalance( to_vault: Address, shares: i128, min_out: i128, - route_to: Protocol, -) -> i128 +) -> Result ``` | Parameter | Description | @@ -103,9 +86,9 @@ pub fn rebalance( | `to_vault` | Vault contract to deposit into. | | `shares` | mUSDC share count to burn on `from_vault`. | | `min_out` | Minimum USDC stroops the withdrawal must return. Reverts on slippage. | -| `route_to` | Protocol hint forwarded to `to_vault.deposit`. | -Returns the number of shares minted by `to_vault`. +Returns the number of shares minted by `to_vault`, or `RouterError::SlippageExceeded` +if the withdrawal from `from_vault` returned fewer stroops than `min_out`. ### Auth model @@ -124,13 +107,6 @@ signing, so no special handling is needed in the frontend beyond the standard a sensible value by multiplying the current share redemption rate by `(1 - slippage_tolerance)` and converting to stroops. -## Shared `Protocol` type - -The router re-declares `Protocol` with the same `#[contracttype]` annotation and -variant names as the vault. Soroban serialises contracttype enums to XDR using -the variant name, so identical declarations produce identical wire bytes and -cross-contract calls work without sharing a crate. - ## Building and deploying ```bash diff --git a/docs/signing-flow.md b/docs/signing-flow.md index 6faf56c8..57794430 100644 --- a/docs/signing-flow.md +++ b/docs/signing-flow.md @@ -6,9 +6,9 @@ browser hands that XDR to the user's wallet (Freighter) for signing. The signed relayed back through the API only to be forwarded to the Stellar network — the API still never signs anything itself. -This document is the reference for anyone implementing the XDR builder (#14) or the Freighter -adapter (#8). Implement against this doc; you should not need to read the Soroban auth docs to -get the flow right. +This document is the reference for how the XDR builder and the Freighter adapter fit together. +Implement against this doc; you should not need to read the Soroban auth docs to get the flow +right. ## Sequence