Skip to content

Subgraph: Query stake.link Data with GraphQL

stake.link publishes a public subgraph that indexes the protocol’s Ethereum contracts. Query it with GraphQL to get reward history, total staked and holder balances without running your own indexer.

https://graph-readonly.linkpool.pro/subgraphs/name/stakedotlink-ethereum-mainnet
  • Network: Ethereum mainnet
  • Method: POST with a JSON body {"query": "..."}
  • Authentication: none
  • CORS: open to all origins, so you can call it directly from a browser
Terminal window
curl -s https://graph-readonly.linkpool.pro/subgraphs/name/stakedotlink-ethereum-mainnet \
-H 'Content-Type: application/json' \
-d '{"query":"{ _meta { block { number timestamp } hasIndexingErrors } }"}'

The _meta query returns the last indexed block. Use it to check that the data is up to date.

From JavaScript:

const SUBGRAPH =
'https://graph-readonly.linkpool.pro/subgraphs/name/stakedotlink-ethereum-mainnet';
async function querySubgraph(query, variables = {}) {
const res = await fetch(SUBGRAPH, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
});
const { data, errors } = await res.json();
if (errors) throw new Error(errors[0].message);
return data;
}

Each rebase of the LINK StakingPool creates a LinkStakingDistribution entry.

{
linkStakingDistributions(first: 5, orderBy: ts, orderDirection: desc) {
ts
reward_rate
reward_amount
total_staked
fee_percentage
fees
time_since_last_distribution
tx_hash
}
}
Field Meaning
ts Unix timestamp of the distribution, in seconds
reward_rate Annualized net reward rate (after fees) since the previous distribution, in percent
reward_amount Gross rewards earned in this rebase, including fees, in wei (18 decimals)
total_staked Total LINK staked, in wei
fee_percentage Share of rewards taken as protocol fees, in percent
fees Fees taken, in wei
time_since_last_distribution Seconds since the previous distribution

polStakingDistributions and espStakingDistributions have the same core fields:

{
polStakingDistributions(first: 1, orderBy: ts, orderDirection: desc) {
ts
reward_rate
total_staked
}
espStakingDistributions(first: 1, orderBy: ts, orderDirection: desc) {
ts
reward_rate
total_staked
}
}

tokenHolder returns an address’s LST balances and the rewards it has earned. The id is the address in lowercase.

query Holder($id: ID!) {
tokenHolder(id: $id) {
stLinkBalance
wstLinkBalance
stLinkAllRewardsEarned
stPolBalance
stPolAllRewardsEarned
stEspBalance
stEspAllRewardsEarned
sdlBalance
}
}

Pass the address as a variable, for example {"id": "0x911d86c72155c33993d594b0ec7e6206b4c803da"}.

{
tokenHolders(first: 10, orderBy: stLinkBalance, orderDirection: desc) {
id
stLinkBalance
}
}

The largest holders include protocol contracts, such as the Priority Pool and the wstLINK wrapper. Compare against the contract addresses to filter them out.

  • Amounts are strings in wei. Token amounts are 18-decimal integers returned as strings. Divide by 1018 with a big-number library, not with JavaScript Number.
  • Addresses are lowercase. Use lowercase addresses in id and where filters.
  • Pagination: each query returns at most 1,000 items. Page with first and skip, or filter on ts for long histories.
  • Explore the schema: every entity and field is available through GraphQL introspection. Open the endpoint in a GraphQL client such as Altair or Postman to browse it.