- fix sort order of open order accounts in buildHealthRemainingAccounts

- refactor buildHealthRemainingAccounts to be more idiomatic
This commit is contained in:
tjs 2023-01-07 17:06:35 -05:00
parent 9efe95cc75
commit 9d168f56bd
2 changed files with 64 additions and 75 deletions

View File

@ -22,11 +22,7 @@ import {
import bs58 from 'bs58'; import bs58 from 'bs58';
import { Bank, MintInfo, TokenIndex } from './accounts/bank'; import { Bank, MintInfo, TokenIndex } from './accounts/bank';
import { Group } from './accounts/group'; import { Group } from './accounts/group';
import { import { MangoAccount } from './accounts/mangoAccount';
MangoAccount,
PerpPosition,
TokenPosition,
} from './accounts/mangoAccount';
import { StubOracle } from './accounts/oracle'; import { StubOracle } from './accounts/oracle';
import { import {
FillEvent, FillEvent,
@ -48,12 +44,7 @@ import { OPENBOOK_PROGRAM_ID } from './constants';
import { Id } from './ids'; import { Id } from './ids';
import { IDL, MangoV4 } from './mango_v4'; import { IDL, MangoV4 } from './mango_v4';
import { I80F48 } from './numbers/I80F48'; import { I80F48 } from './numbers/I80F48';
import { import { FlashLoanType, InterestRateParams, OracleConfigParams } from './types';
AdditionalHealthAccounts,
FlashLoanType,
InterestRateParams,
OracleConfigParams,
} from './types';
import { import {
I64_MAX_BN, I64_MAX_BN,
createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountIdempotentInstruction,
@ -1198,31 +1189,26 @@ export class MangoClient {
externalMarketPk.toBase58(), externalMarketPk.toBase58(),
)!; )!;
let ooPk; let openOrderPk: PublicKey | undefined = undefined;
let additionalAccounts: AdditionalHealthAccounts | undefined = undefined; const banks: Bank[] = [];
let tokenIsInactive; const openOrdersForMarket: [Serum3Market, PublicKey][] = [];
let baseBank;
if (!mangoAccount.getSerum3Account(serum3Market.marketIndex)) { if (!mangoAccount.getSerum3Account(serum3Market.marketIndex)) {
const ix = await this.serum3CreateOpenOrdersIx( const ix = await this.serum3CreateOpenOrdersIx(
group, group,
mangoAccount, mangoAccount,
serum3Market.serumMarketExternal, serum3Market.serumMarketExternal,
); );
ixs.push(ix);
ooPk = await serum3Market.findOoPda( openOrderPk = await serum3Market.findOoPda(
this.program.programId, this.program.programId,
mangoAccount.publicKey, mangoAccount.publicKey,
); );
openOrdersForMarket.push([serum3Market, openOrderPk]);
const tokenIndex = serum3Market.baseTokenIndex; const tokenIndex = serum3Market.baseTokenIndex;
baseBank = group.getFirstBankByTokenIndex(tokenIndex); // only include bank if no deposit has been previously made for same token
if (!mangoAccount.getToken(tokenIndex)?.isActive()) {
// only push bank/oracle if no deposit has been previously made for same token banks.push(group.getFirstBankByTokenIndex(tokenIndex));
tokenIsInactive = !mangoAccount.getToken(tokenIndex)?.isActive(); }
additionalAccounts = {
openOrders: [ooPk],
};
ixs.push(ix);
} }
const healthRemainingAccounts: PublicKey[] = const healthRemainingAccounts: PublicKey[] =
@ -1230,9 +1216,9 @@ export class MangoClient {
AccountRetriever.Fixed, AccountRetriever.Fixed,
group, group,
[mangoAccount], [mangoAccount],
tokenIsInactive ? [baseBank] : [], banks,
[], [],
additionalAccounts, openOrdersForMarket,
); );
const serum3MarketExternal = group.serum3ExternalMarketsMap.get( const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
@ -1280,7 +1266,7 @@ export class MangoClient {
account: mangoAccount.publicKey, account: mangoAccount.publicKey,
owner: (this.program.provider as AnchorProvider).wallet.publicKey, owner: (this.program.provider as AnchorProvider).wallet.publicKey,
openOrders: openOrders:
ooPk || openOrderPk ||
mangoAccount.getSerum3Account(serum3Market.marketIndex)?.openOrders, mangoAccount.getSerum3Account(serum3Market.marketIndex)?.openOrders,
serumMarket: serum3Market.publicKey, serumMarket: serum3Market.publicKey,
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster], serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
@ -2565,9 +2551,9 @@ export class MangoClient {
retriever: AccountRetriever, retriever: AccountRetriever,
group: Group, group: Group,
mangoAccounts: MangoAccount[], mangoAccounts: MangoAccount[],
banks: Bank[], banks: Bank[] = [],
perpMarkets: PerpMarket[], perpMarkets: PerpMarket[] = [],
additionalAccounts?: AdditionalHealthAccounts, openOrdersForMarket: [Serum3Market, PublicKey][] = [],
): PublicKey[] { ): PublicKey[] {
if (retriever === AccountRetriever.Fixed) { if (retriever === AccountRetriever.Fixed) {
return this.buildFixedAccountRetrieverHealthAccounts( return this.buildFixedAccountRetrieverHealthAccounts(
@ -2575,7 +2561,7 @@ export class MangoClient {
mangoAccounts[0], mangoAccounts[0],
banks, banks,
perpMarkets, perpMarkets,
additionalAccounts, openOrdersForMarket,
); );
} else { } else {
return this.buildScanningAccountRetrieverHealthAccounts( return this.buildScanningAccountRetrieverHealthAccounts(
@ -2594,33 +2580,26 @@ export class MangoClient {
// but user would potentially open new positions. // but user would potentially open new positions.
banks: Bank[], banks: Bank[],
perpMarkets: PerpMarket[], perpMarkets: PerpMarket[],
additionalAccounts: AdditionalHealthAccounts = { openOrdersForMarket: [Serum3Market, PublicKey][],
openOrders: [],
},
): PublicKey[] { ): PublicKey[] {
const healthRemainingAccounts: PublicKey[] = []; const healthRemainingAccounts: PublicKey[] = [];
const allTokenIndices = mangoAccount.tokens.map( const tokenPositions = [...mangoAccount.tokens];
(token) => token.tokenIndex, for (const bank of banks) {
); const tokenPositionExists = tokenPositions.find(
(t) => t.tokenIndex === bank.tokenIndex,
if (banks) { );
for (const bank of banks) { if (!tokenPositionExists) {
if (allTokenIndices.indexOf(bank.tokenIndex) < 0) { const inActiveTokenPosition = tokenPositions.find((t) => !t.isActive());
allTokenIndices[ if (inActiveTokenPosition) {
mangoAccount.tokens.findIndex( inActiveTokenPosition.tokenIndex = bank.tokenIndex;
(token, index) =>
!token.isActive() &&
allTokenIndices[index] == TokenPosition.TokenIndexUnset,
)
] = bank.tokenIndex;
} }
} }
} }
const mintInfos = allTokenIndices
.filter((index) => index != TokenPosition.TokenIndexUnset)
.map((tokenIndex) => group.mintInfosMapByTokenIndex.get(tokenIndex)!);
const mintInfos = tokenPositions
.filter((token) => token.isActive())
.map((token) => group.mintInfosMapByTokenIndex.get(token.tokenIndex)!);
healthRemainingAccounts.push( healthRemainingAccounts.push(
...mintInfos.map((mintInfo) => mintInfo.firstBank()), ...mintInfos.map((mintInfo) => mintInfo.firstBank()),
); );
@ -2628,35 +2607,49 @@ export class MangoClient {
...mintInfos.map((mintInfo) => mintInfo.oracle), ...mintInfos.map((mintInfo) => mintInfo.oracle),
); );
const allPerpIndices = mangoAccount.perps.map((perp) => perp.marketIndex);
// insert any extra perp markets in the free perp position slots // insert any extra perp markets in the free perp position slots
if (perpMarkets) { const perpPositions = [...mangoAccount.perps];
for (const perpMarket of perpMarkets) { for (const perpMarket of perpMarkets) {
if (allPerpIndices.indexOf(perpMarket.perpMarketIndex) < 0) { const perpPositionExists = perpPositions.find(
allPerpIndices[ (p) => p.marketIndex === perpMarket.perpMarketIndex,
mangoAccount.perps.findIndex( );
(perp, index) => if (!perpPositionExists) {
!perp.isActive() && const perpPosition = perpPositions.find((perp) => !perp.isActive());
allPerpIndices[index] == PerpPosition.PerpMarketIndexUnset, if (perpPosition) {
) perpPosition.marketIndex = perpMarket.perpMarketIndex;
] = perpMarket.perpMarketIndex;
} }
} }
} }
const allPerpMarkets = allPerpIndices
.filter((index) => index != PerpPosition.PerpMarketIndexUnset) const allPerpMarkets = perpPositions
.map((index) => group.findPerpMarket(index)!); .filter((perp) => perp.isActive())
.map((perp) => group.getPerpMarketByMarketIndex(perp.marketIndex)!);
healthRemainingAccounts.push( healthRemainingAccounts.push(
...allPerpMarkets.map((perp) => perp.publicKey), ...allPerpMarkets.map((perp) => perp.publicKey),
); );
healthRemainingAccounts.push(...allPerpMarkets.map((perp) => perp.oracle)); healthRemainingAccounts.push(...allPerpMarkets.map((perp) => perp.oracle));
// insert any extra open orders accounts in the cooresponding free serum market slot
const serumOpenOrders = [...mangoAccount.serum3];
for (const [serum3Market, openOrderPk] of openOrdersForMarket) {
const ooPositionExists = serumOpenOrders.find(
(oo) => oo.marketIndex === serum3Market.marketIndex,
);
if (!ooPositionExists) {
const serum3Order = serumOpenOrders.find(
(serumOrder) => !serumOrder.isActive(),
);
if (serum3Order) {
serum3Order.marketIndex = serum3Market.marketIndex;
serum3Order.openOrders = openOrderPk;
}
}
}
healthRemainingAccounts.push( healthRemainingAccounts.push(
...mangoAccount.serum3 ...serumOpenOrders
.filter((serum3Account) => serum3Account.marketIndex !== 65535) .filter((serum3Account) => serum3Account.isActive())
.map((serum3Account) => serum3Account.openOrders), .map((serum3Account) => serum3Account.openOrders),
...additionalAccounts.openOrders,
); );
// debugHealthAccounts(group, mangoAccount, healthRemainingAccounts); // debugHealthAccounts(group, mangoAccount, healthRemainingAccounts);

View File

@ -26,7 +26,3 @@ export class OracleConfigParams {
confFilter: number; confFilter: number;
maxStalenessSlots: number | null; maxStalenessSlots: number | null;
} }
export type AdditionalHealthAccounts = {
openOrders: PublicKey[];
};