Configuration schemas

Currently there is only a single configuration schema, version 1. This configuration file is required for instant / no-code subgraphs.

Version 1

  • [REQUIRED] version (string, must be "1") - The version of the configuration schema.
  • [OPTIONAL] name (string) - The name of the subgraph.
  • [REQUIRED] abis (map of object) - A map of ABI names to ABI source configurations.
    • [REQUIRED] path (string) - The path to the ABI source, relative to the configuration file.
  • [REQUIRED] instances (array of object) - A list of data source or data template instances to index.
  • [OPTIONAL] enableCallHandlers (boolean) - Whether to enable call handler indexing for the subgraph

Data source instance

Data sources are instances derived from a single contract address.

  • [REQUIRED] abi (string) - The name of the ABI source.
  • [REQUIRED] address (string) - The contract address to index.
  • [REQUIRED] startBlock (number) - The block to start indexing from.
  • [REQUIRED] chain (string) - The chain to index on.
  • [OPTIONAL] enrich (object) - An object containing enrichment configurations.

Data template instance

Data templates are instances derived from an event emitted by a contract. The event signature must include an address parameter that contains the contract address that will be indexed.

  • [REQUIRED] abi (string) - The name of the ABI data template instance (e.g., the pool).
  • [REQUIRED] source (object) - The source event details to create a new data template instance.
    • [REQUIRED] abi (string) - The name of the ABI data template source (e.g., the factory).
    • [REQUIRED] eventSignature (string) - The event signature to listen for.
    • [REQUIRED] addressParam (string) - The parameter to extract the contract address from.
  • [OPTIONAL] enrich (object) - An object containing enrichment configurations.

Instance enrichment

Enrichments allow data source and template instances to be enriched by performing eth calls and mapping the outputs to one or more fields and/or entities.

  • [OPTIONAL] options (object) - enrichment options.
    • [OPTIONAL] debugging (boolean) - Flag to emit debugging logs.
    • [OPTIONAL] imports (array of string) - List of additional imports to include in the generated mapping file. You only need to include additional imports if you are using those types within your configuration.
  • [REQUIRED] handlers (map of object) - A map of trigger signatures to enrichment handler configurations (signature must be defined in the instance abi).
    • [OPTIONAL] calls (map of object) - A map of call reference names to eth call configurations. This can be omitted if mapping expressions do not require any eth calls.
    • [REQUIRED] entities (map of object) - A map of entity names to entity configurations.

Enrichment call configuration

Enrichment call configurations capture all information required to perform an eth call within the context of an existing event or call handler mapping function.

  • [OPTIONAL] abi (string) - The name of the abi defining the call to perform (if omitted then we’ll use the instance abi).
  • [OPTIONAL] source (string) - The contract address source expression to use for the call (if omitted then we’ll use the current instance source).
  • [REQUIRED] name (string) - The name of the eth call to perform. Note that this must be the exact name as defined in the ABI. The eth call invoker will actually call the try_<name> function to safely handle a potential revert and prevent any errors in the subgraph due to an invalid eth call. If the eth call is required then the subgraph will result in an error state.
  • [OPTIONAL] params (string) - The parameter expression to use when performing the eth call (this can be omitted if the eth call requires no parameters, and must include all parameters separated by commas otherwise). e.g., "event.params.owner, event.params.tokenId".
  • [OPTIONAL] depends_on (array of string) - List of call reference names that this call depends on (this should be used if a parameter is derived from a previously defined call).
  • [OPTIONAL] required (boolean) - Flag to indicate that the call must succeed for the enrichment to take place (if the call does not succeed then the enrichment is aborted and no enrichment entity mapping will take place).
  • [OPTIONAL] conditions (object) - Optional condition expressions to test before and after performing the call (if either condition fails then the enrichment is aborted and no enrichment entity mapping will take place).
    • [OPTIONAL] pre (string) - The condition to test before performing the call.
    • [OPTIONAL] post (string) - The condition to test after performing the call.

Enrichment entity configuration

Enrichement entity configurations are a map of field name and type to field value expressions. The configuration supports both a simplified single configuration and a multi-instance configuration. The single configuration is most likely all that is needed for most use cases, but if the need arises to describe an enriched entity where multiple instances are created within a single mapping (think of creating the same entity with different ids for the same event or call handler), then we can describe the entity as an array of configurations where each also includes an id expression for determining the unique id suffix.

  • An entity field mapping key looks like <field_name> <field_type>, e.g., tokenId uint256
    • the field name can be any valid GraphQL schema field identifier, typically this would either be a camelCase or snake_case string
    • the field type can be any valid ABI type name
  • An entity field mapping value is an expression, e.g., calls.owner.toHexString()
    • it must return a value of the type specified in the field mapping key (i.e., address must be converted to string using .toHexString())

When configuring an entity for multiple instances, the configuration takes the following form

  • [REQUIRED] id (string) - The expression to determine the unique id suffix for the entity instance.
  • [REQUIRED] mapping (map of object) - A map of field name and type to field value expressions (as described above).

Enrichment expressions

Enrichment expressions are AssemblyScript expressions that can be used to produce static or dynamic values based on the available runtime context. The expression runtime context includes the event object (or the call object for call handlers), the (parent) entity object, and the calls object reference to all previously executed eth calls. Expressions can include any combination of string concatenation, type transformation, math result, or logical branching, meaning that there is a lot of customization available to the configuration when declaring an expression. Note however that static expressions may often be the most appropriate for simple enrichments.

Below each of the runtime context elements are described in more detail:

  • event and call - The incoming event/call object to the mapping handler function. The parameters to this object will already be converted to the entity fields, one for each parameter defined in the corresponding ABI file.
  • entity - The parent entity object to the mapping handler function, this entity will have already been saved before enrichment begins.
  • calls - The object containing all previously executed eth calls. This object is used to reference the results of previous calls in the current call configuration. Calls not yet executed can still be referenced but they will be null until the call is invoked. Any calls that are marked required (or marked as a dependency of another call) will throw an error if accessed before the call is invoked.

Explanation of common patterns

Single source pattern

{
  "version": "1",
  "name": "TokenDeployed",
  "abis": {
    "TokenRegistry": {
      "path": "./path/to/your/abi.json"
    }
  },
  "instances": [
    {
      "abi": "TokenRegistry",
      "address": "0x...",
      "startBlock": 13983724,
      "chain": "your_chain"
    }
  ]
}
  • "version": "1": The version of this config, we only support a value of “1” right now.
  • "name": "TokenDeployed": The name of the event you want to track as specified in the ABI file.
  • "abis": { "TokenRegistry": { "path": "./path/to/your/abi.json" } }: Mapping of ABIs names (can be anything you want) to ABI files.
  • "abi": "TokenRegistry": The ABI you want to track. This name must match a key in the abis object above.
  • "address": "0x...",: The address of the contract.
  • "startBlock": 13983724: The block from which you want your subgraph to start indexing (in most cases, this is the block that deployed your contract)
  • "chain": "your_chain": The chain you want to track this contract on

Factory pattern

Some contracts create other child contracts, which then emit events that you need to track. The configuration here can handle that by allowing you specify a source inside an instance entry. The source tells the indexer which Factory contract event creates a new contract, and the address of the new contract as inferred from the event argument.

{
  "name": "TokenDeployed",
  "abis": {
    "Factory": {
      "path": "./abis/factory.json"
    },
    "Pool": {
      "path": "./abis/pool.json"
    }
  },
  "instances": [
    {
      "abi": "Factory",
      "address": "0xa98242820EBF3a405D265CCd22A4Ea8F64AFb281",
      "startBlock": 16748800,
      "chain": "bsc"
    },
    {
      "abi": "Pool",
      "source": {
        "abi": "Factory",
        "eventSignature": "PoolCreated(address,address,bool)",
        "addressParam": "pool"
      }
    }
  ]
}
  • "Factory": { "path": "./abis/factory.json" }: The path to the ABI for the Factory contract
  • "Pool": { "path": "./abis/pool.json" }: The path the ABI for the contract deployed by the Factory contract
  • { "abi": "Pool" }: This is the main difference between the configuration for factory vs non-factory applications. In this example, the Factory contract makes new Pool contracts and the below configuration specifies that with this source object.
  • "source": { "abi": "Factory" }: The ABI name which creates this contract.
  • "eventSignature": "PoolCreated(address,address,bool)",: This is the signature of the event from the Factory contract which indicates that this contract was created.
  • "addressParam": "pool": The name of the parameter from the Factory contract’s event that contains the new address to track.

In this pattern, there is a defined factory contract that makes many pools, and each pool needs to be tracked. We have two ABIs and the last instance entry looks for any PoolCreated event in the Factory ABI, gets a parameter from it, and uses that as a data source to watch for future Pool events in the Pool ABI.

Enrichment pattern

{
  "version": "1",
  "name": "TokenDeployed",
  "abis": {
    "TokenRegistry": {
      "path": "./path/to/your/abi.json"
    }
  },
  "instances": [
    {
      "abi": "TokenRegistry",
      "address": "0x...",
      "startBlock": 13983724,
      "chain": "your_chain"
      "enrich": {
        "Minted(address)": {
          "calls": {
            "balance": {
              "name": "balanceOf",
              "params": "event.params.owner"
            },
          },
          "entities": {
            "Balance": {
              "owner address": "event.params.owner.toHexString()",
              "balance uint256": "calls.balance"
            }
          }
        }
      }
    }
  ]
}
  • "Minted(address)": the event signature (as defined in the TokenRegistry ABI) to perform the enrichment within.
  • "balance": the name of the call reference.
  • "name": "balanceOf": the name of the eth call to perform.
  • "params": "event.params.owner": the parameter to pass to the balanceOf eth call. event represents the incoming event object to the Minted(address) mapping handler function.
  • "Balance": the new enrichment entity name to create.
  • "owner address": the first field name and type for the entity. In this case we would see Balance.owner defined as a String in the generated schema because the address type serializes to a String.
  • "event.params.owner.toHexString()": the expression to determine the value for the owner field. event represents the incoming event object to the Minted(address) mapping handler function. Since event.params.owner is an address type, we need to convert it to a String using the .toHexString() method.
  • "balance uint256": the second field name and type for the entity. In this case we would see Balance.balance defined as a BigInt in the generated schema.
  • "calls.balance": the expression to determine the value for the balance field. calls represents the object containing all previously executed eth calls and balance refers to our call reference name.

Examples

Multi-chain

This example shows how to define multiple chains with many addresses.

{
  "name": "TokenDeployed",
  "abis": {
    "TokenRegistry": {
      "path": "./abis/tokenRegistryAbi.json"
    }
  },
  "instances": [
    {
      "abi": "TokenRegistry",
      "address": "0x0A6f564C5c9BeBD66F1595f1B51D1F3de6Ef3b79",
      "startBlock": 13983724,
      "chain": "mainnet"
    },
    {
      "abi": "TokenRegistry",
      "address": "0x2d6775C1673d4cE55e1f827A0D53e62C43d1F304",
      "startBlock": 13718798,
      "chain": "avalanche"
    },
    {
      "abi": "TokenRegistry",
      "address": "0x10B84C73001745D969e7056D7ca474ce1D959FE8",
      "startBlock": 59533,
      "chain": "evmos"
    },
    {
      "abi": "TokenRegistry",
      "address": "0xa7E4Fea3c1468D6C1A3A77e21e6e43Daed855C1b",
      "startBlock": 171256,
      "chain": "moonbeam"
    },
    {
      "abi": "TokenRegistry",
      "address": "0x19d4b0F5871913c714554Bbb457F2a1549f52E04",
      "startBlock": 1356181,
      "chain": "milkomedac1"
    }
  ]
}

This configuration results in multiple deployed subgraphs, each with an identical GraphQL schema for you to fetch data. If you prefer a combined view of the data across all deployed subgraphs, please have a look at cross-chain subgraphs.

Nouns enrichment with balances on transfer

{
  "version": "1",
  "name": "nouns/1.0.0",
  "abis": {
    "nouns": {
      "path": "./abis/nouns.json"
    }
  },
  "instances": [
    {
      "abi": "nouns",
      "address": "0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03",
      "startBlock": 12985438,
      "chain": "mainnet",
      "enrich": {
        "handlers": {
          "Transfer(indexed address,indexed address,indexed uint256)": {
            "calls": {
              "nouns_balance": {
                "name": "balanceOf",
                "params": "event.params.to"
              }
            },
            "entities": {
              "EnrichmentBalance": {
                "tokenId uint256": "event.params.tokenId",
                "previousOwner address": "event.params.from.toHexString()",
                "owner address": "event.params.to.toHexString()",
                "nouns uint256": "calls.nouns_balance"
              }
            }
          }
        }
      }
    }
  ]
}

This configuration will create a new EnrichmentBalance entity that contains a nouns balance field for each Transfer event that occurs on the nouns contract. Transfer entities will automatically define an enrichmentBalances field that will yield an array of enrichment balances for each transfer event. Similarly, all EnrichmentBalance entities will define a transfer field that will yield the Transfer entity that triggered the enrichment. Below is an example GraphQL query to fetch transfers and enrichment balances in various ways.

query NounsTransfersAndBalancesDemo {
  enrichmentBalances(first:1, orderBy:timestamp_, orderDirection:desc) {
    id
    timestamp_
    tokenId
    previousOwner
    owner
    nouns
    transfer {
      id
      transactionHash_
    }
  }
  transfers(first:1, orderBy:timestamp_, orderDirection:desc) {
    id
    transactionHash_
    timestamp_
    tokenId
    from
    to
    enrichmentBalances {
      id
      nouns
    }
  }
}