# OneSource Web3 API Documentation > Complete API reference and guides for the OneSource Web3 API - GraphQL-based blockchain data access for Ethereum, Base, Optimism, and more. This file contains all documentation content in a single document following the llmstxt.org standard. ## Account Access import React, { useEffect } from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; import ZoomImage from '@site/src/components/ZoomImage'; Your OneSource account dashboard can be accessed via the [Sign In](https://app.onesource.io/login) page. :::tip If you have not yet registered a OneSource account, you can do so by following the steps detailed in [Get an API Key](./getting-started/get-an-api-key). ::: Enter the email address with which you registered and click **Send link**. An email containing a link to log in to your account will be distributed to the provided email address. Click the link in the email and you will be taken to your OneSource account dashboard. Via your OneSource dashboard you can: - View your existing API key(s) - Create a new API key - Check the status of your subscription plan and API keys - View your current cycle utilization --- ## AI Instructions {/* TODO: Update endpoint when /federation/ path is removed from API */} # OneSource Web3 API - AI Instructions This page provides structured guidance for AI assistants and coding agents working with the OneSource Web3 API. ## Quick Reference | Property | Value | |----------|-------| | **API Type** | GraphQL | | **Base URL** | `https://api.onesource.io/federation/{chain}/graphql` | | **Auth Header** | `x-bp-token: BP-{YOUR_API_KEY}` | | **Supported Chains** | ethereum, base, optimism, avax, sepolia, shape | ## Authentication All requests require an API key passed as an HTTP header: ``` x-bp-token: BP-{YOUR_API_KEY} ``` **Key format:** `BP-` prefix followed by 26 alphanumeric characters (e.g., `BP-12345678912345678912345678`) **Common auth errors:** - `401 Unauthorized` = Missing or invalid API key - Ensure header name is exactly `x-bp-token` (not `Authorization: Bearer`) ## Making Requests ### Endpoint Pattern Replace `{chain}` with the target blockchain: ``` https://api.onesource.io/federation/ethereum/graphql https://api.onesource.io/federation/base/graphql https://api.onesource.io/federation/optimism/graphql https://api.onesource.io/federation/avax/graphql https://api.onesource.io/federation/sepolia/graphql https://api.onesource.io/federation/shape/graphql ``` ### Example Request (JavaScript) ```javascript const response = await fetch('https://api.onesource.io/federation/ethereum/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-bp-token': 'BP-YOUR_API_KEY' }, body: JSON.stringify({ // ERC-20 token lookup query: ` query GetToken($address: AddressString!) { token(address: $address) { name symbol decimals totalSupply { formatted } } } `, variables: { address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } }) }); const data = await response.json(); ``` **NFT lookup example:** ```javascript const response = await fetch('https://api.onesource.io/federation/ethereum/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-bp-token': 'BP-YOUR_API_KEY' }, body: JSON.stringify({ query: ` query GetNFT($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name metadata { name description } media { thumbnails(preset: MEDIUM) { url } } } } `, variables: { contract: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", tokenId: "1" } }) }); const data = await response.json(); ``` ### Example Request (curl) ```bash curl -X POST "https://api.onesource.io/federation/ethereum/graphql" \ -H "Content-Type: application/json" \ -H "x-bp-token: BP-YOUR_API_KEY" \ -d '{"query": "{ token(address: \"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\") { name symbol decimals } }"}' ``` ## Query Patterns ### Single Item Queries Queries like `token`, `nft`, `contract`, `block`, `transaction` return a single object: ```graphql # ERC-20 token query { token(address: "0x...") { name symbol decimals } } # NFT query { nft(contract: "0x...", tokenId: "1") { tokenId name metadata { name description } } } ``` ### List Queries with Pagination Queries like `tokens`, `contracts`, `balances` return paginated lists: ```graphql query { tokens(first: 20, after: null, where: { ... }) { totalCount pageInfo { hasNextPage endCursor } entries { address name symbol } } } ``` **Pagination parameters:** - `first`: Number of items to return (default 10, max 100) - `after`: Cursor for the next page (use `pageInfo.endCursor` from previous response) ### Filtering Use the `where` parameter with type-specific filter objects: ```graphql query { tokens(where: { contract: { address: "0x..." } }) { entries { tokenId } } } ``` ### Ordering Use `orderBy` and `orderDirection` parameters: ```graphql query { blocks(orderBy: NUMBER, orderDirection: DESC, first: 10) { entries { number timestamp } } } ``` ## Common Queries by Use Case | Use Case | Query | Key Parameters | |----------|-------|----------------| | Get ERC-20 token | `token` | `address` | | Get NFT details | `nft` | `contract`, `tokenId` | | List NFTs by contract | `tokens` | `where: { contract: { address: "0x..." } }` | | Get wallet balances | `balances` | `where: { account: "0x..." }` | | Get contract info | `contract` | `address` | | Get transaction | `transaction` | `hash` | | List recent blocks | `blocks` | `orderBy: NUMBER`, `orderDirection: DESC` | ## Error Handling | Status | Meaning | Solution | |--------|---------|----------| | 401 | Invalid/missing API key | Check `x-bp-token` header format | | 429 | Rate limit exceeded | Reduce request frequency | | 400 | Invalid GraphQL query | Check query syntax and field names | GraphQL errors appear in the response body: ```json { "errors": [ { "message": "Cannot query field 'foo' on type 'Token'", "path": ["token", "foo"] } ] } ``` ## Important Notes for AI Assistants 1. **Always use lowercase** for Ethereum addresses (e.g., `0xabcd...` not `0xABCD...`) 2. **Contract addresses are strings**, not checksummed 3. **TokenIds are strings**, even if numeric (e.g., `"123"` not `123`) 4. **Pagination is required** for large result sets - check `pageInfo.hasNextPage` to detect the last page and use `pageInfo.endCursor` as the `after` value for the next request 5. **Chain selection matters** - ensure you're querying the correct chain endpoint 6. **Timestamps** are ISO 8601 format (e.g., `"2024-01-15T12:00:00Z"`) ## Available Query Types **Data Queries:** - `token` / `tokens` - NFT and token data - `contract` / `contracts` - Smart contract information - `balance` / `balances` - Wallet token holdings - `transaction` / `transactions` - Transaction history - `block` / `blocks` - Block data - `metadata` / `metadataList` - NFT metadata - `media` / `mediaList` - NFT media files **Aggregate Queries:** - `holdersCount` - Number of token holders - `blockCount` - Number of blocks matching filter - `transactionCount` - Number of transactions matching filter ## GraphQL Schema Types **Scalars:** `String`, `Int`, `Boolean`, `JSON`, `Time`, `UInt64`, `UInt256` **Key Enums:** - `AssetType`: `ERC20`, `ERC721`, `ERC1155` - `Ordering`: `ASC`, `DESC` - `ThumbnailPreset`: `MICRO`, `SMALL`, `MEDIUM`, `LARGE`, `XLARGE`, `ORIGINAL` **Filter Inputs:** `TokenFilter`, `BalanceFilter`, `ContractFilter`, `TransactionFilter`, `BlockFilter`, `MediaFilter`, `MetadataFilter` --- ## Getting Started ## [Subscription Plans](subscription-plans) OneSource is currently available through a free subscription plan. ## [Get an API Key](get-an-api-key) Create an account and get an API key via OneSource.io or AWS Marketplace. ## [Authentication](authentication) Making OneSource API calls requires proper authentication with an API key. ## [Playgrounds](playgrounds) Create your first query using a GraphQL playground. --- ## Authentication {/* TODO: Update endpoint when /federation/ path is removed from API */} To access the OneSource API, you need to include an API key in your requests. The API key is passed as an HTTP header. ## API Key Format Your API key follows this format: ```plaintext x-bp-token: BP-{KEYVALUE} ``` * `x-bp-token`: This is the header key used to pass the API key. * `BP-{KEYVALUE}`: This is the API key value, where `{KEYVALUE}` is a unique 26 character alphanumeric string provided to you via your OneSource dashboard. import React from 'react'; import ZoomImage from '@site/src/components/ZoomImage'; ## How to Use the API Key Include the API key in the headers of your HTTP requests. Here's an example using `curl`: ```bash curl -X GET "https://api.onesource.io/federation/ethereum/graphql" \ -H "x-bp-token: BP-12345678912345678912345678" ``` Replace `BP-12345678912345678912345678` with your actual API key. *** ## Getting Your API Key Refer to [Get an API Key](get-an-api-key) for information on how to obtain an API key. *** ## Best Practices * **Keep your API key secure**: Do not share your API key publicly or commit it to version control systems like GitHub. * **Rotate keys regularly**: For enhanced security, rotate your API keys periodically. * **Use environment variables**: Store your API key in environment variables to avoid hardcoding it in your application. *** ## Troubleshooting If you receive a `status code 401` or `Invalid API token` error, double-check that: * The API key is correctly formatted (`x-bp-token: BP-{KEYVALUE}`). * The API key is included in the requests header. * The API key is valid and has not expired. You can check this via your OneSource dashboard. --- ## Get an API Key To get started with OneSource, you must first obtain an API key. import React, { useEffect } from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; import ZoomImage from '@site/src/components/ZoomImage'; {() => { const { useEffect } = require('react'); const useAnchorScroll = () => { useEffect(() => { const hash = window.location.hash; if (hash) { const element = document.getElementById(hash.substring(1)); if (element) { setTimeout(() => { element.scrollIntoView(); }, 100); } } }, []); }; useAnchorScroll(); return null; }} ## Create a OneSource Account Navigate to the [Sign Up](https://app.onesource.io/signup) page to create your OneSource account. Enter your name, email address and organization (agreeing to receive emails from OneSource is optional) and click **Get Started**. An email containing a sign in link will be sent to the provided email address. Click **Get Started Now** in the email to be taken to your OnceSource account dashboard. ## The OneSource Account Dashboard The OneSource account dashboard is where you can view important account information such as license details, billing date, current billing cycle utilization, plan status and your API keys. ## Create an API Key To create an API key, click on **API Keys** in the sidebar on the left. Click the **Create new API key +** button in the upper right corner to generate a new API key. Your new API key will appear in the table of API keys. You can create and remove API keys from your account as needed. ## Alternative: AWS Marketplace OneSource is also available through the [AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-at5xou6obw4s4). This method of account creation may be preferable for those who already utilize the AWS Marketplace. ### Create or Sign In to an AWS Account If you do not yet have an AWS account, you will need to [create one](https://signin.aws.amazon.com/signup?request_type=register). If you already have an AWS account, you will need to [sign in](https://aws.amazon.com/marketplace/login). ### Review Subscription Options and Confirm Subscription Once you've signed into your AWS account, return to [OneSource Web3 API on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-at5xou6obw4s4) and click the **View purchase options** button. Select your auto-renewal preference and then scroll down to 'Pricing details and unit configuration'. Select your desired subscription tier and then click the **Subscribe** button to finalize your subscription. Refer to the [Subscription Plans](subscription-plans) page for more information on subscriptions. ### Set up OneSource Account It will take a couple of minutes for AWS Marketplace to complete your subscription. When your subscription is complete, click the **Set up your account** button to be taken to the OneSource [Sign Up](https://app.onesource.io/signup) page. Proceed with account creation as described in the [Create a OneSource Account](#create-a-onesource-account) section above. --- ## Playgrounds Playgrounds are interactive environments where you can test and explore our GraphQL API in real-time. Whether you just want to give OneSource Web3 API a try or you're experimenting with queries and debugging responses, playgrounds provide a user-friendly interface to help you get the most out of our API. ## [GraphiQL Playground](graphiql) The embedded GraphiQL Playground allows you to explore the OneSource Web3 API Ethereum endpoint without leaving this documentation and without your own API key. This is a great option for those who simply want to give OneSource a try. ## [Apollo Sandbox](apollo) Apollo Sandbox provides a full-featured playground experience with an interactive query builder. --- ## Apollo Sandbox import React, { useEffect } from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; import ZoomImage from '@site/src/components/ZoomImage'; ## Key Features * **Interactive Query Builder**: Build queries by selecting arguments and fields from the schema documentation. * **Comprehensive Documentation**: Auto-generated documentation for all types and queries including field descriptions. * **Debugging Tools**: Pretty-printed JSON results with collapsible sections and detailed error messages with path highlighting. ## Accessing Apollo Sandbox This [link](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapi.onesource.io%2Ffederation%2Fethereum%2Fgraphql&headers=%7B%22x-bp-token%22%3A%22BP-%7BKEYVALUE%7D%22%7D) loads Apollo Sandbox pre-configured with the OneSource Web3 API Ethereum [endpoint](onesource-web3-api-reference/README.md#graphql-endpoints) and header key. Simply replace `BP-{KEYVALUE}` in the **Headers** tab with your [API key value](/getting-started/authentication#api-key-format) (`BP-{KEYVALUE}` only) and you're ready to begin. ## Using Apollo Sandbox Although the above link to Apollo Sandbox is not pre-configured with query templates, it is quite easy to build your own queries with the Apollo Sandbox interactive query builder. *** :::warning It is important to note that Apollo Sandbox by default polls the connected endpoint for schema changes every 5 seconds. Each poll is counted by OneSource as a request against your API key. You may wish to disable this feature so as to conserve your requests, particularly if you intend to be working with the Apollo Sandbox for extended periods of time. This setting can be disabled by navigating to the **settings** where you can edit the **Connection settings**. In the **Connection settings** menu, toggle the **Auto Update** feature to the **OFF** position as shown below. ::: ## Other Features Apollo Sandbox allows you to retain and reference your history and browse the schema just like the OneSource Playground. You can access these features as shown below. --- ## GraphiQL Playground import GraphQLPlayground from '@site/src/components/GraphQLPlayground'; import GraphiQLurlGenerator from '@site/src/components/GraphiQLurlGenerator'; import BrowserOnly from '@docusaurus/BrowserOnly'; {() => { const { useEffect } = require('react'); const useAnchorScroll = () => { useEffect(() => { const hash = window.location.hash; if (hash) { const element = document.getElementById(hash.substring(1)); if (element) { setTimeout(() => { element.scrollIntoView(); }, 100); } } }, []); }; useAnchorScroll(); return null; }} This embedded playground allows you to submit any query to the OneSource Web3 API Ethereum endpoint and get a real-time response without needing your own API key. --- ## Subscription Plans | Sandbox | |---------| | $0/month1 request/second1M requests/month | :::info GraphQL APIs like OneSource are typically able to deliver the data you need in fewer requests than REST APIs. ::: Proceed to [Get an API Key](get-an-api-key) or visit our [Sign Up](https://www.onesource.io/signup) page to get started with our free subscription plan. --- ## Guides ## [Integrating OneSource Queries](integrating-onesource-queries) Tips for integrating OneSource queries into your project. ## [Working with API Responses](working-with-api-responses) Tips for working with API responses received from the OneSource Web3 API. ## [Troubleshooting](troubleshooting) Tips for resolving common issues when working with the OneSource Web3 API. ## [AI Editor Setup](ai-editor-setup) Connect your AI coding assistant to the OneSource docs using MCP. --- ## AI Editor Setup Connect your AI coding assistant to the OneSource documentation using [MCP](https://modelcontextprotocol.io/) (Model Context Protocol). This gives your AI direct access to the full OneSource Web3 API reference, so it can answer questions and generate accurate code without you copy-pasting docs. ## Prerequisites MCP documentation serving is powered by [`mcpdoc`](https://github.com/langchain-ai/mcpdoc), which requires [`uv`](https://docs.astral.sh/uv/) (a fast Python package manager). Install it if you don't have it: **macOS / Linux:** ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` **Windows (PowerShell):** ```powershell powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` You do **not** need to install `mcpdoc` manually — your editor will run it automatically via `uvx`. ## Claude Code ### Project-level (recommended) If you've cloned the [OneSource developer docs repo](https://github.com/blockparty-global/1s-developer-docs), MCP is already configured. The `.mcp.json` file in the project root registers the server automatically — no setup needed. ### User-level To make the OneSource docs available in **all** Claude Code sessions, run: ```bash claude mcp add-json onesource-docs '{"type":"stdio","command":"uvx","args":["--from","mcpdoc","mcpdoc","--urls","OneSourceDocs:https://docs.onesource.io/llms.txt","--follow-redirects"]}' ``` ## Cursor Add the following to `.cursor/mcp.json` in your project root (create the file if it doesn't exist): ```json { "mcpServers": { "onesource-docs": { "command": "uvx", "args": [ "--from", "mcpdoc", "mcpdoc", "--urls", "OneSourceDocs:https://docs.onesource.io/llms.txt", "--follow-redirects" ] } } } ``` ## VS Code / Continue Add the following to your `.vscode/mcp.json` (create the file if it doesn't exist): ```json { "servers": { "onesource-docs": { "type": "stdio", "command": "uvx", "args": [ "--from", "mcpdoc", "mcpdoc", "--urls", "OneSourceDocs:https://docs.onesource.io/llms.txt", "--follow-redirects" ] } } } ``` ## Windsurf Add the following to `~/.codeium/windsurf/mcp_config.json` (create the file if it doesn't exist): ```json { "mcpServers": { "onesource-docs": { "command": "uvx", "args": [ "--from", "mcpdoc", "mcpdoc", "--urls", "OneSourceDocs:https://docs.onesource.io/llms.txt", "--follow-redirects" ] } } } ``` ## Direct access If your tool doesn't support MCP, you can access the documentation directly: - **[llms.txt](https://docs.onesource.io/llms.txt)** — Index of all documentation pages (lightweight, suitable for discovery) - **[llms-full.txt](https://docs.onesource.io/llms-full.txt)** — Complete documentation in a single file (suitable for ingestion into context windows) Paste either URL into your AI tool's context or download the file for local use. --- ## Integrating OneSource Queries {/* TODO: Update endpoint when /federation/ path is removed from API */} There are many ways to integrate OneSource GraphQL queries into your project. The method you choose should be based on the needs of your specific project. This guide contains examples of how to integrate OneSource queries into your project using a handful of popular programming languages and tools. ## JavaScript (Fetch API) ```javascript const apiKey = "BP-XXXXXXXXXXXXXXXXXXXXXXXXXX"; const query = ` query GetNFT($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name standard metadata { description attributes { traitType value } } media { thumbnails(preset: MEDIUM) { url } } contract { address name standards } } } `; const variables = { contract: "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", tokenId: "1" }; fetch("https://api.onesource.io/federation/ethereum/graphql", { method: "POST", headers: { "Content-Type": "application/json", "x-bp-token": apiKey }, body: JSON.stringify({ query, variables }) }) .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error)); ``` ## JavaScript (Axios) ```javascript const axios = require("axios"); const apiKey = process.env.ONE_SOURCE_API_KEY; const query = ` query GetNFT($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name standard metadata { description attributes { traitType value } } media { thumbnails(preset: MEDIUM) { url } } contract { address name standards } } } `; const variables = { contract: "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", tokenId: "1" }; axios.post( "https://api.onesource.io/federation/ethereum/graphql", { query, variables }, { headers: { "Content-Type": "application/json", "x-bp-token": apiKey } } ) .then((response) => console.log(response.data)) .catch((error) => console.error("Error:", error)); ``` ## Python (Requests) ```python import requests import os api_key = os.getenv("ONE_SOURCE_API_KEY") url = "https://api.onesource.io/federation/ethereum/graphql" query = """ query GetNFT($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name standard metadata { description attributes { traitType value } } media { thumbnails(preset: MEDIUM) { url } } contract { address name standards } } } """ variables = { "contract": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", "tokenId": "1" } headers = { "Content-Type": "application/json", "x-bp-token": api_key } response = requests.post(url, json={"query": query, "variables": variables}, headers=headers) print(response.json()) ``` ## GraphQL Clients (Apollo) ```javascript import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client"; import { gql } from '@apollo/client'; const client = new ApolloClient({ link: new HttpLink({ uri: "https://api.onesource.io/federation/ethereum/graphql", headers: { "x-bp-token": process.env.API_KEY } }), cache: new InMemoryCache() }); const GET_NFT_DETAILS = gql` query GetNFT($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name standard metadata { description attributes { traitType value } } media { thumbnails(preset: MEDIUM) { url } } contract { address name standards } } } `; const variables = { contract: "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", tokenId: "1" }; client.query({ query: GET_NFT_DETAILS, variables }) .then((result) => console.log(result.data)) .catch((error) => console.error("Error:", error)); ``` ## Best Practices ### Using Environment Variables for API Keys To securely store and use your API key, we recommend using environment variables. This prevents hardcoding sensitive information in your code and makes it easier to manage keys across different environments. #### Step 1: Set the Environment Variable * **For Local Development**: Create a `.env` file and add your API key (or set the environment variable in your system). Create a `.env` file: ```text API_KEY=BP-12345678912345678912345678 ``` Set the environment variable in your system: ```bash export API_KEY=BP-12345678912345678912345678 ``` * **For Production**: Use a secret management tool like AWS Secrets Manager or Azure Key Vault. #### Step 2: Access the Environment Variable **JavaScript**: ```javascript const apiKey = process.env.API_KEY; ``` **Python**: ```python import os api_key = os.getenv("API_KEY") ``` ### Handle Errors Gracefully Check for errors in the response and provide meaningful feedback. ### Optimize Queries Use only the query fields you need to reduce response size and improve performance in your project. ## Other Ways to Use GraphQL ### Relay (React) Relay is a high-performance GraphQL client for React. ### Server-Side Frameworks Integrate GraphQL into server-side applications using frameworks like Express (Node.js) or Django (Python). ### Mobile Development Use GraphQL in mobile apps with Apollo Client (React Native) or Relay. ### Static Site Generators Fetch data at build time or runtime in static sites built with Next.js or Gatsby. ### Low-Code/No-Code Platforms Integrate GraphQL into platforms like Zapier, Make or Retool. --- ## Troubleshooting This guide helps you resolve common issues when working with the OneSource Web3 API. ## Authentication Errors ### `Invalid API token` or `Status Code 401` If you receive this error: 1. Check API key format to ensure your key starts with `BP-` followed by 26 characters (i.e., `BP-12345678912345678912345678`). 2. Verify header implementation: ```jsx // Correct implementation headers: { "Content-Type": "application/json", "x-bp-token": "BP-12345678912345678912345678" // Note the exact case of "x-bp-token" } ``` 3. Confirm your subscription status by logging in to your [OneSource Dashboard](../account-access.mdx) to verify your API key is active and hasn't expired. 4. Test your API key with a [playground](../getting-started/playgrounds/README.md) to determine if the issue is with your query or the API itself. ## Query Error ### `Cannot query field X on type Y` This error occurs when your query references fields that don't exist in our schema. To resolve: 1. Check field spelling to ensure field names match exactly what's in our schema. 2. Review schema changes in the [GraphQL Schema](https://docs.onesource.io/onesource-web3-api-reference/). 3. Use [Apollo Sandbox](../getting-started/playgrounds/apollo.mdx) to help build valid queries. ## Rate Limiting ### `Rate limit exceeded` If you hit rate limits: 1. **Implement caching** - if you're making the same requests over and over, caching the results can cut down on requests. 2. **Optimize queries** - only ask for the data you actually need with smaller, focused queries. 3. **Upgrade your plan** - if you consistently hit limits, you may need to move to a [higher-tier plan](../getting-started/subscription-plans.md) that offers higher limits. --- ## Frequently Asked Questions ### What chains does OneSource support? OneSource supports the following chains: - **Ethereum Mainnet** - `ethereum` - **Base** - `base` - **Optimism** - `optimism` - **Avalanche C-Chain** - `avax` - **Sepolia Testnet** - `sepolia` - **Shape** - `shape` Use the chain identifier in your endpoint URL: `https://api.onesource.io/federation/{chain}/graphql` ### How do I paginate through large result sets? Use the `first` and `after` parameters with cursor-based pagination: ```graphql query { tokens(first: 20, where: { ... }) { totalCount pageInfo { hasNextPage endCursor } entries { name symbol } } } ``` - `first` — Number of items to return per page (max 100) - `after` — Pass the `endCursor` from `pageInfo` to fetch the next page - Check `pageInfo.hasNextPage` to know if more results exist - `totalCount` gives the total number of matching results across all pages To fetch the next page, pass the `endCursor` from the previous response: ```graphql query { tokens(first: 20, after: "eyJpZCI6MTAwfQ==", where: { ... }) { totalCount pageInfo { hasNextPage endCursor } entries { name symbol } } } ``` ### Why is my query returning null? A null response typically means: - **Token/contract doesn't exist** — Verify the contract address and token ID are correct. - **Wrong chain** — The asset may exist on a different chain than you're querying. - **Address format** — Ensure addresses are checksummed hex strings (e.g., `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045`). - **Data not yet indexed** — Recently minted tokens may take a few minutes to appear. ### What's the difference between `token` and `nft` queries? - **`token`** — Fetches a single ERC-20 fungible token by its contract address. Use when you want details about a specific fungible token (e.g., USDC, WETH). - **`nft`** — Fetches a single NFT by contract address and token ID. Use when you know exactly which NFT you want. - **`tokens`** — Fetches a filtered, paginated list of ERC-20 tokens. Use for browsing or searching fungible tokens. - **`nfts`** — Fetches a filtered, paginated list of NFTs across contracts. Use for browsing collections, searching, or displaying galleries. Same pattern applies to other query pairs: `contract`/`contracts`, `transaction`/`transactions`, `block`/`blocks`, `address`/`addresses`. ### How do I get NFT metadata and images? Use the `nft` query with nested `metadata` and `media` fields: ```graphql query { nft(contract: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", tokenId: "1") { name metadata { name description image attributes { traitType value } } media { fileType mediaType thumbnails(preset: MEDIUM) { url preset format fileSize } } } } ``` Use thumbnail presets (`MICRO`, `SMALL`, `MEDIUM`, `LARGE`, `XLARGE`, `ORIGINAL`) for optimized image loading. ### What's the difference between ERC-20, ERC-721, and ERC-1155? - **ERC-20** — Fungible tokens (e.g., USDC, WETH). Each token is identical and divisible. - **ERC-721** — Non-fungible tokens (NFTs). Each token is unique with its own token ID. - **ERC-1155** — Multi-token standard. Supports both fungible and non-fungible tokens in one contract. Filter by standard using the `standards` field on `ContractFilter`: ```graphql query { contracts(where: { standards: [ERC721] }) { entries { address name } } } ``` ### How do I filter queries? Use the `where` parameter with structured filter input objects: ```graphql query { nfts(where: { contract: { address: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" } nameLike: "Ape" }) { entries { tokenId name } } } ``` Common filter patterns: - **Range filters** use nested objects with `gte` (greater than or equal) and `lte` (less than or equal) fields — e.g., `holdersCount: { gte: 100 }`, `createdAt: { gte: "2025-01-01T00:00:00Z" }` - **Text search** uses the `Like` suffix for case-insensitive partial matching — e.g., `nameLike`, `symbolLike` - **Exact match** uses the field directly — e.g., `address: "0x..."`, `symbol: "USDC"` - See the [Inputs documentation](/onesource-web3-api-reference/types/inputs/address-filter) for all available filter inputs. --- ## Still Need Help? If none of the above helps, contact support at [support@onesource.io](mailto:support@onesource.io) with: - The full request you're making. - Any error messages you're receiving. - The chain endpoint you're using. --- ## Working With API Responses ## Structure of the Response This is an example response you might get when executing an `nft` query. ```json { "data": { "nft": { "tokenId": "42141102", "name": "Singular Focus", "standard": "ERC721", "contract": { "address": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c", "name": "Art Blocks", "symbol": "BLOCKS", "standards": ["ERC721"] }, "metadata": { "name": "Singular Focus", "description": "The Partition is formed...", "image": "ipfs://QmExample...", "attributes": [ { "traitType": "Artist", "value": "Example Artist" } ] }, "media": [ { "fileType": "IMAGE_PNG", "mediaType": "IMAGE", "thumbnails": [ { "url": "https://media.onesource.io/thumbnails/medium/1/42141102.png", "preset": "MEDIUM", "format": "png", "fileSize": 45230 } ], "updatedAt": "2025-01-10T08:30:00Z" } ] } } } ``` ### Response Structure Here's the general response structure for the above `nft` query response. ```json { "data": { "nft": { "tokenId": "String (TokenId)", "name": "String", "standard": "Standard (ERC721 | ERC1155)", "contract": { "address": "String (AddressString)", "name": "String", "symbol": "String", "standards": ["Standard"] }, "metadata": { "name": "String", "description": "String", "image": "String", "attributes": [ { "traitType": "String", "value": "String" } ] }, "media": [ { "fileType": "FileType", "mediaType": "MediaType", "thumbnails": [ { "url": "String", "preset": "ThumbnailPreset", "format": "String", "fileSize": "Int" } ], "updatedAt": "Timestamp" } ] } } } ``` ### Response Object Definitions * **data**: The root object containing the response data. * **nft**: The object representing the NFT details. May resolve to a `SingleHolderNFT` (ERC-721) or `MultiHolderNFT` (ERC-1155). * **tokenId**: The unique token identifier within its contract (string, to support uint256 values). * **name**: The human-readable name of the NFT from its metadata. * **standard**: The token standard — `ERC721` or `ERC1155`. * **contract**: The NFT collection contract. * **address**: The contract address as a checksummed hex string. * **name**: The collection name. * **symbol**: The collection symbol. * **standards**: Array of token standards implemented by this contract. * **metadata**: Parsed metadata for the NFT. * **name**: The name from the token metadata. * **description**: The description from the token metadata. * **image**: The image URL from the token metadata. * **attributes**: Array of trait objects. * **traitType**: The trait category (e.g., "Background", "Eyes"). * **value**: The trait value (e.g., "Blue", "Laser Eyes"). * **media**: Array of media files associated with the NFT. * **fileType**: The specific file format as a `FileType` enum value (e.g., `IMAGE_JPEG`, `IMAGE_PNG`, `VIDEO_MP4`). * **mediaType**: The general media category as a `MediaType` enum value (`IMAGE`, `VIDEO`, `AUDIO`, `ANIMATION`, `DOCUMENT`). * **thumbnails**: Array of pre-generated thumbnail images. * **url**: The thumbnail URL. * **preset**: The thumbnail size preset (`MICRO`, `SMALL`, `MEDIUM`, `LARGE`, `XLARGE`, `ORIGINAL`). * **format**: The image format of the thumbnail (e.g., `webp`, `png`, `jpeg`). * **fileSize**: The file size of the thumbnail in bytes, or null if not available. * **updatedAt**: Timestamp when this media was last processed or refreshed. --- ## Accessing the Data The response structure is consistent and its fields can be accessed in a predictable manner. Below are some examples of how the response fields may be accessed in a project. ### JavaScript ```javascript if (response.data && response.data.nft) { const nft = response.data.nft; const tokenId = nft.tokenId; const tokenName = nft.name; const standard = nft.standard; const contractAddress = nft.contract.address; const description = nft.metadata?.description; const imageUrl = nft.metadata?.image; const thumbnailUrl = nft.media?.[0]?.thumbnails?.[0]?.url; console.log("Token ID: " + tokenId); console.log("Name: " + tokenName); console.log("Standard: " + standard); console.log("Contract: " + contractAddress); console.log("Description: " + description); console.log("Image URL: " + imageUrl); console.log("Thumbnail URL: " + thumbnailUrl); } else { console.error("Invalid response structure"); } ``` ### Python ```python if "data" in response and "nft" in response["data"]: nft = response["data"]["nft"] token_id = nft["tokenId"] token_name = nft.get("name") standard = nft.get("standard") contract_address = nft["contract"]["address"] description = nft.get("metadata", {}).get("description") image_url = nft.get("metadata", {}).get("image") media = nft.get("media", []) thumbnail_url = None if media and media[0].get("thumbnails"): thumbnail_url = media[0]["thumbnails"][0].get("url") print("Token ID:", token_id) print("Name:", token_name) print("Standard:", standard) print("Contract:", contract_address) print("Description:", description) print("Image URL:", image_url) print("Thumbnail URL:", thumbnail_url) else: print("Invalid response structure") ``` ## Handling Errors Since the response is dynamic, developers should always check for errors and validate the structure of the response before accessing data. ### JavaScript ```javascript if (response.errors) { console.error("GraphQL Error: " + response.errors[0].message); } else if (response.data && response.data.nft) { const standard = response.data.nft.standard; const standards = response.data.nft.contract.standards; console.log("Token standard: " + standard); console.log("Contract implements: " + standards.join(", ")); } else { console.error("Invalid response structure"); } ``` ### Python ```python if "errors" in response: print("GraphQL Error: " + response["errors"][0]["message"]) elif "data" in response and "nft" in response["data"]: standard = response["data"]["nft"].get("standard") standards = response["data"]["nft"]["contract"].get("standards", []) print("Token standard:", standard) print("Contract implements:", ", ".join(standards)) else: print("Invalid response structure") ``` ## Common Use Cases ### Displaying NFT Information #### JavaScript ```javascript function displayNFTInfo(response) { if (response.data?.nft) { const nft = response.data.nft; document.getElementById("token-name").innerText = nft.name || "Unnamed"; document.getElementById("token-description").innerText = nft.metadata?.description || ""; // Use thumbnail for optimized loading const thumbnail = nft.media?.[0]?.thumbnails?.[0]; if (thumbnail) { const img = document.getElementById("token-image"); img.src = thumbnail.url; } // Display token standard document.getElementById("token-type").innerText = nft.standard === "ERC721" ? "ERC-721 (Unique)" : nft.standard === "ERC1155" ? "ERC-1155 (Semi-fungible)" : "Unknown"; } else { console.error("Invalid response structure"); } } ``` ### Filtering Contracts by Standard #### Python ```python def filter_contracts_by_standard(response): if "data" in response and "contracts" in response["data"]: contracts = response["data"]["contracts"]["entries"] erc20 = [c for c in contracts if "ERC20" in c.get("standards", [])] erc721 = [c for c in contracts if "ERC721" in c.get("standards", [])] erc1155 = [c for c in contracts if "ERC1155" in c.get("standards", [])] print(f"ERC-20 Tokens: {len(erc20)}") print(f"ERC-721 Collections: {len(erc721)}") print(f"ERC-1155 Collections: {len(erc1155)}") else: print("Invalid response structure") ``` ## Ordering OneSource databases are built with PostgreSQL which does not have default ordering. The only way to ensure consistent ordering of results within responses is to use the sorting arguments `orderBy` and `orderDirection`. ### Example ```graphql query GetWalletTokenBalances { address(address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") { tokenBalances( first: 20 orderBy: VALUE orderDirection: DESC ) { totalCount pageInfo { hasNextPage endCursor } entries { token { name symbol address } value { formatted decimals } } } } } ``` ## Pagination When querying large datasets, it is best to paginate through results to avoid requesting the entire dataset in a single response. Refer to the GraphQL [documentation on pagination](https://graphql.org/learn/pagination/) for a complete overview of GraphQL pagination. OneSource uses cursor-based pagination. All list queries accept these arguments: | Argument | Type | Description | | ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------ | | `first` | `Int` | Maximum number of items to return (default: `10`, max: `100`). | | `after` | `Cursor` | Opaque cursor from `pageInfo.endCursor`. Pass this to fetch the next page of results. | | `where` | Filter input | Filter criteria for results (varies per query — e.g., `TokenFilter`, `NFTFilter`, `ContractFilter`). | | `orderBy` | Enum | Field to sort results by (varies per query — e.g., `TokenOrderBy`, `ContractOrderBy`). | | `orderDirection` | `OrderDirection` | Sort direction: `ASC` (ascending) or `DESC` (descending). | All list responses include a consistent structure: | Field | Type | Description | | ---------------- | ----------- | -------------------------------------------------------------------- | | `entries` | `[Type!]!` | Array of results for the current page. | | `totalCount` | `Int!` | Total number of items matching the query across all pages. | | `pageInfo` | `PageInfo!` | Pagination metadata containing `hasNextPage`, `endCursor`, and `count`. | :::tip Use `orderBy` and `orderDirection` in conjunction with pagination when querying large datasets to ensure consistent ordering across pages. ::: ### Example ```graphql query ListTokens($after: Cursor) { tokens(first: 50, orderBy: NAME, orderDirection: ASC, after: $after) { totalCount pageInfo { hasNextPage endCursor count } entries { address name symbol holdersCount } } } ``` #### JavaScript ```javascript const PAGE_SIZE = 50; let after = null; let allTokens = []; let totalCount = 0; while (true) { const query = ` query ListTokens($after: Cursor) { tokens(first: ${PAGE_SIZE}, orderBy: NAME, orderDirection: ASC, after: $after) { totalCount pageInfo { hasNextPage endCursor } entries { address name symbol holdersCount } } } `; const response = await fetch("https://api.onesource.io/federation/ethereum/graphql", { method: "POST", headers: { "x-bp-token": ONESOURCE_APIKEY, "Content-Type": "application/json" }, body: JSON.stringify({ query, variables: { after } }), }).then((r) => r.json()); const result = response.data?.tokens; if (!result || result.entries.length === 0) break; allTokens.push(...result.entries); totalCount = result.totalCount; if (!result.pageInfo.hasNextPage) break; after = result.pageInfo.endCursor; } console.log(`Fetched ${allTokens.length} of ${totalCount} total tokens`); ``` #### Python ```python import requests PAGE_SIZE = 50 after = None all_tokens = [] total_count = 0 while True: query = """ query ListTokens($after: Cursor) { tokens(first: %s, orderBy: NAME, orderDirection: ASC, after: $after) { totalCount pageInfo { hasNextPage endCursor } entries { address name symbol holdersCount } } } """ % PAGE_SIZE response = requests.post( "https://api.onesource.io/federation/ethereum/graphql", headers={ "x-bp-token": ONESOURCE_APIKEY, "Content-Type": "application/json" }, json={"query": query, "variables": {"after": after}} ).json() result = response.get("data", {}).get("tokens", {}) entries = result.get("entries", []) page_info = result.get("pageInfo", {}) total_count = result.get("totalCount", 0) all_tokens.extend(entries) if not page_info.get("hasNextPage"): break after = page_info.get("endCursor") print(f"Fetched {len(all_tokens)} of {total_count} total tokens") ``` ## Best Practices * **Validate the Response Structure**: Always check if the expected fields exist before accessing them. * **Handle Missing Fields Gracefully**: Use optional chaining (JavaScript) or `.get()` (Python) to avoid runtime errors when fields like `metadata`, `media`, or `name` are null. * **Cache Responses**: Cache frequently accessed data to reduce API calls and improve performance. * **Use TypeScript or Type Annotations**: If possible, use TypeScript (JavaScript) or type annotations (Python) to ensure type safety when working with the response. * **Check `standards` Array**: Use the `standards` array on contract types to determine what token standards a contract implements, rather than checking individual boolean flags. * **Paginate Sensibly**: Choose a `first` value of 50–100 to balance response size vs. response time. Always check `pageInfo.hasNextPage` to know when you've reached the end, and use `totalCount` to show progress or total result counts in your UI. * **Order Consistently**: Use `orderBy` and `orderDirection` to ensure consistent ordering across pages, especially when paginating through large result sets. --- ## Overview 📄 AI-optimized API reference # Overview OneSource is a robust backend platform designed to empower Web3 applications with seamless, scalable, and high-performance data infrastructure. Whether you're building decentralized applications (dApps) or integrating on-chain data, OneSource ensures your projects are fast, reliable, and ready to scale as your user base grows. *** ## Key Features ### **GraphQL** OneSource leverages GraphQL to give you precise control over the data you retrieve. Access exactly what you need, where you need it, for faster development and iteration. ### **High-Performance** Our low-latency API ensures that your applications remain fast and responsive, even under heavy traffic. Retrieve data instantly and keep your users engaged. ### **Scalability** As your user base grows, OneSource scales effortlessly to meet demand. Our infrastructure is designed to handle high traffic volumes without compromising performance. ### **Reliability & Uptime** With 99.9% uptime, OneSource ensures that your operations run smoothly with minimal interruptions. Depend on us for consistent and reliable data access. ### **Media Caching** Our cached NFT media ensures fast loading times and a great user experience for NFT-based applications. *** ## What You Can Do with OneSource OneSource provides a comprehensive GraphQL API organized around six core entity types. Each entity supports both singular lookups (by identifier) and plural list queries (with filtering, sorting, and cursor-based pagination): * **Addresses**: Look up any on-chain address — whether an externally owned account (EOA) or a smart contract — to view activity, ERC-20 token balances, NFT holdings, and transaction history. Search and filter addresses by activity, nonce, and more with the [`address`](onesource-web3-api-reference/queries/address) and [`addresses`](onesource-web3-api-reference/queries/addresses) queries. * **Contracts**: Retrieve smart contract details including deployment info, token standards, and creator address. The API automatically resolves contracts to their specific type — `Token` (ERC-20), `NFTContract` (ERC-721/ERC-1155), or generic `Contract`. Filter by standard, name, symbol, holder count, and more with the [`contract`](onesource-web3-api-reference/queries/contract) and [`contracts`](onesource-web3-api-reference/queries/contracts) queries. * **Tokens**: Access ERC-20 fungible token data including name, symbol, decimals, total supply, and holder counts. Traverse into paginated holder lists and individual balances. Search by name or symbol and filter by supply or holder ranges with the [`token`](onesource-web3-api-reference/queries/token) and [`tokens`](onesource-web3-api-reference/queries/tokens) queries. * **NFTs**: Query individual non-fungible tokens with full metadata, media (including cached thumbnails at multiple sizes), ownership, and mint/burn provenance. The API distinguishes between single-owner (ERC-721) and multi-owner (ERC-1155) NFTs with type-specific fields. Search across collections and filter by name, standard, contract properties, and more with the [`nft`](onesource-web3-api-reference/queries/nft) and [`nfts`](onesource-web3-api-reference/queries/nfts) queries. * **Transactions**: Retrieve complete transaction data including sender, recipient, value, gas usage, status, and block context. Filter by address, value range, status, and timestamp with the [`transaction`](onesource-web3-api-reference/queries/transaction) and [`transactions`](onesource-web3-api-reference/queries/transactions) queries. * **Blocks**: Access block-level data including number, timestamp, gas metrics, transaction counts, and individual transactions within a block. Look up blocks by number or hash, and filter by timestamp, size, or gas ranges with the [`block`](onesource-web3-api-reference/queries/block) and [`blocks`](onesource-web3-api-reference/queries/blocks) queries. *** ## Get Started Ready to dive in? Check out [Getting Started](getting-started/README.md) to learn how to use OneSource and integrate it into your application. For detailed information about our API, explore the [OneSource Web3 API Reference](onesource-web3-api-reference/README.md). --- ## OneSource Web3 API Reference {/* TODO: Update endpoints when /federation/ path is removed from API */} ## GraphQL Endpoints The OneSource Web3 API receives requests at the following endpoint URLs: * **Avalanche C-Chain**: [https://api.onesource.io/federation/avax/graphql](https://api.onesource.io/federation/avax/graphql) * **Base**: [https://api.onesource.io/federation/base/graphql](https://api.onesource.io/federation/base/graphql) * **Ethereum Mainnet**: [https://api.onesource.io/federation/ethereum/graphql](https://api.onesource.io/federation/ethereum/graphql) * **Optimism**: [https://api.onesource.io/federation/optimism/graphql](https://api.onesource.io/federation/optimism/graphql) * **Sepolia Testnet**: [https://api.onesource.io/federation/sepolia/graphql](https://api.onesource.io/federation/sepolia/graphql) * **Shape**: [https://api.onesource.io/federation/shape/graphql](https://api.onesource.io/federation/shape/graphql) ## Using the API Reference When using the API Reference, it may be helpful to refer to the [GraphQL documentation](https://graphql.org/learn/) for information on GraphQL terms and concepts. Defined below are terms as they relate to this API Reference. ### Queries The root `Type` that is the entry point for all read operations. | Term | Description | |--------------|-----------------------------------------------------------------------------| | **Arguments** | Customizes `Query` behavior. | | **Return Type** | Usually an `Object`, what constitutes a response to a `Query`. | ### Types `Types` define the shape and structure of the data received in response to a `Query`. | Term | Description | |-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| | **Fields** | A unit of data that can be requested from a `Type`. | | **Member Of** | The schema components of which this `Type` is a constituent. | | **Returned By** | The `Types` that return this `Type` as a response. | | **Values** | Predefined, named constants that represent the only valid options for a `Field` or `Argument`. | --- ## address {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/address.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build wallet profile pages by looking up any address to display its activity, balances, and transaction history. - Determine whether an address is an EOA or a smart contract to customize UI rendering and available actions. - Display a wallet's ERC-20 portfolio by traversing the `tokenBalances` field on the returned `Address`. - Show NFT holdings for a specific address using the `nftBalances` field for gallery or portfolio views. - Retrieve recent transactions for an address via the `transactions` field to build activity feeds. - Check address activity status using `lastActiveAt` and `nonce` for wallet health or risk assessment tools. ## Example ### Query #### Look Up a Wallet Address with Balances and Recent Activity ```graphql query WalletProfile($address: AddressString!) { address(address: $address) { address nonce lastActiveAt transactionCount tokenBalances(first: 5, orderDirection: DESC) { totalCount entries { token { name symbol decimals } balance { formatted } } } nftBalances(first: 5) { totalCount entries { contract { name symbol } nftCount } } transactions(first: 3, orderBy: TIMESTAMP, orderDirection: DESC) { entries { hash blockNumber timestamp from { address } to { address } value { formatted } status } } } } ``` ### Variables ```json { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" } ``` ### Response ```json { "data": { "address": { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "nonce": 1247, "lastActiveAt": "2025-06-10T14:22:08Z", "transactionCount": 1893, "tokenBalances": { "totalCount": 42, "entries": [ { "token": { "name": "USD Coin", "symbol": "USDC", "decimals": 6 }, "balance": { "formatted": "15000.000000" } }, { "token": { "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18 }, "balance": { "formatted": "12.450000000000000000" } } ] }, "nftBalances": { "totalCount": 8, "entries": [ { "contract": { "name": "Art Blocks", "symbol": "BLOCKS" }, "nftCount": 3 }, { "contract": { "name": "Nouns", "symbol": "NOUN" }, "nftCount": 1 } ] }, "transactions": { "entries": [ { "hash": "0x1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890", "blockNumber": 19876543, "timestamp": "2025-06-10T14:22:08Z", "from": { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" }, "to": { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" }, "value": { "formatted": "0.000000000000000000" }, "status": "SUCCESS" } ] } } } } ``` ## Implementation Notes :::note - The `address` query returns `null` if the address has never been seen on-chain. Always handle the null case in your application. - The return type is the [`Address`](/onesource-web3-api-reference/types/interfaces/address) interface, which is implemented by [`EOA`](/onesource-web3-api-reference/types/objects/eoa) and contract types ([`Token`](/onesource-web3-api-reference/types/objects/token), [`NFTContract`](/onesource-web3-api-reference/types/objects/nftcontract), [`Contract`](/onesource-web3-api-reference/types/objects/contract)). Use GraphQL inline fragments (`... on EOA`, `... on Token`) to access type-specific fields. - The `tokenBalances`, `nftBalances`, and `transactions` fields are paginated sub-queries with their own `first`, `after`, `where`, `orderBy`, and `orderDirection` parameters. - To look up only smart contracts, use the [`contract`](/onesource-web3-api-reference/queries/contract) query instead, which returns the appropriate contract type directly. - To search and filter across multiple addresses, use the [`addresses`](/onesource-web3-api-reference/queries/addresses) query with pagination and filtering support. - The `nonce` field reflects the number of transactions sent from the address. For EOAs, this is the standard account nonce. - The `transactionCount` field includes all transactions involving the address (both sent and received). ::: --- ## addresses {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/addresses.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build address explorer pages by listing and paginating through on-chain addresses with sorting and filtering. - Identify highly active wallets by ordering with `orderBy: TRANSACTION_COUNT` or `orderBy: NONCE` to surface power users. - Monitor recently active addresses by filtering with `lastActiveAt: { gte: "..." }` for real-time activity dashboards. - Find dormant wallets by filtering with `lastActiveAt: { lte: "..." }` for analytics on inactive addresses. - Look up a specific address within a filtered result set using the `address` filter on `AddressFilter`. - Filter addresses by transaction activity using `nonce: { gte, lte }` ranges to segment wallets by engagement level. ## Example ### Query #### Find Most Active Addresses Recently ```graphql query ActiveAddresses( $where: AddressFilter $first: Int $orderBy: AddressOrderBy $orderDirection: OrderDirection ) { addresses( where: $where first: $first orderBy: $orderBy orderDirection: $orderDirection ) { totalCount pageInfo { hasNextPage endCursor } entries { address nonce lastActiveAt transactionCount } } } ``` ### Variables ```json { "where": { "lastActiveAt": { "gte": "2025-06-01T00:00:00Z" }, "nonce": { "gte": 100 } }, "first": 20, "orderBy": "TRANSACTION_COUNT", "orderDirection": "DESC" } ``` ### Response ```json { "data": { "addresses": { "totalCount": 5842, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6MjB9" }, "entries": [ { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "nonce": 1247, "lastActiveAt": "2025-06-10T14:22:08Z", "transactionCount": 1893 }, { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2", "nonce": 983, "lastActiveAt": "2025-06-09T21:15:44Z", "transactionCount": 1456 }, { "address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "nonce": 714, "lastActiveAt": "2025-06-10T09:03:17Z", "transactionCount": 1102 } ] } } } ``` ## Implementation Notes :::note - The `addresses` query returns both EOAs and contract addresses. Use the [`contracts`](/onesource-web3-api-reference/queries/contracts) query if you need to filter specifically by contract type or token standard. - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - Range filters use structured input objects with `gte` (greater than or equal) and `lte` (less than or equal) fields — e.g., `nonce: { gte: 100 }`, `lastActiveAt: { gte: "2025-01-01T00:00:00Z" }`. - Use `orderBy` and `orderDirection` to ensure consistent ordering when paginating through large result sets. - Available `orderBy` values are `ADDRESS`, `NONCE`, `LAST_ACTIVE`, and `TRANSACTION_COUNT`. - The entries in the result implement the [`Address`](/onesource-web3-api-reference/types/interfaces/address) interface. Use inline fragments (`... on EOA`, `... on Token`, `... on NFTContract`) to access type-specific fields. - To look up a single address directly, use the [`address`](/onesource-web3-api-reference/queries/address) query instead. ::: --- ## block {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/block.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Display detailed block information in block explorer applications. - Verify that a specific transaction was included in a block and retrieve its confirmation count for payment validation. - Analyze gas usage patterns across blocks to help users optimize their transaction timing and gas price strategies. - Monitor blocks for specific criteria such as high gas usage, large transaction volumes, or particular transaction patterns. - List contracts deployed within a specific block using the nested `contractsCreated` field. ## Example ### Query #### Identify Wrapped Ether (WETH) Transactions in a Specific Block ```graphql query WrappedEtherBlockTransactions( $number: BlockNumber $first: Int $where: BlockTransactionFilter ) { block(number: $number) { number hash timestamp transactionCount size gasUsed gasLimit confirmations transactions(first: $first, where: $where) { totalCount pageInfo { hasNextPage endCursor } entries { hash from { address } to { address } value { raw formatted } gas { limit price used } timestamp status } } } } ``` ### Variables ```json { "number": 22530116, "first": 5, "where": { "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" } } ``` ### Response ```json { "data": { "block": { "number": 22530116, "hash": "0x968ae3cd572c782192b122d7d579672e0237aee0d644a90b591d4f5b7f745b95", "timestamp": "2025-09-14T18:22:35Z", "transactionCount": 312, "size": 170635, "gasUsed": "35980169", "gasLimit": "36000000", "confirmations": 1149727, "transactions": { "totalCount": 8, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6NX0=" }, "entries": [ { "hash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", "from": { "address": "0x32f5ae3af145dbd51f5a84b0004b7588a87c9790" }, "to": { "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" }, "value": { "raw": "1000000000000000000", "formatted": "1.0" }, "gas": { "limit": 54244, "price": "1250655772", "used": 46109 }, "timestamp": "2025-09-14T18:22:35Z", "status": "SUCCESS" } ] } } } } ``` ## Implementation Notes :::note - The `block` query returns `null` if the block doesn't exist. Always handle the null case in your application. - Provide either `number` or `hash` to look up a block, not both. The `number` argument is of type [`BlockNumber`](/onesource-web3-api-reference/types/scalars/block-number) (a positive integer representing block height). The `hash` argument is of type [`BlockHash`](/onesource-web3-api-reference/types/scalars/block-hash) (a `0x`-prefixed, 66-character hex string). - Using `number` to query by block height is generally most efficient and the most common approach. Use `hash` when you have a specific block hash from a transaction receipt or block header. - The `gasUsed` and `gasLimit` fields are [`BigInt`](/onesource-web3-api-reference/types/scalars/big-int) strings (not integers), since gas values can exceed JavaScript's safe integer range. - The `timestamp` field returns an ISO 8601 formatted string (e.g., `"2025-09-14T18:22:35Z"`), not a Unix integer. - The `confirmations` field updates dynamically as new blocks are mined. Higher confirmation counts indicate lower risk of chain reorganization. - The nested `transactions` field returns a [`BlockTransactionList`](/onesource-web3-api-reference/types/objects/block-transaction-list) and accepts its own pagination arguments (`first`, `after`) and filter/sort arguments. The filter type is [`BlockTransactionFilter`](/onesource-web3-api-reference/types/inputs/block-transaction-filter) (not `TransactionFilter`), which supports filtering by `from`, `to`, `value`, `gasPrice`, `gasUsed`, and `status`. - Transactions within a block are returned as [`BlockTransaction`](/onesource-web3-api-reference/types/objects/block-transaction) objects — a lighter-weight representation than the full `Transaction` type. Fields like `from` and `to` return [`Address`](/onesource-web3-api-reference/types/interfaces/address) objects (use `from { address }` to get the hex string), `value` returns a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount) object, and `gas` returns a [`GasInfo`](/onesource-web3-api-reference/types/objects/gas-info) object. - The nested `contractsCreated` field returns a [`ContractList`](/onesource-web3-api-reference/types/objects/contract-list) of contracts deployed in that block, with its own pagination and filter/sort arguments using [`ContractFilter`](/onesource-web3-api-reference/types/inputs/contract-filter) and [`ContractOrderBy`](/onesource-web3-api-reference/types/enums/contract-order-by). - To search and filter across multiple blocks, use the [`blocks`](/onesource-web3-api-reference/queries/blocks) query with pagination and filtering support. ::: --- ## blocks {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/blocks.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Display detailed block information in block explorer applications. - Conduct historical analysis of block data using filters such as block number ranges (`number: { gte, lte }`), timestamps (`timestamp: { gte, lte }`), and transaction counts (`transactionCount: { gte, lte }`). - Monitor for unusual or significant on-chain activity such as blocks with high transaction volumes or gas usage. - Build custom blockchain data pipelines with filtered block ranges. ## Example ### Query #### Get 5 Most Recent Blocks ```graphql query Get5MostRecentBlocks($first: Int, $orderBy: BlockOrderBy, $orderDirection: OrderDirection) { blocks(first: $first, orderBy: $orderBy, orderDirection: $orderDirection) { totalCount pageInfo { hasNextPage endCursor } entries { number timestamp size gasLimit gasUsed hash transactionCount confirmations } } } ``` ### Variables ```json { "first": 5, "orderBy": "NUMBER", "orderDirection": "DESC" } ``` ### Response ```json { "data": { "blocks": { "totalCount": 21473892, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6NX0=" }, "entries": [ { "number": 142934353, "timestamp": "2026-02-10T14:31:23Z", "size": 17819, "gasLimit": "40000000", "gasUsed": "12861377", "hash": "0x968ae3cd572c782192b122d7d579672e0237aee0d644a90b591d4f5b7f745b95", "transactionCount": 187, "confirmations": 12 }, { "number": 142934352, "timestamp": "2026-02-10T14:31:11Z", "size": 12601, "gasLimit": "40000000", "gasUsed": "36751745", "hash": "0x513896b1480a75493aebf1c53e65c2c247a14036a49e782a635bcc9bbe3b02fd", "transactionCount": 312, "confirmations": 13 }, { "number": 142934351, "timestamp": "2026-02-10T14:30:59Z", "size": 14156, "gasLimit": "40000000", "gasUsed": "13804391", "hash": "0x68ad70ff88c833981ffed861569e0671b205ec9d63d09426876536564da66048", "transactionCount": 145, "confirmations": 14 }, { "number": 142934350, "timestamp": "2026-02-10T14:30:47Z", "size": 11536, "gasLimit": "40000000", "gasUsed": "12190918", "hash": "0x48c9ac300357d4b0a441a0173a5271cf3512d813f0567765a249d7b457a817c6", "transactionCount": 98, "confirmations": 15 }, { "number": 142934349, "timestamp": "2026-02-10T14:30:35Z", "size": 15769, "gasLimit": "40000000", "gasUsed": "9269063", "hash": "0x79a4c9f0f7d0fa7df0788918e656ada4b7938d127e0a035eac72ca0fcea6d608", "transactionCount": 132, "confirmations": 16 } ] } } } ``` ## Implementation Notes :::note - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - Range filters use structured input objects with `gte` and `lte` fields — e.g., `number: { gte: 18000000, lte: 19000000 }`, `timestamp: { gte: "2025-01-01T00:00:00Z" }`, `gasUsed: { gte: "10000000" }`. - Use `orderBy` and `orderDirection` (`ASC` for oldest first, `DESC` for newest first) to ensure consistent ordering when paginating. - Each `Block` entry supports a nested `transactions()` field with its own pagination arguments (`first`, `after`, `where`, `orderBy`, `orderDirection`) for fetching transactions within that block. - Each `Block` entry also supports a nested `contractsCreated()` field for listing contracts deployed in that block. - The `gasLimit` and `gasUsed` fields are `BigInt` strings. The `timestamp` field is an ISO 8601 formatted string. ::: --- ## contract {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/contract.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build contract detail pages by looking up a specific smart contract to display its name, symbol, deployment info, and token standards. - Determine whether a contract is an ERC-20 token, NFT collection (ERC-721/ERC-1155), or generic contract to customize UI rendering and available actions. - Retrieve deployment provenance by accessing `createdAt`, `createdInBlock`, `createdByTransaction`, and `creator` for contract verification or audit tools. - Display holder counts and total supply for a known token contract address in portfolio or analytics views. - Access a contract's token balances, NFT balances, and transaction history through the nested paginated fields on the returned type. - Check contract activity status using `lastActiveAt` and `transactionCount` for monitoring or alerting dashboards. ## Example ### Query #### Look Up a Smart Contract by Address ```graphql query ContractDetails($address: AddressString!) { contract(address: $address) { ... on Token { address name symbol decimals holdersCount totalSupply { raw formatted } standards createdAt createdInBlock { number } creator { address } nonce lastActiveAt transactionCount } ... on NFTContract { address name symbol holdersCount nftCount standards createdAt createdInBlock { number } creator { address } nonce lastActiveAt transactionCount } ... on Contract { address name symbol standards createdAt createdInBlock { number } creator { address } nonce lastActiveAt transactionCount } } } ``` ### Variables ```json { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" } ``` ### Response ```json { "data": { "contract": { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "holdersCount": 2150000, "totalSupply": { "raw": "25000000000000000", "formatted": "25000000000.000000" }, "standards": ["ERC20"], "createdAt": "2018-08-03T18:25:43Z", "createdInBlock": { "number": 6082465 }, "creator": { "address": "0x95Ba4cF87D6723ad9C0Db21737D862bE44e917A2" }, "nonce": 1, "lastActiveAt": "2026-02-13T10:30:00Z", "transactionCount": 289450000 } } } ``` ## Implementation Notes :::note - The `contract` query returns `null` if the given address is not a smart contract (i.e., it is an EOA or has never been seen on-chain). Always handle the null case in your application. - The return type is [`IContract`](/onesource-web3-api-reference/types/interfaces/icontract), which may resolve to [`Token`](/onesource-web3-api-reference/types/objects/token) (ERC-20), [`NFTContract`](/onesource-web3-api-reference/types/objects/nftcontract) (ERC-721/ERC-1155), or [`Contract`](/onesource-web3-api-reference/types/objects/contract) (generic). Use GraphQL inline fragments (`... on Token`, `... on NFTContract`, `... on Contract`) to access type-specific fields. - The `createdInBlock`, `createdByTransaction`, and `creator` fields return full objects (`Block`, `Transaction`, and `Address` respectively), not scalar values. Select the specific sub-fields you need. - The `tokenBalances`, `nftBalances`, and `transactions` fields are paginated sub-queries available on the returned contract, each with their own `first`, `after`, `where`, `orderBy`, and `orderDirection` parameters. - If you already know the contract is an ERC-20 token, consider using the [`token`](/onesource-web3-api-reference/queries/token) query instead for a more targeted return type. - To search and filter across multiple contracts, use the [`contracts`](/onesource-web3-api-reference/queries/contracts) query with pagination and filtering support. - To look up any address (including EOAs), use the [`address`](/onesource-web3-api-reference/queries/address) query instead. ::: --- ## contracts {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/contracts.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Browse all ERC-20 tokens by filtering with `standards: [ERC20]` to build token directories or DEX interfaces. - Monitor newly deployed contracts by filtering with `createdAt: { gte: "..." }` to track ecosystem growth. - Search for specific contracts by name or symbol using `nameLike` and `symbolLike` filters for discovery features. - Analyze popular contracts by ordering with `orderBy: HOLDERS` to identify trending tokens and high-adoption projects. - Track NFT collections by filtering with `standards: [ERC721]` or `standards: [ERC1155]` to build NFT marketplaces or galleries. - Combine filters like `createdBlock: { gte, lte }` and `holdersCount: { gte, lte }` to create advanced analytics and reporting tools. - Find contracts deployed by a specific address using the `creator` filter. ## Example ### Query #### Find Popular ERC-20 Contracts Created Recently ```graphql query FindPopularContracts( $first: Int $where: ContractFilter $orderBy: ContractOrderBy $orderDirection: OrderDirection ) { contracts( first: $first where: $where orderBy: $orderBy orderDirection: $orderDirection ) { totalCount pageInfo { hasNextPage endCursor } entries { ... on Token { address name symbol decimals holdersCount totalSupply { formatted } createdAt standards } ... on NFTContract { address name symbol holdersCount nftCount createdAt standards } ... on Contract { address name symbol createdAt standards } } } } ``` ### Variables ```json { "first": 10, "where": { "standards": ["ERC20"], "createdAt": { "gte": "2025-11-01T00:00:00Z" }, "holdersCount": { "gte": 100 } }, "orderBy": "HOLDERS", "orderDirection": "DESC" } ``` ### Response ```json { "data": { "contracts": { "totalCount": 234, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6MTB9" }, "entries": [ { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "holdersCount": 2150000, "totalSupply": { "formatted": "25000000000.000000" }, "createdAt": "2025-11-05T12:34:56Z", "standards": ["ERC20"] }, { "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "name": "Dai Stablecoin", "symbol": "DAI", "decimals": 18, "holdersCount": 890000, "totalSupply": { "formatted": "3500000000.000000000000000000" }, "createdAt": "2025-11-22T09:15:30Z", "standards": ["ERC20"] } ] } } } ``` ## Implementation Notes :::note - The `contracts` query returns a [`ContractList`](/onesource-web3-api-reference/types/objects/contract-list) containing [`IContract`](/onesource-web3-api-reference/types/interfaces/icontract) entries, which may resolve to [`Token`](/onesource-web3-api-reference/types/objects/token) (ERC-20), [`NFTContract`](/onesource-web3-api-reference/types/objects/nftcontract) (ERC-721/ERC-1155), or [`Contract`](/onesource-web3-api-reference/types/objects/contract) (generic). Use inline fragments (`... on Token`, `... on NFTContract`) to access type-specific fields. - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - Filter by token standard using `standards: [ERC20]`, `standards: [ERC721]`, or `standards: [ERC1155]`. A contract can implement multiple standards. - Range filters use structured input objects with `gte` and `lte` fields — e.g., `holdersCount: { gte: 100 }`, `createdAt: { gte: "2025-01-01T00:00:00Z" }`, `createdBlock: { gte: 18000000, lte: 19000000 }`. - Use `orderBy` and `orderDirection` (`ASC` or `DESC`) to ensure consistent ordering when paginating through large result sets. - Available `orderBy` values are `ADDRESS`, `CREATED_AT`, `CREATED_BLOCK`, `NAME`, `SYMBOL`, `HOLDERS`, and `NONCE`. - The `nameLike` and `symbolLike` filters perform case-insensitive partial matching, making them ideal for search functionality. - Use the `creator` filter to find all contracts deployed by a specific address. - To look up a single contract directly by address, use the [`contract`](/onesource-web3-api-reference/queries/contract) query instead. ::: --- ## nft {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/nft.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build NFT detail pages by looking up a specific token with its full metadata, media, and ownership information. - Verify NFT ownership and authenticity for marketplace listings, gating, or wallet displays. - Display NFT artwork and media with optimized thumbnails at various preset sizes for galleries and collection viewers. - Check whether an NFT has been burned by inspecting the `burned` field and `burnTransaction`. - Retrieve mint provenance by accessing the `mintTransaction` field for historical analysis or verification. - Differentiate between ERC-721 and ERC-1155 NFTs using inline fragments to access standard-specific fields like `holder` or `holdersCount`. ## Example ### Query #### Look Up an ERC-721 NFT with Metadata and Media ```graphql query GetNFTDetails($contract: AddressString!, $tokenId: TokenId!) { nft(contract: $contract, tokenId: $tokenId) { tokenId name standard contract { address name symbol standards } metadata { description image attributes { traitType value } } media { url contentType thumbnails(preset: MEDIUM) { url width height } } ... on SingleHolderNFT { holder { address } burned mintTransaction { hash timestamp } } ... on MultiHolderNFT { totalSupply holdersCount holders(first: 5) { entries { address { address } balance } } } } } ``` ### Variables ```json { "contract": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c", "tokenId": "42141102" } ``` ### Response ```json { "data": { "nft": { "tokenId": "42141102", "name": "Singular Focus", "standard": "ERC721", "contract": { "address": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c", "name": "Art Blocks", "symbol": "BLOCKS", "standards": ["ERC721"] }, "metadata": { "description": "The Partition is formed by a set of recursive subdivisions of the canvas...", "image": "ipfs://QmExample123abc456def789ghi", "attributes": [ { "traitType": "Artist", "value": "Example Artist" }, { "traitType": "Collection", "value": "Curated" } ] }, "media": [ { "url": "https://media.onesource.io/nft/1/42141102.png", "contentType": "image/png", "thumbnails": [ { "url": "https://media.onesource.io/thumbnails/medium/1/42141102.png", "width": 300, "height": 300 } ] } ], "holder": { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2" }, "burned": false, "mintTransaction": { "hash": "0x9f8e7d6c5b4a3210fedcba9876543210fedcba9876543210fedcba9876543210", "timestamp": "2025-03-20T16:45:22Z" } } } } ``` ## Implementation Notes :::note - Both `contract` and `tokenId` arguments are required to uniquely identify an NFT. The `tokenId` is a string to support the full `uint256` range. - The `nft` query returns `null` if the NFT doesn't exist or hasn't been minted. Always handle the null case in your application. - The return type is the [`NFT`](/onesource-web3-api-reference/types/interfaces/nft) interface, which resolves to either [`SingleHolderNFT`](/onesource-web3-api-reference/types/objects/single-holder-nft) (ERC-721) or [`MultiHolderNFT`](/onesource-web3-api-reference/types/objects/multi-holder-nft) (ERC-1155). Use inline fragments (`... on SingleHolderNFT`, `... on MultiHolderNFT`) to access type-specific fields. - For ERC-721 NFTs, the `holder` field returns the single current owner. For ERC-1155 NFTs, use `holdersCount` and the paginated `holders` field to access multiple owners and their balances. - The `metadata` field contains parsed token metadata including `description`, `image`, and `attributes`. This data is fetched and cached from the token URI — it may be `null` if the metadata hasn't been fetched yet or the URI is unreachable. - The `media` array contains processed media files with pre-generated `thumbnails` at various presets (`SMALL`, `MEDIUM`, `LARGE`, `ORIGINAL`). Use thumbnails for performant rendering in galleries and lists. - To search and filter across multiple NFTs, use the [`nfts`](/onesource-web3-api-reference/queries/nfts) query with pagination and filtering support. ::: --- ## nfts {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/nfts.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build NFT marketplace browse pages by listing NFTs across collections with filtering, sorting, and pagination. - Search for NFTs by name using `nameLike` for discovery features and search bars. - Browse a specific collection's NFTs by filtering with `contract: { address: "0x..." }` to build collection detail pages. - Filter NFTs by token standard using `standard: ERC721` or `standard: ERC1155` to separate single-owner and multi-owner NFTs. - Display NFT galleries with optimized media by selecting `media` and `thumbnails` fields for performant image rendering. - Perform cross-contract NFT search by combining `contract: { nameLike: "..." }` with NFT-level filters for broad discovery. ## Example ### Query #### Browse NFTs in a Collection with Media ```graphql query BrowseCollectionNFTs( $where: NFTFilter $first: Int $orderBy: NFTOrderBy $orderDirection: OrderDirection ) { nfts( where: $where first: $first orderBy: $orderBy orderDirection: $orderDirection ) { totalCount pageInfo { hasNextPage endCursor } entries { tokenId name standard contract { address name symbol } metadata { description attributes { traitType value } } media { url contentType thumbnails(preset: MEDIUM) { url width height } } ... on SingleHolderNFT { holder { address } burned } ... on MultiHolderNFT { totalSupply holdersCount } } } } ``` ### Variables ```json { "where": { "contract": { "address": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c" }, "standard": "ERC721" }, "first": 20, "orderBy": "TOKEN_ID", "orderDirection": "ASC" } ``` ### Response ```json { "data": { "nfts": { "totalCount": 12480, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6MjB9" }, "entries": [ { "tokenId": "0", "name": "Chromie Squiggle #0", "standard": "ERC721", "contract": { "address": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c", "name": "Art Blocks", "symbol": "BLOCKS" }, "metadata": { "description": "Simple and easily identifiable, each squiggle embodies the soul of the Art Blocks platform.", "attributes": [ { "traitType": "Color Spread", "value": "7" }, { "traitType": "Steps Between", "value": "20" } ] }, "media": [ { "url": "https://media.onesource.io/nft/1/0.png", "contentType": "image/png", "thumbnails": [ { "url": "https://media.onesource.io/thumbnails/medium/1/0.png", "width": 300, "height": 300 } ] } ], "holder": { "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2" }, "burned": false }, { "tokenId": "1", "name": "Chromie Squiggle #1", "standard": "ERC721", "contract": { "address": "0xC9041f80DCe73721A5f6a779672Ec57EF255d27c", "name": "Art Blocks", "symbol": "BLOCKS" }, "metadata": { "description": "Simple and easily identifiable, each squiggle embodies the soul of the Art Blocks platform.", "attributes": [ { "traitType": "Color Spread", "value": "5" }, { "traitType": "Steps Between", "value": "15" } ] }, "media": [ { "url": "https://media.onesource.io/nft/1/1.png", "contentType": "image/png", "thumbnails": [ { "url": "https://media.onesource.io/thumbnails/medium/1/1.png", "width": 300, "height": 300 } ] } ], "holder": { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" }, "burned": false } ] } } } ``` ## Implementation Notes :::note - The `nfts` query searches across **all NFT contracts**. To browse NFTs within a single collection, filter with `contract: { address: "0x..." }`. To look up a single NFT directly, use the [`nft`](/onesource-web3-api-reference/queries/nft) query instead. - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - The `NFTFilter` supports nested `contract` filtering using a [`ContractFilter`](/onesource-web3-api-reference/types/inputs/contract-filter), enabling cross-contract search — e.g., `contract: { nameLike: "Art" }` finds NFTs across all collections with "Art" in the name. - Available `orderBy` values are `TOKEN_ID`, `NAME`, `TOTAL_SUPPLY`, and `HOLDERS`. The `TOTAL_SUPPLY` and `HOLDERS` options are primarily useful for ERC-1155 NFTs. - Use `orderBy` and `orderDirection` to ensure consistent ordering when paginating through large result sets. - Entries in the result implement the [`NFT`](/onesource-web3-api-reference/types/interfaces/nft) interface. Use inline fragments (`... on SingleHolderNFT`, `... on MultiHolderNFT`) to access standard-specific fields like `holder` or `holdersCount`. - The `nameLike` filter performs case-insensitive partial matching, making it ideal for search functionality. - Additional filters include `media` and `metadata` sub-filters for querying by media properties or metadata content. ::: --- ## token {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/token.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Display token detail pages showing an ERC-20 token's name, symbol, supply, and holder count. - Build portfolio views by looking up individual tokens and their `totalSupply` with both `raw` and `formatted` values. - Analyze token distribution by paginating through the `holders` field to see top holders and their balances. - Retrieve deployment provenance via `createdAt`, `createdInBlock`, `createdByTransaction`, and `creator` for token verification or analytics. - Check a token contract's on-chain activity using `transactionCount` and `lastActiveAt` for health monitoring. - Access the token contract's own asset holdings through the `tokenBalances` and `nftBalances` fields. ## Example ### Query #### Look Up an ERC-20 Token with Supply and Top Holders ```graphql query GetTokenDetails($address: AddressString!) { token(address: $address) { address name symbol decimals standards totalSupply { raw formatted decimals } holdersCount holders(first: 5, orderBy: BALANCE, orderDirection: DESC) { totalCount pageInfo { hasNextPage endCursor } entries { address { address } balance { formatted } } } createdAt creator { address } transactionCount lastActiveAt } } ``` ### Variables ```json { "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } ``` ### Response ```json { "data": { "token": { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "standards": ["ERC20"], "totalSupply": { "raw": "25000000000000000", "formatted": "25000000000.000000", "decimals": 6 }, "holdersCount": 2150000, "holders": { "totalCount": 2150000, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6NX0" }, "entries": [ { "address": { "address": "0x28C6c06298d514Db089934071355E5743bf21d60" }, "balance": { "formatted": "3200000000.000000" } }, { "address": { "address": "0x21a31Ee1afC51d94C2eFcCAa2092aD1028285549" }, "balance": { "formatted": "1850000000.000000" } }, { "address": { "address": "0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503" }, "balance": { "formatted": "1200000000.000000" } }, { "address": { "address": "0xDFd5293D8e347dFe59E90eFd55b2956a1343963d" }, "balance": { "formatted": "980000000.000000" } }, { "address": { "address": "0x5041ed759Dd4aFc3a72b8192C143F72f4724081A" }, "balance": { "formatted": "750000000.000000" } } ] }, "createdAt": "2018-08-03T18:28:32Z", "creator": { "address": "0x95Ba4cF87D6723ad9C0Db21737D862bE44A0E3F0" }, "transactionCount": 245893012, "lastActiveAt": "2025-06-10T14:22:08Z" } } } ``` ## Implementation Notes :::note - The `token` query looks up a **fungible ERC-20 token** by its contract address. To look up an NFT, use the [`nft`](/onesource-web3-api-reference/queries/nft) query instead. - The query returns `null` if the address is not an ERC-20 token contract. Always handle the null case in your application. - The `totalSupply` field returns a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount) object with `raw` (base unit string), `formatted` (human-readable with decimals applied), and `decimals` values. - The `holders` field returns a paginated [`TokenHolderList`](/onesource-web3-api-reference/types/objects/token-holder-list). Each entry contains the holder's `address` and their `balance` as a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount). Use `orderBy: BALANCE` with `orderDirection: DESC` to retrieve top holders. - The `holdersCount` field returns the number of unique addresses holding a non-zero balance of this token. - Timestamps such as `createdAt` and `lastActiveAt` are ISO 8601 formatted strings in UTC (e.g., `"2024-01-15T14:30:00Z"`). - To search and filter across multiple ERC-20 tokens, use the [`tokens`](/onesource-web3-api-reference/queries/tokens) query with pagination and filtering support. ::: --- ## tokens {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/tokens.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Build token directories and DEX interfaces by listing ERC-20 tokens with filtering and sorting. - Search for tokens by name or symbol using `nameLike` and `symbolLike` for token discovery features. - Analyze popular tokens by ordering with `orderBy: HOLDERS` to identify high-adoption tokens. - Monitor newly deployed tokens by filtering with `createdAt: { gte: "..." }` to track ecosystem growth. - Filter tokens by supply range using `totalSupply: { gte, lte }` for analytics and categorization. - Combine filters like `holdersCount` ranges and `decimals` to create advanced token screening tools. ## Example ### Query #### Find Popular ERC-20 Tokens by Holder Count ```graphql query PopularTokens($where: TokenFilter, $first: Int) { tokens(where: $where, first: $first, orderBy: HOLDERS, orderDirection: DESC) { totalCount pageInfo { hasNextPage endCursor } entries { address name symbol decimals totalSupply { formatted } holdersCount createdAt } } } ``` ### Variables ```json { "where": { "holdersCount": { "gte": 1000 }, "createdAt": { "gte": "2025-01-01T00:00:00Z" } }, "first": 20 } ``` ### Response ```json { "data": { "tokens": { "totalCount": 347, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6MjB9" }, "entries": [ { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "totalSupply": { "formatted": "25000000000.000000" }, "holdersCount": 2150000, "createdAt": "2025-03-15T08:22:10Z" }, { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "name": "Tether USD", "symbol": "USDT", "decimals": 6, "totalSupply": { "formatted": "40000000000.000000" }, "holdersCount": 1890000, "createdAt": "2025-02-10T14:05:33Z" } ] } } } ``` ## Implementation Notes :::note - The `tokens` query is specifically for **ERC-20 fungible tokens**. To query NFTs, use the [`nfts`](/onesource-web3-api-reference/queries/nfts) query instead. - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - Range filters use structured input objects with `gte` (greater than or equal) and `lte` (less than or equal) fields — e.g., `holdersCount: { gte: 100 }`, `totalSupply: { gte: "1000000" }`. - Use `orderBy` and `orderDirection` to ensure consistent ordering when paginating through large result sets. - The `nameLike` and `symbolLike` filters perform case-insensitive partial matching, making them ideal for search functionality. - The `totalSupply` field returns a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount) object with both `raw` (base unit string) and `formatted` (human-readable) values. - The `holdersCount` field returns the number of unique addresses holding a non-zero balance. - Use the `holders` paginated field on individual `Token` entries to retrieve the actual list of holder addresses and their balances. ::: --- ## transaction {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/transaction.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Monitor transaction finality and confirmation depth for payment systems, exchanges, and dApps. - Analyze historical gas costs by inspecting the `gas` field to optimize future transaction pricing and fee strategies. - Build transaction explorers, wallet dashboards, or audit trails for addresses. - Verify and debug smart contract calls, including contract deployments (when `to` is `null` and `contractCreated` is populated). - Retrieve full block context for a transaction via the nested `block` field. ## Example ### Query #### Verify Payment Status by Transaction Hash ```graphql query VerifyPayment($hash: TransactionHash!) { transaction(hash: $hash) { hash timestamp status confirmations from { address } to { address } value { raw formatted decimals } gas { limit price used feeCap tipCap } block { number timestamp } } } ``` ### Variables ```json { "hash": "0x22e64f2a255bbe709a702fbc22412339e7fa876b422493b6b7e03aa5fcd3b49c" } ``` ### Response ```json { "data": { "transaction": { "hash": "0x22e64f2a255bbe709a702fbc22412339e7fa876b422493b6b7e03aa5fcd3b49c", "timestamp": "2025-10-18T10:44:47Z", "status": "SUCCESS", "confirmations": 40059, "from": { "address": "0x710312405c896cedcaef82cb9105bee07ecbeab8" }, "to": { "address": "0x99866915b444637925058a7a9984ab32e2c0714c" }, "value": { "raw": "3976740000000000", "formatted": "0.00397674", "decimals": 18 }, "gas": { "limit": 21000, "price": "1250655772", "used": 21000, "feeCap": "1500000000", "tipCap": "100000000" }, "block": { "number": 23602632, "timestamp": "2025-10-18T10:44:47Z" } } } } ``` ## Implementation Notes :::note - The `transaction` query returns `null` if the transaction doesn't exist or is still pending. Always handle the null case in your application. - The `hash` argument is of type [`TransactionHash!`](/onesource-web3-api-reference/types/scalars/transaction-hash) — a `0x`-prefixed, 66-character hex string. - The `from` and `to` fields return [`Address`](/onesource-web3-api-reference/types/interfaces/address) objects, not plain strings. Use `from { address }` or `to { address }` to access the hex string. You can also traverse into balances, transaction history, and other address fields. - The `to` field is `null` for contract creation transactions. In that case, use the `contractCreated` field to access the deployed contract (returned as [`IContract`](/onesource-web3-api-reference/types/interfaces/icontract)). - The `value` field returns a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount) object with `raw` (wei string), `formatted` (human-readable with decimals applied), and `decimals` fields. Use `formatted` for display instead of converting manually. - Gas information is nested under the [`gas`](/onesource-web3-api-reference/types/objects/gas-info) field as a [`GasInfo`](/onesource-web3-api-reference/types/objects/gas-info) object. It includes `limit` (max gas authorized), `price` (effective gas price in wei), `used` (actual gas consumed, or `null` if pending), `feeCap` (EIP-1559 max fee per gas), and `tipCap` (EIP-1559 priority fee). For legacy transactions, `feeCap` and `tipCap` will be `null`. - The `block` field returns a [`Block`](/onesource-web3-api-reference/types/objects/block) object containing the block `number`, `timestamp`, `hash`, and other block-level data. It is `null` for pending transactions. - The `confirmations` field is a top-level integer on the `Transaction` type that updates dynamically as new blocks are mined. Higher values indicate greater finality. - The `timestamp` field returns an ISO 8601 formatted string (e.g., `"2025-10-18T10:44:47Z"`), not a Unix integer. - The `status` field returns a [`TransactionStatus`](/onesource-web3-api-reference/types/enums/transaction-status) enum: `SUCCESS`, `FAILED`, or `PENDING`. - To search and filter across multiple transactions, use the [`transactions`](/onesource-web3-api-reference/queries/transactions) query with pagination and filtering support. ::: --- ## transactions {/* TODO: Update endpoint when /federation/ path is removed from API */} import GeneratedContent from '../../schema/operations/queries/transactions.mdx' API Context — Endpoint: https://api.onesource.io/federation/{'{'}chain{'}'}/graphql | Auth Header: x-bp-token: BP-{'{'}YOUR_API_KEY{'}'} | Method: POST | Content-Type: application/json ## Common Use Cases - Display a user's complete transaction history by filtering on the `from` or `to` address fields in [`TransactionFilter`](/onesource-web3-api-reference/types/inputs/transaction-filter). - Find all transactions involving a specific address (as sender or recipient) using the `involvesAddress` filter. - Monitor large value transfers by filtering with `value: { gte: "1000000000000000000" }` (1 ETH in wei). - Track interactions with specific smart contracts by filtering on the `to` address. - Analyze gas price trends by filtering with `gasPrice: { gte, lte }` ranges and ordering with `orderBy: GAS`. - Query transactions within a block range using `blockNumber: { gte, lte }` or a time window using `timestamp: { gte, lte }`. - Filter by transaction outcome using `status: SUCCESS`, `status: FAILED`, or `status: PENDING`. - Find contract deployment transactions using `hasContractCreation: true`. ## Example ### Query #### Get 5 Most Recent Transactions to the USDT Contract ```graphql query GetRecentUSDTTransactions( $where: TransactionFilter $first: Int $orderBy: TransactionOrderBy $orderDirection: OrderDirection ) { transactions( where: $where first: $first orderBy: $orderBy orderDirection: $orderDirection ) { totalCount pageInfo { hasNextPage endCursor } entries { hash timestamp from { address } to { address } value { raw formatted } gas { limit price used } block { number } status } } } ``` ### Variables ```json { "where": { "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "first": 5, "orderBy": "TIMESTAMP", "orderDirection": "DESC" } ``` ### Response ```json { "data": { "transactions": { "totalCount": 483920, "pageInfo": { "hasNextPage": true, "endCursor": "eyJpZCI6NX0=" }, "entries": [ { "hash": "0x59fb9291481bc4e944928f468ffe6977396bd96cdfb0c28a1438121953c0bfdd", "timestamp": "2025-10-17T08:04:11Z", "from": { "address": "0x80787af194c33b74a811f5e5c549316269d7ee1a" }, "to": { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "value": { "raw": "0", "formatted": "0.0" }, "gas": { "limit": 110000, "price": "3000000000", "used": 63209 }, "block": { "number": 23598257 }, "status": "SUCCESS" }, { "hash": "0x7b794f64afb33ed5bf5a6856d3407fd03f89a34b1a0bf27dfc193490e14717b0", "timestamp": "2025-10-17T08:04:11Z", "from": { "address": "0xe0ca2012c3304e41c9d6e6a0d7f62c33b74356cf" }, "to": { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "value": { "raw": "0", "formatted": "0.0" }, "gas": { "limit": 49273, "price": "1297332589", "used": 46321 }, "block": { "number": 23598257 }, "status": "SUCCESS" }, { "hash": "0x4eada9f9ff04b915ca6273a4ad92635a8fcd592ed7c0807aab7483506d9f906d", "timestamp": "2025-10-17T08:04:11Z", "from": { "address": "0x2df9b935c44057ac240634c7536511d8aa03028d" }, "to": { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "value": { "raw": "0", "formatted": "0.0" }, "gas": { "limit": 84000, "price": "1608773990", "used": 63197 }, "block": { "number": 23598257 }, "status": "SUCCESS" }, { "hash": "0xd50760ac010e21f902bca8e961a0de92d3df594205a52443093e6a3651312a4d", "timestamp": "2025-10-17T08:04:11Z", "from": { "address": "0x8e04af7f7c76daa9ab429b1340e0327b5b835748" }, "to": { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "value": { "raw": "0", "formatted": "0.0" }, "gas": { "limit": 250000, "price": "1323416235", "used": 63197 }, "block": { "number": 23598257 }, "status": "SUCCESS" }, { "hash": "0xf3ab3a5b91b1365388ef446ecff71f1d95e8a48a0816d8572b9759ac5d5e43cd", "timestamp": "2025-10-17T08:04:11Z", "from": { "address": "0x5fa8139192d8bb5f6684c6d808b7665e02df4833" }, "to": { "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, "value": { "raw": "0", "formatted": "0.0" }, "gas": { "limit": 70447, "price": "1601480988", "used": 63209 }, "block": { "number": 23598257 }, "status": "SUCCESS" } ] } } } ``` ## Implementation Notes :::note - The `transactions` query returns a [`TransactionList`](/onesource-web3-api-reference/types/objects/transaction-list) containing `entries`, `totalCount`, `pageInfo`, and optional `stats` for aggregate analytics. - The default `first` value is `10`. OneSource enforces a maximum `first` value of `100`. Adjust based on your needs. - Use cursor-based pagination with `after` and `pageInfo.endCursor` to page through results. Check `pageInfo.hasNextPage` to determine if more pages exist. - The `from` and `to` fields return [`Address`](/onesource-web3-api-reference/types/interfaces/address) objects, not plain strings. Use `from { address }` or `to { address }` to access the hex string. You can also traverse into balances and other address fields. - The `to` field is `null` for contract creation transactions. Use `hasContractCreation: true` in the filter to find these, and access the deployed contract via the `contractCreated` field. - The `value` field returns a [`TokenAmount`](/onesource-web3-api-reference/types/objects/token-amount) object with `raw` (wei string), `formatted` (human-readable), and `decimals` fields. Use `formatted` for display rather than converting manually. - Gas information is nested under the [`gas`](/onesource-web3-api-reference/types/objects/gas-info) field as a `GasInfo` object with `limit`, `price`, `used`, `feeCap`, and `tipCap`. For EIP-1559 transactions, `feeCap` and `tipCap` are populated; for legacy transactions, use `price`. - The `block` field returns a [`Block`](/onesource-web3-api-reference/types/objects/block) object (or `null` if pending). Access the block number with `block { number }` and confirmations with the top-level `confirmations` field. - The `timestamp` field returns an ISO 8601 formatted string (e.g., `"2025-10-17T08:04:11Z"`), not a Unix integer. - Range filters use structured input objects with `gte` and `lte` fields — e.g., `value: { gte: "1000000000000000000" }`, `blockNumber: { gte: 18000000, lte: 19000000 }`, `timestamp: { gte: "2025-01-01T00:00:00Z" }`. - Use `orderBy` and `orderDirection` (`ASC` or `DESC`) to ensure consistent ordering when paginating. Available `orderBy` values are `BLOCK_NUMBER`, `TIMESTAMP`, `VALUE`, `HASH`, `GAS`, `NONCE`, `FROM`, and `TO`. - The optional `stats` field on the response provides aggregate statistics including `totalCount`, `totalValue`, `averageGasPrice`, `averageGasUsed`, and `uniqueAddresses`. - To look up a single transaction by hash, use the [`transaction`](/onesource-web3-api-reference/queries/transaction) query instead. ::: --- ## AddressOrderBy import GeneratedContent from '../../../schema/types/enums/address-order-by.mdx' --- ## BlockOrderBy import GeneratedContent from '../../../schema/types/enums/block-order-by.mdx' --- ## BlockTransactionOrderBy import GeneratedContent from '../../../schema/types/enums/block-transaction-order-by.mdx' --- ## ContractOrderBy import GeneratedContent from '../../../schema/types/enums/contract-order-by.mdx' --- ## FileType import GeneratedContent from '../../../schema/types/enums/file-type.mdx' --- ## MediaType import GeneratedContent from '../../../schema/types/enums/media-type.mdx' --- ## NFTBalanceOrderBy import GeneratedContent from '../../../schema/types/enums/nftbalance-order-by.mdx' --- ## NFTContractHolderOrderBy import GeneratedContent from '../../../schema/types/enums/nftcontract-holder-order-by.mdx' --- ## NFTContractOrderBy import GeneratedContent from '../../../schema/types/enums/nftcontract-order-by.mdx' --- ## NFTHolderOrderBy import GeneratedContent from '../../../schema/types/enums/nftholder-order-by.mdx' --- ## NFTOrderBy import GeneratedContent from '../../../schema/types/enums/nftorder-by.mdx' --- ## OrderDirection import GeneratedContent from '../../../schema/types/enums/order-direction.mdx' --- ## OwnedNFTOrderBy import GeneratedContent from '../../../schema/types/enums/owned-nftorder-by.mdx' --- ## Standard import GeneratedContent from '../../../schema/types/enums/standard.mdx' --- ## ThumbnailPreset import GeneratedContent from '../../../schema/types/enums/thumbnail-preset.mdx' --- ## TokenBalanceOrderBy import GeneratedContent from '../../../schema/types/enums/token-balance-order-by.mdx' --- ## TokenHolderOrderBy import GeneratedContent from '../../../schema/types/enums/token-holder-order-by.mdx' --- ## TokenOrderBy import GeneratedContent from '../../../schema/types/enums/token-order-by.mdx' --- ## TransactionOrderBy import GeneratedContent from '../../../schema/types/enums/transaction-order-by.mdx' --- ## TransactionStatus import GeneratedContent from '../../../schema/types/enums/transaction-status.mdx' --- ## AddressFilter import GeneratedContent from '../../../schema/types/inputs/address-filter.mdx' --- ## BigIntRange import GeneratedContent from '../../../schema/types/inputs/big-int-range.mdx' --- ## BlockFilter import GeneratedContent from '../../../schema/types/inputs/block-filter.mdx' --- ## BlockRange import GeneratedContent from '../../../schema/types/inputs/block-range.mdx' --- ## BlockTransactionFilter import GeneratedContent from '../../../schema/types/inputs/block-transaction-filter.mdx' --- ## ContractFilter import GeneratedContent from '../../../schema/types/inputs/contract-filter.mdx' --- ## DateRange import GeneratedContent from '../../../schema/types/inputs/date-range.mdx' --- ## IntRange import GeneratedContent from '../../../schema/types/inputs/int-range.mdx' --- ## MediaFilter import GeneratedContent from '../../../schema/types/inputs/media-filter.mdx' --- ## MetadataFilter import GeneratedContent from '../../../schema/types/inputs/metadata-filter.mdx' --- ## NFTBalanceFilter import GeneratedContent from '../../../schema/types/inputs/nftbalance-filter.mdx' --- ## NFTContractHolderFilter import GeneratedContent from '../../../schema/types/inputs/nftcontract-holder-filter.mdx' --- ## NFTFilter import GeneratedContent from '../../../schema/types/inputs/nftfilter.mdx' --- ## NFTHolderFilter import GeneratedContent from '../../../schema/types/inputs/nftholder-filter.mdx' --- ## OwnedNFTFilter import GeneratedContent from '../../../schema/types/inputs/owned-nftfilter.mdx' --- ## TokenBalanceFilter import GeneratedContent from '../../../schema/types/inputs/token-balance-filter.mdx' --- ## TokenFilter import GeneratedContent from '../../../schema/types/inputs/token-filter.mdx' --- ## TokenHolderFilter import GeneratedContent from '../../../schema/types/inputs/token-holder-filter.mdx' --- ## TransactionFilter import GeneratedContent from '../../../schema/types/inputs/transaction-filter.mdx' --- ## Address(Interfaces) import GeneratedContent from '../../../schema/types/interfaces/address.mdx' --- ## HasDecimals import GeneratedContent from '../../../schema/types/interfaces/has-decimals.mdx' --- ## IContract import GeneratedContent from '../../../schema/types/interfaces/icontract.mdx' --- ## MultipleHolders import GeneratedContent from '../../../schema/types/interfaces/multiple-holders.mdx' --- ## NFT(Interfaces) import GeneratedContent from '../../../schema/types/interfaces/nft.mdx' --- ## PaginatedList import GeneratedContent from '../../../schema/types/interfaces/paginated-list.mdx' --- ## SingleHolder import GeneratedContent from '../../../schema/types/interfaces/single-holder.mdx' --- ## AddressList import GeneratedContent from '../../../schema/types/objects/address-list.mdx' --- ## Attribute import GeneratedContent from '../../../schema/types/objects/attribute.mdx' --- ## BlockList import GeneratedContent from '../../../schema/types/objects/block-list.mdx' --- ## BlockTransactionList import GeneratedContent from '../../../schema/types/objects/block-transaction-list.mdx' --- ## BlockTransaction import GeneratedContent from '../../../schema/types/objects/block-transaction.mdx' --- ## Block(Objects) import GeneratedContent from '../../../schema/types/objects/block.mdx' --- ## ContractList import ManualContent from '../../../schema/types/objects/contract-list.mdx' --- ## Contract(Objects) import ManualContent from '../../../schema/types/objects/contract.mdx' --- ## EOA import GeneratedContent from '../../../schema/types/objects/eoa.mdx' --- ## GasInfo import GeneratedContent from '../../../schema/types/objects/gas-info.mdx' --- ## MediaThumbnail import ManualContent from '../../../schema/types/objects/media-thumbnail.mdx' --- ## Media import ManualContent from '../../../schema/types/objects/media.mdx' --- ## Metadata import GeneratedContent from '../../../schema/types/objects/metadata.mdx' --- ## MultiHolderNFT import GeneratedContent from '../../../schema/types/objects/multi-holder-nft.mdx' --- ## NFTBalanceList import GeneratedContent from '../../../schema/types/objects/nftbalance-list.mdx' --- ## NFTBalance import GeneratedContent from '../../../schema/types/objects/nftbalance.mdx' --- ## NFTContractHolderList import GeneratedContent from '../../../schema/types/objects/nftcontract-holder-list.mdx' --- ## NFTContractHolder import GeneratedContent from '../../../schema/types/objects/nftcontract-holder.mdx' --- ## NFTContractList import GeneratedContent from '../../../schema/types/objects/nftcontract-list.mdx' --- ## NFTContract import GeneratedContent from '../../../schema/types/objects/nftcontract.mdx' --- ## NFTHolderList import GeneratedContent from '../../../schema/types/objects/nftholder-list.mdx' --- ## NFTHolder import GeneratedContent from '../../../schema/types/objects/nftholder.mdx' --- ## NFTList import GeneratedContent from '../../../schema/types/objects/nftlist.mdx' --- ## OwnedNFT import GeneratedContent from '../../../schema/types/objects/owned-nft.mdx' --- ## OwnedNFTList import GeneratedContent from '../../../schema/types/objects/owned-nftlist.mdx' --- ## PageInfo import GeneratedContent from '../../../schema/types/objects/page-info.mdx' --- ## SingleHolderNFT import GeneratedContent from '../../../schema/types/objects/single-holder-nft.mdx' --- ## TokenAmount import GeneratedContent from '../../../schema/types/objects/token-amount.mdx' --- ## TokenBalanceList import GeneratedContent from '../../../schema/types/objects/token-balance-list.mdx' --- ## TokenBalance import GeneratedContent from '../../../schema/types/objects/token-balance.mdx' --- ## TokenHolderList import GeneratedContent from '../../../schema/types/objects/token-holder-list.mdx' --- ## TokenHolder import GeneratedContent from '../../../schema/types/objects/token-holder.mdx' --- ## TokenList import ManualContent from '../../../schema/types/objects/token-list.mdx' --- ## Token(Objects) import ManualContent from '../../../schema/types/objects/token.mdx' --- ## TransactionList import GeneratedContent from '../../../schema/types/objects/transaction-list.mdx' --- ## TransactionStats import GeneratedContent from '../../../schema/types/objects/transaction-stats.mdx' --- ## Transaction(Objects) import GeneratedContent from '../../../schema/types/objects/transaction.mdx' --- ## AddressString import GeneratedContent from '../../../schema/types/scalars/address-string.mdx' --- ## BigInt import GeneratedContent from '../../../schema/types/scalars/big-int.mdx' --- ## BlockHash import GeneratedContent from '../../../schema/types/scalars/block-hash.mdx' --- ## BlockNumber import GeneratedContent from '../../../schema/types/scalars/block-number.mdx' --- ## Boolean import ManualContent from '../../../schema/types/scalars/boolean.mdx' ## Example ```graphql query SearchERC721Contracts{ contracts(where: { isERC721: true }) { entries { name } } } ``` --- ## Cursor import GeneratedContent from '../../../schema/types/scalars/cursor.mdx' --- ## Gas import GeneratedContent from '../../../schema/types/scalars/gas.mdx' --- ## Int import ManualContent from '../../../schema/types/scalars/int.mdx' ## Example ```graphql query GetLatestBlocks{ blocks(first: 20, orderBy: NUMBER, orderDirection: DESC) { entries { number } } } ``` --- ## JSON import ManualContent from '../../../schema/types/scalars/json.mdx' ## Example ```graphql query GetRawMetadata{ metadata(contract: "0x76be3b62873462d2142405439777e971754e8e77", tokenId: "10521") { raw # Returns complete raw JSON metadata } } ``` --- ## String import ManualContent from '../../../schema/types/scalars/string.mdx' ## Example ```graphql query GetSenderAndRecipient{ transaction(hash: "0x8137130bac2f86462db8bee16d7b6ff75fecbfc282a8eaf5968518208ee8a4f3") { to from } } ``` :::tip `String` values are always enclosed in double quotes (`"`) when passed as arguments in queries. ::: --- ## Timestamp import GeneratedContent from '../../../schema/types/scalars/timestamp.mdx' --- ## TokenId import GeneratedContent from '../../../schema/types/scalars/token-id.mdx' --- ## TransactionHash import GeneratedContent from '../../../schema/types/scalars/transaction-hash.mdx' --- ## UInt256 import ManualContent from '../../../schema/types/scalars/uint-256.mdx' ## Example ```graphql query GetTransactionValue{ transaction(hash: "0x1d3e087034c3aaf93b4560f618f833f6cfa7257cb5373a475d42e71ec6fec26a") { value } } ``` --- ## UInt64 import ManualContent from '../../../schema/types/scalars/uint-64.mdx' ## Example ```graphql query GetBlockCountLast7Days{ blockCount(where: { timestampLte: 1761116745 }) } ``` --- ## Wei import GeneratedContent from '../../../schema/types/scalars/wei.mdx' --- ## feat: Build OneSource Docs Skill # Build OneSource Docs Skill A Claude Code skill plugin that embeds OneSource API knowledge directly into Claude's context window — teaching it how to write correct GraphQL queries, understand schema relationships, and avoid common pitfalls. This is the "how" complement to the Mach10 skill's "do." ## Overview The OneSource ecosystem currently has a gap: the MCP docs server (`onesource-docs` in `.mcp.json`) delivers raw documentation via `fetch_docs`, but no skill tells Claude *what* to fetch or *how* to interpret it. The Docs Skill closes this gap by providing structured API knowledge in-context — authentication patterns, all 12 root GraphQL queries, schema concepts, deprecated query tombstones, and integration examples. **Test data supports this:** readability testing shows docs-in-context improves AI accuracy from 89% baseline to 96%. The skill targets >= 96% on the existing 55-question test suite. ``` llms.txt / llms-full.txt <-- Source content (public docs, AI-optimized) | v .mcp.json (onesource-docs) <-- Phase 1: mcpdoc serves llms.txt via MCP | v Docs Skill (skill.md) <-- THIS: teaches Claude what/how to query | v Mach10 Skill + MCP Server <-- Execution: runs queries via mach10_graphql ``` ## Problem Statement 1. **No structured guidance for Claude.** The MCP docs server serves raw `llms.txt` content, but Claude must figure out correct query patterns on its own. This leads to errors (e.g., the universal Query 2.1 failure where all models generate `nft()` instead of the correct query shape). 2. **Discovery bottleneck.** GPT-4o has only 20% discovery rate for `llms.txt` — models that can't find docs get 89% accuracy vs 96%+ with docs in context. A skill eliminates the discovery problem entirely. 3. **Dual-API confusion potential.** The public federation API (`api.onesource.io`, `BP-` keys) and internal SRE-Dash API (`api-sre-dash.onesource.io`, `sk_` keys) use different schemas. Without clear boundaries, Claude may mix query patterns across APIs. ## Proposed Solution Build a **skill-only plugin** at `onesource-claude/plugins/onesource-docs/` that: - Embeds the public OneSource API schema knowledge in Claude's context - Uses modular structure (skill.md + sub-documents) following the Mach10 skill pattern - Includes explicit API boundary statements to prevent cross-API confusion - Contains deprecated query tombstones to override Claude's training-data biases - References the existing `onesource-docs` MCP server for deeper lookups when needed - Is self-sufficient — works without the MCP server configured ## Technical Approach ### Architecture ``` onesource-claude/plugins/onesource-docs/ plugin.json # Plugin manifest (skill-only, no server) README.md # Installation and usage guide skills/ onesource-docs/ skill.md # Main entry point (~3K tokens, core patterns) docs/ api-reference.md # Complete query reference (all 12 root queries) schema-concepts.md # Pagination, filtering, ordering, field selection authentication.md # Endpoints, API keys, chains, rate limits examples/ basic-queries.md # One example per root query integration-patterns.md # JS fetch, Axios, Python, Apollo Client, curl multi-step-workflows.md # Dashboard building, wallet analysis troubleshooting/ common-errors.md # 401, null responses, wrong query shapes deprecated-queries.md # Tombstones: queries that no longer exist ``` ### Content Strategy **Source material:** `static/llms-full.txt` (public API, old schema) — NOT the Mach10 skill (internal API, new schema). **Token budget:** ~15K tokens total across all files: - `skill.md`: ~3K tokens (trigger conditions, quick reference, key patterns, links to sub-docs) - `docs/`: ~6K tokens (schema reference, auth, concepts) - `examples/`: ~4K tokens (query examples, integration code) - `troubleshooting/`: ~2K tokens (errors, tombstones) This is about half the Mach10 skill's footprint (~30K), leaving room for both skills to coexist in context. ### Implementation Phases #### Phase 1: Schema Verification & Content Prep (Pre-requisite) Verify which schema the public federation API actually uses before writing any skill content. - [ ] Query the public API introspection endpoint to confirm schema shape ```bash curl -X POST "https://api.onesource.io/federation/ethereum/graphql" \ -H "Content-Type: application/json" \ -H "x-bp-token: BP-{key}" \ -d '{"query": "{ __schema { queryType { fields { name } } } }"}' ``` - [ ] Document which queries exist on the public API (old schema's 12 queries vs Mach10's 12) - [ ] Confirm pagination pattern (`items` vs `entries`, `edges/node` vs flat `entries`) - [ ] Confirm filter pattern (`filter:` vs `where:`, flat suffixes vs nested ranges) - [ ] Clean `llms-full.txt` of Docusaurus JSX artifacts (`import React`, ``, `:::tip`) - [ ] Run the existing 55-question test suite with cleaned `llms-full.txt` as context to establish the actual baseline for the public API schema **Files involved:** - `static/llms-full.txt` — source content to audit - `scripts/ai-readability-test/data/test-queries.json` — validators to check against confirmed schema **Blocking:** All subsequent phases depend on this. If the public API has migrated to the new schema, the test suite validators must be updated before proceeding. #### Phase 2: Plugin Scaffold & skill.md Core (Foundation) Create the plugin directory structure and write the main `skill.md`. - [ ] Create `onesource-ai-tools/onesource-claude/plugins/onesource-docs/` directory tree - [ ] Write `plugin.json` manifest ```json { "name": "onesource-docs", "version": "1.0.0", "description": "OneSource API documentation skill — teaches Claude how to write correct GraphQL queries against the public OneSource API", "author": { "name": "OneSource Team" }, "skills": [ { "name": "onesource-docs", "description": "Guide for understanding and querying the OneSource Web3 GraphQL API. Use when writing queries against api.onesource.io, understanding schema types, or troubleshooting API issues. NOT for mach10_graphql tool — use the Mach10 skill for that.", "path": "skills/onesource-docs/skill.md" } ], "commands": [], "mcpServers": [] } ``` - [ ] Write `skill.md` with these sections: 1. **Title + API Boundary Statement** — "This skill covers the public OneSource API at `api.onesource.io/federation/{chain}/graphql` with `x-bp-token: BP-...` authentication. For the internal SRE-Dash API via `mach10_graphql`, use the Mach10 skill instead." 2. **When to Use This Skill** — trigger conditions (user mentions OneSource, `api.onesource.io`, `BP-` keys, GraphQL queries for blockchain data) 3. **Conflict Resolution** — "If the user is working with `mach10_graphql`, defer to the Mach10 skill. If the user mentions `x-bp-token`, `BP-` keys, `api.onesource.io`, or the federation endpoint, use this skill." 4. **Quick Reference Table** — all 12 root queries with one-line descriptions 5. **Authentication Quick Start** — endpoint format, header, key format, example curl 6. **Key Schema Concepts Summary** — pagination, filtering, ordering (brief, with links to sub-docs) 7. **Common Pitfalls** — top 3-5 errors (NFT query confusion, wrong timestamp format, etc.) 8. **MCP Server Integration** — how to use `fetch_docs` for deeper lookups (if configured) 9. **Links to Sub-Documents** — organized by docs/, examples/, troubleshooting/ - [ ] Write `README.md` with installation instructions, prerequisites, and relationship to Mach10 plugin **Files to create:** - `plugins/onesource-docs/plugin.json` - `plugins/onesource-docs/README.md` - `plugins/onesource-docs/skills/onesource-docs/skill.md` #### Phase 3: Reference Documentation (Core Content) Write the sub-documents under `docs/`. - [ ] `docs/api-reference.md` — All 12 root queries with: - Query signature (arguments, types) - Available fields on the return type - Filter input type (with all fields) - Ordering enum values - One minimal example query with variables - Expected response shape (abbreviated) - [ ] `docs/schema-concepts.md` — Cross-cutting patterns: - Cursor-based pagination (`first`, `after`, `pageInfo`, `totalCount`) - Nested range filters (timestamp, blockNumber, value ranges) - Field selection and nested types (TokenAmount, Address interfaces, etc.) - Enum reference (OrderDirection, TransactionStatus, AssetType/Standard) - Response envelope structure - [ ] `docs/authentication.md` — Everything about connecting: - Endpoint URL pattern: `https://api.onesource.io/federation/{chain}/graphql` - Supported chains: ethereum, base, optimism, avax, sepolia, shape - API key format: `BP-{uuid}` in `x-bp-token` header - Subscription plans (Free, Starter, Pro) — rate limits and features - CORS and browser usage notes **Files to create:** - `plugins/onesource-docs/skills/onesource-docs/docs/api-reference.md` - `plugins/onesource-docs/skills/onesource-docs/docs/schema-concepts.md` - `plugins/onesource-docs/skills/onesource-docs/docs/authentication.md` #### Phase 4: Examples & Integration Patterns Write the sub-documents under `examples/`. - [ ] `examples/basic-queries.md` — One complete example per root query: - GraphQL query string - Variables object - Expected response (abbreviated) - Notes on gotchas for that specific query - [ ] `examples/integration-patterns.md` — Working code in 4 formats: - JavaScript `fetch()` — basic POST with headers - JavaScript Axios — same query via Axios - Python `requests` — same query in Python - Apollo Client — setup + query execution - `curl` — command-line example - Pagination loop pattern (JS and Python) - [ ] `examples/multi-step-workflows.md` — Real-world patterns: - "Analyze a wallet" — get address → get balances → get recent transactions - "Track whale movements" — filter high-value transactions → resolve addresses - "Token dashboard" — get token details → top holders → recent transfers - "NFT collection overview" — get contract → list NFTs → metadata + media **Files to create:** - `plugins/onesource-docs/skills/onesource-docs/examples/basic-queries.md` - `plugins/onesource-docs/skills/onesource-docs/examples/integration-patterns.md` - `plugins/onesource-docs/skills/onesource-docs/examples/multi-step-workflows.md` #### Phase 5: Troubleshooting & Tombstones Write the sub-documents under `troubleshooting/`. - [ ] `troubleshooting/common-errors.md` — Error patterns and fixes: - **401 Unauthorized** — wrong key format, missing header, expired key - **Null response** — querying wrong type (e.g., EOA address as contract) - **Empty results** — wrong chain, wrong filter syntax, case sensitivity - **Pagination issues** — using `skip` instead of cursor, missing `pageInfo` - **Field not found** — using new-schema field names on old-schema API (or vice versa) - **GraphQL error response structure** — `{"errors": [{"message": "...", "locations": [...]}]}` - [ ] `troubleshooting/deprecated-queries.md` — Tombstones with prominent warnings: - List every query/pattern that Claude's training data might suggest but that is wrong - For each: what Claude might generate, why it's wrong, what to use instead - **NFT query confusion** (first entry, most prominent) — explain the correct pattern with a callout - Old field names that were renamed - Old filter syntax patterns **Files to create:** - `plugins/onesource-docs/skills/onesource-docs/troubleshooting/common-errors.md` - `plugins/onesource-docs/skills/onesource-docs/troubleshooting/deprecated-queries.md` #### Phase 6: Registration & Validation Register the plugin and validate against the test suite. - [ ] Add `onesource-docs` entry to `onesource-claude/.claude-plugin/marketplace.json` - [ ] Run 55-question test suite with the Docs Skill content as context ```bash cd scripts/ai-readability-test npx tsx src/index.ts run --provider anthropic --mode readability --model claude-sonnet-4-20250514 npx tsx scripts/score-mcp-sim.ts --mode readability ``` - [ ] Check: accuracy >= 96% (211+ out of 220 points)? - [ ] If below target: identify failing questions, update skill content, re-run - [ ] Run MCP-live test to validate skill + MCP server combination ```bash npx tsx src/index.ts mcp-live -m deepseek-coder --quick ``` **Files to modify:** - `onesource-ai-tools/onesource-claude/.claude-plugin/marketplace.json` **Files to reference:** - `scripts/ai-readability-test/data/test-queries.json` — test validators - `scripts/ai-readability-test/results/reports/mcp-phase1-validation.md` — fill in TBD fields ## Alternative Approaches Considered ### 1. Monolithic skill.md (Rejected) Put all content in a single `skill.md` file like some simpler skills do. **Why rejected:** The Mach10 skill (889 lines, ~30K tokens) already proves that comprehensive API docs are too large for a single file. Modular structure allows Claude to load the core skill quickly and fetch details on demand. It also follows the established pattern. ### 2. Wrapper around Mach10 skill content (Rejected) Adapt the Mach10 skill's content for the public API endpoint. **Why rejected:** The Mach10 skill documents the NEW SRE-Dash schema, which differs significantly from the public API's schema. Copying and adapting would introduce errors at every divergence point. Content should be sourced from `llms-full.txt` (which documents the public API) instead. ### 3. MCP-only approach — no skill (Rejected) Rely entirely on the `onesource-docs` MCP server (`fetch_docs`) to deliver content on demand. **Why rejected:** Test data shows that docs-in-context (skill approach) achieves 96% accuracy vs lower rates when Claude must discover and interpret docs on its own. The skill provides structured, pre-interpreted knowledge that eliminates navigation overhead. ### 4. Combined Docs + Execution skill (Rejected) Merge the Docs Skill and Mach10 Skill into one plugin. **Why rejected:** They cover different APIs (public vs internal) with different schemas, auth, and endpoints. Combining them would increase context size (~45K+ tokens) and create confusion about which API a query targets. Clean separation with explicit boundary statements is safer. ## Acceptance Criteria ### Functional Requirements - [ ] Plugin installs correctly via marketplace (`/plugin install onesource-docs@onesource-claude`) - [ ] `skill.md` loads into Claude's context when relevant queries are detected - [ ] All 12 root GraphQL queries are documented with parameters, filters, and examples - [ ] Deprecated query tombstones prevent Claude from suggesting removed/incorrect queries - [ ] Authentication section covers endpoint format, API key format, and all 6 supported chains - [ ] Integration examples work in JavaScript (fetch, Axios, Apollo), Python (requests), and curl - [ ] Troubleshooting covers the top error patterns found in testing - [ ] Explicit API boundary statement prevents confusion with Mach10 skill ### Non-Functional Requirements - [ ] Total token footprint ≤ 15K tokens across all files - [ ] `skill.md` (entry point) ≤ 3K tokens — fast to load - [ ] Self-sufficient — works without the MCP docs server configured - [ ] All code examples are syntactically valid and copy-pasteable - [ ] No Docusaurus JSX artifacts in any skill content ### Quality Gates - [ ] 55-question test suite: >= 96% accuracy (211+ / 220 points) - [ ] No regressions on previously passing questions - [ ] Query 2.1 (NFT lookup) specifically addressed by tombstone/correction - [ ] Both Docs + Mach10 skills loaded simultaneously produce no conflicting guidance ## Success Metrics | Metric | Target | How to Measure | |--------|--------|----------------| | Test accuracy | >= 96% | Run readability test suite with skill as context | | Token footprint | ≤ 15K total | Count tokens across all skill files | | Query 2.1 fix | Pass | Previously universal failure, should now pass | | Install friction | Zero errors | Fresh install from marketplace on clean config | | Dual-skill safety | No conflicts | Load both skills, run 10 representative queries | ## Dependencies & Prerequisites ### Blocking Dependencies | Dependency | Status | Owner | Impact if Unresolved | |------------|--------|-------|----------------------| | Schema verification (public API) | **Not started** | Dev team | Cannot write accurate query examples | | Clean `llms-full.txt` | **Not started** | Docs team | JSX artifacts will pollute skill content | | Test suite alignment check | **Not started** | QA | May need validator updates for new schema | ### Non-Blocking Dependencies | Dependency | Status | Impact | |------------|--------|--------| | MCP docs server configured | **Done** (`.mcp.json`) | Skill works without it, but enhanced with it | | Marketplace registration | **Not started** | Needed for install, not for development | | Phase 2 MCP tools | **Not started** | Forward-compat notes only, not required | ## Risk Analysis & Mitigation ### Risk 1: Schema Mismatch (High Impact, Medium Probability) **Risk:** The public API has migrated to the new schema but `llms-full.txt` hasn't been updated, causing the skill to teach outdated patterns. **Mitigation:** Phase 1 introspection query confirms the actual schema before any content is written. If the public API has migrated, update `llms-full.txt` first, then write the skill. ### Risk 2: Test Suite / Skill Misalignment (High Impact, Medium Probability) **Risk:** The 55-question test validators expect old-schema answers but the skill teaches new-schema patterns (or vice versa), making 96% target unreachable. **Mitigation:** Phase 1 includes running the test suite against confirmed schema to identify mismatches. Update validators if needed before Phase 6 validation. ### Risk 3: Dual-Skill Conflict (Medium Impact, High Probability) **Risk:** Users with both Docs and Mach10 skills get mixed API guidance. **Mitigation:** Explicit API boundary statement in `skill.md` line 1. Conflict resolution rules in Section 3 of skill.md. Plugin description in `plugin.json` explicitly says "NOT for mach10_graphql." ### Risk 4: Context Window Pressure (Medium Impact, Medium Probability) **Risk:** Both skills loaded = ~45K tokens consumed before conversation starts. **Mitigation:** Target 15K token budget (half of Mach10). Keep `skill.md` compact (~3K) with links to sub-docs. Claude loads sub-docs only when needed. ### Risk 5: NFT Query Confusion Persists (Low Impact, High Probability) **Risk:** Even with tombstones, Claude reverts to training-data patterns for NFT queries. **Mitigation:** Make the NFT tombstone the FIRST entry in `deprecated-queries.md`, use a prominent callout format, and include it in `skill.md` Common Pitfalls section (double coverage). ## Future Considerations ### Phase 2 MCP Tools (Forward Compatibility) The skill should include a brief "Future Tools" section noting that these tools are planned but NOT yet available: | Tool | Purpose | Status | |------|---------|--------| | `search_docs` | Semantic search across all documentation | Planned | | `get_query_reference` | Full reference for a specific GraphQL query | Planned | | `get_type_definition` | Schema definition for a GraphQL type | Planned | | `list_examples` | Working examples filtered by topic | Planned | Frame as: "When these tools become available, use them for detailed lookups instead of `fetch_docs`. Do not attempt to call them until they are registered as available MCP tools." ### Distribution Channels Once validated, the skill should be published to: - NPM as `@onesource/docs-skill` (planned) - ClawHub agent marketplace (planned) - Skill.sh skill registry (planned) ### Content Sync Pipeline Long-term, the skill content should auto-generate from `llms-full.txt` so docs updates propagate to the skill without manual editing. This is out of scope for v1. ## Documentation Plan - [ ] `README.md` in plugin root — installation, prerequisites, usage, relationship to other plugins - [ ] Update `ONESOURCE-AI-TOOLS-CATALOG.md` — change status from "~0% (planned)" to "~100% (complete)" and fill in details - [ ] Update `memory/mcp-phase1.md` — mark "Build a skill" as complete - [ ] Fill in `scripts/ai-readability-test/results/reports/mcp-phase1-validation.md` TBD fields with test results ## References & Research ### Internal References - Mach10 skill (reference implementation): `onesource-ai-tools/onesource-claude/plugins/mcp-mach10/skills/mach10/skill.md` - Mach10 plugin.json (manifest pattern): `onesource-ai-tools/onesource-claude/plugins/mcp-mach10/plugin.json` - Plugin conventions: `onesource-ai-tools/onesource-claude/CLAUDE.md` - Marketplace registry: `onesource-ai-tools/onesource-claude/.claude-plugin/marketplace.json` - MCP server config: `.mcp.json` (project root) - Source content: `static/llms-full.txt` - Test suite: `scripts/ai-readability-test/data/test-queries.json` - Test report: `scripts/ai-readability-test/LLM-DISCOVERABILITY-REPORT.md` - Catalog: `ONESOURCE-AI-TOOLS-CATALOG.md` (lines 290-467) - MCP phase 1 notes: `memory/mcp-phase1.md` ### Key Findings from Research 1. **Readability improvement:** 89% baseline → 96% with docs in context (from LLM discoverability report) 2. **Universal failure:** Query 2.1 (NFT lookup) fails in every model — needs explicit tombstone 3. **Biggest test gains:** Integration Patterns (+16%), Troubleshooting (+14%), Core API Knowledge (+14%) 4. **Token efficiency:** Skill-in-context eliminates discovery overhead (no web fetches needed) 5. **Thin connector pattern:** Keep server minimal, put all knowledge in the skill (from CLAUDE.md best practices) 6. **Schema divergence:** Public API (`llms-full.txt`) uses different schema than internal API (Mach10 skill) — content must be sourced from `llms-full.txt` ### Critical Questions (to resolve in Phase 1) 1. **Which schema does the public API currently use?** — Introspection query will confirm 2. **Do test validators need updating?** — Depends on schema answer 3. **How stale is `llms-full.txt`?** — Compare against live API introspection ## File Summary ### Files to Create (11) | File | Purpose | Token Budget | |------|---------|-------------| | `plugins/onesource-docs/plugin.json` | Plugin manifest | ~100 | | `plugins/onesource-docs/README.md` | Installation guide | ~500 | | `plugins/onesource-docs/skills/onesource-docs/skill.md` | Main skill entry point | ~3,000 | | `plugins/onesource-docs/skills/onesource-docs/docs/api-reference.md` | All 12 queries | ~3,000 | | `plugins/onesource-docs/skills/onesource-docs/docs/schema-concepts.md` | Pagination, filters, ordering | ~2,000 | | `plugins/onesource-docs/skills/onesource-docs/docs/authentication.md` | Endpoints, keys, chains | ~1,000 | | `plugins/onesource-docs/skills/onesource-docs/examples/basic-queries.md` | One example per query | ~2,000 | | `plugins/onesource-docs/skills/onesource-docs/examples/integration-patterns.md` | JS/Python/curl code | ~1,500 | | `plugins/onesource-docs/skills/onesource-docs/examples/multi-step-workflows.md` | Real-world patterns | ~1,000 | | `plugins/onesource-docs/skills/onesource-docs/troubleshooting/common-errors.md` | Error patterns | ~1,000 | | `plugins/onesource-docs/skills/onesource-docs/troubleshooting/deprecated-queries.md` | Tombstones | ~800 | ### Files to Modify (2) | File | Change | |------|--------| | `onesource-ai-tools/onesource-claude/.claude-plugin/marketplace.json` | Add onesource-docs plugin entry | | `ONESOURCE-AI-TOOLS-CATALOG.md` | Update status from planned to complete | --- ## MCP Phase 1 — Completion Report Branch `feat/mcp-phase1` is ready to merge into `develop`. This document captures everything built, every decision made, known issues discovered, and what comes next. --- ## What We Built ### 1. MCP Documentation Server A thin MCP server wrapping `llms-full.txt` via [mcpdoc](https://github.com/langchain-ai/mcpdoc). Spawns as a subprocess over stdio, exposing two tools to Claude Code: - `list_doc_sources` — returns the `llms-full.txt` URL - `fetch_docs(url)` — fetches and returns documentation content **Config** (`.mcp.json`): ```json { "onesource-docs": { "type": "stdio", "command": "uvx", "args": ["--from", "mcpdoc", "mcpdoc", "--urls", "OneSourceDocs:https://docs.onesource.io/llms-full.txt", "--follow-redirects"] } } ``` ### 2. Claude Code Docs Skill Plugin A complete skill plugin at `onesource-ai-tools/onesource-claude/plugins/onesource-docs/` with 11 content files (~15K tokens total): | File | Purpose | ~Tokens | |------|---------|---------| | `SKILL.md` | Entry point — quick reference, auth, common pitfalls | 3,000 | | `docs/api-reference.md` | All 12 root queries with filters and ordering | 3,000 | | `docs/schema-concepts.md` | Pagination, polymorphism, filters, enums | 2,000 | | `docs/authentication.md` | Endpoints, API keys, 6 supported chains | 1,000 | | `examples/basic-queries.md` | One complete example per root query | 2,000 | | `examples/integration-patterns.md` | JS (fetch, Axios, Apollo), Python, curl | 1,500 | | `examples/multi-step-workflows.md` | Wallet analysis, whale tracking, NFT collections | 1,000 | | `troubleshooting/common-errors.md` | 401, nulls, wrong query shapes, pagination | 1,000 | | `troubleshooting/deprecated-queries.md` | Tombstones — NFT confusion (prominent), old filters, removed queries | 800 | ### 3. LLM Discoverability Infrastructure **`docusaurus-plugin-llms`** added to the Docusaurus build: - Generates `llms.txt` (index with links) and `llms-full.txt` (full content, ~330K chars) - Config: `pathTransformation: { ignorePaths: ["docs"] }` strips `/docs/` prefix for correct URLs **LLM meta tags** added to `docusaurus.config.js`: - ``, `llms:description`, `llms:instructions` - `