Skip to content

Commit

Permalink
undefinedorgcr#83 fetching top pools
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianvrj committed Feb 7, 2025
1 parent 1585791 commit df773ec
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 140 deletions.
1 change: 1 addition & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @type {import('next').NextConfig} */

const nextConfig = {
reactStrictMode: true,
async rewrites() {
return [
{
Expand Down
140 changes: 125 additions & 15 deletions src/apis/ekuboApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Contract, num } from 'starknet';
import axios, { AxiosError } from 'axios';

import pLimit from 'p-limit';
import { Token } from '@/types/Tokens';
import { Pool } from '@/types/Pool';
import { PoolState } from '@/types/PoolState';
Expand All @@ -10,10 +10,13 @@ import { normalizeHex, tickToPrice } from '@/lib/utils';
import { EKUBO_POSITIONS_MAINNET_ADDRESS, provider } from '@/constants';
import { EKUBO_POSITIONS } from '@/abis/EkuboPositions';
import { fetchCryptoPrice } from './pragma';
import { TokenPriceCache } from '@/lib/cache/tokenPriceCache';

const BASE_URL = "https://mainnet-api.ekubo.org";
const QUOTER_URL = "https://quoter-mainnet-api.ekubo.org";

const tokenPriceCache = new TokenPriceCache(60000);

// TODO: implement this tokens
// export const TOP_TOKENS_SYMBOL = [
// "STRK", "USDC", "ETH", "EKUBO", "DAI", "WBTC",
Expand All @@ -25,6 +28,31 @@ export const TOP_TOKENS_SYMBOL = [
"STRK", "USDC", "ETH"
];


const TOP_PAIRS = [
{ "token0": "ETH", "token1": "USDC" },
{ "token0": "STRK", "token1": "USDC" },
{ "token0": "xSTRK", "token1": "STRK" },
{ "token0": "LORDS", "token1": "ETH" },
{ "token0": "USDC", "token1": "USDT" },
{ "token0": "STRK", "token1": "ETH" },
{ "token0": "STRK", "token1": "EKUBO" },
{ "token0": "USDC", "token1": "EKUBO" },
{ "token0": "WBTC", "token1": "ETH" },
{ "token0": "LORDS", "token1": "STRK" },
// { "token0": "STAM", "token1": "STRK" },
{ "token0": "USDC", "token1": "DAI" },
// { "token0": "CASH", "token1": "ETH" },
{ "token0": "ETH", "token1": "EKUBO" },
// { "token0": "NSTR", "token1": "STRK" },
// { "token0": "DAIv0", "token1": "ETH" },
// { "token0": "DAIv0", "token1": "USDC" },
{ "token0": "wstETH", "token1": "ETH" },
// { "token0": "CASH", "token1": "USDC" },
{ "token0": "ETH", "token1": "USDT" },
{ "token0": "WBTC", "token1": "USDC" }
];

// Utility functions
const getTokenDecimals = (symbol: string): number =>
symbol === "USDC" || symbol === "USDT" ? 6 : 18;
Expand Down Expand Up @@ -107,6 +135,84 @@ export async function fetchPool(t0: Token, t1: Token, fee: number): Promise<Pool
}
}

export async function fetchPoolByAddress(t0symbol: string, t1symbol: string): Promise<Pool | null> {
try {
const { data } = await axios.get(
`${BASE_URL}/pair/${t0symbol}/${t1symbol}/pools`
);
return data.topPools;
} catch (error) {
console.log('Error fetching pool by address:', error);
return null;
}
}

const sortPoolsByFees = (pools: any) => {
return pools.sort((a: any, b: any) => {
const sumA = BigInt(a.pool.fees0_24h) + BigInt(a.pool.fees1_24h);
const sumB = BigInt(b.pool.fees0_24h) + BigInt(b.pool.fees1_24h);

// Orden descendente: mayores sumas primero
if (sumB > sumA) return 1;
if (sumB < sumA) return -1;
return 0;
});
};

const limit = pLimit(5);

export async function fetchTopPools() {
try {
const pools: Array<any> = [];
const tokens = await fetchTokens();

await Promise.all(
TOP_PAIRS.map(async (pair) => {
const { data } = await axios.get(
`${BASE_URL}/pair/${pair.token0}/${pair.token1}/pools`
);
data.topPools.forEach((pool: Pool) => {
const t0 = tokens.find((token: Token) => token.symbol.toLowerCase() === pair.token0.toLowerCase());
const t1 = tokens.find((token: Token) => token.symbol.toLowerCase() === pair.token1.toLowerCase());
pools.push({
token0: t0,
token1: t1,
pool: pool,
totalFees: 0,
totalTvl: 0,
});
});
})
);
const sortedPools = sortPoolsByFees(pools).slice(0, 20);

const updatedPools = await Promise.all(
sortedPools.map((pool: any) =>
limit(async () => {
pool.totalFees = (await valToUsd(
pool.token0,
pool.token1,
pool.pool.fees0_24h,
pool.pool.fees1_24h
));
pool.totalTvl = (await valToUsd(
pool.token0,
pool.token1,
pool.pool.tvl0_total,
pool.pool.tvl1_total
));
return pool;
})
)
);

return sortPoolsByFees(updatedPools).slice(0, 20);
} catch (error) {
console.error('Error fetching top pairs:', error);
throw error;
}
}

export async function fetchPoolKeyHash(t0: Token, t1: Token, fee: number): Promise<PoolState | undefined> {
try {
const { data } = await axios.get(`${BASE_URL}/pools`);
Expand All @@ -125,23 +231,27 @@ export async function fetchPoolKeyHash(t0: Token, t1: Token, fee: number): Promi
}
}

export async function fetchTvl(t0: Token, t1: Token): Promise<number> {
export async function valToUsd(t0: Token, t1: Token, amount0: number, amount1: number): Promise<number> {
try {
const { data } = await axios.get(
`${BASE_URL}/pair/${t0.l2_token_address}/${t1.l2_token_address}/tvl`
);

const [t0price, t1price] = await Promise.all([
fetchCryptoPrice(t0.symbol),
fetchCryptoPrice(t1.symbol)
]);

const val1 = formatTokenAmount(data.tvlByToken[0].balance, t0.symbol) * t0price;
const val2 = formatTokenAmount(data.tvlByToken[1].balance, t1.symbol) * t1price;
let t0price = tokenPriceCache.get(t0.symbol);
let t1price = tokenPriceCache.get(t1.symbol);
console.log("Cached t0Price: ", t0.symbol, t0price);
console.log("Cached t1Price: ", t1.symbol, t1price);
if (t0price == null) {
t0price = await fetchCryptoPrice(t0.symbol);
tokenPriceCache.set(t0.symbol, t0price);
}
if (t1price == null) {
t1price = await fetchCryptoPrice(t1.symbol);
tokenPriceCache.set(t1.symbol, t1price);
}

const val1 = formatTokenAmount(amount0, t0.symbol) * t0price;
const val2 = formatTokenAmount(amount1, t1.symbol) * t1price;

return val1 + val2;
} catch (error) {
console.error('Error fetching TVL:', error);
console.error('Error fetching USD price:', error);
throw error;
}
}
Expand Down Expand Up @@ -173,7 +283,7 @@ export async function fetchLiquidityInRange(
totalLiquidity = BigInt(0);

for (const tick in liquidityMap) {
const liquidity = liquidityMap[tick];
const liquidity = liquidityMap[tick];
const price = tickToPrice(Number(tick)) * (10 ** (t0.decimals - t1.decimals));
if (price >= minPrice && price <= maxPrice) {
totalLiquidity += liquidity;
Expand Down
6 changes: 2 additions & 4 deletions src/app/calculators/ekubo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client'
import { fetchLatestPairVolume, fetchTokens, fetchTvl, TOP_TOKENS_SYMBOL } from "@/apis/ekuboApi";
import { fetchLatestPairVolume, fetchTokens, TOP_TOKENS_SYMBOL } from "@/apis/ekuboApi";
import Footer from "@/components/ui/footer";
import Navbar from "@/components/ui/navbar";
import { Token } from "@/types/Tokens";
Expand All @@ -24,7 +24,6 @@ export default function Calculators() {
const [token1, setToken1] = useState<Token | undefined>(undefined);
const [volume, setVolume] = useState<number | null>(null);
const [initialPrice, setInitialPrice] = useState(0);
const [poolLiquidity, setPoolLiquidity] = useState(0);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -59,7 +58,6 @@ export default function Calculators() {
setIsLoading(true);
const t0price = Number(await fetchCryptoPrice(token0.symbol));
const t1price = Number(await fetchCryptoPrice(token1.symbol));
setPoolLiquidity(await fetchTvl(token0, token1));
setVolume(await fetchLatestPairVolume(token0, token1, t0price, t1price));
setInitialPrice(t0price / t1price);
setShowCalculator(true);
Expand Down Expand Up @@ -157,7 +155,7 @@ export default function Calculators() {
feeRate={fee}
initialPrice={initialPrice}
volume={volume}
liquidity={poolLiquidity}
liquidity={1}
/>
)}

Expand Down
Loading

0 comments on commit df773ec

Please sign in to comment.