Public GraphQL Subgraphs
Public Soccerverse GraphQL subgraphs for SVC/Democrit trading, Xaya chain stats, and shop, referral and pricing data.
Purpose: document the read-only GraphQL subgraphs used by Soccerverse clients and tooling. These subgraphs index public Polygon/Xaya events and expose query-only GraphQL endpoints.
Scope
Covered here:
- Democrit/SVC GraphQL for Soccerverse exchange trades, orders, vaults, pools, and deposits.
- Xaya account/name stats used for wallet/name lookup and generic public chain stats.
- Soccerverse-specific Polygon subgraph data for referrals, pack/shop sales, sale pricing, club sale status, and WCHI/USDC/WETH price observations.
Access model
Protocol: HTTP POST with JSON body.
Header:
Content-Type: application/json
No authentication is required for the public endpoints. If these endpoints are later put behind Cloudflare or a gateway bypass, examples should use placeholders only, for example:
-H "X-Cloudflare-Bypass-Secret: $CLOUDFLARE_BYPASS_SECRET"
Do not put real secret values in docs, logs, curl history, or committed test fixtures.
Endpoint catalogue
| Family | Public alias URL | Deployment hash / direct Graph Node path | Source references |
|---|---|---|---|
| Democrit/SVC production | https://graph.soccerverse.com/democrit-sv | /subgraphs/id/QmeNciuwUvmGmUMsW9AMujc46fZKuByU3JzUdBQPUWzDXs | aws-soccerverse/clusters/prod/graph/ingress-democrit-sv.yaml; soccerverse_nextjs/lib/api/svc-exchange.ts; soccerverse_mcp/democrit_api.py |
| Democrit/SVC test | https://graph.soccerverse.com/democrit-svt | /subgraphs/id/QmeXoguDWYrNDH7FBiZrAXnbwPGrtawCTcViCJ1Na199ND | aws-soccerverse/clusters/prod/graph/ingress-democrit-svt.yaml; test overlay references |
| Xaya stats | https://graph.soccerverse.com/xaya-stats | /subgraphs/id/QmagTKHVncUvDTzycyqcB8zzmBFcAoAUYpEgtAjijoHLhC | aws-soccerverse/clusters/prod/graph/ingress-xaya-stats.yaml; soccerverse_nextjs/lib/api/xaya-graphql.ts |
| Soccerverse shop/referrals/pricing | https://graph.soccerverse.com/sv-subgraph | /subgraphs/id/QmSx3oQyKzn9674LhMPu2sep978SjxJAr6DqmzseHiGNLA | aws-soccerverse/clusters/prod/graph/ingress-sv-subgraph.yaml; sv-full-stack/graph-admin/sv-subgraph/*; soccerverse_mcp/shop_api.py |
Notes:
- The public alias URLs are friendlier and stable behind Nginx ingress rewrite rules. The deployment hash paths are useful for diagnostics and provenance.
- Frontend production currently sets
NEXT_PUBLIC_DEMOCRIT_GRAPHQL_URLto the Democrit deployment-hash URL andNEXT_PUBLIC_XAYA_GRAPHQL_URLto the Xaya stats deployment-hash URL inargocd-deployments/frontend/base/deployment.yaml. - The MCP shop client reads
SHOP_GRAPHQL_URL; source code shows it expects the Soccerverse shop/referrals/pricing schema exposed bysv-subgraph.
Common GraphQL patterns
Minimal curl template
curl -sS 'https://graph.soccerverse.com/sv-subgraph' \
-H 'Content-Type: application/json' \
--data '{"query":"query { saleClubs(first: 1) { clubId minted remainingInTranche } }"}'
Python standard-library template
import json
import urllib.request
url = "https://graph.soccerverse.com/democrit-sv"
query = """
query RecentTrades {
trades(first: 3, orderBy: timestamp, orderDirection: desc) {
id
timestamp
asset
amount
sats
price
seller
buyer
}
}
"""
request = urllib.request.Request(
url,
data=json.dumps({"query": query}).encode("utf-8"),
headers={
"Content-Type": "application/json",
"User-Agent": "soccerverse-docs-readonly/1.0",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
payload = json.loads(response.read().decode("utf-8"))
if "errors" in payload:
raise RuntimeError(payload["errors"])
print(payload["data"])
Note: some non-browser/stdlib clients may receive HTTP 403 without a standard User-Agent; always send a descriptive User-Agent header as shown above.
Introspection query
Use this lightweight introspection query to verify entity names and fields before writing examples:
query IntrospectionLite {
__schema {
queryType {
name
}
types {
name
kind
fields {
name
type {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
args {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
Pagination, ordering, and filtering
These subgraphs use The Graph query conventions:
first: Nlimits results. Keep docs/examples small (first: 1tofirst: 5) and avoid large pages unless explicitly needed.skip: Npaginates by offset. For large scans, prefer deterministic ordering and page in batches.orderBy: fieldandorderDirection: asc|descsort results.where: { ... }filters results. Nested filters use the underscore form, for exampleprimaryClub_: { clubId: 50 }.- Entity IDs are often bytes/hex strings or stringified numeric IDs depending on the schema. Do not assume all
idfields are decimal.
Example pagination query:
query SaleClubPage {
saleClubs(first: 5, skip: 0, orderBy: clubId, orderDirection: asc) {
clubId
minted
remainingInTranche
trancheIndex
}
}
Democrit/SVC subgraph
Production base URL:
https://graph.soccerverse.com/democrit-sv
Deployment hash:
QmeNciuwUvmGmUMsW9AMujc46fZKuByU3JzUdBQPUWzDXs
Test/staging alias:
https://graph.soccerverse.com/democrit-svt
Test deployment hash:
QmeXoguDWYrNDH7FBiZrAXnbwPGrtawCTcViCJ1Na199ND
Purpose: index Democrit exchange data for Soccerverse currency/assets, including trades, active sell/buy orders, vaults, pools, and deposits.
Source references:
sv-full-stack/graph-admin/sv-smart-contracts/lib/democrit-evm/subgraph/schema.graphqlsv-full-stack/graph-admin/sv-smart-contracts/lib/democrit-evm/subgraph/subgraph.yamlsoccerverse_nextjs/lib/api/svc-exchange.tssoccerverse_nextjs/lib/trading/vault-checkpoint.tssoccerverse_mcp/democrit_api.pysoccerverse_mcp/handlers/democrit_trades.pyaws-soccerverse/clusters/prod/graph/ingress-democrit-sv.yaml
Democrit entity table
| Entity | Root fields | Purpose | Important fields |
|---|---|---|---|
Trade | trade, trades | Completed Democrit trades | id, timestamp, asset, amount, sats, price, seller, buyer |
SellOrder | sellOrder, sellOrders | Active limit sell orders | id, vault, creator, seller, asset, amount, totalSats, price |
BuyOrder | buyOrder, buyOrders | Active limit buy orders | id, tradingPool, creator, buyer, asset, amount, totalSats, price |
Vault | vault, vaults | Existing non-empty vaults controlled by the contract | id, founder, asset, balance, linked order/pool/deposit fields |
TradingPool | tradingPool, tradingPools | Active trading pools | id, vault, operator, endpoint, asset, balance, relFee |
SellDeposit | sellDeposit, sellDeposits | Active sell deposits | id, vault, owner, asset, balance |
AutoId | autoId, autoIds | Internal sequence helper | id, next |
Democrit queries
Recent SVC/Democrit trades:
query RecentTrades {
trades(first: 3, orderBy: timestamp, orderDirection: desc) {
id
timestamp
asset
amount
sats
price
seller
buyer
}
}
Response-field notes:
timestampis a Unix timestamp in seconds, returned as a string-like GraphQLBigIntin JSON.amountis raw asset amount. Frontend source divides byAPI_VALUE_DIVISOR(10000) for SVC-style display.satsis WCHI satoshis; divide by100000000for WCHI decimal display.pricein live JSON is returned as a string. Source comments call it sats per asset unit; frontend code divides by10000for WCHI per SVC display.assetwas observed assmcin live results.
Cheapest current sell orders:
query CheapestSellOrders {
sellOrders(first: 3, orderBy: price, orderDirection: asc) {
id
vault {
id
}
seller
asset
amount
totalSats
price
}
}
Vault sample:
query VaultSample {
vaults(first: 3) {
id
founder
asset
balance
}
}
Democrit pitfalls
- GraphQL order data is not the only validation step for trading actions. Frontend source validates active sell orders on-chain with
checkSellOrdersand validates vaults with GSPget_vaultsbefore using them for trading operations. GraphQL provides read/index data only, not sufficient proof that an order can be filled. - Seller strings are case-sensitive in source comments (
fetchUserSellOrdersdeliberately avoids lowercasing the username). democrit-svtcontains test-looking data and should not be described as production SVC liquidity.- Keep example pages small. The app fetches up to 1000 sell orders for its own UI, but public docs should not encourage heavy queries.
Xaya stats subgraph
Base URL:
https://graph.soccerverse.com/xaya-stats
Deployment hash:
QmagTKHVncUvDTzycyqcB8zzmBFcAoAUYpEgtAjijoHLhC
Purpose: index public Xaya Accounts contract data on Polygon. Soccerverse frontend uses it for fast wallet/name lookup instead of many RPC calls.
Source references:
sv-full-stack/graph-admin/stats-subgraph/schema.graphqlsv-full-stack/graph-admin/stats-subgraph/subgraph.yamlsoccerverse_nextjs/lib/api/xaya-graphql.tsargocd-deployments/frontend/base/deployment.yamlaws-soccerverse/clusters/prod/graph/ingress-xaya-stats.yaml
Xaya entity table
| Entity | Root fields | Purpose | Important fields |
|---|---|---|---|
Address | address, addresses | Wallet/address currently holding or historically related to names | id, names |
Name | name, names | Xaya names, including p/ player names | id, ns, name, owner, profile/move relations |
Namespace | namespace, namespaces | Xaya namespaces | id, ns, names |
Game | game, games | Game IDs observed in moves | id, game, moves |
Move | move, moves | Raw name moves | id, tx, name, move, payment, games |
GameMove | gameMove, gameMoves | Game-specific move records | id, move, tx, game, gamemove |
Payment | payment, payments | WCHI payment attached to a move | id, move, receiver, amount |
ProfileData | introspected on Name.profileData; source schema entity | Public profile key/value data | account, admin, key, value |
Transaction | transaction, transactions | Chain transaction metadata | id, height, timestamp |
Xaya queries
Lookup the sample username snailbrain:
query NameByAccount {
names(first: 5, where: { name: "snailbrain" }) {
id
name
ns {
ns
}
owner {
id
}
}
}
Lookup names owned by a wallet:
query NamesByOwner {
addresses(where: { id: "0x59f5f46dc284e3414f67eda31ee90126bc48bf95" }) {
id
names(first: 100, orderBy: name, orderDirection: asc) {
id
name
ns {
ns
}
}
}
}
This is the shape used by frontend source (addresses(where: { id: ... }) { names(...) { id name ns { ns } } }).
Recent generic game move stats:
query RecentGameMoves {
gameMoves(first: 3, orderBy: tx__timestamp, orderDirection: desc) {
id
game {
game
}
tx {
height
timestamp
}
}
}
Xaya pitfalls
- Frontend source normalizes wallet addresses to lowercase before querying
addresses(where: { id: ... }). - The frontend filters names to namespace
pafter fetching;Name.ns.ns == "p"is the Soccerverse player-name namespace used by the app. - Some names can be blank or whitespace; do not assume every
Name.nameis display-safe without validation.
Soccerverse shop/referrals/pricing subgraph
Base URL:
https://graph.soccerverse.com/sv-subgraph
Deployment hash:
QmSx3oQyKzn9674LhMPu2sep978SjxJAr6DqmzseHiGNLA
Purpose: index Soccerverse-specific Polygon contracts for referral leaderboards, shop pack purchases, club sale status, sale pricing tiers, pack composition, and Uniswap-derived price observations.
Source references:
sv-full-stack/graph-admin/sv-subgraph/schema.graphqlsv-full-stack/graph-admin/sv-subgraph/subgraph.yamlsoccerverse_mcp/shop_api.pysoccerverse_mcp/handlers/shop.pysoccerverse_mcp/handlers/soccerverse_market.pyaws-soccerverse/clusters/prod/graph/ingress-sv-subgraph.yaml
Indexed contracts from subgraph.yaml:
| Data source | Network | Address | Start block | Main indexed events/entities |
|---|---|---|---|---|
ReferralTracker | matic | 0x329612BB11Eea1Ea5065993DC32b41C86D6068c2 | 62773319 | Referrer, Referral, ReferrerTotal, ReferrerBonus |
ClubMinter | matic | 0xeEE0d855112b71Dc68d72053594Af03F92C586Ae | 66056325 | SaleClub |
UniswapV2Factory | matic | 0x9e5A52f57b3038F1B8EeE45F28b3C1967e22799C | 66056324 | Token, TokenPair, PriceObservation |
PackSaleForReferrals template | matic | dynamic sale contracts | n/a | ReferrerBonus, ReferrerTotal |
PackSaleForShop template | matic | dynamic sale contracts | n/a | PacksBought, PriceStep, SaleClub, SaleTier, pack contents |
Soccerverse entity table
| Entity | Root fields | Purpose | Important fields |
|---|---|---|---|
Referrer | referrer, referrers | A user that has referred others | account, referrals, bonuses, totals, currentTotal |
Referral | referral, referrals | A referred account | account, referrer, timestamp |
ReferrerBonus | referrerBonus, referrerBonuses | Referral bonus from pack purchase | timestamp, clubId, packsBought, usdSpent, bonusShares |
ReferrerTotal | referrerTotal, referrerTotals | Running referral totals snapshots | timestamp, index, referrals, bonusShares, usdSpent |
SaleTier | saleTier, saleTiers | Shop sale tier / sale contract | name, active, related clubs/pricing/purchases |
PricingStep | pricingStep, pricingSteps | Price tranche data inside a tier | index, numShares, price, fromTotal, toTotal, tier |
SaleClub | saleClub, saleClubs | Club sale state | clubId, tier, pausedInTier, minted, trancheIndex, remainingInTranche |
Pack | pack, packs | Pack for a primary club | primaryClub, shares, maxPacks, cost |
PackShareContent | packShareContent, packShareContents | Clubs/shares contained in a pack | pack, club, num |
PacksBought | packsBought, packsBoughts | Individual pack purchase event | timestamp, buyer, receiver, primaryClub, tier, numPacks, usdSpent |
Token | token, tokens | Tracked token metadata | id, decimals, symbol |
TokenPair | tokenPair, tokenPairs | Tracked Uniswap pair | tradedToken, baseToken, uniswapPair, prices |
PriceObservation | priceObservation, priceObservations | Periodic Uniswap moving-average observations | virtualTimestamp, exactTimestamp, cumulativePrice, average24h, pair |
Soccerverse queries
Club sale status for sample club_id=50:
query ClubSaleStatus {
saleClubs(first: 5, where: { clubId: 50 }) {
id
clubId
minted
remainingInTranche
trancheIndex
tier {
name
active
}
pausedInTier {
name
active
}
}
}
Pack composition for sample club_id=50:
query PackForClub50 {
packs(first: 5, where: { primaryClub_: { clubId: 50 } }) {
id
maxPacks
cost
primaryClub {
clubId
minted
remainingInTranche
tier {
name
}
}
shares(first: 3) {
club {
clubId
}
num
}
}
}
Recent pack purchases:
query RecentPackPurchases {
packsBoughts(first: 3, orderBy: timestamp, orderDirection: desc) {
id
timestamp
receiver
numPacks
usdSpent
primaryClub {
clubId
minted
remainingInTranche
}
tier {
name
active
}
}
}
Current referral leaderboard:
query CurrentReferralLeaders {
referrers(first: 5, orderBy: currentTotal__referrals, orderDirection: desc) {
account
currentTotal {
timestamp
referrals
bonusShares
usdSpent
}
}
}
Prefer this over ordering referrerTotals directly when you want one row per referrer; referrerTotals is a historical snapshot table and can contain many rows for the same account.
Pricing tier steps:
query PricingSteps {
pricingSteps(first: 5, orderBy: fromTotal, orderDirection: asc) {
id
index
numShares
price
fromTotal
toTotal
tier {
name
active
}
}
}
Price observations:
query PriceObservations {
priceObservations(first: 3, orderBy: exactTimestamp, orderDirection: desc) {
id
virtualTimestamp
exactTimestamp
average24h
pair {
tradedToken {
symbol
decimals
}
baseToken {
symbol
decimals
}
}
}
}
Live results include WCHI/WETH and WETH/USDC pairs.
Soccerverse response-field notes
usdSpent,cost, andpriceare raw integer units from indexed contracts. Do not assume human decimals without checking the token/contract unit. For MCP/shop docs, keep raw values labelled as raw unless another source explicitly converts them.minted,remainingInTranche,numPacks,maxPacks, andnumSharesare plain integer counts.ReferrerTotalis append-only snapshot history. UseReferrer.currentTotalfor current leaderboard views.PriceObservation.average24his a large fixed-point integer. Source schema says prices use a2**112denominator and already account for token decimals.
Soccerverse pitfalls
- Nested filtering is useful but easy to get wrong. For club pack lookup, use
packs(where: { primaryClub_: { clubId: 50 } }), notpacks(where: { clubId: 50 }). - Historical snapshot tables (
referrerTotals,priceObservations) produce multiple rows per logical object. Explain whether an example is fetching current state or history. - The MCP
shop_api.pysanitises GraphQL string/int interpolation; docs should show variables or carefully quoted literals, not encourage raw string concatenation from user input. - Pack/shop subgraph data is public chain-derived data, not the Datacentre REST
/shop/*API. Cross-link them in final docs but keep responsibilities separate.
Operational notes
- The public ingress sets
proxy-body-size: 200mand CORS*for the subgraph aliases, but clients should still keep query bodies and pages small. - The Graph's
first: 1000is used by app code for some market views; public examples should use smallfirstvalues and document pagination instead of recommending max-size queries. - Sorting on nested fields is supported (
tx__timestamp,currentTotal__referrals), but nested sort fields should be verified against introspection when added to docs. - Endpoint aliases rewrite to specific deployment hashes; when schemas change, update both the hash and the generated entity table.
- Some non-browser/stdlib clients can receive HTTP 403 without a standard
User-Agent; always send a descriptiveUser-Agentheader as shown in the Python template above.