From 83785653294f587248c2c818c805cd78290620a3 Mon Sep 17 00:00:00 2001 From: keenbeen32 Date: Tue, 30 Dec 2025 14:11:15 +0200 Subject: [PATCH 1/5] feat: added query conversion page --- CONVERSION_RULES.md | 624 ++++++++++++++++++ docs/HyperIndex/migration-guide.md | 2 +- docs/HyperIndex/query-conversion.md | 317 +++++++++ docs/HyperIndex/supported-networks/hoodi.md | 63 ++ .../supported-networks/injective.md | 63 ++ docs/HyperRPC/hyperrpc-supported-networks.md | 4 + .../HyperSync/hypersync-supported-networks.md | 4 + sidebarsHyperIndex.js | 2 +- supported-networks.json | 2 + 9 files changed, 1079 insertions(+), 2 deletions(-) create mode 100644 CONVERSION_RULES.md create mode 100644 docs/HyperIndex/query-conversion.md create mode 100644 docs/HyperIndex/supported-networks/hoodi.md create mode 100644 docs/HyperIndex/supported-networks/injective.md diff --git a/CONVERSION_RULES.md b/CONVERSION_RULES.md new file mode 100644 index 00000000..7f09df58 --- /dev/null +++ b/CONVERSION_RULES.md @@ -0,0 +1,624 @@ +# Complete Conversion Rules: The Graph → Envio/Hyperindex + +This document lists all conversion rules implemented in the subgraph-to-hyperindex query converter. Each rule includes both the rule statement and a concrete example. + +The rules are organized into two sections: +- **Direct Changes**: Simple, straightforward mappings (e.g., `first` → `limit`, `orderBy` → `order_by`) +- **Advanced/Nuanced Differences**: More complex conversions that require schema introspection or special handling + +--- + +## Part I: Direct Changes + +These are simple, direct mappings that can be applied without schema introspection or complex logic. + +### 1. Entity Name Conversion + +#### Rule 1.1: Entity Naming Convention +**Rule**: The Graph uses pluralized entity names (e.g., `streams`, `users`, `orders`), while Envio/Hyperindex uses the entity name as-is from the schema (singular, PascalCase). The converter automatically singularizes and capitalizes The Graph's plural entity names to match Envio's schema. + +**Example**: +```graphql +# The Graph +query { + streams(first: 10) { id } + users(first: 5) { id } + lpActions(first: 100) { id } + orders(first: 20) { id } + user(id: "0x123") { totalVolume } +} + +# Envio/Hyperindex +query { + Stream(limit: 10) { id } + User(limit: 5) { id } + LpAction(limit: 100) { id } + Order(limit: 20) { id } + User_by_pk(id: "0x123") { totalVolume } +} +``` + +### 2. Pagination Parameters + +#### Rule 2.1: First → Limit +**Rule**: The `first` parameter is converted to `limit`. + +**Example**: +```graphql +# The Graph +query { + streams(first: 10) { id } +} + +# Envio/Hyperindex +query { + Stream(limit: 10) { id } +} +``` + +#### Rule 2.2: Skip → Offset +**Rule**: The `skip` parameter is converted to `offset`. + +**Example**: +```graphql +# The Graph +query { + streams(first: 10, skip: 20) { id } +} + +# Envio/Hyperindex +query { + Stream(limit: 10, offset: 20) { id } +} +``` + +#### Rule 2.3: Pagination Variables Preserved +**Rule**: GraphQL variables used for pagination are preserved in the converted query. + +**Example**: +```graphql +# The Graph +query GetStreams($first: Int!, $skip: Int!) { + streams(first: $first, skip: $skip) { id } +} + +# Envio/Hyperindex +query GetStreams($first: Int!, $skip: Int!) { + Stream(limit: $first, offset: $skip) { id } +} +``` + +### 3. Ordering Parameters + +#### Rule 3.1: OrderBy and OrderDirection → Order_by +**Rule**: The `orderBy` and `orderDirection` parameters are combined into a single `order_by: {field: direction}` clause. + +**Example**: +```graphql +# The Graph +query { + streams(orderBy: name, orderDirection: desc) { id name } +} + +# Envio/Hyperindex +query { + Stream(order_by: {name: desc}) { id name } +} +``` + +#### Rule 3.2: Ascending Order +**Rule**: `orderDirection: asc` is converted to `order_by: {field: asc}`. + +**Example**: +```graphql +# The Graph +query { + streams(orderBy: timestamp, orderDirection: asc) { id timestamp } +} + +# Envio/Hyperindex +query { + Stream(order_by: {timestamp: asc}) { id timestamp } +} +``` + +### 4. Filter Operators + +#### Rule 4.1: Equality Filter +**Rule**: Simple equality filters are converted to `field: {_eq: value}` format. + +**Example**: +```graphql +# The Graph +query { + streams(name: "test") { id name } +} + +# Envio/Hyperindex +query { + Stream(where: {name: {_eq: "test"}}) { id name } +} +``` + +#### Rule 4.2: Comparison Filters +**Rule**: Comparison filters (`_not`, `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_not_in`) are converted to their Hasura equivalents. + +**Examples**: +```graphql +# The Graph +query { + streams(id_not: "0x123") { id } + streams(amount_gt: 100) { id amount } + streams(amount_gte: 100) { id amount } + streams(timestamp_lt: 1650000000) { id timestamp } + streams(timestamp_lte: 1650000000) { id timestamp } + streams(id_in: ["1", "2", "3"]) { id name } + streams(id_not_in: ["1", "2", "3"]) { id name } +} + +# Envio/Hyperindex +query { + Stream(where: {id: {_neq: "0x123"}}) { id } + Stream(where: {amount: {_gt: 100}}) { id amount } + Stream(where: {amount: {_gte: 100}}) { id amount } + Stream(where: {timestamp: {_lt: 1650000000}}) { id timestamp } + Stream(where: {timestamp: {_lte: 1650000000}}) { id timestamp } + Stream(where: {id: {_in: ["1", "2", "3"]}}) { id name } + Stream(where: {id: {_nin: ["1", "2", "3"]}}) { id name } +} +``` + +#### Rule 4.3: String Filters +**Rule**: String filters (`_contains`, `_starts_with`, `_ends_with`, and their `_not` and `_nocase` variants) are converted to `_ilike` with appropriate wildcards. + +**Examples**: +```graphql +# The Graph +query { + streams(name_contains: "graph") { id name } + streams(name_not_contains: "graph") { id name } + streams(symbol_starts_with: "ETH") { id symbol } + streams(symbol_ends_with: "USD") { id symbol } + streams(name_not_starts_with: "A") { id name } + streams(name_not_ends_with: "x") { id name } + streams(name_contains_nocase: "alice") { id name } + streams(name_starts_with_nocase: "test") { id name } + streams(name_ends_with_nocase: "def") { id name } +} + +# Envio/Hyperindex +query { + Stream(where: {name: {_ilike: "%graph%"}}) { id name } + Stream(where: {_not: {name: {_ilike: "%graph%"}}}) { id name } + Stream(where: {symbol: {_ilike: "ETH%"}}) { id symbol } + Stream(where: {symbol: {_ilike: "%USD"}}) { id symbol } + Stream(where: {_not: {name: {_ilike: "A%"}}}) { id name } + Stream(where: {_not: {name: {_ilike: "%x"}}}) { id name } + Stream(where: {name: {_ilike: "%alice%"}}) { id name } + Stream(where: {name: {_ilike: "test%"}}) { id name } + Stream(where: {name: {_ilike: "%def"}}) { id name } +} +``` + +#### Rule 4.4: Unsupported Filters +**Rule**: `field_containsAny` and `field_containsAll` filters are not supported and will return an error. + +**Example**: +```graphql +# The Graph +query { + streams(tags_containsAny: ["tag1", "tag2"]) { id } +} +# ❌ Returns ConversionError::UnsupportedFilter("tags_containsAny") +``` + +### 5. Variable Type Conversions + +#### Rule 5.1: ID → String +**Rule**: Variable types `ID` and `ID!` are converted to `String` and `String!` respectively. + +**Example**: +```graphql +# The Graph +query getUserVolume($id: ID!) { + user(id: $id) { totalVolume } +} + +# Envio/Hyperindex +query getUserVolume($id: String!) { + User_by_pk(id: $id) { totalVolume } +} +``` + +#### Rule 5.2: Bytes → String +**Rule**: Variable types `Bytes` and `Bytes!` are converted to `String` and `String!` respectively. + +**Example**: +```graphql +# The Graph +query getPoolActions($id: Bytes) { + lpActions(where: { user: $id }) { user timestamp } +} + +# Envio/Hyperindex +query getPoolActions($id: String) { + LpAction(where: {user: {id: {_eq: $id}}}) { user timestamp } +} +``` + +#### Rule 5.3: BigInt → numeric +**Rule**: Variable types `BigInt` and `BigInt!` are converted to `numeric` and `numeric!` respectively. + +**Example**: +```graphql +# The Graph +query Limits($minLeverage: BigInt) { + limits(where: { leverage_gte: $minLeverage }) { id leverage } +} + +# Envio/Hyperindex +query Limits($minLeverage: numeric) { + Limit(where: {leverage: {_gte: $minLeverage}}) { id leverage } +} +``` + +#### Rule 5.4: BigDecimal → numeric +**Rule**: Variable types `BigDecimal` and `BigDecimal!` are converted to `numeric` and `numeric!` respectively. + +**Example**: +```graphql +# The Graph +query GetPrice($price: BigDecimal!) { + pairs(where: { price: $price }) { id } +} + +# Envio/Hyperindex +query GetPrice($price: numeric!) { + Pair(where: {price: {_eq: $price}}) { id } +} +``` + +#### Rule 5.5: Array Types +**Rule**: Array types for `ID`, `Bytes`, and `BigInt` are converted to their corresponding array types (`[String!]`, `[numeric!]`, etc.). + +**Examples**: +```graphql +# The Graph +query ReferredUsers($addresses: [ID!]) { + users(where: { id_in: $addresses }) { id totalVolume } +} + +query GetUserLpShares($addresses: [Bytes!]) { + lpShares(where: { id_in: $addresses }) { id shares } +} + +query GetAmounts($amounts: [BigInt!]!) { + streams(where: { amount_in: $amounts }) { id amount } +} + +# Envio/Hyperindex +query ReferredUsers($addresses: [String!]) { + User(where: {id: {_in: $addresses}}) { id totalVolume } +} + +query GetUserLpShares($addresses: [String!]) { + LpShare(where: {id: {_in: $addresses}}) { id shares } +} + +query GetAmounts($amounts: [numeric!]!) { + Stream(where: {amount: {_in: $amounts}}) { id amount } +} +``` + +--- + +## Part II: Advanced/Nuanced Differences + +These conversions require schema introspection, special handling, or more complex logic. + +### 6. Nested Entity References + +#### Rule 6.1: Simple Scalar Reference +**Rule**: When a nested entity field is referenced with a simple scalar value (string/number or variable), it's converted to `field: {id: {_eq: value}}`. This requires schema introspection to determine if a field is a nested entity. + +**Example**: +```graphql +# The Graph +query { + orders(where: { pair: "0" }) { id } + trades(where: { trader: "0x123" }) { id } +} + +# Envio/Hyperindex +query { + Order(where: {pair: {id: {_eq: "0"}}}) { id } + Trade(where: {trader: {id: {_eq: "0x123"}}}) { id } +} +``` + +#### Rule 6.2: Nested Entity with Variable +**Rule**: Variables used to reference nested entities are also converted to the `{id: {_eq: $variable}}` format. Schema introspection determines if the field is a nested entity. + +**Example**: +```graphql +# The Graph +query GetTrades($pairId: String!) { + trades(where: { pair: $pairId }) { id } +} + +# Envio/Hyperindex +query GetTrades($pairId: String!) { + Trade(where: {pair: {id: {_eq: $pairId}}}) { id } +} +``` + +#### Rule 6.3: Nested Filters (Dot Notation - Single Level) +**Rule**: Filters on nested entities using dot notation are converted to nested where clauses. Schema introspection is used to determine nested entity relationships. + +**Example**: +```graphql +# The Graph +query { + orders(where: { user.name_contains: "john" }) { id } +} + +# Envio/Hyperindex +query { + Order(where: {user: {name: {_ilike: "%john%"}}}) { id } +} +``` + +#### Rule 6.4: Nested Filters (Dot Notation - Multiple Levels) +**Rule**: Filters on deeply nested entities are recursively converted to nested where clauses. Schema introspection is used to traverse the nested entity hierarchy. + +**Example**: +```graphql +# The Graph +query { + orders(where: { pair.token.amount_gt: 100 }) { id } + orders(where: { pair.group.name: "test" }) { id } +} + +# Envio/Hyperindex +query { + Order(where: {pair: {token: {amount: {_gt: 100}}}}) { id } + Order(where: {pair: {group: {name: {_eq: "test"}}}}) { id } +} +``` + +#### Rule 6.5: Multiple Conditions on Same Nested Field +**Rule**: Multiple conditions on the same nested field are wrapped in `_and`. Schema introspection ensures proper nesting. + +**Example**: +```graphql +# The Graph +query { + orders(where: { + user.name_contains: "john", + user.name_not: "doe" + }) { id } +} + +# Envio/Hyperindex +query { + Order(where: { + user: { + _and: [ + {name: {_ilike: "%john%"}}, + {name: {_neq: "doe"}} + ] + } + }) { id } +} +``` + +### 7. Chain ID Handling + +#### Rule 7.1: Default Endpoints (No Chain ID) +**Rule**: Default endpoints (`/` and `/debug`) do not add a `chainId` filter automatically. + +**Example**: +```graphql +# The Graph +query { + streams(first: 10) { id } +} + +# Envio/Hyperindex (via / endpoint) +query { + Stream(limit: 10) { id } +} +# No chainId filter added +``` + +#### Rule 7.2: Chain-Specific Endpoint (Collection Queries) +**Rule**: The `/chainId/{chain_id}` endpoint automatically adds `where: {chainId: {_eq: "{chain_id}"}}` to collection queries. + +**Example**: +```graphql +# The Graph +query { + streams(first: 10) { id } +} + +# Envio/Hyperindex (via /chainId/5 endpoint) +query { + Stream(limit: 10, where: {chainId: {_eq: "5"}}) { id } +} +``` + +#### Rule 7.3: Chain-Specific Endpoint (Primary Key Queries) +**Rule**: Single entity by primary key queries (`EntityName_by_pk`) do NOT get a `chainId` filter, even when using the `/chainId/{chain_id}` endpoint. + +**Example**: +```graphql +# The Graph +query { + user(id: "0x123") { totalVolume } +} + +# Envio/Hyperindex (via /chainId/1 endpoint) +query { + User_by_pk(id: "0x123") { totalVolume } +} +# No chainId filter added for _by_pk queries +``` + +### 8. Special Query Types + +#### Rule 8.1: Introspection Queries +**Rule**: Queries with operation name exactly matching `IntrospectionQuery` are passed through unchanged. + +**Example**: +```graphql +# The Graph +query IntrospectionQuery { + __schema { + types { name } + } +} + +# Envio/Hyperindex +query IntrospectionQuery { + __schema { + types { name } + } +} +# Passed through unchanged +``` + +**Note**: Only exact match on operation name. Queries containing `__schema` or `__type` but with different operation names are still converted. + +### 9. Fragments + +#### Rule 9.1: Fragment Preservation +**Rule**: GraphQL fragments are extracted, sanitized (arguments removed), and passed through unchanged in the converted query. + +**Example**: +```graphql +# The Graph +fragment VaultFields on Vault { + id + shares + sharePrice +} + +query getProtocolData { + vault(id: "usdc-vault") { + ...VaultFields + __typename + } +} + +# Envio/Hyperindex +fragment VaultFields on Vault { + id + shares + sharePrice +} + +query getProtocolData { + Vault_by_pk(id: "usdc-vault") { + ...VaultFields + __typename + } +} +# Fragment preserved, entity name converted +``` + +### 10. Multiple Entities in Single Query + +#### Rule 10.1: Independent Entity Conversion +**Rule**: Multiple entities in a single query are each converted independently. + +**Example**: +```graphql +# The Graph +query { + users(first: 10) { id } + orders(first: 5) { id } + trades(first: 3) { id } +} + +# Envio/Hyperindex +query { + User(limit: 10) { id } + Order(limit: 5) { id } + Trade(limit: 3) { id } +} +``` + +#### Rule 10.2: Mixed Query Types +**Rule**: A single query can contain both collection queries and primary key queries, each converted appropriately. + +**Example**: +```graphql +# The Graph +query { + users(first: 10) { id } + user(id: "0x123") { totalVolume } + orders(first: 5) { id } +} + +# Envio/Hyperindex +query { + User(limit: 10) { id } + User_by_pk(id: "0x123") { totalVolume } + Order(limit: 5) { id } +} +``` + +### 11. Where Clause Variable + +#### Rule 11.1: Where Variable Pass-Through +**Rule**: If `where` is passed as a variable (e.g., `where: $where`), it's passed through directly without conversion. + +**Example**: +```graphql +# The Graph +query GetStreams($where: Stream_filter) { + streams(where: $where) { id } +} + +# Envio/Hyperindex +query GetStreams($where: Stream_filter) { + Stream(where: $where) { id } +} +# where clause passed through unchanged +``` + +### 12. OrderBy Variable Handling + +#### Rule 12.1: OrderBy Variable Handling +**Rule**: If `orderBy` is a variable (e.g., `$orderBy`), the `order_by` clause is ignored to keep the query valid. + +**Example**: +```graphql +# The Graph +query GetStreams($orderBy: String, $orderDirection: String) { + streams(orderBy: $orderBy, orderDirection: $orderDirection) { id } +} + +# Envio/Hyperindex +query GetStreams($orderBy: String, $orderDirection: String) { + Stream { id } + # order_by clause omitted because orderBy is a variable +} +``` + +--- + +## Summary Table + +| Category | The Graph | Envio/Hyperindex | Example | +|----------|-----------|------------------|---------| +| **Entity Names** | Plural camelCase | Singular PascalCase (as-is from schema) | `streams` → `Stream` | +| **Pagination** | `first`, `skip` | `limit`, `offset` | `first: 10, skip: 20` → `limit: 10, offset: 20` | +| **Ordering** | `orderBy`, `orderDirection` | `order_by: {field: direction}` | `orderBy: name, orderDirection: desc` → `order_by: {name: desc}` | +| **Equality Filter** | `field: value` | `field: {_eq: value}` | `name: "test"` → `name: {_eq: "test"}` | +| **Comparison Filters** | `field_gt`, `field_gte`, etc. | `field: {_gt: value}`, etc. | `amount_gt: 100` → `amount: {_gt: 100}` | +| **String Filters** | `_contains`, `_starts_with`, etc. | `_ilike` with `%` wildcards | `name_contains: "test"` → `name: {_ilike: "%test%"}` | +| **Variable Types** | `ID`, `Bytes`, `BigInt`, `BigDecimal` | `String`, `numeric` | `$id: ID!` → `$id: String!` | +| **Nested Entities** | `field: "id"` | `field: {id: {_eq: "id"}}` | `pair: "0"` → `pair: {id: {_eq: "0"}}` | +| **Nested Filters** | `field.subfield_operator: value` | `field: {subfield: {_operator: value}}` | `user.name_contains: "john"` → `user: {name: {_ilike: "%john%"}}` | +| **Chain ID** | Manual filter | Auto-added via `/chainId/{id}` endpoint | `/chainId/5` adds `chainId: {_eq: "5"}` | diff --git a/docs/HyperIndex/migration-guide.md b/docs/HyperIndex/migration-guide.md index 66e75557..833facf3 100644 --- a/docs/HyperIndex/migration-guide.md +++ b/docs/HyperIndex/migration-guide.md @@ -196,7 +196,7 @@ HyperIndex is a powerful tool that can be used to index any contract. There are - Use the `field_selection` option to add additional fields to your index. Doc here: [field selection](../HyperIndex/configuration-file#field-selection) - Use the `unordered_multichain_mode` option to enable unordered multichain mode, this is the most common need for multichain indexing. However comes with tradeoffs worth understanding. Doc here: [unordered multichain mode](../HyperIndex/configuration-file#unordered-multichain-mode) - Use wildcard indexing to index by event signatures rather than by contract address. -- HyperIndex uses the standard graphql query language, where as the subgraph uses a custom query language. You can read about the slight nuances [here](https://docs.sablier.com/api/caveats). (We are working on a basic tool to help with backwards compatibility, please check in with us on discord for it's current status). +- Query Conversion: HyperIndex uses the standard GraphQL query language, whereas The Graph uses a custom GraphQL syntax. While queries are very similar, there are important differences when querying your indexed data. See the [Query Conversion Guide](./query-conversion) for detailed conversion rules and examples. - Loaders are a powerful feature to optimize historical sync performance. You can read more about them [here](../HyperIndex/loaders). - HyperIndex is very flexible and can be used to index offchain data too or send messages to a queue etc for fetching external data, you can further optimise the fetching by using the [effects api](/docs/HyperIndex/effect-api) diff --git a/docs/HyperIndex/query-conversion.md b/docs/HyperIndex/query-conversion.md new file mode 100644 index 00000000..82783525 --- /dev/null +++ b/docs/HyperIndex/query-conversion.md @@ -0,0 +1,317 @@ +--- +id: query-conversion +title: Query Conversion Guide +sidebar_label: Query Conversion +slug: /query-conversion +description: Learn how to convert queries from TheGraph's custom GraphQL syntax to Envio's standard GraphQL syntax. +--- + +# Query Conversion + +Envio uses standard GraphQL query language, while TheGraph uses a custom GraphQL syntax. While the queries are very similar, there are some important differences to be aware of when migrating. + +This guide covers all the differences between TheGraph's query syntax and Envio's query syntax, with examples for each conversion rule. + +## Converter Tool + +We've built a [query converter tool](https://github.com/enviodev/subgraph-to-hyperindex-query-converter) that automatically converts TheGraph queries to Envio's GraphQL syntax. You can: + +- **Convert and execute**: Provide your Envio GraphQL endpoint and a query written in TheGraph syntax. The tool will convert it, execute it against your endpoint, and return the results +- **Convert only**: Use the tool to convert queries and view the converted output without executing them + +**Repository**: [subgraph-to-hyperindex-query-converter](https://github.com/enviodev/subgraph-to-hyperindex-query-converter) + +:::warning Beta Status +This converter tool is still very much in beta. We're actively working on it and discovering new query conversions that need to be handled. + +**If you encounter any conversion failures or incorrect conversions, please [file a GitHub issue](https://github.com/enviodev/subgraph-to-hyperindex-query-converter/issues) in the repository so we can address it.** +::: + +--- + +### 1. Entity Name Conversion + +**Rule**: TheGraph uses pluralized entity names (e.g., `pools`, `factories`, `tokens`), while Envio uses the entity name as-is from the schema (singular, PascalCase). When converting, plural entity names are automatically singularized and capitalized to match Envio's schema. + +**Example**: + +```graphql +# TheGraph +query { + pools { id } + factories { id } + tokens { id } +} + +# Envio +query { + Pool { id } + Factory { id } + Token { id } +} +``` + +Single entity queries use `EntityName_by_pk` (by primary key) in Envio. Alternatively, you could use a `where` clause with the primary key field: + +```graphql +# TheGraph +query { + pool(id: "0x123") { value } +} + +# Envio +query { + Pool_by_pk(id: "0x123") { value } +} +``` + +Or using a `where` clause: + +```graphql +# Envio +query { + Pool(where: {id: {_eq: "0x123"}}) { value } +} +``` + +### 2. Pagination Parameters + +#### First → Limit + +**Rule**: The `first` parameter is converted to `limit`. + +**Example**: + +```graphql +# TheGraph +query { + pools(first: 10) { id } +} + +# Envio +query { + Pool(limit: 10) { id } +} +``` + +#### Skip → Offset + +**Rule**: The `skip` parameter is converted to `offset`. + +**Example**: + +```graphql +# TheGraph +query { + pools(skip: 20) { id } +} + +# Envio +query { + Pool(offset: 20) { id } +} +``` + +### 3. Ordering Parameters + +#### OrderBy and OrderDirection → Order_by + +**Rule**: The `orderBy` and `orderDirection` parameters are combined into a single `order_by: {field: direction}` clause. + +**Example**: + +```graphql +# TheGraph +query { + pools(orderBy: name, orderDirection: desc) { id name } + # Use orderDirection: asc for ascending order +} + +# Envio +query { + Pool(order_by: {name: desc}) { id name } + # Use asc for ascending order, e.g., order_by: {name: asc} +} +``` + +### 4. Filter Operators + +#### Equality Filter + +**Rule**: Simple equality filters are converted to `where: {field: {_eq: value}}` format. + +**Example**: + +```graphql +# TheGraph +query { + pools(name: "test") { id name } +} + +# Envio +query { + Pool(where: {name: {_eq: "test"}}) { id name } +} +``` + +#### Comparison Filters + +**Rule**: Comparison filters (`_not`, `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_not_in`) are converted to their Hasura equivalents. + +**Examples**: + +```graphql +# TheGraph +query { + pools(id_not: "0x123") { id } + pools(amount_gt: 100) { id amount } + pools(amount_gte: 100) { id amount } + pools(timestamp_lt: 1650000000) { id timestamp } + pools(timestamp_lte: 1650000000) { id timestamp } + pools(id_in: ["1", "2", "3"]) { id name } + pools(id_not_in: ["1", "2", "3"]) { id name } +} + +# Envio +query { + Pool(where: {id: {_neq: "0x123"}}) { id } + Pool(where: {amount: {_gt: 100}}) { id amount } + Pool(where: {amount: {_gte: 100}}) { id amount } + Pool(where: {timestamp: {_lt: 1650000000}}) { id timestamp } + Pool(where: {timestamp: {_lte: 1650000000}}) { id timestamp } + Pool(where: {id: {_in: ["1", "2", "3"]}}) { id name } + Pool(where: {id: {_nin: ["1", "2", "3"]}}) { id name } +} +``` + +#### String Filters + +**Rule**: String filters (`_contains`, `_starts_with`, `_ends_with`, and their `_not` and `_nocase` variants) are converted to `_ilike` with appropriate wildcards. The `%` symbol represents any text at that position in the pattern. + +**Examples**: + +```graphql +# TheGraph +query { + pools(name_contains: "test") { id name } + pools(name_not_contains: "test") { id name } + pools(symbol_starts_with: "ABC") { id symbol } + pools(symbol_ends_with: "XYZ") { id symbol } + pools(name_not_starts_with: "A") { id name } + pools(name_not_ends_with: "x") { id name } + pools(name_contains_nocase: "test") { id name } + pools(name_starts_with_nocase: "test") { id name } + pools(name_ends_with_nocase: "test") { id name } +} + +# Envio +query { + Pool(where: {name: {_ilike: "%test%"}}) { id name } + Pool(where: {_not: {name: {_ilike: "%test%"}}}) { id name } + Pool(where: {symbol: {_ilike: "ABC%"}}) { id symbol } + Pool(where: {symbol: {_ilike: "%XYZ"}}) { id symbol } + Pool(where: {_not: {name: {_ilike: "A%"}}}) { id name } + Pool(where: {_not: {name: {_ilike: "%x"}}}) { id name } + Pool(where: {name: {_ilike: "%test%"}}) { id name } + Pool(where: {name: {_ilike: "test%"}}) { id name } + Pool(where: {name: {_ilike: "%test"}}) { id name } +} +``` + +### 5. Variable Type Conversions + +#### ID → String + +**Rule**: Variable types `ID` and `ID!` are converted to `String` and `String!` respectively. + +**Example**: + +```graphql +# TheGraph +query getPoolValue($id: ID!) { + pool(id: $id) { value } +} + +# Envio +query getPoolValue($id: String!) { + Pool_by_pk(id: $id) { value } +} +``` + +#### Bytes → String + +**Rule**: Variable types `Bytes` and `Bytes!` are converted to `String` and `String!` respectively. + +**Example**: + +```graphql +# TheGraph +query getTokens($id: Bytes) { + tokens(where: { id: $id }) { id timestamp } +} + +# Envio +query getTokens($id: String) { + Token(where: {id: {_eq: $id}}) { id timestamp } +} +``` + +#### BigInt → numeric + +**Rule**: Variable types `BigInt` and `BigInt!` are converted to `numeric` and `numeric!` respectively. + +**Example**: + +```graphql +# TheGraph +query GetTokens($amount: BigInt) { + tokens(where: { amount: $amount }) { id amount } +} + +# Envio +query GetTokens($amount: numeric) { + Token(where: {amount: {_eq: $amount}}) { id amount } +} +``` + +#### BigDecimal → numeric + +**Rule**: Variable types `BigDecimal` and `BigDecimal!` are converted to `numeric` and `numeric!` respectively. + +**Example**: + +```graphql +# TheGraph +query GetValue($value: BigDecimal!) { + pools(where: { value: $value }) { id } +} + +# Envio +query GetValue($value: numeric!) { + Pool(where: {value: {_eq: $value}}) { id } +} +``` + +--- + +## Summary Table + +| Category | TheGraph | Envio | Example | +|----------|-----------|-------|---------| +| **Entity Names** | Plural camelCase | Singular PascalCase (as-is from schema) | `pools` → `Pool` | +| **Pagination** | `first`, `skip` | `limit`, `offset` | `first: 10, skip: 20` → `limit: 10, offset: 20` | +| **Ordering** | `orderBy`, `orderDirection` | `order_by: {field: direction}` | `orderBy: name, orderDirection: desc` → `order_by: {name: desc}` | +| **Equality Filter** | `field: value` | `field: {_eq: value}` | `name: "test"` → `name: {_eq: "test"}` | +| **Comparison Filters** | `field_gt`, `field_gte`, etc. | `field: {_gt: value}`, etc. | `amount_gt: 100` → `amount: {_gt: 100}` | +| **String Filters** | `_contains`, `_starts_with`, etc. | `_ilike` with `%` wildcards | `name_contains: "test"` → `name: {_ilike: "%test%"}` | +| **Variable Types** | `ID`, `Bytes`, `BigInt`, `BigDecimal` | `String`, `numeric` | `$id: ID!` → `$id: String!` | + +--- + +## Getting Help + +If you encounter any issues with query conversion or have questions: + +- **Converter Issues**: File a [GitHub issue](https://github.com/enviodev/subgraph-to-hyperindex-query-converter/issues) for the converter tool +- **General Questions**: Join our [Discord community](https://discord.gg/envio) for support + diff --git a/docs/HyperIndex/supported-networks/hoodi.md b/docs/HyperIndex/supported-networks/hoodi.md new file mode 100644 index 00000000..0ce766e9 --- /dev/null +++ b/docs/HyperIndex/supported-networks/hoodi.md @@ -0,0 +1,63 @@ +--- +id: hoodi +title: Hoodi +sidebar_label: Hoodi +slug: /hoodi +--- + +# Hoodi + +## Indexing Hoodi Data with Envio + +| **Field** | **Value** | +|-------------------------------|----------------------------------------------------------------------------------------------------| +| **Hoodi Chain ID** | 560048 | +| **HyperSync URL Endpoint** | [https://hoodi.hypersync.xyz](https://hoodi.hypersync.xyz) or [https://560048.hypersync.xyz](https://560048.hypersync.xyz) | +| **HyperRPC URL Endpoint** | [https://hoodi.rpc.hypersync.xyz](https://hoodi.rpc.hypersync.xyz) or [https://560048.rpc.hypersync.xyz](https://560048.rpc.hypersync.xyz) | + +--- + +### Tier + +TESTNET 🎒 + +### Overview + +Envio is a modular hyper-performant data indexing solution for Hoodi, enabling applications and developers to efficiently index and aggregate real-time and historical blockchain data. Envio offers three primary solutions for indexing and accessing large amounts of data: [HyperIndex](/docs/HyperIndex/overview) (a customizable indexing framework), [HyperSync](/docs/HyperSync/overview) (a real-time indexed data layer), and [HyperRPC](/docs/HyperRPC/overview-hyperrpc) (extremely fast read-only RPC). + +HyperSync accelerates the synchronization of historical data on Hoodi, enabling what usually takes hours to sync millions of events to be completed in under a minute—up to 1000x faster than traditional RPC methods. + +Designed to optimize the user experience, Envio offers automatic code generation, flexible language support, multi-chain data aggregation, and a reliable, cost-effective hosted service. + +To get started, see our documentation or follow our quickstart [guide](/docs/HyperIndex/contract-import). + +--- + +### Defining Network Configurations + +```yaml +name: IndexerName # Specify indexer name +description: Indexer Description # Include indexer description +networks: + - id: 560048 # Hoodi + start_block: START_BLOCK_NUMBER # Specify the starting block + contracts: + - name: ContractName + address: + - "0xYourContractAddress1" + - "0xYourContractAddress2" + handler: ./src/EventHandlers.ts + events: + - event: Event # Specify event + - event: Event +``` + +With these steps completed, your application will be set to efficiently index Hoodi data using Envio’s blockchain indexer. + +For more information on how to set up your config, define a schema, and write event handlers, refer to the guides section in our [documentation](/docs/HyperIndex/configuration-file). + +### Support + +Can’t find what you’re looking for or need support? Reach out to us on [Discord](https://discord.com/invite/Q9qt8gZ2fX); we’re always happy to help! + +--- diff --git a/docs/HyperIndex/supported-networks/injective.md b/docs/HyperIndex/supported-networks/injective.md new file mode 100644 index 00000000..da1c1382 --- /dev/null +++ b/docs/HyperIndex/supported-networks/injective.md @@ -0,0 +1,63 @@ +--- +id: injective +title: Injective +sidebar_label: Injective +slug: /injective +--- + +# Injective + +## Indexing Injective Data with Envio + +| **Field** | **Value** | +|-------------------------------|----------------------------------------------------------------------------------------------------| +| **Injective Chain ID** | 1776 | +| **HyperSync URL Endpoint** | [https://injective.hypersync.xyz](https://injective.hypersync.xyz) or [https://1776.hypersync.xyz](https://1776.hypersync.xyz) | +| **HyperRPC URL Endpoint** | [https://injective.rpc.hypersync.xyz](https://injective.rpc.hypersync.xyz) or [https://1776.rpc.hypersync.xyz](https://1776.rpc.hypersync.xyz) | + +--- + +### Tier + +BRONZE 🥉 + +### Overview + +Envio is a modular hyper-performant data indexing solution for Injective, enabling applications and developers to efficiently index and aggregate real-time and historical blockchain data. Envio offers three primary solutions for indexing and accessing large amounts of data: [HyperIndex](/docs/HyperIndex/overview) (a customizable indexing framework), [HyperSync](/docs/HyperSync/overview) (a real-time indexed data layer), and [HyperRPC](/docs/HyperRPC/overview-hyperrpc) (extremely fast read-only RPC). + +HyperSync accelerates the synchronization of historical data on Injective, enabling what usually takes hours to sync millions of events to be completed in under a minute—up to 1000x faster than traditional RPC methods. + +Designed to optimize the user experience, Envio offers automatic code generation, flexible language support, multi-chain data aggregation, and a reliable, cost-effective hosted service. + +To get started, see our documentation or follow our quickstart [guide](/docs/HyperIndex/contract-import). + +--- + +### Defining Network Configurations + +```yaml +name: IndexerName # Specify indexer name +description: Indexer Description # Include indexer description +networks: + - id: 1776 # Injective + start_block: START_BLOCK_NUMBER # Specify the starting block + contracts: + - name: ContractName + address: + - "0xYourContractAddress1" + - "0xYourContractAddress2" + handler: ./src/EventHandlers.ts + events: + - event: Event # Specify event + - event: Event +``` + +With these steps completed, your application will be set to efficiently index Injective data using Envio’s blockchain indexer. + +For more information on how to set up your config, define a schema, and write event handlers, refer to the guides section in our [documentation](/docs/HyperIndex/configuration-file). + +### Support + +Can’t find what you’re looking for or need support? Reach out to us on [Discord](https://discord.com/invite/Q9qt8gZ2fX); we’re always happy to help! + +--- diff --git a/docs/HyperRPC/hyperrpc-supported-networks.md b/docs/HyperRPC/hyperrpc-supported-networks.md index 59f52dda..af48310f 100644 --- a/docs/HyperRPC/hyperrpc-supported-networks.md +++ b/docs/HyperRPC/hyperrpc-supported-networks.md @@ -38,6 +38,8 @@ Here is a table of the currently supported networks on HyperRPC and their respec + + @@ -76,7 +78,9 @@ Here is a table of the currently supported networks on HyperRPC and their respec | Gnosis Traces | 100 | https://gnosis-traces.rpc.hypersync.xyz or https://100-traces.rpc.hypersync.xyz | | Harmony Shard 0 | 1666600000 | https://harmony-shard-0.rpc.hypersync.xyz or https://1666600000.rpc.hypersync.xyz | | Holesky | 17000 | https://holesky.rpc.hypersync.xyz or https://17000.rpc.hypersync.xyz | +| Hoodi | 560048 | https://hoodi.rpc.hypersync.xyz or https://560048.rpc.hypersync.xyz | | Hyperliquid | 999 | https://hyperliquid.rpc.hypersync.xyz or https://999.rpc.hypersync.xyz | +| Injective | 1776 | https://injective.rpc.hypersync.xyz or https://1776.rpc.hypersync.xyz | | Ink | 57073 | https://ink.rpc.hypersync.xyz or https://57073.rpc.hypersync.xyz | | Kroma | 255 | https://kroma.rpc.hypersync.xyz or https://255.rpc.hypersync.xyz | | Linea | 59144 | https://linea.rpc.hypersync.xyz or https://59144.rpc.hypersync.xyz | diff --git a/docs/HyperSync/hypersync-supported-networks.md b/docs/HyperSync/hypersync-supported-networks.md index 5300dac3..30c8c9b6 100644 --- a/docs/HyperSync/hypersync-supported-networks.md +++ b/docs/HyperSync/hypersync-supported-networks.md @@ -46,6 +46,8 @@ If you are a network operator or user and would like improved service support or + + @@ -84,7 +86,9 @@ If you are a network operator or user and would like improved service support or | Gnosis Traces | 100 | https://gnosis-traces.hypersync.xyz or https://100-traces.hypersync.xyz | 🥉 | | Harmony Shard 0 | 1666600000 | https://harmony-shard-0.hypersync.xyz or https://1666600000.hypersync.xyz | 🪨 | | Holesky | 17000 | https://holesky.hypersync.xyz or https://17000.hypersync.xyz | 🎒 | +| Hoodi | 560048 | https://hoodi.hypersync.xyz or https://560048.hypersync.xyz | 🎒 | | Hyperliquid | 999 | https://hyperliquid.hypersync.xyz or https://999.hypersync.xyz | 🪨 | +| Injective | 1776 | https://injective.hypersync.xyz or https://1776.hypersync.xyz | 🥉 | | Ink | 57073 | https://ink.hypersync.xyz or https://57073.hypersync.xyz | 🪨 | | Kroma | 255 | https://kroma.hypersync.xyz or https://255.hypersync.xyz | 🪨 | | Linea | 59144 | https://linea.hypersync.xyz or https://59144.hypersync.xyz | 🥉 | diff --git a/sidebarsHyperIndex.js b/sidebarsHyperIndex.js index a756393a..01b6915b 100644 --- a/sidebarsHyperIndex.js +++ b/sidebarsHyperIndex.js @@ -45,7 +45,7 @@ module.exports = { type: "category", label: "Migrate to Envio", collapsed: false, - items: ["migration-guide", "migrate-from-alchemy", "migrate-to-v3"], + items: ["migration-guide", "migrate-from-alchemy", "migrate-to-v3", "query-conversion"], }, { diff --git a/supported-networks.json b/supported-networks.json index 94c8ce5b..0a03eedf 100644 --- a/supported-networks.json +++ b/supported-networks.json @@ -86,9 +86,11 @@ "supported-networks/harmony-shard-0", "supported-networks/heco-chain", "supported-networks/holesky", + "supported-networks/hoodi", "supported-networks/hyperliquid", "supported-networks/immutable-zkevm", "supported-networks/immutable-zkevm-testnet", + "supported-networks/injective", "supported-networks/ink", "supported-networks/iotex-network", "supported-networks/japan-open-chain", From fe68d7fcda5ab2369dec6369a27432e672ab4cf7 Mon Sep 17 00:00:00 2001 From: keenbeen32 Date: Tue, 30 Dec 2025 14:28:21 +0200 Subject: [PATCH 2/5] feat: added info on hosting converter --- CONVERSION_RULES.md | 624 ------------------ docs/HyperIndex/query-conversion.md | 4 + docs/HyperRPC/hyperrpc-supported-networks.md | 2 + .../HyperSync/hypersync-supported-networks.md | 2 + 4 files changed, 8 insertions(+), 624 deletions(-) delete mode 100644 CONVERSION_RULES.md diff --git a/CONVERSION_RULES.md b/CONVERSION_RULES.md deleted file mode 100644 index 7f09df58..00000000 --- a/CONVERSION_RULES.md +++ /dev/null @@ -1,624 +0,0 @@ -# Complete Conversion Rules: The Graph → Envio/Hyperindex - -This document lists all conversion rules implemented in the subgraph-to-hyperindex query converter. Each rule includes both the rule statement and a concrete example. - -The rules are organized into two sections: -- **Direct Changes**: Simple, straightforward mappings (e.g., `first` → `limit`, `orderBy` → `order_by`) -- **Advanced/Nuanced Differences**: More complex conversions that require schema introspection or special handling - ---- - -## Part I: Direct Changes - -These are simple, direct mappings that can be applied without schema introspection or complex logic. - -### 1. Entity Name Conversion - -#### Rule 1.1: Entity Naming Convention -**Rule**: The Graph uses pluralized entity names (e.g., `streams`, `users`, `orders`), while Envio/Hyperindex uses the entity name as-is from the schema (singular, PascalCase). The converter automatically singularizes and capitalizes The Graph's plural entity names to match Envio's schema. - -**Example**: -```graphql -# The Graph -query { - streams(first: 10) { id } - users(first: 5) { id } - lpActions(first: 100) { id } - orders(first: 20) { id } - user(id: "0x123") { totalVolume } -} - -# Envio/Hyperindex -query { - Stream(limit: 10) { id } - User(limit: 5) { id } - LpAction(limit: 100) { id } - Order(limit: 20) { id } - User_by_pk(id: "0x123") { totalVolume } -} -``` - -### 2. Pagination Parameters - -#### Rule 2.1: First → Limit -**Rule**: The `first` parameter is converted to `limit`. - -**Example**: -```graphql -# The Graph -query { - streams(first: 10) { id } -} - -# Envio/Hyperindex -query { - Stream(limit: 10) { id } -} -``` - -#### Rule 2.2: Skip → Offset -**Rule**: The `skip` parameter is converted to `offset`. - -**Example**: -```graphql -# The Graph -query { - streams(first: 10, skip: 20) { id } -} - -# Envio/Hyperindex -query { - Stream(limit: 10, offset: 20) { id } -} -``` - -#### Rule 2.3: Pagination Variables Preserved -**Rule**: GraphQL variables used for pagination are preserved in the converted query. - -**Example**: -```graphql -# The Graph -query GetStreams($first: Int!, $skip: Int!) { - streams(first: $first, skip: $skip) { id } -} - -# Envio/Hyperindex -query GetStreams($first: Int!, $skip: Int!) { - Stream(limit: $first, offset: $skip) { id } -} -``` - -### 3. Ordering Parameters - -#### Rule 3.1: OrderBy and OrderDirection → Order_by -**Rule**: The `orderBy` and `orderDirection` parameters are combined into a single `order_by: {field: direction}` clause. - -**Example**: -```graphql -# The Graph -query { - streams(orderBy: name, orderDirection: desc) { id name } -} - -# Envio/Hyperindex -query { - Stream(order_by: {name: desc}) { id name } -} -``` - -#### Rule 3.2: Ascending Order -**Rule**: `orderDirection: asc` is converted to `order_by: {field: asc}`. - -**Example**: -```graphql -# The Graph -query { - streams(orderBy: timestamp, orderDirection: asc) { id timestamp } -} - -# Envio/Hyperindex -query { - Stream(order_by: {timestamp: asc}) { id timestamp } -} -``` - -### 4. Filter Operators - -#### Rule 4.1: Equality Filter -**Rule**: Simple equality filters are converted to `field: {_eq: value}` format. - -**Example**: -```graphql -# The Graph -query { - streams(name: "test") { id name } -} - -# Envio/Hyperindex -query { - Stream(where: {name: {_eq: "test"}}) { id name } -} -``` - -#### Rule 4.2: Comparison Filters -**Rule**: Comparison filters (`_not`, `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_not_in`) are converted to their Hasura equivalents. - -**Examples**: -```graphql -# The Graph -query { - streams(id_not: "0x123") { id } - streams(amount_gt: 100) { id amount } - streams(amount_gte: 100) { id amount } - streams(timestamp_lt: 1650000000) { id timestamp } - streams(timestamp_lte: 1650000000) { id timestamp } - streams(id_in: ["1", "2", "3"]) { id name } - streams(id_not_in: ["1", "2", "3"]) { id name } -} - -# Envio/Hyperindex -query { - Stream(where: {id: {_neq: "0x123"}}) { id } - Stream(where: {amount: {_gt: 100}}) { id amount } - Stream(where: {amount: {_gte: 100}}) { id amount } - Stream(where: {timestamp: {_lt: 1650000000}}) { id timestamp } - Stream(where: {timestamp: {_lte: 1650000000}}) { id timestamp } - Stream(where: {id: {_in: ["1", "2", "3"]}}) { id name } - Stream(where: {id: {_nin: ["1", "2", "3"]}}) { id name } -} -``` - -#### Rule 4.3: String Filters -**Rule**: String filters (`_contains`, `_starts_with`, `_ends_with`, and their `_not` and `_nocase` variants) are converted to `_ilike` with appropriate wildcards. - -**Examples**: -```graphql -# The Graph -query { - streams(name_contains: "graph") { id name } - streams(name_not_contains: "graph") { id name } - streams(symbol_starts_with: "ETH") { id symbol } - streams(symbol_ends_with: "USD") { id symbol } - streams(name_not_starts_with: "A") { id name } - streams(name_not_ends_with: "x") { id name } - streams(name_contains_nocase: "alice") { id name } - streams(name_starts_with_nocase: "test") { id name } - streams(name_ends_with_nocase: "def") { id name } -} - -# Envio/Hyperindex -query { - Stream(where: {name: {_ilike: "%graph%"}}) { id name } - Stream(where: {_not: {name: {_ilike: "%graph%"}}}) { id name } - Stream(where: {symbol: {_ilike: "ETH%"}}) { id symbol } - Stream(where: {symbol: {_ilike: "%USD"}}) { id symbol } - Stream(where: {_not: {name: {_ilike: "A%"}}}) { id name } - Stream(where: {_not: {name: {_ilike: "%x"}}}) { id name } - Stream(where: {name: {_ilike: "%alice%"}}) { id name } - Stream(where: {name: {_ilike: "test%"}}) { id name } - Stream(where: {name: {_ilike: "%def"}}) { id name } -} -``` - -#### Rule 4.4: Unsupported Filters -**Rule**: `field_containsAny` and `field_containsAll` filters are not supported and will return an error. - -**Example**: -```graphql -# The Graph -query { - streams(tags_containsAny: ["tag1", "tag2"]) { id } -} -# ❌ Returns ConversionError::UnsupportedFilter("tags_containsAny") -``` - -### 5. Variable Type Conversions - -#### Rule 5.1: ID → String -**Rule**: Variable types `ID` and `ID!` are converted to `String` and `String!` respectively. - -**Example**: -```graphql -# The Graph -query getUserVolume($id: ID!) { - user(id: $id) { totalVolume } -} - -# Envio/Hyperindex -query getUserVolume($id: String!) { - User_by_pk(id: $id) { totalVolume } -} -``` - -#### Rule 5.2: Bytes → String -**Rule**: Variable types `Bytes` and `Bytes!` are converted to `String` and `String!` respectively. - -**Example**: -```graphql -# The Graph -query getPoolActions($id: Bytes) { - lpActions(where: { user: $id }) { user timestamp } -} - -# Envio/Hyperindex -query getPoolActions($id: String) { - LpAction(where: {user: {id: {_eq: $id}}}) { user timestamp } -} -``` - -#### Rule 5.3: BigInt → numeric -**Rule**: Variable types `BigInt` and `BigInt!` are converted to `numeric` and `numeric!` respectively. - -**Example**: -```graphql -# The Graph -query Limits($minLeverage: BigInt) { - limits(where: { leverage_gte: $minLeverage }) { id leverage } -} - -# Envio/Hyperindex -query Limits($minLeverage: numeric) { - Limit(where: {leverage: {_gte: $minLeverage}}) { id leverage } -} -``` - -#### Rule 5.4: BigDecimal → numeric -**Rule**: Variable types `BigDecimal` and `BigDecimal!` are converted to `numeric` and `numeric!` respectively. - -**Example**: -```graphql -# The Graph -query GetPrice($price: BigDecimal!) { - pairs(where: { price: $price }) { id } -} - -# Envio/Hyperindex -query GetPrice($price: numeric!) { - Pair(where: {price: {_eq: $price}}) { id } -} -``` - -#### Rule 5.5: Array Types -**Rule**: Array types for `ID`, `Bytes`, and `BigInt` are converted to their corresponding array types (`[String!]`, `[numeric!]`, etc.). - -**Examples**: -```graphql -# The Graph -query ReferredUsers($addresses: [ID!]) { - users(where: { id_in: $addresses }) { id totalVolume } -} - -query GetUserLpShares($addresses: [Bytes!]) { - lpShares(where: { id_in: $addresses }) { id shares } -} - -query GetAmounts($amounts: [BigInt!]!) { - streams(where: { amount_in: $amounts }) { id amount } -} - -# Envio/Hyperindex -query ReferredUsers($addresses: [String!]) { - User(where: {id: {_in: $addresses}}) { id totalVolume } -} - -query GetUserLpShares($addresses: [String!]) { - LpShare(where: {id: {_in: $addresses}}) { id shares } -} - -query GetAmounts($amounts: [numeric!]!) { - Stream(where: {amount: {_in: $amounts}}) { id amount } -} -``` - ---- - -## Part II: Advanced/Nuanced Differences - -These conversions require schema introspection, special handling, or more complex logic. - -### 6. Nested Entity References - -#### Rule 6.1: Simple Scalar Reference -**Rule**: When a nested entity field is referenced with a simple scalar value (string/number or variable), it's converted to `field: {id: {_eq: value}}`. This requires schema introspection to determine if a field is a nested entity. - -**Example**: -```graphql -# The Graph -query { - orders(where: { pair: "0" }) { id } - trades(where: { trader: "0x123" }) { id } -} - -# Envio/Hyperindex -query { - Order(where: {pair: {id: {_eq: "0"}}}) { id } - Trade(where: {trader: {id: {_eq: "0x123"}}}) { id } -} -``` - -#### Rule 6.2: Nested Entity with Variable -**Rule**: Variables used to reference nested entities are also converted to the `{id: {_eq: $variable}}` format. Schema introspection determines if the field is a nested entity. - -**Example**: -```graphql -# The Graph -query GetTrades($pairId: String!) { - trades(where: { pair: $pairId }) { id } -} - -# Envio/Hyperindex -query GetTrades($pairId: String!) { - Trade(where: {pair: {id: {_eq: $pairId}}}) { id } -} -``` - -#### Rule 6.3: Nested Filters (Dot Notation - Single Level) -**Rule**: Filters on nested entities using dot notation are converted to nested where clauses. Schema introspection is used to determine nested entity relationships. - -**Example**: -```graphql -# The Graph -query { - orders(where: { user.name_contains: "john" }) { id } -} - -# Envio/Hyperindex -query { - Order(where: {user: {name: {_ilike: "%john%"}}}) { id } -} -``` - -#### Rule 6.4: Nested Filters (Dot Notation - Multiple Levels) -**Rule**: Filters on deeply nested entities are recursively converted to nested where clauses. Schema introspection is used to traverse the nested entity hierarchy. - -**Example**: -```graphql -# The Graph -query { - orders(where: { pair.token.amount_gt: 100 }) { id } - orders(where: { pair.group.name: "test" }) { id } -} - -# Envio/Hyperindex -query { - Order(where: {pair: {token: {amount: {_gt: 100}}}}) { id } - Order(where: {pair: {group: {name: {_eq: "test"}}}}) { id } -} -``` - -#### Rule 6.5: Multiple Conditions on Same Nested Field -**Rule**: Multiple conditions on the same nested field are wrapped in `_and`. Schema introspection ensures proper nesting. - -**Example**: -```graphql -# The Graph -query { - orders(where: { - user.name_contains: "john", - user.name_not: "doe" - }) { id } -} - -# Envio/Hyperindex -query { - Order(where: { - user: { - _and: [ - {name: {_ilike: "%john%"}}, - {name: {_neq: "doe"}} - ] - } - }) { id } -} -``` - -### 7. Chain ID Handling - -#### Rule 7.1: Default Endpoints (No Chain ID) -**Rule**: Default endpoints (`/` and `/debug`) do not add a `chainId` filter automatically. - -**Example**: -```graphql -# The Graph -query { - streams(first: 10) { id } -} - -# Envio/Hyperindex (via / endpoint) -query { - Stream(limit: 10) { id } -} -# No chainId filter added -``` - -#### Rule 7.2: Chain-Specific Endpoint (Collection Queries) -**Rule**: The `/chainId/{chain_id}` endpoint automatically adds `where: {chainId: {_eq: "{chain_id}"}}` to collection queries. - -**Example**: -```graphql -# The Graph -query { - streams(first: 10) { id } -} - -# Envio/Hyperindex (via /chainId/5 endpoint) -query { - Stream(limit: 10, where: {chainId: {_eq: "5"}}) { id } -} -``` - -#### Rule 7.3: Chain-Specific Endpoint (Primary Key Queries) -**Rule**: Single entity by primary key queries (`EntityName_by_pk`) do NOT get a `chainId` filter, even when using the `/chainId/{chain_id}` endpoint. - -**Example**: -```graphql -# The Graph -query { - user(id: "0x123") { totalVolume } -} - -# Envio/Hyperindex (via /chainId/1 endpoint) -query { - User_by_pk(id: "0x123") { totalVolume } -} -# No chainId filter added for _by_pk queries -``` - -### 8. Special Query Types - -#### Rule 8.1: Introspection Queries -**Rule**: Queries with operation name exactly matching `IntrospectionQuery` are passed through unchanged. - -**Example**: -```graphql -# The Graph -query IntrospectionQuery { - __schema { - types { name } - } -} - -# Envio/Hyperindex -query IntrospectionQuery { - __schema { - types { name } - } -} -# Passed through unchanged -``` - -**Note**: Only exact match on operation name. Queries containing `__schema` or `__type` but with different operation names are still converted. - -### 9. Fragments - -#### Rule 9.1: Fragment Preservation -**Rule**: GraphQL fragments are extracted, sanitized (arguments removed), and passed through unchanged in the converted query. - -**Example**: -```graphql -# The Graph -fragment VaultFields on Vault { - id - shares - sharePrice -} - -query getProtocolData { - vault(id: "usdc-vault") { - ...VaultFields - __typename - } -} - -# Envio/Hyperindex -fragment VaultFields on Vault { - id - shares - sharePrice -} - -query getProtocolData { - Vault_by_pk(id: "usdc-vault") { - ...VaultFields - __typename - } -} -# Fragment preserved, entity name converted -``` - -### 10. Multiple Entities in Single Query - -#### Rule 10.1: Independent Entity Conversion -**Rule**: Multiple entities in a single query are each converted independently. - -**Example**: -```graphql -# The Graph -query { - users(first: 10) { id } - orders(first: 5) { id } - trades(first: 3) { id } -} - -# Envio/Hyperindex -query { - User(limit: 10) { id } - Order(limit: 5) { id } - Trade(limit: 3) { id } -} -``` - -#### Rule 10.2: Mixed Query Types -**Rule**: A single query can contain both collection queries and primary key queries, each converted appropriately. - -**Example**: -```graphql -# The Graph -query { - users(first: 10) { id } - user(id: "0x123") { totalVolume } - orders(first: 5) { id } -} - -# Envio/Hyperindex -query { - User(limit: 10) { id } - User_by_pk(id: "0x123") { totalVolume } - Order(limit: 5) { id } -} -``` - -### 11. Where Clause Variable - -#### Rule 11.1: Where Variable Pass-Through -**Rule**: If `where` is passed as a variable (e.g., `where: $where`), it's passed through directly without conversion. - -**Example**: -```graphql -# The Graph -query GetStreams($where: Stream_filter) { - streams(where: $where) { id } -} - -# Envio/Hyperindex -query GetStreams($where: Stream_filter) { - Stream(where: $where) { id } -} -# where clause passed through unchanged -``` - -### 12. OrderBy Variable Handling - -#### Rule 12.1: OrderBy Variable Handling -**Rule**: If `orderBy` is a variable (e.g., `$orderBy`), the `order_by` clause is ignored to keep the query valid. - -**Example**: -```graphql -# The Graph -query GetStreams($orderBy: String, $orderDirection: String) { - streams(orderBy: $orderBy, orderDirection: $orderDirection) { id } -} - -# Envio/Hyperindex -query GetStreams($orderBy: String, $orderDirection: String) { - Stream { id } - # order_by clause omitted because orderBy is a variable -} -``` - ---- - -## Summary Table - -| Category | The Graph | Envio/Hyperindex | Example | -|----------|-----------|------------------|---------| -| **Entity Names** | Plural camelCase | Singular PascalCase (as-is from schema) | `streams` → `Stream` | -| **Pagination** | `first`, `skip` | `limit`, `offset` | `first: 10, skip: 20` → `limit: 10, offset: 20` | -| **Ordering** | `orderBy`, `orderDirection` | `order_by: {field: direction}` | `orderBy: name, orderDirection: desc` → `order_by: {name: desc}` | -| **Equality Filter** | `field: value` | `field: {_eq: value}` | `name: "test"` → `name: {_eq: "test"}` | -| **Comparison Filters** | `field_gt`, `field_gte`, etc. | `field: {_gt: value}`, etc. | `amount_gt: 100` → `amount: {_gt: 100}` | -| **String Filters** | `_contains`, `_starts_with`, etc. | `_ilike` with `%` wildcards | `name_contains: "test"` → `name: {_ilike: "%test%"}` | -| **Variable Types** | `ID`, `Bytes`, `BigInt`, `BigDecimal` | `String`, `numeric` | `$id: ID!` → `$id: String!` | -| **Nested Entities** | `field: "id"` | `field: {id: {_eq: "id"}}` | `pair: "0"` → `pair: {id: {_eq: "0"}}` | -| **Nested Filters** | `field.subfield_operator: value` | `field: {subfield: {_operator: value}}` | `user.name_contains: "john"` → `user: {name: {_ilike: "%john%"}}` | -| **Chain ID** | Manual filter | Auto-added via `/chainId/{id}` endpoint | `/chainId/5` adds `chainId: {_eq: "5"}` | diff --git a/docs/HyperIndex/query-conversion.md b/docs/HyperIndex/query-conversion.md index 82783525..c91de2c5 100644 --- a/docs/HyperIndex/query-conversion.md +++ b/docs/HyperIndex/query-conversion.md @@ -21,6 +21,10 @@ We've built a [query converter tool](https://github.com/enviodev/subgraph-to-hyp **Repository**: [subgraph-to-hyperindex-query-converter](https://github.com/enviodev/subgraph-to-hyperindex-query-converter) +### Dedicated Tier: Hosted Converter Endpoint + +For users on our dedicated tier, we can host the query converter as a proxy endpoint for your hosted indexer. This allows you to continue using TheGraph query syntax without making any changes to your existing queries or client code. Simply point your applications to the converter endpoint, which will automatically translate TheGraph queries to Envio's syntax and forward them to your indexer. + :::warning Beta Status This converter tool is still very much in beta. We're actively working on it and discovering new query conversions that need to be handled. diff --git a/docs/HyperRPC/hyperrpc-supported-networks.md b/docs/HyperRPC/hyperrpc-supported-networks.md index af48310f..51948513 100644 --- a/docs/HyperRPC/hyperrpc-supported-networks.md +++ b/docs/HyperRPC/hyperrpc-supported-networks.md @@ -40,6 +40,8 @@ Here is a table of the currently supported networks on HyperRPC and their respec + + diff --git a/docs/HyperSync/hypersync-supported-networks.md b/docs/HyperSync/hypersync-supported-networks.md index 30c8c9b6..eb530a18 100644 --- a/docs/HyperSync/hypersync-supported-networks.md +++ b/docs/HyperSync/hypersync-supported-networks.md @@ -48,6 +48,8 @@ If you are a network operator or user and would like improved service support or + + From ceb2f33587dd5082e7d4e675a3b5af6d1a02435e Mon Sep 17 00:00:00 2001 From: keenbeen32 Date: Tue, 30 Dec 2025 15:08:49 +0200 Subject: [PATCH 3/5] chore: improved wording --- docs/HyperIndex/query-conversion.md | 22 +++++++++---------- docs/HyperRPC/hyperrpc-supported-networks.md | 2 ++ .../HyperSync/hypersync-supported-networks.md | 2 ++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/HyperIndex/query-conversion.md b/docs/HyperIndex/query-conversion.md index c91de2c5..ac0d760e 100644 --- a/docs/HyperIndex/query-conversion.md +++ b/docs/HyperIndex/query-conversion.md @@ -35,7 +35,7 @@ This converter tool is still very much in beta. We're actively working on it and ### 1. Entity Name Conversion -**Rule**: TheGraph uses pluralized entity names (e.g., `pools`, `factories`, `tokens`), while Envio uses the entity name as-is from the schema (singular, PascalCase). When converting, plural entity names are automatically singularized and capitalized to match Envio's schema. +**Rule**: TheGraph uses pluralized entity names (e.g., `pools`, `factories`, `tokens`), while Envio uses the entity name exactly as defined in your schema (singular, PascalCase). **Example**: @@ -82,7 +82,7 @@ query { #### First → Limit -**Rule**: The `first` parameter is converted to `limit`. +**Rule**: Use `limit` instead of `first`. **Example**: @@ -100,7 +100,7 @@ query { #### Skip → Offset -**Rule**: The `skip` parameter is converted to `offset`. +**Rule**: Use `offset` instead of `skip`. **Example**: @@ -120,7 +120,7 @@ query { #### OrderBy and OrderDirection → Order_by -**Rule**: The `orderBy` and `orderDirection` parameters are combined into a single `order_by: {field: direction}` clause. +**Rule**: Combine `orderBy` and `orderDirection` into a single `order_by: {field: direction}` clause. **Example**: @@ -142,7 +142,7 @@ query { #### Equality Filter -**Rule**: Simple equality filters are converted to `where: {field: {_eq: value}}` format. +**Rule**: Use `where: {field: {_eq: value}}` format for simple equality filters. **Example**: @@ -160,7 +160,7 @@ query { #### Comparison Filters -**Rule**: Comparison filters (`_not`, `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_not_in`) are converted to their Hasura equivalents. +**Rule**: Use Hasura filter operators for comparison filters. Replace `_not` with `_neq`, `_not_in` with `_nin`, and keep `_gt`, `_gte`, `_lt`, `_lte`, `_in` as-is, wrapping them in `where: {field: {_operator: value}}`. **Examples**: @@ -190,7 +190,7 @@ query { #### String Filters -**Rule**: String filters (`_contains`, `_starts_with`, `_ends_with`, and their `_not` and `_nocase` variants) are converted to `_ilike` with appropriate wildcards. The `%` symbol represents any text at that position in the pattern. +**Rule**: Use `_ilike` with `%` wildcards for string filters. Replace `_contains` with `_ilike: "%text%"`, `_starts_with` with `_ilike: "text%"`, and `_ends_with` with `_ilike: "%text"`. The `%` symbol represents any text at that position in the pattern. **Examples**: @@ -226,7 +226,7 @@ query { #### ID → String -**Rule**: Variable types `ID` and `ID!` are converted to `String` and `String!` respectively. +**Rule**: Use `String` and `String!` instead of `ID` and `ID!` for variable types. **Example**: @@ -244,7 +244,7 @@ query getPoolValue($id: String!) { #### Bytes → String -**Rule**: Variable types `Bytes` and `Bytes!` are converted to `String` and `String!` respectively. +**Rule**: Use `String` and `String!` instead of `Bytes` and `Bytes!` for variable types. **Example**: @@ -262,7 +262,7 @@ query getTokens($id: String) { #### BigInt → numeric -**Rule**: Variable types `BigInt` and `BigInt!` are converted to `numeric` and `numeric!` respectively. +**Rule**: Use `numeric` and `numeric!` instead of `BigInt` and `BigInt!` for variable types. **Example**: @@ -280,7 +280,7 @@ query GetTokens($amount: numeric) { #### BigDecimal → numeric -**Rule**: Variable types `BigDecimal` and `BigDecimal!` are converted to `numeric` and `numeric!` respectively. +**Rule**: Use `numeric` and `numeric!` instead of `BigDecimal` and `BigDecimal!` for variable types. **Example**: diff --git a/docs/HyperRPC/hyperrpc-supported-networks.md b/docs/HyperRPC/hyperrpc-supported-networks.md index 51948513..04c8c2bb 100644 --- a/docs/HyperRPC/hyperrpc-supported-networks.md +++ b/docs/HyperRPC/hyperrpc-supported-networks.md @@ -42,6 +42,8 @@ Here is a table of the currently supported networks on HyperRPC and their respec + + diff --git a/docs/HyperSync/hypersync-supported-networks.md b/docs/HyperSync/hypersync-supported-networks.md index eb530a18..829fbd94 100644 --- a/docs/HyperSync/hypersync-supported-networks.md +++ b/docs/HyperSync/hypersync-supported-networks.md @@ -50,6 +50,8 @@ If you are a network operator or user and would like improved service support or + + From 51a2547aaca28a81fec25fa1874d7a7d6f6a2bf2 Mon Sep 17 00:00:00 2001 From: keenbeen32 Date: Tue, 30 Dec 2025 15:11:16 +0200 Subject: [PATCH 4/5] fix: grammar error --- docs/HyperIndex/migration-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HyperIndex/migration-guide.md b/docs/HyperIndex/migration-guide.md index 833facf3..78091d1c 100644 --- a/docs/HyperIndex/migration-guide.md +++ b/docs/HyperIndex/migration-guide.md @@ -196,7 +196,7 @@ HyperIndex is a powerful tool that can be used to index any contract. There are - Use the `field_selection` option to add additional fields to your index. Doc here: [field selection](../HyperIndex/configuration-file#field-selection) - Use the `unordered_multichain_mode` option to enable unordered multichain mode, this is the most common need for multichain indexing. However comes with tradeoffs worth understanding. Doc here: [unordered multichain mode](../HyperIndex/configuration-file#unordered-multichain-mode) - Use wildcard indexing to index by event signatures rather than by contract address. -- Query Conversion: HyperIndex uses the standard GraphQL query language, whereas The Graph uses a custom GraphQL syntax. While queries are very similar, there are important differences when querying your indexed data. See the [Query Conversion Guide](./query-conversion) for detailed conversion rules and examples. +- Query Conversion: HyperIndex uses the standard GraphQL query language, whereas TheGraph uses a custom GraphQL syntax. While queries are very similar, there are important differences when querying your indexed data. See the [Query Conversion Guide](./query-conversion) for detailed conversion rules and examples. - Loaders are a powerful feature to optimize historical sync performance. You can read more about them [here](../HyperIndex/loaders). - HyperIndex is very flexible and can be used to index offchain data too or send messages to a queue etc for fetching external data, you can further optimise the fetching by using the [effects api](/docs/HyperIndex/effect-api) From f6318b4c40737e4ea0aa90c54f5e6097002242b2 Mon Sep 17 00:00:00 2001 From: keenbeen32 Date: Tue, 30 Dec 2025 15:23:07 +0200 Subject: [PATCH 5/5] chore: moved to guides section --- docs/HyperIndex/{ => Guides}/query-conversion.md | 0 docs/HyperIndex/migration-guide.md | 2 +- docs/HyperRPC/hyperrpc-supported-networks.md | 2 ++ docs/HyperSync/hypersync-supported-networks.md | 2 ++ sidebarsHyperIndex.js | 3 ++- 5 files changed, 7 insertions(+), 2 deletions(-) rename docs/HyperIndex/{ => Guides}/query-conversion.md (100%) diff --git a/docs/HyperIndex/query-conversion.md b/docs/HyperIndex/Guides/query-conversion.md similarity index 100% rename from docs/HyperIndex/query-conversion.md rename to docs/HyperIndex/Guides/query-conversion.md diff --git a/docs/HyperIndex/migration-guide.md b/docs/HyperIndex/migration-guide.md index 78091d1c..2df84967 100644 --- a/docs/HyperIndex/migration-guide.md +++ b/docs/HyperIndex/migration-guide.md @@ -196,7 +196,7 @@ HyperIndex is a powerful tool that can be used to index any contract. There are - Use the `field_selection` option to add additional fields to your index. Doc here: [field selection](../HyperIndex/configuration-file#field-selection) - Use the `unordered_multichain_mode` option to enable unordered multichain mode, this is the most common need for multichain indexing. However comes with tradeoffs worth understanding. Doc here: [unordered multichain mode](../HyperIndex/configuration-file#unordered-multichain-mode) - Use wildcard indexing to index by event signatures rather than by contract address. -- Query Conversion: HyperIndex uses the standard GraphQL query language, whereas TheGraph uses a custom GraphQL syntax. While queries are very similar, there are important differences when querying your indexed data. See the [Query Conversion Guide](./query-conversion) for detailed conversion rules and examples. +- Query Conversion: HyperIndex uses the standard GraphQL query language, whereas TheGraph uses a custom GraphQL syntax. While queries are very similar, there are important differences when querying your indexed data. See the [Query Conversion Guide](../HyperIndex/query-conversion) for detailed conversion rules and examples. - Loaders are a powerful feature to optimize historical sync performance. You can read more about them [here](../HyperIndex/loaders). - HyperIndex is very flexible and can be used to index offchain data too or send messages to a queue etc for fetching external data, you can further optimise the fetching by using the [effects api](/docs/HyperIndex/effect-api) diff --git a/docs/HyperRPC/hyperrpc-supported-networks.md b/docs/HyperRPC/hyperrpc-supported-networks.md index 04c8c2bb..76392db6 100644 --- a/docs/HyperRPC/hyperrpc-supported-networks.md +++ b/docs/HyperRPC/hyperrpc-supported-networks.md @@ -44,6 +44,8 @@ Here is a table of the currently supported networks on HyperRPC and their respec + + diff --git a/docs/HyperSync/hypersync-supported-networks.md b/docs/HyperSync/hypersync-supported-networks.md index 829fbd94..9154a619 100644 --- a/docs/HyperSync/hypersync-supported-networks.md +++ b/docs/HyperSync/hypersync-supported-networks.md @@ -52,6 +52,8 @@ If you are a network operator or user and would like improved service support or + + diff --git a/sidebarsHyperIndex.js b/sidebarsHyperIndex.js index 01b6915b..388a1bcc 100644 --- a/sidebarsHyperIndex.js +++ b/sidebarsHyperIndex.js @@ -45,7 +45,7 @@ module.exports = { type: "category", label: "Migrate to Envio", collapsed: false, - items: ["migration-guide", "migrate-from-alchemy", "migrate-to-v3", "query-conversion"], + items: ["migration-guide", "migrate-from-alchemy", "migrate-to-v3"], }, { @@ -62,6 +62,7 @@ module.exports = { "Guides/testing", "Guides/navigating-hasura", "Guides/environment-variables", + "Guides/query-conversion", ], }, {