From 03313b7237c32f366a7a1fd2a177d6ecdefab400 Mon Sep 17 00:00:00 2001 From: Gidon Katten <55109236+gidonkatten@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:23:28 +0000 Subject: [PATCH] option to include additional interest in net rate and yield calculations (#76) * option to include xalgo staking apr in net rate and yield calculations * fix import * subtract xalgo staking apr if borrow * generalise * fix changeset comment --- .changeset/dirty-waves-sell.md | 5 +++++ src/lend/loan.ts | 12 ++++++++++-- src/lend/types.ts | 9 +++++++++ src/lend/utils.ts | 30 +++++++++++++++++++++++++++++- src/math-lib.ts | 2 ++ 5 files changed, 55 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..118a8154 --- /dev/null +++ b/.changeset/dirty-waves-sell.md @@ -0,0 +1,5 @@ +--- +"@folks-finance/algorand-sdk": patch +--- + +option to include additional interest in net rate and yield calculations diff --git a/src/lend/loan.ts b/src/lend/loan.ts index 079d0b01..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,6 +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 additionalInterests - optional additional interest to consider in loan net rate/yield * @returns Promise user loans infos */ async function retrieveUserLoansInfo( @@ -154,6 +156,7 @@ async function retrieveUserLoansInfo( poolManagerAppId: number, oracle: Oracle, userAddr: string, + additionalInterests?: AssetsAdditionalInterest, ): Promise { const userLoanInfos: UserLoanInfo[] = []; @@ -178,7 +181,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, additionalInterests), + currentRound, + }); } return userLoanInfos; @@ -193,6 +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 additionalInterests - optional additional interest to consider in loan net rate/yield * @returns Promise user loan info */ async function retrieveUserLoanInfo( @@ -201,6 +208,7 @@ async function retrieveUserLoanInfo( poolManagerAppId: number, oracle: Oracle, escrowAddr: string, + additionalInterests?: AssetsAdditionalInterest, ): Promise { // get all prerequisites const loanInfoReq = retrieveLoanInfo(client, loanAppId); @@ -212,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), 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 ab904937..653eb522 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 { @@ -15,6 +24,7 @@ import { } from "./formulae"; import type { + AssetsAdditionalInterest, DepositStakingInfo, DepositStakingProgramInfo, LoanInfo, @@ -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 additionalInterests - optional additional interest to consider 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, + additionalInterests?: AssetsAdditionalInterest, ): UserLoanInfo { const { pools: poolManagerPools } = poolManagerInfo; const { pools: loanPools } = loanInfo; @@ -403,6 +415,14 @@ export function userLoanInfo( netRate += balanceValue * depositInterestRate; netYield += balanceValue * depositInterestYield; + // 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 * rateBps * ONE_12_DP; + netYield += balanceValue * yieldBps * ONE_12_DP; + } + collaterals.push({ poolAppId, assetId, @@ -463,6 +483,14 @@ export function userLoanInfo( netRate -= borrowBalanceValue * interestRate; netYield -= borrowBalanceValue * interestYield; + // 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 * rateBps * ONE_12_DP; + netYield -= borrowBalanceValue * yieldBps * ONE_12_DP; + } + borrows.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,