Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: add protocol addition doc #34

Merged
merged 3 commits into from
Jun 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
333 changes: 333 additions & 0 deletions docs/add_new_protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
# Add New Protocol

This documentation provides a guide on how to create a store for a protocol, which implements the IDapp class from `IDapp.store.ts`. The store fetches, processes, and manages APR and pool data for a specific decentralized application (dApp).

## Prerequisites

Before starting, ensure you have the following:

- A basic understanding of TypeScript and object-oriented programming.
- Familiarity with React and state management using Jotai.
- Knowledge of how to fetch and handle data from APIs.

## Steps to Create a Store

1. Define the Protocol Class

Create a new file for your protocol, e.g., `myprotocol.store.ts` in `src/store`. Import necessary modules and extend the IDapp class.

```TypeScript
'use client';

import CONSTANTS, { TOKENS, TokenName } from '@/constants';
import {
APRSplit,
Category,
PoolInfo,
PoolMetadata,
PoolType,
ProtocolAtoms,
} from './pools';
import { atom } from 'jotai';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';
import { TokenInfo } from '@/strategies/IStrategy';
import { IDapp } from './IDapp.store';
const fetcher = (...args: any[]) => {
return fetch(args[0], args[1]).then((res) => res.json());
};

const POOL_NAMES: string[] = ['STRK/USDC', 'STRK/ETH', 'ETH/USDC', 'USDC/USDT'];

export class MyProtocol extends IDapp<BaseAPYT> {}
```

2. Implement Function to Compute Pools Info

Add a method to compute pool information within your protocol class.

```TypeScript
export class MyProtocol extends IDapp<BaseAPYT> {
_computePoolsInfo(data: any) {
try {
const myData = data[this.incentiveDataKey];
if (!myData) return [];
const pools: PoolInfo[] = [];

Object.keys(myData)
.filter(this.commonVaultFilter)
.forEach((poolName) => {
const arr = myData[poolName];
let category = Category.Others;
if (poolName === 'USDC/USDT') {
category = Category.Stable;
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

const tokens: TokenName[] = poolName.split('/');
const logo1 = CONSTANTS.LOGOS[tokens[0]];
const logo2 = CONSTANTS.LOGOS[tokens[1]];

const poolInfo: PoolInfo = {
pool: {
name: poolName,
logos: [logo1, logo2],
},
protocol: {
name: this.name,
link: this.link,
logo: this.logo,
},
apr: arr[arr.length - 1].apr,
tvl: arr[arr.length - 1].tvl_usd,
aprSplits: [
{
apr: arr[arr.length - 1].apr,
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
type: PoolType.DEXV3,
lending: {
collateralFactor: 0,
},
borrow: {
borrowFactor: 0,
apr: 0,
},
};
pools.push(poolInfo);
});

return pools;
} catch (err) {
throw err;
}
}

commonVaultFilter(poolName: string) {
const supportedPools = [
'ETH/USDC',
'STRK/USDC',
'STRK/ETH',
'USDC/USDT',
'USDC',
'USDT',
'ETH',
'STRK',
];
return supportedPools.includes(poolName);
}
}
```

3. Implement Function to Calculate Base APRs

Add a method to calculate the base APRs.

```TypeScript
export class MyProtocol extends IDapp<BaseAPYT> {
// previous code ...

getBaseAPY(p: PoolInfo, data: AtomWithQueryResult<BaseAPYT, Error>) {
// logic to calculate the base APRs for the pools in the protocol you're adding goes here.

// base APR is calculated by:
const baseAPR = 365 * ((fees0 + fees1) / (tvl0 + tvl1));

/**
* where:
* fees0 = fees for base token
* fees1 = fees for quote token
* tvl0 = total volume locked for base token
* tvl1 = total volume locked for quote token
*/


// see getBaseAPY() IDapp.store.ts for how the data is returned.
}
}
```

4. Instantiate Protocol class

```TypeScript
export const myProtocol = new MyProtocol();
```

5. Set Up Jotai Atoms

Set up Jotai atoms to manage the state and data fetching for the protocol.

```TypeScript
const MyProtocolAtoms: ProtocolAtoms = {
baseAPRs: atomWithQuery((get) => ({
queryKey: ['myprotocol_base_aprs'],
queryFn: async ({ queryKey }) => {
// logic to fetch pools data from the protocol's APIs goes here
// These data is used to calculate the base APRs for the pools
})),
pools: atom((get) => {
const poolsInfo = get(StrkDexIncentivesAtom);
const empty: PoolInfo[] = [];
if (!MyProtocolAtoms.baseAPRs) return empty;
const baseInfo = get(MyProtocolAtoms.baseAPRs);
if (poolsInfo.data) {
const pools = myProtocol._computePoolsInfo(poolsInfo.data);
return myProtocol.addBaseAPYs(pools, baseInfo);
}
return empty;
}),
};
```

5. Export Protocol Atoms

```TypeScript
export default MyProtocolAtoms;
```

## Complete code

```TypeScript
'use client';

import CONSTANTS, { TOKENS, TokenName } from '@/constants';
import {
APRSplit,
Category,
PoolInfo,
PoolMetadata,
PoolType,
ProtocolAtoms,
} from './pools';
import { atom } from 'jotai';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';
import { TokenInfo } from '@/strategies/IStrategy';
import { IDapp } from './IDapp.store';
const fetcher = (...args: any[]) => {
return fetch(args[0], args[1]).then((res) => res.json());
};

const POOL_NAMES: string[] = ['STRK/USDC', 'STRK/ETH', 'ETH/USDC', 'USDC/USDT'];

export class MyProtocol extends IDapp<BaseAPYT> {
_computePoolsInfo(data: any) {
try {
const myData = data[this.incentiveDataKey];
if (!myData) return [];
const pools: PoolInfo[] = [];

Object.keys(myData)
.filter(this.commonVaultFilter)
.forEach((poolName) => {
const arr = myData[poolName];
let category = Category.Others;
if (poolName === 'USDC/USDT') {
category = Category.Stable;
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

const tokens: TokenName[] = poolName.split('/');
const logo1 = CONSTANTS.LOGOS[tokens[0]];
const logo2 = CONSTANTS.LOGOS[tokens[1]];

const poolInfo: PoolInfo = {
pool: {
name: poolName,
logos: [logo1, logo2],
},
protocol: {
name: this.name,
link: this.link,
logo: this.logo,
},
apr: arr[arr.length - 1].apr,
tvl: arr[arr.length - 1].tvl_usd,
aprSplits: [
{
apr: arr[arr.length - 1].apr,
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
type: PoolType.DEXV3,
lending: {
collateralFactor: 0,
},
borrow: {
borrowFactor: 0,
apr: 0,
},
};
pools.push(poolInfo);
});

return pools;
} catch (err) {
throw err;
}
}

commonVaultFilter(poolName: string) {
const supportedPools = [
'ETH/USDC',
'STRK/USDC',
'STRK/ETH',
'USDC/USDT',
'USDC',
'USDT',
'ETH',
'STRK',
];
return supportedPools.includes(poolName);
}

getBaseAPY(p: PoolInfo, data: AtomWithQueryResult<BaseAPYT, Error>) {
}
}

export const myProtocol = new MyProtocol();

const MyProtocolAtoms: ProtocolAtoms = {
baseAPRs: atomWithQuery((get) => ({
queryKey: ['myprotocol_base_aprs'],
queryFn: async ({ queryKey }) => {
})),
pools: atom((get) => {
const poolsInfo = get(StrkDexIncentivesAtom);
const empty: PoolInfo[] = [];
if (!MyProtocolAtoms.baseAPRs) return empty;
const baseInfo = get(MyProtocolAtoms.baseAPRs);
if (poolsInfo.data) {
const pools = myProtocol._computePoolsInfo(poolsInfo.data);
return myProtocol.addBaseAPYs(pools, baseInfo);
}
return empty;
}),
};

export default MyProtocolAtoms;
```

6. Import Protocol in `src/store/pools.ts`

```TypeScript
import MyProtocolAtoms, { myProtocol } from './myprotocol.store';
```

7. Add Protocol to `PROTOCOLS` array `src/store/pools.ts`

```TypeScript
export const PROTOCOLS = [
// other protocols...
{
name: myprotocol.name,
class: myprotocol,
atoms: MyProtocolAtoms,
}
]
```
Loading