Skip to Content
wstGBP is live on Ethereum mainnet · all addresses in these docs are verifiable on-chain
ReferenceREST API

REST API

https://api.arb.capital is a free, public REST API operated by Arb Capital . It serves wstGBP state as plain JSON over GET — live on-chain reads (NAV, prices, reserves, quotes) and indexed history (snapshots, APR, events, per-chain supply) — with no API key or authentication required.

Pick the right tool for the job: use this API when you want numbers without running any infrastructure, the subgraph for arbitrary historical queries over GraphQL, and direct contract reads when you need trust-minimized live state.

Responses are edge-cached, and history routes serve periodically collected snapshots (hourly for wrapper and pool metrics, 6-hourly for per-chain supply). Treat this API as a convenience layer — for settlement-grade numbers, read the contracts directly.

Conventions

  • {token} path segment — accepts the token slug (wstgbp) or the home-chain contract address 0x57C3571f10767E49C9d7b60feb6c67804783B7aE, case-insensitive. Unknown tokens return 404.
  • Amounts — wei values are serialized as strings (BigInt-safe); fields with a Float suffix carry the same value as a display-precision float. Prices (navprice, mintcost, burncost) are WAD — 18-decimal fixed point, tGBP per 1 wstGBP (see the NAV mechanic).
  • Rate limits — per IP, per path, over a 1-minute window: 60 req/min by default, 30 req/min for routes that read the chain live (/api/tokens/{token}, /reserves, /pools, /quote). Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; exceeding the limit returns 429 with Retry-After: 60.
  • Caching — most routes are served through an edge cache; the freshness column below is each route’s cache lifetime, so a response can be up to that many seconds old (plus a short stale-while-revalidate grace). The cache directives are consumed at the edge and not echoed back — to see whether a response was cached, check the Age and X-Vercel-Cache (HIT / MISS) response headers rather than Cache-Control. /quote is never cached.
  • CORS — server-side calls need no special headers. Browser (cross-origin) access is limited to allowlisted origins; email ops@wstgbp.com to request one.

Endpoints

EndpointReturnsFreshness
/api/tokensToken list with cached metrics120 s
/api/tokens/{token}Live wrapper state60 s
…/snapshotsHourly NAV / supply / TVL history300 s
…/dailyDaily rollup of the same series300 s
…/aprTrailing APR from the NAV curve300 s
…/price-historyOracle poke history300 s
…/supplyPer-chain supply (home + bridged)300 s
…/total-supply, …/circulating-supplyPlain-number supply (listing sites)300 s
…/chainsStatic bridge / deployment metadata3600 s
…/reservesLive backing / collateralization check60 s
…/eventsMint / redeem / claim event feed120 s
…/poolsLive Uniswap v4 pool state60 s
…/pools/historyHourly pool snapshots300 s
…/quoteMint / redeem previewuncached

GET /api/tokens

All tracked tokens with cached headline metrics (currently just wstGBP).

{ "tokens": [{ "slug": "wstgbp", "name": "Wren Staked tGBP", "symbol": "wstGBP", "decimals": 18, "chainId": 1, "address": "0x57C3571f10767E49C9d7b60feb6c67804783B7aE", "underlying": { "symbol": "tGBP", "address": "0x27f6c8289550fCE67f6B50BeD1F519966aFE5287", "decimals": 18 }, "description": "NAV-accruing GBP token wrapping tGBP. A weekly oracle poke raises the redemption price; balances never rebase.", "deploymentCount": 11, "poolCount": 3, "apr30d": 3.5, // trailing 30d simple APR, percent "apr7d": 3.4, // may be null — pokes land weekly "aprTrend": "stable", // "up" | "down" | "stable" "navprice": "1005372972418361109", "navpriceFloat": 1.005372, "tvl": "19939965230686339736621", "tvlFloat": 19939.96523, "holderCount": 34, "updatedAt": "2026-07-12 03:00:45" }] }

GET /api/tokens/{token}

Token detail with live on-chain wrapper state.

{ "slug": "wstgbp", "name": "Wren Staked tGBP", "symbol": "wstGBP", "decimals": 18, "chainId": 1, "address": "0x57C3571f10767E49C9d7b60feb6c67804783B7aE", "underlying": { "symbol": "tGBP", "address": "0x27f6c8289550fCE67f6B50BeD1F519966aFE5287", "decimals": 18 }, "live": { "navprice": "1005372972418361109", // WAD: tGBP per 1 wstGBP "navpriceFloat": 1.005372, "mintcost": "1005372972418361109", // WAD buy price (nav + bpsin) "mintcostFloat": 1.005372, "burncost": "1002859539987315207", // WAD sell price (nav - bpsout) "burncostFloat": 1.002859, "totalSupply": "19830263038853336707255", "totalSupplyFloat": 19830.263038, "tvl": "19939965230686339736621", // underlying tGBP held by the wrapper "tvlFloat": 19939.96523, "mintable": true, "burnable": true, "capacity": "115792089237316195423570985008687907853269984665640564039457584007913129639935", // 2^256-1 = uncapped "cooldown": "0", // 0 = atomic redemption "redemptionCount": "24", "feeBps": { "in": 0, "out": 25 } }, "fetchedAt": "2026-07-12T03:15:43.371Z" }

GET /api/tokens/{token}/snapshots

Hourly NAV price / supply / TVL snapshots, ascending (max 1000 rows).

ParamTypeRequiredDescription
daysnumberNoLookback period (default 30, 1–365)
{ "snapshots": [{ "timestamp": "2026-07-12 00:36:30", "navprice": "1005372972418361109", "total_supply": "19830263038853336707255", "tvl": "19939965230686339736621" }] }

GET /api/tokens/{token}/daily

Daily rollup — the last snapshot of each day. Same shape as /snapshots plus a date field, under a daily key.

ParamTypeRequiredDescription
daysnumberNoLookback period (default 30, 1–365)
{ "daily": [{ "date": "2026-07-12", "timestamp": "2026-07-12 03:00:45", "navprice": "1005372972418361109", "total_supply": "19830263038853336707255", "tvl": "19939965230686339736621" }] }

GET /api/tokens/{token}/apr

Trailing 30-day NAV growth annualized over the actual elapsed seconds — simple APR, no compounding, computed from the oracle-poke history. This is the same method the subgraph page derives by hand, precomputed.

{ "apr30d": 3.5, // percent "apr7d": 3.4, // percent; may be null (weekly pokes) "aprWad": "34961230279451382", // WAD, 1e18 == 100% "aprTrend": "stable", "windowDays": 30, "sampleCount": 5, // non-pause pokes inside the window "fromTimestamp": 1780680287, // unix s of the start price used "asOf": 1783698131, // unix s of the latest poke used "updatedAt": "2026-07-12 03:00:45" }

GET /api/tokens/{token}/price-history

Oracle poke history (pauses included — price is 0 and is_pause is 1 on a pause).

ParamTypeRequiredDescription
daysnumberNoLookback period (default 90, 1–730)
{ "updates": [{ "price": "1002695027107482205", "is_pause": 0, "block_timestamp": 1781290787, "timestamp": "2026-06-12 18:59:47" }] }

GET /api/tokens/{token}/supply

Per-chain supply across the home chain and all bridged deployments.

Every bridged deployment is a canonical lock-and-mint bridge, so L2 supply is backed 1:1 by tokens escrowed in a home-chain contract. The primary per-chain figure (totalSupply, source: "escrow") is that L1 escrow balance, snapshotted every 6 hours. A weekly cross-check reads the actual totalSupply() on each L2 (l2Supply); an l2Supply above the escrow balance would indicate an unbacked mint on that chain. Escrow can legitimately run slightly ahead of L2 supply while deposits or withdrawals are in flight (up to the ~7-day challenge window for optimistic-rollup exits). A chain whose snapshot is older than 24 h is flagged stale. bridgedSupply mirrors escrowed home-chain tokens, so it is not additive with home supply.

{ "home": { "chainId": 1, "address": "0x57C3571f10767E49C9d7b60feb6c67804783B7aE", "totalSupply": "19830263038853336707255", // actual totalSupply() on Ethereum "totalSupplyFloat": 19830.263038, "timestamp": "2026-07-12 00:59:18", "stale": false, "source": "escrow" }, "chains": [{ "chainId": 8453, "name": "Base", "address": "0xEa4Fcd4dE37eC038947a6834751E71Bb8FE68fff", "totalSupply": "1000000000000000000", // primary: L1 escrow balance "totalSupplyFloat": 1, "timestamp": "2026-07-12 00:59:18", "stale": false, "source": "escrow", // "escrow" | "l2" "l1Escrow": "0x3154Cf16ccdb4C6d922629664174b904d80F2C35", "l2Supply": "1000000000000000000", // weekly on-L2 verification (null until first check) "l2SupplyFloat": 1, "l2Timestamp": "2026-07-12 00:36:44" }], "aggregate": { "bridgedSupply": "11000000000000000000", // sum of L1 escrow balances "bridgedSupplyFloat": 11, "chainsReporting": 11, "chainsConfigured": 11, "l2ReportedSupply": "11000000000000000000", "l2ReportedSupplyFloat": 11, "l2ChainsReporting": 11 } }

GET /api/tokens/{token}/total-supply / …/circulating-supply

Listing-site supply endpoints in the format CoinGecko requires : the response body is only the supply as a plain decimal number in token units — text/plain, no JSON wrapper. Both read the wrapper’s live totalSupply(); for wstGBP circulating equals total (no locked tranches, and bridged balances mirror escrowed home-chain supply). Error responses are still JSON.

19830.263038853336707255

GET /api/tokens/{token}/chains

Static chain and bridge metadata for every deployment — no data reads, just configuration. l1Escrow is the home-chain contract that locks the token 1:1 against the bridged supply: the L1StandardBridge (OP Stack), L1ERC20Gateway (Arbitrum Nitro), ERC20Predicate (Polygon PoS), the OmniBridge Foreign mediator (Gnosis), or L1NativeTokenVault (zkSync Elastic). Per-chain addresses and bridge walkthroughs live on the Bridges & Portals page.

{ "home": { "chainId": 1, "name": "Ethereum", "address": "0x57C3571f10767E49C9d7b60feb6c67804783B7aE" }, "chains": [{ "chainId": 8453, "name": "Base", "address": "0xEa4Fcd4dE37eC038947a6834751E71Bb8FE68fff", "bridgeType": "op-stack", // op-stack | arbitrum-nitro | polygon-pos | gnosis-omnibridge | zksync-elastic "l1Escrow": "0x3154Cf16ccdb4C6d922629664174b904d80F2C35", "explorerUrl": "https://basescan.org", "bridgeUrl": "https://bridge.base.org", "status": "live" }] }

GET /api/tokens/{token}/reserves

Live backing / collateralization check. expectedBacking = totalSupply × navprice — what full redemption would owe. Solvency is judged on net, which credits the redeem-fee buffer (diff + expectedBacking × bpsout / 10000): holders pay the exit fee, so the wrapper only needs to cover the net amount.

{ "navprice": "1005372972418361109", "navpriceFloat": 1.005372, "totalSupply": "19830263038853336707255", "totalSupplyFloat": 19830.263038, "backing": "19939965230686339736621", // underlying tGBP actually held "backingFloat": 19939.96523, "expectedBacking": "19936810495209941434186", // owed at full redemption "expectedBackingFloat": 19936.810495, "diff": "3154735476398302435", "diffFloat": 3.154735, "net": "52996761714423156020", // diff + redeem-fee buffer "netFloat": 52.996761, "bpsout": 25, "collateralizationPct": 100.01, "status": "OVER-COLLATERALIZED", // OVER-COLLATERALIZED | BALANCED | SHORTFALL "minTopUp": "0", "minTopUpFloat": 0, "fetchedAt": "2026-07-12T03:17:37.937Z" }

GET /api/tokens/{token}/events

Mint / redeem / claim event feed, newest first.

ParamTypeRequiredDescription
typemint | redeem | claimNoFilter by event type
useraddressNoFilter by account
daysnumberNoLookback period (default 30, 1–365)
limitnumberNoMax rows (default 100, 1–500)

Denominations: mint amounts are the wstGBP minted; redeem and claim amounts are the underlying tGBP — the same convention as the subgraph entities.

{ "events": [{ "action": "mint", // mint | redeem | claim "user_address": "0x1234…abcd", "amount": "53955718038521932143", "price": "1005372972418361109", // mint only "redemption_id": null, // redeem/claim only "tx_hash": "0xe89c…a468", "block_number": 25510188, "chain_id": 1, "timestamp": "2026-07-11 15:12:11" }] }

GET /api/tokens/{token}/pools

Live Uniswap v4 state, one array entry per configured venue. The primary venue is the tGBP/wstGBP backstop pool (Uniswap v4 & Aggregators): its hook routes every swap through the wrapper’s atomic mint/redeem, so it has no LP curve — fairPrice, deviationPpm, and estimatedFeePpm are null, and the wrapper’s NAV band is reported in band instead. For venues with an LP curve, price is the pool’s spot price and fairPrice the oracle-implied one (both WAD, pairToken per 1 wstGBP), deviationPpm their gap in parts-per-million, and estimatedFeePpm a per-direction fee estimate.

{ "pools": [{ "key": "backstop", "label": "tGBP/wstGBP Backstop", "type": "backstop", "poolId": "0xdb21c31f461611ebeeab8af1280c77a82bb81725e1bf9d6093fbbc207a375ce5", "hook": "0xfE36B48c9c0240991E4CEf006a2445F2ff524888", "pairToken": { "symbol": "tGBP", "address": "0x27f6c8289550fCE67f6B50BeD1F519966aFE5287", "decimals": 18 }, "sqrtPriceX96": "79228162514264337593543950336", "tick": 0, "liquidity": "0", // backstop holds no LP liquidity by design "price": "1005372972418361109", // WAD: tGBP per 1 wstGBP "priceFloat": 1.005372, "fairPrice": null, "fairPriceFloat": null, "deviationPpm": null, "estimatedFeePpm": null, "band": { // backstop only: the wrapper's NAV band "navprice": "1005372972418361109", "mintcost": "1005372972418361109", "burncost": "1002859539987315207", "bandBps": 24 } }], "fetchedAt": "2026-07-12T03:16:03.121Z" }

GET /api/tokens/{token}/pools/history

Hourly snapshots for one venue.

ParamTypeRequiredDescription
poolstringYesPool key (e.g. backstop) or 0x… poolId
daysnumberNoLookback period (default 30, 1–365)
{ "snapshots": [{ "pool_key": "backstop", "price": "1005372972418361109", "fair_price": null, // null for the backstop "deviation_ppm": null, "liquidity": "0", "fee_mint_ppm": null, "fee_redeem_ppm": null, "timestamp": "2026-07-12 00:36:42" }] }

GET /api/tokens/{token}/quote

Preview a mint or redeem at current on-chain prices. Amounts are exact-input. The response echoes the on-chain WsgemQuoter verdict under quoter — its previewSwap also checks cooldown and wrapper funding, and overrides the local executability checks when configured. Never cached.

ParamTypeRequiredDescription
sidemint | redeemYesmint pays tGBP in, redeem pays wstGBP in
amountstringYesExact input amount in wei (BigInt as string)
// GET /api/tokens/wstgbp/quote?side=mint&amount=250000000000000000000 { "side": "mint", "amountIn": "250000000000000000000", // tGBP in "amountOut": "248663935532940386388", // wstGBP out "amountOutFloat": 248.663935, "price": "1005372972418361109", // mintcost used "priceFloat": 1.005372, "feeBps": 0, "executable": true, "reason": null, // e.g. "below mint dust threshold", "exceeds capacity" "cooldown": "0", "quoter": { "amountIn": "250000000000000000000", "amountOut": "248663935532940386388" } }

Errors

Errors are JSON — { "error": "<message>" } with a matching HTTP status:

StatusExample bodyWhen
400{ "error": "side must be mint or redeem" }Invalid or missing query parameters
404{ "error": "Token not found" } / { "error": "Pool not found" }Unknown {token} segment or pool
429{ "error": "Too many requests" }Rate limit exceeded — retry after Retry-After seconds
500{ "error": "Database error" }History routes, transient
502{ "error": "On-chain read failed" }Live routes, transient — safe to retry

The two plain-text supply endpoints (/total-supply, /circulating-supply) also return JSON for their error cases.

Last updated on