Skip to Content
Welcome to RitoSwap's documentation!

Rate Limiting Library

All token-gate APIs share a Cloudflare Durable Object that tracks request volume, SIWE nonces, and quotas. The server-side helpers in dapp/app/lib/rateLimit/rateLimit.server.ts wrap that worker so every request—whether it lands on Vercel, a local dev server, or another host—pulls from the exact same counters.

The library exposes:

  • isRateLimitEnabled() – true only when NEXT_PUBLIC_ENABLE_STATE_WORKER=true, the state worker creds exist, and the worker client can initialize.
  • checkRateLimitWithNonce() – applies a per-endpoint limiter, an optional global limiter, and opportunistically returns the cached nonce for SIWE flows.
  • Helpers for extracting client identifiers and translating limiter metadata into HTTP headers (X-RateLimit-*, Retry-After).

Architecture Overview

  1. Client identifier – derived from x-forwarded-for (production) or the request socket/IP (everywhere else) via getIdentifier.
  2. Per-endpoint limiter – configuration-driven (RATE_LIMITER_CONFIGS) and enforced by the Durable Object (state:ratelimit:check action).
  3. Global limiter – optional 100 req/hour bucket for most endpoints (token-status polling is exempt).
  4. Nonce lookup – the same worker stores SIWE nonces; if one already exists for the identifier it is injected into the limiter response to avoid redundant writes.

Configuration

NEXT_PUBLIC_ENABLE_STATE_WORKER=true STATE_WORKER_URL=https://ritoswap-state-worker.worker.dev/state STATE_WORKER_API_KEY=your_shared_secret

Disable the flag locally to bypass rate limits entirely while iterating. In production, the worker must be reachable before any route can perform SIWE or rate-limited operations.

Limiter Catalog

LimiterLimitWindowPrefixPurpose
nonce1060srl:nonce:SIWE nonce generation attempts
gateAccess560srl:gate-access:Main gate unlock endpoint
formSubmissionGate360srl:form-submission:Single-use message submission endpoint
tokenStatus6060srl:token-status:High-frequency polling for UI updates (no global limiter)
global1003600srl:global:Catch-all protection shared across most routes

The configuration lives in RATE_LIMITER_CONFIGS and feeds both the worker payload (limit, windowSeconds) and the doc site.

Request Lifecycle

1. Identifier Extraction

getIdentifier(req) prioritizes x-forwarded-for, falls back to x-real-ip, and finally req.ip. Development requests default to 127.0.0.1 so the limiter remains stable between reloads.

2. Per-Route Check

applyLimiter(type, identifier) calls getStateClient().checkRateLimit(...). Failures (network issues, worker downtime) are logged and treated as success: true to avoid blocking the app.

3. Optional Global Check

For everything except tokenStatus, a second limiter (global) is evaluated to cap total throughput per identifier.

4. Nonce Lookup

When SIWE is enabled, the helper also tries getStateClient().getNonce(identifier) so /api/nonce can reuse the cached value without performing another worker round trip.

5. Response Headers

Successful checks compute Retry-After, X-RateLimit-Limit, and X-RateLimit-Remaining. Routes include those headers on 429 responses (and optionally on successful responses).

Surfacing to APIs

Routes typically integrate like this:

const result = await checkRateLimitWithNonce(request, 'gateAccess'); if (!result.success) { return rateLimitResponse(result, 'Rate limit exceeded'); } // result.nonce may contain a SIWE nonce for reuse
  • /api/gate-access passes 'gateAccess' and leaves includeGlobal at true.
  • /api/token-status/[tokenId] uses 'tokenStatus' and explicitly sets includeGlobal=false to keep polling responsive.
  • /api/form-submission-gate uses 'formSubmissionGate' before parsing any user content, protecting the most expensive code path.

Testing & Debugging

dapp/app/lib/rateLimit/__tests__/rateLimit.server.test.ts verifies:

  • Identifier extraction fallbacks
  • Global limiter bypass for tokenStatus
  • Error resilience when the worker is unreachable
  • Header/metadata formatting for 429 responses

For manual diagnostics, /api/debug/status responds in development with booleans indicating whether the state worker URL and API key are configured.

⚠️

Because all rate limiting, nonce storage, and quota tracking shares the same Durable Object, never expose its STATE_SERVICE_AUTH_TOKEN publicly. The worker authenticates every request coming from the Next.js app.

File Locations

RitoSwap Docs does not store, collect or access any of your conversations. All saved prompts are stored locally in your browser only.