Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
aprs
Browse files Browse the repository at this point in the history
p0mvn committed Aug 6, 2024
1 parent 22b353a commit 397b12a
Showing 4 changed files with 175 additions and 19 deletions.
7 changes: 7 additions & 0 deletions packages/server/src/queries/complex/pools/index.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { z } from "zod";
import { IS_TESTNET } from "../../../env";
import { search, SearchSchema } from "../../../utils/search";
import { PoolRawResponse } from "../../osmosis";
import { PoolIncentives } from "./incentives";
import { getPoolsFromSidecar } from "./providers";

const allPooltypes = [
@@ -25,6 +26,8 @@ export type Pool = {
spreadFactor: RatePretty;
reserveCoins: CoinPretty[];
totalFiatValueLocked: PricePretty;

marketIncentives?: PoolIncentives;
};

/** Async function that provides simplified pools from any data source.
@@ -34,6 +37,7 @@ export type PoolProvider = (params: {
chainList: Chain[];
poolIds?: string[];
minLiquidityUsd?: number;
withMarketIncetives?: boolean;
}) => Promise<Pool[]>;

export const PoolFilterSchema = z.object({
@@ -46,6 +50,8 @@ export const PoolFilterSchema = z.object({
types: z.array(z.enum(allPooltypes)).optional(),
/** Search using exact match with pools denoms */
denoms: z.array(z.string()).optional(),
/** Include market incentive data. */
withMarketIncetives: z.boolean().optional(),
});

/** Params for filtering pools. */
@@ -82,6 +88,7 @@ export async function getPools(
...params,
poolIds: params?.poolIds,
minLiquidityUsd: params?.minLiquidityUsd,
withMarketIncetives: params?.withMarketIncetives,
});

if (params?.types) {
121 changes: 116 additions & 5 deletions packages/server/src/queries/complex/pools/providers/sidecar.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
import { CoinPretty, PricePretty, RatePretty } from "@keplr-wallet/unit";
import { CoinPretty, Dec, PricePretty, RatePretty } from "@keplr-wallet/unit";
import { AssetList, Chain } from "@osmosis-labs/types";
import { timeout } from "@osmosis-labs/utils";
import cachified, { CacheEntry } from "cachified";
import { LRUCache } from "lru-cache";

import { IS_TESTNET } from "../../../../env";
import { EXCLUDED_EXTERNAL_BOOSTS_POOL_IDS, IS_TESTNET } from "../../../../env";
import { PoolRawResponse } from "../../../../queries/osmosis";
import { queryPools } from "../../../../queries/sidecar";
import { queryPools, SQSAprData } from "../../../../queries/sidecar";
import { DEFAULT_LRU_OPTIONS } from "../../../../utils/cache";
import { getAsset } from "../../assets";
import { DEFAULT_VS_CURRENCY } from "../../assets/config";
import { getCosmwasmPoolTypeFromCodeId } from "../env";
import { Pool, PoolType } from "../index";
import { Pool, PoolIncentives, PoolIncentiveType, PoolType } from "../index";

type SidecarPool = Awaited<ReturnType<typeof queryPools>>[number];

const poolsCache = new LRUCache<string, CacheEntry>(DEFAULT_LRU_OPTIONS);

/**
* Pools that are excluded from showing external boost incentives APRs.
*/
const ExcludedExternalBoostPools: string[] =
(EXCLUDED_EXTERNAL_BOOSTS_POOL_IDS ?? []) as string[];

/** Lightly cached pools from sidecar service. */
export function getPoolsFromSidecar({
assetLists,
poolIds,
minLiquidityUsd,
withMarketIncetives: withMarketIncentives,
}: {
assetLists: AssetList[];
chainList: Chain[];
poolIds?: string[];
minLiquidityUsd?: number;
withMarketIncetives?: boolean;
}): Promise<Pool[]> {
return cachified({
cache: poolsCache,
@@ -37,7 +45,11 @@ export function getPoolsFromSidecar({
getFreshValue: async () => {
const sidecarPools = await timeout(
() =>
queryPools({ poolIds, minLiquidityCap: minLiquidityUsd?.toString() }),
queryPools({
poolIds,
minLiquidityCap: minLiquidityUsd?.toString(),
withMarketIncentives: withMarketIncentives,
}),
9_000, // 9 seconds
"sidecarQueryPools"
)();
@@ -74,6 +86,8 @@ function makePoolFromSidecarPool({
// to ease integrations.
if (!reserveCoins && !IS_TESTNET) return;

const pool_id = getPoolIdFromChainPool(sidecarPool.chain_model);

return {
id: getPoolIdFromChainPool(sidecarPool.chain_model),
type: getPoolTypeFromChainPool(sidecarPool.chain_model),
@@ -86,7 +100,104 @@ function makePoolFromSidecarPool({
DEFAULT_VS_CURRENCY,
sidecarPool.liquidity_cap
),

marketIncentives: getMarketincentivesData(pool_id, sidecarPool.apr_data),
};
}

function getMarketincentivesData(
pool_id: string,
apr?: SQSAprData
): PoolIncentives {
let totalUpper = maybeMakeRatePretty(apr?.total_apr.upper ?? 0);
let totalLower = maybeMakeRatePretty(apr?.total_apr.lower ?? 0);
const swapFeeUpper = maybeMakeRatePretty(apr?.swap_fees.upper ?? 0);
const swapFeeLower = maybeMakeRatePretty(apr?.swap_fees.lower ?? 0);
const superfluidUpper = maybeMakeRatePretty(apr?.superfluid.upper ?? 0);
const superfluidLower = maybeMakeRatePretty(apr?.superfluid.lower ?? 0);
const osmosisUpper = maybeMakeRatePretty(apr?.osmosis.upper ?? 0);
const osmosisLower = maybeMakeRatePretty(apr?.osmosis.lower ?? 0);
let boostUpper = maybeMakeRatePretty(apr?.boost.upper ?? 0);
let boostLower = maybeMakeRatePretty(apr?.boost.lower ?? 0);

// Temporarily exclude pools in this array from showing boost incentives given an issue on chain
if (
ExcludedExternalBoostPools.includes(pool_id) &&
totalUpper &&
totalLower &&
boostUpper &&
boostLower
) {
totalUpper = new RatePretty(totalUpper.toDec().sub(totalUpper.toDec()));
totalLower = new RatePretty(totalLower.toDec().sub(totalLower.toDec()));
boostUpper = undefined;
boostLower = undefined;
}

// add list of incentives that are defined
const incentiveTypes: PoolIncentiveType[] = [];
if (superfluidUpper && superfluidLower) incentiveTypes.push("superfluid");
if (osmosisUpper && osmosisLower) incentiveTypes.push("osmosis");
if (boostUpper && osmosisLower) incentiveTypes.push("boost");
if (
!superfluidUpper &&
!superfluidLower &&
!osmosisUpper &&
!osmosisLower &&
!boostUpper &&
!boostLower
)
incentiveTypes.push("none");
const hasBreakdownData =
totalUpper ||
totalLower ||
swapFeeUpper ||
swapFeeLower ||
superfluidUpper ||
superfluidLower ||
osmosisUpper ||
osmosisLower ||
boostUpper ||
boostLower;

const aprFormatted = {
aprBreakdown: hasBreakdownData
? {
total: {
upper: totalUpper,
lower: totalLower,
},
swapFee: {
upper: swapFeeUpper,
lower: swapFeeLower,
},
superfluid: {
upper: superfluidUpper,
lower: superfluidLower,
},
osmosis: {
upper: osmosisUpper,
lower: osmosisLower,
},
boost: {
upper: boostUpper,
lower: boostLower,
},
}
: undefined,
incentiveTypes,
};

return aprFormatted;
}

function maybeMakeRatePretty(value: number): RatePretty | undefined {
// numia will return 0 or null if the APR is not applicable, so return undefined to indicate that
if (value === 0 || value === null) {
return undefined;
}

return new RatePretty(new Dec(value).quo(new Dec(100)));
}

function getPoolIdFromChainPool(
43 changes: 42 additions & 1 deletion packages/server/src/queries/sidecar/pools.ts
Original file line number Diff line number Diff line change
@@ -48,6 +48,29 @@ export type ChainPool =
| ChainConcentratedPool
| ChainCosmwasmPool;

export type SQSAprData = {
swap_fees: {
upper?: number;
lower?: number;
};
superfluid: {
upper?: number;
lower?: number;
};
osmosis: {
upper?: number;
lower?: number;
};
boost: {
upper?: number;
lower: number;
};
total_apr: {
upper?: number;
lower?: number;
};
};

export type SqsPool = {
/** Sidecar returns the same pool models as the node. */
chain_model: ChainPool;
@@ -59,12 +82,27 @@ export type SqsPool = {
/** Int: capitalization in USD. Will be `"0"` if liquidity_cap_error is present. */
liquidity_cap: string;
liquidity_cap_error: string;

apr_data?: SQSAprData;
fees_data?: {
pool_id?: string;
volume_24h?: number;
volume_7d?: number;
fees_spent_24h?: number;
fees_spent_7d?: number;
fees_percentage?: string;
};
};

export function queryPools({
poolIds,
minLiquidityCap,
}: { poolIds?: string[]; minLiquidityCap?: string } = {}) {
withMarketIncentives,
}: {
poolIds?: string[];
minLiquidityCap?: string;
withMarketIncentives?: boolean;
} = {}) {
const url = new URL("/pools", SIDECAR_BASE_URL);
const params = new URLSearchParams();

@@ -74,6 +112,9 @@ export function queryPools({
if (minLiquidityCap) {
params.append("min_liquidity_cap", minLiquidityCap);
}
if (withMarketIncentives) {
params.append("with_market_incentives", "true");
}

url.search = params.toString();
return apiClient<SqsPool[]>(url.toString());
23 changes: 10 additions & 13 deletions packages/trpc/src/pools.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ import {
getUserPools,
getUserSharePools,
IncentivePoolFilterSchema,
isIncentivePoolFiltered,
maybeCachePaginatedItems,
PoolFilterSchema,
} from "@osmosis-labs/server";
@@ -110,34 +109,32 @@ export const poolsRouter = createTRPCRouter({
}) =>
maybeCachePaginatedItems({
getFreshItems: async () => {
const [pools, incentives, marketMetrics] = await Promise.all([
const [pools, marketMetrics] = await Promise.all([
getPools({
...ctx,
search,
minLiquidityUsd,
types,
denoms,
withMarketIncetives: true,
}),
getCachedPoolIncentivesMap(),
getCachedPoolMarketMetricsMap(),
]);

const marketIncentivePools = pools
.map((pool) => {
const incentivesForPool = incentives.get(pool.id);
const metricsForPool = marketMetrics.get(pool.id) ?? {};

const isIncentiveFiltered =
incentivesForPool &&
isIncentivePoolFiltered(incentivesForPool, {
incentiveTypes,
});

if (isIncentiveFiltered) return;
const {
marketIncentives: { aprBreakdown, incentiveTypes } = {}, // Destructure aprBreakdown and incentiveTypes from marketIncentives
...restPool // Get the rest of the properties of pool excluding marketIncentives
} = pool;

return {
...pool,
...incentivesForPool,
...restPool,
aprBreakdown,
incentiveTypes,

...metricsForPool,
};
})

0 comments on commit 397b12a

Please sign in to comment.