Implements four backend features for operator fee reporting and GDPR compliance, and fixes a batch of pre-existing CI breakage on main that was unrelated to these issues but left the build red (rust fmt/clippy/test, backend lint, and several dead/broken modules) — a green CI was part of the ask, so it's included here rather than built on top of a broken baseline.
GET /api/v1/admin/fees/dashboardreturns per-vault fee metrics:contractId,name,totalOperatorFees,epochCount,feeBps,lastEpochFee.- Ordered by
totalOperatorFees DESC; accepts optionalfrom/todate filters that bound the fee computation. - Returns all vaults via a
LEFT JOIN, including ones with no fee history yet.
GET /api/v1/users/:address/data-exportstreams a ZIP containinguser.json,positions.json,yield-history.json,events.json, withContent-Type: application/zipandContent-Disposition: attachment. Admin-key protected.- Fixed a real bug in the hand-rolled ZIP encoder: the central-directory record was byte-misaligned (wrong field offsets and compression-method value), producing an archive that failed to extract. Corrected the offsets to match the ZIP spec and verified with
unzipand Python'szipfile.
DELETE /api/v1/admin/users/:addressdeletes theusersrow, anonymisesuser_addressto[REDACTED]inuser_vault_positions,share_balance_snapshots, andredemption_requests, and returns{ address, deletedAt, recordsAffected }. Admin-key protected.GET /api/v1/users/:addressreturns 404 afterward.- Fixed
recordsAffectedcounting every[REDACTED]row ever written (across all past deletions) instead of just this call's affected rows. - Added anonymisation of the historical event log (
indexed_events.payload), which was previously untouched despite "historical event data is anonymised, not deleted" being an explicit acceptance criterion.
- New
admin_audit_logtable:id,api_key_label,action,target,ip_address,created_at,request_body_hash. - Logged on every admin write (backfill trigger, API key deletion, user deletion) with the API key label, client IP, and a SHA-256 hex hash of the request body (never the raw body).
GET /api/v1/admin/audit-logreturns entries ordered bycreated_at DESC, paginated.
- Removed five dead pre-refactor directories (
backend/src/routes,src/database,src/middleware,src/indexer,src/tasks) never imported by the live app, plus the dangling npm scripts pointing at them. - Restored
ErrorCode/AppError, anEventEmitterimport, an SSE handler, and aCORS_MAX_AGEconfig field — all dropped by a bad merge onmain. - Fixed 9 pre-existing ESLint errors and a flaky test caused by an unmocked real DNS lookup.
- Rust:
cargo fmt, one clippy lint, a contract error discriminant defined as= 2instead of= 52, missingVaultInfostruct fields, a missingdefault_operator_fee_bpsgetter, and several test bugs (wrongredeem_at_maturityargs, deposits below the funding target, a test relying on the ledger's default zero timestamp colliding with a "no deposit" sentinel).
backend:npm run lint,npm run build,npm run test— all pass (288/288 tests, 31/31 files).sdk:npm run lint,npm run build— pass.soroban-contracts:cargo fmt --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace— all pass (393 + 40 tests).- Added/extended unit tests for
getAdminFeesDashboard,deleteUser, andgetAdminAuditLog. - Added a structural ZIP-validity check (parses local file headers, inflates each entry, verifies CRC/size) to the export test — this is what caught the ZIP corruption bug.
Closes #794 Closes #795 Closes #796 Closes #797