From 7ad39238b130f4eed80c48a67f61d85523839ffb Mon Sep 17 00:00:00 2001 From: microwavedcola1 <89031858+microwavedcola1@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:19:37 +0200 Subject: [PATCH] mc/mango account units docs (#181) * cleanup mango account docs Signed-off-by: microwavedcola1 * note Signed-off-by: microwavedcola1 * Fixes from reviews Signed-off-by: microwavedcola1 * Fixes from reviews Signed-off-by: microwavedcola1 * Fixes from reviews Signed-off-by: microwavedcola1 Signed-off-by: microwavedcola1 --- ts/client/src/accounts/mangoAccount.ts | 137 +++++++++++++++++++------ 1 file changed, 107 insertions(+), 30 deletions(-) diff --git a/ts/client/src/accounts/mangoAccount.ts b/ts/client/src/accounts/mangoAccount.ts index 29ab87aa9..2439151ef 100644 --- a/ts/client/src/accounts/mangoAccount.ts +++ b/ts/client/src/accounts/mangoAccount.ts @@ -99,12 +99,13 @@ export class MangoAccount { return this.serum3.find((sa) => sa.marketIndex == marketIndex); } - getNative(bank: Bank): I80F48 { - const ta = this.findToken(bank.tokenIndex); - return ta ? ta.native(bank) : ZERO_I80F48; - } + // How to navigate + // * if a function is returning a I80F48, then usually the return value is in native quote or native token, unless specified + // * if a function is returning a number, then usually the return value is in native tokens, unless specified + // * functions try to be explicit by having native or ui in the name to better reflect the value + // * some values might appear unexpected large or small, usually the doc contains a "note" - static getEquivalentNativeUsdcPosition( + static getEquivalentUsdcPosition( sourceBank: Bank, nativeTokenPosition: TokenPosition, ): I80F48 { @@ -117,7 +118,7 @@ export class MangoAccount { : ZERO_I80F48; } - static getEquivalentNativeTokenPosition( + static getEquivalentTokenPosition( targetBank: Bank, nativeUsdcPosition: I80F48, ): I80F48 { @@ -127,51 +128,100 @@ export class MangoAccount { .mul(I80F48.fromNumber(Math.pow(10, targetBank.mintDecimals))); } - getNativeDeposits(bank: Bank): I80F48 { - const native = this.getNative(bank); + /** + * + * @param bank + * @returns native balance for a token + */ + getTokenBalance(bank: Bank): I80F48 { + const ta = this.findToken(bank.tokenIndex); + return ta ? ta.native(bank) : ZERO_I80F48; + } + + /** + * + * @param bank + * @returns native balance for a token, 0 or more + */ + getTokenDeposits(bank: Bank): I80F48 { + const native = this.getTokenBalance(bank); return native.gte(ZERO_I80F48) ? native : ZERO_I80F48; } - getNativeBorrows(bank: Bank): I80F48 { - const native = this.getNative(bank); + /** + * + * @param bank + * @returns native balance for a token, 0 or less + */ + getTokenBorrows(bank: Bank): I80F48 { + const native = this.getTokenBalance(bank); return native.lte(ZERO_I80F48) ? native : ZERO_I80F48; } - getUi(bank: Bank): number { + /** + * + * @param bank + * @returns UI balance for a token + */ + getTokenBalanceUi(bank: Bank): number { const ta = this.findToken(bank.tokenIndex); - return ta ? ta.ui(bank) : 0; + return ta ? ta.balanceUi(bank) : 0; } - deposits(bank: Bank): number { + /** + * + * @param bank + * @returns UI balance for a token, 0 or more + */ + getTokenDepositsUi(bank: Bank): number { const ta = this.findToken(bank.tokenIndex); - return ta ? ta.uiDeposits(bank) : 0; + return ta ? ta.depositsUi(bank) : 0; } - borrows(bank: Bank): number { + /** + * + * @param bank + * @returns UI balance for a token, 0 or less + */ + getTokenBorrowsUi(bank: Bank): number { const ta = this.findToken(bank.tokenIndex); - return ta ? ta.uiBorrows(bank) : 0; + return ta ? ta.borrowsUi(bank) : 0; } + /** + * Health, see health.rs or https://docs.mango.markets/mango-markets/health-overview + * @param healthType + * @returns raw health number, in native quote + */ getHealth(healthType: HealthType): I80F48 { return healthType == HealthType.init ? (this.accountData as MangoAccountData).initHealth : (this.accountData as MangoAccountData).maintHealth; } + /** + * Health ratio, which is computed so `100 * (assets-liabs)/liabs` + * Note: health ratio is technically ∞ if liabs are 0 + * @param healthType + * @returns health ratio, in percentage form + */ getHealthRatio(healthType: HealthType): I80F48 { return this.accountData.healthCache.healthRatio(healthType); } + /** + * Health ratio + * @param healthType + * @returns health ratio, in percentage form, capped to 100 + */ getHealthRatioUi(healthType: HealthType): number { - const ratio = this.accountData.healthCache - .healthRatio(healthType) - .toNumber(); - + const ratio = this.getHealthRatio(healthType).toNumber(); return ratio > 100 ? 100 : Math.trunc(ratio); } /** * Sum of all the assets i.e. token deposits, borrows, total assets in spot open orders, (perps positions is todo) in terms of quote value. + * @returns equity, in native quote */ getEquity(): I80F48 { const equity = (this.accountData as MangoAccountData).equity; @@ -184,6 +234,7 @@ export class MangoAccount { /** * The amount of native quote you could withdraw against your existing assets. + * @returns collateral value, in native quote */ getCollateralValue(): I80F48 { return this.getHealth(HealthType.init); @@ -191,15 +242,17 @@ export class MangoAccount { /** * Sum of all positive assets. + * @returns assets, in native quote */ - getAssetsVal(healthType: HealthType): I80F48 { + getAssetsValue(healthType: HealthType): I80F48 { return this.accountData.healthCache.assets(healthType); } /** * Sum of all negative assets. + * @returns liabs, in native quote */ - getLiabsVal(healthType: HealthType): I80F48 { + getLiabsValue(healthType: HealthType): I80F48 { return this.accountData.healthCache.liabs(healthType); } @@ -207,16 +260,17 @@ export class MangoAccount { * The amount of given native token you can borrow, considering all existing assets as collateral except the deposits for this token. * Note 1: The existing native deposits need to be added to get the full amount that could be withdrawn. * Note 2: The group might have less native deposits than what this returns. TODO: loan origination fees + * @returns amount of given native token you can borrow, considering all existing assets as collateral except the deposits for this token, in native token */ getMaxWithdrawWithBorrowForToken(group: Group, mintPk: PublicKey): I80F48 { const bank: Bank = group.getFirstBankByMint(mintPk); const initHealth = (this.accountData as MangoAccountData).initHealth; - const inUsdcUnits = MangoAccount.getEquivalentNativeUsdcPosition( + const inUsdcUnits = MangoAccount.getEquivalentUsdcPosition( bank, this.findToken(bank.tokenIndex), ).max(ZERO_I80F48); const newInitHealth = initHealth.sub(inUsdcUnits.mul(bank.initAssetWeight)); - return MangoAccount.getEquivalentNativeTokenPosition( + return MangoAccount.getEquivalentTokenPosition( bank, newInitHealth.div(bank.initLiabWeight), ); @@ -227,6 +281,7 @@ export class MangoAccount { * note: slippageAndFeesFactor is a normalized number, <1, * e.g. a slippage of 5% and some fees which are 1%, then slippageAndFeesFactor = 0.94 * the factor is used to compute how much target can be obtained by swapping source + * @returns max amount of given source native token you can swap to a target token, in native token */ getMaxSourceForTokenSwap( group: Group, @@ -247,6 +302,8 @@ export class MangoAccount { /** * Simulates new health ratio after applying tokenChanges to the token positions. * e.g. useful to simulate health after a potential swap. + * Note: health ratio is technically ∞ if liabs are 0 + * @returns health ratio, in percentage form */ simHealthRatioWithTokenPositionChanges( group: Group, @@ -267,7 +324,7 @@ export class MangoAccount { * The remaining native quote margin available for given market. * * TODO: this is a very bad estimation atm. - * It assumes quote asset is always USDC, + * It assumes quote asset is always quote, * it assumes that there are no interaction effects, * it assumes that there are no existing borrows for either of the tokens in the market. */ @@ -284,7 +341,7 @@ export class MangoAccount { * The remaining native quote margin available for given market. * * TODO: this is a very bad estimation atm. - * It assumes quote asset is always USDC, + * It assumes quote asset is always quote, * it assumes that there are no interaction effects, * it assumes that there are no existing borrows for either of the tokens in the market. */ @@ -370,18 +427,38 @@ export class TokenPosition { } } - public ui(bank: Bank): number { + /** + * @param bank + * @returns position in UI decimals, is signed + */ + public balanceUi(bank: Bank): number { return nativeI80F48ToUi(this.native(bank), bank.mintDecimals).toNumber(); } - public uiDeposits(bank: Bank): number { + /** + * @param bank + * @returns position in UI decimals, 0 if position has borrows + */ + public depositsUi(bank: Bank): number { + if (this.indexedPosition && this.indexedPosition.lt(ZERO_I80F48)) { + return 0; + } + return nativeI80F48ToUi( bank.depositIndex.mul(this.indexedPosition), bank.mintDecimals, ).toNumber(); } - public uiBorrows(bank: Bank): number { + /** + * @param bank + * @returns position in UI decimals, can be 0 or negative, 0 if position has deposits + */ + public borrowsUi(bank: Bank): number { + if (this.indexedPosition && this.indexedPosition.gt(ZERO_I80F48)) { + return 0; + } + return nativeI80F48ToUi( bank.borrowIndex.mul(this.indexedPosition), bank.mintDecimals, @@ -395,7 +472,7 @@ export class TokenPosition { if (bank) { const native = this.native(bank); extra += ', native: ' + native.toNumber(); - extra += ', ui: ' + this.ui(bank); + extra += ', ui: ' + this.balanceUi(bank); extra += ', tokenName: ' + bank.name; } }