2022-05-11 04:33:01 -07:00
import { BN } from '@project-serum/anchor' ;
2022-04-12 07:19:58 -07:00
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes' ;
2022-09-23 20:14:12 -07:00
import { OpenOrders , Order , Orderbook } from '@project-serum/serum/lib/market' ;
2022-10-04 12:13:24 -07:00
import { AccountInfo , PublicKey } from '@solana/web3.js' ;
2022-04-12 21:37:36 -07:00
import { MangoClient } from '../client' ;
2022-09-23 20:14:12 -07:00
import { SERUM3_PROGRAM_ID } from '../constants' ;
2022-09-30 06:07:43 -07:00
import { I80F48 , I80F48Dto , ONE_I80F48 , ZERO_I80F48 } from '../numbers/I80F48' ;
import { toNativeI80F48 , toUiDecimals , toUiDecimalsForQuote } from '../utils' ;
2022-09-29 06:51:09 -07:00
import { Bank , TokenIndex } from './bank' ;
2022-06-03 06:34:05 -07:00
import { Group } from './group' ;
2022-09-29 06:51:09 -07:00
import { HealthCache } from './healthCache' ;
import { PerpMarket , PerpMarketIndex , PerpOrder , PerpOrderSide } from './perp' ;
import { MarketIndex , Serum3Side } from './serum3' ;
2022-04-02 23:57:45 -07:00
export class MangoAccount {
2022-10-11 00:34:02 -07:00
public name : string ;
2022-06-22 02:21:02 -07:00
public tokens : TokenPosition [ ] ;
public serum3 : Serum3Orders [ ] ;
2022-08-18 04:45:31 -07:00
public perps : PerpPosition [ ] ;
2022-09-20 03:57:01 -07:00
public perpOpenOrders : PerpOo [ ] ;
2022-04-02 23:57:45 -07:00
static from (
publicKey : PublicKey ,
obj : {
group : PublicKey ;
owner : PublicKey ;
2022-08-04 01:41:54 -07:00
name : number [ ] ;
2022-04-02 23:57:45 -07:00
delegate : PublicKey ;
accountNum : number ;
2022-10-11 00:34:02 -07:00
beingLiquidated : number ;
inHealthRegion : number ;
2022-08-15 02:10:33 -07:00
netDeposits : BN ;
netSettled : BN ;
2022-10-11 00:34:02 -07:00
healthRegionBeginInitHealth : BN ;
2022-08-04 00:07:32 -07:00
headerVersion : number ;
2022-08-04 01:41:54 -07:00
tokens : unknown ;
2022-08-04 10:42:41 -07:00
serum3 : unknown ;
2022-08-04 01:41:54 -07:00
perps : unknown ;
perpOpenOrders : unknown ;
2022-04-02 23:57:45 -07:00
} ,
2022-10-11 00:34:02 -07:00
) : MangoAccount {
2022-04-02 23:57:45 -07:00
return new MangoAccount (
publicKey ,
obj . group ,
obj . owner ,
2022-08-04 01:41:54 -07:00
obj . name ,
2022-04-02 23:57:45 -07:00
obj . delegate ,
obj . accountNum ,
2022-10-11 00:34:02 -07:00
obj . beingLiquidated == 1 ,
obj . inHealthRegion == 1 ,
2022-08-04 01:41:54 -07:00
obj . netDeposits ,
obj . netSettled ,
2022-10-11 00:34:02 -07:00
obj . healthRegionBeginInitHealth ,
2022-08-04 01:41:54 -07:00
obj . headerVersion ,
obj . tokens as TokenPositionDto [ ] ,
obj . serum3 as Serum3PositionDto [ ] ,
obj . perps as PerpPositionDto [ ] ,
2022-09-20 03:57:01 -07:00
obj . perpOpenOrders as PerpOoDto [ ] ,
2022-09-29 06:51:09 -07:00
new Map ( ) , // serum3OosMapByMarketIndex
2022-04-02 23:57:45 -07:00
) ;
}
constructor (
public publicKey : PublicKey ,
2022-04-07 09:58:42 -07:00
public group : PublicKey ,
public owner : PublicKey ,
2022-08-04 01:41:54 -07:00
name : number [ ] ,
2022-04-07 09:58:42 -07:00
public delegate : PublicKey ,
2022-08-04 01:41:54 -07:00
public accountNum : number ,
2022-10-11 00:34:02 -07:00
public beingLiquidated : boolean ,
public inHealthRegion : boolean ,
public netDeposits : BN ,
public netSettled : BN ,
public healthRegionBeginInitHealth : BN ,
public headerVersion : number ,
2022-07-25 07:07:53 -07:00
tokens : TokenPositionDto [ ] ,
serum3 : Serum3PositionDto [ ] ,
perps : PerpPositionDto [ ] ,
2022-09-20 03:57:01 -07:00
perpOpenOrders : PerpOoDto [ ] ,
2022-09-29 06:51:09 -07:00
public serum3OosMapByMarketIndex : Map < number , OpenOrders > ,
2022-04-02 23:57:45 -07:00
) {
2022-04-12 07:19:58 -07:00
this . name = utf8 . decode ( new Uint8Array ( name ) ) . split ( '\x00' ) [ 0 ] ;
2022-07-25 07:07:53 -07:00
this . tokens = tokens . map ( ( dto ) = > TokenPosition . from ( dto ) ) ;
this . serum3 = serum3 . map ( ( dto ) = > Serum3Orders . from ( dto ) ) ;
2022-08-18 04:45:31 -07:00
this . perps = perps . map ( ( dto ) = > PerpPosition . from ( dto ) ) ;
2022-09-20 03:57:01 -07:00
this . perpOpenOrders = perpOpenOrders . map ( ( dto ) = > PerpOo . from ( dto ) ) ;
2022-04-02 23:57:45 -07:00
}
2022-09-29 06:51:09 -07:00
async reload ( client : MangoClient ) : Promise < MangoAccount > {
2022-09-02 15:47:09 -07:00
const mangoAccount = await client . getMangoAccount ( this ) ;
2022-09-29 06:51:09 -07:00
await mangoAccount . reloadAccountData ( client ) ;
2022-09-02 15:47:09 -07:00
Object . assign ( this , mangoAccount ) ;
return mangoAccount ;
2022-07-04 03:29:35 -07:00
}
2022-09-05 09:31:57 -07:00
async reloadWithSlot (
client : MangoClient ,
) : Promise < { value : MangoAccount ; slot : number } > {
const resp = await client . getMangoAccountWithSlot ( this . publicKey ) ;
2022-09-29 06:51:09 -07:00
await resp ? . value . reloadAccountData ( client ) ;
2022-09-05 09:31:57 -07:00
Object . assign ( this , resp ? . value ) ;
return { value : resp ! . value , slot : resp ! . slot } ;
}
2022-09-29 06:51:09 -07:00
async reloadAccountData ( client : MangoClient ) : Promise < MangoAccount > {
const serum3Active = this . serum3Active ( ) ;
const ais =
await client . program . provider . connection . getMultipleAccountsInfo (
serum3Active . map ( ( serum3 ) = > serum3 . openOrders ) ,
) ;
this . serum3OosMapByMarketIndex = new Map (
Array . from (
ais . map ( ( ai , i ) = > {
if ( ! ai ) {
throw new Error (
` Undefined AI for open orders ${ serum3Active [ i ] . openOrders } and market ${ serum3Active [ i ] . marketIndex } ! ` ,
) ;
}
const oo = OpenOrders . fromAccountInfo (
serum3Active [ i ] . openOrders ,
ai ,
SERUM3_PROGRAM_ID [ client . cluster ] ,
) ;
return [ serum3Active [ i ] . marketIndex , oo ] ;
} ) ,
) ,
) ;
2022-08-27 00:55:55 -07:00
return this ;
2022-04-12 21:37:36 -07:00
}
2022-08-31 02:36:44 -07:00
tokensActive ( ) : TokenPosition [ ] {
return this . tokens . filter ( ( token ) = > token . isActive ( ) ) ;
}
serum3Active ( ) : Serum3Orders [ ] {
return this . serum3 . filter ( ( serum3 ) = > serum3 . isActive ( ) ) ;
}
perpActive ( ) : PerpPosition [ ] {
return this . perps . filter ( ( perp ) = > perp . isActive ( ) ) ;
}
2022-09-20 03:57:01 -07:00
perpOrdersActive ( ) : PerpOo [ ] {
return this . perpOpenOrders . filter (
( oo ) = > oo . orderMarket !== PerpOo . OrderMarketUnset ,
) ;
}
2022-09-29 06:51:09 -07:00
getToken ( tokenIndex : TokenIndex ) : TokenPosition | undefined {
2022-04-02 23:57:45 -07:00
return this . tokens . find ( ( ta ) = > ta . tokenIndex == tokenIndex ) ;
}
2022-09-29 06:51:09 -07:00
getSerum3Account ( marketIndex : MarketIndex ) : Serum3Orders | undefined {
2022-04-08 03:30:21 -07:00
return this . serum3 . find ( ( sa ) = > sa . marketIndex == marketIndex ) ;
}
2022-10-07 04:52:04 -07:00
getPerpPosition ( perpMarketIndex : PerpMarketIndex ) : PerpPosition | undefined {
return this . perps . find ( ( pp ) = > pp . marketIndex == perpMarketIndex ) ;
}
getPerpPositionUi ( group : Group , perpMarketIndex : PerpMarketIndex ) : number {
const pp = this . perps . find ( ( pp ) = > pp . marketIndex == perpMarketIndex ) ;
if ( ! pp ) {
throw new Error ( ` No position found for PerpMarket ${ perpMarketIndex } ! ` ) ;
}
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
return pp . getBasePositionUi ( perpMarket ) ;
}
2022-09-29 06:51:09 -07:00
getSerum3OoAccount ( marketIndex : MarketIndex ) : OpenOrders {
const oo : OpenOrders | undefined =
this . serum3OosMapByMarketIndex . get ( marketIndex ) ;
if ( ! oo ) {
throw new Error (
` Open orders account not loaded for market with marketIndex ${ marketIndex } ! ` ,
) ;
}
return oo ;
}
2022-08-18 07:19:37 -07:00
// How to navigate
// * if a function is returning a I80F48, then usually the return value is in native quote or native token, unless specified
2022-08-18 07:39:22 -07:00
// * if a function is returning a number, then usually the return value is in ui tokens, unless specified
2022-08-18 07:19:37 -07:00
// * 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"
2022-04-02 23:57:45 -07:00
2022-08-18 07:19:37 -07:00
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns native balance for a token , is signed
2022-08-18 07:19:37 -07:00
* /
getTokenBalance ( bank : Bank ) : I80F48 {
2022-09-29 06:51:09 -07:00
const tp = this . getToken ( bank . tokenIndex ) ;
2022-09-01 00:48:23 -07:00
return tp ? tp . balance ( bank ) : ZERO_I80F48 ( ) ;
2022-08-18 07:19:37 -07:00
}
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns native deposits for a token , 0 if position has borrows
2022-08-18 07:19:37 -07:00
* /
getTokenDeposits ( bank : Bank ) : I80F48 {
2022-09-29 06:51:09 -07:00
const tp = this . getToken ( bank . tokenIndex ) ;
2022-09-01 00:48:23 -07:00
return tp ? tp . deposits ( bank ) : ZERO_I80F48 ( ) ;
2022-07-04 03:29:35 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns native borrows for a token , 0 if position has deposits
2022-08-18 07:19:37 -07:00
* /
getTokenBorrows ( bank : Bank ) : I80F48 {
2022-09-29 06:51:09 -07:00
const tp = this . getToken ( bank . tokenIndex ) ;
2022-09-01 00:48:23 -07:00
return tp ? tp . borrows ( bank ) : ZERO_I80F48 ( ) ;
2022-07-04 03:29:35 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns UI balance for a token , is signed
2022-08-18 07:19:37 -07:00
* /
getTokenBalanceUi ( bank : Bank ) : number {
2022-09-29 06:51:09 -07:00
const tp = this . getToken ( bank . tokenIndex ) ;
2022-08-23 04:47:08 -07:00
return tp ? tp . balanceUi ( bank ) : 0 ;
2022-04-02 23:57:45 -07:00
}
2022-05-11 04:33:01 -07:00
2022-08-18 07:19:37 -07:00
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns UI deposits for a token , 0 or more
2022-08-18 07:19:37 -07:00
* /
getTokenDepositsUi ( bank : Bank ) : number {
2022-09-29 06:51:09 -07:00
const ta = this . getToken ( bank . tokenIndex ) ;
2022-08-18 07:19:37 -07:00
return ta ? ta . depositsUi ( bank ) : 0 ;
2022-06-29 12:55:39 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
*
* @param bank
2022-08-23 04:47:08 -07:00
* @returns UI borrows for a token , 0 or less
2022-08-18 07:19:37 -07:00
* /
getTokenBorrowsUi ( bank : Bank ) : number {
2022-09-29 06:51:09 -07:00
const ta = this . getToken ( bank . tokenIndex ) ;
2022-08-18 07:19:37 -07:00
return ta ? ta . borrowsUi ( bank ) : 0 ;
2022-06-29 12:55:39 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
* Health , see health . rs or https : //docs.mango.markets/mango-markets/health-overview
* @param healthType
* @returns raw health number , in native quote
* /
2022-09-29 06:51:09 -07:00
getHealth ( group : Group , healthType : HealthType ) : I80F48 {
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc . health ( healthType ) ;
2022-07-12 03:05:19 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
* 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
* /
2022-09-29 06:51:09 -07:00
getHealthRatio ( group : Group , healthType : HealthType ) : I80F48 {
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc . healthRatio ( healthType ) ;
2022-07-12 03:05:19 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
* Health ratio
* @param healthType
* @returns health ratio , in percentage form , capped to 100
* /
2022-09-30 06:07:43 -07:00
getHealthRatioUi ( group : Group , healthType : HealthType ) : number {
2022-09-29 06:51:09 -07:00
const ratio = this . getHealthRatio ( group , healthType ) . toNumber ( ) ;
2022-09-30 06:07:43 -07:00
return ratio > 100 ? 100 : Math.trunc ( ratio ) ;
2022-08-11 12:06:01 -07:00
}
2022-07-04 03:29:35 -07:00
/ * *
2022-09-30 04:33:21 -07:00
* Sum of all the assets i . e . token deposits , borrows , total assets in spot open orders , and perps positions .
2022-08-18 07:19:37 -07:00
* @returns equity , in native quote
2022-07-04 03:29:35 -07:00
* /
2022-09-29 06:51:09 -07:00
getEquity ( group : Group ) : I80F48 {
const tokensMap = new Map < number , I80F48 > ( ) ;
for ( const tp of this . tokensActive ( ) ) {
const bank = group . getFirstBankByTokenIndex ( tp . tokenIndex ) ;
tokensMap . set ( tp . tokenIndex , tp . balance ( bank ) . mul ( bank . price ) ) ;
2022-08-31 02:41:12 -07:00
}
2022-09-29 06:51:09 -07:00
for ( const sp of this . serum3Active ( ) ) {
const oo = this . getSerum3OoAccount ( sp . marketIndex ) ;
const baseBank = group . getFirstBankByTokenIndex ( sp . baseTokenIndex ) ;
tokensMap
. get ( baseBank . tokenIndex ) !
2022-09-30 06:07:43 -07:00
. iadd ( I80F48 . fromI64 ( oo . baseTokenTotal ) . mul ( baseBank . price ) ) ;
2022-09-29 06:51:09 -07:00
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 (
2022-09-30 06:07:43 -07:00
I80F48 . fromI64 (
oo . quoteTokenTotal . add ( ( oo as any ) . referrerRebatesAccrued ) ,
2022-09-29 06:51:09 -07:00
) . mul ( quoteBank . price ) ,
) ;
2022-08-31 02:41:12 -07:00
}
2022-09-29 06:51:09 -07:00
const tokenEquity = Array . from ( tokensMap . values ( ) ) . reduce (
( a , b ) = > a . add ( b ) ,
ZERO_I80F48 ( ) ,
) ;
const perpEquity = this . perpActive ( ) . reduce (
( a , b ) = >
a . add ( b . getEquity ( group . getPerpMarketByMarketIndex ( b . marketIndex ) ) ) ,
ZERO_I80F48 ( ) ,
) ;
return tokenEquity . add ( perpEquity ) ;
2022-07-04 03:29:35 -07:00
}
/ * *
* The amount of native quote you could withdraw against your existing assets .
2022-08-18 07:19:37 -07:00
* @returns collateral value , in native quote
2022-07-04 03:29:35 -07:00
* /
2022-09-29 06:51:09 -07:00
getCollateralValue ( group : Group ) : I80F48 {
return this . getHealth ( group , HealthType . init ) ;
2022-07-04 03:29:35 -07:00
}
/ * *
2022-08-11 08:44:12 -07:00
* Sum of all positive assets .
2022-08-18 07:19:37 -07:00
* @returns assets , in native quote
2022-07-04 03:29:35 -07:00
* /
2022-09-29 06:51:09 -07:00
getAssetsValue ( group : Group , healthType : HealthType ) : I80F48 {
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc . assets ( healthType ) ;
2022-07-04 03:29:35 -07:00
}
/ * *
2022-08-11 08:44:12 -07:00
* Sum of all negative assets .
2022-08-18 07:19:37 -07:00
* @returns liabs , in native quote
2022-07-04 03:29:35 -07:00
* /
2022-09-29 06:51:09 -07:00
getLiabsValue ( group : Group , healthType : HealthType ) : I80F48 {
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc . liabs ( healthType ) ;
2022-07-04 03:29:35 -07:00
}
2022-09-23 11:39:51 -07:00
/ * *
* @returns Overall PNL , in native quote
* PNL is defined here as spot value + serum3 open orders value + perp value - net deposits value ( evaluated at native quote price at the time of the deposit / withdraw )
* spot value + serum3 open orders value + perp value is returned by getEquity ( open orders values are added to spot token values implicitly )
* /
2022-09-29 06:51:09 -07:00
getPnl ( group : Group ) : I80F48 {
return this . getEquity ( group ) ? . add (
2022-09-27 08:33:51 -07:00
I80F48 . fromI64 ( this . netDeposits ) . mul ( I80F48 . fromNumber ( - 1 ) ) ,
) ;
2022-07-04 03:29:35 -07:00
}
/ * *
2022-08-23 04:47:08 -07:00
* The amount of given native token you can withdraw including borrows , considering all existing assets as collateral .
* @returns amount of given native token you can borrow , considering all existing assets as collateral , in native token
2022-07-04 03:29:35 -07:00
* /
2022-10-07 10:48:05 -07:00
getMaxWithdrawWithBorrowForToken ( group : Group , mintPk : PublicKey ) : I80F48 {
2022-08-23 04:47:08 -07:00
const tokenBank : Bank = group . getFirstBankByMint ( mintPk ) ;
2022-09-29 06:51:09 -07:00
const initHealth = this . getHealth ( group , HealthType . init ) ;
2022-08-31 02:41:12 -07:00
2022-08-23 04:47:08 -07:00
// Case 1:
// Cannot withdraw if init health is below 0
2022-09-01 00:48:23 -07:00
if ( initHealth . lte ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-23 04:47:08 -07:00
}
// Deposits need special treatment since they would neither count towards liabilities
// nor would be charged loanOriginationFeeRate when withdrawn
2022-09-29 06:51:09 -07:00
const tp = this . getToken ( tokenBank . tokenIndex ) ;
2022-09-01 00:48:23 -07:00
const existingTokenDeposits = tp ? tp . deposits ( tokenBank ) : ZERO_I80F48 ( ) ;
2022-09-13 23:14:46 -07:00
let existingPositionHealthContrib = ZERO_I80F48 ( ) ;
2022-09-01 00:48:23 -07:00
if ( existingTokenDeposits . gt ( ZERO_I80F48 ( ) ) ) {
2022-09-13 23:14:46 -07:00
existingPositionHealthContrib = existingTokenDeposits
. mul ( tokenBank . price )
2022-09-01 00:48:23 -07:00
. imul ( tokenBank . initAssetWeight ) ;
2022-08-23 04:47:08 -07:00
}
// Case 2: token deposits have higher contribution than initHealth,
// can withdraw without borrowing until initHealth reaches 0
if ( existingPositionHealthContrib . gt ( initHealth ) ) {
const withdrawAbleExistingPositionHealthContrib = initHealth ;
return withdrawAbleExistingPositionHealthContrib
. div ( tokenBank . initAssetWeight )
. div ( tokenBank . price ) ;
}
// Case 3: withdraw = withdraw existing deposits + borrows until initHealth reaches 0
const initHealthWithoutExistingPosition = initHealth . sub (
existingPositionHealthContrib ,
) ;
const maxBorrowNative = initHealthWithoutExistingPosition
. div ( tokenBank . initLiabWeight )
. div ( tokenBank . price ) ;
const maxBorrowNativeWithoutFees = maxBorrowNative . div (
2022-09-01 00:48:23 -07:00
ONE_I80F48 ( ) . add ( tokenBank . loanOriginationFeeRate ) ,
2022-08-23 04:47:08 -07:00
) ;
return maxBorrowNativeWithoutFees . add ( existingTokenDeposits ) ;
}
2022-10-07 10:48:05 -07:00
getMaxWithdrawWithBorrowForTokenUi ( group : Group , mintPk : PublicKey ) : number {
2022-08-31 02:41:12 -07:00
const maxWithdrawWithBorrow = this . getMaxWithdrawWithBorrowForToken (
group ,
mintPk ,
2022-07-14 05:29:44 -07:00
) ;
2022-10-07 10:48:05 -07:00
return toUiDecimals ( maxWithdrawWithBorrow , group . getMintDecimals ( mintPk ) ) ;
2022-07-04 03:29:35 -07:00
}
2022-08-23 07:21:05 -07:00
/ * *
* The max amount of given source ui token you can swap to a target token .
2022-09-23 02:43:26 -07:00
* PriceFactor is ratio between A - how many source tokens can be traded for target tokens
* and B - source native oracle price / target native oracle price .
* e . g . a slippage of 5 % and some fees which are 1 % , then priceFactor = 0.94
2022-08-23 07:21:05 -07:00
* the factor is used to compute how much target can be obtained by swapping source
2022-09-23 02:43:26 -07:00
* in reality , and not only relying on oracle prices , and taking in account e . g . slippage which
* can occur at large size
2022-08-23 07:21:05 -07:00
* @returns max amount of given source ui token you can swap to a target token , in ui token
* /
getMaxSourceUiForTokenSwap (
group : Group ,
sourceMintPk : PublicKey ,
targetMintPk : PublicKey ,
2022-09-23 02:43:26 -07:00
priceFactor : number ,
2022-10-07 10:48:05 -07:00
) : number {
2022-09-23 02:43:26 -07:00
if ( sourceMintPk . equals ( targetMintPk ) ) {
return 0 ;
}
2022-09-29 06:51:09 -07:00
const hc = HealthCache . fromMangoAccount ( group , this ) ;
const maxSource = hc . getMaxSourceForTokenSwap (
2022-09-23 02:43:26 -07:00
group . getFirstBankByMint ( sourceMintPk ) ,
group . getFirstBankByMint ( targetMintPk ) ,
2022-09-29 06:51:09 -07:00
I80F48 . fromNumber ( 2 ) , // target 2% health
2022-09-23 02:43:26 -07:00
I80F48 . fromNumber ( priceFactor ) ,
) ;
maxSource . idiv (
ONE_I80F48 ( ) . add (
group . getFirstBankByMint ( sourceMintPk ) . loanOriginationFeeRate ,
) ,
2022-08-23 07:21:05 -07:00
) ;
2022-10-07 10:48:05 -07:00
return toUiDecimals ( maxSource , group . getMintDecimals ( sourceMintPk ) ) ;
2022-08-23 07:21:05 -07:00
}
2022-08-23 04:47:08 -07:00
/ * *
* Simulates new health ratio after applying tokenChanges to the token positions .
* Note : token changes are expected in ui amounts
*
* 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
* /
simHealthRatioWithTokenPositionUiChanges (
group : Group ,
uiTokenChanges : {
uiTokenAmount : number ;
mintPk : PublicKey ;
} [ ] ,
healthType : HealthType = HealthType . init ,
2022-10-07 10:48:05 -07:00
) : number {
2022-08-23 04:47:08 -07:00
const nativeTokenChanges = uiTokenChanges . map ( ( tokenChange ) = > {
return {
2022-09-30 06:07:43 -07:00
nativeTokenAmount : toNativeI80F48 (
2022-08-23 04:47:08 -07:00
tokenChange . uiTokenAmount ,
group . getMintDecimals ( tokenChange . mintPk ) ,
) ,
mintPk : tokenChange.mintPk ,
} ;
} ) ;
2022-09-29 06:51:09 -07:00
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc
2022-08-23 07:21:05 -07:00
. simHealthRatioWithTokenPositionChanges (
group ,
nativeTokenChanges ,
healthType ,
)
. toNumber ( ) ;
2022-07-12 03:05:19 -07:00
}
2022-09-23 20:14:12 -07:00
public async loadSerum3OpenOrdersAccounts (
client : MangoClient ,
2022-09-25 17:27:44 -07:00
) : Promise < OpenOrders [ ] > {
2022-09-25 17:26:02 -07:00
const response =
2022-09-23 20:14:12 -07:00
await client . program . provider . connection . getMultipleAccountsInfo (
this . serum3 . map ( ( s ) = > s . openOrders ) ,
) ;
2022-09-25 17:26:02 -07:00
const accounts = response . filter ( ( a ) : a is AccountInfo < Buffer > = >
Boolean ( a ) ,
) ;
2022-09-23 20:14:12 -07:00
return accounts . map ( ( acc , index ) = > {
2022-09-25 17:26:02 -07:00
return OpenOrders . fromAccountInfo (
this . serum3 [ index ] . openOrders ,
acc ,
SERUM3_PROGRAM_ID [ client . cluster ] ,
) ;
2022-09-23 20:14:12 -07:00
} ) ;
}
2022-08-31 02:36:44 -07:00
public async loadSerum3OpenOrdersForMarket (
client : MangoClient ,
group : Group ,
externalMarketPk : PublicKey ,
) : Promise < Order [ ] > {
2022-09-29 06:51:09 -07:00
const serum3Market =
group . getSerum3MarketByExternalMarket ( externalMarketPk ) ;
2022-08-31 02:36:44 -07:00
const serum3OO = this . serum3Active ( ) . find (
( s ) = > s . marketIndex === serum3Market . marketIndex ,
) ;
if ( ! serum3OO ) {
throw new Error ( ` No open orders account found for ${ externalMarketPk } ` ) ;
}
2022-09-29 06:51:09 -07:00
const serum3MarketExternal = group . serum3ExternalMarketsMap . get (
2022-08-31 02:36:44 -07:00
externalMarketPk . toBase58 ( ) ,
) ! ;
const [ bidsInfo , asksInfo ] =
await client . program . provider . connection . getMultipleAccountsInfo ( [
serum3MarketExternal . bidsAddress ,
serum3MarketExternal . asksAddress ,
] ) ;
2022-09-29 06:51:09 -07:00
if ( ! bidsInfo ) {
2022-08-31 02:55:54 -07:00
throw new Error (
2022-09-29 06:51:09 -07:00
` Undefined bidsInfo for serum3Market with externalMarket ${ externalMarketPk . toString ( ) ! } ` ,
) ;
}
if ( ! asksInfo ) {
2022-08-31 02:55:54 -07:00
throw new Error (
2022-09-29 06:51:09 -07:00
` Undefined asksInfo for serum3Market with externalMarket ${ externalMarketPk . toString ( ) ! } ` ,
2022-08-31 02:55:54 -07:00
) ;
}
2022-08-31 02:36:44 -07:00
const bids = Orderbook . decode ( serum3MarketExternal , bidsInfo . data ) ;
const asks = Orderbook . decode ( serum3MarketExternal , asksInfo . data ) ;
return [ . . . bids , . . . asks ] . filter ( ( o ) = >
o . openOrdersAddress . equals ( serum3OO . openOrders ) ,
) ;
}
2022-07-04 03:29:35 -07:00
/ * *
2022-08-31 02:36:44 -07:00
* @param group
2022-09-23 02:43:26 -07:00
* @param externalMarketPk
2022-10-07 04:52:04 -07:00
* @returns maximum ui quote which can be traded at oracle price for base token given current health
2022-07-04 03:29:35 -07:00
* /
2022-08-31 02:36:44 -07:00
public getMaxQuoteForSerum3BidUi (
group : Group ,
externalMarketPk : PublicKey ,
) : number {
2022-09-29 06:51:09 -07:00
const serum3Market =
group . getSerum3MarketByExternalMarket ( externalMarketPk ) ;
const baseBank = group . getFirstBankByTokenIndex (
serum3Market . baseTokenIndex ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-29 06:51:09 -07:00
const quoteBank = group . getFirstBankByTokenIndex (
serum3Market . quoteTokenIndex ,
) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
let nativeAmount = hc . getMaxSerum3OrderForHealthRatio (
baseBank ,
quoteBank ,
serum3Market ,
Serum3Side . bid ,
I80F48 . fromNumber ( 2 ) ,
) ;
// If its a bid then the reserved fund and potential loan is in base
// also keep some buffer for fees, use taker fees for worst case simulation.
nativeAmount = nativeAmount
. div ( quoteBank . price )
. div ( ONE_I80F48 ( ) . add ( baseBank . loanOriginationFeeRate ) )
. div ( ONE_I80F48 ( ) . add ( I80F48 . fromNumber ( group . getSerum3FeeRates ( false ) ) ) ) ;
2022-08-31 02:36:44 -07:00
return toUiDecimals (
nativeAmount ,
group . getFirstBankByTokenIndex ( serum3Market . quoteTokenIndex ) . mintDecimals ,
) ;
}
/ * *
* @param group
2022-09-23 02:43:26 -07:00
* @param externalMarketPk
2022-10-07 04:52:04 -07:00
* @returns maximum ui base which can be traded at oracle price for quote token given current health
2022-08-31 02:36:44 -07:00
* /
public getMaxBaseForSerum3AskUi (
group : Group ,
externalMarketPk : PublicKey ,
) : number {
2022-09-29 06:51:09 -07:00
const serum3Market =
group . getSerum3MarketByExternalMarket ( externalMarketPk ) ;
const baseBank = group . getFirstBankByTokenIndex (
serum3Market . baseTokenIndex ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-29 06:51:09 -07:00
const quoteBank = group . getFirstBankByTokenIndex (
serum3Market . quoteTokenIndex ,
) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
let nativeAmount = hc . getMaxSerum3OrderForHealthRatio (
baseBank ,
quoteBank ,
serum3Market ,
Serum3Side . ask ,
I80F48 . fromNumber ( 2 ) ,
) ;
// If its a ask then the reserved fund and potential loan is in base
// also keep some buffer for fees, use taker fees for worst case simulation.
nativeAmount = nativeAmount
. div ( baseBank . price )
. div ( ONE_I80F48 ( ) . add ( baseBank . loanOriginationFeeRate ) )
. div ( ONE_I80F48 ( ) . add ( I80F48 . fromNumber ( group . getSerum3FeeRates ( false ) ) ) ) ;
2022-08-31 02:36:44 -07:00
return toUiDecimals (
nativeAmount ,
group . getFirstBankByTokenIndex ( serum3Market . baseTokenIndex ) . mintDecimals ,
) ;
}
/ * *
*
* @param group
2022-09-23 02:43:26 -07:00
* @param uiQuoteAmount
* @param externalMarketPk
2022-08-31 02:36:44 -07:00
* @param healthType
2022-09-23 02:43:26 -07:00
* @returns health ratio after a bid with uiQuoteAmount is placed
2022-08-31 02:36:44 -07:00
* /
2022-09-23 02:43:26 -07:00
public simHealthRatioWithSerum3BidUiChanges (
2022-08-31 02:36:44 -07:00
group : Group ,
2022-09-23 02:43:26 -07:00
uiQuoteAmount : number ,
externalMarketPk : PublicKey ,
2022-08-31 02:36:44 -07:00
healthType : HealthType = HealthType . init ,
2022-09-23 02:43:26 -07:00
) : number {
2022-09-29 06:51:09 -07:00
const serum3Market =
group . getSerum3MarketByExternalMarket ( externalMarketPk ) ;
const baseBank = group . getFirstBankByTokenIndex (
serum3Market . baseTokenIndex ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-29 06:51:09 -07:00
const quoteBank = group . getFirstBankByTokenIndex (
serum3Market . quoteTokenIndex ,
) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc
2022-09-23 02:43:26 -07:00
. simHealthRatioWithSerum3BidChanges (
2022-09-29 06:51:09 -07:00
baseBank ,
quoteBank ,
2022-09-30 06:07:43 -07:00
toNativeI80F48 (
2022-09-23 02:43:26 -07:00
uiQuoteAmount ,
group . getFirstBankByTokenIndex ( serum3Market . quoteTokenIndex )
. mintDecimals ,
) ,
serum3Market ,
healthType ,
)
. toNumber ( ) ;
2022-08-31 02:36:44 -07:00
}
2022-09-23 02:43:26 -07:00
/ * *
*
* @param group
* @param uiBaseAmount
* @param externalMarketPk
* @param healthType
* @returns health ratio after an ask with uiBaseAmount is placed
* /
public simHealthRatioWithSerum3AskUiChanges (
2022-08-31 02:36:44 -07:00
group : Group ,
2022-09-23 02:43:26 -07:00
uiBaseAmount : number ,
2022-08-31 02:36:44 -07:00
externalMarketPk : PublicKey ,
healthType : HealthType = HealthType . init ,
) : number {
2022-09-29 06:51:09 -07:00
const serum3Market =
group . getSerum3MarketByExternalMarket ( externalMarketPk ) ;
const baseBank = group . getFirstBankByTokenIndex (
serum3Market . baseTokenIndex ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-29 06:51:09 -07:00
const quoteBank = group . getFirstBankByTokenIndex (
serum3Market . quoteTokenIndex ,
) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc
2022-09-23 02:43:26 -07:00
. simHealthRatioWithSerum3AskChanges (
2022-09-29 06:51:09 -07:00
baseBank ,
quoteBank ,
2022-09-30 06:07:43 -07:00
toNativeI80F48 (
2022-09-23 02:43:26 -07:00
uiBaseAmount ,
group . getFirstBankByTokenIndex ( serum3Market . baseTokenIndex )
. mintDecimals ,
) ,
serum3Market ,
healthType ,
)
. toNumber ( ) ;
2022-08-31 02:36:44 -07:00
}
/ * *
*
* @param group
2022-09-23 02:43:26 -07:00
* @param perpMarketName
2022-10-07 04:52:04 -07:00
* @returns maximum ui quote which can be traded at oracle price for quote token given current health
2022-08-31 02:36:44 -07:00
* /
2022-09-23 02:43:26 -07:00
public getMaxQuoteForPerpBidUi (
2022-08-31 02:36:44 -07:00
group : Group ,
2022-09-29 06:51:09 -07:00
perpMarketIndex : PerpMarketIndex ,
2022-09-23 02:43:26 -07:00
) : number {
2022-09-29 06:51:09 -07:00
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
2022-10-07 04:52:04 -07:00
const pp = this . getPerpPosition ( perpMarket . perpMarketIndex ) ;
2022-09-29 06:51:09 -07:00
const hc = HealthCache . fromMangoAccount ( group , this ) ;
const baseLots = hc . getMaxPerpForHealthRatio (
2022-09-23 02:43:26 -07:00
perpMarket ,
2022-10-07 04:52:04 -07:00
pp
? pp
: PerpPosition . emptyFromPerpMarketIndex ( perpMarket . perpMarketIndex ) ,
2022-09-23 02:43:26 -07:00
PerpOrderSide . bid ,
2022-09-29 06:51:09 -07:00
I80F48 . fromNumber ( 2 ) ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-30 06:07:43 -07:00
const nativeBase = baseLots . mul ( I80F48 . fromI64 ( perpMarket . baseLotSize ) ) ;
2022-09-29 06:51:09 -07:00
const nativeQuote = nativeBase . mul ( perpMarket . price ) ;
2022-09-30 06:07:43 -07:00
return toUiDecimalsForQuote ( nativeQuote ) ;
2022-08-31 02:36:44 -07:00
}
2022-09-23 02:43:26 -07:00
/ * *
*
* @param group
* @param perpMarketName
* @param uiPrice ui price at which ask would be placed at
* @returns max ui base ask
* /
public getMaxBaseForPerpAskUi (
2022-08-31 02:36:44 -07:00
group : Group ,
2022-09-29 06:51:09 -07:00
perpMarketIndex : PerpMarketIndex ,
2022-08-31 02:36:44 -07:00
) : number {
2022-09-29 06:51:09 -07:00
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
2022-10-07 04:52:04 -07:00
const pp = this . getPerpPosition ( perpMarket . perpMarketIndex ) ;
2022-09-29 06:51:09 -07:00
const hc = HealthCache . fromMangoAccount ( group , this ) ;
const baseLots = hc . getMaxPerpForHealthRatio (
2022-09-23 02:43:26 -07:00
perpMarket ,
2022-10-07 04:52:04 -07:00
pp
? pp
: PerpPosition . emptyFromPerpMarketIndex ( perpMarket . perpMarketIndex ) ,
2022-09-23 02:43:26 -07:00
PerpOrderSide . ask ,
2022-09-29 06:51:09 -07:00
I80F48 . fromNumber ( 2 ) ,
2022-08-31 02:36:44 -07:00
) ;
2022-09-23 02:43:26 -07:00
return perpMarket . baseLotsToUi ( new BN ( baseLots . toString ( ) ) ) ;
2022-07-04 03:29:35 -07:00
}
2022-10-07 04:52:04 -07:00
public simHealthRatioWithPerpBidUiChanges (
group : Group ,
perpMarketIndex : PerpMarketIndex ,
size : number ,
) : number {
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
const pp = this . getPerpPosition ( perpMarket . perpMarketIndex ) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc
. simHealthRatioWithPerpOrderChanges (
perpMarket ,
pp
? pp
: PerpPosition . emptyFromPerpMarketIndex ( perpMarket . perpMarketIndex ) ,
perpMarket . uiBaseToLots ( size ) ,
PerpOrderSide . bid ,
HealthType . init ,
)
. toNumber ( ) ;
}
public simHealthRatioWithPerpAskUiChanges (
group : Group ,
perpMarketIndex : PerpMarketIndex ,
size : number ,
) : number {
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
const pp = this . getPerpPosition ( perpMarket . perpMarketIndex ) ;
const hc = HealthCache . fromMangoAccount ( group , this ) ;
return hc
. simHealthRatioWithPerpOrderChanges (
perpMarket ,
pp
? pp
: PerpPosition . emptyFromPerpMarketIndex ( perpMarket . perpMarketIndex ) ,
perpMarket . uiBaseToLots ( size ) ,
PerpOrderSide . ask ,
HealthType . init ,
)
. toNumber ( ) ;
}
2022-09-20 03:57:01 -07:00
public async loadPerpOpenOrdersForMarket (
client : MangoClient ,
2022-08-31 02:41:12 -07:00
group : Group ,
2022-09-29 06:51:09 -07:00
perpMarketIndex : PerpMarketIndex ,
2022-09-20 03:57:01 -07:00
) : Promise < PerpOrder [ ] > {
2022-09-29 06:51:09 -07:00
const perpMarket = group . getPerpMarketByMarketIndex ( perpMarketIndex ) ;
2022-09-20 03:57:01 -07:00
const [ bids , asks ] = await Promise . all ( [
perpMarket . loadBids ( client ) ,
perpMarket . loadAsks ( client ) ,
] ) ;
return [ . . . Array . from ( bids . items ( ) ) , . . . Array . from ( asks . items ( ) ) ] . filter (
( order ) = > order . owner . equals ( this . publicKey ) ,
) ;
2022-07-04 03:09:33 -07:00
}
2022-06-03 06:34:05 -07:00
toString ( group? : Group ) : string {
2022-07-04 03:29:35 -07:00
let res = 'MangoAccount' ;
res = res + '\n pk: ' + this . publicKey . toString ( ) ;
res = res + '\n name: ' + this . name ;
2022-08-26 01:08:45 -07:00
res = res + '\n owner: ' + this . owner ;
2022-07-05 10:31:47 -07:00
res = res + '\n delegate: ' + this . delegate ;
2022-07-04 03:09:33 -07:00
2022-09-20 03:57:01 -07:00
res =
res +
` \ n max token slots ${ this . tokens . length } , max serum3 slots ${ this . serum3 . length } , max perp slots ${ this . perps . length } , max perp oo slots ${ this . perpOpenOrders . length } ` ;
2022-07-04 03:09:33 -07:00
res =
this . tokensActive ( ) . length > 0
? res +
'\n tokens:' +
JSON . stringify (
2022-08-10 08:17:16 -07:00
this . tokens . map ( ( token , i ) = >
token . isActive ( )
? token . toString ( group , i )
: ` index: ${ i } - empty slot ` ,
) ,
2022-07-04 03:09:33 -07:00
null ,
4 ,
)
: res + '' ;
res =
this . serum3Active ( ) . length > 0
? res + '\n serum:' + JSON . stringify ( this . serum3Active ( ) , null , 4 )
: res + '' ;
res =
this . perpActive ( ) . length > 0
? res + '\n perps:' + JSON . stringify ( this . perpActive ( ) , null , 4 )
: res + '' ;
2022-09-20 03:57:01 -07:00
res =
this . perpOrdersActive ( ) . length > 0
? res +
'\n perps oo:' +
JSON . stringify ( this . perpOrdersActive ( ) , null , 4 )
: res + '' ;
2022-07-04 03:09:33 -07:00
return res ;
2022-05-11 04:33:01 -07:00
}
2022-04-02 23:57:45 -07:00
}
2022-06-22 02:21:02 -07:00
export class TokenPosition {
2022-08-04 10:42:41 -07:00
static TokenIndexUnset = 65535 ;
2022-09-29 06:51:09 -07:00
static from ( dto : TokenPositionDto ) : TokenPosition {
2022-06-22 02:21:02 -07:00
return new TokenPosition (
2022-06-23 06:36:08 -07:00
I80F48 . from ( dto . indexedPosition ) ,
2022-09-29 06:51:09 -07:00
dto . tokenIndex as TokenIndex ,
2022-04-02 23:57:45 -07:00
dto . inUseCount ,
) ;
}
constructor (
2022-06-22 02:21:02 -07:00
public indexedPosition : I80F48 ,
2022-09-29 06:51:09 -07:00
public tokenIndex : TokenIndex ,
2022-04-02 23:57:45 -07:00
public inUseCount : number ,
) { }
2022-05-02 09:26:25 -07:00
2022-06-03 06:34:05 -07:00
public isActive ( ) : boolean {
2022-08-10 01:15:28 -07:00
return this . tokenIndex !== TokenPosition . TokenIndexUnset ;
2022-05-02 09:26:25 -07:00
}
2022-06-03 06:34:05 -07:00
2022-08-23 04:47:08 -07:00
/ * *
*
* @param bank
* @returns native balance
* /
public balance ( bank : Bank ) : I80F48 {
2022-06-22 02:21:02 -07:00
if ( this . indexedPosition . isPos ( ) ) {
return bank . depositIndex . mul ( this . indexedPosition ) ;
2022-06-03 06:34:05 -07:00
} else {
2022-06-22 02:21:02 -07:00
return bank . borrowIndex . mul ( this . indexedPosition ) ;
2022-06-03 06:34:05 -07:00
}
}
2022-08-18 07:19:37 -07:00
/ * *
2022-08-23 04:47:08 -07:00
*
2022-08-18 07:19:37 -07:00
* @param bank
2022-08-23 04:47:08 -07:00
* @returns native deposits , 0 if position has borrows
2022-08-18 07:19:37 -07:00
* /
2022-08-23 04:47:08 -07:00
public deposits ( bank : Bank ) : I80F48 {
2022-09-01 00:48:23 -07:00
if ( this . indexedPosition && this . indexedPosition . lt ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-23 04:47:08 -07:00
}
return this . balance ( bank ) ;
2022-06-03 06:34:05 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
2022-08-23 04:47:08 -07:00
*
2022-08-18 07:19:37 -07:00
* @param bank
2022-08-23 04:47:08 -07:00
* @returns native borrows , 0 if position has deposits
2022-08-18 07:19:37 -07:00
* /
2022-08-23 04:47:08 -07:00
public borrows ( bank : Bank ) : I80F48 {
2022-09-01 00:48:23 -07:00
if ( this . indexedPosition && this . indexedPosition . gt ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-18 07:19:37 -07:00
}
2022-08-23 04:47:08 -07:00
return this . balance ( bank ) . abs ( ) ;
}
2022-08-18 07:19:37 -07:00
2022-08-23 04:47:08 -07:00
/ * *
* @param bank
* @returns UI balance , is signed
* /
public balanceUi ( bank : Bank ) : number {
2022-09-30 06:07:43 -07:00
return toUiDecimals ( this . balance ( bank ) , bank . mintDecimals ) ;
2022-06-29 12:55:39 -07:00
}
2022-08-18 07:19:37 -07:00
/ * *
* @param bank
2022-08-23 04:47:08 -07:00
* @returns UI deposits , 0 if position has borrows
2022-08-18 07:19:37 -07:00
* /
2022-08-23 04:47:08 -07:00
public depositsUi ( bank : Bank ) : number {
2022-09-30 06:07:43 -07:00
return toUiDecimals ( this . deposits ( bank ) , bank . mintDecimals ) ;
2022-08-23 04:47:08 -07:00
}
2022-08-18 07:19:37 -07:00
2022-08-23 04:47:08 -07:00
/ * *
* @param bank
* @returns UI borrows , 0 if position has deposits
* /
public borrowsUi ( bank : Bank ) : number {
2022-09-30 06:07:43 -07:00
return toUiDecimals ( this . borrows ( bank ) , bank . mintDecimals ) ;
2022-06-29 12:55:39 -07:00
}
2022-08-10 08:17:16 -07:00
public toString ( group? : Group , index? : number ) : string {
2022-08-04 10:42:41 -07:00
let extra = '' ;
2022-06-03 06:34:05 -07:00
if ( group ) {
2022-08-17 23:48:45 -07:00
const bank : Bank = group . getFirstBankByTokenIndex ( this . tokenIndex ) ;
2022-06-03 06:34:05 -07:00
if ( bank ) {
2022-08-23 04:47:08 -07:00
const native = this . balance ( bank ) ;
2022-06-03 06:34:05 -07:00
extra += ', native: ' + native . toNumber ( ) ;
2022-08-18 07:19:37 -07:00
extra += ', ui: ' + this . balanceUi ( bank ) ;
2022-06-03 06:34:05 -07:00
extra += ', tokenName: ' + bank . name ;
}
}
return (
2022-08-10 08:17:16 -07:00
( index !== undefined ? 'index: ' + index : '' ) +
', tokenIndex: ' +
2022-06-03 06:34:05 -07:00
this . tokenIndex +
', inUseCount: ' +
this . inUseCount +
', indexedValue: ' +
2022-06-22 02:21:02 -07:00
this . indexedPosition . toNumber ( ) +
2022-06-03 06:34:05 -07:00
extra
) ;
}
2022-04-02 23:57:45 -07:00
}
2022-06-23 07:02:35 -07:00
export class TokenPositionDto {
2022-04-02 23:57:45 -07:00
constructor (
2022-06-23 06:36:08 -07:00
public indexedPosition : I80F48Dto ,
2022-04-02 23:57:45 -07:00
public tokenIndex : number ,
public inUseCount : number ,
public reserved : number [ ] ,
) { }
}
2022-06-22 02:21:02 -07:00
export class Serum3Orders {
2022-04-03 11:08:56 -07:00
static Serum3MarketIndexUnset = 65535 ;
2022-08-10 01:15:28 -07:00
static from ( dto : Serum3PositionDto ) : Serum3Orders {
2022-06-22 02:21:02 -07:00
return new Serum3Orders (
2022-04-03 07:02:14 -07:00
dto . openOrders ,
2022-09-29 06:51:09 -07:00
dto . marketIndex as MarketIndex ,
dto . baseTokenIndex as TokenIndex ,
dto . quoteTokenIndex as TokenIndex ,
2022-04-03 07:02:14 -07:00
) ;
}
constructor (
public openOrders : PublicKey ,
2022-09-29 06:51:09 -07:00
public marketIndex : MarketIndex ,
public baseTokenIndex : TokenIndex ,
public quoteTokenIndex : TokenIndex ,
2022-04-03 07:02:14 -07:00
) { }
2022-07-04 03:09:33 -07:00
public isActive ( ) : boolean {
return this . marketIndex !== Serum3Orders . Serum3MarketIndexUnset ;
}
2022-04-03 07:02:14 -07:00
}
2022-06-23 07:02:35 -07:00
export class Serum3PositionDto {
2022-04-03 07:02:14 -07:00
constructor (
public openOrders : PublicKey ,
public marketIndex : number ,
public baseTokenIndex : number ,
public quoteTokenIndex : number ,
public reserved : number [ ] ,
) { }
}
2022-05-11 04:33:01 -07:00
2022-08-18 04:45:31 -07:00
export class PerpPosition {
2022-05-11 04:33:01 -07:00
static PerpMarketIndexUnset = 65535 ;
2022-09-29 06:51:09 -07:00
static from ( dto : PerpPositionDto ) : PerpPosition {
2022-08-18 04:45:31 -07:00
return new PerpPosition (
2022-09-29 06:51:09 -07:00
dto . marketIndex as PerpMarketIndex ,
2022-09-30 06:07:43 -07:00
dto . basePositionLots ,
2022-09-29 06:51:09 -07:00
I80F48 . from ( dto . quotePositionNative ) ,
2022-09-30 06:07:43 -07:00
dto . bidsBaseLots ,
dto . asksBaseLots ,
dto . takerBaseLots ,
dto . takerQuoteLots ,
2022-09-29 06:51:09 -07:00
I80F48 . from ( dto . longSettledFunding ) ,
I80F48 . from ( dto . shortSettledFunding ) ,
2022-05-11 04:33:01 -07:00
) ;
}
2022-10-07 04:52:04 -07:00
static emptyFromPerpMarketIndex (
perpMarketIndex : PerpMarketIndex ,
) : PerpPosition {
return new PerpPosition (
perpMarketIndex ,
new BN ( 0 ) ,
ZERO_I80F48 ( ) ,
new BN ( 0 ) ,
new BN ( 0 ) ,
new BN ( 0 ) ,
new BN ( 0 ) ,
ZERO_I80F48 ( ) ,
ZERO_I80F48 ( ) ,
) ;
}
2022-05-11 04:33:01 -07:00
constructor (
2022-09-29 06:51:09 -07:00
public marketIndex : PerpMarketIndex ,
2022-09-30 06:07:43 -07:00
public basePositionLots : BN ,
2022-09-29 06:51:09 -07:00
public quotePositionNative : I80F48 ,
2022-09-30 06:07:43 -07:00
public bidsBaseLots : BN ,
public asksBaseLots : BN ,
public takerBaseLots : BN ,
public takerQuoteLots : BN ,
2022-09-29 06:51:09 -07:00
public longSettledFunding : I80F48 ,
public shortSettledFunding : I80F48 ,
2022-05-11 04:33:01 -07:00
) { }
2022-07-04 03:09:33 -07:00
isActive ( ) : boolean {
2022-08-18 04:45:31 -07:00
return this . marketIndex != PerpPosition . PerpMarketIndexUnset ;
2022-07-04 03:09:33 -07:00
}
2022-09-29 06:51:09 -07:00
2022-10-07 04:52:04 -07:00
public getBasePositionUi ( perpMarket : PerpMarket ) : number {
return perpMarket . baseLotsToUi ( this . basePositionLots ) ;
}
public getUnsettledFunding ( perpMarket : PerpMarket ) : I80F48 {
2022-09-30 06:07:43 -07:00
if ( this . basePositionLots . gt ( new BN ( 0 ) ) ) {
2022-09-29 06:51:09 -07:00
return perpMarket . longFunding
. sub ( this . longSettledFunding )
2022-09-30 06:07:43 -07:00
. mul ( I80F48 . fromI64 ( this . basePositionLots ) ) ;
} else if ( this . basePositionLots . lt ( new BN ( 0 ) ) ) {
2022-09-29 06:51:09 -07:00
return perpMarket . shortFunding
. sub ( this . shortSettledFunding )
2022-09-30 06:07:43 -07:00
. mul ( I80F48 . fromI64 ( this . basePositionLots ) ) ;
2022-09-29 06:51:09 -07:00
}
return ZERO_I80F48 ( ) ;
}
public getEquity ( perpMarket : PerpMarket ) : I80F48 {
2022-09-30 06:07:43 -07:00
const lotsToQuote = I80F48 . fromI64 ( perpMarket . baseLotSize ) . mul (
perpMarket . price ,
) ;
2022-09-29 06:51:09 -07:00
2022-09-30 06:07:43 -07:00
const baseLots = I80F48 . fromI64 (
this . basePositionLots . add ( this . takerBaseLots ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-10-07 04:52:04 -07:00
const unsettledFunding = this . getUnsettledFunding ( perpMarket ) ;
2022-09-30 06:07:43 -07:00
const takerQuote = I80F48 . fromI64 (
new BN ( this . takerQuoteLots ) . mul ( perpMarket . quoteLotSize ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-09-30 06:07:43 -07:00
const quoteCurrent = this . quotePositionNative
2022-09-29 06:51:09 -07:00
. sub ( unsettledFunding )
. add ( takerQuote ) ;
return baseLots . mul ( lotsToQuote ) . add ( quoteCurrent ) ;
}
public hasOpenOrders ( ) : boolean {
2022-09-30 06:07:43 -07:00
const zero = new BN ( 0 ) ;
2022-09-29 06:51:09 -07:00
return (
2022-09-30 06:07:43 -07:00
! this . asksBaseLots . eq ( zero ) ||
! this . bidsBaseLots . eq ( zero ) ||
! this . takerBaseLots . eq ( zero ) ||
! this . takerQuoteLots . eq ( zero )
2022-09-29 06:51:09 -07:00
) ;
}
2022-05-11 04:33:01 -07:00
}
2022-06-23 07:02:35 -07:00
export class PerpPositionDto {
2022-05-11 04:33:01 -07:00
constructor (
public marketIndex : number ,
public reserved : [ ] ,
public basePositionLots : BN ,
public quotePositionNative : { val : BN } ,
public bidsBaseLots : BN ,
public asksBaseLots : BN ,
public takerBaseLots : BN ,
public takerQuoteLots : BN ,
2022-09-29 06:51:09 -07:00
public longSettledFunding : I80F48Dto ,
public shortSettledFunding : I80F48Dto ,
2022-05-11 04:33:01 -07:00
) { }
}
2022-07-04 03:29:35 -07:00
2022-09-20 03:57:01 -07:00
export class PerpOo {
static OrderMarketUnset = 65535 ;
2022-09-29 06:51:09 -07:00
static from ( dto : PerpOoDto ) : PerpOo {
2022-09-20 03:57:01 -07:00
return new PerpOo (
dto . orderSide ,
dto . orderMarket ,
2022-09-30 06:07:43 -07:00
dto . clientOrderId ,
2022-09-20 03:57:01 -07:00
dto . orderId ,
) ;
}
constructor (
public orderSide : any ,
public orderMarket : 0 ,
2022-09-30 06:07:43 -07:00
public clientOrderId : BN ,
2022-09-20 03:57:01 -07:00
public orderId : BN ,
) { }
}
export class PerpOoDto {
constructor (
public orderSide : any ,
public orderMarket : 0 ,
public clientOrderId : BN ,
public orderId : BN ,
) { }
}
2022-07-04 03:29:35 -07:00
export class HealthType {
static maint = { maint : { } } ;
static init = { init : { } } ;
}