diff --git a/adapters/mendi/src/index.ts b/adapters/mendi/src/index.ts index cd7acc9a..8da7b46c 100644 --- a/adapters/mendi/src/index.ts +++ b/adapters/mendi/src/index.ts @@ -1,4 +1,4 @@ -import { CHAINS, PROTOCOLS, SNAPSHOTS_BLOCKS } from "./sdk/config"; +import { CHAINS, PROTOCOLS } from "./sdk/config"; import { getLPValueByUserAndPoolFromActivities, getActivitiesForAddressByPoolAtBlock, @@ -12,64 +12,68 @@ import { import fs from "fs"; import { write } from "fast-csv"; import { getMarketInfos } from "./sdk/marketDetails"; -import { exit } from "process"; import { bigMath } from "./sdk/abi/helpers"; -interface CSVRow { - block_number: string; - timestamp: string; +interface BlockData { + blockNumber: number; + blockTimestamp: number; +} + +type OutputDataSchemaRow = { + block_number: number; + timestamp: number; user_address: string; token_address: string; - token_balance: string; - token_symbol: string; -} + token_balance: bigint; + token_symbol: string; //token symbol should be empty string if it is not available + usd_price: number; //assign 0 if not available +}; -const getData = async () => { +export const getUserTVLByBlock = async (blocks: BlockData) => { const marketInfos = await getMarketInfos( "0x1b4d3b0421ddc1eb216d230bc01527422fb93103" ); - const csvRows: CSVRow[] = []; + const csvRows: OutputDataSchemaRow[] = []; + const block = blocks.blockNumber; - for (let block of SNAPSHOTS_BLOCKS) { - const { tokens, accountBorrows } = - await getActivitiesForAddressByPoolAtBlock( - block, - "", - "", - CHAINS.LINEA, - PROTOCOLS.MENDI - ); + const { tokens, accountBorrows } = await getActivitiesForAddressByPoolAtBlock( + block, + "", + "", + CHAINS.LINEA, + PROTOCOLS.MENDI + ); - console.log(`Block: ${block}`); - console.log("Tokens: ", tokens.length); - console.log("Account Borrows: ", accountBorrows.length); + console.log(`Block: ${block}`); + console.log("Tokens: ", tokens.length); + console.log("Account Borrows: ", accountBorrows.length); - let lpValueByUsers = getLPValueByUserAndPoolFromActivities( - tokens, - accountBorrows - ); + let lpValueByUsers = getLPValueByUserAndPoolFromActivities( + tokens, + accountBorrows + ); - const timestamp = new Date(await getTimestampAtBlock(block)).toISOString(); + const timestamp = await getTimestampAtBlock(block); - lpValueByUsers.forEach((value, owner) => { - value.forEach((amount, market) => { - if (bigMath.abs(amount) < 1) return; + lpValueByUsers.forEach((value, owner) => { + value.forEach((amount, market) => { + if (bigMath.abs(amount) < 1) return; - const marketInfo = marketInfos.get(market.toLowerCase()); + const marketInfo = marketInfos.get(market.toLowerCase()); - // Accumulate CSV row data - csvRows.push({ - user_address: owner, - token_address: marketInfo?.underlyingAddress ?? "", - token_symbol: marketInfo?.underlyingSymbol ?? "", - token_balance: (amount / BigInt(1e18)).toString(), - block_number: block.toString(), - timestamp, - }); + // Accumulate CSV row data + csvRows.push({ + block_number: block, + timestamp: timestamp, + user_address: owner, + token_address: marketInfo?.underlyingAddress ?? "", + token_balance: amount / BigInt(1e18), + token_symbol: marketInfo?.underlyingSymbol ?? "", + usd_price: 0, }); }); - } + }); // Write the CSV output to a file const ws = fs.createWriteStream("outputData.csv"); @@ -78,8 +82,6 @@ const getData = async () => { .on("finish", () => { console.log("CSV file has been written."); }); -}; -getData().then(() => { - console.log("Done"); -}); + return csvRows; +}; diff --git a/adapters/mendi/src/sdk/config.ts b/adapters/mendi/src/sdk/config.ts index 8c5f1a99..1be86669 100644 --- a/adapters/mendi/src/sdk/config.ts +++ b/adapters/mendi/src/sdk/config.ts @@ -9,7 +9,7 @@ export const enum PROTOCOLS { export const SUBGRAPH_URLS = { [CHAINS.LINEA]: { [PROTOCOLS.MENDI]: { - url: "https://api.goldsky.com/api/public/project_cltshpix6kkj301x1b4ilh6pm/subgraphs/mendi-linea/1.2/gn", + url: "https://api.goldsky.com/api/public/project_cltshpix6kkj301x1b4ilh6pm/subgraphs/mendi-linea/1.4/gn", }, }, }; @@ -17,5 +17,3 @@ export const SUBGRAPH_URLS = { export const RPC_URLS = { [CHAINS.LINEA]: "https://rpc.linea.build", }; - -export const SNAPSHOTS_BLOCKS = [3210170]; diff --git a/adapters/mendi/src/sdk/marketDetails.ts b/adapters/mendi/src/sdk/marketDetails.ts index ea339481..189fd765 100644 --- a/adapters/mendi/src/sdk/marketDetails.ts +++ b/adapters/mendi/src/sdk/marketDetails.ts @@ -3,6 +3,7 @@ import { CHAINS, RPC_URLS } from "./config"; import { linea } from "viem/chains"; import comptrollerAbi from "./abi/comptroller.abi"; import ctokenAbi from "./abi/ctoken.abi"; +import { MarketActivity } from "./subgraphDetails"; export interface MarketInfo { address: string; @@ -99,3 +100,30 @@ export const getMarketInfos = async ( return marketInfos; }; + +export const getBorrowBalanceStoredByAccounts = async ( + activities: MarketActivity[], + blockNumber?: bigint +) => { + const publicClient = createPublicClient({ + chain: extractChain({ chains: [linea], id: CHAINS.LINEA }), + transport: http(RPC_URLS[CHAINS.LINEA]), + }); + + const borrowBalanceResults = await publicClient.multicall({ + contracts: activities + .map((m) => [ + { + address: m.market, + abi: ctokenAbi, + functionName: "borrowBalanceStored", + args: [m.owner], + }, + ]) + .flat() as any, + blockNumber, + }); + const borrowBalances = borrowBalanceResults.map((v) => v.result as bigint); + + return borrowBalances; +}; diff --git a/adapters/mendi/src/sdk/subgraphDetails.ts b/adapters/mendi/src/sdk/subgraphDetails.ts index 0a7f7c8d..ea71d06c 100644 --- a/adapters/mendi/src/sdk/subgraphDetails.ts +++ b/adapters/mendi/src/sdk/subgraphDetails.ts @@ -1,8 +1,10 @@ -import { CHAINS, PROTOCOLS, RPC_URLS, SUBGRAPH_URLS } from "./config"; import { createPublicClient, extractChain, http } from "viem"; import { linea } from "viem/chains"; -import { getMarketInfos } from "./marketDetails"; -import { bigMath } from "./abi/helpers"; +import { CHAINS, PROTOCOLS, RPC_URLS, SUBGRAPH_URLS } from "./config"; +import { + getBorrowBalanceStoredByAccounts, + getMarketInfos, +} from "./marketDetails"; export interface MarketActivity { id: string; @@ -151,11 +153,50 @@ export const getActivitiesForAddressByPoolAtBlock = async ( }) .filter((x) => x !== undefined); + accountBorrows = await normalizeAccountBorrows(blockNumber, accountBorrows); accountBorrows.forEach((t: any) => (t.amount = t.amount * BigInt(1e18))); return { tokens, accountBorrows }; }; +export const normalizeAccountBorrows = async ( + blockNumber: number, + accountBorrows: MarketActivity[] +): Promise => { + const result: MarketActivity[] = []; + + accountBorrows.sort((a, b) => b.blockNumber - a.blockNumber); + + for (let i = 0; i < accountBorrows.length; i++) { + var marketActivity = accountBorrows[i]; + + var doesExist = + result.findIndex( + (x: MarketActivity) => + x.owner == marketActivity.owner && x.market == marketActivity.market + ) > -1; + if (doesExist) continue; + + result.push(marketActivity); + } + + const chuckCount = 2000; + for (let i = 0; i < result.length; i += chuckCount) { + var end = Math.min(result.length, i + chuckCount); + + var currentBorrows = await getBorrowBalanceStoredByAccounts( + result.slice(i, end), + BigInt(blockNumber) + ); + + for (let j = 0; j < currentBorrows.length; j++) { + result[i + j].amount = -BigInt(currentBorrows[j]); + } + } + + return result; +}; + export const getLPValueByUserAndPoolFromActivities = ( tokens: MarketActivity[], accountBorrows: MarketActivity[]