Onboarding a New Data Provider

Add a new blockchain data provider to the Backpack API

The new GraphQL API that powers Backpack (currently in the middle of migration) revolves around a single interface, or common API, for all blockchain and wallet data providers. This allows the onboarding additional data providers to remain as silo-ed as possible while remaining easy to maintain and extend in the future.

While this API is constantly evolving, the aim is provide the most simplistic, yet source agnostic, interface for developer to implement for their blockchain or wallet data provider and be supported in Backpack as quickly as possible.

Any code snippets below are up-to-date at the time of publishing, but may be subject to change with future additions and iterations. When contributing to the API, please be mindful of the organization patterns that exist. They are likely there for a reason.

Checklist

1. Create a New GraphQL variant

The GraphQL schema file (schema.graphql) contains the definition of the interface for the entire API, as well as caching and privacy control measures.

Within the schema, there is a definition for enum ProviderID which contains the variants for all registered blockchain and/or wallet data providers that can be used for querying on-chain data for a wallet address(es).

In order to add a new one, simply navigate to the file (linked above) and type in your new variant. For example, if I were to onboard a new data provider for "Gigachain", I would make the following update:

enum ProviderID {
  BITCOIN
  ECLIPSE
  ETHEREUM
  GIGACHAIN # <-- my new variant
  POLYGON
  SOLANA
}

Updating Generated Type Definitions

The Backpack repository utilizes the @graphql-codegen/typescript package to generate type definitions from the GraphQL schema file itself and inject them where needed in the mono-repo. Since this variant update will fundamental alter the typing of the ProviderID enum, the script must be run to update the generated types.

In the root of the Backpack repository, run:

$ yarn gql

This find the local schema file and re-generate the types in all relevant packages and directories.

2. Switch Cases for the New Provider

After re-generating the types for the schema update, you may notice that the root provider file is now failing its type-check.

All you need to do is add a new switch case to the getProviderForId and inferProviderIdFromString functions for your new variant (following the casing conventions). This will resolve the type-check failures as the switch blocks should now be extensive over all known enum variants.

import { Gigachain } from "./gigachain"

case ProviderId.Gigachain: {
  return new Gigachain({ context })
}
// ...and...
case "gigachain": {
  return ProviderId.Gigachain
}

3. Implement the Common Provider Interface

You'll notice that in the providers directory there is a file for each data provider type containing an exported class. This is where you will make your new file and class for your data provider.

// providers/gigachain.ts
export class Gigachain implements BlockchainDataProvider {
    // ...
}

If your new data provider is an L2(+) or some abstraction of on-chain data, you will likely want to extend the implementation of an existing L1 provider and override where necessary. (See the Polygon data provider as an example)

// providers/gigachain.ts
export class Gigachain extends SolanaRpc implements BlockchainDataProvider {
    // ...
}

The common interface defines functions that require provider self-identification metadata and operational functions to get balances, NFTs, and transaction history for the argued wallet address.

4. New Token Curation List

In order to reduce the load and real-time reliance on CoinGecko and to provide a more extensible token registry with quicker turn-over, we house custom token curation lists for each of the data providers.

When adding a new data provider, the need for a new token curation list is reliant on 2 things:

  1. Is the new provider an L1? (needs a new token list)

  2. Are the tokens and addresses consistent with the underlying L1 of my L2 or that I am abstracting?

If the answer to #2 is "yes", then you can simply reuse the token curation list of the existing L1 and there is no need to create an additional one and you can skip to the next section. If the answer was "no", then you will need create a new list an link it to the appropriate systems.

// packages/common/src/tokens/gigachain.ts
import type { CustomTokenList, TokenListEntry } from "./types"

export const GigachainTokenList: CustomTokenList = {
    "native": {
        // ...required token metadata
    }
    [mintOrContractAddress: string]: TokenListEntry
}

// ...and...

// packages/common/src/tokens/index.ts
export { GigachainTokenList } from "./gigachain"

Once this is done, you may need to build the package for the changes to propagate to the rest of the mono-repo packages:

~/backpack/packages/common
$ yarn build

5. Update the Price Indexer

If you did not need to create a new token curation list determined by the previous step, you can skip this.

The repository contains a Cloudflare worker that operates on a CRON schedule to index and cache CoinGecko prices and market data for each of the tokens in the curation lists to reduce load and request duplication from the core API.

Simply add your new token list to the handling list in the scheduled.ts file so induce it in the indexing:

// backend/workers/price-indexer/src/schedules.ts
import { GigachainTokenList } from "@coral-xyz/common"

const mergedTokenLists = [
    BitcoinToken,
    ...Object.values(EclipseTokenList),
    ...Object.values(EthereumTokenList),
    ...Object.values(GigachainTokenList),
    ...Object.values(PolygonTokenList),
    ...Object.values(SolanaTokenList),
];

Once your pull-request is approved and merged, the Backpack team will deploy the updates to the worker to being indexing your new token list market data for the API to query.

Last updated