Data & Analytics
The wstGBP subgraph indexes the token and its modules on Ethereum mainnet via The Graph , exposing supply, holders, price history, gate config, and the full redemption lifecycle over GraphQL. Use it for anything historical or aggregated; use direct reads for live state. For common aggregates over plain REST (no Graph API key), see the REST API.
Endpoints
Confirm the production query URL before relying on it: the live gateway URL is issued at deploy time and is not committed in the source repo. Querying the decentralized gateway requires your own Graph API key.
- Studio slug:
wstgbp - Known deployment id:
BEJdaXsoMoJwXJgcaUYKfXqksmov6NRzhdJT8PxHphoG(view in Graph Explorer )
# Decentralized gateway (production). Send your Graph API key as a header:
# Authorization: Bearer <API_KEY>
https://gateway.thegraph.com/api/subgraphs/id/BEJdaXsoMoJwXJgcaUYKfXqksmov6NRzhdJT8PxHphoG
# (legacy key-in-path form, still accepted)
https://gateway.thegraph.com/api/<API_KEY>/subgraphs/id/BEJdaXsoMoJwXJgcaUYKfXqksmov6NRzhdJT8PxHphoG
# Subgraph Studio (development / testing)
https://api.studio.thegraph.com/query/<STUDIO_ID>/wstgbp/<VERSION>Indexed data sources
All start at block 24852284 (the deployment cluster):
| Source | Address | Indexed |
|---|---|---|
wstGBP (token) | 0x57C3571f10767E49C9d7b60feb6c67804783B7aE | Transfer, Approval, ContractCreated, ContractRedemption, ClaimProcessed, Settled, Issued, Smelted |
Price (pip) | 0x6A79dCe61A12aa4b75449e0B03746260765D07dF | Poke, Kiss, Diss, File, proxy/admin events |
Gate (act) | 0xB59cB4d3075a8ce5013C78e8Bd7aDA3Fd1300f7f | Open/Halt mint & burn, Bpsin, Bpsout, Cooldown, Capacity |
Treasury (adm) | 0xa8F5bE8457D6ed5c659647fa8d107C3F2086626A | Bestow, Depose (issuer registry) |
Guard (cop) | 0x794cF5948444b14105587455EbE96Caace036d52 | proxy/admin events |
Key entities
Protocol(singleton, id"wstGBP"):totalSupply,holderCount,totalMinted,totalRedemptionClaims,totalClaimed(tGBP actually paid out, vs claims registered),redemptionCount,totalIssued,totalSmelted,totalSettled, plus links tocurrentPriceandgateConfig.Account: per-holder balance and lifetime totals;isIssuer.Redemption(+RedemptionStatus:PENDING/PARTIALLY_CLAIMED/FULLY_CLAIMED) andClaimEvent: the redemption lifecycle per on-chain id.PriceUpdatehistory +CurrentPricesingleton (with the feed’s decimals);PriceFileEvent(oracleFileparameter changes) andFeedReader/FeedReaderEvent(the Kiss/Diss price-feed reader allowlist).GateConfigsingleton: theopenMint/haltMint/openBurn/haltBurnmarket-window schedule plusbpsin/bpsout/cooldown/capacity, withGateConfigChangehistory.Issuer/IssuerEvent: the treasury issuer registry (Bestow/Depose).- Event logs:
TransferEvent,ApprovalEvent,MintEvent,IssueEvent,SmeltEvent,SettleEvent; admin/proxy:Ward+WardEvent,ImplementationChange,ProxyState.
Denomination gotchas: redemption and claim amounts are denominated in the underlying
gem (tGBP), not in wstGBP burned; the wstGBP burned is captured by the Transfer to
0x0. And the raw price on PriceUpdate / CurrentPrice is in the feed’s own
decimals (CurrentPrice.priceDecimals), whereas the token’s navprice() used for
quoting is always WAD (18 decimals).
Example queries
{
protocol(id: "wstGBP") {
totalSupply
holderCount
totalRedemptionClaims
currentPrice { price priceDecimals isPaused }
gateConfig { bpsin bpsout cooldown capacity }
}
redemptions(where: { status: PARTIALLY_CLAIMED }) {
id
claimAmount # underlying-gem (tGBP) owed, NOT wstGBP burned
remaining
claims { amount timestamp }
}
transferEvents(first: 5, orderBy: timestamp, orderDirection: desc) {
from
to
amount
isMint
isBurn
}
}Price history (for charting accrual)
{
priceUpdates(first: 100, orderBy: timestamp, orderDirection: asc) {
price
timestamp
isPause
}
}The rising price series is the NAV curve described in Architecture.
Trailing APR from the NAV curve
To turn the curve into a headline figure, measure the return across a trailing window
(30 days here) and annualize over the actual elapsed seconds. Pokes land weekly, so
fixed-window math would distort the figure. Fetch the in-window pokes plus one anchor
poke just before the window start (the first in-window poke can be up to a week inside
the boundary), excluding pauses (isPause: false):
query NavGrowth($since: BigInt!) {
window: priceUpdates(
first: 100, orderBy: timestamp, orderDirection: asc,
where: { timestamp_gte: $since, isPause: false }
) { price timestamp }
anchor: priceUpdates(
first: 1, orderBy: timestamp, orderDirection: desc,
where: { timestamp_lt: $since, isPause: false }
) { price timestamp }
}const WAD = 10n ** 18n
const wdiv = (x: bigint, y: bigint) => (x * WAD) / y
const SECONDS_PER_YEAR = 365n * 24n * 60n * 60n // simple APR, no compounding
// start = the anchor poke (or the first in-window poke if no anchor exists),
// end = the latest in-window poke
function computeTrailingApr(
start: { price: bigint; timestamp: number },
end: { price: bigint; timestamp: number },
): bigint | null {
if (start.price <= 0n) return null
const elapsed = BigInt(end.timestamp - start.timestamp)
if (elapsed <= 0n) return null
const growthWad = wdiv(end.price - start.price, start.price)
return (growthWad * SECONDS_PER_YEAR) / elapsed // APR in WAD: 1e18 == 100%
}$since is now - 30 days as a unix timestamp. Both prices come from the same feed, so
the feed-decimals caveat above cancels out in the ratio.