Skip to content

feat(console): treasury screen onto org-scoped /orgs/:id/treasury #64

Description

@hitakshiA

Epic: #58 · Step D (Treasury)

Rewire the console's Treasury screen off its demo-era (Stellar-legacy) endpoints onto the real org-scoped backend. Builds on the cookie-SIWE session (#60) and onboarding (#63, which already provisions the managed treasury).

Backend contract (source of truth — services/api/src/routes/orgs.ts)

GET  /orgs/:id/treasury                → 200 { address, custody:"managed", registered, consented,
                                              custodyConsent:{consented,consentedAt,consentedBy},
                                              balances:[{ token:"usdc"|"eurc", tokenId, symbol, decimals, amount }] }
POST /orgs/:id/treasury/deposit        → 201|202|200 { amount, approvalTxHash?, source:"direct",
       { amount, token:"usdc"|"eurc", idempotencyKey }   status:"confirmed"|"submitted", token, tokenId, txHash }
GET  /orgs/:id/treasury/deposits       → 200 { deposits:[{ id, kind:"direct"|"cctp", amount, token,
       (query: limit?, before?)              status:"pending"|"credited"|"failed", txHash, sourceChain, createdAt, updatedAt }],
                                              nextCursor }

amount in the deposit request is minor units (/^[1-9][0-9]*$/, USDC 6dp). idempotencyKey is required (generate one per attempt). All monetary fields are strings.

Changes

  • lib/api.ts — replace the treasury client fns with the org-scoped ones: orgTreasury(orgId)GET /orgs/:id/treasury; depositToTreasury(orgId, { amount, token, idempotencyKey })POST /orgs/:id/treasury/deposit; treasuryDeposits(orgId, { limit?, before? })GET /orgs/:id/treasury/deposits. REMOVE the no-backend fns/paths: treasury(GET /treasury), treasuryPublicBalance, treasuryReceive, treasurySendPublic, fundTreasury, proveBalance, proveTotal, proveSolvency (and their /treasury/* paths).
  • lib/store.tsx — the treasury read model is now org-scoped: only fetch it when session.activeOrg exists, calling api.orgTreasury(activeOrg.id). Keep the independent-read resilience (don't blank on a transient miss).
  • screens/Treasury.tsx — render the real treasury: the managed treasury address, registered status, and the per-token encrypted balances. Primary action = Add fundsdepositToTreasury (direct ERC20 → encrypted balance) with a generated idempotencyKey, reflecting status (submitted vs confirmed) + the tx. Show recent deposits from treasuryDeposits (kind/amount/status/tx). REMOVE from real mode the actions with no backend (public balance, receive/QR, send-to-wallet, prove-balance/total/solvency). Money shown in USDC (6dp) via the existing formatters.
  • packages/types — treasury view + deposit + deposits-history response types matching the backend (drop the Stellar-legacy stroops/issuer shapes for real mode).
  • Demo — update demo/api.ts/demo/data.ts treasury stubs to the new shapes so VITE_DEMO_MODE=1 still renders a populated Treasury screen.

Out of scope

Cross-chain CCTP funding (/orgs/:id/treasury/fund-intent) — leave a clear TODO seam but don't build the CCTP UX in this PR. Payroll (E), hiding no-backend screens (B).

Definition of done — run ALL, make them pass:

pnpm --filter @benzo/console typecheck · test · build · VITE_DEMO_MODE=1 … build · pnpm exec biome lint .

Operating rules

No git (can't commit). Match the codebase style. End with (1) a one-line conventional-commit message and (2) the exact git add <paths> list.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions