Skip to content

Commit

Permalink
Merge pull request #231 from helgaxdev/main
Browse files Browse the repository at this point in the history
zkdx: fix usdc balances
  • Loading branch information
0xroll authored Jun 25, 2024
2 parents 63c5c1f + fdf4eb1 commit 0d02e99
Show file tree
Hide file tree
Showing 5 changed files with 2,365 additions and 8,228 deletions.
2 changes: 1 addition & 1 deletion adapters/zkdx/hourly_blocks.csv
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
number,timestamp
4243360,1714773599
5941671,1719285809
88 changes: 24 additions & 64 deletions adapters/zkdx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import fs from 'fs';
import {write} from 'fast-csv';
import {gql, GraphQLClient} from "graphql-request";
import csv from 'csv-parser';
import {getBalanceMap, getExchangerBalanceMap} from "./lib/fetcher";
import {assets, USDC} from "./lib/utils";

export interface BlockData {
interface BlockData {
blockTimestamp: number;
blockNumber: number
}
Expand All @@ -18,36 +19,29 @@ type OutputDataSchemaRow = {
usd_price: number; // assign 0 if not available
};

const assets: { [key: string]: string } = {
"0x176211869ca2b568f2a7d4ee941e073a821ee1ff": "USDC",
"0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f": "WETH",
};

const graphClient = new GraphQLClient("https://api.studio.thegraph.com/query/47302/zkdx-graph-linea/v0.0.6")
const getBalanceShots = gql`
query getBalanceShots($block_number: Int) {
tokenBalanceShots(
first: 1000
orderBy: block_number
orderDirection: desc
where: {
block_number_lte: $block_number
})
{
user_address
token_address
token_balance
block_number
}
}`


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

console.log(`Snapshot for block: ${blockNumber}:`);
let balanceMap = await getBalanceMap(blockNumber);
let [balanceMap, exchangerBalanceMap] = await Promise.all([getBalanceMap(blockNumber), getExchangerBalanceMap(blockNumber)]);

for(let [userAddress, tokenBalance] of exchangerBalanceMap) {
let tokenBalanceStored = balanceMap.get(userAddress);
if (tokenBalanceStored == undefined) {
let newBalance = new Map<string, string>();
newBalance.set(USDC, tokenBalance);
balanceMap.set(userAddress, newBalance);
} else {
let balance = tokenBalanceStored.get(USDC);
if (balance == undefined)
tokenBalanceStored.set(USDC, tokenBalance);
else
tokenBalanceStored.set(USDC, (BigInt(balance) + BigInt(tokenBalance)).toString());
}
}

for (let [user_address, tokenBalances] of balanceMap) {
for (let [token_address, token_balance] of tokenBalances) {
let balance = BigInt(token_balance);
Expand All @@ -68,40 +62,6 @@ export const getUserTVLByBlock = async (blocks: BlockData) => {
return csvRows;
}

export const getBalanceMap = async (blockNumber: number) => {
let balanceMap = new Map<string, Map<string, string>>(); // user => token => balance
let hasNext = true;
while (hasNext) {
console.log(`Processing balance snapshots before block: ${blockNumber} ...`);
let data: any = await graphClient.request(getBalanceShots, {block_number: blockNumber});
let tokenBalanceShots = data["tokenBalanceShots"];

for (let tokenBalanceShot of tokenBalanceShots) {
let user_address = tokenBalanceShot["user_address"];
let token_address = tokenBalanceShot["token_address"];
let token_balance = tokenBalanceShot["token_balance"];
token_balance = BigInt(token_balance) < 0 ? BigInt(0) : BigInt(token_balance);

let tokenBalance = balanceMap.get(user_address);
if (tokenBalance == undefined) {
let newBalance = new Map<string, string>();
newBalance.set(token_address, token_balance);
balanceMap.set(user_address, newBalance);
} else {
let balance = tokenBalance.get(token_address);
if (balance == undefined)
tokenBalance.set(token_address, token_balance);
}
}
if (tokenBalanceShots.length < 1000) {
hasNext = false;
} else {
blockNumber = parseInt(data["tokenBalanceShots"][999]["block_number"]);
}
}

return balanceMap;
}

const readBlocksFromCSV = async (filePath: string): Promise<BlockData[]> => {
const blocks: BlockData[] = [];
Expand All @@ -113,7 +73,7 @@ const readBlocksFromCSV = async (filePath: string): Promise<BlockData[]> => {
const blockNumber = parseInt(row.number, 10);
const blockTimestamp = parseInt(row.timestamp, 10);
if (!isNaN(blockNumber) && blockTimestamp) {
blocks.push({ blockNumber: blockNumber, blockTimestamp });
blocks.push({blockNumber: blockNumber, blockTimestamp});
}
})
.on('end', () => {
Expand Down Expand Up @@ -144,8 +104,8 @@ readBlocksFromCSV('hourly_blocks.csv').then(async (blocks: any[]) => {
await new Promise((resolve, reject) => {
// const randomTime = Math.random() * 1000;
// setTimeout(resolve, randomTime);
const ws = fs.createWriteStream(`outputData.csv`, { flags: 'w' });
write(allCsvRows, { headers: true })
const ws = fs.createWriteStream(`outputData.csv`, {flags: 'w'});
write(allCsvRows, {headers: true})
.pipe(ws)
.on("finish", () => {
console.log(`CSV file has been written.`);
Expand All @@ -158,4 +118,4 @@ readBlocksFromCSV('hourly_blocks.csv').then(async (blocks: any[]) => {

}).catch((err) => {
console.error('Error reading CSV file:', err);
});
});
130 changes: 130 additions & 0 deletions adapters/zkdx/src/lib/fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {gql} from "graphql-request";
import {batchSize, EXCHANGER, graphClient, usdcContract, zusdContract} from "./utils";

const getBalanceShots = gql`
query getBalanceShots($block_number: Int) {
tokenBalanceShots(
first: 1000
orderBy: block_number
orderDirection: desc
where: {
block_number_lte: $block_number
})
{
user_address
token_address
token_balance
block_number
}
}`

const getHolders = gql`
query getHolders($lastId: ID) {
zusdholders(
first: 1000
where:{id_gt: $lastId}
){
id
}
}`

export const getBalanceMap = async (blockNumber: number) => {
let balanceMap = new Map<string, Map<string, string>>(); // user => token => balance
let hasNext = true;
while (hasNext) {
console.log(`Processing staking balances before block: ${blockNumber} ...`);
let data: any = await graphClient.request(getBalanceShots, {block_number: blockNumber});
let tokenBalanceShots = data["tokenBalanceShots"];

for (let tokenBalanceShot of tokenBalanceShots) {
let user_address = tokenBalanceShot["user_address"];
let token_address = tokenBalanceShot["token_address"];
let token_balance = tokenBalanceShot["token_balance"];
token_balance = BigInt(token_balance) < 0 ? BigInt(0) : BigInt(token_balance);

let tokenBalance = balanceMap.get(user_address);
if (tokenBalance == undefined) {
let newBalance = new Map<string, string>();
newBalance.set(token_address, token_balance);
balanceMap.set(user_address, newBalance);
} else {
let balance = tokenBalance.get(token_address);
if (balance == undefined)
tokenBalance.set(token_address, token_balance);
}
}
if (tokenBalanceShots.length < 1000) {
hasNext = false;
} else {
blockNumber = parseInt(data["tokenBalanceShots"][999]["block_number"]);
}
}

return balanceMap;
}

async function getZusdHolders(): Promise<string[]> {
let hasNext = true;
let page = 1;
let lastId = ""
let holders = [];
while (hasNext) {
console.log(`Getting holders page: ${page} ...`);
let data: any = await graphClient.request(getHolders, {lastId: lastId});
let result = data["zusdholders"];
for (let i = 0; i < result.length; i++)
holders.push(result[i]["id"]);

if (result.length < 1000)
hasNext = false;
else
lastId = data["zusdholders"][999]["id"];
page++;
}
return holders;
}



export async function getExchangerBalanceMap(blockNumber: number): Promise<Map<string, string>> {

let usdcBalance = await usdcContract.balanceOf(EXCHANGER, {blockTag: blockNumber});
let zusdTotal = await zusdContract.totalSupply({blockTag: blockNumber});
let holders = await getZusdHolders();
let manager = "0x709b9146219e61dc811f7a469943ada60e013475";
let managerBalance = await zusdContract.balanceOf(manager, {blockTag: blockNumber});
zusdTotal = zusdTotal.sub(managerBalance);
holders = holders.filter((holder) => holder != manager && holder != "0x0000000000000000000000000000000000000000")

let zusdBalances = await executeInBatches(holders, blockNumber, batchSize);

const usdcBalanceMap = new Map<string, string>();
for (let i = 0; i < holders.length; i++){
try {
let holder = holders[i];
let balance = zusdBalances[i];
let usdcEquivalent = usdcBalance.mul(balance).div(zusdTotal);
usdcBalanceMap.set(holder, usdcEquivalent.toString());
} catch (e) {
console.log("Error", e)
}
}
return usdcBalanceMap;
}

async function getZusdBalance(holder: string, blockNumber: number) {
return await zusdContract.balanceOf(holder, {blockTag: blockNumber});
}

async function executeInBatches(holders: string[], blockNumber: number, batchSize: number) {
let results: any = [];

for (let i = 0; i < holders.length; i += batchSize) {
console.log(`Processing exchanger balances batch, left: ${holders.length - i} ...`)
const batch = holders.slice(i, i + batchSize);
const batchPromises = batch.map(holder => getZusdBalance(holder, blockNumber));
const batchResults = await Promise.all(batchPromises);
results = results.concat(batchResults);
}
return results;
}
24 changes: 24 additions & 0 deletions adapters/zkdx/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {Contract, ethers} from "ethers";
import {GraphQLClient} from "graphql-request";

export const USDC = "0x176211869ca2b568f2a7d4ee941e073a821ee1ff";
export const WETH = "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f";
export const EXCHANGER = "0x3a85b87e81cd99d4a6670f95a4f0dedaac207da0";
export const ZUSD = "0x2167C4D5FE05A1250588F0B8AA83A599e7732eae"

export const assets: { [key: string]: string } = {
"0x176211869ca2b568f2a7d4ee941e073a821ee1ff": "USDC",
"0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f": "WETH",
};

export const providerLinea = new ethers.providers.JsonRpcProvider("https://rpc.linea.build")
export const graphClient = new GraphQLClient("https://api.studio.thegraph.com/query/47302/zkdx-graph-linea/v0.1.1")

const abi = [
"function balanceOf(address account) public view returns (uint256)",
"function totalSupply() public view returns (uint256)"
]

export const usdcContract = new Contract(USDC, abi, providerLinea);
export const zusdContract = new Contract(ZUSD, abi, providerLinea);
export const batchSize = 50;
Loading

0 comments on commit 0d02e99

Please sign in to comment.