Skip to content

Custodial wallet resilience: reconciliation, retry & admin recovery — backend EPIC #81

Description

@grantfox-oss

DESCRIPTION

Every account gets a custodial Stellar wallet created via WalletService.createWalletRecord() (apps/api/src/escrow/wallet.service.ts). When funding fails, the wallet is silently persisted with status: 'failed' (see AuthService.register and UsersService.ensureProfileWallet in apps/api/src/auth/auth.service.ts / apps/api/src/users/users.service.ts) — there is no retry, no reconciliation job, and no admin-facing way to recover a user stuck without a working wallet. Since the wallet is required to hold bond tokens on-chain, a failed wallet silently breaks that user's ability to participate. This epic builds the missing resilience layer.

GOAL

Wallets that failed to create/fund are automatically retried, an admin can see which accounts have a broken wallet and trigger a manual retry, and this status is visible where user data is already surfaced.

REQUIREMENTS

  • A reconciliation routine (scheduled job or on-demand endpoint — pick whichever fits the current deployment, e.g. @nestjs/schedule if acceptable, or an admin-triggered endpoint if a scheduler is out of scope) that finds profiles/parties with stellar_wallet_status: 'failed' and retries WalletService.createWalletRecord().
  • Bounded retry (e.g. backoff + max attempts) so a permanently-broken dependency doesn't loop forever; record the last error (stellar_wallet_error, already a column) and attempt count.
  • POST /users/:id/wallet/retry (admin-only, mirroring the authorization pattern in UsersService.listUsers/setRole) to manually trigger a retry for one user.
  • Surface wallet status (created / funded / failed, with last error) in the admin-facing user data — wire into the user-directory admin console if that epic has landed, otherwise expose it via GET /users for now.

ACCEPTANCE CRITERIA

  • A profile/party with stellar_wallet_status: 'failed' is retried automatically (or via the admin endpoint) and transitions to funded/created once the retry succeeds — proven with a test against a mocked WalletService.
  • Retries are bounded and don't hammer the network indefinitely on a permanent failure.
  • POST /users/:id/wallet/retry is admin-only and audited via AuditService.emit().
  • npm run build, npm run lint, npm run test pass in apps/api with NO VELAR credentials — the reconciliation logic must be testable against a mocked WalletService/SupabaseService, not real Stellar testnet calls.
  • docs/BACKEND.md updated describing the reconciliation flow and the manual-retry endpoint.

NOTES

Relevant files: apps/api/src/escrow/wallet.service.ts, apps/api/src/auth/auth.service.ts, apps/api/src/users/users.service.ts, apps/api/src/users/users.controller.ts, apps/api/src/audit/audit.service.ts. Do not change the wallet creation/funding logic itself — only add retry/reconciliation around the existing createWalletRecord() (docs/AGENTS.md §3).

No VELAR credentials required: verifiable locally with a mocked WalletService/SupabaseService — no real Stellar testnet calls or VELAR credentials required for tests.

Difficulty: Advanced
Priority: Medium

PR REQUIREMENTS

  • Ship this as a single pull request — do not split into stacked/sequential PRs.
  • Your PR description must include Closes #<this-issue-number> (this issue's GitHub number) so merging auto-closes it.

CAMPAIGN REQUIREMENTS (FWC26)

To be eligible for the reward on this issue, the contributor must:

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third Campaign

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions