feat(tangle-cloud): shielded payments sidebar — pool, credits, spend auth#3147
Conversation
✅ Deploy Preview for tangle-cloud ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for tangle-leaderboard ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for tangle-dapp ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
7d29a42 to
757cd55
Compare
Integrate anonymous payment flows into tangle-cloud (app.tangle.tools) as a new "Payments" sidebar section with Shielded Pool and Credits sub-items. Architecture: - ShieldedProvider: keypair derivation (wallet sig → HKDF domain separation), per-address IndexedDB note storage, encrypted key-at-rest via AES-256-GCM with per-user random PBKDF2 salt - CreditsProvider: ephemeral credit key generation (crypto.getRandomValues salt), IndexedDB persistence, credit account lifecycle - Real EIP-712 spend authorization signing via viem (concat-based digest, account.sign for raw signing, input validation for uint64/uint8/expiry) Pages: - /payments/pool — Deposit/Withdraw from VAnchor shielded pool (gated until @tangle-network/shielded-sdk is linked for ZK proof generation) - /payments/credits — Fund credit accounts, sign spend authorizations, view on-chain credit balances Security: - Private keys encrypted at rest (PBKDF2 + AES-256-GCM) - Per-user random salt stored alongside ciphertext - Single wallet signature with domain-separated derivation - No private key material rendered in DOM - Session-locked keypairs requiring explicit unlock - Input validation on all spend authorization fields - No dangling token approvals without atomic deposits Consumes contracts from tnt-core: ShieldedGateway, ShieldedCredits, VAnchor (Vitalik's "ZK API Usage Credits" pattern).
…ixes Design system: - All payment containers use Typography, Card, Alert, Button, Input, Chip, SkeletonLoader from @tangle-network/ui-components - Replaced raw HTML (h1, p, button, input) with design system components - Added ARIA roles (role=tab, aria-selected, role=tabpanel) to payment tabs - RequireWallet component with ConnectWalletButton for consistent empty states - PaymentsLayout now includes Header (network selector, wallet connect, tx history) Architecture: - SharedPageLayout component replaces 7 duplicate layout files - Route-level code splitting via React.lazy + Suspense with skeleton fallback - TANGLE_CLOUD_NETWORKS extracted to shared constant (was defined twice) Cloud-wide fixes: - Removed 'use client' directives (Vite SPA, not Next.js) - Fixed react-router-dom → react-router imports (3 files) - Fixed hardcoded '/instances' → PagePath.INSTANCES - Consistent padding across all page layouts (md:px-8 lg:px-10)
P1 fixes:
- EIP-712 prefix uses explicit Uint8Array([0x19, 0x01]) instead of
toBytes('0x1901') for unambiguous encoding
- addNote/removeNote/importNotes use sequential write queue (promise
chain) to prevent concurrent persist() from losing notes
- btoa uses chunked Array.from() to avoid spread operator argument limit
P2 fixes:
- AmountInput uses string-based truncation instead of Number().toFixed()
to preserve precision for large balances
- NoteCard and CreditAccountCard use shortenHex from ui-components
instead of duplicated truncateHex
- Removed dead onWithdraw prop from CreditAccountCard (no consumer)
- Deleted unused sdkBridge.ts (single 5-line function, no importers)
46fc999 to
cc5eeb0
Compare
Credit account spending private keys are now encrypted in IndexedDB using AES-256-GCM, keyed by a domain-separated hash of the shielded keypair. Keys are decrypted in-memory when the user unlocks their shielded account.
Tests now use async waitFor() to handle React.lazy + Suspense. Added test cases for /payments/pool and /payments/credits routes with their layout and page mocks. All 51 tests pass.
❌ PR Review #6 w/ codex, claude
✅ Fixed since last review
🔴 HIGH (5)
Scenario: addNote(A) then addNote(B) called rapidly.
🟠 MEDIUM (17)
The standard fix is to add `db.onversionchange = () => { db.close(); dbPromis…
🟡 LOW (4)
🎯 What would get this approved
pr-reviewer v0.5.0 · review #6 · 2026-03-21T00:25:01.052228+00:00 |
Wallet-switch safety: - Credit storage scoped by owner address (owner field + filtered loads) - useKeypair guards async completions with addressRef to prevent stale wallet's keypair from being set after account switch - ShieldedProvider resets notes synchronously and flushes write queue on address change to prevent cross-wallet note leakage - Suspense moved inside each route's layout so Header stays visible during lazy page loads Security: - Credit keys not stored when encryption key is unavailable (empty string instead of plaintext fallback) - Decryption failure returns empty private key, not ciphertext blob - Nonce read failure aborts signing (fail-closed, no 0n fallback) Correctness: - SpendAuth uses TOKEN_DECIMALS constant instead of hardcoded 18 - /payments redirects to /payments/pool (was 404) - Test for /payments redirect added (52 tests pass)
Summary
Pages
/payments/pool— Deposit/Withdraw from VAnchor shielded pool/payments/credits— Fund credit accounts, sign spend authorizations, view on-chain balancesSecurity
SDK Integration (next PR)
When @tangle-network/shielded-sdk is linked with ethers v6:
Test plan
yarn nx typecheck tangle-cloudpassesyarn nx lint tangle-cloudpasses (0 errors, 0 warnings)yarn nx build tangle-cloudsucceeds/payments/poolrenders deposit/withdraw tabs/payments/creditsrenders accounts/fund/authorize tabs