From 4cd7bf9cecbe2b7cff25d53ab9a2aebef3915040 Mon Sep 17 00:00:00 2001 From: gidonkatten Date: Thu, 2 Jan 2025 15:49:50 +0000 Subject: [PATCH 1/5] option to include xalgo staking apr in net rate and yield calculations --- .changeset/dirty-waves-sell.md | 5 +++++ src/lend/loan.ts | 11 +++++++++-- src/lend/utils.ts | 21 ++++++++++++++++++++- src/math-lib.ts | 2 ++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 .changeset/dirty-waves-sell.md diff --git a/.changeset/dirty-waves-sell.md b/.changeset/dirty-waves-sell.md new file mode 100644 index 00000000..e1619f56 --- /dev/null +++ b/.changeset/dirty-waves-sell.md @@ -0,0 +1,5 @@ +--- +"@folks-finance/algorand-sdk": patch +--- + +option to include xalgo staking apr in net rate and yield calculations diff --git a/src/lend/loan.ts b/src/lend/loan.ts index 079d0b01..8e72b3b4 100644 --- a/src/lend/loan.ts +++ b/src/lend/loan.ts @@ -146,6 +146,7 @@ async function retrieveLoanLocalState( * @param poolManagerAppId - pool manager application to query about * @param oracle - oracle to query * @param userAddr - account address for the user + * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield * @returns Promise user loans infos */ async function retrieveUserLoansInfo( @@ -154,6 +155,7 @@ async function retrieveUserLoansInfo( poolManagerAppId: number, oracle: Oracle, userAddr: string, + xAlgoStakingRateBps?: bigint, ): Promise { const userLoanInfos: UserLoanInfo[] = []; @@ -178,7 +180,10 @@ async function retrieveUserLoansInfo( ); if (state === undefined) throw Error(`Could not find loan ${loanAppId} in escrow ${escrowAddr}`); const localState = loanLocalState(state, loanAppId, escrowAddr); - userLoanInfos.push({ ...userLoanInfo(localState, poolManagerInfo, loanInfo, oraclePrices), currentRound }); + userLoanInfos.push({ + ...userLoanInfo(localState, poolManagerInfo, loanInfo, oraclePrices, xAlgoStakingRateBps), + currentRound, + }); } return userLoanInfos; @@ -193,6 +198,7 @@ async function retrieveUserLoansInfo( * @param poolManagerAppId - pool manager application to query about * @param oracle - oracle to query * @param escrowAddr - account address for the loan escrow + * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield * @returns Promise user loan info */ async function retrieveUserLoanInfo( @@ -201,6 +207,7 @@ async function retrieveUserLoanInfo( poolManagerAppId: number, oracle: Oracle, escrowAddr: string, + xAlgoStakingRateBps?: bigint, ): Promise { // get all prerequisites const loanInfoReq = retrieveLoanInfo(client, loanAppId); @@ -212,7 +219,7 @@ async function retrieveUserLoanInfo( const { currentRound, localState: state } = await getAccountApplicationLocalState(client, loanAppId, escrowAddr); if (state === undefined) throw Error(`Could not find loan ${loanAppId} in escrow ${escrowAddr}`); const localState = loanLocalState(state, loanAppId, escrowAddr); - return { ...userLoanInfo(localState, poolInfo, loanInfo, oraclePrices), currentRound }; + return { ...userLoanInfo(localState, poolInfo, loanInfo, oraclePrices, xAlgoStakingRateBps), currentRound }; } /** diff --git a/src/lend/utils.ts b/src/lend/utils.ts index ab904937..411a8bb1 100644 --- a/src/lend/utils.ts +++ b/src/lend/utils.ts @@ -1,6 +1,15 @@ import { encodeAddress, getApplicationAddress } from "algosdk"; -import { compoundEverySecond, maximum, mulScale, ONE_10_DP, ONE_16_DP, ONE_4_DP, SECONDS_IN_YEAR } from "../math-lib"; +import { + compoundEverySecond, + maximum, + mulScale, + ONE_10_DP, + ONE_12_DP, + ONE_16_DP, + ONE_4_DP, + SECONDS_IN_YEAR +} from "../math-lib"; import { enc, fromIntToByteHex, getParsedValueFromState, parseUint64s, unixTime } from "../utils"; import { @@ -31,6 +40,7 @@ import type { } from "./types"; import type { Indexer } from "algosdk"; import type { TealKeyValue } from "algosdk/dist/types/client/v2/algod/models/types"; +import {MainnetPools} from "./constants/mainnet-constants"; export async function getEscrows( indexerClient: Indexer, @@ -359,6 +369,7 @@ export function loanLocalState(state: TealKeyValue[], loanAppId: number, escrowA * @param poolManagerInfo - pool manager info which is returned by retrievePoolManagerInfo function * @param loanInfo - loan info which is returned by retrieveLoanInfo function * @param oraclePrices - oracle prices which is returned by getOraclePrices function + * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield * @returns Promise user loans info */ export function userLoanInfo( @@ -366,6 +377,7 @@ export function userLoanInfo( poolManagerInfo: PoolManagerInfo, loanInfo: LoanInfo, oraclePrices: OraclePrices, + xAlgoStakingRateBps?: bigint, ): UserLoanInfo { const { pools: poolManagerPools } = poolManagerInfo; const { pools: loanPools } = loanInfo; @@ -403,6 +415,13 @@ export function userLoanInfo( netRate += balanceValue * depositInterestRate; netYield += balanceValue * depositInterestYield; + // add xALGO staking apr if requested (must be mainnet) + if (assetId === MainnetPools.xALGO.assetId && xAlgoStakingRateBps) { + // multiply by 1e12 to standardise at 16 d.p. + netRate += balanceValue * xAlgoStakingRateBps * ONE_12_DP; + netYield += balanceValue * xAlgoStakingRateBps * ONE_12_DP; + } + collaterals.push({ poolAppId, assetId, diff --git a/src/math-lib.ts b/src/math-lib.ts index 715ff789..dabf02ae 100644 --- a/src/math-lib.ts +++ b/src/math-lib.ts @@ -4,6 +4,7 @@ const HOURS_IN_YEAR = BigInt(365 * 24); const ONE_2_DP = BigInt(1e2); const ONE_4_DP = BigInt(1e4); const ONE_10_DP = BigInt(1e10); +const ONE_12_DP = BigInt(1e12); const ONE_14_DP = BigInt(1e14); const ONE_16_DP = BigInt(1e16); @@ -81,6 +82,7 @@ export { HOURS_IN_YEAR, ONE_16_DP, ONE_14_DP, + ONE_12_DP, ONE_10_DP, ONE_4_DP, ONE_2_DP, From ff7901ce233e1d6304bb7bfaded7fca9b34ce7fb Mon Sep 17 00:00:00 2001 From: gidonkatten Date: Thu, 2 Jan 2025 15:51:55 +0000 Subject: [PATCH 2/5] fix import --- src/lend/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lend/utils.ts b/src/lend/utils.ts index 411a8bb1..51ad9b78 100644 --- a/src/lend/utils.ts +++ b/src/lend/utils.ts @@ -12,6 +12,7 @@ import { } from "../math-lib"; import { enc, fromIntToByteHex, getParsedValueFromState, parseUint64s, unixTime } from "../utils"; +import { MainnetPools } from "./constants/mainnet-constants"; import { calcBorrowAssetLoanValue, calcBorrowBalance, @@ -40,7 +41,6 @@ import type { } from "./types"; import type { Indexer } from "algosdk"; import type { TealKeyValue } from "algosdk/dist/types/client/v2/algod/models/types"; -import {MainnetPools} from "./constants/mainnet-constants"; export async function getEscrows( indexerClient: Indexer, From 1d0a06b48ce3bd8c864d2d9282779964c98adbfa Mon Sep 17 00:00:00 2001 From: gidonkatten Date: Thu, 2 Jan 2025 15:56:53 +0000 Subject: [PATCH 3/5] subtract xalgo staking apr if borrow --- src/lend/utils.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lend/utils.ts b/src/lend/utils.ts index 51ad9b78..6c890dca 100644 --- a/src/lend/utils.ts +++ b/src/lend/utils.ts @@ -8,7 +8,7 @@ import { ONE_12_DP, ONE_16_DP, ONE_4_DP, - SECONDS_IN_YEAR + SECONDS_IN_YEAR, } from "../math-lib"; import { enc, fromIntToByteHex, getParsedValueFromState, parseUint64s, unixTime } from "../utils"; @@ -482,6 +482,13 @@ export function userLoanInfo( netRate -= borrowBalanceValue * interestRate; netYield -= borrowBalanceValue * interestYield; + // subtract xALGO staking apr if requested (must be mainnet) + if (assetId === MainnetPools.xALGO.assetId && xAlgoStakingRateBps) { + // multiply by 1e12 to standardise at 16 d.p. + netRate -= borrowBalanceValue * xAlgoStakingRateBps * ONE_12_DP; + netYield -= borrowBalanceValue * xAlgoStakingRateBps * ONE_12_DP; + } + borrows.push({ poolAppId, assetId, From fc3fff58f5e7d6255e247e9f782684616cdff9b9 Mon Sep 17 00:00:00 2001 From: gidonkatten Date: Thu, 2 Jan 2025 16:09:46 +0000 Subject: [PATCH 4/5] generalise --- .changeset/dirty-waves-sell.md | 2 +- src/lend/loan.ts | 13 +++++++------ src/lend/types.ts | 9 +++++++++ src/lend/utils.ts | 24 +++++++++++++----------- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.changeset/dirty-waves-sell.md b/.changeset/dirty-waves-sell.md index e1619f56..5ac244d5 100644 --- a/.changeset/dirty-waves-sell.md +++ b/.changeset/dirty-waves-sell.md @@ -2,4 +2,4 @@ "@folks-finance/algorand-sdk": patch --- -option to include xalgo staking apr in net rate and yield calculations +option to include additional apr in net rate and yield calculations diff --git a/src/lend/loan.ts b/src/lend/loan.ts index 8e72b3b4..c42a3738 100644 --- a/src/lend/loan.ts +++ b/src/lend/loan.ts @@ -26,6 +26,7 @@ import { getOraclePrices, prepareRefreshPricesInOracleAdapter } from "./oracle"; import { getEscrows, loanLocalState, userLoanInfo } from "./utils"; import type { + AssetsAdditionalInterest, LoanInfo, LoanLocalState, LPToken, @@ -146,7 +147,7 @@ async function retrieveLoanLocalState( * @param poolManagerAppId - pool manager application to query about * @param oracle - oracle to query * @param userAddr - account address for the user - * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield + * @param additionalInterests - optional additional interest to consider in loan net rate/yield * @returns Promise user loans infos */ async function retrieveUserLoansInfo( @@ -155,7 +156,7 @@ async function retrieveUserLoansInfo( poolManagerAppId: number, oracle: Oracle, userAddr: string, - xAlgoStakingRateBps?: bigint, + additionalInterests?: AssetsAdditionalInterest, ): Promise { const userLoanInfos: UserLoanInfo[] = []; @@ -181,7 +182,7 @@ async function retrieveUserLoansInfo( if (state === undefined) throw Error(`Could not find loan ${loanAppId} in escrow ${escrowAddr}`); const localState = loanLocalState(state, loanAppId, escrowAddr); userLoanInfos.push({ - ...userLoanInfo(localState, poolManagerInfo, loanInfo, oraclePrices, xAlgoStakingRateBps), + ...userLoanInfo(localState, poolManagerInfo, loanInfo, oraclePrices, additionalInterests), currentRound, }); } @@ -198,7 +199,7 @@ async function retrieveUserLoansInfo( * @param poolManagerAppId - pool manager application to query about * @param oracle - oracle to query * @param escrowAddr - account address for the loan escrow - * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield + * @param additionalInterests - optional additional interest to consider in loan net rate/yield * @returns Promise user loan info */ async function retrieveUserLoanInfo( @@ -207,7 +208,7 @@ async function retrieveUserLoanInfo( poolManagerAppId: number, oracle: Oracle, escrowAddr: string, - xAlgoStakingRateBps?: bigint, + additionalInterests?: AssetsAdditionalInterest, ): Promise { // get all prerequisites const loanInfoReq = retrieveLoanInfo(client, loanAppId); @@ -219,7 +220,7 @@ async function retrieveUserLoanInfo( const { currentRound, localState: state } = await getAccountApplicationLocalState(client, loanAppId, escrowAddr); if (state === undefined) throw Error(`Could not find loan ${loanAppId} in escrow ${escrowAddr}`); const localState = loanLocalState(state, loanAppId, escrowAddr); - return { ...userLoanInfo(localState, poolInfo, loanInfo, oraclePrices, xAlgoStakingRateBps), currentRound }; + return { ...userLoanInfo(localState, poolInfo, loanInfo, oraclePrices, additionalInterests), currentRound }; } /** diff --git a/src/lend/types.ts b/src/lend/types.ts index e4e564a5..a4be70d0 100644 --- a/src/lend/types.ts +++ b/src/lend/types.ts @@ -339,6 +339,13 @@ interface UserLoanInfo { liquidationMargin: bigint; // 4 d.p. } +interface AssetAdditionalInterest { + rateBps: bigint; // 4 d.p. + yieldBps: bigint; // 4 d.p. +} + +type AssetsAdditionalInterest = Partial>; // assetId -> interest + interface Oracle { oracle0AppId: number; oracle1AppId?: number; @@ -397,6 +404,8 @@ export { UserLoanInfoCollateral, UserLoanInfoBorrow, UserLoanInfo, + AssetAdditionalInterest, + AssetsAdditionalInterest, Oracle, OraclePrice, OraclePrices, diff --git a/src/lend/utils.ts b/src/lend/utils.ts index 6c890dca..653eb522 100644 --- a/src/lend/utils.ts +++ b/src/lend/utils.ts @@ -12,7 +12,6 @@ import { } from "../math-lib"; import { enc, fromIntToByteHex, getParsedValueFromState, parseUint64s, unixTime } from "../utils"; -import { MainnetPools } from "./constants/mainnet-constants"; import { calcBorrowAssetLoanValue, calcBorrowBalance, @@ -25,6 +24,7 @@ import { } from "./formulae"; import type { + AssetsAdditionalInterest, DepositStakingInfo, DepositStakingProgramInfo, LoanInfo, @@ -369,7 +369,7 @@ export function loanLocalState(state: TealKeyValue[], loanAppId: number, escrowA * @param poolManagerInfo - pool manager info which is returned by retrievePoolManagerInfo function * @param loanInfo - loan info which is returned by retrieveLoanInfo function * @param oraclePrices - oracle prices which is returned by getOraclePrices function - * @param xAlgoStakingRateBps - optional xALGO staking rate (in bps) to include in loan net rate/yield + * @param additionalInterests - optional additional interest to consider in loan net rate/yield * @returns Promise user loans info */ export function userLoanInfo( @@ -377,7 +377,7 @@ export function userLoanInfo( poolManagerInfo: PoolManagerInfo, loanInfo: LoanInfo, oraclePrices: OraclePrices, - xAlgoStakingRateBps?: bigint, + additionalInterests?: AssetsAdditionalInterest, ): UserLoanInfo { const { pools: poolManagerPools } = poolManagerInfo; const { pools: loanPools } = loanInfo; @@ -415,11 +415,12 @@ export function userLoanInfo( netRate += balanceValue * depositInterestRate; netYield += balanceValue * depositInterestYield; - // add xALGO staking apr if requested (must be mainnet) - if (assetId === MainnetPools.xALGO.assetId && xAlgoStakingRateBps) { + // add additional interests if specified + if (additionalInterests && additionalInterests[assetId]) { + const { rateBps, yieldBps } = additionalInterests[assetId]; // multiply by 1e12 to standardise at 16 d.p. - netRate += balanceValue * xAlgoStakingRateBps * ONE_12_DP; - netYield += balanceValue * xAlgoStakingRateBps * ONE_12_DP; + netRate += balanceValue * rateBps * ONE_12_DP; + netYield += balanceValue * yieldBps * ONE_12_DP; } collaterals.push({ @@ -482,11 +483,12 @@ export function userLoanInfo( netRate -= borrowBalanceValue * interestRate; netYield -= borrowBalanceValue * interestYield; - // subtract xALGO staking apr if requested (must be mainnet) - if (assetId === MainnetPools.xALGO.assetId && xAlgoStakingRateBps) { + // subtracts additional interests if specified + if (additionalInterests && additionalInterests[assetId]) { + const { rateBps, yieldBps } = additionalInterests[assetId]; // multiply by 1e12 to standardise at 16 d.p. - netRate -= borrowBalanceValue * xAlgoStakingRateBps * ONE_12_DP; - netYield -= borrowBalanceValue * xAlgoStakingRateBps * ONE_12_DP; + netRate -= borrowBalanceValue * rateBps * ONE_12_DP; + netYield -= borrowBalanceValue * yieldBps * ONE_12_DP; } borrows.push({ From 381528424ea462db0a397dbe9327bdabf35424d3 Mon Sep 17 00:00:00 2001 From: gidonkatten Date: Thu, 2 Jan 2025 16:10:56 +0000 Subject: [PATCH 5/5] fix changeset comment --- .changeset/dirty-waves-sell.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dirty-waves-sell.md b/.changeset/dirty-waves-sell.md index 5ac244d5..118a8154 100644 --- a/.changeset/dirty-waves-sell.md +++ b/.changeset/dirty-waves-sell.md @@ -2,4 +2,4 @@ "@folks-finance/algorand-sdk": patch --- -option to include additional apr in net rate and yield calculations +option to include additional interest in net rate and yield calculations