Skip to content

Commit

Permalink
Merge pull request #56 from chef-jojo/add-pcs
Browse files Browse the repository at this point in the history
Add PancakeSwap
  • Loading branch information
nitish-91 authored Apr 17, 2024
2 parents b1a4451 + ff46743 commit 4831828
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 0 deletions.
26 changes: 26 additions & 0 deletions adapters/pancakeswap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "pancakeswap",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "commonjs",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node dist/index.js",
"compile": "tsc",
"watch": "tsc -w",
"clear": "rm -rf dist"
},
"keywords": [],
"author": "",
"license": "UNLICENSED",
"dependencies": {
"csv-parser": "^3.0.0",
"fast-csv": "^5.0.1",
"viem": "^2.8.13"
},
"devDependencies": {
"@types/node": "^20.11.17",
"typescript": "^5.3.3"
}
}
98 changes: 98 additions & 0 deletions adapters/pancakeswap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { promisify } from 'util';
import stream from 'stream';
import csv from 'csv-parser';
import fs from 'fs';
import { write } from 'fast-csv';

import { BlockData, OutputSchemaRow } from './sdk/types';
import {
getTimestampAtBlock,
getV3UserPositionsAtBlock,
} from './sdk/lib';

const pipeline = promisify(stream.pipeline);

const readBlocksFromCSV = async (
filePath: string,
): Promise<number[]> => {
const blocks: number[] = [];
await pipeline(
fs.createReadStream(filePath),
csv(),
async function* (source) {
for await (const chunk of source) {
// Assuming each row in the CSV has a column 'block' with the block number
if (chunk.block) blocks.push(parseInt(chunk.block, 10));
}
},
);
return blocks;
};

const getData = async () => {
const blocks = [3759558]; //await readBlocksFromCSV('src/sdk/mode_chain_daily_blocks.csv');

const csvRows: OutputSchemaRow[] = [];

for (const block of blocks) {
const timestamp = await getTimestampAtBlock(block);

csvRows.push(
...(await getUserTVLByBlock({
blockNumber: block,
blockTimestamp: timestamp,
})),
);
}

// Write the CSV output to a file
const ws = fs.createWriteStream('outputData.csv');
write(csvRows, { headers: true })
.pipe(ws)
.on('finish', () => {
console.log('CSV file has been written.');
});
};

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

const v3Positions = await getV3UserPositionsAtBlock(blockNumber);

const combinedPositions = v3Positions;
const balances: Record<string, Record<string, bigint>> = {};
for (const position of combinedPositions) {
balances[position.user] = balances[position.user] || {};

if (position.token0.balance > 0n)
balances[position.user][position.token0.address] =
(balances?.[position.user]?.[position.token0.address] ?? 0n) +
position.token0.balance;

if (position.token1.balance > 0n)
balances[position.user][position.token1.address] =
(balances?.[position.user]?.[position.token1.address] ?? 0n) +
position.token1.balance;
}

for (const [user, tokenBalances] of Object.entries(balances)) {
for (const [token, balance] of Object.entries(tokenBalances)) {
result.push({
block_number: blockNumber,
timestamp: blockTimestamp,
user_address: user,
token_address: token,
token_balance: balance,
});
}
}

return result;
};

getData().then(() => {
console.log('Done');
});
10 changes: 10 additions & 0 deletions adapters/pancakeswap/src/sdk/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createPublicClient, http } from 'viem';
import { linea } from 'viem/chains';

export const V3_SUBGRAPH_URL =
'https://api.studio.thegraph.com/query/45376/exchange-v3-linea/version/latest';

export const client = createPublicClient({
chain: linea,
transport: http('https://rpc.linea.build'),
});
157 changes: 157 additions & 0 deletions adapters/pancakeswap/src/sdk/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { V3_SUBGRAPH_URL, client } from './config';
import { UserPosition } from './types';

type V3Position = {
id: string;
liquidity: string;
owner: string;
pool: {
sqrtPrice: string;
tick: string;
token0: {
id: string;
symbol: string;
};
token1: {
id: string;
symbol: string;
};
token0Price: string;
token1Price: string;
};
tickLower: {
tickIdx: string;
};
tickUpper: {
tickIdx: string;
};
};

const getV3PositionReserves = (position: V3Position) => {
const liquidity = +position.liquidity;
const _sqrtPrice = +position.pool.sqrtPrice;
const currentTick = +position.pool.tick;
const tickLower = +position.tickLower.tickIdx;
const tickUpper = +position.tickUpper.tickIdx;

let reserve0 = 0n;
let reserve1 = 0n;

if (liquidity === 0) {
return {
reserve0,
reserve1,
};
}

const sqrtRatioA = Math.sqrt(1.0001 ** tickLower);
const sqrtRatioB = Math.sqrt(1.0001 ** tickUpper);
const sqrtPrice = _sqrtPrice / 2 ** 96;

if (currentTick >= tickLower && currentTick < tickUpper) {
reserve0 = BigInt(
Math.floor(
liquidity *
((sqrtRatioB - sqrtPrice) / (sqrtPrice * sqrtRatioB)),
),
);
reserve1 = BigInt(
Math.floor(liquidity * (sqrtPrice - sqrtRatioA)),
);
}

return {
reserve0,
reserve1,
};
};

export const getV3UserPositionsAtBlock = async (
blockNumber: number,
): Promise<UserPosition[]> => {
const result: UserPosition[] = [];

let skip = 0;
let fetchNext = true;
while (fetchNext) {
const query = `query {
positions(
first: 1000,
where: { liquidity_gt: 0, id_gt: ${skip} },
block: { number: ${blockNumber} },
orderBy: id
) {
id
liquidity
owner
pool {
sqrtPrice
tick
token0 {
id
symbol
}
token1 {
id
symbol
}
token0Price
token1Price
}
tickLower {
tickIdx
}
tickUpper {
tickIdx
}
}
}`;

const response = await fetch(V3_SUBGRAPH_URL, {
method: 'POST',
body: JSON.stringify({ query }),
headers: { 'Content-Type': 'application/json' },
});

const { data } = await response.json();

const { positions } = data;

result.push(
...positions.map((position: V3Position) => {
const { reserve0, reserve1 } =
getV3PositionReserves(position);
return {
user: position.owner,
token0: {
address: position.pool.token0.id,
balance: reserve0,
symbol: position.pool.token0.symbol,
usdPrice: +position.pool.token0Price,
},
token1: {
address: position.pool.token1.id,
balance: reserve1,
symbol: position.pool.token1.symbol,
usdPrice: +position.pool.token1Price,
},
};
}),
);

if (positions.length < 1000) {
fetchNext = false;
} else {
skip = positions[positions.length - 1].id;
}
}

return result;
};

export const getTimestampAtBlock = async (blockNumber: number) => {
const block = await client.getBlock({
blockNumber: BigInt(blockNumber),
});
return Number(block.timestamp * 1000n);
};
26 changes: 26 additions & 0 deletions adapters/pancakeswap/src/sdk/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface BlockData {
blockNumber: number;
blockTimestamp: number;
}

export type OutputSchemaRow = {
block_number: number;
timestamp: number;
user_address: string;
token_address: string;
token_balance: bigint;
token_symbol?: string;
usd_price?: number;
};

export type UserPosition = {
user: string;
token0: {
address: string,
balance: bigint,
}
token1: {
address: string,
balance: bigint,
}
}
Loading

0 comments on commit 4831828

Please sign in to comment.