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-04-08 08:21:49 -07:00
|
|
|
import { PublicKey } from '@solana/web3.js';
|
2022-04-12 21:37:36 -07:00
|
|
|
import { MangoClient } from '../client';
|
2022-06-21 03:38:01 -07:00
|
|
|
import { nativeI80F48ToUi } from '../utils';
|
2022-04-02 23:57:45 -07:00
|
|
|
import { Bank } from './bank';
|
2022-06-03 06:34:05 -07:00
|
|
|
import { Group } from './group';
|
2022-05-02 09:26:25 -07:00
|
|
|
import { I80F48, I80F48Dto, ZERO_I80F48 } from './I80F48';
|
2022-04-02 23:57:45 -07:00
|
|
|
export class MangoAccount {
|
2022-06-22 02:21:02 -07:00
|
|
|
public tokens: TokenPosition[];
|
|
|
|
public serum3: Serum3Orders[];
|
|
|
|
public perps: PerpPositions[];
|
2022-04-12 07:19:58 -07:00
|
|
|
public name: string;
|
2022-04-02 23:57:45 -07:00
|
|
|
|
|
|
|
static from(
|
|
|
|
publicKey: PublicKey,
|
|
|
|
obj: {
|
2022-04-12 07:19:58 -07:00
|
|
|
name: number[];
|
2022-04-02 23:57:45 -07:00
|
|
|
group: PublicKey;
|
|
|
|
owner: PublicKey;
|
|
|
|
delegate: PublicKey;
|
|
|
|
tokens: unknown;
|
|
|
|
serum3: Object;
|
|
|
|
perps: unknown;
|
|
|
|
beingLiquidated: number;
|
|
|
|
isBankrupt: number;
|
|
|
|
accountNum: number;
|
|
|
|
bump: number;
|
|
|
|
reserved: number[];
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
return new MangoAccount(
|
|
|
|
publicKey,
|
2022-04-12 07:19:58 -07:00
|
|
|
obj.name,
|
2022-04-02 23:57:45 -07:00
|
|
|
obj.group,
|
|
|
|
obj.owner,
|
|
|
|
obj.delegate,
|
|
|
|
obj.tokens as { values: TokenAccountDto[] },
|
2022-04-03 07:02:14 -07:00
|
|
|
obj.serum3 as { values: Serum3AccountDto[] },
|
2022-05-11 04:33:01 -07:00
|
|
|
obj.perps as { accounts: PerpAccountDto[] },
|
2022-04-02 23:57:45 -07:00
|
|
|
obj.beingLiquidated,
|
|
|
|
obj.isBankrupt,
|
|
|
|
obj.accountNum,
|
|
|
|
obj.bump,
|
|
|
|
obj.reserved,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public publicKey: PublicKey,
|
2022-04-12 07:19:58 -07:00
|
|
|
name: number[],
|
2022-04-07 09:58:42 -07:00
|
|
|
public group: PublicKey,
|
|
|
|
public owner: PublicKey,
|
|
|
|
public delegate: PublicKey,
|
2022-04-02 23:57:45 -07:00
|
|
|
tokens: { values: TokenAccountDto[] },
|
2022-04-03 07:02:14 -07:00
|
|
|
serum3: { values: Serum3AccountDto[] },
|
2022-05-11 04:33:01 -07:00
|
|
|
perps: { accounts: PerpAccountDto[] },
|
2022-04-02 23:57:45 -07:00
|
|
|
beingLiquidated: number,
|
|
|
|
isBankrupt: number,
|
|
|
|
accountNum: number,
|
|
|
|
bump: number,
|
|
|
|
reserved: number[],
|
|
|
|
) {
|
2022-04-12 07:19:58 -07:00
|
|
|
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
2022-06-22 02:21:02 -07:00
|
|
|
this.tokens = tokens.values.map((dto) => TokenPosition.from(dto));
|
|
|
|
this.serum3 = serum3.values.map((dto) => Serum3Orders.from(dto));
|
|
|
|
this.perps = perps.accounts.map((dto) => PerpPositions.from(dto));
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
|
|
|
|
2022-04-12 21:37:36 -07:00
|
|
|
async reload(client: MangoClient) {
|
|
|
|
Object.assign(this, await client.getMangoAccount(this));
|
|
|
|
}
|
|
|
|
|
2022-06-22 02:21:02 -07:00
|
|
|
findToken(tokenIndex: number): TokenPosition | undefined {
|
2022-04-02 23:57:45 -07:00
|
|
|
return this.tokens.find((ta) => ta.tokenIndex == tokenIndex);
|
|
|
|
}
|
|
|
|
|
2022-06-22 02:21:02 -07:00
|
|
|
findSerum3Account(marketIndex: number): Serum3Orders | undefined {
|
2022-04-08 03:30:21 -07:00
|
|
|
return this.serum3.find((sa) => sa.marketIndex == marketIndex);
|
|
|
|
}
|
|
|
|
|
2022-06-03 06:34:05 -07:00
|
|
|
getNative(bank: Bank): I80F48 {
|
2022-04-03 07:02:14 -07:00
|
|
|
const ta = this.findToken(bank.tokenIndex);
|
2022-06-03 06:34:05 -07:00
|
|
|
return ta ? ta.native(bank) : ZERO_I80F48;
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
|
|
|
|
2022-06-03 06:34:05 -07:00
|
|
|
getUi(bank: Bank): number {
|
2022-04-03 07:02:14 -07:00
|
|
|
const ta = this.findToken(bank.tokenIndex);
|
2022-06-03 06:34:05 -07:00
|
|
|
return ta ? ta.ui(bank) : 0;
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
2022-05-11 04:33:01 -07:00
|
|
|
|
2022-06-22 02:21:02 -07:00
|
|
|
tokens_active(): TokenPosition[] {
|
2022-06-03 06:34:05 -07:00
|
|
|
return this.tokens.filter((token) => token.isActive());
|
|
|
|
}
|
|
|
|
|
|
|
|
toString(group?: Group): string {
|
2022-05-11 04:33:01 -07:00
|
|
|
return (
|
|
|
|
'tokens:' +
|
|
|
|
JSON.stringify(
|
2022-06-03 06:34:05 -07:00
|
|
|
this.tokens
|
2022-06-22 02:21:02 -07:00
|
|
|
.filter((token) => token.tokenIndex != TokenPosition.TokenIndexUnset)
|
2022-06-03 06:34:05 -07:00
|
|
|
.map((token) => token.toString(group)),
|
2022-05-11 04:33:01 -07:00
|
|
|
null,
|
|
|
|
4,
|
|
|
|
) +
|
|
|
|
'\nserum:' +
|
|
|
|
JSON.stringify(
|
|
|
|
this.serum3.filter(
|
|
|
|
(serum3) =>
|
2022-06-22 02:21:02 -07:00
|
|
|
serum3.marketIndex != Serum3Orders.Serum3MarketIndexUnset,
|
2022-05-11 04:33:01 -07:00
|
|
|
),
|
|
|
|
null,
|
|
|
|
4,
|
|
|
|
) +
|
|
|
|
'\nperps:' +
|
|
|
|
JSON.stringify(
|
|
|
|
this.perps.filter(
|
2022-06-22 02:21:02 -07:00
|
|
|
(perp) => perp.marketIndex != PerpPositions.PerpMarketIndexUnset,
|
2022-05-11 04:33:01 -07:00
|
|
|
),
|
|
|
|
null,
|
|
|
|
4,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
|
|
|
|
2022-06-22 02:21:02 -07:00
|
|
|
export class TokenPosition {
|
2022-05-11 04:33:01 -07:00
|
|
|
static TokenIndexUnset: number = 65535;
|
2022-04-02 23:57:45 -07:00
|
|
|
static from(dto: TokenAccountDto) {
|
2022-06-22 02:21:02 -07:00
|
|
|
return new TokenPosition(
|
2022-04-02 23:57:45 -07:00
|
|
|
I80F48.from(dto.indexedValue),
|
|
|
|
dto.tokenIndex,
|
|
|
|
dto.inUseCount,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
2022-06-22 02:21:02 -07:00
|
|
|
public indexedPosition: I80F48,
|
2022-04-02 23:57:45 -07:00
|
|
|
public tokenIndex: number,
|
|
|
|
public inUseCount: number,
|
|
|
|
) {}
|
2022-05-02 09:26:25 -07:00
|
|
|
|
2022-06-03 06:34:05 -07:00
|
|
|
public isActive(): boolean {
|
2022-05-02 09:26:25 -07:00
|
|
|
return this.tokenIndex !== 65535;
|
|
|
|
}
|
2022-06-03 06:34:05 -07:00
|
|
|
|
|
|
|
public native(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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public ui(bank: Bank): number {
|
2022-06-21 03:38:01 -07:00
|
|
|
return nativeI80F48ToUi(this.native(bank), bank.mintDecimals).toNumber();
|
2022-06-03 06:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public toString(group?: Group): String {
|
|
|
|
let extra: string = '';
|
|
|
|
if (group) {
|
|
|
|
let bank = group.findBank(this.tokenIndex);
|
|
|
|
if (bank) {
|
|
|
|
let native = this.native(bank);
|
|
|
|
extra += ', native: ' + native.toNumber();
|
|
|
|
extra += ', ui: ' + this.ui(bank);
|
|
|
|
extra += ', tokenName: ' + bank.name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
'tokenIndex: ' +
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
export class TokenAccountDto {
|
|
|
|
constructor(
|
|
|
|
public indexedValue: I80F48Dto,
|
|
|
|
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-04-03 07:02:14 -07:00
|
|
|
static from(dto: Serum3AccountDto) {
|
2022-06-22 02:21:02 -07:00
|
|
|
return new Serum3Orders(
|
2022-04-03 07:02:14 -07:00
|
|
|
dto.openOrders,
|
|
|
|
dto.marketIndex,
|
|
|
|
dto.baseTokenIndex,
|
|
|
|
dto.quoteTokenIndex,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public openOrders: PublicKey,
|
|
|
|
public marketIndex: number,
|
|
|
|
public baseTokenIndex: number,
|
|
|
|
public quoteTokenIndex: number,
|
|
|
|
) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Serum3AccountDto {
|
|
|
|
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-06-22 02:21:02 -07:00
|
|
|
export class PerpPositions {
|
2022-05-11 04:33:01 -07:00
|
|
|
static PerpMarketIndexUnset = 65535;
|
|
|
|
static from(dto: PerpAccountDto) {
|
2022-06-22 02:21:02 -07:00
|
|
|
return new PerpPositions(
|
2022-05-11 04:33:01 -07:00
|
|
|
dto.marketIndex,
|
|
|
|
dto.basePositionLots.toNumber(),
|
|
|
|
dto.quotePositionNative.val.toNumber(),
|
|
|
|
dto.bidsBaseLots.toNumber(),
|
|
|
|
dto.asksBaseLots.toNumber(),
|
|
|
|
dto.takerBaseLots.toNumber(),
|
|
|
|
dto.takerQuoteLots.toNumber(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public marketIndex: number,
|
|
|
|
public basePositionLots: number,
|
|
|
|
public quotePositionNative: number,
|
|
|
|
public bidsBaseLots: number,
|
|
|
|
public asksBaseLots: number,
|
|
|
|
public takerBaseLots: number,
|
|
|
|
public takerQuoteLots: number,
|
|
|
|
) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PerpAccountDto {
|
|
|
|
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,
|
|
|
|
) {}
|
|
|
|
}
|