Purpose: Running list of minor gaps, incomplete items, and polish work left behind by OSS contributors. OSS contributors are scoped to the issue — they ship what the issue asks for, not the surrounding system. This file is the maintainer's (0xDeon) personal queue to close after each campaign wave.
Key: 🔴 Must fix before merge (blocking) | 🟠 Fix soon after merge | 🟡 Nice to have / polish
- During PR review, drop items here instead of blocking the contributor on minor things.
- After the OSS wave closes, work through these top-to-bottom.
- Strike through items as they are resolved with the commit SHA.
-
seed.sql users table is based on migration 001, not the full chain. Migration
007_update_users_tabledropsemail, renamesname→display_name, addswallet_address TEXT NOT NULL, addskyc_status TEXT NOT NULL DEFAULT 'pending'. The current seed INSERT referencesemailandname— both will error against the live schema. Fix: rewrite users block inscripts/seed.sqlto match the post-007 schema and update the INSERT. -
API healthcheck endpoint mismatch.
docker-compose.ymlprobes/healthzbut the README, PR description, and issue all say/health. One of them is wrong. Verify the actual Go router and make everything consistent.
-
Frontend
Dockerfile.devuses npm in a pnpm monorepo.apps/dapp/frontend/Dockerfile.devrunsnpm ciwithpackage-lock.json. The workspace is managed by pnpm. Should usepnpm install --frozen-lockfileto guarantee the same dependency tree CI and root installs resolve. -
JWT_SECRET(or equivalent auth secret) not set in compose API service. The issue template included it. Without it, the API either panics on startup or falls back to an empty/default secret, making all dev tokens trivially forgeable. Add it todocker-compose.ymlwith a dev placeholder comment. -
Go version pin drift.
apps/api/go.modtargets Go 1.25.0 but both Dockerfiles usegolang:1.24-alpine(1.25 not yet on Docker Hub). Track this — bump Dockerfiles togolang:1.25-alpineonce the image is published.
- seed.sql does not include
user_rolestable (introduced in PR #270). After PR #270 merges,scripts/seed.sqlneeds aCREATE TABLE IF NOT EXISTS user_rolesblock and a seed row granting the test user an admin role so the full admin flow is exercisable locally.
-
Verify
turbo.jsonhas no remainingdapp/backendpipeline entries. Confirmed clean — nodapp/backendreferences found inturbo.json. -
Verify root
README.mdhas no remaining references to the old Express service. Confirmed clean — no Express ordapp/backendreferences in rootREADME.md. Also patchedservices/api/README.mdto remove stale "transitioning from Node.js/Express" language.
-
GetRolespassesid.String()instead of the rawuuid.UUIDto pgx. (resolved in commite8072f4) File:apps/api/internal/repository/postgres/user_repository.gopgx v5 handlesuuid.UUIDnatively. Passing.String()forced string serialisation and an implicit server-side cast. This PR now passesuuid.UUIDvalues directly. -
bootstrap-admindoes not calldb.Ping()aftersql.Open(). File:apps/api/cmd/bootstrap-admin/main.gosql.Openonly validates DSN format — it does not connect. A bad DSN or unreachable host surfaces as a confusing query error. Add:if err := db.Ping(); err != nil { return fmt.Errorf("cannot reach database: %w", err) }
-
bootstrap-admindoes not validate Stellar address format. A typo (wrong prefix, wrong length) producesno user found with wallet addressrather thaninvalid Stellar address. Low risk — the tool is operator-only — but a basic check (strings.HasPrefix(*wallet, "G") && len(*wallet) == 56) improves UX. -
Document re-authentication requirement in migration runbook. All currently active admin sessions have tokens with empty
Roles. After deploying migration 009, admins must log out and back in. This is stated in the PR description but should be in the deployment runbook /apps/api/migrations/README.md(or equivalent) so it isn't missed during a future on-call deploy.
-
initiateSettlementacceptsuser_idfrom the request body instead of the JWT. File:apps/api/internal/handler/settlement_handler.go—initiateSettlementhandler. Any authenticated caller can create a settlement on behalf of any other user by supplying a differentuser_idin the JSON body. The fix is the same pattern applied in this PR: extract caller fromauth.GetUserFromContext, ignore the bodyuser_idfield entirely. This is a second BOLA vector on the creation endpoint — out of scope for #271 but directly referenced by issue #239's Step 3 ("audit every settlement endpoint"). -
GET /api/v1/settlements/{id}has no ownership check. Any authenticated user can read any settlement by UUID. Read-only, so lower severity than a mutation, but it enables UUID enumeration which is the precondition for the BOLA attack fixed in this PR. Consider returning 404 (not 403) for settlements the caller doesn't own to avoid confirming resource existence.
- 403 on non-owner PATCH confirms settlement existence.
File:
apps/api/internal/service/settlement_service.goNon-existent UUIDs return 404; non-owned UUIDs return 403. An attacker can distinguish between the two, using the 403 to confirm a UUID is a live settlement before targeting it. Return 404 for both cases (treat non-ownership as non-existence) to eliminate this signal.
- Add impairment regression test to vault contract.
File:
packages/contracts/contracts/vault/src/test.rsThe issue explicitly required: "User deposits at rate 1.0, rate halves (impairment) → zero performance fee." The code handles it correctly (yield_part < 0→ fee skipped) but the test proving it is missing. Addedimpairment_charges_zero_performance_fee: deposits at rate 1.0, halves the share price via a negativereport_yield, and asserts both the preview and the actual withdrawal charge no performance fee.
-
preview_withdrawreturns gross pre-fee amount — document or fix for DApp. File:packages/contracts/contracts/vault/src/lib.rspreview_withdraw(shares)returnsamount_for_shares(shares)— the gross amount before management, early-withdrawal, and performance fees are deducted. EIP-4626'spreviewRedeemis supposed to include fees. A frontend that passespreview_withdrawoutput directly asmin_assets_outwill getSlippageExceededon every fee-bearing withdrawal. Fix options: (a) add apreview_withdraw_netfunction that applies fee estimates on-chain, or (b) document explicitly in the function's doc comment that the return value is gross pre-fee and the DApp must subtract estimated fees before using it asmin_assets_out.
- Verify
cargo test -p vault-contractpasses in CI with vault_token.wasm artifact. The contributor left this box unchecked in the PR test plan. Confirm integration tests are not silently passing vacuously due to missing WASM artifact. CI now buildsvault_token.wasm, asserts the artifact exists, runs the tests explicitly, and fails the job if zero vault-contract tests execute (see.github/workflows/ci.yml).
PR was BLOCKED — do not merge. Required fixes listed here for when the contributor resubmits.
-
Persist last indexed ledger to database — current in-memory cursor corrupts data on restart. File:
apps/api/cmd/api/main.go—startEventIndexer/startLedgervariable.startLedgeris a localuint64initialized to 0 on every API start. All balance update queries are additive (total_deposited + amount). Any restart replays all historical events and doubles every vault balance. Fix: add asystem_statetable (or key-value row) to persist the last successfully indexed ledger sequence; read it on startup and resume from there. -
startLedger = 0triggers RPC error on first call — indexer never runs. StellargetEventsrejects ledger sequence 0. On first boot with no persisted cursor, start from the current ledger tip (not 0) to avoid replaying full chain history. -
Make all balance updates idempotent. Either (a) store processed event IDs in a
processed_eventstable and skip duplicates, or (b) use absolute SET values derived from on-chain state rather than additive+= amount. Option (b) is safer and simpler if the on-chain state can be queried directly.
-
Move indexer logic into
internal/stellar/— implementEventPoller.PollEventsproperly. The existingEventPollerininternal/stellar/events.gohasPollEventsreturning an empty stub. The PR added 247 lines of parallel logic inmain.goinstead of fixing the existing struct. Consolidate: implementPollEventsin the existing package so the logic is testable and reuses the repository layer instead of raw*sql.DB. -
Remove
float64case inextractEventAmount. File:apps/api/cmd/api/main.gofloat64loses precision on large integer amounts (>2^53). Soroban event amounts come as strings. Treat any non-string amount type as unparseable and returnfalse. -
Add tests for
applyIndexedEventandextractEventAmount. These functions write to the financial database based on external RPC input. They must have unit tests covering: deposit event, withdraw event, pause/unpause events, unknown event (no-op), missing amount field, malformed amount string.
-
Migration numbering collision (pre-existing debt). Two files share the
007prefix:007_add_vault_deleted_at.up.sql007_update_users_table.up.sqlThis will confuse any migration runner that applies files in lexicographic order. Rename one of them and renumber consistently. Needs care — check if the runner is order-sensitive before touching.
-
No migration runner is wired into the Go API startup. Resolved:
golang-migrateruns on API startup whenRUN_MIGRATIONS=true(set indocker-compose.ymlfor local dev). Pending migrations apply incrementally onmake devwithout requiringmake dev-reset. Seeapps/api/migrations/README.md.
| Wave | PRs Reviewed | Items Added | Items Closed |
|---|---|---|---|
| OSS Wave 1 | #268, #269, #270, #271 | 17 | 0 |
| OSS Wave 2 | #275, #276, #277 | 11 | 0 |