Skip to content

Commit

Permalink
chore: add lending rate query for neptune service
Browse files Browse the repository at this point in the history
  • Loading branch information
shane-moore committed Nov 18, 2024
1 parent 60704a2 commit bec0bc4
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 38 deletions.
9 changes: 9 additions & 0 deletions packages/sdk-ts/src/client/wasm/neptune/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AssetInfo } from './types.js'

export function getDenom(assetInfo: AssetInfo): string | undefined {
if ('native_token' in assetInfo) {
return assetInfo.native_token.denom
}

return assetInfo.token.contract_addr
}
8 changes: 4 additions & 4 deletions packages/sdk-ts/src/client/wasm/neptune/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from './queries'
export * from './transformer'
export * from './types'
export * from './service'
export * from './types.js'
export * from './service.js'
export * from './transformer.js'
export * from './queries/index.js'

export const NEPTUNE_PRICE_CONTRACT = 'inj1u6cclz0qh5tep9m2qayry9k97dm46pnlqf8nre'
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BaseWasmQuery } from '../../BaseWasmQuery'
import { toBase64 } from '../../../../utils'
import { AssetInfo } from '../types'
import { BaseWasmQuery } from '../../BaseWasmQuery.js'
import { toBase64 } from '../../../../utils/index.js'
import { AssetInfo } from '../types.js'

export declare namespace QueryGetPrices {
export interface Params {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { BaseWasmQuery } from '../../BaseWasmQuery.js';
import { toBase64 } from '../../../../utils/index.js';
import { AssetInfo } from '../types.js';

export declare namespace QueryGetAllLendingRates {
export interface Params {
limit?: number;
startAfter?: AssetInfo;
}
}

export class QueryGetAllLendingRates extends BaseWasmQuery<QueryGetAllLendingRates.Params> {
toPayload() {
const { limit, startAfter } = this.params;

const query: any = { get_all_lending_rates: {} };
if (limit !== undefined) {
query.get_all_lending_rates.limit = limit;
}
if (startAfter !== undefined) {
query.get_all_lending_rates.start_after = startAfter;
}

return toBase64(query);
}
}
3 changes: 2 additions & 1 deletion packages/sdk-ts/src/client/wasm/neptune/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { QueryGetPrices } from './QueryGetPrices'
export { QueryGetPrices } from './QueryGetPrices.js'
export { QueryGetAllLendingRates } from './QueryLendingRates.js'
160 changes: 134 additions & 26 deletions packages/sdk-ts/src/client/wasm/neptune/service.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import {
Network,
isMainnet,
NetworkEndpoints,
getNetworkEndpoints,
} from '@injectivelabs/networks'
import { NetworkEndpoints } from '@injectivelabs/networks'
import { AssetInfo, NEPTUNE_USDT_CW20_CONTRACT, AssetInfoWithPrice } from './types'
import { ChainGrpcWasmApi } from '../../chain'
import { QueryGetPrices } from './queries'
import { PriceQueryTransformer } from './transformer'
import ExecArgNeptuneDeposit from '../../../core/modules/wasm/exec-args/ExecArgNeptuneDeposit'
import ExecArgNeptuneWithdraw from '../../../core/modules/wasm/exec-args/ExecArgNeptuneWithdraw'

import MsgExecuteContractCompat from '../../../core/modules/wasm/msgs/MsgExecuteContractCompat'

import { getDenom } from './helper.js'
import { ChainGrpcWasmApi } from '../../chain/index.js'
import { QueryGetPrices, QueryGetAllLendingRates } from './queries/index.js'
import { NeptuneQueryTransformer } from './transformer.js'
import ExecArgNeptuneDeposit from '../../../core/modules/wasm/exec-args/ExecArgNeptuneDeposit.js'
import ExecArgNeptuneWithdraw from '../../../core/modules/wasm/exec-args/ExecArgNeptuneWithdraw.js'
import MsgExecuteContractCompat from '../../../core/modules/wasm/msgs/MsgExecuteContractCompat.js'
import { GeneralException } from '@injectivelabs/exceptions'
import { NEPTUNE_PRICE_CONTRACT } from './index'
import { NEPTUNE_PRICE_CONTRACT } from './index.js'
import {
AssetInfo,
AssetInfoWithPrice,
NEPTUNE_USDT_CW20_CONTRACT,
} from './types.js'

const NEPTUNE_USDT_MARKET_CONTRACT = 'inj1nc7gjkf2mhp34a6gquhurg8qahnw5kxs5u3s4u'
const NEPTUNE_USDT_MARKET_CONTRACT =
'inj1nc7gjkf2mhp34a6gquhurg8qahnw5kxs5u3s4u'
const NEPTUNE_USDT_INTEREST_CONTRACT =
'inj1ftech0pdjrjawltgejlmpx57cyhsz6frdx2dhq'

export class NeptuneService {
private client: ChainGrpcWasmApi
Expand All @@ -29,7 +35,7 @@ export class NeptuneService {
*/
constructor(
network: Network = Network.MainnetSentry,
endpoints?: NetworkEndpoints
endpoints?: NetworkEndpoints,
) {
if (!isMainnet(network)) {
throw new GeneralException(new Error('Please switch to mainnet network'))
Expand All @@ -51,10 +57,11 @@ export class NeptuneService {
try {
const response = await this.client.fetchSmartContractState(
this.priceOracleContract,
queryGetPricesPayload
queryGetPricesPayload,
)

const prices = PriceQueryTransformer.contractPricesResponseToPrices(response)
const prices =
NeptuneQueryTransformer.contractPricesResponseToPrices(response)

return prices
} catch (error) {
Expand All @@ -69,8 +76,11 @@ export class NeptuneService {
* @param nativeAsset AssetInfo for the native token.
* @returns Redemption ratio as a number.
*/
async fetchRedemptionRatio({ cw20Asset, nativeAsset }: {
cw20Asset: AssetInfo,
async fetchRedemptionRatio({
cw20Asset,
nativeAsset,
}: {
cw20Asset: AssetInfo
nativeAsset: AssetInfo
}): Promise<number> {
const prices = await this.fetchPrices([cw20Asset, nativeAsset])
Expand All @@ -79,7 +89,9 @@ export class NeptuneService {
const [nativePrice] = prices.reverse()

if (!cw20Price || !nativePrice) {
throw new GeneralException(new Error('Failed to compute redemption ratio'))
throw new GeneralException(
new Error('Failed to compute redemption ratio'),
)
}

return Number(cw20Price.price) / Number(nativePrice.price)
Expand Down Expand Up @@ -113,10 +125,15 @@ export class NeptuneService {
* @param amount Amount to deposit as a string.
* @returns MsgExecuteContractCompat message.
*/
createDepositMsg({ denom, amount, sender, contractAddress = NEPTUNE_USDT_MARKET_CONTRACT }: {
denom: string,
amount: string,
sender: string,
createDepositMsg({
denom,
amount,
sender,
contractAddress = NEPTUNE_USDT_MARKET_CONTRACT,
}: {
denom: string
amount: string
sender: string
contractAddress?: string
}): MsgExecuteContractCompat {
return MsgExecuteContractCompat.fromJSON({
Expand All @@ -143,10 +160,10 @@ export class NeptuneService {
cw20ContractAddress = NEPTUNE_USDT_CW20_CONTRACT,
marketContractAddress = NEPTUNE_USDT_MARKET_CONTRACT,
}: {
amount: string,
sender: string,
cw20ContractAddress?: string,
marketContractAddress?: string,
amount: string
sender: string
cw20ContractAddress?: string
marketContractAddress?: string
}): MsgExecuteContractCompat {
return MsgExecuteContractCompat.fromJSON({
sender,
Expand All @@ -157,4 +174,95 @@ export class NeptuneService {
}),
})
}

/**
* Fetch lending rates with optional pagination parameters.
* @param limit Maximum number of lending rates to fetch.
* @param startAfter AssetInfo to start after for pagination.
* @returns Array of [AssetInfo, Decimal256] tuples.
*/
async getLendingRates({
limit,
startAfter,
contractAddress = NEPTUNE_USDT_INTEREST_CONTRACT,
}: {
limit?: number
startAfter?: AssetInfo
contractAddress?: string
}): Promise<Array<{ assetInfo: AssetInfo; lendingRate: string }>> {
const query = new QueryGetAllLendingRates({ limit, startAfter })
const payload = query.toPayload()

try {
const response = await this.client.fetchSmartContractState(
contractAddress,
payload,
)

const lendingRates =
NeptuneQueryTransformer.contractLendingRatesResponseToLendingRates(
response,
)

return lendingRates
} catch (error) {
console.error('Error fetching lending rates:', error)
throw new GeneralException(new Error('Failed to fetch lending rates'))
}
}

/**
* Fetch the lending rate for a specific denom by querying the smart contract with pagination.
* @param denom The denomination string of the asset to find the lending rate for.
* @returns Lending rate as a string.
*/
async getLendingRateByDenom({
denom,
contractAddress = NEPTUNE_USDT_INTEREST_CONTRACT,
}: {
denom: string
contractAddress?: string
}): Promise<string | undefined> {
const limit = 10
let startAfter = undefined

while (true) {
const lendingRates = await this.getLendingRates({
limit,
startAfter,
contractAddress,
})

if (lendingRates.length === 0) {
return
}

for (const { assetInfo, lendingRate } of lendingRates) {
const currentDenom = getDenom(assetInfo)

if (currentDenom === denom) {
return lendingRate
}
}

if (lendingRates.length < limit) {
return
}

const lastLendingRate = lendingRates[lendingRates.length - 1]

startAfter = lastLendingRate.assetInfo
}
}

/**
* Calculates APY from APR and compounding frequency.
*
* @param apr - The annual percentage rate as a decimal (e.g., 0.10 for 10%)
* @param compoundingFrequency - Number of times interest is compounded per year
* @returns The annual percentage yield as a decimal
*/
calculateAPY(apr: number, compoundingFrequency = 365): number {
return Math.pow(1 + apr / compoundingFrequency, compoundingFrequency) - 1
}
}
19 changes: 15 additions & 4 deletions packages/sdk-ts/src/client/wasm/neptune/transformer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { WasmContractQueryResponse } from '../types'
import { toUtf8 } from '../../../utils'
import { AssetInfo, PriceResponse } from './types'
import { WasmContractQueryResponse } from '../types.js'
import { toUtf8 } from '../../../utils/index.js'
import { AssetInfo, PriceResponse, LendingRateResponse } from './types.js'

export class PriceQueryTransformer {
export class NeptuneQueryTransformer {
static contractPricesResponseToPrices(
response: WasmContractQueryResponse,
): Array<{ assetInfo: AssetInfo; price: string }> {
Expand All @@ -13,4 +13,15 @@ export class PriceQueryTransformer {
price: priceInfo.price,
}))
}

static contractLendingRatesResponseToLendingRates(
response: WasmContractQueryResponse,
): Array<{ assetInfo: AssetInfo; lendingRate: string }> {
const data = JSON.parse(toUtf8(response.data)) as LendingRateResponse

return data.map(([assetInfo, lendingRate]) => ({
assetInfo,
lendingRate,
}))
}
}
1 change: 1 addition & 0 deletions packages/sdk-ts/src/client/wasm/neptune/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type AssetInfo =
export type AssetInfoWithPrice = {assetInfo: AssetInfo, price: string }

export type PriceResponse = Array<[AssetInfo, { price: string }]>
export type LendingRateResponse = Array<[AssetInfo, string]>

export const NEPTUNE_USDT_CW20_CONTRACT =
'inj1cy9hes20vww2yr6crvs75gxy5hpycya2hmjg9s'

0 comments on commit bec0bc4

Please sign in to comment.