Mc/ts numbers - cleanup usage of all numbers (#259)
* ts: a higher error tolerance is sufficient Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: move stuff around Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: string representation while printing Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: number cleanup Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: fix tsc errors Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: cleanup creation of I80F48 from BN Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: fixed from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * revert Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * ts: fix from call Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
d1079bb1b9
commit
bafaf73745
|
@ -1,8 +1,8 @@
|
|||
import { BN } from '@project-serum/anchor';
|
||||
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { As, nativeI80F48ToUi } from '../utils';
|
||||
import { I80F48, I80F48Dto, ZERO_I80F48 } from './I80F48';
|
||||
import { I80F48, I80F48Dto, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { As, toUiDecimals } from '../utils';
|
||||
|
||||
export const QUOTE_DECIMALS = 6;
|
||||
|
||||
|
@ -209,61 +209,61 @@ export class Bank implements BankForHealth {
|
|||
'\n oracle - ' +
|
||||
this.oracle.toBase58() +
|
||||
'\n price - ' +
|
||||
this._price?.toNumber() +
|
||||
this._price?.toString() +
|
||||
'\n uiPrice - ' +
|
||||
this._uiPrice +
|
||||
'\n deposit index - ' +
|
||||
this.depositIndex.toNumber() +
|
||||
this.depositIndex.toString() +
|
||||
'\n borrow index - ' +
|
||||
this.borrowIndex.toNumber() +
|
||||
this.borrowIndex.toString() +
|
||||
'\n indexedDeposits - ' +
|
||||
this.indexedDeposits.toNumber() +
|
||||
this.indexedDeposits.toString() +
|
||||
'\n indexedBorrows - ' +
|
||||
this.indexedBorrows.toNumber() +
|
||||
this.indexedBorrows.toString() +
|
||||
'\n cachedIndexedTotalDeposits - ' +
|
||||
this.cachedIndexedTotalDeposits.toNumber() +
|
||||
this.cachedIndexedTotalDeposits.toString() +
|
||||
'\n cachedIndexedTotalBorrows - ' +
|
||||
this.cachedIndexedTotalBorrows.toNumber() +
|
||||
this.cachedIndexedTotalBorrows.toString() +
|
||||
'\n indexLastUpdated - ' +
|
||||
new Date(this.indexLastUpdated.toNumber() * 1000) +
|
||||
'\n bankRateLastUpdated - ' +
|
||||
new Date(this.bankRateLastUpdated.toNumber() * 1000) +
|
||||
'\n avgUtilization - ' +
|
||||
this.avgUtilization.toNumber() +
|
||||
this.avgUtilization.toString() +
|
||||
'\n adjustmentFactor - ' +
|
||||
this.adjustmentFactor.toNumber() +
|
||||
this.adjustmentFactor.toString() +
|
||||
'\n maxRate - ' +
|
||||
this.maxRate.toNumber() +
|
||||
this.maxRate.toString() +
|
||||
'\n util0 - ' +
|
||||
this.util0.toNumber() +
|
||||
this.util0.toString() +
|
||||
'\n rate0 - ' +
|
||||
this.rate0.toNumber() +
|
||||
this.rate0.toString() +
|
||||
'\n util1 - ' +
|
||||
this.util1.toNumber() +
|
||||
this.util1.toString() +
|
||||
'\n rate1 - ' +
|
||||
this.rate1.toNumber() +
|
||||
this.rate1.toString() +
|
||||
'\n loanFeeRate - ' +
|
||||
this.loanFeeRate.toNumber() +
|
||||
this.loanFeeRate.toString() +
|
||||
'\n loanOriginationFeeRate - ' +
|
||||
this.loanOriginationFeeRate.toNumber() +
|
||||
this.loanOriginationFeeRate.toString() +
|
||||
'\n maintAssetWeight - ' +
|
||||
this.maintAssetWeight.toNumber() +
|
||||
this.maintAssetWeight.toString() +
|
||||
'\n initAssetWeight - ' +
|
||||
this.initAssetWeight.toNumber() +
|
||||
this.initAssetWeight.toString() +
|
||||
'\n maintLiabWeight - ' +
|
||||
this.maintLiabWeight.toNumber() +
|
||||
this.maintLiabWeight.toString() +
|
||||
'\n initLiabWeight - ' +
|
||||
this.initLiabWeight.toNumber() +
|
||||
this.initLiabWeight.toString() +
|
||||
'\n liquidationFee - ' +
|
||||
this.liquidationFee.toNumber() +
|
||||
this.liquidationFee.toString() +
|
||||
'\n uiDeposits() - ' +
|
||||
this.uiDeposits() +
|
||||
'\n uiBorrows() - ' +
|
||||
this.uiBorrows() +
|
||||
'\n getDepositRate() - ' +
|
||||
this.getDepositRate().toNumber() +
|
||||
this.getDepositRate().toString() +
|
||||
'\n getBorrowRate() - ' +
|
||||
this.getBorrowRate().toNumber()
|
||||
this.getBorrowRate().toString()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -294,17 +294,17 @@ export class Bank implements BankForHealth {
|
|||
}
|
||||
|
||||
uiDeposits(): number {
|
||||
return nativeI80F48ToUi(
|
||||
return toUiDecimals(
|
||||
this.indexedDeposits.mul(this.depositIndex),
|
||||
this.mintDecimals,
|
||||
).toNumber();
|
||||
);
|
||||
}
|
||||
|
||||
uiBorrows(): number {
|
||||
return nativeI80F48ToUi(
|
||||
return toUiDecimals(
|
||||
this.indexedBorrows.mul(this.borrowIndex),
|
||||
this.mintDecimals,
|
||||
).toNumber();
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,9 +16,9 @@ import BN from 'bn.js';
|
|||
import { MangoClient } from '../client';
|
||||
import { SERUM3_PROGRAM_ID } from '../constants';
|
||||
import { Id } from '../ids';
|
||||
import { toNativeDecimals, toUiDecimals } from '../utils';
|
||||
import { I80F48, ONE_I80F48 } from '../numbers/I80F48';
|
||||
import { toNative, toNativeI80F48, toUiDecimals } from '../utils';
|
||||
import { Bank, MintInfo, TokenIndex } from './bank';
|
||||
import { I80F48, ONE_I80F48 } from './I80F48';
|
||||
import {
|
||||
isPythOracle,
|
||||
isSwitchboardOracle,
|
||||
|
@ -92,7 +92,7 @@ export class Group {
|
|||
public perpMarketsMapByName: Map<string, PerpMarket>,
|
||||
public mintInfosMapByTokenIndex: Map<TokenIndex, MintInfo>,
|
||||
public mintInfosMapByMint: Map<string, MintInfo>,
|
||||
public vaultAmountsMap: Map<string, number>,
|
||||
public vaultAmountsMap: Map<string, BN>,
|
||||
) {}
|
||||
|
||||
public async reloadAll(client: MangoClient): Promise<void> {
|
||||
|
@ -382,9 +382,10 @@ export class Group {
|
|||
if (!vaultAi) {
|
||||
throw new Error(`Undefined vaultAi for ${vaultPks[i]}`!);
|
||||
}
|
||||
const vaultAmount = coder()
|
||||
.accounts.decode('token', vaultAi.data)
|
||||
.amount.toNumber();
|
||||
const vaultAmount = coder().accounts.decode(
|
||||
'token',
|
||||
vaultAi.data,
|
||||
).amount;
|
||||
return [vaultPks[i].toBase58(), vaultAmount];
|
||||
}),
|
||||
);
|
||||
|
@ -412,34 +413,28 @@ export class Group {
|
|||
return banks[0];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mintPk
|
||||
* @returns sum of native balances of vaults for all banks for a token (fetched from vaultAmountsMap cache)
|
||||
*/
|
||||
public getTokenVaultBalanceByMint(mintPk: PublicKey): I80F48 {
|
||||
const banks = this.banksMapByMint.get(mintPk.toBase58());
|
||||
if (!banks) throw new Error(`No bank found for mint ${mintPk}!`);
|
||||
let totalAmount = 0;
|
||||
for (const bank of banks) {
|
||||
const amount = this.vaultAmountsMap.get(bank.vault.toBase58());
|
||||
if (amount) {
|
||||
totalAmount += amount;
|
||||
}
|
||||
}
|
||||
return I80F48.fromNumber(totalAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mintPk
|
||||
* @returns sum of ui balances of vaults for all banks for a token
|
||||
*/
|
||||
public getTokenVaultBalanceByMintUi(mintPk: PublicKey): number {
|
||||
const vaultBalance = this.getTokenVaultBalanceByMint(mintPk);
|
||||
const mintDecimals = this.getMintDecimals(mintPk);
|
||||
const banks = this.banksMapByMint.get(mintPk.toBase58());
|
||||
if (!banks) {
|
||||
throw new Error(`No bank found for mint ${mintPk}!`);
|
||||
}
|
||||
const totalAmount = new BN(0);
|
||||
for (const bank of banks) {
|
||||
const amount = this.vaultAmountsMap.get(bank.vault.toBase58());
|
||||
if (!amount) {
|
||||
throw new Error(
|
||||
`Vault balance not found for bank ${bank.name} ${bank.bankNum}!`,
|
||||
);
|
||||
}
|
||||
totalAmount.iadd(amount);
|
||||
}
|
||||
|
||||
return toUiDecimals(vaultBalance, mintDecimals);
|
||||
return toUiDecimals(totalAmount, this.getMintDecimals(mintPk));
|
||||
}
|
||||
|
||||
public getSerum3MarketByMarketIndex(marketIndex: MarketIndex): Serum3Market {
|
||||
|
@ -575,31 +570,21 @@ export class Group {
|
|||
}
|
||||
|
||||
public toUiPrice(price: I80F48, baseDecimals: number): number {
|
||||
return price
|
||||
.mul(
|
||||
I80F48.fromNumber(
|
||||
Math.pow(10, baseDecimals - this.getInsuranceMintDecimals()),
|
||||
),
|
||||
)
|
||||
.toNumber();
|
||||
return toUiDecimals(price, baseDecimals - this.getInsuranceMintDecimals());
|
||||
}
|
||||
|
||||
public toNativePrice(uiPrice: number, baseDecimals: number): I80F48 {
|
||||
return I80F48.fromNumber(uiPrice).mul(
|
||||
I80F48.fromNumber(
|
||||
Math.pow(
|
||||
10,
|
||||
// note: our oracles are quoted in USD and our insurance mint is USD
|
||||
// please update when these assumptions change
|
||||
this.getInsuranceMintDecimals() - baseDecimals,
|
||||
),
|
||||
),
|
||||
return toNativeI80F48(
|
||||
uiPrice,
|
||||
// note: our oracles are quoted in USD and our insurance mint is USD
|
||||
// please update when these assumptions change
|
||||
this.getInsuranceMintDecimals() - baseDecimals,
|
||||
);
|
||||
}
|
||||
|
||||
public toNativeDecimals(uiAmount: number, mintPk: PublicKey): BN {
|
||||
const decimals = this.getMintDecimals(mintPk);
|
||||
return toNativeDecimals(uiAmount, decimals);
|
||||
return toNative(uiAmount, decimals);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { BN } from '@project-serum/anchor';
|
||||
import { OpenOrders } from '@project-serum/serum';
|
||||
import { expect } from 'chai';
|
||||
import { I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { toUiDecimalsForQuote } from '../utils';
|
||||
import { BankForHealth, TokenIndex } from './bank';
|
||||
import { HealthCache, PerpInfo, Serum3Info, TokenInfo } from './healthCache';
|
||||
import { I80F48, ZERO_I80F48 } from './I80F48';
|
||||
import { HealthType, PerpPosition } from './mangoAccount';
|
||||
import { PerpMarket } from './perp';
|
||||
import { MarketIndex } from './serum3';
|
||||
|
@ -81,12 +81,12 @@ describe('Health Cache', () => {
|
|||
const pM = mockPerpMarket(9, 0.1, 0.2, targetBank.price);
|
||||
const pp = new PerpPosition(
|
||||
pM.perpMarketIndex,
|
||||
3,
|
||||
new BN(3),
|
||||
I80F48.fromNumber(-310),
|
||||
7,
|
||||
11,
|
||||
1,
|
||||
2,
|
||||
new BN(7),
|
||||
new BN(11),
|
||||
new BN(1),
|
||||
new BN(2),
|
||||
I80F48.fromNumber(0),
|
||||
I80F48.fromNumber(0),
|
||||
);
|
||||
|
@ -182,12 +182,12 @@ describe('Health Cache', () => {
|
|||
const pM = mockPerpMarket(9, 0.1, 0.2, bank2.price);
|
||||
const pp = new PerpPosition(
|
||||
pM.perpMarketIndex,
|
||||
fixture.perp1[0],
|
||||
new BN(fixture.perp1[0]),
|
||||
I80F48.fromNumber(fixture.perp1[1]),
|
||||
fixture.perp1[2],
|
||||
fixture.perp1[3],
|
||||
0,
|
||||
0,
|
||||
new BN(fixture.perp1[2]),
|
||||
new BN(fixture.perp1[3]),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
I80F48.fromNumber(0),
|
||||
I80F48.fromNumber(0),
|
||||
);
|
||||
|
@ -430,6 +430,6 @@ describe('Health Cache', () => {
|
|||
I80F48.fromNumber(0.95),
|
||||
),
|
||||
).toFixed(3),
|
||||
).equals('90.477');
|
||||
).equals('90.176');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,15 +2,15 @@ import { BN } from '@project-serum/anchor';
|
|||
import { OpenOrders } from '@project-serum/serum';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import _ from 'lodash';
|
||||
import { Bank, BankForHealth, TokenIndex } from './bank';
|
||||
import { Group } from './group';
|
||||
import {
|
||||
HUNDRED_I80F48,
|
||||
I80F48,
|
||||
I80F48Dto,
|
||||
MAX_I80F48,
|
||||
ZERO_I80F48,
|
||||
} from './I80F48';
|
||||
} from '../numbers/I80F48';
|
||||
import { Bank, BankForHealth, TokenIndex } from './bank';
|
||||
import { Group } from './group';
|
||||
|
||||
import { HealthType, MangoAccount, PerpPosition } from './mangoAccount';
|
||||
import { PerpMarket, PerpOrderSide } from './perp';
|
||||
|
@ -735,7 +735,7 @@ export class HealthCache {
|
|||
const perpInfoIndex = this.getOrCreatePerpInfoIndex(perpMarket);
|
||||
const perpInfo = this.perpInfos[perpInfoIndex];
|
||||
const oraclePrice = perpInfo.oraclePrice;
|
||||
const baseLotSize = I80F48.fromString(perpMarket.baseLotSize.toString());
|
||||
const baseLotSize = I80F48.fromI64(perpMarket.baseLotSize);
|
||||
|
||||
// If the price is sufficiently good then health will just increase from trading
|
||||
const finalHealthSlope =
|
||||
|
@ -940,21 +940,21 @@ export class Serum3Info {
|
|||
oo: OpenOrders,
|
||||
): Serum3Info {
|
||||
// add the amounts that are freely settleable
|
||||
const baseFree = I80F48.fromString(oo.baseTokenFree.toString());
|
||||
const baseFree = I80F48.fromI64(oo.baseTokenFree);
|
||||
// NOTE: referrerRebatesAccrued is not declared on oo class, but the layout
|
||||
// is aware of it
|
||||
const quoteFree = I80F48.fromString(
|
||||
oo.quoteTokenFree.add((oo as any).referrerRebatesAccrued).toString(),
|
||||
const quoteFree = I80F48.fromI64(
|
||||
oo.quoteTokenFree.add((oo as any).referrerRebatesAccrued),
|
||||
);
|
||||
baseInfo.balance.iadd(baseFree.mul(baseInfo.oraclePrice));
|
||||
quoteInfo.balance.iadd(quoteFree.mul(quoteInfo.oraclePrice));
|
||||
|
||||
// add the reserved amount to both sides, to have the worst-case covered
|
||||
const reservedBase = I80F48.fromString(
|
||||
oo.baseTokenTotal.sub(oo.baseTokenFree).toString(),
|
||||
const reservedBase = I80F48.fromI64(
|
||||
oo.baseTokenTotal.sub(oo.baseTokenFree),
|
||||
);
|
||||
const reservedQuote = I80F48.fromString(
|
||||
oo.quoteTokenTotal.sub(oo.quoteTokenFree).toString(),
|
||||
const reservedQuote = I80F48.fromI64(
|
||||
oo.quoteTokenTotal.sub(oo.quoteTokenFree),
|
||||
);
|
||||
const reservedBalance = reservedBase
|
||||
.mul(baseInfo.oraclePrice)
|
||||
|
@ -1059,21 +1059,17 @@ export class PerpInfo {
|
|||
perpMarket: PerpMarket,
|
||||
perpPosition: PerpPosition,
|
||||
): PerpInfo {
|
||||
const baseLotSize = I80F48.fromString(perpMarket.baseLotSize.toString());
|
||||
const baseLots = I80F48.fromNumber(
|
||||
perpPosition.basePositionLots + perpPosition.takerBaseLots,
|
||||
const baseLotSize = I80F48.fromI64(perpMarket.baseLotSize);
|
||||
const baseLots = I80F48.fromI64(
|
||||
perpPosition.basePositionLots.add(perpPosition.takerBaseLots),
|
||||
);
|
||||
|
||||
const unsettledFunding = perpPosition.unsettledFunding(perpMarket);
|
||||
|
||||
const takerQuote = I80F48.fromString(
|
||||
new BN(perpPosition.takerQuoteLots)
|
||||
.mul(perpMarket.quoteLotSize)
|
||||
.toString(),
|
||||
const takerQuote = I80F48.fromI64(
|
||||
new BN(perpPosition.takerQuoteLots).mul(perpMarket.quoteLotSize),
|
||||
);
|
||||
const quoteCurrent = I80F48.fromString(
|
||||
perpPosition.quotePositionNative.toString(),
|
||||
)
|
||||
const quoteCurrent = perpPosition.quotePositionNative
|
||||
.sub(unsettledFunding)
|
||||
.add(takerQuote);
|
||||
|
||||
|
@ -1118,26 +1114,18 @@ export class PerpInfo {
|
|||
// scenario1 < scenario2
|
||||
// iff abs(bidsNetLots) > abs(asksNetLots)
|
||||
|
||||
const bidsNetLots = baseLots.add(
|
||||
I80F48.fromNumber(perpPosition.bidsBaseLots),
|
||||
);
|
||||
const asksNetLots = baseLots.sub(
|
||||
I80F48.fromNumber(perpPosition.asksBaseLots),
|
||||
);
|
||||
const bidsNetLots = baseLots.add(I80F48.fromI64(perpPosition.bidsBaseLots));
|
||||
const asksNetLots = baseLots.sub(I80F48.fromI64(perpPosition.asksBaseLots));
|
||||
|
||||
const lotsToQuote = baseLotSize.mul(perpMarket.price);
|
||||
|
||||
let base, quote;
|
||||
if (bidsNetLots.abs().gt(asksNetLots.abs())) {
|
||||
const bidsBaseLots = I80F48.fromString(
|
||||
perpPosition.bidsBaseLots.toString(),
|
||||
);
|
||||
const bidsBaseLots = I80F48.fromI64(perpPosition.bidsBaseLots);
|
||||
base = bidsNetLots.mul(lotsToQuote);
|
||||
quote = quoteCurrent.sub(bidsBaseLots.mul(lotsToQuote));
|
||||
} else {
|
||||
const asksBaseLots = I80F48.fromString(
|
||||
perpPosition.asksBaseLots.toString(),
|
||||
);
|
||||
const asksBaseLots = I80F48.fromI64(perpPosition.asksBaseLots);
|
||||
base = asksNetLots.mul(lotsToQuote);
|
||||
quote = quoteCurrent.add(asksBaseLots.mul(lotsToQuote));
|
||||
}
|
||||
|
|
|
@ -4,16 +4,11 @@ import { OpenOrders, Order, Orderbook } from '@project-serum/serum/lib/market';
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import { MangoClient } from '../client';
|
||||
import { SERUM3_PROGRAM_ID } from '../constants';
|
||||
import {
|
||||
nativeI80F48ToUi,
|
||||
toNative,
|
||||
toUiDecimals,
|
||||
toUiDecimalsForQuote,
|
||||
} from '../utils';
|
||||
import { I80F48, I80F48Dto, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { toNativeI80F48, toUiDecimals, toUiDecimalsForQuote } from '../utils';
|
||||
import { Bank, TokenIndex } from './bank';
|
||||
import { Group } from './group';
|
||||
import { HealthCache } from './healthCache';
|
||||
import { I80F48, I80F48Dto, ONE_I80F48, ZERO_I80F48 } from './I80F48';
|
||||
import { PerpMarket, PerpMarketIndex, PerpOrder, PerpOrderSide } from './perp';
|
||||
import { MarketIndex, Serum3Side } from './serum3';
|
||||
export class MangoAccount {
|
||||
|
@ -262,13 +257,9 @@ export class MangoAccount {
|
|||
* @param healthType
|
||||
* @returns health ratio, in percentage form, capped to 100
|
||||
*/
|
||||
getHealthRatioUi(group: Group, healthType: HealthType): number | undefined {
|
||||
getHealthRatioUi(group: Group, healthType: HealthType): number {
|
||||
const ratio = this.getHealthRatio(group, healthType).toNumber();
|
||||
if (ratio) {
|
||||
return ratio > 100 ? 100 : Math.trunc(ratio);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
return ratio > 100 ? 100 : Math.trunc(ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,19 +278,15 @@ export class MangoAccount {
|
|||
const baseBank = group.getFirstBankByTokenIndex(sp.baseTokenIndex);
|
||||
tokensMap
|
||||
.get(baseBank.tokenIndex)!
|
||||
.iadd(
|
||||
I80F48.fromString(oo.baseTokenTotal.toString()).mul(baseBank.price),
|
||||
);
|
||||
.iadd(I80F48.fromI64(oo.baseTokenTotal).mul(baseBank.price));
|
||||
const quoteBank = group.getFirstBankByTokenIndex(sp.quoteTokenIndex);
|
||||
// NOTE: referrerRebatesAccrued is not declared on oo class, but the layout
|
||||
// is aware of it
|
||||
tokensMap
|
||||
.get(baseBank.tokenIndex)!
|
||||
.iadd(
|
||||
I80F48.fromString(
|
||||
oo.quoteTokenTotal
|
||||
.add((oo as any).referrerRebatesAccrued)
|
||||
.toString(),
|
||||
I80F48.fromI64(
|
||||
oo.quoteTokenTotal.add((oo as any).referrerRebatesAccrued),
|
||||
).mul(quoteBank.price),
|
||||
);
|
||||
}
|
||||
|
@ -477,7 +464,7 @@ export class MangoAccount {
|
|||
): number | undefined {
|
||||
const nativeTokenChanges = uiTokenChanges.map((tokenChange) => {
|
||||
return {
|
||||
nativeTokenAmount: toNative(
|
||||
nativeTokenAmount: toNativeI80F48(
|
||||
tokenChange.uiTokenAmount,
|
||||
group.getMintDecimals(tokenChange.mintPk),
|
||||
),
|
||||
|
@ -634,7 +621,7 @@ export class MangoAccount {
|
|||
.simHealthRatioWithSerum3BidChanges(
|
||||
baseBank,
|
||||
quoteBank,
|
||||
toNative(
|
||||
toNativeI80F48(
|
||||
uiQuoteAmount,
|
||||
group.getFirstBankByTokenIndex(serum3Market.quoteTokenIndex)
|
||||
.mintDecimals,
|
||||
|
@ -672,7 +659,7 @@ export class MangoAccount {
|
|||
.simHealthRatioWithSerum3AskChanges(
|
||||
baseBank,
|
||||
quoteBank,
|
||||
toNative(
|
||||
toNativeI80F48(
|
||||
uiBaseAmount,
|
||||
group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
||||
.mintDecimals,
|
||||
|
@ -703,11 +690,9 @@ export class MangoAccount {
|
|||
I80F48.fromNumber(2),
|
||||
group.toNativePrice(uiPrice, perpMarket.baseDecimals),
|
||||
);
|
||||
const nativeBase = baseLots.mul(
|
||||
I80F48.fromString(perpMarket.baseLotSize.toString()),
|
||||
);
|
||||
const nativeBase = baseLots.mul(I80F48.fromI64(perpMarket.baseLotSize));
|
||||
const nativeQuote = nativeBase.mul(perpMarket.price);
|
||||
return toUiDecimalsForQuote(nativeQuote.toNumber());
|
||||
return toUiDecimalsForQuote(nativeQuote);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -856,7 +841,7 @@ export class TokenPosition {
|
|||
* @returns UI balance, is signed
|
||||
*/
|
||||
public balanceUi(bank: Bank): number {
|
||||
return nativeI80F48ToUi(this.balance(bank), bank.mintDecimals).toNumber();
|
||||
return toUiDecimals(this.balance(bank), bank.mintDecimals);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -864,7 +849,7 @@ export class TokenPosition {
|
|||
* @returns UI deposits, 0 if position has borrows
|
||||
*/
|
||||
public depositsUi(bank: Bank): number {
|
||||
return nativeI80F48ToUi(this.deposits(bank), bank.mintDecimals).toNumber();
|
||||
return toUiDecimals(this.deposits(bank), bank.mintDecimals);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -872,7 +857,7 @@ export class TokenPosition {
|
|||
* @returns UI borrows, 0 if position has deposits
|
||||
*/
|
||||
public borrowsUi(bank: Bank): number {
|
||||
return nativeI80F48ToUi(this.borrows(bank), bank.mintDecimals).toNumber();
|
||||
return toUiDecimals(this.borrows(bank), bank.mintDecimals);
|
||||
}
|
||||
|
||||
public toString(group?: Group, index?: number): string {
|
||||
|
@ -947,12 +932,12 @@ export class PerpPosition {
|
|||
static from(dto: PerpPositionDto): PerpPosition {
|
||||
return new PerpPosition(
|
||||
dto.marketIndex as PerpMarketIndex,
|
||||
dto.basePositionLots.toNumber(),
|
||||
dto.basePositionLots,
|
||||
I80F48.from(dto.quotePositionNative),
|
||||
dto.bidsBaseLots.toNumber(),
|
||||
dto.asksBaseLots.toNumber(),
|
||||
dto.takerBaseLots.toNumber(),
|
||||
dto.takerQuoteLots.toNumber(),
|
||||
dto.bidsBaseLots,
|
||||
dto.asksBaseLots,
|
||||
dto.takerBaseLots,
|
||||
dto.takerQuoteLots,
|
||||
I80F48.from(dto.longSettledFunding),
|
||||
I80F48.from(dto.shortSettledFunding),
|
||||
);
|
||||
|
@ -960,12 +945,12 @@ export class PerpPosition {
|
|||
|
||||
constructor(
|
||||
public marketIndex: PerpMarketIndex,
|
||||
public basePositionLots: number,
|
||||
public basePositionLots: BN,
|
||||
public quotePositionNative: I80F48,
|
||||
public bidsBaseLots: number,
|
||||
public asksBaseLots: number,
|
||||
public takerBaseLots: number,
|
||||
public takerQuoteLots: number,
|
||||
public bidsBaseLots: BN,
|
||||
public asksBaseLots: BN,
|
||||
public takerBaseLots: BN,
|
||||
public takerQuoteLots: BN,
|
||||
public longSettledFunding: I80F48,
|
||||
public shortSettledFunding: I80F48,
|
||||
) {}
|
||||
|
@ -975,32 +960,32 @@ export class PerpPosition {
|
|||
}
|
||||
|
||||
public unsettledFunding(perpMarket: PerpMarket): I80F48 {
|
||||
if (this.basePositionLots > 0) {
|
||||
if (this.basePositionLots.gt(new BN(0))) {
|
||||
return perpMarket.longFunding
|
||||
.sub(this.longSettledFunding)
|
||||
.mul(I80F48.fromString(this.basePositionLots.toString()));
|
||||
} else if (this.basePositionLots < 0) {
|
||||
.mul(I80F48.fromI64(this.basePositionLots));
|
||||
} else if (this.basePositionLots.lt(new BN(0))) {
|
||||
return perpMarket.shortFunding
|
||||
.sub(this.shortSettledFunding)
|
||||
.mul(I80F48.fromString(this.basePositionLots.toString()));
|
||||
.mul(I80F48.fromI64(this.basePositionLots));
|
||||
}
|
||||
return ZERO_I80F48();
|
||||
}
|
||||
|
||||
public getEquity(perpMarket: PerpMarket): I80F48 {
|
||||
const lotsToQuote = I80F48.fromString(
|
||||
perpMarket.baseLotSize.toString(),
|
||||
).mul(perpMarket.price);
|
||||
const lotsToQuote = I80F48.fromI64(perpMarket.baseLotSize).mul(
|
||||
perpMarket.price,
|
||||
);
|
||||
|
||||
const baseLots = I80F48.fromNumber(
|
||||
this.basePositionLots + this.takerBaseLots,
|
||||
const baseLots = I80F48.fromI64(
|
||||
this.basePositionLots.add(this.takerBaseLots),
|
||||
);
|
||||
|
||||
const unsettledFunding = this.unsettledFunding(perpMarket);
|
||||
const takerQuote = I80F48.fromString(
|
||||
new BN(this.takerQuoteLots).mul(perpMarket.quoteLotSize).toString(),
|
||||
const takerQuote = I80F48.fromI64(
|
||||
new BN(this.takerQuoteLots).mul(perpMarket.quoteLotSize),
|
||||
);
|
||||
const quoteCurrent = I80F48.fromString(this.quotePositionNative.toString())
|
||||
const quoteCurrent = this.quotePositionNative
|
||||
.sub(unsettledFunding)
|
||||
.add(takerQuote);
|
||||
|
||||
|
@ -1008,11 +993,12 @@ export class PerpPosition {
|
|||
}
|
||||
|
||||
public hasOpenOrders(): boolean {
|
||||
const zero = new BN(0);
|
||||
return (
|
||||
this.asksBaseLots != 0 ||
|
||||
this.bidsBaseLots != 0 ||
|
||||
this.takerBaseLots != 0 ||
|
||||
this.takerQuoteLots != 0
|
||||
!this.asksBaseLots.eq(zero) ||
|
||||
!this.bidsBaseLots.eq(zero) ||
|
||||
!this.takerBaseLots.eq(zero) ||
|
||||
!this.takerQuoteLots.eq(zero)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1038,7 +1024,7 @@ export class PerpOo {
|
|||
return new PerpOo(
|
||||
dto.orderSide,
|
||||
dto.orderMarket,
|
||||
dto.clientOrderId.toNumber(),
|
||||
dto.clientOrderId,
|
||||
dto.orderId,
|
||||
);
|
||||
}
|
||||
|
@ -1046,7 +1032,7 @@ export class PerpOo {
|
|||
constructor(
|
||||
public orderSide: any,
|
||||
public orderMarket: 0,
|
||||
public clientOrderId: number,
|
||||
public clientOrderId: BN,
|
||||
public orderId: BN,
|
||||
) {}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
SwitchboardDecimal,
|
||||
} from '@switchboard-xyz/switchboard-v2';
|
||||
import BN from 'bn.js';
|
||||
import { I80F48, I80F48Dto } from './I80F48';
|
||||
import { I80F48, I80F48Dto } from '../numbers/I80F48';
|
||||
|
||||
const SBV1_DEVNET_PID = new PublicKey(
|
||||
'7azgmy1pFXHikv36q1zZASvFq5vFa39TT9NweVugKKTU',
|
||||
|
@ -20,7 +20,7 @@ let sbv2MainnetProgram;
|
|||
|
||||
export class StubOracle {
|
||||
public price: I80F48;
|
||||
public lastUpdated: number;
|
||||
public lastUpdated: BN;
|
||||
|
||||
static from(
|
||||
publicKey: PublicKey,
|
||||
|
@ -48,7 +48,7 @@ export class StubOracle {
|
|||
lastUpdated: BN,
|
||||
) {
|
||||
this.price = I80F48.from(price);
|
||||
this.lastUpdated = lastUpdated.toNumber();
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import Big from 'big.js';
|
||||
import { MangoClient } from '../client';
|
||||
import { As, U64_MAX_BN } from '../utils';
|
||||
import { I80F48, I80F48Dto } from '../numbers/I80F48';
|
||||
import { As, toNative, U64_MAX_BN } from '../utils';
|
||||
import { OracleConfig, QUOTE_DECIMALS } from './bank';
|
||||
import { I80F48, I80F48Dto } from './I80F48';
|
||||
|
||||
export type PerpMarketIndex = number & As<'perp-market-index'>;
|
||||
|
||||
|
@ -22,8 +22,8 @@ export class PerpMarket {
|
|||
public maxFunding: I80F48;
|
||||
public longFunding: I80F48;
|
||||
public shortFunding: I80F48;
|
||||
public openInterest: number;
|
||||
public seqNum: number;
|
||||
public openInterest: BN;
|
||||
public seqNum: BN;
|
||||
public feesAccrued: I80F48;
|
||||
priceLotsToUiConverter: number;
|
||||
baseLotsToUiConverter: number;
|
||||
|
@ -146,8 +146,8 @@ export class PerpMarket {
|
|||
this.maxFunding = I80F48.from(maxFunding);
|
||||
this.longFunding = I80F48.from(longFunding);
|
||||
this.shortFunding = I80F48.from(shortFunding);
|
||||
this.openInterest = openInterest.toNumber();
|
||||
this.seqNum = seqNum.toNumber();
|
||||
this.openInterest = openInterest;
|
||||
this.seqNum = seqNum;
|
||||
this.feesAccrued = I80F48.from(feesAccrued);
|
||||
|
||||
this.priceLotsToUiConverter = new Big(10)
|
||||
|
@ -241,21 +241,17 @@ export class PerpMarket {
|
|||
}
|
||||
|
||||
public uiPriceToLots(price: number): BN {
|
||||
return new BN(price * Math.pow(10, QUOTE_DECIMALS))
|
||||
return toNative(price, QUOTE_DECIMALS)
|
||||
.mul(this.baseLotSize)
|
||||
.div(this.quoteLotSize.mul(new BN(Math.pow(10, this.baseDecimals))));
|
||||
}
|
||||
|
||||
public uiBaseToLots(quantity: number): BN {
|
||||
return new BN(quantity * Math.pow(10, this.baseDecimals)).div(
|
||||
this.baseLotSize,
|
||||
);
|
||||
return toNative(quantity, this.baseDecimals).div(this.baseLotSize);
|
||||
}
|
||||
|
||||
public uiQuoteToLots(uiQuote: number): BN {
|
||||
return new BN(uiQuote * Math.pow(10, QUOTE_DECIMALS)).div(
|
||||
this.quoteLotSize,
|
||||
);
|
||||
return toNative(uiQuote, QUOTE_DECIMALS).div(this.quoteLotSize);
|
||||
}
|
||||
|
||||
public priceLotsToUi(price: BN): number {
|
||||
|
@ -276,19 +272,19 @@ export class PerpMarket {
|
|||
'\n perpMarketIndex -' +
|
||||
this.perpMarketIndex +
|
||||
'\n maintAssetWeight -' +
|
||||
this.maintAssetWeight.toNumber() +
|
||||
this.maintAssetWeight.toString() +
|
||||
'\n initAssetWeight -' +
|
||||
this.initAssetWeight.toNumber() +
|
||||
this.initAssetWeight.toString() +
|
||||
'\n maintLiabWeight -' +
|
||||
this.maintLiabWeight.toNumber() +
|
||||
this.maintLiabWeight.toString() +
|
||||
'\n initLiabWeight -' +
|
||||
this.initLiabWeight.toNumber() +
|
||||
this.initLiabWeight.toString() +
|
||||
'\n liquidationFee -' +
|
||||
this.liquidationFee.toNumber() +
|
||||
this.liquidationFee.toString() +
|
||||
'\n makerFee -' +
|
||||
this.makerFee.toNumber() +
|
||||
this.makerFee.toString() +
|
||||
'\n takerFee -' +
|
||||
this.takerFee.toNumber()
|
||||
this.takerFee.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { SERUM3_PROGRAM_ID } from '../constants';
|
|||
import { As } from '../utils';
|
||||
import { TokenIndex } from './bank';
|
||||
import { Group } from './group';
|
||||
import { MAX_I80F48, ONE_I80F48, ZERO_I80F48 } from './I80F48';
|
||||
import { MAX_I80F48, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
|
||||
export type MarketIndex = number & As<'market-index'>;
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import {
|
|||
import bs58 from 'bs58';
|
||||
import { Bank, MintInfo, TokenIndex } from './accounts/bank';
|
||||
import { Group } from './accounts/group';
|
||||
import { I80F48 } from './accounts/I80F48';
|
||||
import {
|
||||
MangoAccount,
|
||||
PerpPosition,
|
||||
|
@ -52,12 +51,13 @@ import {
|
|||
import { SERUM3_PROGRAM_ID } from './constants';
|
||||
import { Id } from './ids';
|
||||
import { IDL, MangoV4 } from './mango_v4';
|
||||
import { I80F48 } from './numbers/I80F48';
|
||||
import { FlashLoanType, InterestRateParams } from './types';
|
||||
import {
|
||||
createAssociatedTokenAccountIdempotentInstruction,
|
||||
getAssociatedTokenAddress,
|
||||
I64_MAX_BN,
|
||||
toNativeDecimals,
|
||||
toNative,
|
||||
} from './utils';
|
||||
import { sendTransaction } from './utils/rpc';
|
||||
|
||||
|
@ -741,7 +741,7 @@ export class MangoClient {
|
|||
amount: number,
|
||||
): Promise<TransactionSignature> {
|
||||
const decimals = group.getMintDecimals(mintPk);
|
||||
const nativeAmount = toNativeDecimals(amount, decimals).toNumber();
|
||||
const nativeAmount = toNative(amount, decimals);
|
||||
return await this.tokenDepositNative(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
@ -754,7 +754,7 @@ export class MangoClient {
|
|||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
mintPk: PublicKey,
|
||||
nativeAmount: number,
|
||||
nativeAmount: BN,
|
||||
): Promise<TransactionSignature> {
|
||||
const bank = group.getFirstBankByMint(mintPk);
|
||||
|
||||
|
@ -769,13 +769,13 @@ export class MangoClient {
|
|||
const additionalSigners: Signer[] = [];
|
||||
if (mintPk.equals(WRAPPED_SOL_MINT)) {
|
||||
wrappedSolAccount = new Keypair();
|
||||
const lamports = nativeAmount + 1e7;
|
||||
const lamports = nativeAmount.add(new BN(1e7));
|
||||
|
||||
preInstructions = [
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: mangoAccount.owner,
|
||||
newAccountPubkey: wrappedSolAccount.publicKey,
|
||||
lamports,
|
||||
lamports: lamports.toNumber(),
|
||||
space: 165,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
|
@ -841,10 +841,7 @@ export class MangoClient {
|
|||
amount: number,
|
||||
allowBorrow: boolean,
|
||||
): Promise<TransactionSignature> {
|
||||
const nativeAmount = toNativeDecimals(
|
||||
amount,
|
||||
group.getMintDecimals(mintPk),
|
||||
).toNumber();
|
||||
const nativeAmount = toNative(amount, group.getMintDecimals(mintPk));
|
||||
return await this.tokenWithdrawNative(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
@ -858,7 +855,7 @@ export class MangoClient {
|
|||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
mintPk: PublicKey,
|
||||
nativeAmount: number,
|
||||
nativeAmount: BN,
|
||||
allowBorrow: boolean,
|
||||
): Promise<TransactionSignature> {
|
||||
const bank = group.getFirstBankByMint(mintPk);
|
||||
|
@ -1852,7 +1849,7 @@ export class MangoClient {
|
|||
|
||||
const flashLoanBeginIx = await this.program.methods
|
||||
.flashLoanBegin([
|
||||
toNativeDecimals(amountIn, inputBank.mintDecimals),
|
||||
toNative(amountIn, inputBank.mintDecimals),
|
||||
new BN(
|
||||
0,
|
||||
) /* we don't care about borrowing the target amount, this is just a dummy */,
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
///
|
||||
/// debugging
|
||||
///
|
||||
|
||||
import { AccountMeta, PublicKey } from '@solana/web3.js';
|
||||
import { Bank } from './accounts/bank';
|
||||
import { Group } from './accounts/group';
|
||||
import { MangoAccount, Serum3Orders } from './accounts/mangoAccount';
|
||||
import { PerpMarket } from './accounts/perp';
|
||||
|
||||
export function debugAccountMetas(ams: AccountMeta[]): void {
|
||||
for (const am of ams) {
|
||||
console.log(
|
||||
`${am.pubkey.toBase58()}, isSigner: ${am.isSigner
|
||||
.toString()
|
||||
.padStart(5, ' ')}, isWritable - ${am.isWritable
|
||||
.toString()
|
||||
.padStart(5, ' ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function debugHealthAccounts(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
publicKeys: PublicKey[],
|
||||
): void {
|
||||
const banks = new Map(
|
||||
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
||||
banks[0].publicKey.toBase58(),
|
||||
`${banks[0].name} bank`,
|
||||
]),
|
||||
);
|
||||
const oracles = new Map(
|
||||
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
||||
banks[0].oracle.toBase58(),
|
||||
`${banks[0].name} oracle`,
|
||||
]),
|
||||
);
|
||||
const serum3 = new Map(
|
||||
mangoAccount.serum3Active().map((serum3: Serum3Orders) => {
|
||||
const serum3Market = Array.from(
|
||||
group.serum3MarketsMapByExternal.values(),
|
||||
).find((serum3Market) => serum3Market.marketIndex === serum3.marketIndex);
|
||||
if (!serum3Market) {
|
||||
throw new Error(
|
||||
`Serum3Orders for non existent market with market index ${serum3.marketIndex}`,
|
||||
);
|
||||
}
|
||||
return [serum3.openOrders.toBase58(), `${serum3Market.name} spot oo`];
|
||||
}),
|
||||
);
|
||||
const perps = new Map(
|
||||
Array.from(group.perpMarketsMapByName.values()).map(
|
||||
(perpMarket: PerpMarket) => [
|
||||
perpMarket.publicKey.toBase58(),
|
||||
`${perpMarket.name} perp market`,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
publicKeys.map((pk) => {
|
||||
if (banks.get(pk.toBase58())) {
|
||||
console.log(banks.get(pk.toBase58()));
|
||||
}
|
||||
if (oracles.get(pk.toBase58())) {
|
||||
console.log(oracles.get(pk.toBase58()));
|
||||
}
|
||||
if (serum3.get(pk.toBase58())) {
|
||||
console.log(serum3.get(pk.toBase58()));
|
||||
}
|
||||
if (perps.get(pk.toBase58())) {
|
||||
console.log(perps.get(pk.toBase58()));
|
||||
}
|
||||
});
|
||||
}
|
|
@ -4,7 +4,7 @@ import { MangoClient } from './client';
|
|||
import { MANGO_V4_ID } from './constants';
|
||||
|
||||
export * from './accounts/bank';
|
||||
export * from './accounts/I80F48';
|
||||
export * from './numbers/I80F48';
|
||||
export * from './accounts/mangoAccount';
|
||||
export {
|
||||
Serum3Market,
|
||||
|
|
|
@ -45,7 +45,7 @@ export class I80F48 {
|
|||
}
|
||||
static fromNumber(x: number): I80F48 {
|
||||
const int_part = Math.trunc(x);
|
||||
const v = new BN(int_part).iushln(48);
|
||||
const v = new BN(int_part.toFixed(0)).iushln(48);
|
||||
v.iadd(new BN((x - int_part) * I80F48.MULTIPLIER_NUMBER));
|
||||
return new I80F48(v);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import BN from 'bn.js';
|
||||
import { expect } from 'chai';
|
||||
import { U64_MAX_BN } from '../utils';
|
||||
import { I80F48 } from './I80F48';
|
||||
|
||||
describe('Math', () => {
|
||||
it('js number to BN and I80F48', () => {
|
||||
// BN can be only be created from js numbers which are <=2^53
|
||||
expect(function () {
|
||||
new BN(0x1fffffffffffff);
|
||||
}).to.not.throw('Assertion failed');
|
||||
expect(function () {
|
||||
new BN(0x20000000000000);
|
||||
}).to.throw('Assertion failed');
|
||||
|
||||
// max BN cant be converted to a number
|
||||
expect(function () {
|
||||
U64_MAX_BN.toNumber();
|
||||
}).to.throw('Number can only safely store up to 53 bits');
|
||||
|
||||
// max I80F48 can be converted to a number
|
||||
// though, the number is represented in scientific notation
|
||||
// anything above ^20 gets represented with scientific notation
|
||||
expect(
|
||||
I80F48.fromString('604462909807314587353087.999999999999996')
|
||||
.toNumber()
|
||||
.toString(),
|
||||
).equals('6.044629098073146e+23');
|
||||
|
||||
// I80F48 constructor takes a BN, but it doesnt do what one might think it does
|
||||
expect(new I80F48(new BN(10)).toNumber()).not.equals(10);
|
||||
expect(I80F48.fromI64(new BN(10)).toNumber()).equals(10);
|
||||
|
||||
// BN treats input as whole integer
|
||||
expect(new BN(1.5).toNumber()).equals(1);
|
||||
});
|
||||
});
|
|
@ -4,7 +4,6 @@ import {
|
|||
TOKEN_PROGRAM_ID,
|
||||
} from '@solana/spl-token';
|
||||
import {
|
||||
AccountMeta,
|
||||
AddressLookupTableAccount,
|
||||
MessageV0,
|
||||
PublicKey,
|
||||
|
@ -14,107 +13,51 @@ import {
|
|||
VersionedTransaction,
|
||||
} from '@solana/web3.js';
|
||||
import BN from 'bn.js';
|
||||
import { Bank, QUOTE_DECIMALS } from './accounts/bank';
|
||||
import { Group } from './accounts/group';
|
||||
import { I80F48 } from './accounts/I80F48';
|
||||
import { MangoAccount, Serum3Orders } from './accounts/mangoAccount';
|
||||
import { PerpMarket } from './accounts/perp';
|
||||
import { QUOTE_DECIMALS } from './accounts/bank';
|
||||
import { I80F48 } from './numbers/I80F48';
|
||||
|
||||
///
|
||||
/// numeric helpers
|
||||
///
|
||||
export const U64_MAX_BN = new BN('18446744073709551615');
|
||||
export const I64_MAX_BN = new BN('9223372036854775807').toTwos(64);
|
||||
|
||||
// https://stackoverflow.com/questions/70261755/user-defined-type-guard-function-and-type-narrowing-to-more-specific-type/70262876#70262876
|
||||
export declare abstract class As<Tag extends keyof never> {
|
||||
private static readonly $as$: unique symbol;
|
||||
private [As.$as$]: Record<Tag, true>;
|
||||
export function toNativeI80F48(uiAmount: number, decimals: number): I80F48 {
|
||||
return I80F48.fromNumber(uiAmount * Math.pow(10, decimals));
|
||||
}
|
||||
|
||||
export function debugAccountMetas(ams: AccountMeta[]): void {
|
||||
for (const am of ams) {
|
||||
console.log(
|
||||
`${am.pubkey.toBase58()}, isSigner: ${am.isSigner
|
||||
.toString()
|
||||
.padStart(5, ' ')}, isWritable - ${am.isWritable
|
||||
.toString()
|
||||
.padStart(5, ' ')}`,
|
||||
);
|
||||
export function toNative(uiAmount: number, decimals: number): BN {
|
||||
return new BN((uiAmount * Math.pow(10, decimals)).toFixed(0));
|
||||
}
|
||||
|
||||
export function toUiDecimals(
|
||||
nativeAmount: BN | I80F48 | number,
|
||||
decimals: number,
|
||||
): number {
|
||||
if (nativeAmount instanceof BN) {
|
||||
return nativeAmount.div(new BN(Math.pow(10, decimals))).toNumber();
|
||||
} else if (nativeAmount instanceof I80F48) {
|
||||
return nativeAmount
|
||||
.div(I80F48.fromNumber(Math.pow(10, decimals)))
|
||||
.toNumber();
|
||||
}
|
||||
return nativeAmount / Math.pow(10, decimals);
|
||||
}
|
||||
|
||||
export function debugHealthAccounts(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
publicKeys: PublicKey[],
|
||||
): void {
|
||||
const banks = new Map(
|
||||
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
||||
banks[0].publicKey.toBase58(),
|
||||
`${banks[0].name} bank`,
|
||||
]),
|
||||
);
|
||||
const oracles = new Map(
|
||||
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
||||
banks[0].oracle.toBase58(),
|
||||
`${banks[0].name} oracle`,
|
||||
]),
|
||||
);
|
||||
const serum3 = new Map(
|
||||
mangoAccount.serum3Active().map((serum3: Serum3Orders) => {
|
||||
const serum3Market = Array.from(
|
||||
group.serum3MarketsMapByExternal.values(),
|
||||
).find((serum3Market) => serum3Market.marketIndex === serum3.marketIndex);
|
||||
if (!serum3Market) {
|
||||
throw new Error(
|
||||
`Serum3Orders for non existent market with market index ${serum3.marketIndex}`,
|
||||
);
|
||||
}
|
||||
return [serum3.openOrders.toBase58(), `${serum3Market.name} spot oo`];
|
||||
}),
|
||||
);
|
||||
const perps = new Map(
|
||||
Array.from(group.perpMarketsMapByName.values()).map(
|
||||
(perpMarket: PerpMarket) => [
|
||||
perpMarket.publicKey.toBase58(),
|
||||
`${perpMarket.name} perp market`,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
publicKeys.map((pk) => {
|
||||
if (banks.get(pk.toBase58())) {
|
||||
console.log(banks.get(pk.toBase58()));
|
||||
}
|
||||
if (oracles.get(pk.toBase58())) {
|
||||
console.log(oracles.get(pk.toBase58()));
|
||||
}
|
||||
if (serum3.get(pk.toBase58())) {
|
||||
console.log(serum3.get(pk.toBase58()));
|
||||
}
|
||||
if (perps.get(pk.toBase58())) {
|
||||
console.log(perps.get(pk.toBase58()));
|
||||
}
|
||||
});
|
||||
export function toUiDecimalsForQuote(
|
||||
nativeAmount: BN | I80F48 | number,
|
||||
): number {
|
||||
return toUiDecimals(nativeAmount, QUOTE_DECIMALS);
|
||||
}
|
||||
|
||||
export async function findOrCreate<T>(
|
||||
entityName: string,
|
||||
findMethod: (...x: any) => any,
|
||||
findArgs: any[],
|
||||
createMethod: (...x: any) => any,
|
||||
createArgs: any[],
|
||||
): Promise<T> {
|
||||
let many: T[] = await findMethod(...findArgs);
|
||||
let one: T;
|
||||
if (many.length > 0) {
|
||||
one = many[0];
|
||||
return one;
|
||||
}
|
||||
await createMethod(...createArgs);
|
||||
many = await findMethod(...findArgs);
|
||||
one = many[0];
|
||||
return one;
|
||||
export function toUiI80F48(nativeAmount: I80F48, decimals: number): I80F48 {
|
||||
return nativeAmount.div(I80F48.fromNumber(Math.pow(10, decimals)));
|
||||
}
|
||||
|
||||
///
|
||||
/// web3js extensions
|
||||
///
|
||||
|
||||
/**
|
||||
* Get the address of the associated token account for a given mint and owner
|
||||
*
|
||||
|
@ -168,40 +111,6 @@ export async function createAssociatedTokenAccountIdempotentInstruction(
|
|||
});
|
||||
}
|
||||
|
||||
export function toNative(uiAmount: number, decimals: number): I80F48 {
|
||||
return I80F48.fromNumber(uiAmount).mul(
|
||||
I80F48.fromNumber(Math.pow(10, decimals)),
|
||||
);
|
||||
}
|
||||
|
||||
export function toNativeDecimals(amount: number, decimals: number): BN {
|
||||
return new BN(Math.trunc(amount * Math.pow(10, decimals)));
|
||||
}
|
||||
|
||||
export function toUiDecimals(
|
||||
amount: I80F48 | number,
|
||||
decimals: number,
|
||||
): number {
|
||||
amount = amount instanceof I80F48 ? amount.toNumber() : amount;
|
||||
return amount / Math.pow(10, decimals);
|
||||
}
|
||||
|
||||
export function toUiDecimalsForQuote(amount: I80F48 | number): number {
|
||||
amount = amount instanceof I80F48 ? amount.toNumber() : amount;
|
||||
return amount / Math.pow(10, QUOTE_DECIMALS);
|
||||
}
|
||||
|
||||
export function toU64(amount: number, decimals: number): BN {
|
||||
const bn = toNativeDecimals(amount, decimals).toString();
|
||||
console.log('bn', bn);
|
||||
|
||||
return new BN(bn);
|
||||
}
|
||||
|
||||
export function nativeI80F48ToUi(amount: I80F48, decimals: number): I80F48 {
|
||||
return amount.div(I80F48.fromNumber(Math.pow(10, decimals)));
|
||||
}
|
||||
|
||||
export async function buildVersionedTx(
|
||||
provider: AnchorProvider,
|
||||
ix: TransactionInstruction[],
|
||||
|
@ -222,3 +131,13 @@ export async function buildVersionedTx(
|
|||
]);
|
||||
return vTx;
|
||||
}
|
||||
|
||||
///
|
||||
/// ts extension
|
||||
///
|
||||
|
||||
// https://stackoverflow.com/questions/70261755/user-defined-type-guard-function-and-type-narrowing-to-more-specific-type/70262876#70262876
|
||||
export declare abstract class As<Tag extends keyof never> {
|
||||
private static readonly $as$: unique symbol;
|
||||
private [As.$as$]: Record<Tag, true>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue