From 853074ab6a8b6adaaef5513919744b8f8191d20e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Pr=C3=A9vost?=
<998369+prevostc@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:32:51 +0200
Subject: [PATCH 1/2] Add beefy CLM
---
adapters/beefy/src/abi/BeefyClmStrategy.ts | 19 ++++++
adapters/beefy/src/abi/BeefyVaultConcLiq.ts | 29 ++++++++++
adapters/beefy/src/config.ts | 4 +-
adapters/beefy/src/index.ts | 15 +++--
.../src/sdk/breakdown/getVaultBreakdown.ts | 2 +
.../sdk/breakdown/protocol_type/beefy_clm.ts | 52 +++++++++++++++++
.../src/sdk/breakdown/protocol_type/gamma.ts | 1 +
.../src/sdk/breakdown/protocol_type/ichi.ts | 1 +
.../src/sdk/breakdown/protocol_type/mendi.ts | 1 +
.../sdk/breakdown/protocol_type/solidly.ts | 1 +
adapters/beefy/src/sdk/breakdown/types.ts | 1 +
.../src/sdk/vault/getBeefyVaultConfig.ts | 13 ++++-
...reTokenBalances.ts => getTokenBalances.ts} | 58 ++++++++-----------
13 files changed, 153 insertions(+), 44 deletions(-)
create mode 100644 adapters/beefy/src/abi/BeefyClmStrategy.ts
create mode 100644 adapters/beefy/src/abi/BeefyVaultConcLiq.ts
create mode 100644 adapters/beefy/src/sdk/breakdown/protocol_type/beefy_clm.ts
rename adapters/beefy/src/sdk/vault/{getVaultShareTokenBalances.ts => getTokenBalances.ts} (55%)
diff --git a/adapters/beefy/src/abi/BeefyClmStrategy.ts b/adapters/beefy/src/abi/BeefyClmStrategy.ts
new file mode 100644
index 00000000..76b4abac
--- /dev/null
+++ b/adapters/beefy/src/abi/BeefyClmStrategy.ts
@@ -0,0 +1,19 @@
+export const BeefyClmStrategyAbi = [
+ {
+ type: "function",
+ name: "price",
+ inputs: [],
+ outputs: [{ name: "_price", type: "uint256", internalType: "uint256" }],
+ stateMutability: "view",
+ },
+ {
+ type: "function",
+ name: "range",
+ inputs: [],
+ outputs: [
+ { name: "lowerPrice", type: "uint256", internalType: "uint256" },
+ { name: "upperPrice", type: "uint256", internalType: "uint256" },
+ ],
+ stateMutability: "view",
+ },
+] as const;
diff --git a/adapters/beefy/src/abi/BeefyVaultConcLiq.ts b/adapters/beefy/src/abi/BeefyVaultConcLiq.ts
new file mode 100644
index 00000000..a027d24b
--- /dev/null
+++ b/adapters/beefy/src/abi/BeefyVaultConcLiq.ts
@@ -0,0 +1,29 @@
+export const BeefyVaultConcLiqAbi = [
+ {
+ inputs: [],
+ name: "balances",
+ outputs: [
+ { internalType: "uint256", name: "amount0", type: "uint256" },
+ { internalType: "uint256", name: "amount1", type: "uint256" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "wants",
+ outputs: [
+ { internalType: "address", name: "token0", type: "address" },
+ { internalType: "address", name: "token1", type: "address" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+] as const;
diff --git a/adapters/beefy/src/config.ts b/adapters/beefy/src/config.ts
index 281e67e3..210b37bd 100644
--- a/adapters/beefy/src/config.ts
+++ b/adapters/beefy/src/config.ts
@@ -1,8 +1,8 @@
-export const BEEFY_VAULT_API = "https://api.beefy.finance/vaults";
+export const BEEFY_VAULT_API = "https://api.beefy.finance/harvestable-vaults";
// subgraph source: https://github.com/beefyfinance/l2-lxp-liquidity-subgraph
export const BEEFY_SUBGRAPH_URL =
- "https://api.goldsky.com/api/public/project_clu2walwem1qm01w40v3yhw1f/subgraphs/beefy-l2-lxp-liquidity-linea/latest/gn";
+ "https://api.goldsky.com/api/public/project_clu2walwem1qm01w40v3yhw1f/subgraphs/beefy-balances-linea/latest/gn";
export const SUBGRAPH_PAGE_SIZE = 1000;
diff --git a/adapters/beefy/src/index.ts b/adapters/beefy/src/index.ts
index f8b44ec5..72d1667d 100644
--- a/adapters/beefy/src/index.ts
+++ b/adapters/beefy/src/index.ts
@@ -6,7 +6,7 @@ import { getVaultBreakdowns } from "./sdk/breakdown/getVaultBreakdown";
import { uniq } from "lodash";
import { BeefyVaultBreakdown } from "./sdk/breakdown/types";
import { Hex } from "viem";
-import { getVaultShareTokenBalances } from "./sdk/vault/getVaultShareTokenBalances";
+import { getTokenBalances } from "./sdk/vault/getTokenBalances";
interface BlockData {
blockNumber: number;
@@ -30,11 +30,11 @@ export const getUserTVLByBlock = async (
const [vaultConfigs, investorPositions] = await Promise.all([
getBeefyVaultConfig("linea"),
- getVaultShareTokenBalances(BigInt(blockNumber)),
+ getTokenBalances(BigInt(blockNumber)),
]);
const vaultAddressWithActivePosition = uniq(
- investorPositions.map((pos) => pos.vault_address.toLowerCase())
+ investorPositions.map((pos) => pos.token_address)
);
const vaults = vaultConfigs.filter((vault) =>
vaultAddressWithActivePosition.includes(vault.vault_address)
@@ -53,12 +53,17 @@ export const getUserTVLByBlock = async (
Record
> = {};
for (const position of investorPositions) {
- const breakdown = breakdownByVaultAddress[position.vault_address];
+ const breakdown = breakdownByVaultAddress[position.token_address];
if (!breakdown) {
// some test vaults were never available in the api
continue;
}
+ if (breakdown.isLiquidityEligible === false) {
+ // skip non-eligible vaults
+ continue;
+ }
+
if (!investorTokenBalances[position.user_address]) {
investorTokenBalances[position.user_address] = {};
}
@@ -70,7 +75,7 @@ export const getUserTVLByBlock = async (
}
investorTokenBalances[position.user_address][balance.tokenAddress] +=
- (BigInt(position.shares_balance) * balance.vaultBalance) /
+ (BigInt(position.token_address) * balance.vaultBalance) /
breakdown.vaultTotalSupply;
}
}
diff --git a/adapters/beefy/src/sdk/breakdown/getVaultBreakdown.ts b/adapters/beefy/src/sdk/breakdown/getVaultBreakdown.ts
index c0d53538..bbc55697 100644
--- a/adapters/beefy/src/sdk/breakdown/getVaultBreakdown.ts
+++ b/adapters/beefy/src/sdk/breakdown/getVaultBreakdown.ts
@@ -4,6 +4,7 @@ import { BeefyViemClient, getViemClient } from "../viemClient";
import { getSolidlyVaultBreakdown } from "./protocol_type/solidly";
import { getGammaVaultBreakdown } from "./protocol_type/gamma";
import { getMendiVaultBreakdown } from "./protocol_type/mendi";
+import { getBeefyClmVaultBreakdown } from "./protocol_type/beefy_clm";
type BreakdownMethod = (
client: BeefyViemClient,
@@ -16,6 +17,7 @@ const breakdownMethods: Record = {
mendi: getMendiVaultBreakdown,
gamma: getGammaVaultBreakdown,
ichi: getGammaVaultBreakdown,
+ beefy_clm: getBeefyClmVaultBreakdown,
};
export const getVaultBreakdowns = async (
diff --git a/adapters/beefy/src/sdk/breakdown/protocol_type/beefy_clm.ts b/adapters/beefy/src/sdk/breakdown/protocol_type/beefy_clm.ts
new file mode 100644
index 00000000..17323df7
--- /dev/null
+++ b/adapters/beefy/src/sdk/breakdown/protocol_type/beefy_clm.ts
@@ -0,0 +1,52 @@
+import { Hex, getContract } from "viem";
+import { BeefyVault } from "../../vault/getBeefyVaultConfig";
+import { BeefyViemClient } from "../../viemClient";
+import { BeefyVaultBreakdown } from "../types";
+import { BeefyVaultConcLiqAbi } from "../../../abi/BeefyVaultConcLiq";
+import { BeefyClmStrategyAbi } from "../../../abi/BeefyClmStrategy";
+
+export const getBeefyClmVaultBreakdown = async (
+ client: BeefyViemClient,
+ blockNumber: bigint,
+ vault: BeefyVault
+): Promise => {
+ const managerContract = getContract({
+ client,
+ address: vault.vault_address,
+ abi: BeefyVaultConcLiqAbi,
+ });
+
+ const strategyContract = getContract({
+ client,
+ address: vault.strategy_address,
+ abi: BeefyClmStrategyAbi,
+ });
+
+ const [balances, vaultTotalSupply, wants, range, price] = await Promise.all([
+ managerContract.read.balances({ blockNumber }),
+ managerContract.read.totalSupply({ blockNumber }),
+ managerContract.read.wants({ blockNumber }),
+ strategyContract.read.range({ blockNumber }),
+ strategyContract.read.price({ blockNumber }),
+ ]);
+
+ // special rule to exclude out of range liquidity for concentrated liquidity vaults
+ const isLiquidityEligible = price >= range[0] && price <= range[1];
+
+ return {
+ vault,
+ blockNumber,
+ vaultTotalSupply,
+ isLiquidityEligible,
+ balances: [
+ {
+ tokenAddress: wants[0].toLocaleLowerCase() as Hex,
+ vaultBalance: balances[0],
+ },
+ {
+ tokenAddress: wants[1].toLocaleLowerCase() as Hex,
+ vaultBalance: balances[1],
+ },
+ ],
+ };
+};
diff --git a/adapters/beefy/src/sdk/breakdown/protocol_type/gamma.ts b/adapters/beefy/src/sdk/breakdown/protocol_type/gamma.ts
index 3bfb1879..2a021183 100644
--- a/adapters/beefy/src/sdk/breakdown/protocol_type/gamma.ts
+++ b/adapters/beefy/src/sdk/breakdown/protocol_type/gamma.ts
@@ -35,6 +35,7 @@ export const getGammaVaultBreakdown = async (
vault,
blockNumber,
vaultTotalSupply,
+ isLiquidityEligible: true,
balances: [
{
tokenAddress: token0.toLocaleLowerCase() as Hex,
diff --git a/adapters/beefy/src/sdk/breakdown/protocol_type/ichi.ts b/adapters/beefy/src/sdk/breakdown/protocol_type/ichi.ts
index 21764b48..516ed726 100644
--- a/adapters/beefy/src/sdk/breakdown/protocol_type/ichi.ts
+++ b/adapters/beefy/src/sdk/breakdown/protocol_type/ichi.ts
@@ -46,6 +46,7 @@ export const getGammaVaultBreakdown = async (
vault,
blockNumber,
vaultTotalSupply,
+ isLiquidityEligible: true,
balances: [
{
tokenAddress: token0.toLocaleLowerCase() as Hex,
diff --git a/adapters/beefy/src/sdk/breakdown/protocol_type/mendi.ts b/adapters/beefy/src/sdk/breakdown/protocol_type/mendi.ts
index 3a3f3724..a9c9b038 100644
--- a/adapters/beefy/src/sdk/breakdown/protocol_type/mendi.ts
+++ b/adapters/beefy/src/sdk/breakdown/protocol_type/mendi.ts
@@ -24,6 +24,7 @@ export const getMendiVaultBreakdown = async (
vault,
blockNumber,
vaultTotalSupply,
+ isLiquidityEligible: true,
balances: [
{
tokenAddress: vault.undelying_lp_address.toLocaleLowerCase() as Hex,
diff --git a/adapters/beefy/src/sdk/breakdown/protocol_type/solidly.ts b/adapters/beefy/src/sdk/breakdown/protocol_type/solidly.ts
index 4c4ebffb..00a548f8 100644
--- a/adapters/beefy/src/sdk/breakdown/protocol_type/solidly.ts
+++ b/adapters/beefy/src/sdk/breakdown/protocol_type/solidly.ts
@@ -38,6 +38,7 @@ export const getSolidlyVaultBreakdown = async (
vault,
blockNumber,
vaultTotalSupply,
+ isLiquidityEligible: true,
balances: [
{
tokenAddress: t0.toLocaleLowerCase() as Hex,
diff --git a/adapters/beefy/src/sdk/breakdown/types.ts b/adapters/beefy/src/sdk/breakdown/types.ts
index 1081d1ae..40949ade 100644
--- a/adapters/beefy/src/sdk/breakdown/types.ts
+++ b/adapters/beefy/src/sdk/breakdown/types.ts
@@ -5,6 +5,7 @@ export type BeefyVaultBreakdown = {
vault: BeefyVault;
blockNumber: bigint;
vaultTotalSupply: bigint;
+ isLiquidityEligible: boolean;
balances: {
tokenAddress: Hex;
vaultBalance: bigint;
diff --git a/adapters/beefy/src/sdk/vault/getBeefyVaultConfig.ts b/adapters/beefy/src/sdk/vault/getBeefyVaultConfig.ts
index 4059dc3e..b82f05f0 100644
--- a/adapters/beefy/src/sdk/vault/getBeefyVaultConfig.ts
+++ b/adapters/beefy/src/sdk/vault/getBeefyVaultConfig.ts
@@ -6,13 +6,19 @@ export type BeefyVault = {
id: string;
vault_address: Hex;
undelying_lp_address: Hex;
+ strategy_address: Hex;
chain: string;
protocol_type: BeefyProtocolType;
};
-export type BeefyProtocolType = "gamma" | "ichi" | "mendi" | "solidly";
+export type BeefyProtocolType =
+ | "gamma"
+ | "ichi"
+ | "mendi"
+ | "solidly"
+ | "beefy_clm";
-type ApiPlatformId = "gamma" | "ichi" | "lynex" | "mendi" | "nile"; // and more but we don't use those on linea
+type ApiPlatformId = "gamma" | "ichi" | "lynex" | "mendi" | "nile" | "beefy"; // and more but we don't use those on linea
type ApiVault = {
id: string;
@@ -21,6 +27,7 @@ type ApiVault = {
chain: string;
platformId: ApiPlatformId;
tokenAddress: Hex;
+ strategy: Hex;
};
const protocol_map: Record = {
@@ -29,6 +36,7 @@ const protocol_map: Record = {
lynex: "solidly",
mendi: "mendi",
nile: "solidly",
+ beefy: "beefy_clm",
};
export const getBeefyVaultConfig = memoize(
@@ -49,6 +57,7 @@ export const getBeefyVaultConfig = memoize(
vault_address: vault.earnedTokenAddress.toLocaleLowerCase() as Hex,
chain: vault.chain,
protocol_type,
+ strategy_address: vault.strategy.toLocaleLowerCase() as Hex,
undelying_lp_address: vault.tokenAddress.toLocaleLowerCase() as Hex,
};
});
diff --git a/adapters/beefy/src/sdk/vault/getVaultShareTokenBalances.ts b/adapters/beefy/src/sdk/vault/getTokenBalances.ts
similarity index 55%
rename from adapters/beefy/src/sdk/vault/getVaultShareTokenBalances.ts
rename to adapters/beefy/src/sdk/vault/getTokenBalances.ts
index 96470ac5..47a88118 100644
--- a/adapters/beefy/src/sdk/vault/getVaultShareTokenBalances.ts
+++ b/adapters/beefy/src/sdk/vault/getTokenBalances.ts
@@ -1,56 +1,45 @@
import { Hex } from "viem";
import { BEEFY_SUBGRAPH_URL, SUBGRAPH_PAGE_SIZE } from "../../config";
-type ShareTokenBalance = {
+type TokenBalance = {
user_address: Hex;
- vault_address: Hex;
- shares_balance: bigint;
+ token_address: Hex;
+ balance: bigint;
};
type QueryResult = {
- investorPositions: {
- investor: {
+ tokenBalances: {
+ account: {
id: Hex;
};
- vault: {
- sharesToken: {
- id: Hex;
- };
- underlyingToken: {
- id: Hex;
- symbol: string;
- };
+ token: {
+ id: Hex;
};
- rawSharesBalance: string;
+ amount: string;
}[];
};
-export const getVaultShareTokenBalances = async (
+export const getTokenBalances = async (
blockNumber: bigint
-): Promise => {
- let allPositions: ShareTokenBalance[] = [];
+): Promise => {
+ let allPositions: TokenBalance[] = [];
let skip = 0;
while (true) {
const query = `
query LineaUser($blockNumber: Int!, $skip: Int!, $first: Int!) {
- investorPositions(
+ tokenBalances(
block: {number: $blockNumber}
first: $first
- where: { rawSharesBalance_gt: 0 }
+ where: { amount_gt: 0 }
skip: $skip
) {
- investor {
+ account {
id
}
- vault {
- sharesToken {
- id
- }
- underlyingToken {
- id
- }
+ token {
+ id
}
- rawSharesBalance
+ amount
}
}
`;
@@ -80,17 +69,16 @@ export const getVaultShareTokenBalances = async (
}
allPositions = allPositions.concat(
- res.data.investorPositions.map(
- (position): ShareTokenBalance => ({
- shares_balance: BigInt(position.rawSharesBalance),
- user_address: position.investor.id.toLocaleLowerCase() as Hex,
- vault_address:
- position.vault.sharesToken.id.toLocaleLowerCase() as Hex,
+ res.data.tokenBalances.map(
+ (position): TokenBalance => ({
+ balance: BigInt(position.amount),
+ user_address: position.account.id.toLocaleLowerCase() as Hex,
+ token_address: position.token.id.toLocaleLowerCase() as Hex,
})
)
);
- if (res.data.investorPositions.length < SUBGRAPH_PAGE_SIZE) {
+ if (res.data.tokenBalances.length < SUBGRAPH_PAGE_SIZE) {
break;
}
From 899335607e269238a280cc614c45fdd59fc8b88e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Pr=C3=A9vost?=
<998369+prevostc@users.noreply.github.com>
Date: Fri, 21 Jun 2024 14:17:05 +0200
Subject: [PATCH 2/2] More test blocks
---
adapters/beefy/hourly_blocks.csv | 2 ++
1 file changed, 2 insertions(+)
diff --git a/adapters/beefy/hourly_blocks.csv b/adapters/beefy/hourly_blocks.csv
index 4908bcda..94a6ea25 100644
--- a/adapters/beefy/hourly_blocks.csv
+++ b/adapters/beefy/hourly_blocks.csv
@@ -9,3 +9,5 @@ number,timestamp
4740000,1716263772
4730000,1716233771
4784763,1716398064
+5684890,1718771519
+5784890,1718972096
\ No newline at end of file