Skip to content

Rate Limiting, WebSocket Portfolio Notifications, Achievement Engine, USDC Deposit Flow, DOAN Documentation#248

Merged
GoSTEAN merged 2 commits intoNetwalls:mainfrom
OtowoSamuel:main
Feb 26, 2026
Merged

Rate Limiting, WebSocket Portfolio Notifications, Achievement Engine, USDC Deposit Flow, DOAN Documentation#248
GoSTEAN merged 2 commits intoNetwalls:mainfrom
OtowoSamuel:main

Conversation

@OtowoSamuel
Copy link
Contributor

Closes #134
Closes #226
Closes #115
Closes #87
Closes #95


Summary

This PR resolves 5 tracked issues: wallet-keyed rate limiting, portfolio update notifications over WebSocket, an achievement engine, a USDC deposit flow with on-chain verification, and DOAN architecture documentation.


Changes

Rate Limiting per Wallet Address (Issue 1)

Previously all rate limits keyed by IP address, which allowed a single user to bypass limits by rotating IPs or operating behind shared NAT. Limiters now key by wallet:{publicKey} when the user is authenticated, falling back to user:{userId} then ip:{address}.

  • authRateLimiter: 5 requests/minute
  • predictionRateLimiter: 10 requests/minute (new)
  • tradeRateLimiter: 30 requests/minute (new)
  • All 429 responses now include a Retry-After header populated from the RateLimit-Reset timestamp
  • Uses standardHeaders: 'draft-7' for RFC 9110-compliant rate limit headers
  • tradeRateLimiter applied to buy, sell, and submit-signed-tx routes

WebSocket Portfolio Notifications (Issue 3)

Clients now join a private portfolio:{userId} room on connection and receive a connected acknowledgement with their room list. Three notification helpers emit typed events to that room:

  • notifyPositionChanged — fired after a prediction is committed
  • notifyWinningsClaimed — fired after winnings are claimed, includes new balance
  • notifyBalanceUpdated — fired after any balance change (deposit, withdrawal, winnings, prediction)

setSocketIORef() wires the Socket.IO instance into the realtime module at startup without circular imports.

Achievement Engine (Issue 4)

New achievement.service.ts defines a catalog of 13 achievements across Bronze, Silver, Gold, and Platinum tiers covering prediction volume, win streaks (5/10/25), trade size (whale/mega-whale), win rate (sharp shooter), and profit milestones. checkAndAward() runs all applicable checks in parallel, skips already-awarded achievements via Prisma unique constraint idempotency, and sends a ACHIEVEMENT notification through the existing notification service. Checks are triggered non-blocking after each prediction settlement in market.service.ts.

USDC Deposit Flow (Issue 5)

Two-step deposit flow added to wallet.service.ts:

  1. initiateDeposit() — returns the platform's Stellar address, a user-specific memo (dep:{userId.slice(0,8)}), and a 24-hour expiry. The memo allows the backend to attribute incoming payments to the correct account.
  2. confirmDeposit() — accepts a Stellar transaction hash, fetches and validates the transaction from Horizon (checks memo, source account, USDC asset, payment destination), then atomically credits the user's usdcBalance and creates a Transaction record. Duplicate submissions are rejected via txHash uniqueness. In non-production environments, transaction hashes prefixed with mock-deposit- bypass Horizon for testing.

withdraw() now also persists a Transaction record and emits a balance_updated WebSocket event. Routes POST /api/wallet/deposit/initiate and POST /api/wallet/deposit/confirm added with auth guards, rate limiters, and Swagger documentation.

DOAN Documentation (Issue 2)

DOAN_DOCS.md added at repo root covering the Decentralized Oracle Attestation Network architecture, oracle lifecycle, attestation flow, staking and slashing parameters, REST and WebSocket integration points, Soroban contract interface, and security considerations. Linked from README.md.


Testing

  • tsc --noEmit: zero errors
  • Unit test suite (middleware, websocket, service unit tests): 144 tests across 8 files, all pass
  • Integration tests require a live Redis and PostgreSQL instance; pre-existing ioredis: Connection is closed failures are unrelated to this PR

Files Changed

  • rateLimit.middleware.ts — rewritten
  • trading.ts — added tradeRateLimiter
  • realtime.ts — portfolio room + notification helpers
  • index.ts — setSocketIORef wired at startup
  • prediction.service.ts — portfolio notifications on commit/claim
  • market.service.ts — achievement check after settlement
  • wallet.service.ts — rewritten with deposit flow
  • wallet.controller.ts — deposit handlers
  • wallet.routes.ts — deposit routes with Swagger docs
  • achievement.service.ts — new file
  • index.ts — achievement exports
  • DOAN_DOCS.md — new file
  • README.md — documentation links

@GoSTEAN GoSTEAN merged commit 0a9bd2d into Netwalls:main Feb 26, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants