From stakewise-data-query
Use when the user asks a natural-language question (in any language) about StakeWise V3 staking data — APY, vault TVL, their stake or earnings, exit-queue ETA, osETH/osGNO mint capacity or health factor, boost position, leverage borrow LTV, distributor (merkle) claims, vesting positions, transaction history, exchange rates, or sub-vaults. Read-only. Talks to the public StakeWise subgraphs and backend GraphQL on Mainnet and Gnosis via WebFetch / curl. No SDK install or local server required. Always answer in the user's language. Skip when @stakewise/v3-sdk is imported in the project — that's a developer use case handled by a sibling skill.
How this skill is triggered — by the user, by Claude, or both
Slash command
/stakewise-data-query:stakewise-data-queryThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Answer questions about StakeWise V3 by hitting public read endpoints directly. No SDK, no local server, no auth.
Answer questions about StakeWise V3 by hitting public read endpoints directly. No SDK, no local server, no auth.
Flow. User question → identify data domain → consult the matching entity reference → build GraphQL → WebFetch (parallel where possible) → parse → respond in user's language.
Language. Respond in the same language the user wrote in. Field names, GraphQL keywords, URLs, and 0x addresses stay in their original form — only the prose around them is translated.
Checkpoint once and let the user decide whether to retry.Inputs needed before any query:
vaults(where: { displayName_contains_nocase: "<name>" }, first: 5). None → suggest the other network or check spelling. Many → list candidates.orderBy: totalAssets, orderDirection: desc, first: 10. "Best APY" → orderBy: apy.Lowercase every 0x address before where:. Mixed-case silently returns empty.
BigInt fields are strings — BigInt(...) before arithmetic. Never JS Number.
Cite from sections below — never invent field names or thresholds.
Filter operators: numeric (_gt, _gte, _lt, _lte, _in), boolean (exact), string (_contains_nocase), references (_: { ... } for nested).
| Plane | Mainnet | Gnosis |
|---|---|---|
| Subgraph (primary) | graphs.stakewise.io/mainnet/subgraphs/name/stakewise/prod | graphs.stakewise.io/gnosis/subgraphs/name/stakewise/prod |
| Subgraph (replica) | graphs-replica.stakewise.io/mainnet/subgraphs/name/stakewise/prod | graphs-replica.stakewise.io/gnosis/subgraphs/name/stakewise/prod |
| Backend GraphQL | mainnet-api.stakewise.io/graphql | gnosis-api.stakewise.io/graphql |
Fallback (subgraph only): primary first. On HTTP 5xx, network timeout, or malformed JSON, retry once against the replica for that network. A 200 OK with errors[] is a query bug — fix the query, do NOT rotate. data: { entity: null } is a clean miss (answer accordingly). If both primary and replica fail, return an honest "subgraph is currently degraded" — do not synthesise.
Backend GraphQL for: vault blacklist / hidden / verified flags, paginated validators, OFAC sanctions list, average exit-queue ETA, scoring breakdown. Subgraph handles everything else. Available backend queries: vaults, vaultValidators, ofacAddresses, exitStats, scoringDetails. Backend has no replica.
Boost / leverage is Mainnet only. On Gnosis say so and skip the boost path.
Every query example in this skill is GraphQL. Send one as the query field of a JSON POST to the relevant endpoint URL — this is the only transport, used for all examples below:
curl -sS -X POST -H 'content-type: application/json' \
--data '{"query": "<paste a graphql query here>"}' \
https://graphs.stakewise.io/mainnet/subgraphs/name/stakewise/prod
Response shape: { "data": { ... } }; errors arrive as { "errors": [ ... ] } instead of data. Backend queries use the same POST against the backend URL — note the backend takes id / blacklisted / first as direct arguments on vaults(...), not inside a subgraph-style where:
{
vaults(id: "0xVAULT") {
id
blacklisted
hidden
verified
avgExitQueueLength
}
}
Singular vs plural — a silent trap. vaults(id: …) (plural with a direct id arg) is backend-only. The subgraph's vaults has no id argument: passing one is silently ignored — you get the whole vault list back with no error, so reading the "first" result gives a wrong vault. A single-vault subgraph read MUST use vault(id: …) (singular) or vaults(where: { id: … }). Subgraph-only Vault fields such as allocatorMaxBoostApy, apy, score therefore come via vault(id: …) — never vaults(id: …).
If anything above looks stale, the canonical endpoint list is the StakeWise SDK docs at https://docs.stakewise.io/sdk/endpoints (source: @stakewise/v3-sdk documentation/endpoints.md). Note both the primary host (graphs.stakewise.io) and the replica fallback host (graphs-replica.stakewise.io) serve the /prod deployment; the /stage path on either host is a separate non-production deployment — never use it for user answers.
The patterns below cover the typical user question. Execute directly; consult the relevant entity reference only if the question deviates (extra filter, extra field, multi-vault aggregate, time range).
{
allocators(
where: {
address: "0xUSER",
vault: "0xVAULT"
}
) {
assets
apy
totalEarnedAssets
ltvStatus
mintedOsTokenShares
vault {
displayName
}
}
}
Then:
assets / 1e18 → ETH staked.apy is already percent: parseFloat(apy).toFixed(2) + '%'. Do NOT multiply by 100.totalEarnedAssets / 1e18 → lifetime earnings (can be negative — surface as-is).ltvStatus → quote directly.assetsUsdRate from exchangeRates(first: 1).{
exitRequests(
where: {
owner: "0xUSER",
isClaimed: false
}
) {
isClaimable
withdrawalTimestamp
totalAssets
}
}
Two branches:
isClaimable == true → "ready to withdraw N ETH now".new Date(withdrawalTimestamp * 1000)" if set; otherwise fetch backend exitStats { duration } and surface network-wide average ("network average is ~D days").Field shapes in the Position entities.
Prefer the precomputed Allocator.ltvStatus enum (Healthy / Moderate / Risky / Unhealthy) — quote it directly. Only when the user explicitly wants a numeric health factor, compute it with the HF formula and status mapping in Units and gotchas (LtvStatus). For boost users, surface both LTVs — see Boost entities.
Private vaults gate deposits via a whitelist; some vaults also maintain a blocklist of denied addresses. Fire one combined query:
{
vault(id: "0xVAULT") {
isPrivate
isBlocklist
whitelister
blocklistManager
}
privateVaultAccounts(
where: {
vault: "0xVAULT",
address: "0xUSER"
}
) {
id
}
vaultBlockedAccounts(
where: {
vault: "0xVAULT",
address: "0xUSER"
}
) {
id
}
}
Canonical query names: privateVaultAccounts (whitelist for isPrivate vaults) and vaultBlockedAccounts (blocklist). The natural words "whitelistAccounts" / "blocklistAccounts" do NOT exist on prod and would 200-with-errors[].
Decision:
isPrivate == false && isBlocklist == false → public, anyone can stake.isPrivate == true → "yes" only if privateVaultAccounts is non-empty. Empty → "no, not on whitelist"; surface whitelister so the user knows who to ask.isBlocklist == true → "no" if vaultBlockedAccounts is non-empty; otherwise "yes". Surface blocklistManager in the "no" branch.Field shapes in the Network and misc entities.
Run this same query against both the Mainnet and Gnosis subgraph URLs in parallel (separate WebFetch calls — never sequential):
{
allocators(where: { address: "0xUSER" }) {
assets
totalEarnedAssets
vault {
id
displayName
}
}
}
Sum assets / 1e18 per network → { mainnet: N ETH, gnosis: M GNO }. ETH and GNO are different assets — do NOT add them. For USD totals, multiply each by its network's assetsUsdRate (Gnosis has none on its own subgraph — see Units and gotchas → Gnosis quirks for the Mainnet-fallback rule).
One query finds every vault where an address has a stake, a pending exit, or a boost position — no need to scan the whole vault list. Run against both the Mainnet and Gnosis subgraphs in parallel.
{
vaults(
where: {
or: [
{ allocators_: { address: "0xUSER" } },
{ exitRequests_: { owner: "0xUSER" } },
{ leveragePositions_: { user: "0xUSER" } }
]
}
) {
id
displayName
}
}
Branches: allocators_ = active stake · exitRequests_ = in the exit queue · leveragePositions_ = boost position. A vault appears once if any branch matches; drop branches to narrow (keep only allocators_ for "where am I staking now?"). Then pull per-vault details with the matching Quick lookup above.
where: { address }?errors[] → query bug. NOT a network issue, do NOT rotate. Verify field via introspection (see Units and gotchas → Verify unknown fields), then retry.feePercent basis points i.e. ÷100 for percent, OsTokenConfig.*Percent ×1e16, Aave.*Percent ×1e18, apy already %, snapshot timestamps in microseconds). Or wei→ETH (÷1e18). Negative totalEarnedAssets is real — surface as-is.Checkpoint entity (Network and misc entities) and compare its timestamp with now; surface the lag and suggest a retry if stale.If none apply → introspect the entity to verify the field still exists on prod (schemas evolve).
app.stakewise.io or @stakewise/v3-sdk.eth_call reads (vesting claimable amount, convertToAssets rate, contract liveness). Defer to app.stakewise.io.sETH2 / rETH2) — legacy and out of scope. If a user mentions V2 / sETH2 / rETH2 leftovers, point them to app.stakewise.io to migrate; the skill does not detect, perform, or explain the migration.Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
npx claudepluginhub stakewise/llm-tools --plugin stakewise-data-query