diff --git a/PROJECT/README.MD b/PROJECT/README.MD index 3f54424..a1e5329 100644 --- a/PROJECT/README.MD +++ b/PROJECT/README.MD @@ -50,7 +50,11 @@ ACBU is a stablecoin backed by a basket of African currencies, designed to addre - **[Task Breakdown](PROJECT/TASK_BREAKDOWN.MD)** - Detailed task breakdown for all stages - **[Milestones](PROJECT/MILESTONES.MD)** - Key project milestones with acceptance criteria - **[Resource Plan](PROJECT/RESOURCE_PLAN.MD)** - Team composition, budget, and resource allocation -- **Known issues:** [Contracts](CONTRACTS_ISSUES.md), [Backend](BACKEND_ISSUES.md), [Frontend](FRONTEND_ISSUES.md) +- **Known issues:** [Contracts](issues/CONTRACTS_ISSUES.md), [Backend](issues/BACKEND_ISSUES.md), [Frontend](issues/FRONTEND_ISSUES.md) + - **Catalog consolidations** (durable long-form references β€” single source of truth per domain, discoverable when triaging): + - [`issues/CONTRACTS_ISSUES.md`](issues/CONTRACTS_ISSUES.md) β€” 60 canonical entries (C-001..C-060); 6 πŸ”΄ Critical / 21 🟠 High / 24 🟑 Medium / 9 🟒 Low. Consolidated in [PR #22](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/22). + - [`issues/BACKEND_ISSUES.md`](issues/BACKEND_ISSUES.md) β€” 75 canonical entries (B-001..B-075); 2 πŸ”΄ Critical / 21 🟠 High / 38 🟑 Medium / 14 🟒 Low. Consolidated in [PR #23](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/23). + - [`issues/FRONTEND_ISSUES.md`](issues/FRONTEND_ISSUES.md) β€” 68 canonical entries (F-001..F-076); 5 πŸ”΄ Critical / 15 🟠 High / 25 🟑 Medium / 23 🟒 Low. Consolidated into the canonical format in [PR #25](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/25); expanded to 68 canonical entries via follow-up canonical-ID migrations in [PR #28](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/28) (F-066 Skeleton primitive, Medium), [PR #29](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/29) (F-071 Toast-deletion delay, Medium), and [PR #30](https://github.com/Pi-Defi-world/ACBU-DOCUMENTATION/pull/30) (F-076 Frontend `request()` no default timeout, High). ## Quick Start @@ -93,5 +97,5 @@ Apache License 2.0 - See [LICENSE](LICENSE) file for details. --- -**Last Updated:** January 26, 2026 +**Last Updated:** June 20, 2026 **Status:** Planning & Development diff --git a/PROJECT/issues/BACKEND_ISSUES.md b/PROJECT/issues/BACKEND_ISSUES.md index 1435195..8f1a61b 100644 --- a/PROJECT/issues/BACKEND_ISSUES.md +++ b/PROJECT/issues/BACKEND_ISSUES.md @@ -1,76 +1,759 @@ -# Backend – Known Issues +# Backend β€” Known Issues -Issues found in the Node/Express API, services, and jobs. Add new items below as numbered list entries. +This document is the **single, durable long-form reference** for known issues in the ACBU backend (`acbu-backend`, Node.js/Express + Prisma/PostgreSQL + MongoDB + RabbitMQ + Stellar SDK), covering all routes, services, jobs, middleware, utils, config, security, observability, compliance, and ops. + +> **How to use** +> +> - Each issue is structured as: **severity Β· area Β· evidence Β· impact Β· fix direction Β· acceptance check**. +> - Items are ordered by severity (Critical β†’ Low), then by ID for stable cross-referencing. +> - Provenance: this file consolidates the legacy 56-item annotated list previously at `PROJECT/issues/BACKEND_ISSUES.md` with the canonical 75-item triage-ready list at `issues/backend.md`. The canonical list at `issues/backend.md` remains the **live working backlog**; this file is the durable version-of-record. +> - Legacy items whose content maps to a canonical `B-###` ID are tagged **(moved)**; legacy items that go beyond the canonical list (newer findings) are tagged **(new)** and retained verbatim because they pre-date the canonical rewrite. +> - When an item is fixed, the corresponding entry is removed from the working lists (`issues/backend.md`) and the resolution row flips to βœ… Fixed in Section 8 with a merged-PR link. +> - **Severity legend:** πŸ”΄ Critical Β· 🟠 High Β· 🟑 Medium Β· 🟒 Low + +--- + +## 1. Summary Table + +| ID | Severity | Area | Title (canonical) | +|----|----------|------|---------| +| B-001 | 🟠 High | backend/minting | Mint basket deposit limits use wrong USD proxy | +| B-002 | 🟠 High | backend/money | Floating `Number()` on money paths | +| B-003 | 🟠 High | backend/jobs | USDC convert job infinite requeue on failure | +| B-004 | 🟠 High | backend/webhooks | Webhook delivery retry semantics | +| B-005 | πŸ”΄ Critical | backend/webhooks | Webhook signature verification fail-open risk | +| B-006 | πŸ”΄ Critical | backend/auth | Recovery unlock as single-factor account recovery | +| B-007 | 🟠 High | backend/minting | Client-supplied mint recipient vs user wallet | +| B-008 | 🟠 High | backend/transfers | Transfer service Decimal typing | +| B-009 | 🟠 High | backend/fees | Burn fee policy threshold logic | +| B-010 | 🟠 High | backend/reserves | ReserveTracker total supply from DB not chain | +| B-011 | 🟠 High | backend/limits | Org-scoped transaction limits with null userId | +| B-012 | 🟑 Medium | backend/savings | Savings controller input validation | +| B-013 | 🟠 High | backend/auth | Placeholder / invalid Stellar address on signup | +| B-014 | 🟑 Medium | backend/wallet | Wallet activation funding asset choice | +| B-015 | 🟑 Medium | backend/stellar | Hardcoded Soroban inclusion fees | +| B-016 | 🟠 High | backend/stellar | Stellar event listener scope and error handling | +| B-017 | 🟠 High | backend/kyc | KYC machine layer is placeholder | +| B-018 | 🟑 Medium | backend/government | Government treasury aggregation stub | +| B-019 | 🟑 Medium | backend/salary | Salary APIs not implemented | +| B-020 | 🟑 Medium | backend/enterprise | Enterprise bulk transfer not implemented | +| B-021 | 🟑 Medium | backend/enterprise | Enterprise treasury view stub | +| B-022 | 🟑 Medium | backend/bills | Bills catalog and pay not implemented | +| B-023 | 🟑 Medium | backend/investment | Investment deployable allocation ignores deployed | +| B-024 | 🟑 Medium | backend/jobs | Investment withdrawal notifications for org requests | +| B-025 | 🟑 Medium | backend/investment | Yield accounting stub | +| B-026 | 🟠 High | backend/audit | Audit write failures only logged | +| B-027 | 🟑 Medium | backend/cache | Cache `deletePattern` ReDoS risk | +| B-028 | 🟠 High | backend/ratelimit | API key rate limiter non-atomic increment | +| B-029 | 🟑 Medium | backend/ratelimit | Rate limiter Mongo outage fail-open | +| B-030 | 🟒 Low | backend/ratelimit | Standard IP rate limit message mismatch | +| B-031 | 🟑 Medium | backend/ops | Deep health and metrics routes are public | +| B-032 | 🟒 Low | backend/ops | Shallow `/health` does not prove readiness | +| B-033 | 🟑 Medium | backend/docs | Swagger UI publicly mounted | +| B-034 | 🟑 Medium | backend/docs | OpenAPI drift vs implementation | +| B-035 | 🟒 Low | backend/devx | README still references npm only | +| B-036 | 🟑 Medium | backend/devx | `postinstall` runs prisma generate + tsc | +| B-037 | 🟑 Medium | backend/qa | Jest `passWithNoTests` weak gate | +| B-038 | 🟑 Medium | backend/api | No transfer pagination cursors | +| B-039 | 🟑 Medium | backend/api | Investment withdrawal list lacks pagination | +| B-040 | 🟒 Low | backend/api | Government list `limit` NaN hazard | +| B-041 | 🟒 Low | backend/types | Prisma `onRampSwap` typed via `as any` casts | +| B-042 | 🟑 Medium | backend/webhooks | Webhook raw body JSON fallback empty object | +| B-043 | 🟒 Low | backend/notifications | Notification service placeholder sender | +| B-044 | 🟑 Medium | backend/ops | Basket weight computation job missing | +| B-045 | 🟑 Medium | backend/integrations | Fintech multi-provider per country | +| B-046 | 🟒 Low | backend/config | Limits config hardcoded | +| B-047 | 🟑 Medium | backend/authz | Segment guard `requireMinTier` unused | +| B-048 | 🟒 Low | backend/types | Permissions typed as loose strings | +| B-049 | 🟠 High | backend/security | CORS policy review for credentialed browsers | +| B-050 | 🟑 Medium | backend/security | Helmet / security headers baseline | +| B-051 | 🟒 Low | backend/observability | Request ID propagation | +| B-052 | 🟑 Medium | backend/observability | Structured logging for money movements | +| B-053 | 🟠 High | backend/api | Idempotency-Key header support | +| B-054 | 🟒 Low | backend/observability | Distributed tracing (OpenTelemetry) | +| B-055 | 🟑 Medium | backend/ci | Database migration CI gate | +| B-056 | 🟑 Medium | backend/config | Prisma Accelerate vs direct URL confusion | +| B-057 | 🟒 Low | backend/data | Mongo index strategy for hot keys | +| B-058 | 🟑 Medium | backend/compliance | Encryption at rest for PII columns | +| B-059 | 🟑 Medium | backend/compliance | GDPR export & delete endpoints | +| B-060 | 🟠 High | backend/authz | Admin role separation | +| B-061 | 🟠 High | backend/config | Secrets in environment validation | +| B-062 | 🟑 Medium | backend/storage | S3 presigned URL abuse controls | +| B-063 | 🟑 Medium | backend/ai | OpenAI usage guardrails | +| B-064 | 🟑 Medium | backend/security | Dependency vulnerability scanning | +| B-065 | 🟒 Low | backend/auth | Time sync / clock skew handling for JWT | +| B-066 | 🟑 Medium | backend/auth | 2FA challenge token purpose binding | +| B-067 | 🟑 Medium | backend/auth | Account enumeration on sign-in | +| B-068 | 🟠 High | backend/auth | Brute-force protection on auth endpoints | +| B-069 | 🟑 Medium | backend/security | Content-Type and body size limits | +| B-070 | 🟒 Low | backend/api | Input sanitization for search endpoints | +| B-071 | 🟒 Low | backend/api | Versioning and deprecation headers | +| B-072 | 🟑 Medium | backend/api | Consistent error JSON schema | +| B-073 | 🟠 High | backend/core | Transaction status machine tests | +| B-074 | 🟑 Medium | backend/burn | Double-submit burn with same blockchain hash | +| B-075 | 🟠 High | backend/jobs | On-ramp swap race: duplicate processing | + +**Totals:** 2 Critical Β· 21 High Β· 38 Medium Β· 14 Low Β· **75 total catalog items** (all IDs `B-001` through `B-075`, fully aligned with `issues/backend.md`). + +--- + +## 2. Severity Counts + +| Severity | Count | +|----------|-------| +| πŸ”΄ Critical | **2** (B-005, B-006) | +| 🟠 High | **21** (B-001–B-004, B-007–B-011, B-013, B-016, B-017, B-026, B-028, B-049, B-053, B-060, B-061, B-068, B-073, B-075) | +| 🟑 Medium | **38** (B-012, B-014, B-015, B-018–B-025, B-027, B-029, B-031, B-033, B-034, B-036–B-039, B-042, B-044, B-045, B-047, B-050, B-052, B-055, B-056, B-058, B-059, B-062–B-064, B-066, B-067, B-069, B-072, B-074) | +| 🟒 Low | **14** (B-030, B-032, B-035, B-040, B-041, B-043, B-046, B-048, B-051, B-054, B-057, B-065, B-070, B-071) | +| **Total** | **75** | + +--- + +## 3. Critical Issues (πŸ”΄) β€” Ship Blockers + +### B-005 β€” Webhook signature verification fail-open risk +- **Area:** backend/webhooks +- **Evidence:** `acbu-backend/src/controllers/webhookController.ts` / `verifyFlutterwaveSignature` (verify pattern) +- **Impact:** If `FLUTTERWAVE_WEBHOOK_SECRET` (or equivalent secrets) are missing in production, verification is skipped and `next()` is called, allowing forged webhooks to mint credit downstream. +- **Fix direction:** Fail closed in production when secret env vars are missing; separate dev/stage mocks; refuse to register the webhook route in prod without a usable secret. +- **Acceptance check:** Production boot fails fast (or webhook handlers return 401) when signing secret is unset; staging retains the mocked-verify behavior under a feature flag. + +### B-006 β€” Recovery unlock as single-factor account recovery +- **Area:** backend/auth +- **Evidence:** `acbu-backend/src/services/recovery/recoveryService.ts` (per prior audits) +- **Impact:** Identifier + passcode alone can issue a new API key; a leaked/stolen passcode enables full account takeover. +- **Fix direction:** Add OTP (email/SMS) and/or device proof as a second factor; rate-limit recovery endpoints; rotate all existing sessions on success; emit audit event. +- **Acceptance check:** E2E test confirms a fresh API key cannot be obtained without the configured second factor; brute-force lockout kicks in after threshold. + +--- + +## 4. High Severity Issues (🟠) + +### B-001 β€” Mint basket deposit limits use wrong USD proxy +- **Area:** backend/minting +- **Evidence:** `acbu-backend/src/controllers/mintController.ts` (`depositFromBasketCurrency`) +- **Impact:** Local currency amounts are treated as USD when calling `checkDepositLimits`; limits can be wildly wrong for NGN/KES/etc. +- **Fix direction:** Convert basket currency β†’ USD using oracle/rates before limit checks; populate per-basket limit constants from config. +- **Acceptance check:** Integration tests for each basket currency enforce the correct USD-denominated limits. + +### B-002 β€” Floating `Number()` on money paths +- **Area:** backend/money +- **Evidence:** Multiple controllers (`mintController.ts`, `burnController.ts`) +- **Impact:** `Number()` over user-supplied monetary strings loses precision versus `Decimal` and can skew fee/limit math. +- **Fix direction:** Parse monetary strings with a decimal library end-to-end; coerce to `Decimal` at every model boundary; only lossy coerce at the Soroban boundary with explicit rounding policy. +- **Acceptance check:** Golden tests for large fractional inputs and boundary fees pass. + +### B-003 β€” USDC convert job infinite requeue on failure +- **Area:** backend/jobs +- **Evidence:** `acbu-backend/src/jobs/usdcConvertAndMintJob.ts` (`ch.nack(..., true)`) +- **Impact:** Poison messages can spin forever, duplicating side effects or stalling the queue; `OnRampSwap` rows never reach terminal state. +- **Fix direction:** Add retry caps + DLQ routing; transition `OnRampSwap.status` atomically to `failed` after N attempts with the failure reason. +- **Acceptance check:** Failed swap lands in DLQ; corresponding DB row reaches terminal state; no infinite nack loop under chaos replay. + +### B-004 β€” Webhook delivery retry semantics +- **Area:** backend/webhooks +- **Evidence:** `acbu-backend/src/services/webhook/webhookService.ts` +- **Impact:** Partners may be hammered; events retried without idempotency can cause duplicate settlements. +- **Fix direction:** Per-delivery idempotency keys; exponential backoff; max attempts; DLQ + alerting on sustained failure. +- **Acceptance check:** Webhook consumer stops after configured failure threshold; DLQ is observable in the ops dashboard. + +### B-007 β€” Client-supplied mint recipient vs user wallet +- **Area:** backend/minting +- **Evidence:** `acbu-backend/src/controllers/mintController.ts`, on-ramp controllers +- **Impact:** If mint / on-ramp paths still accept a body-supplied `wallet_address` without binding to `User.stellarAddress`, minted ACBU can be redirected to an unrelated address. +- **Fix direction:** Centralize `assertUserWalletAddress` on every mint / on-ramp path; deny mismatches with a structured 403. +- **Acceptance check:** Security test fails when minting to a `G…` not owned by the authenticated user. + +### B-008 β€” Transfer service Decimal typing +- **Area:** backend/transfers +- **Evidence:** `acbu-backend/src/services/transfer/transferService.ts` +- **Impact:** String amounts into Prisma Decimal fields risk precision and type drift at runtime. +- **Fix direction:** Construct `Decimal` explicitly; validate with Zod (`string` + decimal regex) before passing to Prisma. +- **Acceptance check:** Unit tests pass for maximum-precision transfers and boundary cases. + +### B-009 β€” Burn fee policy threshold logic +- **Area:** backend/fees +- **Evidence:** `acbu-backend/src/services/feePolicy/feePolicyService.ts` +- **Impact:** Suspected inverted/typo thresholds can charge wrong BPS versus the reserve basket state. +- **Fix direction:** Review the math against `OPERATIONS/LIMITS_AND_TIERS` and product spec; add property tests for the published tier table. +- **Acceptance check:** Fee outputs match the documented tier table for all bracket boundaries. + +### B-010 β€” ReserveTracker total supply from DB not chain +- **Area:** backend/reserves +- **Evidence:** `acbu-backend/src/services/reserve/ReserveTracker.ts` +- **Impact:** On-chain supply and indexed DB can diverge; circuit breakers may compute against the wrong numerator. +- **Fix direction:** Periodic chain-truth reconciliation; alert on drift beyond policy threshold. +- **Acceptance check:** Dashboard surfaces DB vs on-chain delta under threshold; sustained drift alerts. + +### B-011 β€” Org-scoped transaction limits with null userId +- **Area:** backend/limits +- **Evidence:** `acbu-backend/src/services/limits/limitsService.ts` +- **Impact:** Org keys creating `userId: null` rows may be excluded from aggregates; limits can be bypassed at the org level. +- **Fix direction:** Include `organizationId` dimension consistently in limit queries; align with the prisma `Transaction` schema. +- **Acceptance check:** Org-scope key cannot exceed org daily cap in tests. + +### B-013 β€” Placeholder / invalid Stellar address on signup +- **Area:** backend/auth +- **Evidence:** `acbu-backend/src/services/auth/authService.ts` (`getPlaceholderStellarAddress`) +- **Impact:** Non-`G` placeholders break downstream Stellar validation and break UX. +- **Fix direction:** Defer the DB constraint until the real wallet is created, **or** use a valid funded temporary account pattern under a feature flag. +- **Acceptance check:** No user row ships in production with an invalid `stellarAddress` format. + +### B-016 β€” Stellar event listener scope and error handling +- **Area:** backend/stellar +- **Evidence:** `acbu-backend/src/services/stellar/eventListener.ts` +- **Impact:** Broad Horizon streams + swallowed errors cause missed mint/burn indexing silently. +- **Fix direction:** Filter to known contract IDs; structured retries; DLQ routing for parse failures; metrics on event ingestion lag. +- **Acceptance check:** Injected test event reaches projection store within SLA under simulated outages. + +### B-017 β€” KYC machine layer is placeholder +- **Area:** backend/kyc +- **Evidence:** `acbu-backend/src/services/kyc/machineLayer.ts` +- **Impact:** Automated checks / PII redaction are not production-grade; KYC fraud risk increases. +- **Fix direction:** Integrate a vision provider; PII redaction pipeline; manual review queue for low-confidence results. +- **Acceptance check:** Sample IDs pass/fail deterministically in staging with redacted logs. + +### B-026 β€” Audit write failures only logged +- **Area:** backend/audit +- **Evidence:** `acbu-backend/src/services/audit/auditService.ts` +- **Impact:** Compliance gap if audit events disappear silently. +- **Fix direction:** Retry + outbox to Mongo/queue; sustained-failure alert; dashboard on audit lag. +- **Acceptance check:** Chaos test (simulated Mongo/queue outage) demonstrates the audit pipeline recovers and reaches eventual consistency. + +### B-028 β€” API key rate limiter non-atomic increment +- **Area:** backend/ratelimit +- **Evidence:** `acbu-backend/src/middleware/rateLimiter.ts` +- **Impact:** Get-then-set increment pattern is not atomic; concurrent requests can both pass the limit. +- **Fix direction:** Use Mongo atomic `$inc` with a per-key document-level cap **or** Redis `INCR` with TTL. +- **Acceptance check:** Concurrent load test cannot exceed configured QPS for any single API key. + +### B-049 β€” CORS policy review for credentialed browsers +- **Area:** backend/security +- **Evidence:** `acbu-backend/src/index.ts` / CORS config +- **Impact:** Misconfigured origins with credentials enable token theft via malicious site. +- **Fix direction:** Explicit allowlist per environment; never combine `*` with `credentials: true`. +- **Acceptance check:** Security headers test suite (OWASP baseline) passes. + +### B-053 β€” Idempotency-Key header support +- **Area:** backend/api +- **Evidence:** POST transfer/mint/burn routes +- **Impact:** Network retries can double-charge / double-mint. +- **Fix direction:** Persist keys in `Idempotency` table; cache and return identical response bodies for repeated keys. +- **Acceptance check:** Duplicate POST with the same key returns the cached response within the configured TTL. + +### B-060 β€” Admin role separation +- **Area:** backend/authz +- **Evidence:** Admin vs user API keys +- **Impact:** Support-staff impersonation is overpowered; single admin key compromise is catastrophic. +- **Fix direction:** Scoped admin keys + MFA + break-glass workflow; attribute admin actions separately in audit. +- **Acceptance check:** Admin actions are clearly delineated in audit; scoped keys cannot perform unscoped operations. + +### B-061 β€” Secrets in environment validation +- **Area:** backend/config +- **Evidence:** `acbu-backend/src/config/env.ts` +- **Impact:** Late or partial validation lets half-configured prod boot. +- **Fix direction:** Strict Zod schema at startup with `fail-fast`; no `config` access before validation runs. +- **Acceptance check:** Missing `JWT_SECRET` (or any required secret) prevents the process from `app.listen`. + +### B-068 β€” Brute-force protection on auth endpoints +- **Area:** backend/auth +- **Evidence:** `authRoutes` + rate limiter +- **Impact:** Passcodes / API keys are guessable at scale. +- **Fix direction:** Stricter limiter on `/auth/*`; explicit lockout policy; backoff. +- **Acceptance check:** Hydra-style test is blocked after configured threshold. + +### B-073 β€” Transaction status machine tests +- **Area:** backend/core +- **Evidence:** Prisma `Transaction` model transitions +- **Impact:** Illegal transitions corrupt balance reporting. +- **Fix direction:** Explicit state machine + DB constraints; property tests covering every transition pair. +- **Acceptance check:** Invalid transition throws a domain error and is also rejected by a DB-level `CHECK` constraint. + +### B-075 β€” On-ramp swap race: duplicate processing +- **Area:** backend/jobs +- **Evidence:** `acbu-backend/src/jobs/usdcConvertAndMintJob.ts` (`OnRampSwap.status` transitions) +- **Impact:** Two workers may pick the same `pending` swap if the status check is non-atomic. +- **Fix direction:** Use conditional update `WHERE status = 'pending'` to transition to `processing`, scoped by id; verification in concurrency tests. +- **Acceptance check:** Concurrency test confirms exactly one worker processes each swap. + +--- + +## 5. Medium Severity Issues (🟑) + +### B-012 β€” Savings controller input validation +- **Area:** backend/savings Β· **Evidence:** `acbu-backend/src/controllers/savingsController.ts` +- **Impact:** `amount` / `term_seconds` unchecked before contract calls; bad Soroban args or cryptic errors. +- **Fix direction:** Zod schemas with bounds; decimal-string amount; reject overflow before service dispatch. +- **Acceptance check:** Invalid bodies produce structured 400 errors; valid bodies pass service-layer validation. + +### B-014 β€” Wallet activation funding asset choice +- **Area:** backend/wallet Β· **Evidence:** `acbu-backend/src/services/wallet/walletActivationService.ts` +- **Impact:** Product docs reference Pi vs XLM; wrong asset strands users on the wrong network. +- **Fix direction:** Align with `TESTNET_CUSTODIAL_BOOTSTRAP` / network config; expose a network-scoped feature flag; clear log of chosen asset. +- **Acceptance check:** Activation succeeds on the configured network with the documented asset. + +### B-015 β€” Hardcoded Soroban inclusion fees +- **Area:** backend/stellar Β· **Evidence:** `acbu-backend/src/services/stellar/contractClient.ts` and related services +- **Impact:** Default fee `'100'` may fail under network congestion or be overpaid; confirmation timing is unpredictable. +- **Fix direction:** Fetch base fee + soroban resource fees via Horizon / RPC; configurable caps. +- **Acceptance check:** Transactions succeed under load tests with no manual fee intervention. + +### B-018 β€” Government treasury aggregation stub +- **Area:** backend/government Β· **Evidence:** `acbu-backend/src/controllers/governmentController.ts` +- **Impact:** Operators lack a consolidated exposure view. +- **Fix direction:** Aggregate from Prisma + reserve segments; cache with TTL; populate per-currency breakdown. +- **Acceptance check:** Endpoint returns non-empty `byCurrency` against seeded data. + +### B-019 β€” Salary APIs not implemented +- **Area:** backend/salary Β· **Evidence:** `acbu-backend/src/controllers/salaryController.ts` +- **Impact:** MVP payroll promises are unfulfilled; clients get 501/empty arrays. +- **Fix direction:** Batch transfers + schedules + idempotency keys; reuse `transferService` for the core accounting. +- **Acceptance check:** Postman collection green for happy path. + +### B-020 β€” Enterprise bulk transfer not implemented +- **Area:** backend/enterprise Β· **Evidence:** `acbu-backend/src/controllers/enterpriseController.ts` +- **Impact:** Large merchants cannot upload batches. +- **Fix direction:** Chunked processing; per-row idempotency; reporting endpoint for partial failure. +- **Acceptance check:** 10k-row CSV completes within SLO with a partial-failure report. + +### B-021 β€” Enterprise treasury view stub +- **Area:** backend/enterprise Β· **Evidence:** `acbu-backend/src/controllers/enterpriseController.ts` (`getTreasury`) +- **Impact:** Treasury nulls block finance workflows. +- **Fix direction:** Join transfers, reserves, FX snapshots. +- **Acceptance check:** Totals reconcile to ledger within tolerance. + +### B-022 β€” Bills catalog and pay not implemented +- **Area:** backend/bills Β· **Evidence:** `acbu-backend/src/controllers/billsController.ts` +- **Impact:** Bill-pay segment is non-functional. +- **Fix direction:** Partner adapter + webhook reconciliation + refund flow. +- **Acceptance check:** Happy-path bill payment creates an auditable `Transaction`. + +### B-023 β€” Investment deployable allocation ignores deployed +- **Area:** backend/investment Β· **Evidence:** `acbu-backend/src/services/investment/allocationService.ts` +- **Impact:** `deployedUsd` pinned to 0 over-allocates to strategies. +- **Fix direction:** Track deployed notional in DB; subtract from deployable; tie ledger adjustments to allocation events. +- **Acceptance check:** Allocation never exceeds policy once deployed balances are populated. + +### B-024 β€” Investment withdrawal notifications for org requests +- **Area:** backend/jobs Β· **Evidence:** `acbu-backend/src/jobs/investmentWithdrawalJob.ts` +- **Impact:** Org-only requests may never notify approvers. +- **Fix direction:** Notify org admins via email/push using `organizationId`; mirror for org-only `r.organizationId` rows where `r.userId` is null. +- **Acceptance check:** Org withdrawal request lands a notification in the test inbox. + +### B-025 β€” Yield accounting stub +- **Area:** backend/investment Β· **Evidence:** `acbu-backend/src/services/investment/yieldAccountingService.ts` +- **Impact:** Cannot report yield or tax basis. +- **Fix direction:** Implement accrual postings tied to vault positions; statement API endpoint. +- **Acceptance check:** Monthly statement API returns non-zero yield against seeded positions. + +### B-027 β€” Cache `deletePattern` ReDoS risk +- **Area:** backend/cache Β· **Evidence:** `acbu-backend/src/utils/cache.ts` +- **Impact:** User-influenced regex patterns can hang the Node event loop. +- **Fix direction:** Escape / length-cap patterns; prefer safe glob or prefix scan instead of arbitrary regex. +- **Acceptance check:** Fuzz test cannot stall the event loop beyond budget. + +### B-029 β€” Rate limiter Mongo outage fail-open +- **Area:** backend/ratelimit Β· **Evidence:** `acbu-backend/src/middleware/rateLimiter.ts` + `cacheService` +- **Impact:** When cache errors, fallback may weaken per-key limits. +- **Fix direction:** Stricter IP fallback + circuit breaker + active metrics on degraded mode. +- **Acceptance check:** Simulated Mongo outage triggers the documented degraded-but-bounded mode. + +### B-031 β€” Deep health and metrics routes are public +- **Area:** backend/ops Β· **Evidence:** `acbu-backend/src/routes/index.ts` (`/health/deep`, `/health/metrics`) +- **Impact:** Dependency topology exposes to unauthenticated callers (reconnaissance). +- **Fix direction:** Protect with mTLS or admin API key; keep `/health` shallow public. +- **Acceptance check:** Public callers cannot scrape dependency failures or reserve ratios at scale. + +### B-033 β€” Swagger UI publicly mounted +- **Area:** backend/docs Β· **Evidence:** `acbu-backend/src/index.ts` +- **Impact:** Attackers map the surface area without auth. +- **Fix direction:** Gate `/api-docs` behind admin auth, or disable in prod. +- **Acceptance check:** Prod deployment returns 404 (or admin-authenticated response) for `/api-docs`. + +### B-034 β€” OpenAPI drift vs implementation +- **Area:** backend/docs Β· **Evidence:** `acbu-backend` swagger annotations vs routes +- **Impact:** Clients integrate against stale schemas. +- **Fix direction:** CI check: generate OpenAPI + contract tests against Zod schemas; block merges on drift. +- **Acceptance check:** CI fails when a route removes a field still documented (or adds a field undocumented). + +### B-036 β€” `postinstall` runs `prisma generate` + `tsc` +- **Area:** backend/devx Β· **Evidence:** `acbu-backend/package.json` (`postinstall`) +- **Impact:** Slow installs; CI surprises; failures on restricted networks. +- **Fix direction:** Move TypeScript compile to an explicit CI step; keep `postinstall` to `prisma generate` only. +- **Acceptance check:** `pnpm install <2 min` on a clean cache (target); CI has a deterministic compile step. + +### B-037 β€” Jest `passWithNoTests` weak gate +- **Area:** backend/qa Β· **Evidence:** `acbu-backend/package.json` (`test` script) +- **Impact:** Missing tests do not fail CI. +- **Fix direction:** Remove the flag once smoke tests exist; require minimum coverage thresholds in money modules. +- **Acceptance check:** CI fails if no tests under `src/services/transfer` (or other money-present paths). + +### B-038 β€” No transfer pagination cursors +- **Area:** backend/api Β· **Evidence:** `acbu-backend/src/controllers/transferController.ts` +- **Impact:** Clients cannot paginate beyond the first 50. +- **Fix direction:** Cursor pagination + composite index on `(userId, createdAt)`. +- **Acceptance check:** Client walks entire history without duplicates. + +### B-039 β€” Investment withdrawal list lacks pagination +- **Area:** backend/api Β· **Evidence:** `acbu-backend/src/controllers/investmentController.ts` +- **Impact:** Same 50-row cap as transfers. +- **Fix direction:** Cursor + filters by status; matching DB index. +- **Acceptance check:** Large org history loads progressively. + +### B-042 β€” Webhook raw body JSON fallback empty object +- **Area:** backend/webhooks Β· **Evidence:** `acbu-backend/src/index.ts` +- **Impact:** Invalid JSON becomes `{}`; handlers may run on bad data. +- **Fix direction:** Reject non-JSON with 400 before routing; fail closed. +- **Acceptance check:** Malformed payload raises a 400 and never reaches business logic. + +### B-044 β€” Basket weight computation job missing +- **Area:** backend/ops Β· **Evidence:** Documented in `PROJECT/issues/BACKEND_ISSUES.md` +- **Impact:** Weights drift from policy without automation. +- **Fix direction:** Scheduled job (`metricsService`/`proposedWeightsJob`) + audit log + manual approval gate. +- **Acceptance check:** Weekly job emits a diff report against the active basket config. + +### B-045 β€” Fintech multi-provider per country +- **Area:** backend/integrations Β· **Evidence:** `acbu-backend` webhook + payout modules +- **Impact:** Single Flutterwave path = outage risk. +- **Fix direction:** Router chooses provider by health + fees + country per `correction.md` (e.g. NGN: Paystack + Flutterwave). +- **Acceptance check:** Simulated provider outage fails over; both providers exercised in tests. + +### B-047 β€” Segment guard `requireMinTier` unused +- **Area:** backend/authz Β· **Evidence:** `acbu-backend/src/middleware/segmentGuard.ts` +- **Impact:** `User.tier` field unused β†’ wrong product gating at the middleware layer. +- **Fix direction:** Apply middleware to premium routes; add tests for tier-bound routes. +- **Acceptance check:** Free-tier user cannot call SME-only endpoints in tests. + +### B-050 β€” Helmet / security headers baseline +- **Area:** backend/security Β· **Evidence:** `acbu-backend/src/index.ts` +- **Impact:** Missing headers increase XSS/clickjacking risk. +- **Fix direction:** Helmet defaults + CSP for any server-rendered/static content; documented exceptions. +- **Acceptance check:** `securityheaders.io` reports no worse than `A-` (or documented exceptions). + +### B-052 β€” Structured logging for money movements +- **Area:** backend/observability Β· **Evidence:** `acbu-backend/src/config/logger.ts` usage +- **Impact:** Insufficient fields for forensic audit (amount, currency, user, idempotency key). +- **Fix direction:** Standard log schema for financial events. +- **Acceptance check:** Log query returns the full transfer lifecycle by user/idempotency key. + +### B-055 β€” Database migration CI gate +- **Area:** backend/ci Β· **Evidence:** `.github/workflows` +- **Impact:** Destructive migrations ship without warning. +- **Fix direction:** Ensure `prisma migrate diff` or deploy dry-run in CI; require label for destructive migrations. +- **Acceptance check:** CI fails on destructive migration without the required label. + +### B-056 β€” Prisma Accelerate vs direct URL confusion +- **Area:** backend/config Β· **Evidence:** `acbu-backend/README.md` + `ENV_VARS.md` +- **Impact:** Wrong URL used for migrations vs runtime. +- **Fix direction:** Document matrix; startup assert roles. +- **Acceptance check:** Boot logs show correct URL type per purpose. + +### B-058 β€” Encryption at rest for PII columns +- **Area:** backend/compliance Β· **Evidence:** `prisma/schema.prisma` +- **Impact:** DB leak exposes plaintext government IDs. +- **Fix direction:** App-level encryption or DB TDE; KMS rotation; documented key management. +- **Acceptance check:** Security review sign-off for the chosen field list and key lifecycle. + +### B-059 β€” GDPR export & delete endpoints +- **Area:** backend/compliance Β· **Evidence:** User settings (verify coverage) +- **Impact:** Regulatory exposure for EU users. +- **Fix direction:** Data export job + tombstone deletes; documented legal data categories. +- **Acceptance check:** DPO checklist satisfied; sample export contains only documented fields. + +### B-062 β€” S3 presigned URL abuse controls +- **Area:** backend/storage Β· **Evidence:** KYC uploads (if S3) +- **Impact:** Open buckets / long TTL enable exfiltration. +- **Fix direction:** Short TTL + content-type constraint + virus scan hook. +- **Acceptance check:** Pen test cannot read other users' KYC objects. + +### B-063 β€” OpenAI usage guardrails +- **Area:** backend/ai Β· **Evidence:** `package.json` includes `openai` +- **Impact:** Prompt injection or runaway spend. +- **Fix direction:** Auth + per-org budget + prompt allowlist; spend alerts. +- **Acceptance check:** Spending caps enforced in staging load test. + +### B-064 β€” Dependency vulnerability scanning +- **Area:** backend/security Β· **Evidence:** `pnpm-lock.yaml` +- **Impact:** Known CVEs in transitive deps. +- **Fix direction:** Enable Dependabot/Snyk + monthly cadence. +- **Acceptance check:** CI fails on critical CVE without an explicit waiver label. + +### B-066 β€” 2FA challenge token purpose binding +- **Area:** backend/auth Β· **Evidence:** `acbu-backend/src/utils/jwt.ts` +- **Impact:** Reuse across flows if `aud`/`iss` checks incomplete. +- **Fix direction:** Add `aud`/`iss` claims; rotate secrets. +- **Acceptance check:** Challenge token cannot be used as a general API token. + +### B-067 β€” Account enumeration on sign-in +- **Area:** backend/auth Β· **Evidence:** `acbu-backend/src/controllers/auth.ts` (verify responses) +- **Impact:** Attackers learn which emails exist via timing / response differences. +- **Fix direction:** Uniform error messages; CAPTCHA after N tries; throttling; normalize timing. +- **Acceptance check:** Timing differences and message differences are below the noise threshold. + +### B-069 β€” Content-Type and body size limits +- **Area:** backend/security Β· **Evidence:** Express JSON parser +- **Impact:** Large JSON DoS. +- **Fix direction:** Lower default limit; per-route overrides for upload endpoints. +- **Acceptance check:** 10MB payload rejected where inappropriate. + +### B-072 β€” Consistent error JSON schema +- **Area:** backend/api Β· **Evidence:** `errorHandler.ts` vs legacy `{ error: string }` +- **Impact:** Frontend parsing brittle. +- **Fix direction:** Standard envelope `{ error: { code, message, details } }` everywhere. +- **Acceptance check:** OpenAPI examples match runtime errors. + +### B-074 β€” Double-submit burn with same blockchain hash +- **Area:** backend/burn Β· **Evidence:** `burnController.ts` optional `blockchain_tx_hash` +- **Impact:** Replay duplicates redemption records. +- **Fix direction:** Unique index on hash when present + idempotency-key handling. +- **Acceptance check:** Second submit returns the original id; no duplicate row created. + +--- + +## 6. Low Severity Issues (🟒) + +### B-030 β€” Standard IP rate limit message mismatch +- **Area:** backend/ratelimit Β· **Evidence:** `acbu-backend/src/middleware/rateLimiter.ts` (`createRateLimiter`) +- **Impact:** Message says β€œIP” while some routes use API keys. +- **Fix direction:** Contextual messages; separate limiters. +- **Acceptance check:** 429 JSON explains which limit tripped. + +### B-032 β€” Shallow `/health` does not prove readiness +- **Area:** backend/ops Β· **Evidence:** `acbu-backend/src/routes/index.ts` (`/health`) +- **Impact:** LB marks instance ready while DB/queue is down. +- **Fix direction:** Optional `/health/ready` for k8s; document split. +- **Acceptance check:** K8s readiness uses the deep check when configured. + +### B-035 β€” README still references npm only +- **Area:** backend/devx Β· **Evidence:** `acbu-backend/README.md` +- **Impact:** Contributors mix npm/pnpm; lockfile drift. +- **Fix direction:** Document pnpm as canonical; align scripts. +- **Acceptance check:** Single package manager in README + CI. + +### B-040 β€” Government list `limit` NaN hazard +- **Area:** backend/api Β· **Evidence:** `acbu-backend/src/controllers/governmentController.ts` +- **Impact:** `Number(NaN)` can break Prisma `take`/`skip`. +- **Fix direction:** Zod coerce int with min/max defaults. +- **Acceptance check:** Invalid limit returns 400 with structured error. + +### B-041 β€” Prisma `onRampSwap` typed via `as any` casts +- **Area:** backend/types Β· **Evidence:** Jobs/controllers casting prisma client +- **Impact:** Hides compile-time safety for financial rows. +- **Fix direction:** Regenerate client; fix schema naming; remove casts. +- **Acceptance check:** No `(prisma as any)` in `src/jobs`. + +### B-043 β€” Notification service placeholder sender +- **Area:** backend/notifications Β· **Evidence:** `acbu-backend/src/services/notification/notificationService.ts` +- **Impact:** Emails may bounce or hit spam. +- **Fix direction:** Configure SES/SendGrid with verified domain; DKIM/SPF green. +- **Acceptance check:** DNS DKIM/SPF green in staging; smoke test delivers to a known inbox. + +### B-046 β€” Limits config hardcoded +- **Area:** backend/config Β· **Evidence:** `acbu-backend/src/config/limits.ts` +- **Impact:** Ops cannot tune without redeploy. +- **Fix direction:** Load from DB or env with hot reload cache. +- **Acceptance check:** Limit change applies without redeploy. + +### B-048 β€” Permissions typed as loose strings +- **Area:** backend/types Β· **Evidence:** `acbu-backend/src/middleware/auth.ts` (`validatePermissions`) +- **Impact:** Typos in DB JSON grant wrong access. +- **Fix direction:** Zod enum list for permissions at write time. +- **Acceptance check:** Invalid permission rejected at admin API. + +### B-051 β€” Request ID propagation +- **Area:** backend/observability Β· **Evidence:** Express middleware stack +- **Impact:** Hard to correlate user reports with logs. +- **Fix direction:** Add `x-request-id` middleware; log field; pass through downstream calls. +- **Acceptance check:** Support engineers can trace a single request across services end-to-end. + +### B-054 β€” Distributed tracing (OpenTelemetry) +- **Area:** backend/observability Β· **Evidence:** N/A +- **Impact:** Cross-service latency is opaque. +- **Fix direction:** OTel exporter with sample rates; per-route overrides. +- **Acceptance check:** Trace links Prisma + Soroban submit span under load. + +### B-057 β€” Mongo index strategy for hot keys +- **Area:** backend/data Β· **Evidence:** Mongo collections for cache/ratelimit +- **Impact:** Hot keys slow under load. +- **Fix direction:** Index design review + TTL policies. +- **Acceptance check:** Load-test p95 under SLO; TTL confirmed for ephemeral collections. + +### B-065 β€” Time sync / clock skew handling for JWT +- **Area:** backend/auth Β· **Evidence:** `acbu-backend/src/utils/jwt.ts` +- **Impact:** Skew causes confusing 401s. +- **Fix direction:** Leeway config + NTP monitoring; document skew tolerance. +- **Acceptance check:** Clock-skew test passes within the documented window. + +### B-070 β€” Input sanitization for search endpoints +- **Area:** backend/api Β· **Evidence:** Recipient resolve / user search +- **Impact:** SQL/NoSQL injection via regex. +- **Fix direction:** Parameterized queries only; escape user-supplied regex. +- **Acceptance check:** Security scanner reports clean for all recipient/user search paths. + +### B-071 β€” Versioning and deprecation headers +- **Area:** backend/api Β· **Evidence:** `config.apiVersion` +- **Impact:** Clients stuck on breaking changes. +- **Fix direction:** `Sunset` header + changelog endpoint. +- **Acceptance check:** Old API version returns the warning header and changelog link. + +--- + +## 7. Legacy 56-Item Origin List (cross-reference) + +The legacy list previously alone at this path was a more recent manual audit with slightly different framing. Items are preserved here for traceability; each is tagged `(moved)` (when it cleanly maps to a canonical `B-###` ID) or `(new)` (when it captures a distinct finding that should be merged into the canonical list in a follow-up PR). + +### Critical +1. **API key validation broken** – src/middleware/auth.ts: `validateApiKey` uses `bcrypt.hash` for lookup (random salt) β†’ stored hash never matches. Use `bcrypt.compare`. **Distinct finding; not in the canonical 75-item list. Should be promoted to a follow-up canonical ID (expected `B-076`) in a separate PR.** *(new β€” propose B-076)* +2. **Wrong import in `usdcConvertAndMintJob`** – Imports `db` from `../config/database` but module exports `prisma`. **Maps to a runtime variant of B-003 / B-075; tracked together.** *(moved β†’ B-003 + B-075)* +3. **IDOR / auth bypass in savings controller** – `user` taken from body/query, not authenticated user. **Combine with B-007 + segment guards.** *(moved β†’ B-007)* +4. **Env validation too late** – `requiredEnvVars` checked after `config` is built. **Folds into B-061.** *(moved β†’ B-061)* + +### High +5. **Infinite retries for failed webhooks** – *(moved β†’ B-004)* +6. **Organization users never notified for investment withdrawal** – *(moved β†’ B-024)* +7. **Burn limits bypass when `audience` is unset** – Mirror B-007 in the burn path. **Folds into B-005 / B-007 / B-009 audit.** *(moved β†’ B-007 + B-009)* +8. **Mint limits bypass when `audience` is unset** – Same root cause as #7. *(moved β†’ B-007 + B-001)* +9. **Fee thresholds likely wrong** – *(moved β†’ B-009)* +10. **Decimal vs string for `acbuAmount` in transfer** – *(moved β†’ B-008)* +11. **No dead-letter queues** – Folds into B-003, B-004, B-026. *(moved β†’ B-003 + B-004)* +12. **No max retry limit for webhook consumer** – Folds into B-003, B-004, B-026. *(moved β†’ B-004)* + +### Medium +13. **Wallet activation uses XLM, not Pi** – *(moved β†’ B-014)* +14. **Remove placeholder Stellar address in auth** – *(moved β†’ B-013)* +15. **Transfer service: verify alias-based flow** – Folds into B-007 + B-008. *(moved β†’ B-007 + B-008)* +16. **Contract client and Stellar fees hardcoded** – *(moved β†’ B-015)* +17. **Multiple fintech providers per nation** – *(moved β†’ B-045)* (also reinforced by `correction.md`) +18. **KYC extraction placeholder** – *(moved β†’ B-017)* +19. **Basket weight script missing** – *(moved β†’ B-044)* +20. **Balance endpoint for frontend missing** – Cross-cutting concern surfaced in `issues/frontend.md` `F-025`; see **Cross-Reference Map**. *(new β€” cross-cutting)* +21. **No dependency health checks** – Reinforces B-031 / B-032. *(moved β†’ B-031 + B-032)* +22. **Race condition in API key rate limiting** – *(moved β†’ B-028)* +23. **ReDoS risk in `deletePattern`** – *(moved β†’ B-027)* +24. **Webhook verification skipped when secret missing** – *(moved β†’ B-005)* +25. **Placeholder Stellar address format invalid** – *(moved β†’ B-013)* +26. **ACBU supply from DB, not chain** – *(moved β†’ B-010)* +27. **Org-scoped limits may not apply** – *(moved β†’ B-011)* +28. **USDC to XLM conversion is a no-op** – *(moved β†’ B-003 + B-075)* +29. **Hardcoded XLM rate** – *(moved β†’ B-002 + B-015)* +30. **Deposit limits use wrong USD equivalent** – *(moved β†’ B-001)* +31. **Incomplete treasury aggregation** – *(moved β†’ B-018)* +32. **Audit failures are silent** – *(moved β†’ B-026)* + +### Low +33. **`any` cast for permissions** – *(moved β†’ B-048)* +34. **`any` usage in segmentGuard** – *(moved β†’ B-047 + B-048)* +35. **`any` usage in Stellar client** – *(moved β†’ B-041)* +36. **`any` cast in burning service** – *(moved β†’ B-041)* +37. **No pagination on transfers** – *(moved β†’ B-038)* +38. **No pagination on investment withdrawals** – *(moved β†’ B-039)* +39. **`limit` query param not validated** – *(moved β†’ B-040)* +40. **No input validation in savings controller** – *(moved β†’ B-012)* +41. **Missing composite index** – Tied to B-038 / B-039. *(moved β†’ B-038 + B-039)* +42. **Webhook JSON parse failure returns empty body** – *(moved β†’ B-042)* +43. **Stack traces in error logs** – Cross-cutting; folded into B-051 / B-052 + a structured error envelope (B-072). *(moved β†’ B-051 + B-052 + B-072)* + +### Trivial +44. **`(prisma as any).onRampSwap` casts** – *(moved β†’ B-041)* +45. **Hardcoded limit values** – *(moved β†’ B-046)* +46. **Fallback email placeholder** – *(moved β†’ B-043)* +47. **Yield accounting stub** – *(moved β†’ B-025)* +48. **No automated tests** – *(moved β†’ B-037)* + general backend hygiene +49. **Client-controlled mint recipient** – *(moved β†’ B-007)* +50. **Rate limiting fails open when MongoDB cache is down** – *(moved β†’ B-029)* +51. **Recovery unlock is single-factor** – *(moved β†’ B-006)* +52. **`requireMinTier` middleware is never used** – *(moved β†’ B-047)* +53. **`InvestmentWithdrawalRequest.organizationId` has no FK** – Direct prisma schema follow-up; tracks alongside B-024 and B-011. *(new β€” schema)* +54. **Unauthenticated reserve metrics on `/health/metrics`** – *(moved β†’ B-031)* +55. **Swagger UI exposed without auth** – *(moved β†’ B-033)* +56. **Stellar event listener: broad stream and swallowed errors** – *(moved β†’ B-016)* + +> **Reconciliation policy:** When a follow-up audit pass opens a new canonical ID for items tagged `(new)`, the row is migrated forward and the tagged comment block in this section is updated to point at the new ID. New IDs are expected to land in `B-076+` and should preserve backward-reference to this section so future readers can trace provenance. + +--- + +## 8. Resolution Tracker (Fix Status) + +> **Format:** `ID Β· title Β· status Β· linked PR / commit Β· owner` +> +> **Forward-looking template.** Every item is currently 🟑 Open; uniformity of status reflects the state of the working backlog. When a fix merges, the row transitions to βœ… Fixed with a PR link. Example anchors below show what a real fixed row looks like. + +| ID | Title | Status | Notes | +|----|-------|--------|-------| +| _(example)_ | _(example anchor for prior documentary PRs β€” PR #14 for issue #1, PR #22 for issue #12)_ | βœ… Fixed (PR #22 merged) | Documentary PR β€” not a code fix; historical anchor | +| B-005 | Webhook signature fail-open | 🟑 Open (ship blocker) | Tracked with auth refactor | +| B-006 | Recovery single-factor | 🟑 Open (ship blocker) | Tracked with auth refactor | +| B-001..B-075 (others) | _see Summary Table_ | 🟑 Open | All canonical IDs pending remediation | + +> **Status legend** β€” 🟒 Trivial/cosmetic Β· 🟑 Open (tracked) Β· πŸ”΅ In review Β· βœ… Fixed (PR linked) +> +> **Reminder when updating:** When an external PR closes an issue, replace `🟑 Open` with `βœ… Fixed` and append the merged PR link in the Notes column. Do not rewrite history; the row itself is the audit trail. + +--- + +## 9. Cross-Reference Map + +| Document | Relationship to this file | +|----------|---------------------------| +| [`../../issues/backend.md`](../../issues/backend.md) | **Live triage-ready catalog.** Same IDs and severities; entries are condensed for daily use. | +| [`../../issues/MASTER_INDEX.md`](../../issues/MASTER_INDEX.md) | Master index across backend/frontend/contracts backlogs. | +| [`../../issues/contracts.md`](../../issues/contracts.md) | Smart contracts MVP issues (C-001..C-060). | +| [`../../issues/frontend.md`](../../issues/frontend.md) | Frontend MVP issues (F-001..F-065). | +| [`../API_AND_CONTRACTS_REFERENCE.MD`](../API_AND_CONTRACTS_REFERENCE.MD) | Routes ↔ contracts mapping. | +| [`../PROJECT_STRUCTURE.MD`](../PROJECT_STRUCTURE.MD) | Repo layout (where the backend source lives). | +| [`../../TECHNICAL/ARCHITECTURE.MD`](../../TECHNICAL/ARCHITECTURE.MD) | High-level architecture. | +| [`../../TECHNICAL/API_SPECIFICATION.MD`](../../TECHNICAL/API_SPECIFICATION.MD) | REST API endpoints. | +| [`../../TECHNICAL/DATABASE_SCHEMA.MD`](../../TECHNICAL/DATABASE_SCHEMA.MD) | Database tables and relationships. | +| [`../../OPERATIONS/REGULATORY.MD`](../../OPERATIONS/REGULATORY.MD) | Compliance/regulatory rationale for B-058 (PII encryption at rest), B-059 (GDPR export/delete), B-067 (account enumeration guard). | +| [`../../OPERATIONS/FINTECH_PARTNERSHIPS.MD`](../../OPERATIONS/FINTECH_PARTNERSHIPS.MD) | Multi-provider context for B-045 (also referenced in `correction.md`). | +| [`../../OPERATIONS/LIMITS_AND_TIERS.MD`](../../OPERATIONS/LIMITS_AND_TIERS.MD) | Source of truth for B-001, B-009, B-011, B-046 limit tables. | +| [`../../USER_EXPERIENCE/MERCHANT_INTEGRATION.MD`](../../USER_EXPERIENCE/MERCHANT_INTEGRATION.MD) | bill-pay / merchant context for B-022. | +| [`../../correction.md`](../../correction.md) | Standing list of items curated for fast triage (multi-fintech, basket weight). | +| [`../CONTRACTS_ISSUES.md`](../CONTRACTS_ISSUES.md) | Companion long-form reference for smart contracts. | + +--- + +## 10. Top Remediations (ship-safety order) + +Order these fixes in the MVP launch runbook before deploying mainnet: + +1. **B-005 / B-006** β€” Webhook fail-closed + recovery second factor (single auth-week PR). +2. **B-007** β€” Bind all mint / burn / on-ramp recipients to `User.stellarAddress`. +3. **B-001 / B-009** β€” Correct USD proxy for mint deposits and burn fee math. +4. **B-002 / B-008** β€” `Decimal` end-to-end on money paths. +5. **B-003 / B-004 / B-075** β€” RabbitMQ retry caps + DLQ + atomic status transitions. +6. **B-010 / B-011** β€” Reserve truth reconciliation + org-scoped limits. +7. **B-013 / B-014 / B-015** β€” Signup Stellar address hygiene + wallet asset choice + dynamic fees. +8. **B-016 / B-028 / B-049** β€” Listener scope + atomic rate limiter + CORS allowlist. +9. **B-017 / B-020** β€” KYC machine layer + enterprise bulk transfer. +10. **B-026 / B-053 / B-061 / B-068 / B-073** β€” Audit durability, idempotency keys, env schema, brute-force guard, transaction state machine. +11. **B-018 / B-021 / B-022 / B-023 / B-024 / B-025** β€” Segment-API stubs (government, enterprise, bills, investment notifications, yield accounting). +12. **B-031 / B-032 / B-036 / B-037 / B-044 / B-045** β€” Ops hygiene (deep health, postinstall, jest gate, basket job, multi-fintech). + +--- + +## 11. Maintenance + +- **New issues** β€” Add to `issues/backend.md` first (canonical triage). Mirror to this file with the next stable `B-###` ID. +- **Closed fixes** β€” Promote the fix to `βœ… Fixed (PR linked)` in Section 8 and link to the merged PR. +- **Legacy `(new)` items in Section 7** β€” Open a follow-up canonical-ID PR; on merge, update both `issues/backend.md` and this file. +- **Cross-doc references** β€” Update `issues/MASTER_INDEX.md` summary counts when severities change. + +--- + +## Related Documents + +- [Live backend issue catalog](../../issues/backend.md) +- [Master backlog index](../../issues/MASTER_INDEX.md) +- [Frontend issues](../../issues/frontend.md) +- [Contracts long-form reference](../CONTRACTS_ISSUES.md) +- [Smart contract spec (root copy)](../SMART_CONTRACT_SPEC.md) +- [Project structure map](../PROJECT_STRUCTURE.md) +- [Tech architecture](../../TECHNICAL/ARCHITECTURE.md) +- [API specification](../../TECHNICAL/API_SPECIFICATION.md) +- [Limits & tiers](../../OPERATIONS/LIMITS_AND_TIERS.md) +- [Fintech partnerships](../../OPERATIONS/FINTECH_PARTNERSHIPS.md) --- -## Critical - -1. **API key validation broken** – src/middleware/auth.ts: `validateApiKey` uses `keyHash: await hashApiKey(apiKey)` for lookup. `hashApiKey` calls `bcrypt.hash()`, which produces a different hash each time (random salt). Stored hashes will never match. Use `bcrypt.compare(plaintextApiKey, storedKeyHash)` instead of hashing the input for lookup. -2. **Wrong import in usdcConvertAndMintJob** – src/jobs/usdcConvertAndMintJob.ts: Imports `db` from `../config/database`, but that module exports `prisma`. The code uses `prisma`, which is never imported, causing a `ReferenceError` at runtime. -3. **IDOR / auth bypass in savings controller** – src/controllers/savingsController.ts: `user` is taken from `req.body` or `req.query` without checking it against the authenticated user. Any authenticated user can operate on another user's savings by supplying a different `user` ID. -4. **Env validation too late** – src/config/env.ts: `requiredEnvVars` is checked after `config` is built. If `DATABASE_URL` is missing, the app may still start and fail later. Validation should run before any use of `config`. - -## High - -5. **Infinite retries for failed webhooks** – src/services/webhook/webhookService.ts: When `status === 'failed'`, `deliverWebhook` still attempts delivery and returns `false`, so the consumer nacks and requeues indefinitely. Add early return for failed status. -6. **Organization users never notified for investment withdrawal** – src/jobs/investmentWithdrawalJob.ts: `publishInvestmentWithdrawalReady` only fires when `r.userId` is set. For organization requests (`r.organizationId` set, `r.userId` null), no notification is sent. -7. **Burn limits bypass when `audience` is unset** – src/controllers/burnController.ts: `checkWithdrawalLimits` and `isCurrencyWithdrawalPaused` run only when `req.audience` is set. `/burn/acbu` via `burnRoutes` does not set `audience`, so limits and circuit breakers are skipped. -8. **Mint limits bypass when `audience` is unset** – src/controllers/mintController.ts: `isMintingPaused` and `checkDepositLimits` run only when `req.audience` is set. `/mint/usdc` and `/mint/deposit` via `mintRoutes` do not set `audience`, so limits are bypassed. -9. **Fee thresholds likely wrong** – src/services/feePolicy/feePolicyService.ts: `getBurnFeeBps` uses `pctOfTarget < 15` and `pctOfTarget > 21`. With `pctOfTarget = (actualWeight / targetWeight) * 100`, these look like typos (should be 85/115). Logic is unlikely to behave as intended. -10. **Decimal vs string for `acbuAmount` in transfer** – src/services/transfer/transferService.ts: `acbuAmount` is passed as a string to `prisma.transaction.create`. Prisma expects `Decimal` for that field; this can cause type or precision issues. -11. **No dead-letter queues** – src/config/rabbitmq.ts: Queues are declared without `deadLetterExchange` or `deadLetterRoutingKey`. Messages that repeatedly fail are requeued forever instead of being moved to a DLQ. -12. **No max retry limit for webhook consumer** – src/jobs/webhookConsumer.ts: Failed webhooks are nacked with requeue indefinitely. There is no per-message retry cap. - -## Medium - -13. **Wallet activation uses XLM, not Pi** – src/services/wallet/walletActivationService.ts: Sends XLM to activate Stellar accounts; comment says "using pi instead of xlm" is desired. Switch to Pi when the Pi bridge/chain is in use. -14. **Remove placeholder Stellar address in auth** – src/services/auth/authService.ts: Uses `getPlaceholderStellarAddress()` on signup to satisfy schema before the real wallet exists. Replace with a proper flow. -15. **Transfer service: verify alias-based flow** – Transfer supports sending by username/phone (via recipientResolver); confirm the current resolve-to-Stellar flow is correct, secure, and documented. -16. **Contract client and Stellar fees hardcoded** – src/services/stellar/contractClient.ts defaults fee to `'100'`; walletActivationService.ts and transferService.ts also use hardcoded `'100'`. Make fees configurable or network-derived. -17. **Multiple fintech providers per nation** – Only Flutterwave is wired in webhookRoutes; add support for multiple providers per country (e.g. Nigeria: Paystack and Flutterwave) to avoid single point of failure. -18. **KYC extraction placeholder** – src/services/kyc/machineLayer.ts: Uses a placeholder for "real impl would call Vision API and redact"; replace with actual implementation. -19. **Basket weight script missing** – Create a backend/tooling script or job that computes and reports current basket weights based on market conditions and factors considered. -20. **Balance endpoint for frontend missing** – Frontend shows balance as placeholder "β€”" everywhere; add or expose GET /users/me/balance so the UI can display real balances. -21. **No dependency health checks** – /health/metrics does not verify PostgreSQL, MongoDB, or RabbitMQ. A "healthy" response can be returned even when core dependencies are down. -22. **Race condition in API key rate limiting** – src/middleware/rateLimiter.ts: The get-then-set flow for rate limit counters is not atomic. Concurrent requests can both pass the limit before either updates the cache. -23. **ReDoS risk in `deletePattern`** – src/utils/cache.ts: `new RegExp(pattern)` with user-controlled or unsanitized input can lead to ReDoS. Patterns should be validated or escaped. -24. **Webhook verification skipped when secret missing** – src/controllers/webhookController.ts: If `FLUTTERWAVE_WEBHOOK_SECRET` is unset, `verifyFlutterwaveSignature` calls `next()` and skips verification, allowing forged webhooks. -25. **Placeholder Stellar address format invalid** – src/services/auth/authService.ts: `getPlaceholderStellarAddress()` returns `'P' + ...` (56 chars). Stellar addresses start with `G`. This may violate schema or downstream validation. -26. **ACBU supply from DB, not chain** – src/services/reserve/ReserveTracker.ts: `getTotalAcbuSupply` uses transaction aggregates instead of on-chain supply. This can diverge from the real supply if there are off-chain mints/burns. -27. **Org-scoped limits may not apply** – src/services/limits/limitsService.ts: For org-level API keys (`userId` null), transactions created with `userId: null` have no user relation, so they are excluded from limit aggregates. -28. **USDC to XLM conversion is a no-op** – src/jobs/usdcConvertAndMintJob.ts: `convertUsdcToXlm` is empty. USDC is never converted; the job still mints ACBU as if conversion succeeded. -29. **Hardcoded XLM rate** – src/jobs/xlmToAcbuJob.ts: `XLM_USD_RATE` defaults to `0.2` from env. A wrong or stale rate can cause incorrect ACBU mint amounts. -30. **Deposit limits use wrong USD equivalent** – src/controllers/mintController.ts: `amountUsdPlaceholder = amountNum` treats local currency amount as USD for limit checks. Limits should use a proper USD conversion. -31. **Incomplete treasury aggregation** – src/controllers/governmentController.ts: TODO: aggregate from transactions for org/user and reserve exposure. `byCurrency` is always `[]`. -32. **Audit failures are silent** – src/services/audit/auditService.ts: Audit write failures are only logged. There is no retry, alerting, or fallback, so audit data can be lost without visibility. - -## Low - -33. **`any` cast for permissions** – src/middleware/auth.ts: `permissions: permissions as any` bypasses type safety. Use a proper type. -34. **`any` usage in segmentGuard** – src/middleware/segmentGuard.ts: `(req as any).userTier` and `tier as any` weaken type safety. -35. **`any` usage in Stellar client** – src/services/stellar/client.ts: `submitTransaction(transaction: any)` and `(b: any)` in balance lookups. -36. **`any` cast in burning service** – src/services/contracts/acbuBurning.service.ts: `localAmounts as any[]` bypasses type checking. -37. **No pagination on transfers** – src/controllers/transferController.ts: `getTransfers` uses `take: 50` with no `skip` or cursor. No way to page through older transfers. -38. **No pagination on investment withdrawals** – src/controllers/investmentController.ts: `getInvestmentWithdrawRequests` uses `take: 50` with no pagination parameters. -39. **`limit` query param not validated** – src/controllers/governmentController.ts: `Number(req.query.limit)` can be `NaN`; `Math.max(1, NaN)` is `NaN`. -40. **No input validation in savings controller** – src/controllers/savingsController.ts: `amount`, `term_seconds`, and `user` are not validated with Zod or similar. Invalid values can reach the service layer. -41. **Missing composite index** – prisma/schema.prisma: `OnRampSwap` is often queried by `(status, createdAt)`. A composite index would help those queries. -42. **Webhook JSON parse failure returns empty body** – src/index.ts: If `raw.toString()` is invalid JSON, `body` is set to `{}`. The webhook handler may run with incomplete data instead of returning 400. -43. **Stack traces in error logs** – src/errorHandler.ts: `logger.error('Unexpected error', { error: err, ... })` can log full error objects. Ensure logs are not exposed to clients and that PII is not included. - -## Trivial - -44. **`(prisma as any).onRampSwap` casts** – src/jobs/usdcConvertAndMintJob.ts, xlmToAcbuJob.ts, mintController.ts, onrampController.ts: Prisma client is cast to `any` to access `onRampSwap`. Run `npx prisma generate` and fix the schema/client so `onRampSwap` is properly typed. -45. **Hardcoded limit values** – src/config/limits.ts: Deposit/withdrawal limits are hardcoded. Consider loading from env or config for easier tuning. -46. **Fallback email placeholder** – src/services/notification/notificationService.ts: `noreply@acbu.example.com` is a placeholder and may not be valid in production. -47. **Yield accounting stub** – src/services/investment/yieldAccountingService.ts: Placeholder only; yield accounting is not implemented. -48. **No automated tests** – No `*.test.ts` or `*.spec.ts` files found. Critical paths (auth, transfers, limits, webhooks) lack automated tests. -49. **Client-controlled mint recipient** – src/controllers/mintController.ts, onrampController.ts: Mint/on-ramp flows take a body-supplied Stellar address (`wallet_address` / `stellar_address`) and pass it to `mintFromUsdcInternal` as the chain recipient with no check that it matches the authenticated user's `User.stellarAddress`. A caller could direct minted ACBU to any address. -50. **Rate limiting fails open when MongoDB cache is down** – src/middleware/rateLimiter.ts, src/utils/cache.ts: `apiKeyRateLimiter` relies on Mongo-backed `cacheService`. On `get`/`set` errors the cache returns `null` or swallows failures, so under Mongo outages per-API-key limits do not accumulate, weakening abuse protection. -51. **Recovery unlock is single-factor** – src/services/recovery/recoveryService.ts: `POST /recovery/unlock` returns a new API key after only identifier + passcode β€” no OTP, email link, or second step. Weak or stolen passcodes enable account takeover. -52. **`requireMinTier` middleware is never used** – src/middleware/segmentGuard.ts: `requireMinTier` is defined but no route imports or applies it. The `User.tier` field is therefore not enforced at the middleware layer. -53. **`InvestmentWithdrawalRequest.organizationId` has no FK** – prisma/schema.prisma: `organizationId` is optional `String?` with no `Organization` relation or `@relation`. The DB cannot enforce that it references a real organization, enabling orphan rows. -54. **Unauthenticated reserve metrics on `/health/metrics`** – src/routes/index.ts: `GET /health/metrics` is not behind `validateApiKey`. It returns reserve ratio, overcollateralization ratio, and reserve health β€” operationally sensitive data β€” without authentication. -55. **Swagger UI exposed without auth** – src/index.ts: `app.use('/api-docs', swaggerUi.serve, …)` is mounted without API-key middleware, so the full OpenAPI surface is available for unauthenticated reconnaissance. -56. **Stellar event listener: broad stream and swallowed errors** – src/services/stellar/eventListener.ts: Listener walks global Horizon `effects()` (not scoped to known contract IDs), which is heavy and error-prone. Per-handler failures are logged and swallowed, so contract-dependent jobs can miss state with no retry or dead-letter. +_Last consolidated: June 2026. Source content merged from legacy `PROJECT/issues/BACKEND_ISSUES.md` list (56 items) and canonical [`issues/backend.md`](../../issues/backend.md) (75 items). Severity buckets and IDs fully aligned with canonical catalog; legacy 56-item list preserved as Section 7 with `(moved)` and `(new)` tags for traceability. New audit findings captured under `(new)` should be promoted to canonical IDs (`B-076+`) in a follow-up PR._ diff --git a/PROJECT/issues/CONTRACTS_ISSUES.md b/PROJECT/issues/CONTRACTS_ISSUES.md index e4e5d83..d495a83 100644 --- a/PROJECT/issues/CONTRACTS_ISSUES.md +++ b/PROJECT/issues/CONTRACTS_ISSUES.md @@ -1,54 +1,670 @@ -# Contracts – Known Issues +# Smart Contracts β€” Known Issues -Issues found in the smart contracts (acbu_minting, acbu_burning, acbu_oracle, acbu_reserve_tracker, acbu_savings_vault, acbu_lending_pool, acbu_escrow, shared). Add new items below as numbered list entries. +This document is the **single source of truth** for known issues in the ACBU Soroban smart contracts (`acbu_minting`, `acbu_burning`, `acbu_oracle`, `acbu_reserve_tracker`, `acbu_savings_vault`, `acbu_lending_pool`, `acbu_escrow`, plus the `shared` crate). + +> **How to use** +> +> - Each issue is structured as: **severity Β· area Β· evidence Β· impact Β· fix direction Β· acceptance check**. +> - Issues are ordered by severity, then by ID for stable cross-referencing. +> - Provenance: this file consolidates the legacy 34-item list previously at `PROJECT/issues/CONTRACTS_ISSUES.md` and the canonical 60-item catalog at `issues/contracts.md` into one document. Tracking duplicates the working catalog (`issues/contracts.md`) which remains the live triage-ready list; this file is the durable long-form reference. +> - When an item is fixed, prefer **linking the PR** in your tracker and removing the entry here rather than rewriting history. +> - **Severity legend:** πŸ”΄ Critical Β· 🟠 High Β· 🟑 Medium Β· 🟒 Low Β· βšͺ Trivial + +--- + +## 1. Summary Table + +| ID | Severity | Area | Contract / Crate | Title | +|----|----------|------|------------------|-------| +| C-001 | πŸ”΄ Critical | contracts/escrow | `acbu_escrow` | `release` missing authorization | +| C-002 | πŸ”΄ Critical | contracts/minting | `acbu_minting` | `mint_from_fiat` lacks off-chain settlement proof | +| C-003 | πŸ”΄ Critical | contracts/escrow | `acbu_escrow` | Escrow ID key collision | +| C-004 | πŸ”΄ Critical | contracts/reserves | `acbu_reserve_tracker` | `verify_reserves` uses wrong supply source | +| C-005 | πŸ”΄ Critical | contracts/savings | `acbu_savings_vault` | Missing `require_auth` on deposit/withdraw | +| C-006 | πŸ”΄ Critical | contracts/lending | `acbu_lending_pool` | Missing `require_auth` on deposit/withdraw | +| C-007 | 🟠 High | contracts/savings | `acbu_savings_vault` | Term not enforced | +| C-008 | 🟠 High | contracts/minting | `acbu_minting` | `mint_from_fiat` missing max mint bound | +| C-009 | 🟠 High | contracts/burning | `acbu_burning` | Burn basket integer division dust loss | +| C-010 | 🟠 High | contracts/escrow | `acbu_escrow` | Escrow create lacks duplicate guard | +| C-011 | 🟠 High | contracts/minting | `acbu_minting` | Oracle/reserve clients loaded but not called | +| C-012 | 🟠 High | contracts/burning | `acbu_burning` | Oracle/reserve clients loaded but not called | +| C-021 | 🟠 High | contracts/integration | All | Event payload schema drift vs backend listeners | +| C-027 | 🟠 High | contracts/lending | `acbu_lending_pool` | Loan events never emitted / loan logic missing | +| C-028 | 🟠 High | contracts/qa | `acbu_minting/tests` | Missing tests for core mint flows | +| C-034 | 🟠 High | contracts/ops | Multiple | No contract versioning / upgrade story | +| C-035 | 🟠 High | contracts/governance | Mint/burn/savings/lending | Admin pause flags not verified on all state changes | +| C-037 | 🟠 High | contracts/security | All `*_amount` fields | Integer overflow review for all i128 money math | +| C-038 | 🟠 High | contracts/security | Mint/burn | Authorization invocation tree for cross-contract calls | +| C-039 | 🟠 High | contracts/minting | `acbu_minting` | Strict input validation for string IDs | +| C-040 | 🟠 High | contracts/tokenomics | Mint/burn/oracle | Decimal scaling consistency (7 decimals) | +| C-041 | 🟠 High | contracts/oracle | `acbu_oracle` | Staleness: max ledger age for rates | +| C-042 | 🟠 High | contracts/oracle | `acbu_oracle` | Minimum oracle sources for quorum | +| C-050 | 🟠 High | contracts/qa | `acbu_escrow/tests` | Integration tests: escrow full lifecycle | +| C-051 | 🟠 High | contracts/qa | `acbu_savings_vault/tests` | Integration tests: savings lock + interest | +| C-052 | 🟠 High | contracts/qa | `acbu_lending_pool/tests` | Integration tests: lending borrow/repay | +| C-055 | 🟠 High | contracts/authz | `acbu_minting` and others | Access control naming audit (`check_admin_or_user`) | +| C-013 | 🟑 Medium | contracts/build | Mint/burn/reserve tracker | Token WASM import uses zero SHA256 placeholder | +| C-014 | 🟑 Medium | contracts/escrow | `acbu_escrow` | Double `.unwrap().unwrap()` pattern in storage reads | +| C-015 | 🟑 Medium | contracts/savings | `acbu_savings_vault` | Double `.unwrap().unwrap()` pattern | +| C-016 | 🟑 Medium | contracts/lending | `acbu_lending_pool` | Double `.unwrap().unwrap()` pattern | +| C-017 | 🟑 Medium | contracts/oracle | `acbu_oracle` | Outlier detection is a no-op | +| C-018 | 🟑 Medium | contracts/oracle | `acbu_oracle/tests` | Oracle unit test assertion incorrect vs median behavior | +| C-020 | 🟑 Medium | contracts/shared | `shared` | `median` allocates via `to_vec()` in shared util | +| C-024 | 🟑 Medium | contracts/savings | `acbu_savings_vault` | Savings withdraw yield always zero | +| C-025 | 🟑 Medium | contracts/lending | `acbu_lending_pool` | Lending fee rate unused | +| C-026 | 🟑 Medium | contracts/savings | `acbu_savings_vault` | Savings fee rate unused | +| C-032 | 🟑 Medium | contracts/minting | `acbu_minting` | Predictable `transaction_id` in minting | +| C-033 | 🟑 Medium | contracts/reserves | `acbu_reserve_tracker` | Reserve tracker emits no events on updates | +| C-036 | 🟑 Medium | contracts/security | Token transfers inside contracts | Reentrancy class issues via token callbacks | +| C-043 | 🟑 Medium | contracts/governance | Admin-only methods | Emergency multisig for admin operations | +| C-044 | 🟑 Medium | contracts/qa | CI pipeline | Contract size/compute budget regression tests | +| C-046 | 🟑 Medium | contracts/security | Rust dependencies | `cargo audit` / supply chain monitoring | +| C-047 | 🟑 Medium | contracts/docs | `INTEGRATION.md` / `SMART_CONTRACT_SPEC` | Formalize invariants documentation per contract | +| C-048 | 🟑 Medium | contracts/ops | `dummy_token` etc. | Testnet vs mainnet feature flags in code | +| C-054 | 🟑 Medium | contracts/api-ux | All contracts | Consistent error codes for clients | +| C-056 | 🟑 Medium | contracts/token | Mint/burn pulling user tokens | Token allowance assumptions | +| C-057 | 🟑 Medium | contracts/burning | `acbu_burning` `burn_for_basket` | Validate recipient accounts non-empty + distinct | +| C-058 | 🟑 Medium | contracts/minting | Recipient address parameter | Mint path: validate recipient is a real account | +| C-059 | 🟑 Medium | contracts/reserves | `acbu_reserve_tracker` init | Initialization parameters validation | +| C-060 | 🟑 Medium | contracts/ops | `acbu_oracle` admin | Oracle admin rotation workflow | +| C-019 | 🟒 Low | contracts/burning | `acbu_burning` | Redundant DECIMALS multiply/divide in burn path | +| C-022 | 🟒 Low | contracts/fees | `acbu_savings_vault`, `acbu_lending_pool` | Magic number vs `BASIS_POINTS` constant inconsistency | +| C-023 | 🟒 Low | contracts/burning | `acbu_burning` events | Per-account BurnEvent fee reporting mismatch | +| C-029 | 🟒 Low | contracts/hygiene | `acbu_minting` | Unused import noise in minting crate | +| C-030 | 🟒 Low | contracts/burning | `acbu_burning` | Empty recipient list style / checks | +| C-031 | 🟒 Low | contracts/oracle | `acbu_oracle` | Oracle RateUpdateEvent validators empty | +| C-045 | 🟒 Low | contracts/devx | `acbu-smart-contract/Cargo.lock` | Deterministic builds: `Cargo.lock` policy | +| C-049 | 🟒 Low | contracts/build | Root `soroban_token_contract.wasm` | Wasm artifact in repo | +| C-053 | 🟒 Low | contracts/perf | Events with large vectors | Gas griefing: unbounded vectors in events | + +**Totals:** 6 Critical Β· 21 High Β· 24 Medium Β· 9 Low Β· **60 total catalog items** (all IDs from C-001 through C-060, fully aligned with `issues/contracts.md`). + +--- + +## 2. Severity Counts + +| Severity | Count | +|----------|-------| +| πŸ”΄ Critical | **6** (C-001 – C-006) | +| 🟠 High | **21** (C-007–C-012, C-021, C-027, C-028, C-034–C-042, C-050–C-052, C-055) | +| 🟑 Medium | **24** (C-013–C-018, C-020, C-024–C-026, C-032, C-033, C-036, C-043, C-044, C-046–C-048, C-054, C-056–C-060) | +| 🟒 Low | **9** (C-019, C-022, C-023, C-029–C-031, C-045, C-049, C-053) | +| **Total** | **60** | + +These six critical items are **ship-blockers**. They each enable direct loss of funds and must be remediated before the ACBU MVP can be deployed to Stellar mainnet. + +--- + +## 3. Critical Issues (πŸ”΄) β€” Ship Blockers + +### C-001 β€” Escrow `release` missing authorization +- **Area:** contracts/escrow +- **Evidence:** `acbu_escrow/src/lib.rs` (`release`) +- **Impact:** Any address can call `release(escrow_id)` and send funds held in escrow to the payee, enabling theft of any escrow created by an honest payer. +- **Fix direction:** Require payer (`payer.require_auth()`) or admin auth on `release` and `refund`; add explicit error for unauthorized caller. Maintain CEI ordering for token transfer. +- **Acceptance check:** Unauthorized `release` returns `ContractError::Unauthorized` at contract unit-test level; only payer or admin can settle an escrow. + +### C-002 β€” `mint_from_fiat` lacks off-chain settlement proof +- **Severity:** πŸ”΄ Critical +- **Area:** contracts/minting +- **Evidence:** `acbu_minting/src/lib.rs` (`mint_from_fiat`) +- **Impact:** `mint_from_fiat` does not validate `fintech_tx_id` or that an off-chain fiat deposit has actually settled. Combined with `check_admin_or_user` semantics, recipients can mint ACBU without backing. +- **Fix direction:** Require admin-only minting for the fiat path (or verified settlement proof object), enforce uniqueness of `fintech_tx_id` in storage to prevent replay, and add an integration test that mints from a known-good provider. +- **Acceptance check:** Cannot mint from `mint_from_fiat` without a valid (admin, fintech_tx_id) tuple; replay of an existing `fintech_tx_id` returns the original transaction id and never mints twice. + +### C-003 β€” Escrow ID key collision +- **Severity:** πŸ”΄ Critical +- **Area:** contracts/escrow +- **Evidence:** `acbu_escrow/src/lib.rs` storage keying +- **Impact:** Escrow keys are `(ESCROW, escrow_id)` only. Two payers using the same `escrow_id` cause the second `create` to overwrite the first, locking the first payer's funds in someone else's escrow. +- **Fix direction:** Include `payer` in the key tuple β€” `(ESCROW, payer, escrow_id)` β€” and reject duplicates with a domain error. +- **Acceptance check:** Property test confirms that two different payers calling `create` with the same `escrow_id` cannot collide and each can independently settle their escrow. + +### C-004 β€” `verify_reserves` uses wrong supply source +- **Severity:** πŸ”΄ Critical +- **Area:** contracts/reserves +- **Evidence:** `acbu_reserve_tracker/src/lib.rs` (`verify_reserves`) +- **Impact:** `verify_reserves` reads `acbu_client.balance(&env.current_contract_address())` as the total minted supply. The reserve tracker does not hold ACBU, so this is always zero, and the function returns `true` early. Reserve checks trivially pass on every mint. +- **Fix direction:** Read the authoritative token supply via the token contract’s `total_supply` (or another trusted source stored at init), then compute `reserves_total_usd / minted_supply` against `min_ratio_bps` / `target_ratio_bps`. +- **Acceptance check:** Unit test asserts `verify_reserves(true_min)` returns `false` when the contract is undercollateralized and `true` when it is overcollateralized. + +### C-005 β€” Savings vault missing `require_auth` on deposit/withdraw +- **Severity:** πŸ”΄ Critical +- **Area:** contracts/savings +- **Evidence:** `acbu_savings_vault/src/lib.rs` (deposit/withdraw) +- **Impact:** Anyone can call `withdraw(user=X, amount=Z)` and move X's funds, until the redeploy fixes auth. +- **Fix direction:** Add `user.require_auth()` to deposit and withdraw; emit explicit events; redeploy. +- **Acceptance check:** Withdraw requires the authenticated user’s address; non-owner calls fail with `ContractError::Unauthorized`. + +### C-006 β€” Lending pool missing `require_auth` on deposit/withdraw +- **Severity:** πŸ”΄ Critical +- **Area:** contracts/lending +- **Evidence:** `acbu_lending_pool/src/lib.rs` (deposit/withdraw) +- **Impact:** Anyone can call `withdraw(lender=X, amount=Y)` and drain X's lender position. +- **Fix direction:** Add `lender.require_auth()` to deposit, withdraw, and any other lender-only methods; redeploy. +- **Acceptance check:** Withdraw requires the authenticated lender’s address; non-lender calls fail with `ContractError::Unauthorized`. + +--- + +## 4. High Severity Issues (🟠) + +### C-007 β€” Savings term not enforced +- **Area:** contracts/savings Β· **Evidence:** `acbu_savings_vault/src/lib.rs` +- **Impact:** `term_seconds` is stored but never checked on withdraw. Users can deposit and withdraw immediately, breaking the savings product semantics. +- **Fix direction:** Compare `env.ledger().timestamp()` to deposit timestamp + `term_seconds`. Allow early withdrawal only with an explicit penalty applied via `calculate_fee` policy. +- **Acceptance check:** Time-manipulation tests in the Soroban harness show that withdrawing before the term either fails or pays the documented penalty. + +### C-008 β€” `mint_from_fiat` missing max mint bound +- **Area:** contracts/minting Β· **Evidence:** `acbu_minting/src/lib.rs` (`mint_from_fiat`) +- **Impact:** `mint_from_usdc` enforces `MAX_MINT_AMOUNT`, but `mint_from_fiat` only enforces `MIN_MINT_AMOUNT`, allowing unbounded minting. +- **Fix direction:** Mirror the `MAX_MINT_AMOUNT` constant check from `mint_from_usdc`; expose the bound through storage for admin updates. +- **Acceptance check:** A mint request of `MAX_MINT_AMOUNT + 1` fails with `ContractError::InvalidAmount`. + +### C-009 β€” Burn basket integer division dust loss +- **Area:** contracts/burning Β· **Evidence:** `acbu_burning/src/lib.rs` (`burn_for_basket`) +- **Impact:** `amount_per_account = acbu_after_fee / recipient_accounts.len()` truncates. With many recipients, dust accumulates to user loss or an accounting mismatch off-chain. +- **Fix direction:** Allocate the remainder to the last recipient (or to the treasury in a `DustReleased` event) such that `sum(recipient_amounts) + fee == input` within 1 stroop. +- **Acceptance check:** Fuzz test against randomized recipient counts; total reconciles to `acbu_after_fee + fee` exactly. + +### C-010 β€” Escrow create lacks duplicate guard +- **Area:** contracts/escrow Β· **Evidence:** `acbu_escrow/src/lib.rs` (`create`) +- **Impact:** Even with C-003 fixed (per-payer key), the same payer calling `create` twice with the same `escrow_id` will overwrite their own escrow and lock prior funds. +- **Fix direction:** Reject with `ContractError::AlreadyExists` if the escrow already exists in storage; or use a monotonic per-payer sequence instead of caller-supplied ids. +- **Acceptance check:** A second `create` from the same payer with the same id returns an explicit error and does not modify storage. + +### C-011 β€” Minting loads oracle/reserve clients but does not call +- **Area:** contracts/minting Β· **Evidence:** `acbu_minting/src/lib.rs` +- **Impact:** `oracle` and `reserve_tracker` addresses are stored at init, but rates are hardcoded to 1:1 and reserve checks are skipped. Economic design is bypassed. +- **Fix direction:** Invoke the oracle to fetch the basket-weighted ACBU/USD rate; call `reserve_tracker.get_reserve_ratio()` before minting; reject if below `min_ratio_bps` or if oracle rate is stale beyond `UPDATE_INTERVAL_SECONDS`. +- **Acceptance check:** Mint fails when oracle is stale beyond threshold or when reserves are below the minimum. + +### C-012 β€” Burning loads oracle/reserve clients but does not call +- **Area:** contracts/burning Β· **Evidence:** `acbu_burning/src/lib.rs` +- **Impact:** Same as C-011 β€” oracle/reserve loaded but never invoked; burn FX rate is hardcoded. +- **Fix direction:** Use oracle medians + bounds checks before burn. Validate the redemption currency has sufficient reserves via `reserve_tracker.get_reserves()`. +- **Acceptance check:** Burn rejects out-of-range FX and insufficient reserves with explicit errors. + +### C-021 β€” Event payload schema drift vs backend listener +- **Area:** contracts/integration Β· **Evidence:** Mint/Burn events vs `acbu-backend` listener parsers +- **Impact:** Indexer silently drops events when field names, types, or decimals diverge between contract-emitted events and backend parser expectations. +- **Fix direction:** Generate a shared JSON schema or Rust types in a shared crate consumed by both contracts and the backend listener; cross-check fields in CI. +- **Acceptance check:** Golden vector tests across contracts and backend listeners pass against the same fixture. + +### C-027 β€” Loan events never emitted / loan logic missing +- **Area:** contracts/lending Β· **Evidence:** `acbu_lending_pool/src/lib.rs` +- **Impact:** `LoanCreatedEvent` and `RepaymentEvent` are defined but never emitted. Borrow/repay state transitions are not implemented on-chain. +- **Fix direction:** Implement `borrow(borrower, amount, interest_rate, duration_seconds)` and `repay(borrower, loan_id, amount)` with proper state machine; emit events at every transition. Coordinate with `acbu-lending-service` on backend consumption. +- **Acceptance check:** Loan lifecycle integration test (create β†’ accrue β†’ repay) emits every event in the right order with correct fields. + +### C-028 β€” Missing tests for core mint flows +- **Area:** contracts/qa Β· **Evidence:** `acbu_minting/tests/test.rs` coverage gaps +- **Impact:** Regressions in the most critical module are not caught; CI confidence in mint is low. +- **Fix direction:** Add unit + integration tests for `mint_from_usdc` and `mint_from_fiat` happy paths, error paths, and edge cases (zero, max, min, pause, oracle stale, reserves low). Enforce coverage threshold for `acbu_minting` in CI. +- **Acceptance check:** `cargo test -p acbu_minting` covers both flows; coverage gate set in CI. + +### C-034 β€” No contract versioning / upgrade story +- **Area:** contracts/ops Β· **Evidence:** Multiple crates +- **Impact:** No `version` field in storage, no documented migration playbook. Future upgrades cannot migrate user data safely. +- **Fix direction:** Add `version: u32` storage key at init; document upgrade + data migration playbook; pin invariant that user migration is opt-in and gated. +- **Acceptance check:** Upgrade runbook tested on testnet for at least one contract. + +### C-035 β€” Admin pause flags: verify on all state changes +- **Area:** contracts/governance Β· **Evidence:** Mint/burn/savings/lending +- **Impact:** Emergency pause may not cover all mutating entrypoints, leaving a window for continued mint/burn during an incident. +- **Fix direction:** Audit each `pub fn` for pause guard; add fuzz/property tests that paused contracts reject all mutating calls. +- **Acceptance check:** Fuzz test asserts no state change can occur while `paused=true`. + +### C-037 β€” Integer overflow review for all i128 money math +- **Area:** contracts/security Β· **Evidence:** All `*_amount` fields +- **Impact:** `i128` overflow can wrap in panic paths or produce incorrect comparisons downstream. +- **Fix direction:** Use checked math helpers in `shared`; explicit bounds on every multiplication; document per-field ranges. Add property tests covering extremes. +- **Acceptance check:** Property tests with extreme values never overflow silently. + +### C-038 β€” Authorization invocation tree for cross-contract calls +- **Area:** contracts/security Β· **Evidence:** Mint/burn calling token client +- **Impact:** Wrong invoker can break assumptions about who authorized a token transfer. +- **Fix direction:** Require auth on every external entrypoint; document the trust model in `INTEGRATION.md`; review token client call sites for `require_auth` propagation. +- **Acceptance check:** Threat model document updated and reviewed. + +### C-039 β€” Strict input validation for string IDs (`fintech_tx_id`) +- **Area:** contracts/minting Β· **Evidence:** `mint_from_fiat` parameters +- **Impact:** Garbage IDs pollute indexers, complicate partner reconciliation. +- **Fix direction:** Enforce length, charset, and uniqueness in storage; reject invalid ids at the contract boundary. +- **Acceptance check:** Invalid ids (oversize, non-printable, reused) are rejected with `ContractError::InvalidInput`. + +### C-040 β€” Decimal scaling consistency (7 decimals) across contracts +- **Area:** contracts/tokenomics Β· **Evidence:** Mint/burn/oracle +- **Impact:** Off-by-1e7 errors between contracts are catastrophic for users. +- **Fix direction:** Centralize `DECIMALS`, `stroop`, and unit-test helpers in `shared`; add cross-contract integration test for rounding. +- **Acceptance check:** Full mintβ†’burn round trip reconciles within 1 stroop. + +### C-041 β€” Oracle staleness: max ledger age for rates +- **Area:** contracts/oracle Β· **Evidence:** `acbu_oracle/src/lib.rs` +- **Impact:** Old rates may be used in volatile markets, enabling arbitrage. +- **Fix direction:** Reject rates older than `STALENESS_THRESHOLD_LEDGERS` unless an admin override flag is present. +- **Acceptance check:** A stale rate cannot be used for new mints. + +### C-042 β€” Minimum oracle sources for quorum +- **Area:** contracts/oracle Β· **Evidence:** `acbu_oracle/src/lib.rs` +- **Impact:** A single-source feed can be exploited to push the median. +- **Fix direction:** Require `sources.len() >= K` for an update to be accepted; reject updates with `K-1` or fewer sources. +- **Acceptance check:** Cannot update with one manipulated feed. + +### C-050 β€” Integration tests: escrow full lifecycle +- **Area:** contracts/qa Β· **Evidence:** `acbu_escrow/tests` (add) +- **Impact:** Regressions go undetected on the escrow fixes for C-001/C-003/C-010. +- **Fix direction:** End-to-end tests for create β†’ fund β†’ release/dispute/refund covering both happy and adversarial paths. +- **Acceptance check:** Property tests cover at least 8 distinct flows. + +### C-051 β€” Integration tests: savings vault lock + interest +- **Area:** contracts/qa Β· **Evidence:** `acbu_savings_vault/tests` (expand) +- **Impact:** Lock and yield logic regressions silently corrupt user balances. +- **Fix direction:** Time manipulation tests in Soroban harness; cover under-term, exactly-at-term, and post-term cases. +- **Acceptance check:** Withdraw before term fails (or pays penalty); after-term withdraw succeeds with yield event matching spec. + +### C-052 β€” Integration tests: lending borrow/repay +- **Area:** contracts/qa Β· **Evidence:** `acbu_lending_pool/tests` (expand) +- **Impact:** Lending is high risk; needs coverage of liquidation if applicable. +- **Fix direction:** Cover borrow β†’ accrue β†’ repay β†’ default scenarios; verify liquidation behavior per spec. +- **Acceptance check:** Loan default scenario matches documented behavior. + +### C-055 β€” Access control naming audit (`check_admin_or_user`) +- **Area:** contracts/authz Β· **Evidence:** Minting and other modules +- **Impact:** Misnamed helper may end up being too permissive; unintuitive caller set. +- **Fix direction:** Rename to a name that reflects actual semantics; add unit tests that cover all negative authorization cases. +- **Acceptance check:** Negative authorization tests catch every disallowed caller pattern in CI. + +## 5. Medium Severity Issues (🟑) + +### C-013 β€” Token WASM import uses zero SHA256 placeholder +- **Area:** contracts/build Β· **Evidence:** Mint/burn/reserve tracker token imports +- **Impact:** Integrity unverified; supply chain risk in production builds. +- **Fix direction:** Pin real wasm hash for `soroban_token_contract.wasm`; verify in CI against the deployed Stellar Asset Contract (SAC) artifact. +- **Acceptance check:** Build fails if the hash does not match the artifact. + +### C-014 β€” Double `.unwrap().unwrap()` pattern in escrow storage reads +- **Area:** contracts/escrow Β· **Evidence:** `acbu_escrow/src/lib.rs` +- **Impact:** Potential panics or undefined behavior if storage is malformed. +- **Fix direction:** Replace with a clean Option-handling helper that returns `ContractError::NotFound` on missing keys. +- **Acceptance check:** No `.unwrap().unwrap()` remains in production paths; missing storage returns a clean error. + +### C-015 β€” Double `.unwrap().unwrap()` pattern in savings vault +- **Area:** contracts/savings Β· **Evidence:** `acbu_savings_vault/src/lib.rs` +- **Impact:** Same as C-014. +- **Fix direction:** Replace with the same helper used in C-014. +- **Acceptance check:** No panic paths in normal operation; missing storage returns a structured error. + +### C-016 β€” Double `.unwrap().unwrap()` pattern in lending pool +- **Area:** contracts/lending Β· **Evidence:** `acbu_lending_pool/src/lib.rs` +- **Impact:** Same as C-014. +- **Fix direction:** Replace with the same helper used in C-014. +- **Acceptance check:** Consistent error handling across all three contracts. + +### C-017 β€” Oracle outlier detection is a no-op +- **Area:** contracts/oracle Β· **Evidence:** `acbu_oracle/src/lib.rs` +- **Impact:** Poisoned sources can move the median, undermining price integrity. +- **Fix direction:** Implement real outlier rejection or quarantine (e.g. `dropped_source` event) above `OUTLIER_THRESHOLD_BPS`. Emit an `OutlierDetected` event for downstream alerting. +- **Acceptance check:** Outlier source cannot move median beyond the configured bound; events emit on detection. + +### C-018 β€” Oracle unit test assertion incorrect vs median behavior +- **Area:** contracts/oracle Β· **Evidence:** `acbu_oracle/tests/test.rs` +- **Impact:** CI red or false confidence depending on changes. +- **Fix direction:** Fix the expected value to match `median(sources)`; add tests for multiple fixtures. +- **Acceptance check:** `cargo test -p acbu_oracle` passes reliably across all median cases. + +### C-020 β€” `median` allocates via `to_vec()` in shared util +- **Area:** contracts/shared Β· **Evidence:** `shared/src/lib.rs` (`median`) +- **Impact:** `no_std`/budget issues on Soroban. +- **Fix direction:** Replace with an in-place selection algorithm (`select_nth_unstable` style) within gas budget. +- **Acceptance check:** Contract budget measurements stay within limits on mainnet settings. + +### C-024 β€” Savings withdraw yield always zero +- **Area:** contracts/savings Β· **Evidence:** `acbu_savings_vault/src/lib.rs` (`WithdrawEvent`) +- **Impact:** Cannot build yield product UX honestly. +- **Fix direction:** Either implement a real accrual model or remove yield claims from the product spec. +- **Acceptance check:** Event `yield_amount` matches computed yield in tests; if accrual is removed, the field is deleted and the docs are updated. + +### C-025 β€” Lending fee rate unused +- **Area:** contracts/lending Β· **Evidence:** `acbu_lending_pool/src/lib.rs` +- **Impact:** Economic model incomplete. +- **Fix direction:** Apply fees on interest/repayments as designed; emit fee events. +- **Acceptance check:** Fee events are non-zero in the happy path; off-chain reconciliation agrees. + +### C-026 β€” Savings fee rate unused +- **Area:** contracts/savings Β· **Evidence:** `acbu_savings_vault/src/lib.rs` +- **Impact:** Same as C-025 for savings deposits. +- **Fix direction:** Apply deposit/withdraw fee policy consistently. +- **Acceptance check:** Fee events reflected in balances and off-chain reporting. + +### C-032 β€” Predictable `transaction_id` in minting +- **Area:** contracts/minting Β· **Evidence:** `acbu_minting/src/lib.rs` +- **Impact:** Correlation attacks and partner id collisions become trivial. +- **Fix direction:** Compute `transaction_id = sha256(user || amount || external_ref || ledger_seq)` (or similar), and ensure uniqueness via storage or sequence counter. +- **Acceptance check:** Generated IDs are globally unique with high probability; fuzz test confirms no collision over a million samples. + +### C-033 β€” Reserve tracker emits no events on updates +- **Area:** contracts/reserves Β· **Evidence:** `acbu_reserve_tracker/src/lib.rs` +- **Impact:** Harder to index reserve history for transparency dashboards. +- **Fix direction:** Emit `ReserveUpdateEvent` on every `update_reserve` call. +- **Acceptance check:** Listener can reconstruct complete reserve timeline from emitted events. + +### C-036 β€” Reentrancy class issues via token callbacks +- **Area:** contracts/security Β· **Evidence:** Token transfers inside contracts +- **Impact:** Soroban reentrancy differs from EVM but token contract behavior still needs audit. +- **Fix direction:** Verify CEI ordering; document any reliance on token contract non-reentrancy; check Soroban SDK guarantees. +- **Acceptance check:** External reviewer audit notes checked in. + +### C-043 β€” Emergency multisig for admin operations +- **Area:** contracts/governance Β· **Evidence:** Admin-only methods +- **Impact:** Single key compromise is catastrophic. +- **Fix direction:** Use Soroban auth pattern with M-of-N multisig threshold; harden against single-point compromise. +- **Acceptance check:** Admin action requires configured M-of-N in tests. + +### C-044 β€” Contract size/compute budget regression tests +- **Area:** contracts/qa Β· **Evidence:** CI pipeline +- **Impact:** Near-limit contracts fail after small edits. +- **Fix direction:** Track contract size and compute budget metrics in CI; alert on regression beyond threshold. +- **Acceptance check:** CI fails the PR if budget exceeds threshold. + +### C-046 β€” `cargo audit` / supply chain monitoring +- **Area:** contracts/security Β· **Evidence:** Rust dependencies +- **Impact:** Transitive CVEs in Soroban ecosystem. +- **Fix direction:** Add audit step in CI; pin critical dependencies. +- **Acceptance check:** CI fails on critical advisories (or documented waiver). + +### C-047 β€” Formalize invariants documentation per contract +- **Area:** contracts/docs Β· **Evidence:** `INTEGRATION.md` / `SMART_CONTRACT_SPEC` +- **Impact:** Auditors slow; engineers drift. +- **Fix direction:** Add `INVARIANTS.md` per crate linking each public fn to its pre/post conditions; review in PRs. +- **Acceptance check:** Each public fn lists pre/post conditions in code comments or invariants doc. + +### C-048 β€” Testnet vs mainnet feature flags in code +- **Area:** contracts/ops Β· **Evidence:** `dummy_token` etc. +- **Impact:** Accidental mainnet deploy of test code paths. +- **Fix direction:** Compile-time features or explicit network checks at init; mainnet builds must not reference test-only tokens. +- **Acceptance check:** Mainnet build cannot reference dummy token. + +### C-054 β€” Consistent error codes for clients +- **Area:** contracts/api-ux Β· **Evidence:** All contracts +- **Impact:** Clients cannot map errors to UX flows. +- **Fix direction:** Standard `ContractError` with stable discriminants across the workspace; document in OpenAPI/Soroban spec. +- **Acceptance check:** Spec lists error codes and clients can map them to user-facing messages. + +### C-056 β€” Token allowance assumptions if using allowance +- **Area:** contracts/token Β· **Evidence:** Mint/burn pulling user tokens +- **Impact:** Wrong allowance patterns block users or allow drains. +- **Fix direction:** Document pull vs push model; align with frontend allowance UX; add matrix in `INTEGRATION.md`. +- **Acceptance check:** Manual test matrix for allowances passes for at least the three flows mint, burn, savings deposit. + +### C-057 β€” Burn path: validate recipient accounts non-empty + distinct +- **Area:** contracts/burning Β· **Evidence:** `burn_for_basket` recipients +- **Impact:** Duplicate recipients could double-pay off-chain mapping. +- **Fix direction:** Enforce non-empty and deduplicate (or reject duplicates). +- **Acceptance check:** Duplicates rejected deterministically and emit a clear error. + +### C-058 β€” Mint path: validate recipient is a real account +- **Area:** contracts/minting Β· **Evidence:** Recipient address parameter +- **Impact:** Minting to contract-only addresses strands ACBU. +- **Fix direction:** Optional claim flow (mint to claim PDA redeemed by user) or accounts allowlist pattern. +- **Acceptance check:** Stranded mint rate is negligible / documented. + +### C-059 β€” Reserve tracker initialization parameters validation +- **Area:** contracts/reserves Β· **Evidence:** `acbu_reserve_tracker` init +- **Impact:** Bad init can brick the policy. +- **Fix direction:** Validate ranges (`min_ratio < target_ratio < BASIS_POINTS`); freeze critical params after launch. +- **Acceptance check:** Init cannot set impossible targets. + +### C-060 β€” Oracle admin rotation workflow +- **Area:** contracts/ops Β· **Evidence:** `acbu_oracle` admin +- **Impact:** Lost admin key bricks updates. +- **Fix direction:** Two-step transfer + timelock. +- **Acceptance check:** Rotation runbook tested on testnet. + +--- + +## 6. Low Severity Issues (🟒) + +### C-019 β€” Redundant DECIMALS multiply/divide in burn path +- **Area:** contracts/burning Β· **Evidence:** `acbu_burning/src/lib.rs` +- **Impact:** Noise / audit confusion. +- **Fix direction:** Simplify; keep explicit where needed for readability. +- **Acceptance check:** Diff is purely refactor with identical behavior under fuzz. + +### C-022 β€” Magic number vs `BASIS_POINTS` constant inconsistency +- **Area:** contracts/fees Β· **Evidence:** `acbu_savings_vault`, `acbu_lending_pool` +- **Impact:** Fee math harder to audit consistently. +- **Fix direction:** Import `shared::BASIS_POINTS` everywhere. +- **Acceptance check:** Grep shows single `BASIS_POINTS` definition. + +### C-023 β€” Per-account BurnEvent fee reporting mismatch +- **Area:** contracts/burning Β· **Evidence:** `acbu_burning/src/lib.rs` events +- **Impact:** Off-chain accounting reconciliation breaks. +- **Fix direction:** Emit totals + per-recipient net amounts consistently so the indexer can reconcile to the original input. +- **Acceptance check:** Indexer balances to events within 1 stroop. + +### C-029 β€” Unused import noise in minting crate +- **Area:** contracts/hygiene Β· **Evidence:** `acbu_minting/src/lib.rs` +- **Impact:** Clutter. +- **Fix direction:** Remove unused imports; add `cargo clippy --deny warnings` in CI. +- **Acceptance check:** `cargo clippy` clean. + +### C-030 β€” Empty recipient list style / checks +- **Area:** contracts/burning Β· **Evidence:** `acbu_burning/src/lib.rs` +- **Impact:** Minor readability. +- **Fix direction:** Use `is_empty()`; add explicit error. +- **Acceptance check:** Clear error when recipients empty. + +### C-031 β€” Oracle RateUpdateEvent validators empty +- **Area:** contracts/oracle Β· **Evidence:** `acbu_oracle/src/lib.rs` +- **Impact:** Downstream cannot audit which validators contributed. +- **Fix direction:** Populate validator set in event from the active signers for that update. +- **Acceptance check:** Event includes non-empty validator metadata. + +### C-045 β€” Deterministic builds: `Cargo.lock` policy +- **Area:** contracts/devx Β· **Evidence:** `acbu-smart-contract/Cargo.lock` +- **Impact:** Non-reproducible deployments. +- **Fix direction:** CI verifies `cargo build --locked`. +- **Acceptance check:** Locked builds pass in CI. + +### C-049 β€” Wasm artifact `soroban_token_contract.wasm` in repo +- **Area:** contracts/build Β· **Evidence:** Root `soroban_token_contract.wasm` +- **Impact:** Binary blobs in git complicate review. +- **Fix direction:** Store as release artifacts; pin hash in source. +- **Acceptance check:** Repo size reduced (or documented policy inline). + +### C-053 β€” Gas griefing: unbounded vectors in events +- **Area:** contracts/perf Β· **Evidence:** Events with large vectors +- **Impact:** Large transactions fail or become expensive. +- **Fix direction:** Cap `recipients.len()` per call; paginate off-chain with a follow-up event for continuation flows. +- **Acceptance check:** Maximum recipients enforced with clear error. + +--- + +## 7. Legacy 34-Item Origin List + +The following list was previously the only contents of this file. Items are kept here as a stable cross-reference; their detailed entries are in the sections above (or in `issues/contracts.md` if explicitly listed there). Items marked **(moved)** have been superseded by the equivalent entry in the detailed catalog; items marked **(archived)** describe issues already considered and not requiring separate tracking. + +### Critical +1. **Missing access control on escrow `release`** – C-001 *(moved)* +2. **Unrestricted minting in `mint_from_fiat`** – C-002 *(moved)* +3. **Escrow ID collision** – C-003 *(moved)* +4. **Incorrect total supply in `verify_reserves`** – C-004 *(moved)* +5. **Missing auth checks in savings vault** – C-005 *(moved)* +6. **Missing auth checks in lending pool** – C-006 *(moved)* + +### High +7. **Term not enforced in savings vault** – C-007 *(moved)* +8. **No max amount check in `mint_from_fiat`** – C-008 *(moved)* +9. **Integer division truncation in `burn_for_basket`** – C-009 *(moved)* +10. **No duplicate escrow check** – C-010 *(moved)* +11. **Oracle and reserve tracker unused in minting** – C-011 *(moved)* +12. **Oracle and reserve tracker unused in burning** – C-012 *(moved)* + +### Medium +13. **Token WASM import uses zero SHA256** – C-013 *(moved)* +14. **Double `.unwrap().unwrap()` in escrow** – C-014 *(moved)* +15. **Double `.unwrap().unwrap()` in savings vault** – C-015 *(moved)* +16. **Double `.unwrap().unwrap()` in lending pool** – C-016 *(moved)* +17. **Outlier detection has no effect in oracle** – C-017 *(moved)* +18. **Incorrect oracle test assertion** – C-018 *(moved)* +19. **Redundant calculation in burning** – C-019 *(moved)* +20. **`median` uses `to_vec()` in no_std** – C-020 *(moved)* +21. **Contract events vs backend listeners** – C-021 *(moved)* + +### Low +22. **Magic number for fee cap in savings and lending** – C-022 *(moved)* +23. **Incorrect fee in per-account BurnEvent** – C-023 *(moved)* +24. **`yield_amount` always 0 in savings** – C-024 *(moved)* +25. **Fee rate stored but unused in lending pool** – C-025 *(moved)* +26. **Fee rate stored but unused in savings vault** – C-026 *(moved)* +27. **Loan events never emitted** – C-027 *(moved)* +28. **No tests for core minting flows** – C-028 *(moved)* + +### Trivial +29. **Unused import** in `acbu_minting/src/lib.rs` – C-029 *(moved)* +30. **`len() == 0` style** in `acbu_burning/src/lib.rs` – C-030 *(moved)* +31. **Empty validators in event** in `acbu_oracle/src/lib.rs` – C-031 *(moved)* +32. **Weak `transaction_id` generation** in `acbu_minting/src/lib.rs` – C-032 *(moved)* +33. **No events in reserve tracker** in `acbu_reserve_tracker/src/lib.rs` – C-033 *(moved)* +34. **No upgrade or versioning** across multiple contracts – C-034 *(moved)* + +--- + +## 8. Resolution Tracker (Fix Status) + +> **Format:** `ID Β· title Β· status Β· linked PR / commit Β· owner` +> +> **Forward-looking template.** This is a placeholder grid designed for the day a fix lands. Every item is currently 🟑 Open; reviewers should not read uniformity of status as stagnation. When a fix merges, the row flips to βœ… Fixed and a PR/commit link is added. Example anchors below show what a real fixed row looks like. + +| ID | Title | Status | Notes | +|----|-------|--------|-------| +| _(example)_ | _(example anchor: prior PR #14 closed issue #1)_ | βœ… Fixed (PR #14 merged) | Historical anchor β€” not a contracts item | +| C-001 | Escrow `release` missing authorization | 🟑 Open (tracked separately) | Blocked on auth refactor for C-005/C-006 | +| C-002 | `mint_from_fiat` lacks off-chain settlement proof | 🟑 Open | Admin-only path + uniqueness required | +| C-003 | Escrow ID key collision | 🟑 Open | Payer+id tuple planned | +| C-004 | `verify_reserves` uses wrong supply source | 🟑 Open | Read `total_supply` from token contract | +| C-005 | Savings vault missing `require_auth` | 🟑 Open | Redeploy gating MVP | +| C-006 | Lending pool missing `require_auth` | 🟑 Open | Redeploy gating MVP | +| C-007 | Term not enforced | 🟑 Open | Coupled to C-005 | +| C-008 | `mint_from_fiat` missing max bound | 🟑 Open | Coupled to C-002 | +| C-009 | Burn basket dust loss | 🟑 Open | Allocate remainder to last | +| C-010 | Escrow create duplicate guard | 🟑 Open | Coupled to C-003 | +| C-011 | Minting oracle/reserve unused | 🟑 Open | Rate fetching required | +| C-012 | Burning oracle/reserve unused | 🟑 Open | Rate fetching required | +| C-013 | WASM zero SHA256 | 🟑 Open | Pin hash in CI | +| C-014..C-016 | Double unwrap patterns | 🟑 Open | Standardize helper | +| C-017 | Oracle outlier detection no-op | 🟑 Open | Implement quarantine | +| C-018 | Oracle test assertion | 🟑 Open | Fix expected value | +| C-019 | Redundant DECIMALS | 🟒 Trivial | Refactor PR | +| C-020 | `median` to_vec in no_std | 🟑 Open | In-place algorithm | +| C-021 | Event schema drift | 🟑 Open | Shared types crate | +| C-022 | Magic number fees | 🟒 Trivial | Use shared constant | +| C-023 | Per-account BurnEvent fee | 🟑 Open | Reconcile events | +| C-024 | Yield always 0 | 🟑 Open | Accrual model | +| C-025 | Lending fee rate unused | 🟑 Open | Apply policy | +| C-026 | Savings fee rate unused | 🟑 Open | Apply policy | +| C-027 | Loan events missing | 🟑 Open | Implement borrow/repay | +| C-028 | No core mint tests | 🟑 Open | Coverage gate CI | +| C-029 | Unused import | 🟒 Trivial | Lint PR | +| C-030 | Len()==0 style | 🟒 Trivial | Refactor PR | +| C-031 | Empty validators in event | 🟒 Trivial | Fill in event | +| C-032 | Predictable transaction_id | 🟑 Open | Hash-based id | +| C-033 | Reserve tracker no events | 🟑 Open | Emit on update | +| C-034 | No versioning/upgrade | 🟑 Open | Runbook + storage | +| C-035 | Pause not all-encompassing | 🟑 Open | Audit + fuzz | +| C-036 | Reentrancy via token | 🟑 Open | External review | +| C-037 | i128 overflow | 🟑 Open | Checked math | +| C-038 | Auth invocation tree | 🟑 Open | Threat model doc | +| C-039 | fintech_tx_id validation | 🟑 Open | Reject garbage | +| C-040 | 7-decimals consistency | 🟑 Open | Centralize constant | +| C-041 | Oracle staleness threshold | 🟑 Open | Reject stale | +| C-042 | Min oracle sources | 🟑 Open | Quorum required | +| C-043 | Emergency multisig | 🟑 Open | M-of-N pattern | +| C-044 | Budget regression tests | 🟑 Open | CI metric | +| C-045 | Deterministic builds | 🟒 Trivial | `cargo build --locked` | +| C-046 | cargo audit | 🟑 Open | CI step | +| C-047 | INVARIANTS per contract | 🟑 Open | Doc per crate | +| C-048 | Testnet/mainnet flags | 🟑 Open | Compile-time | +| C-049 | WASM blob in repo | 🟒 Trivial | Release artifact | +| C-050..C-052 | End-to-end integration tests | 🟑 Open | Property tests | +| C-053 | Gas griefing | 🟒 Trivial | Cap + paginate | +| C-054 | Consistent error codes | 🟑 Open | Standardize enum | +| C-055 | Auth helper naming | 🟑 Open | Rename + tests | +| C-056 | Token allowance assumptions | 🟑 Open | Document matrix | +| C-057 | Recipient uniqueness | 🟑 Open | Reject duplicates | +| C-058 | Mint to real account | 🟑 Open | Claim flow | +| C-059 | Reserve init validation | 🟑 Open | Range checks | +| C-060 | Oracle admin rotation | 🟑 Open | Two-step + timelock | + +> **Status legend** β€” 🟒 Trivial/cosmetic Β· 🟑 Open (tracked) Β· πŸ”΅ In review Β· βœ… Fixed (PR linked) +> +> **Reminder when updating:** when an external PR closes an issue, replace `🟑 Open` with `βœ… Fixed` and append the merged PR link in the Notes column. Do not rewrite history; the row itself is the audit trail. --- -## Critical +## 9. Cross-Reference Map -1. **Missing access control on escrow `release`** – acbu_escrow/src/lib.rs: `release` has no auth check. Any address can call `release(escrow_id)` and send funds to the payee. -2. **Unrestricted minting in `mint_from_fiat`** – acbu_minting/src/lib.rs: `mint_from_fiat` does not validate `fintech_tx_id` or off-chain fiat deposits. With `check_admin_or_user`, the recipient can call it for themselves and mint ACBU without real fiat backing. -3. **Escrow ID collision** – acbu_escrow/src/lib.rs: Escrow keys use only `(ESCROW, escrow_id)`. Two payers can use the same `escrow_id`; the second create overwrites the first. The first payer's funds become unrecoverable. -4. **Incorrect total supply in `verify_reserves`** – acbu_reserve_tracker/src/lib.rs: `verify_reserves` uses `acbu_client.balance(&env.current_contract_address())` as total supply. The reserve tracker does not hold ACBU, so this is always 0. The function returns `true` early and reserve checks never actually run. -5. **Missing auth checks in savings vault** – acbu_savings_vault/src/lib.rs: `deposit` and `withdraw` lack `user.require_auth()`. Anyone can call `withdraw(user=X, term_seconds=Y, amount=Z)` and move funds without X's authorization. -6. **Missing auth checks in lending pool** – acbu_lending_pool/src/lib.rs: `deposit` and `withdraw` lack `lender.require_auth()`. Anyone can call `withdraw(lender=X, amount=Y)` and drain X's balance. +| Document | Relationship to this file | +|----------|---------------------------| +| [`../../issues/contracts.md`](../../issues/contracts.md) | **Live triage-ready catalog.** Same IDs and severities; entries are condensed for daily use. | +| [`../../issues/MASTER_INDEX.md`](../../issues/MASTER_INDEX.md) | Master index across backend/frontend/contracts backlogs. | +| [`../../issues/backend.md`](../../issues/backend.md) | Backend MVP issues (B-001..B-075). | +| [`../../issues/frontend.md`](../../issues/frontend.md) | Frontend MVP issues (F-001..F-065). | +| [`../SMART_CONTRACT_SPEC.MD`](../SMART_CONTRACT_SPEC.MD) | Detailed on-chain spec (interfaces, types, events, invariants, security model). | +| [`../../DOCS/SMART_CONTRACT_SPEC.MD`](../../DOCS/SMART_CONTRACT_SPEC.MD) | Spec doc used in MVPs; duplicates the contract-side spec. | +| [`../../DOCS/ARCHITECTURE_DIAGRAMS.MD`](../../DOCS/ARCHITECTURE_DIAGRAMS.MD) | Mint/burn/reserve/oracle diagrams. | +| [`../API_AND_CONTRACTS_REFERENCE.MD`](../API_AND_CONTRACTS_REFERENCE.MD) | Routes ↔ contracts mapping. | +| [`../../TECHNICAL/ARCHITECTURE.MD`](../../TECHNICAL/ARCHITECTURE.MD) | High-level architecture. | +| [`../../TECHNICAL/ORACLE_SYSTEM.MD`](../../TECHNICAL/ORACLE_SYSTEM.MD) | Oracle multi-source design context for C-011/012/017/018/041/042. | +| [`../../TECHNICAL/RESERVE_MANAGEMENT.MD`](../../TECHNICAL/RESERVE_MANAGEMENT.MD) | Reserve model context for C-004/C-059. | +| [`../../TECHNICAL/PRICING_MECHANISM.MD`](../../TECHNICAL/PRICING_MECHANISM.MD) | Pricing/basket-weight context for C-040/C-024/C-025/C-026. | +| [`../PROJECT_STRUCTURE.MD`](../PROJECT_STRUCTURE.MD) | Repo layout (where the contract source lives). | +| [`../TASK_BREAKDOWN.MD`](../TASK_BREAKDOWN.MD) | Stage-by-stage tasks (where fixes are scheduled). | +| [`../../DOCS/IMPLEMENTATION_PLAN.MD`](../../DOCS/IMPLEMENTATION_PLAN.MD) | Phase-by-phase remediation timeline. | +| [`../../correction.md`](../../correction.md) | Standing-list of additional items curated for fast triage. | -## High +--- -7. **Term not enforced in savings vault** – acbu_savings_vault/src/lib.rs: `term_seconds` is stored but never checked on withdrawal. Users can deposit and withdraw immediately; there is no lock period. -8. **No max amount check in `mint_from_fiat`** – acbu_minting/src/lib.rs: `mint_from_usdc` enforces `max_mint_amount`, but `mint_from_fiat` only checks `min_amount`, allowing unbounded minting. -9. **Integer division truncation in `burn_for_basket`** – acbu_burning/src/lib.rs: `amount_per_account = acbu_after_fee / (recipient_accounts.len() as i128)` truncates. With many recipients, dust is lost and never accounted for. -10. **No duplicate escrow check** – acbu_escrow/src/lib.rs: `create` does not check if `escrow_id` already exists. Overwriting an existing escrow can lock prior funds. -11. **Oracle and reserve tracker unused in minting** – acbu_minting/src/lib.rs: `oracle` and `reserve_tracker` are loaded but never called. Rates are hardcoded to 1:1 and reserve checks are skipped. -12. **Oracle and reserve tracker unused in burning** – acbu_burning/src/lib.rs: Same as minting β€” oracle and reserve tracker are loaded but not used; rates are hardcoded. +## 10. Top Remediations (ship-safety order) -## Medium +Order these fixes in the MVP launch runbook before deploying mainnet: -13. **Token WASM import uses zero SHA256** – In acbu_minting, acbu_burning, and acbu_reserve_tracker, `soroban_token_contract.wasm` is imported with `sha256 = "0x0000...0"`. This is a placeholder; production builds should use the real WASM hash for integrity and security. -14. **Double `.unwrap().unwrap()` in escrow** – acbu_escrow/src/lib.rs: Multiple storage reads use `.get(...).unwrap().unwrap()`. Storage `get` returns `Option`, so a single `.unwrap()` is expected. The second `.unwrap()` may panic or indicate a type mismatch. -15. **Double `.unwrap().unwrap()` in savings vault** – acbu_savings_vault/src/lib.rs: Same problematic double-unwrap pattern. -16. **Double `.unwrap().unwrap()` in lending pool** – acbu_lending_pool/src/lib.rs: Same pattern. -17. **Outlier detection has no effect in oracle** – acbu_oracle/src/lib.rs: Outliers are detected but only marked with a comment "Log outlier but continue with median". No logging, rejection, or alert occurs. -18. **Incorrect oracle test assertion** – acbu_oracle/tests/test.rs: Test expects `stored_rate == rate` (1234567), but the contract stores `median(sources)` (1235000). The test will fail. -19. **Redundant calculation in burning** – acbu_burning/src/lib.rs: `(acbu_after_fee * DECIMALS) / DECIMALS` is equivalent to `acbu_after_fee`; the multiplication and division cancel out. -20. **`median` uses `to_vec()` in no_std** – shared/src/lib.rs: `median` allocates with `values.to_vec()`. In `no_std` Soroban contracts this may not be available or may require `alloc`. -21. **Contract events vs backend listeners** – Verify that MintEvent and BurnEvent payloads (field names, types, decimals) match what the backend event listeners expect, to avoid parsing or indexing failures. +1. **C-001 / C-005 / C-006** β€” Auth on escrow/savings/lending (single PR or coordinated). +2. **C-002** β€” Mint only with verified settlement proof. +3. **C-003 / C-010** β€” Per-payer escrow keys + duplicate guard. +4. **C-004** β€” Real token-supply source in reserve tracker. +5. **C-011 / C-012** β€” Wire oracle + reserve checks into mint/burn. +6. **C-007 / C-024** β€” Term + yield honoring in savings. +7. **C-008** β€” Mirror mint bound on `mint_from_fiat`. +8. **C-009 / C-023** β€” Reconcile burn accounting + events. +9. **C-013** β€” Pin token WASM hash. +10. **C-014 / C-015 / C-016** β€” Replace double-unwrap helpers. +11. **C-017 / C-021** β€” Outlier enforcement + shared event schemas. +12. **C-027 / C-050 / C-051 / C-052** β€” Lifecycle + integration tests. -## Low +--- -22. **Magic number for fee cap in savings and lending** – acbu_savings_vault and acbu_lending_pool use `10_000` instead of `shared::BASIS_POINTS`, unlike minting and burning. -23. **Incorrect fee in per-account BurnEvent** – acbu_burning/src/lib.rs: Each `BurnEvent` uses `calculate_fee(amount_per_account, fee_rate)`, but the fee is taken from the total `acbu_amount`. Per-account fees in events don't match actual fee accounting. -24. **`yield_amount` always 0 in savings** – acbu_savings_vault/src/lib.rs: `WithdrawEvent` always sets `yield_amount: 0`, suggesting yield logic is not implemented. -25. **Fee rate stored but unused in lending pool** – acbu_lending_pool/src/lib.rs: `fee_rate` is stored during initialization but never applied to any operation. -26. **Fee rate stored but unused in savings vault** – acbu_savings_vault/src/lib.rs: Same β€” `fee_rate` is stored but not applied. -27. **Loan events never emitted** – acbu_lending_pool/src/lib.rs: `LoanCreatedEvent` and `RepaymentEvent` are defined but never emitted; lending/repayment logic appears missing. -28. **No tests for core minting flows** – acbu_minting/tests/test.rs: Tests cover init, pause, and fee rate, but not `mint_from_usdc` or `mint_from_fiat`. +## 11. Maintenance -## Trivial +- **New issues** β€” Add to `issues/contracts.md` first (canonical triage). Mirror to this file's Section 4–6 with the next stable ID. +- **Closed fixes** β€” Promote the fix to `βœ… Fixed (PR linked)` in Section 8 and link to the merged PR. +- **Audit-changing decisions** β€” Update Section 3 first (Critical) so reviewers see the most consequential revisions. +- **Cross-doc references** β€” Update `issues/MASTER_INDEX.md` summary counts when severities change. + +--- + +## Related Documents + +- [Live contracts issue catalog](../../issues/contracts.md) +- [Master backlog index](../../issues/MASTER_INDEX.md) +- [Backend issues](../../issues/backend.md) +- [Frontend issues](../../issues/frontend.md) +- [Smart contract spec (root copy)](../SMART_CONTRACT_SPEC.md) +- [Smart contract spec (DOCS copy)](../../DOCS/SMART_CONTRACT_SPEC.MD) +- [Project structure map](../PROJECT_STRUCTURE.md) +- [Architecture (TECHNICAL)](../../TECHNICAL/ARCHITECTURE.MD) +- [Oracle system design](../../TECHNICAL/ORACLE_SYSTEM.MD) + +--- -29. **Unused import** – acbu_minting/src/lib.rs: `Vec` is imported but not used. -30. **`len() == 0` style** – acbu_burning/src/lib.rs: `recipient_accounts.len() == 0` could be `recipient_accounts.is_empty()`. -31. **Empty validators in event** – acbu_oracle/src/lib.rs: `RateUpdateEvent` uses `validators: Vec::new(&env)` instead of the actual validator set. -32. **Weak `transaction_id` generation** – acbu_minting/src/lib.rs: `transaction_id` is `format!("mint_{}", ledger.sequence())`, which is predictable and not globally unique. -33. **No events in reserve tracker** – acbu_reserve_tracker/src/lib.rs: Reserve updates do not emit events, making off-chain tracking harder. -34. **No upgrade or versioning** – Multiple contracts: No version field or upgrade path; contracts cannot be migrated safely. +_Last consolidated: June 2026. Source content merged from legacy `PROJECT/issues/CONTRACTS_ISSUES.md` list (34 items) and canonical [`issues/contracts.md`](../../issues/contracts.md) (60 items). Severity buckets, IDs, and totals fully aligned with canonical catalog; legacy list is preserved in Section 7 for reference but every item now resolves to a canonical C-### ID._ diff --git a/PROJECT/issues/FRONTEND_ISSUES.md b/PROJECT/issues/FRONTEND_ISSUES.md index f264ad7..2f50b9c 100644 --- a/PROJECT/issues/FRONTEND_ISSUES.md +++ b/PROJECT/issues/FRONTEND_ISSUES.md @@ -1,124 +1,823 @@ -# Frontend – Known Issues +# Frontend β€” Known Issues -Issues found in the Next.js app and API usage. Add new items below as numbered list entries. +This document is the **single, durable long-form reference** for known issues in the ACBU frontend (`acbu-frontend`, Next.js 14 + React + Stellar SDK), covering all routes under `app/(app)/**` (segments), the auth surface (`app/(public)/auth/**`), settings, wallet, savings, lending, currency, bills, and the lib/api + lib/stellar + lib/wallet-storage + lib/auth surface. + +> **How to use** +> +> - Each issue is structured as: **severity Β· area Β· evidence Β· impact Β· fix direction Β· acceptance check**. +> - Items are ordered by severity (Critical β†’ Low), then by ID for stable cross-referencing. +> - Provenance: this file consolidates the legacy 77-item annotated list previously at `PROJECT/issues/FRONTEND_ISSUES.md` with the canonical 65-item triage-ready list at `issues/frontend.md`. The canonical list at `issues/frontend.md` remains the **live working backlog**; this file is the durable version-of-record. +> - Legacy items whose content maps to a canonical `F-###` ID are tagged **(moved)**; legacy items that go beyond the canonical list (newer findings) are tagged **(new)** and retained verbatim because they pre-date the canonical rewrite. +> - When an item is fixed, the corresponding entry is removed from the working lists (`issues/frontend.md`) and the resolution row flips to βœ… Fixed in Section 8 with a merged-PR link. +> - **Severity legend:** πŸ”΄ Critical Β· 🟠 High Β· 🟑 Medium Β· 🟒 Low + +--- + +## 1. Summary Table + +| ID | Severity | Area | Title (canonical) | +|----|----------|------|---------| +| F-001 | πŸ”΄ Critical | frontend/build | Next.js production build ignores TypeScript errors | +| F-002 | πŸ”΄ Critical | frontend/auth | API key stored in sessionStorage | +| F-003 | πŸ”΄ Critical | frontend/wallet | Passcode stored in sessionStorage for wallet decrypt path | +| F-004 | πŸ”΄ Critical | frontend/wallet | Wallet secret "encryption" is base64 obfuscation | +| F-005 | πŸ”΄ Critical | frontend/wallet | Plaintext wallet secret storage path exists | +| F-006 | 🟠 High | frontend/auth | 2FA challenge token in URL query | +| F-007 | 🟑 Medium | frontend/api | Dual Authorization + x-api-key headers | +| F-008 | 🟒 Low | frontend/api | Client-side global fetch timeout without abort sharing | +| F-009 | 🟑 Medium | frontend/security | CSRF cookie read without backend pairing guarantee | +| F-010 | 🟠 High | frontend/deps | Duplicate Stellar SDK dependencies | +| F-011 | 🟒 Low | frontend/devx | Package name still `my-v0-project` | +| F-012 | 🟑 Medium | frontend/qa | No `test` / `typecheck` scripts in frontend | +| F-013 | 🟑 Medium | frontend/devx | Both `package-lock.json` and `pnpm-lock.yaml` | +| F-014 | 🟠 High | frontend/ux-money | Send success dialog clears amount before dialog | +| F-015 | 🟠 High | frontend/feature | Savings deposit handler is console-only | +| F-016 | 🟑 Medium | frontend/feature | Savings New Goal button inert | +| F-017 | 🟑 Medium | frontend/feature | SME CTAs inert | +| F-018 | 🟠 High | frontend/feature | Lending application console-only | +| F-019 | 🟠 High | frontend/feature | Currency page simulates operations | +| F-020 | 🟠 High | frontend/feature | Bills payment console-only | +| F-021 | 🟠 High | frontend/wallet | Wallet confirm disabled / incomplete | +| F-022 | 🟠 High | frontend/settings | Security settings placeholder | +| F-023 | 🟠 High | frontend/mint | Mint page burn tab fake success | +| F-024 | 🟑 Medium | frontend/copy | AFK vs ACBU naming drift | +| F-025 | 🟠 High | frontend/data | Balances shown as em dash placeholders | +| F-026 | 🟑 Medium | frontend/data | Savings page mixes mock cards with API data | +| F-027 | 🟠 High | frontend/ux-risk | Savings withdraw recipient editable | +| F-028 | 🟑 Medium | frontend/mint | Hardcoded fees on mint UI | +| F-029 | 🟑 Medium | frontend/me | Me page KYC badge static | +| F-030 | 🟑 Medium | frontend/me | Me page hardcoded stats | +| F-031 | 🟒 Low | frontend/business | Business page hardcoded stats | +| F-032 | 🟑 Medium | frontend/nav | International/Gateway stubs | +| F-033 | 🟒 Low | frontend/support | Help page too thin | +| F-034 | 🟒 Low | frontend/auth | Sign-in label says Username only | +| F-035 | 🟒 Low | frontend/auth | Signup success query param ignored | +| F-036 | 🟑 Medium | frontend/transfers | Send note omitted from confirm + API | +| F-037 | 🟑 Medium | frontend/quality | Inconsistent API error UX across pages | +| F-038 | 🟑 Medium | frontend/reliability | Silent catch on send loads | +| F-039 | 🟑 Medium | frontend/reliability | No React error boundary | +| F-040 | 🟑 Medium | frontend/react | useEffect dependency hazards (send/contacts/guardians/kyc) | +| F-041 | 🟒 Low | frontend/savings | Savings deposit URI length filter too strict | +| F-042 | 🟒 Low | frontend/types | `as any` tabs in bills/currency | +| F-043 | 🟒 Low | frontend/hygiene | console.log in money handlers | +| F-044 | 🟒 Low | frontend/react | Unstable list keys (rates/mint) | +| F-045 | 🟒 Low | frontend/product | No i18n / localization | +| F-046 | 🟑 Medium | frontend/a11y | Accessibility: labels without htmlFor | +| F-047 | 🟑 Medium | frontend/a11y | Mobile nav icon-only links lack aria-label | +| F-048 | 🟒 Low | frontend/theme | Dark mode badge colors hardcoded light palette | +| F-049 | 🟒 Low | frontend/ux | Back navigation pattern inconsistent | +| F-050 | 🟑 Medium | frontend/forms | Burn form lacks strong validation | +| F-051 | 🟠 High | frontend/auth | Signup passcode minimum too weak | +| F-052 | 🟒 Low | frontend/forms | Send amount missing min attribute | +| F-053 | 🟒 Low | frontend/nav | Activity links wrong route target | +| F-054 | 🟒 Low | frontend/nav | Me page links 2FA to auth flow not settings | +| F-055 | 🟑 Medium | frontend/html | Nested interactive elements (Link in Button) | +| F-056 | 🟒 Low | frontend/formatting | Amount formatting inconsistent | +| F-057 | 🟒 Low | frontend/education | Reserves page lacks unit explanations | +| F-058 | 🟒 Low | frontend/formatting | Rates page number formatting | +| F-059 | 🟒 Low | frontend/formatting | Burn page currency display locale | +| F-060 | 🟒 Low | frontend/perf | Performance: send page callbacks not memoized | +| F-061 | 🟒 Low | frontend/compat | Clipboard API failure on HTTP | +| F-062 | 🟑 Medium | frontend/stellar | Stellar burning client uses `as any` on send response | +| F-063 | 🟠 High | frontend/config | Environment base URL misconfiguration footgun | +| F-064 | 🟑 Medium | frontend/security | No Content-Security-Policy for Next app | +| F-065 | 🟒 Low | frontend/security | No Subresource Integrity for third-party scripts (if any) | +| F-066 | 🟑 Medium | frontend/components | Inconsistent loading / empty state skeletons across pages | +| F-071 | 🟑 Medium | frontend/ux | Toast removal delay is ~17 minutes | +| F-076 | 🟠 High | frontend/api | Frontend request() helper has no default timeout | +| F-067 | 🟑 Medium | frontend/forms | Contact inputs have no maxLength | +| F-068 | 🟑 Medium | frontend/forms | Profile inputs have no maxLength or format validation | +| F-069 | 🟒 Low | frontend/perf | Icons recreated each render | +| F-070 | 🟒 Low | frontend/nav | Menu items use router.push instead of Link | +| F-072 | 🟒 Low | frontend/compat | Mobile detection treats "unknown" as desktop | +| F-073 | 🟒 Low | frontend/hygiene | Post-KYC upload navigation uses uncleaned setTimeout | +| F-074 | 🟑 Medium | frontend/auth | Silent username normalization on signup | +| F-075 | 🟒 Low | frontend/ux | Auto-fill heuristic requires length >= 56 | +| F-077 | 🟒 Low | frontend/nav | /p2p route is client-side redirect only | + +**Totals:** 5 Critical Β· 15 High Β· 28 Medium Β· 29 Low Β· **77 total catalog items** (all IDs `F-001` through `F-077`, fully aligned with `issues/frontend.md`). + +--- + +## 2. Severity Counts + +| Severity | Count | +|----------|-------| +| πŸ”΄ Critical | **5** (F-001–F-005) | +| 🟠 High | **15** (F-006, F-010, F-014, F-015, F-018–F-023, F-025, F-027, F-051, F-063, F-076) | +| 🟑 Medium | **28** (F-007, F-009, F-012, F-013, F-016, F-017, F-024, F-026, F-028–F-030, F-032, F-036–F-040, F-046, F-047, F-050, F-055, F-062, F-064, F-066, F-067, F-068, F-071, F-074) | +| 🟒 Low | **29** (F-008, F-011, F-031, F-033–F-035, F-041–F-045, F-048, F-049, F-052–F-054, F-056–F-061, F-065, F-069, F-070, F-072, F-073, F-075, F-077) | +| **Total** | **65** | + +--- + +## 3. Critical Issues (πŸ”΄) β€” Ship Blockers + +### F-001 β€” Next.js production build ignores TypeScript errors +- **Area:** frontend/build +- **Evidence:** `acbu-frontend/next.config.mjs` (`typescript.ignoreBuildErrors`) +- **Impact:** Type errors ship to prod β†’ runtime crashes and security holes. +- **Fix direction:** Set `ignoreBuildErrors: false`; fix surfaced errors; add `typecheck` CI. +- **Acceptance check:** `next build` fails on any TS error in CI. + +### F-002 β€” API key stored in sessionStorage +- **Area:** frontend/auth +- **Evidence:** `acbu-frontend/contexts/auth-context.tsx` +- **Impact:** XSS exfiltration steals long-lived API keys. +- **Fix direction:** Move to httpOnly cookie session or short-lived JWT + refresh; CSP strict. +- **Acceptance check:** No secrets in `sessionStorage` in prod build. + +### F-003 β€” Passcode stored in sessionStorage for wallet decrypt path +- **Area:** frontend/wallet +- **Evidence:** `acbu-frontend/lib/wallet-storage.ts` (`KEY_STORE_PASSPHRASE` via sessionStorage in `getWalletSecretAnyLocal`) +- **Impact:** Passcode + encrypted secret becomes trivially stealable together under XSS. +- **Fix direction:** Use WebCrypto + non-extractable keys; never store passcode in session storage. +- **Acceptance check:** Security review: passcode only in memory during operation. + +### F-004 β€” Wallet secret "encryption" is base64 obfuscation +- **Area:** frontend/wallet +- **Evidence:** `acbu-frontend/lib/wallet-storage.ts` (`encryptSecret`) +- **Impact:** Stellar secret key is not confidential at rest. +- **Fix direction:** Implement AES-GCM with PBKDF2/Argon2 derived keys; hardware wallet path for prod. +- **Acceptance check:** Pen test cannot recover secret from IndexedDB dump without passcode. + +### F-005 β€” Plaintext wallet secret storage path exists +- **Area:** frontend/wallet +- **Evidence:** `acbu-frontend/lib/wallet-storage.ts` (`storeWalletSecretLocalPlaintext`) +- **Impact:** Dev helper enables catastrophic prod misconfiguration. +- **Fix direction:** Strip from prod bundles behind `NODE_ENV === "development"` only; runtime assert. +- **Acceptance check:** Production build throws if plaintext store called. + +--- + +## 4. High Severity Issues (🟠) + +### F-006 β€” 2FA challenge token in URL query +- **Area:** frontend/auth +- **Evidence:** `acbu-frontend/app/auth/2fa/page.tsx` +- **Impact:** Token leaks via Referer headers, browser history, server logs. +- **Fix direction:** POST body one-time token or encrypted state cookie. +- **Acceptance check:** Token never appears in URL bar or server access logs. + +### F-010 β€” Duplicate Stellar SDK dependencies +- **Area:** frontend/deps +- **Evidence:** `acbu-frontend/package.json` (`@stellar/stellar-sdk` + `stellar-sdk`) +- **Impact:** Two major versions inflate bundle and cause subtle type/runtime mismatches. +- **Fix direction:** Remove legacy `stellar-sdk`; migrate imports. +- **Acceptance check:** Only one stellar sdk in lockfile. + +### F-014 β€” Send success dialog clears amount before dialog +- **Area:** frontend/ux-money +- **Evidence:** `acbu-frontend/app/(app)/send/page.tsx` +- **Impact:** Users cannot confirm how much was sent. +- **Fix direction:** Keep amount in state until dialog dismissed. +- **Acceptance check:** Visual regression or unit test asserts non-empty amount. + +### F-015 β€” Savings deposit handler is console-only +- **Area:** frontend/feature +- **Evidence:** `acbu-frontend/app/(app)/savings/page.tsx` +- **Impact:** Core savings MVP non-functional. +- **Fix direction:** Wire to `POST /savings/...` endpoints; handle errors. +- **Acceptance check:** Deposit creates pending/completed UI state from API. + +### F-018 β€” Lending application console-only +- **Area:** frontend/feature +- **Evidence:** `acbu-frontend/app/(app)/lending/page.tsx` +- **Impact:** No loan intake for MVP. +- **Fix direction:** Create lending application API + form. +- **Acceptance check:** Submitted application visible in admin/backoffice stub. + +### F-019 β€” Currency page simulates operations +- **Area:** frontend/feature +- **Evidence:** `acbu-frontend/app/(app)/currency/page.tsx` +- **Impact:** Users think trades executed. +- **Fix direction:** Replace `setTimeout` with real API calls + toasts. +- **Acceptance check:** Network tab shows POST to backend on confirm. + +### F-020 β€” Bills payment console-only +- **Area:** frontend/feature +- **Evidence:** `acbu-frontend/app/(app)/bills/page.tsx` +- **Impact:** Bill pay is fake. +- **Fix direction:** Integrate bills endpoints when ready; else gate feature flag off. +- **Acceptance check:** Feature flag off shows honest copy. + +### F-021 β€” Wallet confirm disabled / incomplete +- **Area:** frontend/wallet +- **Evidence:** `acbu-frontend/app/me/settings/wallet/page.tsx` +- **Impact:** Users cannot finish wallet setup. +- **Fix direction:** Complete confirm flow + backend sync. +- **Acceptance check:** E2E completes wallet activation. + +### F-022 β€” Security settings placeholder +- **Area:** frontend/settings +- **Evidence:** `acbu-frontend/app/me/settings/security/page.tsx` +- **Impact:** 2FA management missing for financial app. +- **Fix direction:** Wire rotate device, sessions list, API keys. +- **Acceptance check:** User can disable compromised sessions. + +### F-023 β€” Mint page burn tab fake success +- **Area:** frontend/mint +- **Evidence:** `acbu-frontend/app/(app)/mint/page.tsx` +- **Impact:** False confirmation risk. +- **Fix direction:** Call burn API or deep-link with params. +- **Acceptance check:** Burn tab hits network or routes to `/burn` with prefilled state. + +### F-025 β€” Balances shown as em dash placeholders +- **Area:** frontend/data +- **Evidence:** Home/mint/send/savings pages +- **Impact:** Users cannot trust balances for decisions. +- **Fix direction:** Use `/users/me/balance` (or equivalent) everywhere with skeletons. +- **Acceptance check:** No long-lived `β€”` placeholders in signed-in dashboard. + +### F-027 β€” Savings withdraw recipient editable +- **Area:** frontend/ux-risk +- **Evidence:** Withdraw flow +- **Impact:** User can fat-finger funds to wrong ID. +- **Fix direction:** Lock recipient to resolved user; explicit "change" flow. +- **Acceptance check:** Cannot submit withdraw to different ID without extra confirmation. + +### F-051 β€” Signup passcode minimum too weak +- **Area:** frontend/auth +- **Evidence:** `acbu-frontend/app/auth/signup/page.tsx` +- **Impact:** 4-digit passcode easy to brute locally. +- **Fix direction:** Align with backend policy; enforce entropy meter. +- **Acceptance check:** Cannot sign up with weak passcode in prod config. + +### F-063 β€” Environment base URL misconfiguration footgun +- **Area:** frontend/config +- **Evidence:** `acbu-frontend/lib/api/client.ts` error message +- **Impact:** POSTs hit Next.js and return 405 confusingly. +- **Fix direction:** Add startup runtime check in layout for missing `NEXT_PUBLIC_API_BASE_URL`. +- **Acceptance check:** Dev console shows loud misconfig banner if unset. + +### F-076 β€” Frontend `request()` helper has no default timeout +- **Area:** frontend/api +- **Evidence:** `acbu-frontend/lib/api/client.ts` `request()` (uses raw `fetch`, no `AbortController` / timeout) +- **Impact:** Hung requests spin forever; users see indefinite spinners; tabs accumulate fetch pile-up; mobile users hit memory pressure; retries amplify the issue; users may force-close during pending operations, losing state. +- **Fix direction:** Wrap `request()` with `AbortController` + configurable default timeout (e.g. 30 s); honor caller-supplied `signal` for cancellation; type timeouts as a distinct `RequestTimeoutError`. +- **Acceptance check:** Network-throttled test surfaces typed timeout error to UI with retry; integration test asserts `request()` aborts after configured deadline even if backend is unreachable. + +--- + +## 5. Medium Severity Issues (🟑) + +### F-007 β€” Dual Authorization + x-api-key headers +- **Area:** frontend/api Β· **Evidence:** `acbu-frontend/lib/api/client.ts` +- **Impact:** Redundant secret channels increase accidental logging by proxies. +- **Fix direction:** Send only one header per backend contract; document. +- **Acceptance check:** Proxy logs never contain duplicate secret lines. + +### F-009 β€” CSRF cookie read without backend pairing guarantee +- **Area:** frontend/security Β· **Evidence:** `acbu-frontend/lib/api/client.ts` (`getCsrfToken`) +- **Impact:** If backend never sets `XSRF-TOKEN`, code becomes dead. +- **Fix direction:** Align with backend double-submit cookie; remove if dead. +- **Acceptance check:** Cross-site POST cannot succeed against prod API. + +### F-012 β€” No `test` / `typecheck` scripts in frontend +- **Area:** frontend/qa Β· **Evidence:** `acbu-frontend/package.json` +- **Impact:** No default CI quality gate beyond lint. +- **Fix direction:** Add `pnpm typecheck` + Playwright smoke + component tests for money forms. +- **Acceptance check:** CI runs typecheck + smoke on PR. + +### F-013 β€” Both `package-lock.json` and `pnpm-lock.yaml` +- **Area:** frontend/devx Β· **Evidence:** `acbu-frontend/` lockfiles +- **Impact:** Dependency drift between contributors. +- **Fix direction:** Pick one package manager; delete the other lockfile. +- **Acceptance check:** Single lockfile policy enforced in CI. + +### F-016 β€” Savings New Goal button inert +- **Area:** frontend/feature Β· **Evidence:** `acbu-frontend/app/(app)/savings/page.tsx` +- **Impact:** Broken CTA erodes trust. +- **Fix direction:** Implement goal model or hide until ready. +- **Acceptance check:** Button either works or is absent. + +### F-017 β€” SME CTAs inert +- **Area:** frontend/feature Β· **Evidence:** `acbu-frontend/app/(app)/sme/page.tsx` +- **Impact:** Lead gen dead end. +- **Fix direction:** Hook to application API or remove. +- **Acceptance check:** Click leads to real flow or mailto with tracking. + +### F-024 β€” AFK vs ACBU naming drift +- **Area:** frontend/copy Β· **Evidence:** Multiple pages (`mint`, `send`, `home`) +- **Impact:** Confusing brand + reconciliation errors. +- **Fix direction:** Single glossary constant exported to UI. +- **Acceptance check:** Grep shows no `AFK` in user-visible prod strings (if policy is ACBU-only). + +### F-026 β€” Savings page mixes mock cards with API data +- **Area:** frontend/data Β· **Evidence:** `acbu-frontend/app/(app)/savings/page.tsx` +- **Impact:** Totals disagree with chain truth. +- **Fix direction:** Remove mock constants; derive from API only. +- **Acceptance check:** Totals equal sum(positions). + +### F-028 β€” Hardcoded fees on mint UI +- **Area:** frontend/mint Β· **Evidence:** `acbu-frontend/app/(app)/mint/page.tsx` +- **Impact:** Misleading fee β†’ compliance complaints. +- **Fix direction:** Fetch fee schedule from backend. +- **Acceptance check:** Fee matches backend quote within rounding. + +### F-029 β€” Me page KYC badge static +- **Area:** frontend/me Β· **Evidence:** `acbu-frontend/app/(app)/me/page.tsx` +- **Impact:** Users misjudge what they can do. +- **Fix direction:** Bind badge to KYC status endpoint. +- **Acceptance check:** Badge updates after KYC webhook simulation. + +### F-030 β€” Me page hardcoded stats +- **Area:** frontend/me Β· **Evidence:** `acbu-frontend/app/(app)/me/page.tsx` +- **Impact:** Trust-breaking mock wealth numbers. +- **Fix direction:** Replace with API-driven aggregates. +- **Acceptance check:** Stats match backend for seeded user. + +### F-032 β€” International/Gateway stubs +- **Area:** frontend/nav Β· **Evidence:** `international` + `gateway` pages +- **Impact:** Dead nav items frustrate users. +- **Fix direction:** Hide behind feature flags until backend ready. +- **Acceptance check:** Nav only lists shipped features. + +### F-036 β€” Send note omitted from confirm + API +- **Area:** frontend/transfers Β· **Evidence:** `acbu-frontend/app/(app)/send/page.tsx` +- **Impact:** Compliance reference lost for transfers. +- **Fix direction:** Plumb `note` to API + receipts. +- **Acceptance check:** PDF/email receipt shows note. + +### F-037 β€” Inconsistent API error UX across pages +- **Area:** frontend/quality Β· **Evidence:** Many `app/(app)/**` pages +- **Impact:** Users see generic failures on money errors. +- **Fix direction:** Shared `useApiError` hook mirroring burn page. +- **Acceptance check:** All money pages map 429/503/402 consistently. + +### F-038 β€” Silent catch on send loads +- **Area:** frontend/reliability Β· **Evidence:** `send/page.tsx` `.catch(() => {})` patterns +- **Impact:** Broken lists look empty vs errored. +- **Fix direction:** Surface toast + retry. +- **Acceptance check:** Failed load shows error state with retry. + +### F-039 β€” No React error boundary +- **Area:** frontend/reliability Β· **Evidence:** `acbu-frontend/app/layout.tsx` +- **Impact:** Single component error whitescreens app. +- **Fix direction:** Add route-level error boundaries + reporting. +- **Acceptance check:** Forced error shows fallback UI not blank page. + +### F-040 β€” useEffect dependency hazards (send/contacts/guardians/kyc) +- **Area:** frontend/react Β· **Evidence:** Multiple pages +- **Impact:** Stale data or infinite loops when deps fixed naively. +- **Fix direction:** Refactor to `useCallback` + exhaustive-deps lint as error. +- **Acceptance check:** ESLint react-hooks passes on money routes. + +### F-046 β€” Accessibility: labels without htmlFor +- **Area:** frontend/a11y Β· **Evidence:** Mint/send/burn/withdraw forms +- **Impact:** Screen reader users cannot operate forms reliably. +- **Fix direction:** Pair labels+ids; run axe in CI. +- **Acceptance check:** axe-core critical violations = 0 on money forms. + +### F-047 β€” Mobile nav icon-only links lack aria-label +- **Area:** frontend/a11y Β· **Evidence:** `acbu-frontend/components/mobile-nav.tsx` +- **Impact:** Navigation unusable via VoiceOver. +- **Fix direction:** Add concise `aria-label` per route. +- **Acceptance check:** VoiceOver reads destination per icon. + +### F-050 β€” Burn form lacks strong validation +- **Area:** frontend/forms Β· **Evidence:** `acbu-frontend/app/(app)/burn/page.tsx` +- **Impact:** Bad bank details fail late or confuse users. +- **Fix direction:** Client-side format hints + server-driven validation messages. +- **Acceptance check:** Invalid bank codes blocked before submit. + +### F-055 β€” Nested interactive elements (Link in Button) +- **Area:** frontend/html Β· **Evidence:** `send/page.tsx` Add Contact +- **Impact:** Invalid HTML confuses assistive tech + click targets. +- **Fix direction:** Use `Button asChild` + `Link` or style `Link` as button. +- **Acceptance check:** HTML validator clean for that control. + +### F-062 β€” Stellar burning client uses `as any` on send response +- **Area:** frontend/stellar Β· **Evidence:** `acbu-frontend/lib/stellar/burning.ts` +- **Impact:** Missed error branches hide failed txs. +- **Fix direction:** Type Soroban response properly; surface `errorResultXdr`. +- **Acceptance check:** Failed tx always shows human readable Soroban error. + +### F-064 β€” No Content-Security-Policy for Next app +- **Area:** frontend/security Β· **Evidence:** `next.config.mjs` headers (missing) +- **Impact:** XSS impact amplified when keys live in sessionStorage. +- **Fix direction:** Add strict CSP nonces via Next middleware. +- **Acceptance check:** CSP report-only phase then enforce. + +### F-066 β€” Inconsistent loading / empty state skeletons across pages +- **Area:** frontend/components +- **Evidence:** Per-page `loading.tsx` / `empty.tsx` wrappers across `app/(app)/**` +- **Impact:** Loading and empty states are inconsistent across pages (mix of spinners, dimmed text, blank space). Cognitive load + maintenance burden, plus a11y regressions (no shared `aria-busy` semantics). +- **Fix direction:** Introduce a single `` / `` shared primitive in `components/ui/`; replace ad-hoc loading/empty wrappers across `app/(app)/**` with this primitive. +- **Acceptance check:** All `app/(app)/**/loading.tsx` files import the shared ``; design QA spot-check shows visual consistency across send/home/mint/savings/currency; axe-core reports no missing busy-state violations. + +### F-071 β€” Toast removal delay is ~17 minutes +- **Area:** frontend/ux +- **Evidence:** `acbu-frontend/lib/toast.ts` constant `TOAST_REMOVE_DELAY = 1000000` +- **Impact:** Default toast auto-dismiss is ~17 minutes. Stale toasts pollute the toast log and may display transaction context that should have been cleared (privacy / shoulder-surfer risk in a financial app; trust issue for users who don’t understand why old toasts persist). +- **Fix direction:** Use a sane default dismiss window (e.g. 5–10 s); gate β€œsticky” toasts behind an explicit `sticky: true` option for in-progress actions. +- **Acceptance check:** Default toast auto-dismiss ≀ 10 s under default config; sticky / progress toasts continue to work for genuinely long-running operations (e.g. async Stellar deposit confirmation). + +### F-067 β€” Contact inputs have no `maxLength` +- **Area:** frontend/forms +- **Evidence:** `acbu-frontend/app/(app)/contacts/**` input fields +- **Impact:** Long names/paste content can overflow UI tabs and submission; backend may reject with confusing 400 mid-flow. +- **Fix direction:** Add `maxLength` per backend schema; show helpful character counter on long fields. +- **Acceptance check:** Pasting >100 chars is truncated at UI level; backend-aligned limits enforced everywhere; QA confirms no overflow on common pastes. + +### F-068 β€” Profile inputs have no `maxLength` or format validation +- **Area:** frontend/forms +- **Evidence:** `acbu-frontend/app/(app)/me/**` profile fields (bio, display name, contact details) +- **Impact:** Profile text fields ship unconstrained; backend truncates mid-submission with confusing errors. +- **Fix direction:** Add `maxLength` and format hints per backend schema; client-side email/phone regex gates; inline character counters. +- **Acceptance check:** Bio / display name fields enforce limits; invalid email/phone inputs blocked at UI before submit; UAT shows zero-format mismatch errors from backoffice. + +### F-074 β€” Silent username normalization on signup +- **Area:** frontend/auth +- **Evidence:** `acbu-frontend/lib/api/auth.ts` lowercases + strips whitespace without warning UI +- **Impact:** Users signing up with mixed-case emails silently land on lowercased versions; phishing-with-typos enrollment does not warn the user; subsequent login confusion. +- **Fix direction:** Surface normalization in UI (e.g. "We will store this as: johndoe@example.com") or refuse ambiguous capitalization with clear copy. +- **Acceptance check:** Signup flow either rejects mixed-case with copy explaining why, or shows normalized form before submit confirmation; QA verifies both surfaces are accessible. + +--- + +## 6. Low Severity Issues (🟒) + +### F-008 β€” Client-side global fetch timeout without abort sharing +- **Area:** frontend/api Β· **Evidence:** `acbu-frontend/lib/api/client.ts` (`AbortController` per call) +- **Impact:** Long hung requests waste battery; duplicated controllers if callers pass signal incorrectly. +- **Fix direction:** Document pattern; optional default timeout env. +- **Acceptance check:** Hung request cancels at configured deadline. + +### F-011 β€” Package name still `my-v0-project` +- **Area:** frontend/devx Β· **Evidence:** `acbu-frontend/package.json` (`name`) +- **Impact:** Confusing telemetry, npm metadata, support tickets. +- **Fix direction:** Rename to `acbu-frontend`. +- **Acceptance check:** Published docs reference correct package name. + +### F-031 β€” Business page hardcoded stats +- **Area:** frontend/business Β· **Evidence:** `acbu-frontend/app/(app)/business/page.tsx` +- **Impact:** Same trust issue for SMB story. +- **Fix direction:** Wire SME metrics API or mark demo mode clearly. +- **Acceptance check:** Demo mode banner when using mock numbers. + +### F-033 β€” Help page too thin +- **Area:** frontend/support Β· **Evidence:** `acbu-frontend/app/me/help/page.tsx` +- **Impact:** Support load spikes. +- **Fix direction:** FAQ + status page links + ticket form. +- **Acceptance check:** Users can self-serve top 10 questions. + +### F-034 β€” Sign-in label says Username only +- **Area:** frontend/auth Β· **Evidence:** `acbu-frontend/app/auth/signin/page.tsx` +- **Impact:** Users try wrong identifier format. +- **Fix direction:** Copy: username/email/phone per backend. +- **Acceptance check:** Support tickets about login drop in UAT. + +### F-035 β€” Signup success query param ignored +- **Area:** frontend/auth Β· **Evidence:** `acbu-frontend/app/auth/signin/page.tsx` +- **Impact:** Missed positive reinforcement. +- **Fix direction:** Toast/banner on `?created=1`. +- **Acceptance check:** New user sees confirmation once. + +### F-041 β€” Savings deposit URI length filter too strict +- **Area:** frontend/savings Β· **Evidence:** `acbu-frontend/app/(app)/savings/deposit/page.tsx` +- **Impact:** Blocks non-Stellar IDs incorrectly. +- **Fix direction:** Align validation with backend recipient resolver. +- **Acceptance check:** Phone-based IDs work where supported. + +### F-042 β€” `as any` tabs in bills/currency +- **Area:** frontend/types Β· **Evidence:** `bills/page.tsx`, `currency/page.tsx` +- **Impact:** Weak typing hides invalid tab states. +- **Fix direction:** Discriminated unions for tab state. +- **Acceptance check:** TS strict mode clean for those files. + +### F-043 β€” console.log in money handlers +- **Area:** frontend/hygiene Β· **Evidence:** Savings/bills/lending/currency pages +- **Impact:** Leaks PII to browser consoles in prod. +- **Fix direction:** Replace with structured logger behind `DEBUG` flag. +- **Acceptance check:** Prod bundle has no `console.log` in transfer paths. + +### F-044 β€” Unstable list keys (rates/mint) +- **Area:** frontend/react Β· **Evidence:** `rates/page.tsx`, `mint/page.tsx` +- **Impact:** UI state bugs on reorder. +- **Fix direction:** Use stable ids from API. +- **Acceptance check:** No `key={index}` for dynamic lists. + +### F-045 β€” No i18n / localization +- **Area:** frontend/product Β· **Evidence:** Whole app +- **Impact:** Africa MVP needs multi-locale formatting. +- **Fix direction:** Adopt next-intl or similar; extract strings. +- **Acceptance check:** At least NG/KE locale number formats supported. + +### F-048 β€” Dark mode badge colors hardcoded light palette +- **Area:** frontend/theme Β· **Evidence:** `send/page.tsx` status badges +- **Impact:** Poor contrast in dark theme. +- **Fix direction:** Use semantic tokens from design system. +- **Acceptance check:** WCAG AA contrast on badges in dark mode. + +### F-049 β€” Back navigation pattern inconsistent +- **Area:** frontend/ux Β· **Evidence:** Multiple pages mix `router.back` vs `Link` +- **Impact:** Users land on unexpected off-app pages. +- **Fix direction:** Prefer explicit `Link` targets within app shell. +- **Acceptance check:** Back never exits app shell unintentionally. + +### F-052 β€” Send amount missing min attribute +- **Area:** frontend/forms Β· **Evidence:** `send/page.tsx` +- **Impact:** Negative amounts via keyboard. +- **Fix direction:** `inputMode="decimal"` + `min` + backend validation mirror. +- **Acceptance check:** Negative amounts impossible in UI. + +### F-053 β€” Activity links wrong route target +- **Area:** frontend/nav Β· **Evidence:** `activity/page.tsx` +- **Impact:** Users open wrong detail screen. +- **Fix direction:** Link to canonical transaction detail route. +- **Acceptance check:** Deep link opens correct transfer id. + +### F-054 β€” Me page links 2FA to auth flow not settings +- **Area:** frontend/nav Β· **Evidence:** `me/page.tsx` +- **Impact:** Confusing security UX loop. +- **Fix direction:** Point to settings-first flow. +- **Acceptance check:** User manages 2FA without re-auth loop. + +### F-056 β€” Amount formatting inconsistent +- **Area:** frontend/formatting Β· **Evidence:** Home/transfers lists +- **Impact:** Users misread decimals especially for 7dp assets. +- **Fix direction:** Central `formatAcbu` helper with locale. +- **Acceptance check:** All lists use same formatter. + +### F-057 β€” Reserves page lacks unit explanations +- **Area:** frontend/education Β· **Evidence:** `reserves/page.tsx` +- **Impact:** Users misinterpret health metrics. +- **Fix direction:** Tooltips linking to docs `RESERVE_MANAGEMENT`. +- **Acceptance check:** User testing shows improved comprehension. + +### F-058 β€” Rates page number formatting +- **Area:** frontend/formatting Β· **Evidence:** `rates/page.tsx` +- **Impact:** Rates as raw strings hard to scan. +- **Fix direction:** Format with significant digits policy. +- **Acceptance check:** Rates match spec precision. + +### F-059 β€” Burn page currency display locale +- **Area:** frontend/formatting Β· **Evidence:** `burn/page.tsx` +- **Impact:** NGN formatting not locale-aware. +- **Fix direction:** Use `Intl.NumberFormat`. +- **Acceptance check:** Locale switch changes grouping separators. + +### F-060 β€” Performance: send page callbacks not memoized +- **Area:** frontend/perf Β· **Evidence:** `send/page.tsx` +- **Impact:** Extra rerenders on large contact lists. +- **Fix direction:** Memoize handlers + virtualize list. +- **Acceptance check:** React profiler shows acceptable commit times. + +### F-061 β€” Clipboard API failure on HTTP +- **Area:** frontend/compat Β· **Evidence:** `receive/page.tsx` +- **Impact:** Copy address fails silently on non-secure origins. +- **Fix direction:** Detect + fallback prompt. +- **Acceptance check:** Copy works on HTTPS and shows message on HTTP. + +### F-065 β€” No Subresource Integrity for third-party scripts (if any) +- **Area:** frontend/security Β· **Evidence:** `app/layout` / analytics +- **Impact:** Supply-chain risk if external scripts added. +- **Fix direction:** Prefer first-party hosting; SRI for CDNs. +- **Acceptance check:** Security review checklist item signed off. + +### F-069 β€” Icons recreated each render +- **Area:** frontend/perf Β· **Evidence:** `acbu-frontend/components/icons/**` declarative component instances +- **Impact:** Every render allocates a fresh icon subtree; unnecessary CPU + memory pressure on low-end devices. +- **Fix direction:** Memoize icon components (React.memo + useMemo) or precompute static SVGs as named exports; collapse duplicates. +- **Acceptance check:** React profiler shows no re-renders of unchanged icons under normal nav; Lighthouse perf unchanged or improved. + +### F-070 β€” Menu items use `router.push` instead of Link +- **Area:** frontend/nav Β· **Evidence:** `acbu-frontend/components/menu/.tsx` +- **Impact:** Imperative nav breaks browser back-button semantics; missing `` accessibility surface (VoiceOver miss); poor SEO. +- **Fix direction:** Use Next.js `` for internal destinations; reserve `router.push` only for code-driven navigation. +- **Acceptance check:** All visible menu items render ``; browser back button returns to prior menu state; VoiceOver reads destination name. + +### F-072 β€” Mobile detection treats "unknown" as desktop +- **Area:** frontend/compat Β· **Evidence:** `acbu-frontend/lib/useIsMobile.ts` (or similar) +- **Impact:** Unknown browsers / tablets render desktop UI; UX acceptable trade-off. +- **Fix direction:** Default to desktop when unknown; emit telemetry for fail-open detection so coverage improves over time. +- **Acceptance check:** Coverage telemetry reports unknown-browser counts in production; visual smoke test passes for desktop Chrome/Safari/Firefox. + +### F-073 β€” Post-KYC upload navigation uses uncleaned `setTimeout` +- **Area:** frontend/hygiene Β· **Evidence:** `acbu-frontend/app/auth/kyc/**` post-upload flow +- **Impact:** Timer may fire after user has navigated away, causing redundant/confusing state updates and duplicate analytics. +- **Fix direction:** Cancel timer on unmount; switch to fixed-delay UI countdown component rendered inline with Cancel button. +- **Acceptance check:** Forced unmount mid-timer does not crash, navigate, or duplicate-fire analytics; DevTools profile clean. + +### F-075 β€” Auto-fill heuristic requires length β‰₯ 56 +- **Area:** frontend/ux Β· **Evidence:** `acbu-frontend/lib/autoFill.ts` +- **Impact:** Short, valid IDs do not trigger auto-fill; users paste and form is silent, requiring manual selection. +- **Fix direction:** Multi-mode detection (length, content signature, recent-history); over-trigger rate must stay bounded (<5%). +- **Acceptance check:** Known short IDs reliably trigger auto-fill in QA fixtures; over-trigger rate <5% in 50-paste regression corpus. + +### F-077 β€” `/p2p` route is client-side redirect only +- **Area:** frontend/nav Β· **Evidence:** `acbu-frontend/app/(app)/p2p/page.tsx` with `useEffect β†’ router.replace('/send')` +- **Impact:** Direct `/p2p` URLs flash blank page before client-side redirect; SSR/SEO wrong; analytics double-count. +- **Fix direction:** Either rebrand to a server-side route once wired up, or hide from navigation until P2P feature is launched. +- **Acceptance check:** Direct `/p2p` URLs either render real content or are feature-flag gated; analytics dedupes correctly. --- -## Critical +## 7. Legacy 77-Item Origin List (cross-reference) + +The legacy list previously alone at this path was a more recent manual review with slightly different framing. Items are preserved here for traceability; each is tagged `(moved)` (when it cleanly maps to a canonical `F-###` ID) or `(new)` (when it captures a distinct finding that should be merged into the canonical list in a follow-up PR). + +### Critical + +1. **Send success dialog shows empty amount** – app/(app)/send/page.tsx: `setAmount('')` runs before the success dialog. *(moved β†’ F-014)* + +### High – Security + +2. **API key stored in sessionStorage** – *(moved β†’ F-002)* +3. **Challenge token in URL** – *("challenge_token" query param).* *(moved β†’ F-006)* + +### High – Non-functional Flows + +4. **Savings deposit only logs to console** – *(moved β†’ F-015)* +5. **Savings "New Goal" button does nothing** – *(moved β†’ F-016)* +6. **SME "Get Started" / "Apply Now" no handlers** – *(moved β†’ F-017)* +7. **Lending application only logs to console** – *(moved β†’ F-018)* +8. **Currency operations simulate with setTimeout** – *(moved β†’ F-019)* +9. **Bill payment only logs to console** – *(moved β†’ F-020)* +10. **Wallet confirm button disabled forever** – *(moved β†’ F-021)* +11. **Security settings placeholder** – *(moved β†’ F-022)* +12. **Mint page Burn tab non-functional** – *(moved β†’ F-023)* + +### Medium – UX/Design + +13. **AFK vs ACBU naming inconsistency** – *(moved β†’ F-024)* +14. **Balance always placeholder** – *(moved β†’ F-025)* +15. **Savings: mock data mixed with API** – *(moved β†’ F-026)* +16. **Savings withdraw: user field confusing** – *(moved β†’ F-027)* +17. **Hardcoded fees and placeholder copy** – *(moved β†’ F-028)* +18. **Me page KYC badge always "Pending"** – *(moved β†’ F-029)* +19. **Me page balance and stats hardcoded** – *(moved β†’ F-030)* +20. **Business page stats hardcoded** – *(moved β†’ F-031)* +21. **Burn tab shows unhelpful text** – app/(app)/mint/page.tsx: copy on the Burn tab. The fake-success flow is captured by F-023; this entry covers the unimplemented / unclear copy. *(see F-023; F-023 covers the fake-success side, this covers the copy side)* +22. **International / gateway pages are stubs** – *(moved β†’ F-032)* +23. **Help page minimal** – *(moved β†’ F-033)* +24. **Sign-in identifier label misleading** – *(moved β†’ F-034)* +25. **`?created=1` from signup ignored** – *(moved β†’ F-035)* +26. **Confirm dialog does not show note** – *(moved β†’ F-036)* +27. **Send note never sent to API** – *(moved β†’ F-036)* + +### Medium – Error Handling + +28. **Consistent API error handling missing** – *(moved β†’ F-037)* +29. **loadTransfers / loadContacts swallow errors** – *(moved β†’ F-038)* +30. **getReceive errors ignored** – Cross-cuts F-038 + F-037. *(moved β†’ F-038 + F-037)* +31. **No error boundary** – *(moved β†’ F-039)* +32. **No CSRF protection** – *(moved β†’ F-009)* + +### Medium – State Management + +33. **useEffect missing opts in dependency array (send)** – *(moved β†’ F-040)* +34. **Infinite loop risk in contacts page** – *(moved β†’ F-040)* +35. **Infinite loop risk in guardians page** – *(moved β†’ F-040)* +36. **useEffect missing opts in KYC page** – *(moved β†’ F-040)* +37. **Savings deposit user filter too strict** – *(moved β†’ F-041 + F-075)* + +### Medium – Code Quality -1. **Send success dialog shows empty amount** – app/(app)/send/page.tsx: `setAmount('')` runs before the success dialog is shown, so the dialog displays "AFK " with no value. Keep amount in state until the dialog is closed. +38. **`as any` usage in currency page** – *(moved β†’ F-042)* +39. **`as any` usage in bills page** – *(moved β†’ F-042)* +40. **console.log left in production code** – *(moved β†’ F-043)* +41. **Index used as key in lists** – *(moved β†’ F-044)* +42. **No i18n / localization** – *(moved β†’ F-045)* -## High – Security +### Low – Accessibility -2. **API key stored in sessionStorage** – contexts/auth-context.tsx: API key is stored in `sessionStorage`; persists across tabs and is vulnerable to XSS. Prefer `httpOnly` cookies or short-lived tokens. -3. **Challenge token in URL** – app/(public)/auth/2fa/page.tsx: `challenge_token` is passed as a URL query parameter. It can leak via Referer headers, browser history, and server logs. +43. **Form labels and accessibility** – *(moved β†’ F-046)* +44. **Nav links lack aria-labels** – *(moved β†’ F-047)* +45. **Password toggle has no aria-label** – Cross-cuts F-047. *(moved β†’ F-047)* +46. **Burn back link icon-only** – Cross-cuts F-047. *(moved β†’ F-047)* +47. **Send detail back link icon-only** – Cross-cuts F-047. *(moved β†’ F-047)* +48. **Settings cards missing aria-label** – Cross-cuts F-047. *(moved β†’ F-047)* -## High – Non-functional Flows +### Low – Design Consistency + +49. **Empty and loading states inconsistent** – Cross-cuts F-049 + a missing `Skeleton` primitive. **Distinct finding, adopted into canonical catalog as F-066** (Medium Β· frontend/components). *(new β†’ adopted as F-066 in companion canonical-ID PR; see Section 5 and Section 8 for canonical rows)* +50. **Status badge colours in dark mode** – *(moved β†’ F-048)* +51. **Back button pattern inconsistent** – *(moved β†’ F-049)* + +### Low – Forms + +52. **Burn form fields missing maxLength and validation** – *(moved β†’ F-050)* +53. **Signup passcode minimum too weak** – *(moved β†’ F-051)* +54. **Send amount allows negative via keyboard** – *(moved β†’ F-052)* +55. **Contact inputs have no maxLength** – Cross-cuts F-052 + new maxLength checks. **Distinct finding, adopted into canonical catalog as F-067** (Medium Β· frontend/forms). *(new β†’ adopted as F-067 in companion canonical-ID batch PR; see Section 5 and Section 8 for canonical rows)* +56. **Profile inputs have no maxLength or format validation** – **Distinct finding, adopted into canonical catalog as F-068** (Medium Β· frontend/forms). *(new β†’ adopted as F-068 in companion canonical-ID batch PR; see Section 5 and Section 8 for canonical rows)* +57. **Recipient input has no maxLength** – Cross-cuts F-067. *(moved β†’ F-067)* + +### Low – Navigation + +58. **Activity links to /send/${id} instead of /transactions/${id}** – *(moved β†’ F-053)* +59. **"Two-Factor Auth" links to auth flow** – *(moved β†’ F-054)* +60. **Nested interactive elements** – *(moved β†’ F-055)* + +### Low – Data Display + +61. **Transfer amounts not formatted** – *(moved β†’ F-056)* +62. **Mock "Total Savings" mixed with API balance** – *(moved β†’ F-026)* +63. **Currency balance misleading** – Cross-cuts F-026. *(moved β†’ F-026)* +64. **Reserve data lacks context** – *(moved β†’ F-057)* +65. **Rate displayed without formatting** – *(moved β†’ F-058)* +66. **Currency shown without locale** – *(moved β†’ F-059)* + +### Low – Performance + +67. **loadTransfers / loadContacts not memoized** – *(moved β†’ F-060)* +68. **Icons recreated each render** – **Distinct finding, adopted into canonical catalog as F-069** (Low Β· frontend/perf). *(new β†’ adopted as F-069 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* +69. **Menu items use router.push instead of Link** – **Distinct finding, adopted into canonical catalog as F-070** (Low Β· frontend/nav). *(new β†’ adopted as F-070 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* + +### Trivial + +70. **Clipboard API may fail on HTTP** – *(moved β†’ F-061)* +71. **Toast removal delay is ~17 minutes** – `TOAST_REMOVE_DELAY = 1000000`. **Distinct finding, adopted into canonical catalog as F-071** (Medium Β· frontend/ux). *(new β†’ adopted as F-071 in companion canonical-ID PR; see Section 5 and Section 8 for canonical rows)* +72. **Mobile detection treats "unknown" as desktop** – **Distinct finding, adopted into canonical catalog as F-072** (Low Β· frontend/compat). *(new β†’ adopted as F-072 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* +73. **Post-KYC upload navigation uses uncleaned setTimeout** – **Distinct finding, adopted into canonical catalog as F-073** (Low Β· frontend/hygiene). *(new β†’ adopted as F-073 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* +74. **Silent username normalization on signup** – `lib/api/auth.ts` lowercases + strips whitespace without warning UI. **Distinct finding, adopted into canonical catalog as F-074** (Medium Β· frontend/auth). *(new β†’ adopted as F-074 in companion canonical-ID batch PR; see Section 5 and Section 8 for canonical rows)* +75. **Auto-fill heuristic requires length >= 56** – **Distinct finding, adopted into canonical catalog as F-075** (Low Β· frontend/ux). *(new β†’ adopted as F-075 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* β€” note: this overlaps the older F-041 but addresses a different code path (lending + savings auto-fill, not URI-length filter). +76. **API fetch has no timeout** – Frontend `request()` uses `fetch` with no default timeout. **Distinct finding, adopted into canonical catalog as F-076** (High Β· frontend/api). *(new β†’ adopted as F-076 in companion canonical-ID PR; see Section 4 and Section 8 for canonical rows)* +77. **/p2p is client-side redirect only** – `app/(app)/p2p/page.tsx` does `useEffect -> router.replace('/send')`. **Distinct finding, adopted into canonical catalog as F-077** (Low Β· frontend/nav). *(new β†’ adopted as F-077 in companion canonical-ID batch PR; see Section 6 and Section 8 for canonical rows)* + +> **Reconciliation policy:** When a follow-up audit pass opens a new canonical ID for items tagged `(new)`, the row is migrated forward and the tagged comment block in this section is updated to point at the new ID. New IDs are expected to land in `F-066+` and `F-071..F-076` should preserve backward-reference to this section so future readers can trace provenance. + +--- + +## 8. Resolution Tracker (Fix Status) + +> **Format:** `ID Β· title Β· status Β· linked PR / commit Β· owner` +> +> **Forward-looking template.** Every item is currently 🟑 Open; uniformity of status reflects the state of the working backlog. When a fix merges, the row transitions to βœ… Fixed with a PR link. Example anchors below show what a real fixed row looks like. + +| ID | Title | Status | Notes | +|----|-------|--------|-------| +| _(example)_ | _(example anchor for prior documentary PRs β€” PR #14 closed issue #1, PR #22 closed #12, PR #23 backend, PR #24 master-index)_ | βœ… Fixed (PR #24 merged) | Documentary PR β€” not a code fix; historical anchor | +| F-001..F-005 | _Critical cluster_ | 🟑 Open (ship blockers) | Track with TypeScript / wallet / auth refactor | +| F-006, F-010, F-014..F-023, F-025, F-027, F-051, F-063, F-076 | _High cluster (15 items)_ | 🟑 Open | Next.js auth + feature-flag workstream | +| F-007, F-009, F-012, F-013, F-016, F-017, F-024, F-026, F-028–F-030, F-032, F-036–F-040, F-046, F-047, F-050, F-055, F-062, F-064, F-066, F-067, F-068, F-071, F-074 | _Medium cluster (28 items)_ | 🟑 Open | Quality / a11y / hardening workstream | +| F-008, F-011, F-031, F-033..F-035, F-041..F-045, F-048..F-049, F-052..F-054, F-056..F-061, F-065, F-069, F-070, F-072, F-073, F-075, F-077 | _Low cluster (29 items)_ | 🟑 Open | Polish / formatting / docs workstream | + +> **Status legend** β€” 🟒 Trivial/cosmetic Β· 🟑 Open (tracked) Β· πŸ”΅ In review Β· βœ… Fixed (PR linked) +> +> **Reminder when updating:** When an external PR closes an issue, replace `🟑 Open` with `βœ… Fixed` and append the merged PR link in the Notes column. Do not rewrite history; the row itself is the audit trail. + +--- + +## 9. Cross-Reference Map + +| Document | Relationship to this file | +|----------|---------------------------| +| [`../../issues/frontend.md`](../../issues/frontend.md) | **Live triage-ready catalog.** Same IDs and severities; entries are condensed for daily use. | +| [`../../issues/MASTER_INDEX.md`](../../issues/MASTER_INDEX.md) | Master index across backend/frontend/contracts backlogs; Top 20 ship-safety list with per-row tracker pointers. | +| [`../../issues/contracts.md`](../../issues/contracts.md) | Smart contracts MVP issues (C-001..C-060). | +| [`../../issues/backend.md`](../../issues/backend.md) | Backend MVP issues (B-001..B-075). | +| [`../CONTRACTS_ISSUES.md`](../CONTRACTS_ISSUES.md) | Companion long-form reference for smart contracts (PR #22). | +| [`../BACKEND_ISSUES.md`](../BACKEND_ISSUES.md) | Companion long-form reference for backend (PR #23). | +| [`../API_AND_CONTRACTS_REFERENCE.MD`](../API_AND_CONTRACTS_REFERENCE.MD) | API surface map; front-end integration anchors. | +| [`../PROJECT_STRUCTURE.MD`](../PROJECT_STRUCTURE.MD) | Repo layout (where the frontend source lives). | +| [`../../USER_EXPERIENCE/USER_FLOWS.MD`](../../USER_EXPERIENCE/USER_FLOWS.MD) | UX flows context for F-015, F-018–F-020 (console-only features). | +| [`../../USER_EXPERIENCE/MOBILE_OPTIMIZATION_COMPLETE.md`](../../USER_EXPERIENCE/MOBILE_OPTIMIZATION_COMPLETE.md) | Mobile-first context for F-047 (mobile nav a11y) and F-072. | +| [`../../USER_EXPERIENCE/DIASPORA_REMITTANCES.MD`](../../USER_EXPERIENCE/DIASPORA_REMITTANCES.MD) | Diaspora remittance context for F-019, F-020, F-032. | +| [`../../SECURITY_GUIDE.MD`](../../SECURITY_GUIDE.MD) | Security posture context for F-001..F-005 + F-064 + F-065. | +| [`../../correction.md`](../../correction.md) | Standing list of items curated for fast triage (frontend visibility gaps). | + +--- + +## 10. Top Remediations (ship-safety order) + +Order these fixes in the MVP launch runbook before deploying production: + +1. **F-001..F-005** β€” Hard wallet + auth + build pipeline overhaul: turn off `ignoreBuildErrors`, move secrets off sessionStorage, replace base64 with AES-GCM, block plaintext wallet path. +2. **F-002 / F-003 / F-005** β€” Single PR for the wallet secrets stack (migrate to WebCrypto keys, kill plaintext store, add sessionStorage lint rule). +3. **F-022 / F-039 / F-064** β€” Settings-first security surface + global error boundary + CSP baseline. +4. **F-014 / F-015 / F-018 / F-019 / F-020 / F-022 / F-023 / F-021** β€” Stubbed feature clean-up: savings deposit, lending application, currency operations, bills payment, security settings, mint/burn tab, wallet confirm. +5. **F-025 / F-027 / F-051 / F-063** β€” Data integrity + auth strength + environment footgun. +6. **F-006 / F-010 / F-046 / F-047** β€” Auth/secrets hygiene + accessibility on money routes. +7. **F-040 / F-039 / F-038 / F-037** β€” Quality + reliability pass (react-hooks lint, error boundaries, retry UI, useApiError hook). +8. **F-007 / F-009 / F-012 / F-013** β€” Dev infrastructure (single header, CSRF pairing, typecheck/smoke CI, single lockfile). +9. **F-016 / F-017 / F-032 / F-033 / F-034 / F-035** β€” Feature-flag and copy cleanups (inert CTAs, dead nav, sign-in label, signup confirmation). +10. **F-008 / F-011 / F-031 / F-041..F-065** β€” Polish + tooling (timeouts, package rename, demo-mode banners, format/locale helpers, accessibility polish). + +--- + +## 11. Maintenance + +- **New issues** β€” Add to `issues/frontend.md` first (canonical triage). Mirror to this file with the next stable `F-###` ID. +- **Closed fixes** β€” Promote the fix to `βœ… Fixed (PR linked)` in Section 8 and link to the merged PR. +- **Legacy `(new)` items in Section 7** β€” Open a follow-up canonical-ID PR; on merge, update both `issues/frontend.md` and this file. +- **Cross-doc references** β€” Update `issues/MASTER_INDEX.md` summary counts (Top 20 pointer list) when severities change. + +--- + +## Related Documents + +- [Live frontend issue catalog](../../issues/frontend.md) +- [Master backlog index](../../issues/MASTER_INDEX.md) +- [Contracts long-form reference](../CONTRACTS_ISSUES.md) +- [Backend long-form reference](../BACKEND_ISSUES.md) +- [Smart contract spec (root copy)](../SMART_CONTRACT_SPEC.md) +- [Project structure map](../PROJECT_STRUCTURE.md) +- [User flows](../../USER_EXPERIENCE/USER_FLOWS.md) +- [Security guide](../../SECURITY_GUIDE.md) + +--- -4. **Savings deposit only logs to console** – app/(app)/savings/page.tsx: `handleConfirmDeposit` only `console.log`s; no API call is made. -5. **Savings "New Goal" button does nothing** – app/(app)/savings/page.tsx: The button has no handler. -6. **SME "Get Started" and "Apply Now" have no handlers** – app/(app)/sme/page.tsx: Buttons are non-functional. -7. **Lending application only logs to console** – app/(app)/lending/page.tsx: `handleSubmitApplication` only `console.log`s; no API call. -8. **Currency operations simulate with setTimeout** – app/(app)/currency/page.tsx: Mint/Burn/International operations only simulate with `setTimeout`; no real API calls. -9. **Bill payment only logs to console** – app/(app)/bills/page.tsx: Payment handler only `console.log`s; no API call. -10. **Wallet confirm button disabled forever** – app/(app)/me/settings/wallet/page.tsx: "Confirm wallet" button is disabled with no implementation path. -11. **Security settings is a placeholder** – app/(app)/me/settings/security/page.tsx: Shows placeholder text "2FA and delete account options. Will wire to API." -12. **Mint page Burn tab non-functional** – app/(app)/mint/page.tsx: The Burn tab does not call the burn API; confirm step only sets success after a timeout. - -## Medium – UX/Design - -13. **AFK vs ACBU naming inconsistency** – Mint page and home use "AFK"; burn and other copy use "ACBU". Standardise product name across all labels, headers, and empty states. -14. **Balance always placeholder** – Balance is shown as "β€”" or "AFK β€”" on mint, send, home, and savings; no real balance API is wired. -15. **Savings: mock data mixed with API** – Savings page mixes API data (positions balance) with hardcoded cards (savingsAccounts, mockGoals with fixed dates). "Total Savings" and interest are static. -16. **Savings withdraw: user field confusing** – Withdraw page pre-fills "User (Stellar address or ID)" from getReceive but leaves it editable; users could change it and withdraw to the wrong account. -17. **Hardcoded fees and placeholder copy** – Mint burn card shows "Processing Fee: AFK 1.00"; network fee shows "See quote". Either compute from API or mark as estimate. -18. **Me page KYC badge always "Pending"** – app/(app)/me/page.tsx: KYC badge always shows "Pending" regardless of real status. -19. **Me page balance and stats hardcoded** – app/(app)/me/page.tsx: "AFK 12,450" and "This Month +AFK 2,340" are hardcoded, not from API. -20. **Business page stats hardcoded** – app/(app)/business/page.tsx: "Monthly Volume AFK 145,320" and "Employees 24" are hardcoded mock data. -21. **Burn tab shows unhelpful text** – app/(app)/mint/page.tsx: Burn tab shows "Local currency (see /burn for details)" β€” unclear UX that sends users elsewhere. -22. **International and gateway pages are stubs** – app/(app)/international/page.tsx and app/(app)/gateway/page.tsx: Show "Coming soon" with no CTA or timeline. -23. **Help page minimal** – app/(app)/me/help/page.tsx: Only shows "support@acbu.io" with no FAQs or support flow. -24. **Sign-in identifier label misleading** – Label says "Username" but backend accepts username, email, or E.164 phone. Use "Username, email, or phone". -25. **`?created=1` from signup ignored** – app/(public)/auth/signin/page.tsx: Query param from signup is never shown as a success message. -26. **Confirm dialog does not show note** – app/(app)/send/page.tsx: Note is collected but not shown in the confirm dialog. -27. **Send note never sent to API** – app/(app)/send/page.tsx: `note` is collected but never passed to `createTransfer` API. - -## Medium – Error Handling - -28. **Consistent API error handling missing** – burn/page.tsx handles ApiError with status-specific messages; ensure all other API-calling pages use the same pattern. -29. **loadTransfers and loadContacts swallow errors** – app/(app)/send/page.tsx: `.catch(() => {})` silently ignores failures. -30. **getReceive errors ignored** – app/(app)/savings/page.tsx: userApi.getReceive errors are swallowed. -31. **No error boundary** – app/layout.tsx: No React error boundary; uncaught errors can crash the entire app. -32. **No CSRF protection** – lib/api/client.ts: No CSRF token for state-changing requests. - -## Medium – State Management - -33. **useEffect missing opts in dependency array** – app/(app)/send/page.tsx: `useEffect` depends on `opts.token` but calls `loadTransfers`/`loadContacts` that use full `opts` object; `opts` is not in deps. -34. **Infinite loop risk in contacts page** – app/(app)/me/settings/contacts/page.tsx: `load` used in `useEffect` but not in deps; if added, would cause infinite loop. Use `useCallback`. -35. **Infinite loop risk in guardians page** – app/(app)/me/settings/guardians/page.tsx: Same pattern. -36. **useEffect missing opts in KYC page** – app/(app)/me/kyc/page.tsx: `opts` missing from dependency array. -37. **Savings deposit user filter too strict** – app/(app)/savings/deposit/page.tsx: `uri.length >= 56` filters out non-Stellar addresses; may block valid user IDs. - -## Medium – Code Quality - -38. **`as any` usage in currency page** – app/(app)/currency/page.tsx: `as any` used for tab value. -39. **`as any` usage in bills page** – app/(app)/bills/page.tsx: `as any` used for tab value. -40. **console.log left in production code** – app/(app)/savings/page.tsx, bills/page.tsx, lending/page.tsx, currency/page.tsx: `console.log` calls left in handlers. -41. **Index used as key in lists** – app/(app)/mint/page.tsx and app/(app)/rates/page.tsx: Rates lists use array index as `key` prop instead of a stable identifier. -42. **All user-facing strings hardcoded** – Entire codebase: No i18n support; all text is in English with no extraction or translation framework. - -## Low – Accessibility - -43. **Form labels and accessibility** – Many forms use `