diff --git a/adapters/zerolend/hourly_blocks.csv b/adapters/zerolend/hourly_blocks.csv new file mode 100644 index 00000000..8ffabfab --- /dev/null +++ b/adapters/zerolend/hourly_blocks.csv @@ -0,0 +1,2 @@ +number,timestamp +4243360,1714773599 \ No newline at end of file diff --git a/adapters/zerolend/src/index.ts b/adapters/zerolend/src/index.ts index ed58aaa1..dcb38919 100644 --- a/adapters/zerolend/src/index.ts +++ b/adapters/zerolend/src/index.ts @@ -1,66 +1,10 @@ import { write } from "fast-csv"; import fs from "fs"; import csv from "csv-parser"; -import { BlockData, getUserTVLByBlock } from "./sdk"; - -// const readBlocksFromCSV = async (filePath: string): Promise => { -// const blocks: BlockData[] = []; - -// await new Promise((resolve, reject) => { -// fs.createReadStream(filePath) -// .pipe(csv()) // Specify the separator as '\t' for TSV files -// .on("data", (row: any) => { -// const blockNumber = parseInt(row.number, 10); -// const blockTimestamp = parseInt(row.block_timestamp, 10); -// if (!isNaN(blockNumber) && blockTimestamp) { -// blocks.push({ blockNumber: blockNumber, blockTimestamp }); -// } -// }) -// .on("end", resolve) -// .on("error", reject); -// }); - -// return blocks; -// }; - -// readBlocksFromCSV("hourly_blocks.csv") -// .then(async (blocks) => { -// const allCsvRows: any[] = []; // Array to accumulate CSV rows for all blocks -// const batchSize = 10; // Size of batch to trigger writing to the file -// let i = 0; -// console.log("block number received") -// for (const block of blocks) { -// try { -// const result = await getUserTVLByBlock(block); - -// // Accumulate CSV rows for all blocks -// allCsvRows.push(...result); - -// i++; -// console.log(`Processed block ${i}`); - -// // Write to file when batch size is reached or at the end of loop -// if (i % batchSize === 0 || i === blocks.length) { -// const ws = fs.createWriteStream(`outputData.csv`, { -// flags: i === batchSize ? "w" : "a", -// }); -// write(allCsvRows, { headers: i === batchSize ? true : false }) -// .pipe(ws) -// .on("finish", () => { -// console.log(`CSV file has been written.`); -// }); - -// // Clear the accumulated CSV rows -// allCsvRows.length = 0; -// } -// } catch (error) { -// console.error(`An error occurred for block ${block}:`, error); -// } -// } -// }) -// .catch((err) => { -// console.error("Error reading CSV file:", err); -// }); +import { BlockData } from "./sdk/types"; +import { getUserTVLByBlock } from "./sdk/tvl"; +import { getUserStakeByBlock } from "./sdk/stake"; +import { getUserLPByBlock } from "./sdk/lp"; module.exports = { getUserTVLByBlock, @@ -72,17 +16,17 @@ const readBlocksFromCSV = async (filePath: string): Promise => { await new Promise((resolve, reject) => { fs.createReadStream(filePath) .pipe(csv()) // Specify the separator as '\t' for TSV files - .on('data', (row) => { + .on("data", (row) => { const blockNumber = parseInt(row.number, 10); const blockTimestamp = parseInt(row.timestamp, 10); if (!isNaN(blockNumber) && blockTimestamp) { blocks.push({ blockNumber: blockNumber, blockTimestamp }); } }) - .on('end', () => { + .on("end", () => { resolve(); }) - .on('error', (err) => { + .on("error", (err) => { reject(err); }); }); @@ -90,31 +34,43 @@ const readBlocksFromCSV = async (filePath: string): Promise => { return blocks; }; -readBlocksFromCSV('hourly_blocks.csv').then(async (blocks: BlockData[]) => { - console.log(blocks); - const allCsvRows: any[] = []; // Array to accumulate CSV rows for all blocks - const batchSize = 1000; // Size of batch to trigger writing to the file - let i = 0; +readBlocksFromCSV("hourly_blocks.csv") + .then(async (blocks: BlockData[]) => { + console.log(blocks); + const allCsvRows: any[] = []; // Array to accumulate CSV rows for all blocks + const batchSize = 1000; // Size of batch to trigger writing to the file + let i = 0; + + for (const block of blocks) { + try { + // const resultTvl = await getUserTVLByBlock(block); + // for (let i = 0; i < resultTvl.length; i++) { + // allCsvRows.push(resultTvl[i]); + // } - for (const block of blocks) { - try { - const result = await getUserTVLByBlock(block); - for (let i = 0; i < result.length; i++) { - allCsvRows.push(result[i]) + // const resultStake = await getUserStakeByBlock(block); + // for (let i = 0; i < resultStake.length; i++) { + // allCsvRows.push(resultStake[i]); + // } + + const resultLp = await getUserLPByBlock(block); + for (let i = 0; i < resultLp.length; i++) { + allCsvRows.push(resultLp[i]); + } + } catch (error) { + console.error(`An error occurred for block ${block}:`, error); } - } catch (error) { - console.error(`An error occurred for block ${block}:`, error); } - } - await new Promise((resolve, reject) => { - const ws = fs.createWriteStream(`outputData.csv`, { flags: 'w' }); - write(allCsvRows, { headers: true }) - .pipe(ws) - .on("finish", () => { - console.log(`CSV file has been written.`); - resolve; - }); + await new Promise((resolve, reject) => { + const ws = fs.createWriteStream(`outputData.csv`, { flags: "w" }); + write(allCsvRows, { headers: true }) + .pipe(ws) + .on("finish", () => { + console.log(`CSV file has been written.`); + resolve; + }); + }); + }) + .catch((err) => { + console.error("Error reading CSV file:", err); }); -}).catch((err) => { - console.error('Error reading CSV file:', err); -}); \ No newline at end of file diff --git a/adapters/zerolend/src/sdk/lp.ts b/adapters/zerolend/src/sdk/lp.ts new file mode 100644 index 00000000..2427a060 --- /dev/null +++ b/adapters/zerolend/src/sdk/lp.ts @@ -0,0 +1,63 @@ +import { + BlockData, + IOmniStakingData, + IOmniStakingResponse, + OutputDataSchemaRow, +} from "./types"; + +const queryURL = + "https://api.goldsky.com/api/public/project_clsk1wzatdsls01wchl2e4n0y/subgraphs/zerolend-omnistaking/1.0.2/gn"; + +const tokenAddress = "0x78354f8dccb269a615a7e0a24f9b0718fdc3c7a7"; //do we need to convert the case +const symbol = "ZERO"; + +export const getUserLPByBlock = async ( + blocks: BlockData +): Promise => { + const timestamp = blocks.blockTimestamp; + const first = 1000; + const rows: OutputDataSchemaRow[] = []; + + let lastAddress = "0x0000000000000000000000000000000000000000"; + + do { + const query = `{ + tokenBalances( + where: {id_gt: "${lastAddress}", balance_omni_lp_gt: "0"} + first: ${first} + ) { + id + balance_omni_lp + } + }`; + + const response = await fetch(queryURL, { + method: "POST", + body: JSON.stringify({ query }), + headers: { "Content-Type": "application/json" }, + }); + const batch: IOmniStakingResponse = await response.json(); + + if (!batch.data || batch.data.tokenBalances.length == 0) break; + + batch.data.tokenBalances.forEach((data: IOmniStakingData) => { + rows.push({ + block_number: blocks.blockNumber, + timestamp, + user_address: data.id, + token_address: tokenAddress, + token_balance: Number(data.balance_omni_lp), + token_symbol: symbol, + usd_price: 0, + }); + + lastAddress = data.id; + }); + + console.log( + `Processed ${rows.length} rows. Last address is ${lastAddress}` + ); + } while (true); + + return rows; +}; diff --git a/adapters/zerolend/src/sdk/stake.ts b/adapters/zerolend/src/sdk/stake.ts new file mode 100644 index 00000000..d11c128a --- /dev/null +++ b/adapters/zerolend/src/sdk/stake.ts @@ -0,0 +1,64 @@ +import { + BlockData, + IOmniStakingData, + IOmniStakingResponse, + ITVLResponse, + OutputDataSchemaRow, +} from "./types"; + +const queryURL = + "https://api.goldsky.com/api/public/project_clsk1wzatdsls01wchl2e4n0y/subgraphs/zerolend-omnistaking/1.0.2/gn"; + +const tokenAddress = "0x78354f8dccb269a615a7e0a24f9b0718fdc3c7a7"; //do we need to convert the case +const symbol = "ZERO"; + +export const getUserStakeByBlock = async ( + blocks: BlockData +): Promise => { + const timestamp = blocks.blockTimestamp; + const first = 1000; + const rows: OutputDataSchemaRow[] = []; + + let lastAddress = "0x0000000000000000000000000000000000000000"; + + do { + const query = `{ + tokenBalances( + where: {id_gt: "${lastAddress}", balance_omni_gt: "0"} + first: ${first} + ) { + id + balance_omni + } + }`; + + const response = await fetch(queryURL, { + method: "POST", + body: JSON.stringify({ query }), + headers: { "Content-Type": "application/json" }, + }); + const batch: IOmniStakingResponse = await response.json(); + + if (!batch.data || batch.data.tokenBalances.length == 0) break; + + batch.data.tokenBalances.forEach((data: IOmniStakingData) => { + rows.push({ + block_number: blocks.blockNumber, + timestamp, + user_address: data.id, + token_address: tokenAddress, + token_balance: Number(data.balance_omni), + token_symbol: symbol, + usd_price: 0, + }); + + lastAddress = data.id; + }); + + console.log( + `Processed ${rows.length} rows. Last address is ${lastAddress}` + ); + } while (true); + + return rows; +}; diff --git a/adapters/zerolend/src/sdk.ts b/adapters/zerolend/src/sdk/tvl.ts similarity index 71% rename from adapters/zerolend/src/sdk.ts rename to adapters/zerolend/src/sdk/tvl.ts index efb596db..23e6af1d 100644 --- a/adapters/zerolend/src/sdk.ts +++ b/adapters/zerolend/src/sdk/tvl.ts @@ -1,37 +1,9 @@ -interface IResponse { - data: { - userReserves: IData[]; - }; -} - -interface IData { - user: { - id: string; - }; - currentTotalDebt: string; - currentATokenBalance: string; - reserve: { - underlyingAsset: string; - symbol: string; - name: string; - }; - liquidityRate: "0"; -} - -type OutputDataSchemaRow = { - block_number: number; - timestamp: number; - user_address: string; - token_address: string; - token_balance: number; - token_symbol: string; - usd_price: number; -}; - -export interface BlockData { - blockNumber: number; - blockTimestamp: number; -} +import { + BlockData, + ITVLData, + ITVLResponse, + OutputDataSchemaRow, +} from "./types"; const queryURL = "https://api.goldsky.com/api/public/project_clsk1wzatdsls01wchl2e4n0y/subgraphs/zerolend-linea/1.0.0/gn"; @@ -71,11 +43,11 @@ export const getUserTVLByBlock = async ( body: JSON.stringify({ query }), headers: { "Content-Type": "application/json" }, }); - const batch: IResponse = await response.json(); + const batch: ITVLResponse = await response.json(); if (!batch.data || batch.data.userReserves.length == 0) break; - batch.data.userReserves.forEach((data: IData) => { + batch.data.userReserves.forEach((data: ITVLData) => { const balance = BigInt(data.currentATokenBalance) - BigInt(data.currentTotalDebt); diff --git a/adapters/zerolend/src/sdk/types.ts b/adapters/zerolend/src/sdk/types.ts new file mode 100644 index 00000000..bf492107 --- /dev/null +++ b/adapters/zerolend/src/sdk/types.ts @@ -0,0 +1,46 @@ +export interface IOmniStakingResponse { + data: { + tokenBalances: IOmniStakingData[]; + }; +} + +export interface IOmniStakingData { + id: string; + balance_omni: string; + balance_omni_lp: string; +} + +export type OutputDataSchemaRow = { + block_number: number; + timestamp: number; + user_address: string; + token_address: string; + token_balance: number; + token_symbol: string; + usd_price: number; +}; + +export interface BlockData { + blockNumber: number; + blockTimestamp: number; +} + +export interface ITVLResponse { + data: { + userReserves: ITVLData[]; + }; +} + +export interface ITVLData { + user: { + id: string; + }; + currentTotalDebt: string; + currentATokenBalance: string; + reserve: { + underlyingAsset: string; + symbol: string; + name: string; + }; + liquidityRate: "0"; +}