diff --git a/src/lib/config.ts b/src/lib/config.ts index e00a1f8..16f6b8f 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -214,6 +214,7 @@ const defaultUnwrapConfig: RpcConfig['unwrap'] = { const defaultHarvestConfig: RpcConfig['harvest'] = { enabled: true, minTvlThresholdUsd: 100, + minClmTvlThresholdUsd: 100, parallelSimulations: 5, profitabilityCheck: { enabled: false, @@ -342,6 +343,7 @@ export const RPC_CONFIG: Record = { harvest: { ...defaultHarvestConfig, minTvlThresholdUsd: 10_000, + minClmTvlThresholdUsd: 1_000, profitabilityCheck: { ...defaultHarvestConfig.profitabilityCheck, enabled: true, diff --git a/src/lib/harvest-chain.ts b/src/lib/harvest-chain.ts index 7dc9161..e95a213 100644 --- a/src/lib/harvest-chain.ts +++ b/src/lib/harvest-chain.ts @@ -169,7 +169,19 @@ export async function harvestChain({ }; } - if (item.vault.tvlUsd < rpcConfig.harvest.minTvlThresholdUsd) { + // TVL checks + if (item.vault.isClmVault) { + // clm vaults have a dedicated threshold + if (item.vault.tvlUsd < rpcConfig.harvest.minClmTvlThresholdUsd) { + return { + shouldHarvest: false, + level: 'info', + tvlThresholdUsd: rpcConfig.harvest.minClmTvlThresholdUsd, + vaultTvlUsd: item.vault.tvlUsd, + notHarvestingReason: 'Tvl do not meet minimum threshold', + }; + } + } else if (item.vault.tvlUsd < rpcConfig.harvest.minTvlThresholdUsd) { // make sure to harvest CLMs at least once per 24 hours regardless if $100 or more is in them. if (!item.vault.isClmManager) { return { diff --git a/src/lib/rpc-config.ts b/src/lib/rpc-config.ts index efa9fc9..0d95232 100644 --- a/src/lib/rpc-config.ts +++ b/src/lib/rpc-config.ts @@ -71,6 +71,8 @@ export type RpcConfig = { // We only harvest if the vault tvl is above this threshold minTvlThresholdUsd: number; + // special threshold for clm + minClmTvlThresholdUsd: number; // wether we should set the transaction gas limit setTransactionGasLimit: boolean; diff --git a/src/lib/vault-list.ts b/src/lib/vault-list.ts index 679a5c7..1778b37 100644 --- a/src/lib/vault-list.ts +++ b/src/lib/vault-list.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { groupBy, mapValues, uniqBy } from 'lodash'; +import { groupBy, keyBy, mapValues, uniqBy } from 'lodash'; import type { Hex } from 'viem'; import { rootLogger } from '../util/logger'; import type { Chain } from './chain'; @@ -9,10 +9,12 @@ import type { BeefyVault, StrategyTypeId } from './vault'; const logger = rootLogger.child({ module: 'vault-list' }); async function fetchVaults(): Promise { - type ApiBeefyVaultResponse = { + type ApiBeefyVault = { id: string; name: string; status: 'eol' | 'active' | 'paused'; + tokenAddress?: string; // the want address + earnedTokenAddress?: string; // the vault address strategy: string; chain: Chain; platformId: string; @@ -20,7 +22,9 @@ async function fetchVaults(): Promise { type?: 'cowcentrated'; strategyTypeId: StrategyTypeId; // + some other fields we don't care about - }[]; + }; + + type ApiBeefyVaultResponse = ApiBeefyVault[]; type ApiBeefyTvlResponse = { [key: string]: { @@ -33,6 +37,13 @@ async function fetchVaults(): Promise { `${BEEFY_API_URL}/harvestable-vaults?_cache_buster=${Date.now()}` ); const rawVaults = vaultResponse.data; + const rawVaultsByAddress = keyBy( + rawVaults.filter(v => v.earnedTokenAddress), + v => `${v.chain}:${v.earnedTokenAddress?.toLocaleLowerCase()}` + ); + const getClmFromVault = (vault: ApiBeefyVault): ApiBeefyVault | null => { + return rawVaultsByAddress[`${vault.chain}:${vault.tokenAddress?.toLocaleLowerCase()}`] ?? null; + }; const tvlResponse = await axios.get(`${BEEFY_API_URL}/tvl?_cache_buster=${Date.now()}`); const rawTvlByChains = tvlResponse.data; @@ -41,6 +52,8 @@ async function fetchVaults(): Promise { // map to a simpler format return rawVaults.map(vault => { const isClmManager = vault.type === 'cowcentrated'; + const isClmVault = getClmFromVault(vault) !== null; + let tvlUsd = rawTvls[vault.id] || 0; if (ADD_RP_TVL_TO_CLM_TVL && isClmManager) { const rpVaultId = `${vault.id}-rp`; @@ -64,6 +77,7 @@ async function fetchVaults(): Promise { lastHarvest: new Date(vault.lastHarvest * 1000), strategyTypeId: vault.strategyTypeId || null, isClmManager, + isClmVault, }; }); } diff --git a/src/lib/vault.ts b/src/lib/vault.ts index 1e5bd5e..ee4eda2 100644 --- a/src/lib/vault.ts +++ b/src/lib/vault.ts @@ -31,4 +31,5 @@ export type BeefyVault = { lastHarvest: Date | null; strategyTypeId: StrategyTypeId | null; isClmManager: boolean; + isClmVault: boolean; };