Queue positions for the token allocation program, ranked from on-chain pool deposits with a time-in-queue multiplier and an optional trading-volume bonus. Read-only except for wallet linking; ranking runs on its own interval.
Responses carry queue position and deposited amounts. Multipliers, volume and scores are never served: a public score reads as an allocation promise.
/healthService health plus latest-snapshot metadata.
ok stays true while a ranking input is degraded — the queue is still served; check inputs.<name>.degraded instead. Inputs: arcusVolume, ammVolume, points, holdings.final is true once the latest snapshot is safe to allocate from: past the lock, every input refreshed after it, nothing pending.relayer is null when no relayer key is configured: gasless deposits and withdrawals are unavailable and their routes 503.{ "ok": true, "latestSnapshotId": 42, "asOf": 1756677600, "users": 1337,
"inputs": {
"arcusVolume": { "enabled": true, "degraded": false, "fetchedAt": 1756677000 },
"ammVolume": { "enabled": true, "degraded": false, "fetchedAt": 1756677000 },
"points": { "enabled": true, "degraded": false, "fetchedAt": 1756677000 },
"holdings": { "enabled": true, "degraded": false, "fetchedAt": 1756677000 } },
"final": false,
"relayer": { "wallets": 2, "redisHealthy": true } }
/leaderboard?limit=50&offset=0The current queue, paginated.
limit defaults to 50, max 500. offset defaults to 0.totalDepositsUsd is the pool-wide sum in whole USD — an aggregate, not anyone's balance.bonusPending: true marks a new depositor ranked on deposits alone while their trading, holdings and points are fetched; it clears within seconds.{ "snapshotId": 42, "asOf": 1756677600, "users": 1337,
"totalDepositsUsd": 523000, "limit": 50, "offset": 0,
"rows": [ { "rank": 1, "user": "0xabc…", "depositUsd": 12000, "bonusPending": false }, … ] }
/leaderboard/:addressOne user's queue position.
{ "snapshotId": 42, "asOf": 1756677600, "users": 1337, "user": "0xabc…", "rank": 17, "bonusPending": false }
/snapshots?limit=20Ids of retained past rankings, newest first (limit max 100).
{ "snapshotIds": [42, 41, 40, …] }
/snapshots/:id?limit=50&offset=0A past ranking, same shape as /leaderboard.
Static presentation data for the IPO page — name, ticker, funding history, links — from a catalog committed with the service. Dollar figures are display strings. Nothing here changes at runtime.
/poolsEvery indexed pool with metadata, as { pools: [ { pool, …metadata } ] }.
/pools/:addressOne pool's metadata.
{ "pool": "0x033d…0973", "name": "Anthropic", "ticker": "ANTH", "logoUrl": null,
"description": "Anthropic is an AI safety and research company…",
"marketCapEstimate": { "low": "$1.5T", "high": "$2.5T" }, "closeTime": null,
"links": { "website": "https://www.anthropic.com", "x": "https://x.com/anthropicai" },
"details": { "sector": "Artificial Intelligence / Enterprise Software", "founded": 2021,
"founder": "Dario Amodei", "headquarters": "San Francisco, CA",
"type": "Public Benefit Corporation" },
"fundingRounds": [ { "label": "Series F", "year": 2025, "amountUsd": "$183B" }, … ],
"totalRaise": "$44B+", "lastValuation": "$1.3T" }
The user signs; the service submits and pays the gas. Present only when a relayer key is configured — check relayer on /health first, and fall back to the user calling the pool directly (for a Paxos pool, deposit through the Paxos deposit adapter — the pool takes the wrapped token — but withdraw from the pool, which pays out USDG). Every consequential field is inside the signature, so the relayer cannot redirect a deposit or withdrawal; it is only ever msg.sender. Amounts, nonces and deadlines are decimal STRINGS in every body: they exceed Number.MAX_SAFE_INTEGER, and a JSON number would round the signed value into one that no longer matches the signature (400). Failures are { error, code, retryable }: retryable: true means resend the same request. 200 carries txHash (sent — wait for the receipt), 202 carries permitTxHash (only the folded permit went out — wait for its receipt before resending), 503 means nothing was sent. FRONTEND.md maps every code.
/relayDepositRelay a Permit2-signed deposit. Body: { pool, owner, beneficiary, amount, permitNonce, deadline, signature }.
additionalAllowances — one signed ERC-2612 permit — and the relayer lands permit() and the deposit back-to-back, both on our gas.additionalAllowances is [{ token, value, deadline, signature }]: at most one entry, token must be the token the deposit pulls (the pool's quote token, or USDG for a Paxos pool), value must cover amount, signature exactly 65 bytes. The permit's spender is always Permit2 — it is not a field. Signed over the TOKEN's own EIP-712 domain with the TOKEN's sequential nonces(owner); see the second typed payload below. Silently dropped when the allowance already suffices, so it is safe to always include for a first-time depositor.permitNonce is a Permit2 *unordered* nonce — any unused 256-bit value, no sequence required. Reusing a spent one is a 400.spender in the signature is the ADAPTER, not the pool: it receives the pulled tokens before forwarding them.quoteToken() is the Paxos wrapped token deposits through the Paxos deposit adapter instead: spender is that adapter, token is the underlying (USDG) it wraps 1:1, any folded permit approves USDG, and the witness struct is named TokenAllocationPaxosDeposit (same fields). Every other pool pulls its own quoteToken() through the Permit2 adapter.txHash is real but pending (permitTxHash too, when a permit was folded). 400 is a bad request (code says which); 202 with permitTxHash means only the folded permit was sent: wait for its receipt (up to ~10s), then resend the same request. Don't send a fresh deposit before the signature's deadline: both can land.// domain — Permit2's, and it has NO version field
{ "name": "Permit2", "chainId": <CHAIN_ID>,
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3" }
// types
PermitWitnessTransferFrom(TokenPermissions permitted, address spender, uint256 nonce, uint256 deadline, TokenAllocationDeposit witness)
TokenPermissions(address token, uint256 amount)
TokenAllocationDeposit(address owner, address beneficiary, address pool)
// message
{ "permitted": { "token": <quoteToken>, "amount": <amount> },
"spender": <DEPOSIT_ADAPTER>, "nonce": <permitNonce>, "deadline": <unix seconds>,
"witness": { "owner": <owner>, "beneficiary": <owner>, "pool": <pool> } }
// primaryType: PermitWitnessTransferFrom
// additionalAllowances[0].signature signs the TOKEN's domain (read it
// via eip712Domain(); the nonce is the token's nonces(owner)):
Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)
// message: { "owner": <owner>, "spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
// "value": <value>, "nonce": <token.nonces(owner)>, "deadline": <unix seconds> }
// → { "txHash": "0x…", "relayer": "0x…", "pool": "0x…", "owner": "0x…",
// "beneficiary": "0x…", "amount": "25000000000", "permitTxHash": "0x…"? }
/relayerWalletsWallets that may be named as a withdrawal's executor.
executor against msg.sender, so a signature naming an address we do not hold is unusable.{ "wallets": ["0x99…", "0x88…"] }
/poolNonce/:pool/:ownerThe nonce a withdrawal or allocation-request signature must carry.
{ "pool": "0x033d…0973", "owner": "0xabc…", "nonce": "3" }
/relayWithdrawRelay a signed withdrawal. Body: { pool, owner, recipient, executor, amount, nonce, deadline, signature }.
version.quoteToken() is the Paxos wrapped token is signed the same way and pays recipient USDG: the pool unwraps through its withdrawal unwrapper. recipient cannot be the pool or its unwrapper.executor we do not hold, more than the owner's unallocated deposit, or an expired deadline.// domain — the POOL is the verifying contract
{ "name": "TokenAllocationPool", "version": "1",
"chainId": <CHAIN_ID>, "verifyingContract": <pool> }
// types
Withdraw(address owner, address recipient, address executor, uint256 amount, uint256 nonce, uint256 deadline)
// message
{ "owner": <owner>, "recipient": <owner>, "executor": <from /relayerWallets>,
"amount": <amount>, "nonce": <from /poolNonce>, "deadline": <unix seconds> }
// primaryType: Withdraw
// → { "txHash": "0x…", "relayer": "0x…", "pool": "0x…", "owner": "0x…",
// "recipient": "0x…", "amount": "12500000000" }
/relayAllocationRequestRelay a signed allocation request. Body: { pool, user, newQuoteAmount, maxPriceUsd, slippageBps, waiveCancellationRights, expectedQuoteAmount, nonce, deadline, signature }.
executor field: the pool does not bind this signature to a sender.newQuoteAmount cannot exceed their deposit.expectedQuoteAmount is the request being replaced: 0 for a first request, otherwise allocationRequests(user).quoteAmount read just before signing. 400 allocation_state_stale if it has changed since: regenerate the request form from fresh state and have the user confirm before re-signing.maxPriceUsd is USD per whole offered token in 1e18 fixed point, and cannot be below finalPriceUsd() once that is set; slippageBps caps the discount from the oracle-implied amount (at most the pool's maxSlippageBps()).waiveCancellationRights is part of the signed struct. false: relayable only before an allocation lock is scheduled. true: relayable any time and fillable while locked — but the request cannot be cancelled once locked.cancelAllocationRequest()/claimOfferedTokens() remain for a user sending their own transaction.// domain — the POOL is the verifying contract
{ "name": "TokenAllocationPool", "version": "1",
"chainId": <CHAIN_ID>, "verifyingContract": <pool> }
// types
AllocationRequest(address user, uint256 newQuoteAmount, uint256 maxPriceUsd, uint256 slippageBps, bool waiveCancellationRights, uint256 expectedQuoteAmount, uint256 nonce, uint256 deadline)
// message
{ "user": <user>, "newQuoteAmount": <newQuoteAmount>, "maxPriceUsd": <1e18 USD>,
"slippageBps": <bps>, "waiveCancellationRights": <bool>,
"expectedQuoteAmount": <current request, 0 if none>,
"nonce": <from /poolNonce>, "deadline": <unix seconds> }
// primaryType: AllocationRequest
// → { "txHash": "0x…", "relayer": "0x…", "pool": "0x…", "user": "0x…",
// "newQuoteAmount": "5000000", "maxPriceUsd": "25000000000000000000",
// "slippageBps": "50", "waiveCancellationRights": false,
// "expectedQuoteAmount": "0" }
/relayCancelAllocationRequestRelay a signed cancellation. Body: { pool, user, nonce, deadline, signature }.
waiveCancellationRights: true cannot be cancelled once locked.// domain — the POOL is the verifying contract
{ "name": "TokenAllocationPool", "version": "1",
"chainId": <CHAIN_ID>, "verifyingContract": <pool> }
// types
CancelAllocationRequest(address user, uint256 nonce, uint256 deadline)
// message
{ "user": <user>, "nonce": <from /poolNonce>, "deadline": <unix seconds> }
// primaryType: CancelAllocationRequest
// → { "txHash": "0x…", "relayer": "0x…", "pool": "0x…", "user": "0x…" }
/relayClaimOfferedTokensRelay a signed claim of filled offered tokens. Body: { pool, user, nonce, deadline, signature }.
user — the signature carries no recipient, so the relayer cannot redirect the tokens.// domain — the POOL is the verifying contract
{ "name": "TokenAllocationPool", "version": "1",
"chainId": <CHAIN_ID>, "verifyingContract": <pool> }
// types
ClaimOfferedTokens(address user, uint256 nonce, uint256 deadline)
// message
{ "user": <user>, "nonce": <from /poolNonce>, "deadline": <unix seconds> }
// primaryType: ClaimOfferedTokens
// → { "txHash": "0x…", "relayer": "0x…", "pool": "0x…", "user": "0x…" }
Credits a trading wallet's volume to a depositing account. The wallet (not the account) signs an EIP-712 message; a per-(account, wallet) nonce makes each signature single-use. An account may link at most 20 wallets, and a wallet linked to one account must be unlinked before it can move to another (409 otherwise).
/linkedWallets/:addressWallets currently linked to an account.
{ "account": "0xabc…", "wallets": ["0xdef…"] }
/linkNonce/:account/:walletThe nonce the next link/unlink signature must carry.
{ "account": "0xabc…", "wallet": "0xdef…", "nonce": 3 }
/linkWalletLink a wallet. Body: { account, wallet, nonce, signature }.
signature is the wallet's EIP-712 signature over the typed data below. ERC-1271 contract wallets verify too.// domain
{ "name": "IPO Leaderboard", "version": "1", "chainId": <CHAIN_ID> }
// types
LinkWallet(address account, address wallet, uint256 nonce)
// message: the lowercased account + wallet and the nonce from /linkNonce
/unlinkWalletUnlink a wallet. Same body and signature scheme as /linkWallet.