From f37f8591f622adfe3e10d3bb8233131d2215d8c2 Mon Sep 17 00:00:00 2001 From: quangdz1704 Date: Sat, 3 Feb 2024 13:43:14 +0700 Subject: [PATCH] feat: add apr boost --- packages/oraidex-server/src/helper.ts | 7 ++- packages/oraidex-sync/src/constants.ts | 2 + packages/oraidex-sync/src/db.ts | 13 +++++ packages/oraidex-sync/src/helper.ts | 23 ++++++++ packages/oraidex-sync/src/index.ts | 10 +++- packages/oraidex-sync/src/pairs.ts | 10 ++-- packages/oraidex-sync/src/pool-helper.ts | 72 +++++++++++++++++++++--- 7 files changed, 119 insertions(+), 18 deletions(-) diff --git a/packages/oraidex-server/src/helper.ts b/packages/oraidex-server/src/helper.ts index 035b0e2c..8d0f2be9 100644 --- a/packages/oraidex-server/src/helper.ts +++ b/packages/oraidex-server/src/helper.ts @@ -24,7 +24,8 @@ import { PoolAmountHistory, calculatePriceByPool, PairInfoData, - findPairAddress + findPairAddress, + getAvgPoolLiquidities } from "@oraichain/oraidex-sync"; import bech32 from "bech32"; import "dotenv/config"; @@ -174,6 +175,7 @@ export const getAllPoolsInfo = async () => { const pools = await getPoolsFromDuckDb(); const allPoolApr = await getPoolAprsFromDuckDb(); const allLiquidities = await getPoolLiquidities(pools); + const avgLiquidities = await getAvgPoolLiquidities(pools); const allPoolAmounts = await getPoolAmounts(pools); const allPoolsInfo: PairInfoDataResponse[] = pools.map((pool, index) => { @@ -203,6 +205,7 @@ export const getAllPoolsInfo = async () => { fee7Days: poolFee.fee.toString(), apr: poolApr.apr, totalLiquidity: allLiquidities[index], + avgLiquidities: avgLiquidities[pool.liquidityAddr], rewardPerSec: poolApr.rewardPerSec, offerPoolAmount: allPoolAmounts[index].offerPoolAmount, askPoolAmount: allPoolAmounts[index].askPoolAmount, @@ -210,7 +213,7 @@ export const getAllPoolsInfo = async () => { } as PairInfoDataResponse; }); - return allPoolsInfo; + return allPoolsInfo.filter(Boolean); } catch (error) { console.log({ errorGetAllPoolsInfo: error }); } diff --git a/packages/oraidex-sync/src/constants.ts b/packages/oraidex-sync/src/constants.ts index 8d978e66..e61f639b 100644 --- a/packages/oraidex-sync/src/constants.ts +++ b/packages/oraidex-sync/src/constants.ts @@ -32,6 +32,8 @@ export const ORAIXOCH_INFO = { }; export const OCH_PRICE = 0.4; // usdt +export const DAYS_PER_WEEK = 7; +export const DAYS_PER_YEAR = 365; export const SEC_PER_YEAR = 60 * 60 * 24 * 365; export const network = { factory: FACTORY_CONTRACT, diff --git a/packages/oraidex-sync/src/db.ts b/packages/oraidex-sync/src/db.ts index 56198cf2..4ee4cf5e 100644 --- a/packages/oraidex-sync/src/db.ts +++ b/packages/oraidex-sync/src/db.ts @@ -477,6 +477,19 @@ export class DuckDb { return result[0] as PoolAmountHistory; } + async getLpAmountByTime(pairAddr: string, timestamp: number) { + const result = await this.conn.all( + ` + SELECT * FROM lp_amount_history + WHERE pairAddr = ? AND timestamp >= ? + ORDER BY timestamp ASC + `, + pairAddr, + timestamp + ); + return result as PoolAmountHistory[]; + } + async insertPoolAmountHistory(ops: PoolAmountHistory[]) { await this.insertBulkData(ops, "lp_amount_history"); } diff --git a/packages/oraidex-sync/src/helper.ts b/packages/oraidex-sync/src/helper.ts index d8f8fb06..f127fde9 100644 --- a/packages/oraidex-sync/src/helper.ts +++ b/packages/oraidex-sync/src/helper.ts @@ -457,6 +457,29 @@ async function getAllFees(): Promise { } // ==== end get fee pair ====> +// get avg liquidity of pair from assetInfos by timestamp +export const getAvgPairLiquidity = async (poolInfo: PairInfoData): Promise => { + const tf = 7 * 24 * 60 * 60; // second of 7 days + const oneWeekBeforeNow = getSpecificDateBeforeNow(new Date(), tf).getTime() / 1000; + + const duckDb = DuckDb.instances; + const poolAmount = await duckDb.getLpAmountByTime(poolInfo.pairAddr, oneWeekBeforeNow); + const numberOfRecords = poolAmount?.length; + + if (!poolAmount || !numberOfRecords) return 0; + const totalLiquidity7Days = poolAmount.reduce((acc, cur) => { + acc = acc + Number(cur.offerPoolAmount) * Math.pow(10, -6); + return acc; + }, 0); + + const avgLiquidity = totalLiquidity7Days / numberOfRecords; + + const baseAssetInfo = JSON.parse(poolInfo.firstAssetInfo); + const priceBaseAssetInUsdt = await getPriceAssetByUsdt(baseAssetInfo); + + return priceBaseAssetInUsdt * avgLiquidity * 2; +}; + export function getDate24hBeforeNow(time: Date) { const twentyFourHoursInMilliseconds = 24 * 60 * 60 * 1000; // 24 hours in milliseconds const date24hBeforeNow = new Date(time.getTime() - twentyFourHoursInMilliseconds); diff --git a/packages/oraidex-sync/src/index.ts b/packages/oraidex-sync/src/index.ts index 878711fb..a42730a1 100644 --- a/packages/oraidex-sync/src/index.ts +++ b/packages/oraidex-sync/src/index.ts @@ -1,11 +1,13 @@ import { SyncData, Txs, WriteData } from "@oraichain/cosmos-rpc-sync"; import "dotenv/config"; import { DuckDb } from "./db"; -import { concatAprHistoryToUniqueKey, concatLpHistoryToUniqueKey, getSymbolFromAsset } from "./helper"; +import { concatAprHistoryToUniqueKey, concatLpHistoryToUniqueKey, getAllFees, getSymbolFromAsset } from "./helper"; import { parseAssetInfo, parsePoolAmount } from "./parse"; import { + calculateBoostApr, fetchAprResult, getAllPairInfos, + getAvgPoolLiquidities, getPairByAssetInfos, getPoolInfos, getPoolLiquidities, @@ -135,11 +137,15 @@ class OraiDexSync { try { const pools = await this.duckDb.getPools(); const allLiquidities = await getPoolLiquidities(pools); + const avgLiquidities = await getAvgPoolLiquidities(pools); + const allFee7Days = await getAllFees(); const { allAprs, allTotalSupplies, allBondAmounts, allRewardPerSec } = await fetchAprResult( pools, allLiquidities ); + const boostAPR = calculateBoostApr(avgLiquidities, allFee7Days); + const poolAprs = allAprs.map((apr, index) => { return { uniqueKey: concatAprHistoryToUniqueKey({ @@ -155,7 +161,7 @@ class OraiDexSync { totalSupply: allTotalSupplies[index], totalBondAmount: allBondAmounts[index], rewardPerSec: JSON.stringify(allRewardPerSec[index]), - apr, + apr: apr + boostAPR[pools[index].liquidityAddr], timestamp: Date.now() } as PoolApr; }); diff --git a/packages/oraidex-sync/src/pairs.ts b/packages/oraidex-sync/src/pairs.ts index 8447765d..17f96484 100644 --- a/packages/oraidex-sync/src/pairs.ts +++ b/packages/oraidex-sync/src/pairs.ts @@ -34,11 +34,6 @@ export const pairs: PairMapping[] = [ symbols: ["ORAIX", "ORAI"], factoryV1: true }, - { - asset_infos: [{ token: { contract_addr: scOraiCw20Address } }, { native_token: { denom: ORAI } }], - lp_token: pairLpTokens.SCORAI_ORAI, - symbols: ["scORAI", "ORAI"] - }, { asset_infos: [{ native_token: { denom: ORAI } }, { native_token: { denom: atomIbcDenom } }], lp_token: pairLpTokens.ATOM_ORAI, @@ -74,6 +69,11 @@ export const pairs: PairMapping[] = [ symbols: ["MILKY", "USDT"], factoryV1: true }, + { + asset_infos: [{ token: { contract_addr: scOraiCw20Address } }, { native_token: { denom: ORAI } }], + lp_token: pairLpTokens.SCORAI_ORAI, + symbols: ["scORAI", "ORAI"] + }, { asset_infos: [{ native_token: { denom: ORAI } }, { token: { contract_addr: usdcCw20Address } }], lp_token: pairLpTokens.USDC_ORAI, diff --git a/packages/oraidex-sync/src/pool-helper.ts b/packages/oraidex-sync/src/pool-helper.ts index 70db2040..1618b193 100644 --- a/packages/oraidex-sync/src/pool-helper.ts +++ b/packages/oraidex-sync/src/pool-helper.ts @@ -10,7 +10,19 @@ import { } from "@oraichain/oraidex-contracts-sdk"; import { PoolResponse } from "@oraichain/oraidex-contracts-sdk/build/OraiswapPair.types"; import { isEqual } from "lodash"; -import { OCH_PRICE, ORAI, ORAIXOCH_INFO, SEC_PER_YEAR, atomic, network, oraiInfo, usdtInfo } from "./constants"; +import { + DAYS_PER_WEEK, + DAYS_PER_YEAR, + OCH_PRICE, + ORAI, + ORAIXOCH_INFO, + SEC_PER_YEAR, + atomic, + network, + oraiInfo, + truncDecimals, + usdtInfo +} from "./constants"; import { calculatePriceByPool, getCosmwasmClient, @@ -19,7 +31,10 @@ import { concatAprHistoryToUniqueKey, concatLpHistoryToUniqueKey, getPairLiquidity, - recalculateTotalShare + recalculateTotalShare, + PoolFee, + getAvgPairLiquidity, + getAllFees } from "./helper"; import { DuckDb } from "./db"; import { pairs } from "./pairs"; @@ -262,6 +277,28 @@ export const calculateAprResult = async ( return aprResult; }; +export const calculateBoostApr = ( + avgLiquidities: Record, + allFee7Days: PoolFee[] +): Record => { + const aprResult = {}; + + for (const _pair of pairs) { + const lpTokenAddress = _pair.lp_token; + const liquidityAmount = avgLiquidities[lpTokenAddress]; + + const poolFee = allFee7Days.find((item) => { + return JSON.stringify(item.assetInfos) === JSON.stringify(_pair.asset_infos); + }); + + const yearlyFees = (DAYS_PER_YEAR * Number(poolFee.fee) * Math.pow(10, -truncDecimals)) / DAYS_PER_WEEK; + + aprResult[lpTokenAddress] = !liquidityAmount ? 0 : (100 * yearlyFees) / liquidityAmount || 0; + } + + return aprResult; +}; + export const fetchAprResult = async (pairInfos: PairInfoData[], allLiquidities: number[]) => { const liquidityAddrs = pairInfos.map((pair) => pair.liquidityAddr); try { @@ -302,6 +339,15 @@ export const getPoolLiquidities = async (pools: PairInfoData[]): Promise> => { + const allLiquidities: Record = {}; + for (const pool of pools) { + const liquidity = await getAvgPairLiquidity(pool); + allLiquidities[pool.liquidityAddr] = liquidity; + } + return allLiquidities; +}; + export const getPoolAmounts = async (pools: PairInfoData[]): Promise => { const duckDb = DuckDb.instances; const allPoolAmounts: PoolAmountHistory[] = []; @@ -341,20 +387,28 @@ export const triggerCalculateApr = async (assetInfos: [AssetInfo, AssetInfo][], const pools = await getAllPoolByAssetInfos(assetInfos); const allLiquidities = await getPoolLiquidities(pools); - const poolAprInfos = []; + const allFee7Days = await getAllFees(); + const avgLiquidities = await getAvgPoolLiquidities(pools); + const poolAprInfos: { + aprInfo: PoolApr; + poolInfo: PairInfoData; + }[] = []; for (const pool of pools) { const aprInfo = await duckDb.getLatestPoolApr(pool.pairAddr); - poolAprInfos.push(aprInfo); + poolAprInfos.push({ aprInfo, poolInfo: pool }); } - const allTotalSupplies = poolAprInfos.map((item) => item.totalSupply); - const allBondAmounts = poolAprInfos.map((info) => info.totalBondAmount); - const allRewardPerSecs = poolAprInfos.map((info) => (info.rewardPerSec ? JSON.parse(info.rewardPerSec) : null)); + const allTotalSupplies = poolAprInfos.map((item) => item.aprInfo.totalSupply); + const allBondAmounts = poolAprInfos.map((info) => info.aprInfo.totalBondAmount); + const allRewardPerSecs = poolAprInfos.map((info) => + info.aprInfo.rewardPerSec ? JSON.parse(info.aprInfo.rewardPerSec) : null + ); const APRs = await calculateAprResult(allLiquidities, allTotalSupplies, allBondAmounts, allRewardPerSecs); + const boostAPR = calculateBoostApr(avgLiquidities, allFee7Days); const newPoolAprs = poolAprInfos.map((poolApr, index) => { return { - ...poolApr, + ...poolApr.aprInfo, height: newOffset, apr: APRs[index], uniqueKey: concatAprHistoryToUniqueKey({ @@ -362,7 +416,7 @@ export const triggerCalculateApr = async (assetInfos: [AssetInfo, AssetInfo][], supply: allTotalSupplies[index], bond: allBondAmounts[index], reward: allRewardPerSecs[index], - apr: APRs[index], + apr: APRs[index] + boostAPR[poolApr.poolInfo.liquidityAddr], pairAddr: pools[index].pairAddr }), timestamp: Date.now() // use timestamp date.now() because we just need to have a order of apr.