Skip to content

Commit

Permalink
Merge pull request #247 from prevostc/add-beefy-rewardpool-support
Browse files Browse the repository at this point in the history
beefy : yield : add support for reward pool staking
  • Loading branch information
0xroll authored Jul 5, 2024
2 parents b90975a + 1a37bf3 commit b39eede
Show file tree
Hide file tree
Showing 6 changed files with 4,351 additions and 2,228 deletions.
3 changes: 2 additions & 1 deletion adapters/beefy/hourly_blocks.csv
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ number,timestamp
4730000,1716233771
4784763,1716398064
5684890,1718771519
5784890,1718972096
5784890,1718972096
6301186,1720005441
1 change: 1 addition & 0 deletions adapters/beefy/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const BEEFY_VAULT_API = "https://api.beefy.finance/harvestable-vaults";
export const BEEFY_GOV_API = "https://api.beefy.finance/gov-vaults";

// subgraph source: https://github.com/beefyfinance/l2-lxp-liquidity-subgraph
export const BEEFY_SUBGRAPH_URL =
Expand Down
57 changes: 48 additions & 9 deletions adapters/beefy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,42 @@ export const getUserTVLByBlock = async (
getTokenBalances(BigInt(blockNumber)),
]);

// merge investor positions for clm and reward pools
const vaultRewardPoolMap: Record<string, string> = {};
for (const vault of vaultConfigs) {
vaultRewardPoolMap[vault.vault_address] = vault.vault_address;
for (const pool of vault.reward_pools) {
vaultRewardPoolMap[pool.reward_pool_address] = vault.vault_address;
}
}

const mergedInvestorPositionsByInvestorAndClmAddress: Record<
string,
(typeof investorPositions)[0]
> = {};
for (const position of investorPositions) {
const vaultAddress = vaultRewardPoolMap[position.token_address];
const key = `${position.user_address}_${vaultAddress}`;
if (!mergedInvestorPositionsByInvestorAndClmAddress[key]) {
mergedInvestorPositionsByInvestorAndClmAddress[key] = position;
} else {
mergedInvestorPositionsByInvestorAndClmAddress[key].balance +=
position.balance;
}
}
const mergedPositions = Object.values(
mergedInvestorPositionsByInvestorAndClmAddress
);

const vaultAddressWithActivePosition = uniq(
investorPositions.map((pos) => pos.token_address)
);
const vaults = vaultConfigs.filter((vault) =>
vaultAddressWithActivePosition.includes(vault.vault_address)
const vaults = vaultConfigs.filter(
(vault) =>
vaultAddressWithActivePosition.includes(vault.vault_address) ||
vault.reward_pools.some((pool) =>
vaultAddressWithActivePosition.includes(pool.reward_pool_address)
)
);
// get breakdowns for all vaults
const breakdowns = await getVaultBreakdowns(BigInt(blockNumber), vaults);
Expand All @@ -52,7 +83,7 @@ export const getUserTVLByBlock = async (
Hex /* investor */,
Record<Hex /* token */, bigint /* amount */>
> = {};
for (const position of investorPositions) {
for (const position of mergedPositions) {
const breakdown = breakdownByVaultAddress[position.token_address];
if (!breakdown) {
// some test vaults were never available in the api
Expand All @@ -68,14 +99,22 @@ export const getUserTVLByBlock = async (
investorTokenBalances[position.user_address] = {};
}

for (const balance of breakdown.balances) {
if (!investorTokenBalances[position.user_address][balance.tokenAddress]) {
investorTokenBalances[position.user_address][balance.tokenAddress] =
BigInt(0);
for (const breakdownBalance of breakdown.balances) {
if (
!investorTokenBalances[position.user_address][
breakdownBalance.tokenAddress
]
) {
investorTokenBalances[position.user_address][
breakdownBalance.tokenAddress
] = BigInt(0);
}

investorTokenBalances[position.user_address][balance.tokenAddress] +=
(position.balance * balance.vaultBalance) / breakdown.vaultTotalSupply;
investorTokenBalances[position.user_address][
breakdownBalance.tokenAddress
] +=
(position.balance * breakdownBalance.vaultBalance) /
breakdown.vaultTotalSupply;
}
}

Expand Down
63 changes: 58 additions & 5 deletions adapters/beefy/src/sdk/vault/getBeefyVaultConfig.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { memoize } from "lodash";
import { Hex } from "viem";
import { BEEFY_VAULT_API } from "../../config";
import { BEEFY_GOV_API, BEEFY_VAULT_API } from "../../config";

export type BeefyVault = {
id: string;
vault_address: Hex;
undelying_lp_address: Hex;
strategy_address: Hex;
vault_token_symbol: string;
chain: string;
protocol_type: BeefyProtocolType;
reward_pools: BeefyRewardPool[];
};

export type BeefyRewardPool = {
id: string;
clm_address: Hex;
reward_pool_address: Hex;
};

export type BeefyProtocolType =
Expand All @@ -18,47 +26,92 @@ export type BeefyProtocolType =
| "solidly"
| "beefy_clm";

type ApiPlatformId = "gamma" | "ichi" | "lynex" | "mendi" | "nile" | "beefy"; // and more but we don't use those on linea
type ApiPlatformId =
| "gamma"
| "ichi"
| "lynex"
| "mendi"
| "nile"
| "velodrome"
| "beefy"; // and more but we don't use those on linea

type ApiVault = {
id: string;
status: "active" | "eol";
earnedTokenAddress: Hex;
chain: string;
earnedToken: string; // cow token symbol
platformId: ApiPlatformId;
tokenAddress: Hex;
strategy: Hex;
};

type ApiGovVault = {
id: string;
status: "active" | "eol";
version: number;
chain: string;
tokenAddress: Hex; // clm address
earnContractAddress: Hex; // reward pool address
};

const protocol_map: Record<ApiPlatformId, BeefyProtocolType> = {
gamma: "gamma",
ichi: "ichi",
lynex: "solidly",
mendi: "mendi",
nile: "solidly",
velodrome: "solidly",
beefy: "beefy_clm",
};

export const getBeefyVaultConfig = memoize(
async (chain: string): Promise<BeefyVault[]> => {
const response = await fetch(BEEFY_VAULT_API);
const data = await response.json();
const [vaultsData, rewardPoolData] = await Promise.all([
fetch(BEEFY_VAULT_API).then((res) => res.json()),
fetch(BEEFY_GOV_API).then((res) => res.json()),
]);

const vaults = data
const rewardPoolsPerClm = rewardPoolData
.filter((pool: ApiGovVault) => pool.status === "active")
.filter((pool: ApiGovVault) => pool.version === 2)
.filter((pool: ApiGovVault) => pool.chain === chain)
.reduce((acc: Record<string, BeefyRewardPool[]>, pool: ApiGovVault) => {
const clm_address = pool.tokenAddress.toLocaleLowerCase() as Hex;
const reward_pool_address =
pool.earnContractAddress.toLocaleLowerCase() as Hex;
if (!acc[clm_address]) {
acc[clm_address] = [];
}
acc[clm_address].push({
id: pool.id,
clm_address,
reward_pool_address,
});
return acc;
}, {} as Record<string, BeefyRewardPool[]>);

const vaults = vaultsData
.filter((vault: ApiVault) => vault.chain === chain)
.filter((vault: ApiVault) => vault.status === "active")
.map((vault: ApiVault): BeefyVault => {
let protocol_type = protocol_map[vault.platformId];
if (!protocol_type) {
throw new Error(`Unknown platformId ${vault.platformId}`);
}

let reward_pools =
rewardPoolsPerClm[vault.earnedTokenAddress.toLocaleLowerCase()] ?? [];

return {
id: vault.id,
vault_address: vault.earnedTokenAddress.toLocaleLowerCase() as Hex,
chain: vault.chain,
vault_token_symbol: vault.earnedToken,
protocol_type,
strategy_address: vault.strategy.toLocaleLowerCase() as Hex,
undelying_lp_address: vault.tokenAddress.toLocaleLowerCase() as Hex,
reward_pools,
};
});

Expand Down
2 changes: 1 addition & 1 deletion adapters/beefy/test/sample_hourly_blocks.csv
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
number,timestamp
4784763,1716398064
6301186,1720005441
Loading

0 comments on commit b39eede

Please sign in to comment.