Skip to content

Commit

Permalink
Merge pull request #152 from oraichain/feat/apr_boost
Browse files Browse the repository at this point in the history
[WIP] - Feat: add apr boost
  • Loading branch information
haunv3 authored Feb 6, 2024
2 parents 8fcee6b + 7d4c4e9 commit d5645b9
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 40 deletions.
2 changes: 1 addition & 1 deletion packages/oraidex-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oraichain/oraidex-server",
"version": "1.0.45",
"version": "1.0.46",
"main": "dist/index.js",
"bin": "dist/index.js",
"license": "MIT",
Expand Down
8 changes: 6 additions & 2 deletions packages/oraidex-server/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
PoolAmountHistory,
calculatePriceByPool,
PairInfoData,
findPairAddress
findPairAddress,
getAvgPoolLiquidities
} from "@oraichain/oraidex-sync";
import bech32 from "bech32";
import "dotenv/config";
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -202,15 +204,17 @@ export const getAllPoolsInfo = async () => {
volume24Hour: poolVolume.volume.toString(),
fee7Days: poolFee.fee.toString(),
apr: poolApr.apr,
aprBoost: poolApr?.aprBoost ?? 0,
totalLiquidity: allLiquidities[index],
avgLiquidities: avgLiquidities[pool.liquidityAddr],
rewardPerSec: poolApr.rewardPerSec,
offerPoolAmount: allPoolAmounts[index].offerPoolAmount,
askPoolAmount: allPoolAmounts[index].askPoolAmount,
totalSupply: poolApr.totalSupply
} as PairInfoDataResponse;
});

return allPoolsInfo;
return allPoolsInfo.filter(Boolean);
} catch (error) {
console.log({ errorGetAllPoolsInfo: error });
}
Expand Down
1 change: 1 addition & 0 deletions packages/oraidex-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ app.get("/v1/pool-detail", async (req: Request<{}, {}, {}, GetPoolDetailQuery>,
volume24Hour: poolVolume?.toString() ?? "0",
volume24hChange: percentVolumeChange,
apr: poolApr?.apr ?? 0,
aprBoost: poolApr?.aprBoost ?? 0,
totalLiquidity: poolLiquidity,
rewardPerSec: poolApr?.rewardPerSec
};
Expand Down
2 changes: 2 additions & 0 deletions packages/oraidex-sync/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 20 additions & 3 deletions packages/oraidex-sync/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -500,6 +513,10 @@ export class DuckDb {
await this.conn.run(`ALTER TABLE pool_apr ADD COLUMN IF NOT EXISTS timestamp UBIGINT DEFAULT 0`);
}

async addAprBoostColToPoolAprTable() {
await this.conn.run(`ALTER TABLE pool_apr ADD COLUMN IF NOT EXISTS aprBoost DOUBLE DEFAULT 0`);
}

async insertPoolAprs(poolAprs: PoolApr[]) {
await this.insertBulkData(poolAprs, "pool_apr");
}
Expand All @@ -522,18 +539,18 @@ export class DuckDb {
const result = await this.conn.all(
`
WITH RankedPool AS (
SELECT pairAddr, apr, rewardPerSec, totalSupply, height,
SELECT pairAddr, apr, rewardPerSec, totalSupply, height, aprBoost,
ROW_NUMBER() OVER (PARTITION BY pairAddr ORDER BY timestamp DESC) AS rn
FROM pool_apr
)
SELECT pairAddr, apr, rewardPerSec, totalSupply
SELECT pairAddr, apr, rewardPerSec, totalSupply, aprBoost
FROM RankedPool
WHERE rn = 1
ORDER BY apr
;
`
);
return result as Pick<PoolApr, "apr" | "pairAddr" | "rewardPerSec" | "totalSupply">[];
return result as Pick<PoolApr, "apr" | "pairAddr" | "rewardPerSec" | "totalSupply" | "aprBoost">[];
}

async getMyEarnedAmount(stakerAddress: string, startTime: number, endTime: number, stakingAssetDenom?: string) {
Expand Down
23 changes: 23 additions & 0 deletions packages/oraidex-sync/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,29 @@ async function getAllFees(): Promise<PoolFee[]> {
}
// ==== end get fee pair ====>

// get avg liquidity of pair from assetInfos by timestamp
export const getAvgPairLiquidity = async (poolInfo: PairInfoData): Promise<number> => {
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 + toDisplay(cur.offerPoolAmount);
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);
Expand Down
18 changes: 14 additions & 4 deletions packages/oraidex-sync/src/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -135,28 +137,35 @@ 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);
// console.log("boostAor", boostAPR);

const poolAprs = allAprs.map((apr, index) => {
const newApr = apr + (boostAPR[pools[index]?.liquidityAddr] || 0);
return {
uniqueKey: concatAprHistoryToUniqueKey({
timestamp: Date.now(),
supply: allTotalSupplies[index],
bond: allBondAmounts[index],
reward: JSON.stringify(allRewardPerSec[index]),
apr,
apr: newApr,
pairAddr: pools[index].pairAddr
}),
pairAddr: pools[index].pairAddr,
height,
totalSupply: allTotalSupplies[index],
totalBondAmount: allBondAmounts[index],
rewardPerSec: JSON.stringify(allRewardPerSec[index]),
apr,
timestamp: Date.now()
apr: newApr,
timestamp: Date.now(),
aprBoost: boostAPR[pools[index]?.liquidityAddr] || 0
} as PoolApr;
});
await this.duckDb.insertPoolAprs(poolAprs);
Expand All @@ -177,6 +186,7 @@ class OraiDexSync {
await this.duckDb.createPoolAprTable();
await this.duckDb.createEarningHistoryTable();
await this.duckDb.addTimestampColToPoolAprTable();
await this.duckDb.addAprBoostColToPoolAprTable();
let currentInd = await this.duckDb.loadHeightSnapshot();
const initialSyncHeight = parseInt(process.env.INITIAL_SYNC_HEIGHT) || 12388825;
// if its' the first time, then we use the height 12388825 since its the safe height for the rpc nodes to include timestamp & new indexing logic
Expand Down
10 changes: 5 additions & 5 deletions packages/oraidex-sync/src/pairs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
78 changes: 67 additions & 11 deletions packages/oraidex-sync/src/pool-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,7 +31,11 @@ import {
concatAprHistoryToUniqueKey,
concatLpHistoryToUniqueKey,
getPairLiquidity,
recalculateTotalShare
recalculateTotalShare,
PoolFee,
getAvgPairLiquidity,
getAllFees,
toDisplay
} from "./helper";
import { DuckDb } from "./db";
import { pairs } from "./pairs";
Expand Down Expand Up @@ -262,6 +278,28 @@ export const calculateAprResult = async (
return aprResult;
};

export const calculateBoostApr = (
avgLiquidities: Record<string, number>,
allFee7Days: PoolFee[]
): Record<string, number> => {
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 * toDisplay(poolFee.fee)) / 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 {
Expand Down Expand Up @@ -302,6 +340,15 @@ export const getPoolLiquidities = async (pools: PairInfoData[]): Promise<number[
return allLiquidities;
};

export const getAvgPoolLiquidities = async (pools: PairInfoData[]): Promise<Record<string, number>> => {
const allLiquidities: Record<string, number> = {};
for (const pool of pools) {
const liquidity = await getAvgPairLiquidity(pool);
allLiquidities[pool.liquidityAddr] = liquidity;
}
return allLiquidities;
};

export const getPoolAmounts = async (pools: PairInfoData[]): Promise<PoolAmountHistory[]> => {
const duckDb = DuckDb.instances;
const allPoolAmounts: PoolAmountHistory[] = [];
Expand Down Expand Up @@ -341,31 +388,40 @@ 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],
apr: APRs[index] + (boostAPR[poolApr.poolInfo?.liquidityAddr] || 0),
uniqueKey: concatAprHistoryToUniqueKey({
timestamp: Date.now(),
supply: allTotalSupplies[index],
bond: allBondAmounts[index],
reward: allRewardPerSecs[index],
apr: APRs[index],
apr: APRs[index] + (boostAPR[poolApr.poolInfo?.liquidityAddr] || 0),
pairAddr: pools[index].pairAddr
}),
timestamp: Date.now() // use timestamp date.now() because we just need to have a order of apr.
timestamp: Date.now(), // use timestamp date.now() because we just need to have a order of apr.
aprBoost: boostAPR[poolApr.poolInfo?.liquidityAddr] || 0
};
});
await duckDb.insertPoolAprs(newPoolAprs);
Expand Down
1 change: 1 addition & 0 deletions packages/oraidex-sync/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ export type PoolApr = {
rewardPerSec: string;
apr: number;
timestamp: number;
aprBoost: number;
};

export type GetPricePairQuery = {
Expand Down
Loading

0 comments on commit d5645b9

Please sign in to comment.