combine open orders create with place order (#362)

* wip

* place order fix

* fixes

* comment

* cleanup

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
Co-authored-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
Adrian Brzeziński 2023-01-05 08:30:15 +01:00 committed by GitHub
parent 5b41ff89a0
commit 068fad98f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 33 deletions

View File

@ -56,6 +56,22 @@ export class Serum3Market {
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0]; this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
} }
public async findOoPda(
programId: PublicKey,
mangoAccount: PublicKey,
): Promise<PublicKey> {
const [openOrderPublicKey] = await PublicKey.findProgramAddress(
[
Buffer.from('Serum3OO'),
mangoAccount.toBuffer(),
this.publicKey.toBuffer(),
],
programId,
);
return openOrderPublicKey;
}
public getFeeRates(taker = true): number { public getFeeRates(taker = true): number {
// See https://github.com/openbook-dex/program/blob/master/dex/src/fees.rs#L81 // See https://github.com/openbook-dex/program/blob/master/dex/src/fees.rs#L81
const ratesBps = const ratesBps =

View File

@ -48,7 +48,12 @@ 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 { FlashLoanType, InterestRateParams, OracleConfigParams } from './types'; import {
AdditionalHealthAccounts,
FlashLoanType,
InterestRateParams,
OracleConfigParams,
} from './types';
import { import {
I64_MAX_BN, I64_MAX_BN,
createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountIdempotentInstruction,
@ -1102,11 +1107,36 @@ export class MangoClient {
.rpc(); .rpc();
} }
public async serum3CloseOpenOrders( public async serum3CreateOpenOrdersIx(
group: Group, group: Group,
mangoAccount: MangoAccount, mangoAccount: MangoAccount,
externalMarketPk: PublicKey, externalMarketPk: PublicKey,
): Promise<TransactionSignature> { ): Promise<TransactionInstruction> {
const serum3Market: Serum3Market = group.serum3MarketsMapByExternal.get(
externalMarketPk.toBase58(),
)!;
const ix = await this.program.methods
.serum3CreateOpenOrders()
.accounts({
group: group.publicKey,
account: mangoAccount.publicKey,
serumMarket: serum3Market.publicKey,
serumProgram: serum3Market.serumProgram,
serumMarketExternal: serum3Market.serumMarketExternal,
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
})
.instruction();
return ix;
}
public async serum3CloseOpenOrdersIx(
group: Group,
mangoAccount: MangoAccount,
externalMarketPk: PublicKey,
): Promise<TransactionInstruction> {
const serum3Market = group.serum3MarketsMapByExternal.get( const serum3Market = group.serum3MarketsMapByExternal.get(
externalMarketPk.toBase58(), externalMarketPk.toBase58(),
)!; )!;
@ -1127,7 +1157,28 @@ export class MangoClient {
solDestination: (this.program.provider as AnchorProvider).wallet solDestination: (this.program.provider as AnchorProvider).wallet
.publicKey, .publicKey,
}) })
.rpc(); .instruction();
}
public async serum3CloseOpenOrders(
group: Group,
mangoAccount: MangoAccount,
externalMarketPk: PublicKey,
): Promise<TransactionSignature> {
const ix = await this.serum3CloseOpenOrdersIx(
group,
mangoAccount,
externalMarketPk,
);
return await sendTransaction(
this.program.provider as AnchorProvider,
[ix],
group.addressLookupTablesList,
{
postSendTxCallback: this.postSendTxCallback,
},
);
} }
public async serum3PlaceOrderIx( public async serum3PlaceOrderIx(
@ -1141,27 +1192,44 @@ export class MangoClient {
orderType: Serum3OrderType, orderType: Serum3OrderType,
clientOrderId: number, clientOrderId: number,
limit: number, limit: number,
): Promise<TransactionInstruction> { ): Promise<TransactionInstruction[]> {
const ixs: TransactionInstruction[] = [];
const serum3Market = group.serum3MarketsMapByExternal.get( const serum3Market = group.serum3MarketsMapByExternal.get(
externalMarketPk.toBase58(), externalMarketPk.toBase58(),
)!; )!;
let ooPk;
let additionalAccounts: AdditionalHealthAccounts | undefined = undefined;
if (!mangoAccount.getSerum3Account(serum3Market.marketIndex)) { if (!mangoAccount.getSerum3Account(serum3Market.marketIndex)) {
await this.serum3CreateOpenOrders( const ix = await this.serum3CreateOpenOrdersIx(
group, group,
mangoAccount, mangoAccount,
serum3Market.serumMarketExternal, serum3Market.serumMarketExternal,
); );
await mangoAccount.reload(this);
} ooPk = await serum3Market.findOoPda(
const serum3MarketExternal = group.serum3ExternalMarketsMap.get( this.program.programId,
externalMarketPk.toBase58(), mangoAccount.publicKey,
)!;
const serum3MarketExternalVaultSigner =
await generateSerum3MarketExternalVaultSignerAddress(
this.cluster,
serum3Market,
serum3MarketExternal,
); );
const tokenIndex =
serum3Market[
side == Serum3Side.bid ? 'baseTokenIndex' : 'quoteTokenIndex'
];
const baseBank = group.getFirstBankByTokenIndex(tokenIndex);
// only push bank/oracle if no deposit has been previously made for same token
const hasBaseBank =
mangoAccount.tokens[tokenIndex].tokenIndex !==
TokenPosition.TokenIndexUnset;
additionalAccounts = {
banks: !hasBaseBank ? [baseBank.publicKey] : [],
oracles: !hasBaseBank ? [baseBank.oracle] : [],
openOrders: [ooPk],
perps: [],
};
ixs.push(ix);
}
const healthRemainingAccounts: PublicKey[] = const healthRemainingAccounts: PublicKey[] =
this.buildHealthRemainingAccounts( this.buildHealthRemainingAccounts(
@ -1170,6 +1238,17 @@ export class MangoClient {
[mangoAccount], [mangoAccount],
[], [],
[], [],
additionalAccounts,
);
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
externalMarketPk.toBase58(),
)!;
const serum3MarketExternalVaultSigner =
await generateSerum3MarketExternalVaultSignerAddress(
this.cluster,
serum3Market,
serum3MarketExternal,
); );
const limitPrice = serum3MarketExternal.priceNumberToLots(price); const limitPrice = serum3MarketExternal.priceNumberToLots(price);
@ -1191,7 +1270,6 @@ export class MangoClient {
})(); })();
const payerBank = group.getFirstBankByTokenIndex(payerTokenIndex); const payerBank = group.getFirstBankByTokenIndex(payerTokenIndex);
const ix = await this.program.methods const ix = await this.program.methods
.serum3PlaceOrder( .serum3PlaceOrder(
side, side,
@ -1207,8 +1285,9 @@ export class MangoClient {
group: group.publicKey, group: group.publicKey,
account: mangoAccount.publicKey, account: mangoAccount.publicKey,
owner: (this.program.provider as AnchorProvider).wallet.publicKey, owner: (this.program.provider as AnchorProvider).wallet.publicKey,
openOrders: mangoAccount.getSerum3Account(serum3Market.marketIndex) openOrders:
?.openOrders, ooPk ||
mangoAccount.getSerum3Account(serum3Market.marketIndex)?.openOrders,
serumMarket: serum3Market.publicKey, serumMarket: serum3Market.publicKey,
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster], serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
serumMarketExternal: serum3Market.serumMarketExternal, serumMarketExternal: serum3Market.serumMarketExternal,
@ -1231,7 +1310,9 @@ export class MangoClient {
) )
.instruction(); .instruction();
return ix; ixs.push(ix);
return ixs;
} }
public async serum3PlaceOrder( public async serum3PlaceOrder(
@ -1246,7 +1327,7 @@ export class MangoClient {
clientOrderId: number, clientOrderId: number,
limit: number, limit: number,
): Promise<TransactionSignature> { ): Promise<TransactionSignature> {
const ix = await this.serum3PlaceOrderIx( const placeOrderIxes = await this.serum3PlaceOrderIx(
group, group,
mangoAccount, mangoAccount,
externalMarketPk, externalMarketPk,
@ -1258,15 +1339,14 @@ export class MangoClient {
clientOrderId, clientOrderId,
limit, limit,
); );
const settleIx = await this.serum3SettleFundsIx(
const ix2 = await this.serum3SettleFundsIx(
group, group,
mangoAccount, mangoAccount,
externalMarketPk, externalMarketPk,
); );
return await this.sendAndConfirmTransaction( return await this.sendAndConfirmTransaction(
[ix, ix2], [...placeOrderIxes, settleIx],
group.addressLookupTablesList, group.addressLookupTablesList,
); );
} }
@ -1319,12 +1399,16 @@ export class MangoClient {
const serum3MarketExternal = group.serum3ExternalMarketsMap.get( const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
externalMarketPk.toBase58(), externalMarketPk.toBase58(),
)!; )!;
const serum3MarketExternalVaultSigner =
await generateSerum3MarketExternalVaultSignerAddress( const [serum3MarketExternalVaultSigner, openOrderPublicKey] =
this.cluster, await Promise.all([
serum3Market, generateSerum3MarketExternalVaultSignerAddress(
serum3MarketExternal, this.cluster,
); serum3Market,
serum3MarketExternal,
),
serum3Market.findOoPda(this.program.programId, mangoAccount.publicKey),
]);
const ix = await this.program.methods const ix = await this.program.methods
.serum3SettleFunds() .serum3SettleFunds()
@ -1332,8 +1416,7 @@ export class MangoClient {
group: group.publicKey, group: group.publicKey,
account: mangoAccount.publicKey, account: mangoAccount.publicKey,
owner: (this.program.provider as AnchorProvider).wallet.publicKey, owner: (this.program.provider as AnchorProvider).wallet.publicKey,
openOrders: mangoAccount.getSerum3Account(serum3Market.marketIndex) openOrders: openOrderPublicKey,
?.openOrders,
serumMarket: serum3Market.publicKey, serumMarket: serum3Market.publicKey,
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster], serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
serumMarketExternal: serum3Market.serumMarketExternal, serumMarketExternal: serum3Market.serumMarketExternal,
@ -2490,6 +2573,7 @@ export class MangoClient {
mangoAccounts: MangoAccount[], mangoAccounts: MangoAccount[],
banks: Bank[], banks: Bank[],
perpMarkets: PerpMarket[], perpMarkets: PerpMarket[],
additionalAccounts?: AdditionalHealthAccounts,
): PublicKey[] { ): PublicKey[] {
if (retriever === AccountRetriever.Fixed) { if (retriever === AccountRetriever.Fixed) {
return this.buildFixedAccountRetrieverHealthAccounts( return this.buildFixedAccountRetrieverHealthAccounts(
@ -2497,6 +2581,7 @@ export class MangoClient {
mangoAccounts[0], mangoAccounts[0],
banks, banks,
perpMarkets, perpMarkets,
additionalAccounts,
); );
} else { } else {
return this.buildScanningAccountRetrieverHealthAccounts( return this.buildScanningAccountRetrieverHealthAccounts(
@ -2515,6 +2600,12 @@ 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 = {
oracles: [],
banks: [],
perps: [],
openOrders: [],
},
): PublicKey[] { ): PublicKey[] {
const healthRemainingAccounts: PublicKey[] = []; const healthRemainingAccounts: PublicKey[] = [];
@ -2541,9 +2632,11 @@ export class MangoClient {
healthRemainingAccounts.push( healthRemainingAccounts.push(
...mintInfos.map((mintInfo) => mintInfo.firstBank()), ...mintInfos.map((mintInfo) => mintInfo.firstBank()),
...additionalAccounts.banks,
); );
healthRemainingAccounts.push( healthRemainingAccounts.push(
...mintInfos.map((mintInfo) => mintInfo.oracle), ...mintInfos.map((mintInfo) => mintInfo.oracle),
...additionalAccounts.oracles,
); );
const allPerpIndices = mangoAccount.perps.map((perp) => perp.marketIndex); const allPerpIndices = mangoAccount.perps.map((perp) => perp.marketIndex);
@ -2567,6 +2660,7 @@ export class MangoClient {
.map((index) => group.findPerpMarket(index)!); .map((index) => group.findPerpMarket(index)!);
healthRemainingAccounts.push( healthRemainingAccounts.push(
...allPerpMarkets.map((perp) => perp.publicKey), ...allPerpMarkets.map((perp) => perp.publicKey),
...additionalAccounts.perps,
); );
healthRemainingAccounts.push(...allPerpMarkets.map((perp) => perp.oracle)); healthRemainingAccounts.push(...allPerpMarkets.map((perp) => perp.oracle));
@ -2574,6 +2668,7 @@ export class MangoClient {
...mangoAccount.serum3 ...mangoAccount.serum3
.filter((serum3Account) => serum3Account.marketIndex !== 65535) .filter((serum3Account) => serum3Account.marketIndex !== 65535)
.map((serum3Account) => serum3Account.openOrders), .map((serum3Account) => serum3Account.openOrders),
...additionalAccounts.openOrders,
); );
// debugHealthAccounts(group, mangoAccount, healthRemainingAccounts); // debugHealthAccounts(group, mangoAccount, healthRemainingAccounts);
@ -2720,7 +2815,7 @@ export class MangoClient {
limit, limit,
), ),
]); ]);
transactionInstructions.push(cancelOrderIx, settleIx, placeOrderIx); transactionInstructions.push(cancelOrderIx, settleIx, ...placeOrderIx);
return await this.sendAndConfirmTransaction( return await this.sendAndConfirmTransaction(
transactionInstructions, transactionInstructions,

View File

@ -1,4 +1,5 @@
import { BN } from '@project-serum/anchor'; import { BN } from '@project-serum/anchor';
import { PublicKey } from '@solana/web3.js';
export type Modify<T, R> = Omit<T, keyof R> & R; export type Modify<T, R> = Omit<T, keyof R> & R;
@ -25,3 +26,10 @@ export class OracleConfigParams {
confFilter: number; confFilter: number;
maxStalenessSlots: number | null; maxStalenessSlots: number | null;
} }
export type AdditionalHealthAccounts = {
banks: PublicKey[];
oracles: PublicKey[];
perps: PublicKey[];
openOrders: PublicKey[];
};