Skip to content

Commit

Permalink
Merge pull request #155 from LedgityLabs/main
Browse files Browse the repository at this point in the history
Ledgity Yield : DeFi
  • Loading branch information
0xroll authored May 23, 2024
2 parents db2de60 + 976abdc commit 2b4df7c
Show file tree
Hide file tree
Showing 8 changed files with 3,143 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ dist/
Ds_Store
.Ds_Store
package-lock.json
bun.lockb
outputData.csv
5 changes: 5 additions & 0 deletions adapters/ledgityyield/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Usage

1. `bun i` (or equivalent) to install dependencies
2. `bun start` to generate `outputData.csv`
3. That's it!
2,937 changes: 2,937 additions & 0 deletions adapters/ledgityyield/outputData.csv

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/ledgityyield/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "ledgityyield-calculator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsx src/index.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"csv-parser": "^3.0.0",
"fast-csv": "^5.0.1",
"tsx": "^4.7.1",
"viem": "^2.8.16"
},
"devDependencies": {
"@types/node": "^20.11.17",
"typescript": "^5.3.3"
}
}
7 changes: 7 additions & 0 deletions adapters/ledgityyield/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createPublicClient, http } from "viem";
import { linea } from "viem/chains";

export const client = createPublicClient({
chain: linea,
transport: http(),
});
132 changes: 132 additions & 0 deletions adapters/ledgityyield/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { promisify } from "util";
import stream from "stream";
import csv from "csv-parser";
import fs from "fs";
import { write } from "fast-csv";
import { erc20Abi, zeroAddress } from "viem";
import { client } from "./client";

let holdersRetrieved = false;
const holders: Set<`0x${string}`> = new Set();

const tokenSymbol = "LUSDC";
const tokenAddress = "0x4AF215DbE27fc030F37f73109B85F421FAB45B7a";

export const getUserTVLByBlock = async (
blocks: BlockData
): Promise<OutputDataSchemaRow[]> => {
const { blockNumber, blockTimestamp } = blocks;
const csvRows: OutputDataSchemaRow[] = [];

// Retrieve holders' addresses list (if not already done)
if (!holdersRetrieved) {
// Retrieve all LUSDC transfers events
const logs = await client.getContractEvents({
address: tokenAddress,
abi: erc20Abi,
eventName: "Transfer",
fromBlock: 211050n,
toBlock: "latest",
});

logs.forEach((log) => {
if (log.args.from && ![tokenAddress, zeroAddress].includes(log.args.from))
holders.add(log.args.from);
if (log.args.to && ![tokenAddress, zeroAddress].includes(log.args.to))
holders.add(log.args.to);
});

// Set activities as retrieved
holdersRetrieved = true;
}

// Compute all holders balances
const reads: any[] = [];
holders.forEach((holder) => {
reads.push({
abi: erc20Abi,
functionName: "balanceOf",
address: tokenAddress,
args: [holder],
blockNumber,
});
});
const balances = await client.multicall({ contracts: reads });

// Append a CSV row for each holder that has a balance at this block
for (let i = 0; i < Array.from(holders).length; i++) {
if (balances[i].result)
csvRows.push({
block_number: blockNumber,
timestamp: blockTimestamp,
user_address: Array.from(holders)[i].toLowerCase(),
token_address: tokenAddress.toLowerCase(),
token_symbol: tokenSymbol,
token_balance: balances[i].result! as bigint,
usd_price: 1,
});
}

console.log(
`- ${tokenSymbol} holders snapshot generated at block ${blockNumber.toString()}`
);
return csvRows;
};

const readBlocksFromCSV = async (filePath: string): Promise<BlockData[]> => {
const blocks: BlockData[] = [];

await new Promise<void>((resolve, reject) => {
fs.createReadStream(filePath)
.pipe(csv()) // Specify the separator as '\t' for TSV files
.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', () => {
resolve();
})
.on('error', (err) => {
reject(err);
});
});

return blocks;
};

// const getBlockTimestamp = async (blockNumber: bigint): Promise<number> => {
// const blockInfos = await client.getBlock({
// blockNumber,
// });
// return Number(blockInfos.timestamp);
// };


readBlocksFromCSV('hourly_blocks.csv').then(async (blocks: any[]) => {
console.log(blocks);
const allCsvRows: any[] = [];

for (const block of blocks) {
try {
const result = await getUserTVLByBlock(block);
allCsvRows.push(...result);
} 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;
});
});

}).catch((err) => {
console.error('Error reading CSV file:', err);
});
14 changes: 14 additions & 0 deletions adapters/ledgityyield/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface BlockData {
blockNumber: number;
blockTimestamp: number;
}

interface OutputDataSchemaRow {
block_number: number;
timestamp: number;
user_address: string;
token_address: 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
}
24 changes: 24 additions & 0 deletions adapters/ledgityyield/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", // Use Node.js's module resolution algorithm
"strict": true, // Enable all strict type-checking options
"esModuleInterop": true, // Enables ESM-style imports
"skipLibCheck": true, // Skip type checking of all declaration files (*.d.ts)
"forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file.
"allowJs": true,
"resolveJsonModule": true,
"isolatedModules": true,
"rootDir": "src/",
"outDir": "dist/"
},
"ts-node": {
"esm": true,
"experimentalResolver": true,
"experimentalSpecifierResolution": "node",
"files": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

0 comments on commit 2b4df7c

Please sign in to comment.