Skip to content

Commit

Permalink
Merge pull request #18 from izumiFinance/add-izumiswap-adapter
Browse files Browse the repository at this point in the history
add izumiswap adapter
  • Loading branch information
RamonReis authored Apr 16, 2024
2 parents 20fc52a + b82afa7 commit 44075e9
Show file tree
Hide file tree
Showing 7 changed files with 972 additions and 0 deletions.
32 changes: 32 additions & 0 deletions adapters/izumiswap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "izumiswap",
"version": "1.0.0",
"description": "",
"main": "index.js",
"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": "ISC",
"dependencies": {
"@types/big.js": "^6.2.2",
"big.js": "^6.2.1",
"bignumber.js": "^9.1.2",
"csv-parser": "^3.0.0",
"decimal.js-light": "^2.5.1",
"fast-csv": "^5.0.1",
"jsbi": "^4.3.0",
"tiny-invariant": "^1.3.1",
"toformat": "^2.0.0",
"viem": "^2.8.13"
},
"devDependencies": {
"@types/node": "^20.11.17",
"typescript": "^5.3.3"
}
}
30 changes: 30 additions & 0 deletions adapters/izumiswap/src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export const enum CHAINS{
MODE = 34443,
LINEA = 59144,
}
export const enum PROTOCOLS{
SUPSWAP = 0,
IZISWAP = 1,
}

export const enum AMM_TYPES{
UNISWAPV3 = 0,
IZISWAP = 1
}

export const SUBGRAPH_URLS = {
[CHAINS.MODE]: {
[PROTOCOLS.IZISWAP]: {
[AMM_TYPES.IZISWAP]: "https://graph-node-api.izumi.finance/query/subgraphs/name/izi-swap-mode"
}
},
[CHAINS.LINEA]: {
[PROTOCOLS.IZISWAP]: {
[AMM_TYPES.IZISWAP]: "https://api.studio.thegraph.com/query/24334/izumi-subgraph-linea/version/latest"
}
}
}
export const RPC_URLS = {
[CHAINS.MODE]: "https://rpc.goldsky.com",
[CHAINS.LINEA]: "https://rpc.linea.build",
}
137 changes: 137 additions & 0 deletions adapters/izumiswap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import BigNumber from "bignumber.js";
import { CHAINS, PROTOCOLS, AMM_TYPES } from "./config/config";
import { getLPValueByUserAndPoolFromPositions, getPositionAtBlock, getPositionDetailsFromPosition, getPositionsForAddressByPoolAtBlock, getTimestampAtBlock } from "./utils/subgraphDetails";
(BigInt.prototype as any).toJSON = function () {
return this.toString();
};

import { promisify } from 'util';
import stream from 'stream';
import csv from 'csv-parser';
import fs from 'fs';
import { format } from 'fast-csv';
import { write } from 'fast-csv';

//Uncomment the following lines to test the getPositionAtBlock function

// const position = getPositionAtBlock(
// 0, // block number 0 for latest block
// 2, // position id
// CHAINS.MODE, // chain id
// PROTOCOLS.SUPSWAP, // protocol
// AMM_TYPES.UNISWAPV3 // amm type
// );
// position.then((position) => {
// // print response
// const result = getPositionDetailsFromPosition(position);
// console.log(`${JSON.stringify(result,null, 4)}
// `)
// });

interface BlockData {
blockNumber: number;
blockTimestamp: number;
}

interface CSVRow {
block_number: number;
timestamp: number;
user_address: string;
token_address: string;
token_balance: string;
token_symbol: string;
usd_price: number;
}

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 snapshotBlocks = [
3055683
]; //await readBlocksFromCSV('src/sdk/mode_chain_daily_blocks.csv');

const csvRows: CSVRow[] = [];

for (let block of snapshotBlocks) {
const positions = await getPositionsForAddressByPoolAtBlock(
block, "", "", CHAINS.LINEA, PROTOCOLS.IZISWAP, AMM_TYPES.IZISWAP
);

console.log(`Block: ${block}`);
console.log("Positions: ", positions.length);

const timestamp = await getTimestampAtBlock(block);

// Assuming this part of the logic remains the same
let positionsWithUSDValue = positions.map(getPositionDetailsFromPosition);
let lpValueByUsers = getLPValueByUserAndPoolFromPositions(positionsWithUSDValue);

lpValueByUsers.forEach((value, key) => {
value.forEach((tokenBalance, tokenKey) => {
// Accumulate CSV row data
csvRows.push({
block_number: block,
timestamp: timestamp / 1000,
user_address: key,
token_address: tokenKey,
token_symbol: tokenBalance.tokenSymbol,
token_balance: tokenBalance.tokenBalance.toString(),
usd_price: tokenBalance.usdPrice,
});
});
});
}

// 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 (blocks: BlockData) => {
const { blockNumber, blockTimestamp } = blocks
const positions = await getPositionsForAddressByPoolAtBlock(
blockNumber, "", "", CHAINS.LINEA, PROTOCOLS.IZISWAP, AMM_TYPES.IZISWAP
);

let positionsWithUSDValue = positions.map(getPositionDetailsFromPosition);
let lpValueByUsers = getLPValueByUserAndPoolFromPositions(positionsWithUSDValue);

const csvRows: CSVRow[] = [];
lpValueByUsers.forEach((value, key) => {
value.forEach((tokenBalance, tokenKey) => {
csvRows.push({
block_number: blockNumber,
timestamp: blockTimestamp,
user_address: key,
token_address: tokenKey,
token_symbol: tokenBalance.tokenSymbol,
token_balance: tokenBalance.tokenBalance.toString(),
usd_price: tokenBalance.usdPrice,
});
});
});

return csvRows
};

getData().then(() => {
console.log("Done");
});
143 changes: 143 additions & 0 deletions adapters/izumiswap/src/utils/positionMath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import BigNumber from "bignumber.js";

export interface TokenInfoFormatted {
chainId: number;
name?: string;
symbol: string;
address: string;
decimal: number;
}
export interface Liquidity {
leftPoint: number;
rightPoint: number;
liquidity: string;
decimalX: number;
decimalY: number;
}

export const point2PoolPriceUndecimalSqrt = (point: number) : number => {
return (1.0001 ** point) ** 0.5;
}

export const _getAmountY = (
liquidity: BigNumber,
sqrtPriceL: number,
sqrtPriceR: number,
sqrtRate: number,
upper: boolean,
): BigNumber => {
const numerator = sqrtPriceR - sqrtPriceL;
const denominator = sqrtRate - 1;
if (!upper) {
const amount = new BigNumber(liquidity.times(numerator).div(denominator).toFixed(0, 3));
return amount;
} else {
const amount = new BigNumber(liquidity.times(numerator).div(denominator).toFixed(0, 2));
return amount;
}
}

export const _getAmountX = (
liquidity: BigNumber,
leftPt: number,
rightPt: number,
sqrtPriceR: number,
sqrtRate: number,
upper: boolean,
): BigNumber => {
const sqrtPricePrPc = Math.pow(sqrtRate, rightPt - leftPt + 1);
const sqrtPricePrPd = Math.pow(sqrtRate, rightPt + 1);

const numerator = sqrtPricePrPc - sqrtRate;
const denominator = sqrtPricePrPd - sqrtPriceR;

if (!upper) {
const amount = new BigNumber(liquidity.times(numerator).div(denominator).toFixed(0, 3));
return amount;
} else {
const amount = new BigNumber(liquidity.times(numerator).div(denominator).toFixed(0, 2));
return amount;
}
}

export const _liquidity2AmountYAtPoint = (
liquidity: BigNumber,
sqrtPrice: number,
upper: boolean
): BigNumber => {
const amountY = liquidity.times(sqrtPrice);
if (!upper) {
return new BigNumber(amountY.toFixed(0, 3));
} else {
return new BigNumber(amountY.toFixed(0, 2));
}
}

export const _liquidity2AmountXAtPoint = (
liquidity: BigNumber,
sqrtPrice: number,
upper: boolean
): BigNumber => {
const amountX = liquidity.div(sqrtPrice);
if (!upper) {
return new BigNumber(amountX.toFixed(0, 3));
} else {
return new BigNumber(amountX.toFixed(0, 2));
}
}

export const amount2Decimal = (amount: BigNumber, decimal: number): number => {
return Number(amount.div(10 ** decimal))
}

export const getLiquidityValue = (
liquidity: Liquidity,
currentPoint: number
): {amountXDecimal: number, amountYDecimal: number, amountX: BigNumber, amountY: BigNumber} => {

let amountX = new BigNumber(0);
let amountY = new BigNumber(0);
const liquid = liquidity.liquidity;
const sqrtRate = Math.sqrt(1.0001);
const leftPtNum = Number(liquidity.leftPoint);
const rightPtNum = Number(liquidity.rightPoint);
// compute amountY without currentPt
if (leftPtNum < currentPoint) {
const rightPt: number = Math.min(currentPoint, rightPtNum);
const sqrtPriceR = point2PoolPriceUndecimalSqrt(rightPt);
const sqrtPriceL = point2PoolPriceUndecimalSqrt(leftPtNum);
amountY = _getAmountY(new BigNumber(liquid), sqrtPriceL, sqrtPriceR, sqrtRate, false);
}

// compute amountX without currentPt
if (rightPtNum > currentPoint + 1) {
const leftPt: number = Math.max(currentPoint + 1, leftPtNum);
const sqrtPriceR = point2PoolPriceUndecimalSqrt(rightPtNum);
amountX = _getAmountX(new BigNumber(liquid), leftPt, rightPtNum, sqrtPriceR, sqrtRate, false);
}

// compute amountX and amountY on currentPt
if (leftPtNum <= currentPoint && rightPtNum > currentPoint) {
const liquidityValue = new BigNumber(liquidity.liquidity);
const maxLiquidityYAtCurrentPt = new BigNumber(0);
const liquidityYAtCurrentPt = liquidityValue.gt(maxLiquidityYAtCurrentPt) ? maxLiquidityYAtCurrentPt : liquidityValue;
const liquidityXAtCurrentPt = liquidityValue.minus(liquidityYAtCurrentPt);
const currentSqrtPrice = point2PoolPriceUndecimalSqrt(currentPoint);
amountX = amountX.plus(_liquidity2AmountXAtPoint(liquidityXAtCurrentPt, currentSqrtPrice, false));
amountY = amountY.plus(_liquidity2AmountYAtPoint(liquidityYAtCurrentPt, currentSqrtPrice, false));
}
const amountXDecimal:number = amount2Decimal(
amountX, liquidity.decimalX
)?? 0;
const amountYDecimal:number = amount2Decimal(
amountY, liquidity.decimalY
)?? 0;
return {
amountX, amountXDecimal,
amountY, amountYDecimal
};
}

export const PositionMath = {
getLiquidityValue
}
Loading

0 comments on commit 44075e9

Please sign in to comment.