Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

Commit

Permalink
fix(reaper): Fix vault definitions (#3122)
Browse files Browse the repository at this point in the history
  • Loading branch information
wpoulin authored Dec 11, 2023
1 parent 2ca00d3 commit ef9d1d9
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 93 deletions.
8 changes: 8 additions & 0 deletions src/apps/reaper/arbitrum/reaper.vault.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';

import { ReaperVaultTokenFetcher } from '../common/reaper.vault.token-fetcher';

@PositionTemplate()
export class ArbitrumReaperVaultTokenFetcher extends ReaperVaultTokenFetcher {
groupLabel = 'Vaults';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';

import { ReaperVaultTokenFetcher } from '../common/reaper.vault.token-fetcher';

@PositionTemplate()
export class BinanceSmartChainReaperVaultTokenFetcher extends ReaperVaultTokenFetcher {
groupLabel = 'Vaults';
}
117 changes: 34 additions & 83 deletions src/apps/reaper/common/reaper.vault.cache-manager.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,49 @@
import Axios from 'axios';
import { uniqBy } from 'lodash';

import { CacheOnInterval } from '~cache/cache-on-interval.decorator';
//import { Cache } from '~cache/cache.decorator';
import { Network } from '~types/network.interface';

type ReaperCryptsAPIData = {
type ReaperVaultData = {
data: {
_id: string;
provider: string;
cryptContent: {
tokens: { name: string; address: string }[];
name: string;
pid: number;
rfSymbol: string;
fees: {
depositFee: number;
withdrawFee: number;
interestFee: number;
[key: string]: {
address: string;
tokens: {
lpToken: {
address: string;
};
};
strategy: {
address: string;
abiName: string;
};
lpToken: {
address: string;
abiName: string;
};
vault: {
address: string;
abiName: string;
};
url: string;
dead: boolean;
promo: boolean;
};
emissionMultiplier: number;
analytics: {
assets: {
name: string;
address: string;
value: number;
}[];
tvl: number;
yields: {
hour: number;
day: number;
week: number;
month: number;
threeMonths: number;
sixMonths: number;
year: number;
};
};
__v: number;
}[];
};
};

export class ReaperVaultCacheManager {
async getCryptDefinitions(network: Network) {
if (network === Network.OPTIMISM_MAINNET) return this.getOptimismCryptDefinitions();
if (network === Network.FANTOM_OPERA_MAINNET) return this.getFantomCryptDefinitions();
throw new Error('Unsupported network');
}

@CacheOnInterval({
key: `studio:fantom:reaper:vault:addresses`,
timeout: 5 * 60 * 1000,
failOnMissingData: false,
})
private async getFantomCryptDefinitions() {
return this.fetchCryptDefinitions('https://yzo0r3ahok.execute-api.us-east-1.amazonaws.com/dev/api/crypts');
}
const NETWORK_CHAIN_ID_HEX: Partial<Record<Network, string>> = {
[Network.ARBITRUM_MAINNET]: '0xa4b1',
[Network.BINANCE_SMART_CHAIN_MAINNET]: '0x38',
[Network.FANTOM_OPERA_MAINNET]: '0xfa',
[Network.OPTIMISM_MAINNET]: '0xa',
};

@CacheOnInterval({
key: `studio:optimism:reaper:vault:addresses`,
timeout: 5 * 60 * 1000,
failOnMissingData: false,
})
private async getOptimismCryptDefinitions() {
return this.fetchCryptDefinitions('https://yzo0r3ahok.execute-api.us-east-1.amazonaws.com/dev/api/optimism/crypts');
export class ReaperVaultCacheManager {
/*@Cache({
key: (network: Network) => `studio:reaper:${network}:farms`,
ttl: 15 * 60, // 60 minutes
})*/
private async getVaultData(network: Network) {
const chainIdHex = NETWORK_CHAIN_ID_HEX[network];
const url = `https://2ch9hbg8hh.execute-api.us-east-1.amazonaws.com/dev/api/vaults/${chainIdHex}`;

const { data } = await Axios.get<ReaperVaultData>(url);
return data;
}

private async fetchCryptDefinitions(apiUrl: string) {
const { data } = await Axios.get<ReaperCryptsAPIData>(apiUrl);

const cryptDefinitions = data.data
.filter(v => !v.cryptContent.dead)
.map(v => ({
name: v.cryptContent.name,
address: v.cryptContent.vault.address.toLowerCase(),
underlyingAddress: v.cryptContent.lpToken.address.toLowerCase(),
apy: v.analytics.yields.year,
}));
async fetchVaults(network: Network) {
const vaultData = await this.getVaultData(network);

return uniqBy(cryptDefinitions, v => v.address);
return Object.values(vaultData.data).map(vault => {
return {
address: vault.address.toLowerCase(),
underlyingTokenAddress: vault.tokens.lpToken.address.toLowerCase(),
};
});
}
}
18 changes: 8 additions & 10 deletions src/apps/reaper/common/reaper.vault.token-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@ import { ReaperCrypt } from '../contracts/viem';
import { ReaperVaultCacheManager } from './reaper.vault.cache-manager';

type ReaperVaultDefinition = {
name: string;
address: string;
underlyingAddress: string;
apy: number;
underlyingTokenAddress: string;
};

export abstract class ReaperVaultTokenFetcher extends AppTokenTemplatePositionFetcher<
ReaperCrypt,
DefaultAppTokenDataProps,
ReaperVaultDefinition
> {
export abstract class ReaperVaultTokenFetcher extends AppTokenTemplatePositionFetcher<ReaperCrypt> {
VaultAddressesNotCompatibleErc20: string[] = [];

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(ReaperViemContractFactory) protected readonly contractFactory: ReaperViemContractFactory,
Expand All @@ -41,15 +37,17 @@ export abstract class ReaperVaultTokenFetcher extends AppTokenTemplatePositionFe
}

async getDefinitions() {
return this.cacheManager.getCryptDefinitions(this.network);
const vaultDefinitions = await this.cacheManager.fetchVaults(this.network);

return vaultDefinitions.filter(v => !this.VaultAddressesNotCompatibleErc20.includes(v.address));
}

async getAddresses({ definitions }: GetAddressesParams) {
return definitions.map(v => v.address);
}

async getUnderlyingTokenDefinitions({ definition }: GetUnderlyingTokensParams<ReaperCrypt, ReaperVaultDefinition>) {
return [{ address: definition.underlyingAddress, network: this.network }];
return [{ address: definition.underlyingTokenAddress, network: this.network }];
}

async getPricePerShare({
Expand Down
2 changes: 2 additions & 0 deletions src/apps/reaper/optimism/reaper.vault.token-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import { ReaperVaultTokenFetcher } from '../common/reaper.vault.token-fetcher';
@PositionTemplate()
export class OptimismReaperVaultTokenFetcher extends ReaperVaultTokenFetcher {
groupLabel = 'Vaults';

VaultAddressesNotCompatibleErc20 = ['0x85a21d07a3deefe58ecd841198d7f774e6444654'];
}
4 changes: 4 additions & 0 deletions src/apps/reaper/reaper.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Module } from '@nestjs/common';

import { AbstractApp } from '~app/app.dynamic-module';

import { ArbitrumReaperVaultTokenFetcher } from './arbitrum/reaper.vault.token-fetcher';
import { BinanceSmartChainReaperVaultTokenFetcher } from './binance-smart-chain/reaper.vault.token-fetcher';
import { ReaperVaultCacheManager } from './common/reaper.vault.cache-manager';
import { ReaperViemContractFactory } from './contracts';
import { FantomReaperVaultTokenFetcher } from './fantom/reaper.vault.token-fetcher';
Expand All @@ -11,6 +13,8 @@ import { OptimismReaperVaultTokenFetcher } from './optimism/reaper.vault.token-f
providers: [
ReaperViemContractFactory,
ReaperVaultCacheManager,
ArbitrumReaperVaultTokenFetcher,
BinanceSmartChainReaperVaultTokenFetcher,
FantomReaperVaultTokenFetcher,
OptimismReaperVaultTokenFetcher,
],
Expand Down

0 comments on commit ef9d1d9

Please sign in to comment.