Adapter

Your dApp or protocol needs adapters if you need to transform the raw on-chain data into meaningful information relevant to the protocol's core logic.

To have more control over the kind of data you want to collect, you can code an adapter that listens to on-chain events of your protocol. You need an adapter if you want to:

  • Calculate the total value locked (TVL) in your protocol,

  • Value extractions from the protocol,

  • Value contributions in the protocol,

  • Average protocol activity,

  • Average transactions per user,

  • And much more.

Overview

The adapter uses two approaches for transforming the raw on-chain data. The first are the transformers that track the contributions (USD value deposited in the protocol) and extraction (USD value withdrawn from protocol), and the second are the TVL extractors that are responsible to calculate the asset in terms of USD locked in a protocol.

Structure

Each adapter basically returns the following object.

type Adapter = {
  appKey: string;
  transformers?: Record<Chain, Array<Transformer>>;
  tvlExtractors?: Record<Chain, Array<TvlExtractor>>;
};

Basic adapter

Below you can see the basic example of an adapter, one that we have coded for Uniswap v3 on Ethereum. It basically marks add liquidity as contribution and remove liquidity as extraction.

projects/uniswap/index.ts
import { sumBalancesUSD } from "../../utils/sumBalances";
import { BurnEventObject, MintEventObject } from "./types/Pool";
import { pool, BURN, MINT, uniswapV3Pool, Label } from "./utils";
import { constants, types, utils } from "@spockanalytics/base";

export async function mintEvent(event: types.Event<MintEventObject>) {
  const pool = await uniswapV3Pool.getPool(event.address, event.chain);
  if (pool) {
    const [block, transaction] = await Promise.all([event.block, event.transaction]);
    const totalSum = await sumBalancesUSD(
      [
        { token: pool.token0, balance: event.params.amount0 },
        { token: pool.token1, balance: event.params.amount1 },
      ],
      event.chain,
      block.timestamp,
    );
    return utils.ProtocolValue.contribution({
      label: Label.DEPOSIT,
      value: parseFloat(totalSum.toString()),
      user: transaction.from,
    });
  }
}

export async function burnEvent(event: types.Event<BurnEventObject>) {
  const pool = await uniswapV3Pool.getPool(event.address, event.chain);
  if (pool) {
    const [block, transaction] = await Promise.all([event.block, event.transaction]);
    const totalSum = await sumBalancesUSD(
      [
        { token: pool.token0, balance: event.params.amount0 },
        { token: pool.token1, balance: event.params.amount1 },
      ],
      event.chain,
      block.timestamp,
    );
    return utils.ProtocolValue.extraction({
      label: Label.WITHDRAW,
      value: parseFloat(totalSum.toString()),
      user: transaction.from,
    });
  }
}

const uniswapAdapter: types.Adapter = {
  appKey: "70dbe55c4987d9ac9d84605d9edb8e6781bae2d631d649e176656e6bd3642fd9",
  transformers: {
    [constants.Chain.ETHEREUM]: [
      {
        contract: pool,
        eventHandlers: {
          [MINT]: mintEvent,
          [BURN]: burnEvent,
        },
        startBlock: 12369621,
      },
    ],
  },
};

export default uniswapAdapter;ja

In this example, we have one transformer for the Uniswap pool contract with two event handlers mint for contribution and burn for extraction.

FAQs

1. Which specific data does the adapter actually monitor?

The adapter collects data from the events that are responsible for contributions and extractions.

2. Is there a way to monitor data across multiple contracts with identical events?

Yes, if there are multiple contracts with the same event signatures like uniswap-v3 pools you can universally sync data by just removing the field of address in the transformer.

Also if you have maintained address mapping you can also pass it in the getAddresses field to sync data for all the specified addresses.

3. What is the process for verifying the functionality of event handling?

You can write tests in the file index.test.ts inside your project directory. In each test, you have to pass the transaction hash to verify that the logic is working as expected.

4. What is the process for performing a contract call in an adapter?

In order to read something from the contract you can use our built-in class.

Need Help? Contact Our Team

If you are stuck anywhere or have a suggestion or feedback, you can fill out the form here, or reach out to our team at support@spockanalytics.xyz.

Last updated