Writing new integrations

Writing an integration is a process that requires writing data that describes assets that use that integration and code to deal with fetching price and generating transactions to enter/exit a position. In general terms, these steps are fundamental:

  • Devising a name for the asset's type

  • Adding asset descriptions

  • Adding ABIs for all relevant contracts

  • Writing a concrete InterfaceStrategy for the integration

  • Writing end-to-end tests

Some optional actions:

  • Writing scripts to populate and update assets according to external sources

  • Writing unit tests for individual parts of the integration

Create a class for new asset type

The main element in order to create an integration supporting a new asset type is a class, namely a strategy to deal with all operations that involve that asset type.

The structure of this class is specified in InterfaceStrategy. The stub of an implementation looks like this:

import {
  FetchPriceDataParams,
  GetPriceParams,
  GenerateStepParams,
} from "./InterfaceStrategy";
import { InterfaceStrategy } from "./InterfaceStrategy";

export class MyNewDepositStrategy extends InterfaceStrategy {
  fetchPriceData({ provider, assetStore, asset }: FetchPriceDataParams) {
  }

  getPrice({ assetStore, asset, requestTree }: GetPriceParams) {
  }

  async generateStep({
    assetAllocation,
    assetStore,
    walletAddress,
    chainId,
    value,
    currentAllocation,
    routerOperation,
  }: GenerateStepParams) {
}

fetchPriceData

This function takes the following parameters:

The expected return is a RequestTree object that returns Promises for all data that needs to be fetched in order to calculate that asset's price.

Example #1: asset that depends on a linked asset

An asset that merely depends on the price data of the underlying asset can do so using fetchPriceData passing the same provider and assetStore, but specifying the linked asset.

In other words, this asset doesn't need to fetcch any data on its own, but delegates it down to its linked asset.

Example #2: asset that needs fetching some data

Another case is an asset that needs to fetch some data about itself in order to subsequently calculate the price.

In the above example. the requestTree will be resolved to get:

  • underlyingAmount from the pool (invoked using its balance function)

  • supply from the pool (invoked using its totalSupply function)

  • All requestData required by underlying asset.

getPrice

The getPrice function takes three parameters:

  • assetStore: AssetStore

  • asset: Asset

  • requestTree: RequestTree - with the resolved promises for all fetched assets.

The expected return is a number with the price.

Example #1: asset that depends on a linked asset

An asset that depends on the price data of the underlying asset just has to invoke and getPrice for that linked asset.

Example #2: asset that needed fetching data

If the getPrice requires using data that was requested on the previous step, this data will be available on the requestTree parameter, with the same key (generally asset.address) and key (whatever it was called on fetchPriceData).

generateStep

This step, the core of the transaction generation, is where information regarding the operation is processed to generate all the steps required. Each step is a call to a smart contract.

These are the steps that will typically be performed:

  • Get all assets relevant to the operation. This is done using the assetStore.

  • Determine which stores will be needed. This is done using routerOperation.stores.findOrInitializeStoreIdx.

  • Evaluate whether the position is being entered or exited. This is done by evaluating the value of assetAllocation.fraction.

  • Update the estimated allocation for all assets using currentAllocation.updateFraction.

  • Add steps for each smart contract interation required using routerOperation.addStep.

Example #1

Add assets

See adding assets

Last updated