2023-06-15 23:04:58 -07:00
|
|
|
import {
|
|
|
|
AnchorProvider,
|
|
|
|
BN,
|
|
|
|
Program,
|
|
|
|
Provider,
|
|
|
|
Wallet,
|
|
|
|
} from '@coral-xyz/anchor';
|
2023-07-03 05:09:11 -07:00
|
|
|
import * as borsh from '@coral-xyz/borsh';
|
2023-07-04 00:26:59 -07:00
|
|
|
import { OpenOrders } from '@project-serum/serum';
|
2023-02-28 03:05:02 -08:00
|
|
|
import {
|
|
|
|
createCloseAccountInstruction,
|
|
|
|
createInitializeAccount3Instruction,
|
|
|
|
} from '@solana/spl-token';
|
2022-04-07 09:58:42 -07:00
|
|
|
import {
|
2023-07-04 00:26:59 -07:00
|
|
|
AccountInfo,
|
2022-04-07 12:00:08 -07:00
|
|
|
AccountMeta,
|
2022-12-15 14:22:10 -08:00
|
|
|
AddressLookupTableAccount,
|
2022-06-11 04:49:45 -07:00
|
|
|
Cluster,
|
2023-02-06 02:59:12 -08:00
|
|
|
Commitment,
|
2023-04-13 03:44:12 -07:00
|
|
|
Connection,
|
2022-05-11 04:33:01 -07:00
|
|
|
Keypair,
|
2022-04-08 03:30:21 -07:00
|
|
|
MemcmpFilter,
|
2022-04-07 12:00:08 -07:00
|
|
|
PublicKey,
|
2022-07-06 21:45:01 -07:00
|
|
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
2022-04-07 12:00:08 -07:00
|
|
|
SYSVAR_RENT_PUBKEY,
|
2022-12-19 06:40:14 -08:00
|
|
|
SystemProgram,
|
2022-05-25 17:29:06 -07:00
|
|
|
TransactionInstruction,
|
2022-06-01 01:10:43 -07:00
|
|
|
TransactionSignature,
|
2023-08-09 14:13:55 -07:00
|
|
|
RecentPrioritizationFees,
|
2022-04-07 12:00:08 -07:00
|
|
|
} from '@solana/web3.js';
|
2022-04-07 23:29:35 -07:00
|
|
|
import bs58 from 'bs58';
|
2023-07-04 00:26:59 -07:00
|
|
|
import chunk from 'lodash/chunk';
|
2023-04-24 05:48:53 -07:00
|
|
|
import cloneDeep from 'lodash/cloneDeep';
|
2023-08-11 06:18:41 -07:00
|
|
|
import groupBy from 'lodash/groupBy';
|
|
|
|
import mapValues from 'lodash/mapValues';
|
|
|
|
import maxBy from 'lodash/maxBy';
|
2023-04-24 05:48:53 -07:00
|
|
|
import uniq from 'lodash/uniq';
|
2022-09-29 06:51:09 -07:00
|
|
|
import { Bank, MintInfo, TokenIndex } from './accounts/bank';
|
2022-04-12 08:28:47 -07:00
|
|
|
import { Group } from './accounts/group';
|
2023-01-10 07:51:03 -08:00
|
|
|
import {
|
|
|
|
MangoAccount,
|
|
|
|
PerpPosition,
|
|
|
|
Serum3Orders,
|
2023-08-03 03:37:01 -07:00
|
|
|
TokenConditionalSwapDisplayPriceStyle,
|
2023-07-03 05:09:11 -07:00
|
|
|
TokenConditionalSwapDto,
|
2023-08-08 09:16:59 -07:00
|
|
|
TokenConditionalSwapIntention,
|
2023-01-10 07:51:03 -08:00
|
|
|
TokenPosition,
|
|
|
|
} from './accounts/mangoAccount';
|
2022-04-12 08:28:47 -07:00
|
|
|
import { StubOracle } from './accounts/oracle';
|
2022-09-15 00:57:48 -07:00
|
|
|
import {
|
2022-09-27 06:13:53 -07:00
|
|
|
FillEvent,
|
|
|
|
OutEvent,
|
2022-09-15 00:57:48 -07:00
|
|
|
PerpEventQueue,
|
|
|
|
PerpMarket,
|
2022-09-29 06:51:09 -07:00
|
|
|
PerpMarketIndex,
|
2022-09-15 00:57:48 -07:00
|
|
|
PerpOrderSide,
|
2022-09-27 06:13:53 -07:00
|
|
|
PerpOrderType,
|
2023-06-15 23:05:16 -07:00
|
|
|
PerpSelfTradeBehavior,
|
2022-09-15 00:57:48 -07:00
|
|
|
} from './accounts/perp';
|
2022-04-08 03:30:21 -07:00
|
|
|
import {
|
2023-04-24 05:48:53 -07:00
|
|
|
MarketIndex,
|
2022-04-08 03:30:21 -07:00
|
|
|
Serum3Market,
|
|
|
|
Serum3OrderType,
|
|
|
|
Serum3SelfTradeBehavior,
|
|
|
|
Serum3Side,
|
2022-12-19 06:40:14 -08:00
|
|
|
generateSerum3MarketExternalVaultSignerAddress,
|
2022-04-12 08:28:47 -07:00
|
|
|
} from './accounts/serum3';
|
2023-01-26 11:27:39 -08:00
|
|
|
import {
|
|
|
|
IxGateParams,
|
|
|
|
PerpEditParams,
|
|
|
|
TokenEditParams,
|
|
|
|
buildIxGate,
|
2023-08-09 04:55:53 -07:00
|
|
|
TokenRegisterParams,
|
2023-01-26 11:27:39 -08:00
|
|
|
} from './clientIxParamBuilder';
|
2023-08-09 14:13:55 -07:00
|
|
|
import {
|
|
|
|
MANGO_V4_ID,
|
|
|
|
MAX_RECENT_PRIORITY_FEE_ACCOUNTS,
|
|
|
|
OPENBOOK_PROGRAM_ID,
|
|
|
|
RUST_U64_MAX,
|
|
|
|
} from './constants';
|
2022-06-21 11:04:21 -07:00
|
|
|
import { Id } from './ids';
|
2022-06-01 01:10:43 -07:00
|
|
|
import { IDL, MangoV4 } from './mango_v4';
|
2022-09-30 06:07:43 -07:00
|
|
|
import { I80F48 } from './numbers/I80F48';
|
2023-01-07 14:06:35 -08:00
|
|
|
import { FlashLoanType, InterestRateParams, OracleConfigParams } from './types';
|
2022-06-02 10:30:39 -07:00
|
|
|
import {
|
2022-12-19 06:40:14 -08:00
|
|
|
I64_MAX_BN,
|
2023-01-19 02:31:54 -08:00
|
|
|
U64_MAX_BN,
|
2022-08-09 15:27:24 -07:00
|
|
|
createAssociatedTokenAccountIdempotentInstruction,
|
2022-06-02 10:30:39 -07:00
|
|
|
getAssociatedTokenAddress,
|
2022-09-30 06:07:43 -07:00
|
|
|
toNative,
|
2023-08-07 04:09:19 -07:00
|
|
|
toNativeSellPerBuyTokenPrice,
|
2022-06-02 10:30:39 -07:00
|
|
|
} from './utils';
|
2023-08-11 10:12:13 -07:00
|
|
|
import { MangoSignatureStatus, sendTransaction } from './utils/rpc';
|
2023-02-28 03:05:02 -08:00
|
|
|
import { NATIVE_MINT, TOKEN_PROGRAM_ID } from './utils/spl';
|
2022-02-23 02:09:17 -08:00
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
export const DEFAULT_TOKEN_CONDITIONAL_SWAP_COUNT = 8;
|
|
|
|
|
2023-01-13 02:23:37 -08:00
|
|
|
export enum AccountRetriever {
|
2022-08-03 09:05:16 -07:00
|
|
|
Scanning,
|
|
|
|
Fixed,
|
|
|
|
}
|
|
|
|
|
2022-08-17 23:48:45 -07:00
|
|
|
export type IdsSource = 'api' | 'static' | 'get-program-accounts';
|
|
|
|
|
2022-11-04 07:35:40 -07:00
|
|
|
export type MangoClientOptions = {
|
|
|
|
idsSource?: IdsSource;
|
|
|
|
postSendTxCallback?: ({ txid }: { txid: string }) => void;
|
|
|
|
prioritizationFee?: number;
|
2023-08-09 14:13:55 -07:00
|
|
|
estimateFee?: boolean;
|
2023-02-03 04:51:18 -08:00
|
|
|
txConfirmationCommitment?: Commitment;
|
2023-03-03 05:04:45 -08:00
|
|
|
openbookFeesToDao?: boolean;
|
2023-09-04 09:06:37 -07:00
|
|
|
prependedGlobalAdditionalInstructions?: TransactionInstruction[];
|
2022-11-04 07:35:40 -07:00
|
|
|
};
|
|
|
|
|
2022-02-23 02:09:17 -08:00
|
|
|
export class MangoClient {
|
2022-11-04 07:35:40 -07:00
|
|
|
private idsSource: IdsSource;
|
2022-09-13 22:44:00 -07:00
|
|
|
private postSendTxCallback?: ({ txid }) => void;
|
2022-08-12 17:15:12 -07:00
|
|
|
private prioritizationFee: number;
|
2023-08-09 14:13:55 -07:00
|
|
|
private estimateFee: boolean;
|
2023-02-03 04:51:18 -08:00
|
|
|
private txConfirmationCommitment: Commitment;
|
2023-03-03 05:04:45 -08:00
|
|
|
private openbookFeesToDao: boolean;
|
2023-08-31 05:36:04 -07:00
|
|
|
private prependedGlobalAdditionalInstructions: TransactionInstruction[] = [];
|
2022-08-12 17:15:12 -07:00
|
|
|
|
2022-06-11 04:49:45 -07:00
|
|
|
constructor(
|
|
|
|
public program: Program<MangoV4>,
|
|
|
|
public programId: PublicKey,
|
|
|
|
public cluster: Cluster,
|
2022-11-04 07:35:40 -07:00
|
|
|
public opts: MangoClientOptions = {},
|
2022-07-17 04:26:57 -07:00
|
|
|
) {
|
2022-12-08 14:23:07 -08:00
|
|
|
this.idsSource = opts?.idsSource || 'get-program-accounts';
|
2022-08-12 17:15:12 -07:00
|
|
|
this.prioritizationFee = opts?.prioritizationFee || 0;
|
2023-08-09 14:13:55 -07:00
|
|
|
this.estimateFee = opts?.estimateFee || false;
|
2022-08-12 17:15:12 -07:00
|
|
|
this.postSendTxCallback = opts?.postSendTxCallback;
|
2023-03-03 05:04:45 -08:00
|
|
|
this.openbookFeesToDao = opts?.openbookFeesToDao ?? true;
|
2023-09-04 09:06:37 -07:00
|
|
|
this.prependedGlobalAdditionalInstructions =
|
|
|
|
opts.prependedGlobalAdditionalInstructions ?? [];
|
2023-02-03 04:51:18 -08:00
|
|
|
this.txConfirmationCommitment =
|
|
|
|
opts?.txConfirmationCommitment ??
|
|
|
|
(program.provider as AnchorProvider).opts.commitment ??
|
|
|
|
'processed';
|
2022-07-17 04:26:57 -07:00
|
|
|
// TODO: evil side effect, but limited backtraces are a nightmare
|
|
|
|
Error.stackTraceLimit = 1000;
|
|
|
|
}
|
2022-02-23 02:09:17 -08:00
|
|
|
|
2023-03-21 15:01:15 -07:00
|
|
|
/// Convenience accessors
|
|
|
|
public get connection(): Connection {
|
|
|
|
return this.program.provider.connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
public get walletPk(): PublicKey {
|
|
|
|
return (this.program.provider as AnchorProvider).wallet.publicKey;
|
|
|
|
}
|
|
|
|
|
2023-01-02 12:35:39 -08:00
|
|
|
/// Transactions
|
2023-02-03 04:55:46 -08:00
|
|
|
public async sendAndConfirmTransaction(
|
2023-01-02 12:35:39 -08:00
|
|
|
ixs: TransactionInstruction[],
|
|
|
|
opts: any = {},
|
2023-08-31 05:36:04 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-09 14:13:55 -07:00
|
|
|
let prioritizationFee: number;
|
|
|
|
if (opts.prioritizationFee) {
|
|
|
|
prioritizationFee = opts.prioritizationFee;
|
|
|
|
} else if (this.estimateFee) {
|
|
|
|
prioritizationFee = await this.estimatePrioritizationFee(ixs);
|
|
|
|
} else {
|
|
|
|
prioritizationFee = this.prioritizationFee;
|
|
|
|
}
|
2023-08-31 05:36:04 -07:00
|
|
|
const status = await sendTransaction(
|
|
|
|
this.program.provider as AnchorProvider,
|
2023-09-04 09:06:37 -07:00
|
|
|
[...this.prependedGlobalAdditionalInstructions, ...ixs],
|
2023-02-03 04:55:46 -08:00
|
|
|
opts.alts ?? [],
|
2023-01-02 12:35:39 -08:00
|
|
|
{
|
|
|
|
postSendTxCallback: this.postSendTxCallback,
|
2023-08-09 14:13:55 -07:00
|
|
|
prioritizationFee,
|
2023-02-03 04:51:18 -08:00
|
|
|
txConfirmationCommitment: this.txConfirmationCommitment,
|
2023-01-02 12:35:39 -08:00
|
|
|
...opts,
|
|
|
|
},
|
|
|
|
);
|
2023-08-31 05:36:04 -07:00
|
|
|
return status;
|
2023-01-02 12:35:39 -08:00
|
|
|
}
|
2022-04-07 09:58:42 -07:00
|
|
|
|
2023-08-11 13:06:21 -07:00
|
|
|
public async sendAndConfirmTransactionForGroup(
|
2023-02-03 04:55:46 -08:00
|
|
|
group: Group,
|
|
|
|
ixs: TransactionInstruction[],
|
|
|
|
opts: any = {},
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransaction(ixs, {
|
|
|
|
alts: group.addressLookupTablesList,
|
|
|
|
...opts,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-21 04:45:07 -07:00
|
|
|
public async adminTokenWithdrawFees(
|
|
|
|
group: Group,
|
|
|
|
bank: Bank,
|
|
|
|
tokenAccountPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-21 04:45:07 -07:00
|
|
|
const admin = (this.program.provider as AnchorProvider).wallet.publicKey;
|
|
|
|
const ix = await this.program.methods
|
2023-08-10 04:32:06 -07:00
|
|
|
.adminTokenWithdrawFees()
|
2023-07-21 04:45:07 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
bank: bank.publicKey,
|
|
|
|
vault: bank.vault,
|
|
|
|
tokenAccount: tokenAccountPk,
|
|
|
|
admin,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransaction([ix]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async adminPerpWithdrawFees(
|
|
|
|
group: Group,
|
|
|
|
perpMarket: PerpMarket,
|
|
|
|
tokenAccountPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-21 04:45:07 -07:00
|
|
|
const bank = group.getFirstBankByTokenIndex(perpMarket.settleTokenIndex);
|
|
|
|
const admin = (this.program.provider as AnchorProvider).wallet.publicKey;
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.adminPerpWithdrawFees()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bank: bank.publicKey,
|
|
|
|
vault: bank.vault,
|
|
|
|
tokenAccount: tokenAccountPk,
|
|
|
|
admin,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransaction([ix]);
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:42:00 -07:00
|
|
|
// Group
|
2022-07-06 05:51:15 -07:00
|
|
|
public async groupCreate(
|
2022-06-09 09:27:31 -07:00
|
|
|
groupNum: number,
|
|
|
|
testing: boolean,
|
2022-08-01 03:56:29 -07:00
|
|
|
version: number,
|
2022-07-06 00:56:14 -07:00
|
|
|
insuranceMintPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-05-18 08:16:14 -07:00
|
|
|
const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey;
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-08-01 03:56:29 -07:00
|
|
|
.groupCreate(groupNum, testing ? 1 : 0, version)
|
2022-04-07 12:00:08 -07:00
|
|
|
.accounts({
|
2022-07-31 00:36:46 -07:00
|
|
|
creator: adminPk,
|
2022-04-07 12:00:08 -07:00
|
|
|
payer: adminPk,
|
2022-07-06 00:56:14 -07:00
|
|
|
insuranceMint: insuranceMintPk,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransaction([ix]);
|
2022-04-07 10:42:00 -07:00
|
|
|
}
|
|
|
|
|
2022-08-03 01:25:09 -07:00
|
|
|
public async groupEdit(
|
|
|
|
group: Group,
|
2022-11-21 10:34:41 -08:00
|
|
|
admin?: PublicKey,
|
|
|
|
fastListingAdmin?: PublicKey,
|
2023-01-12 00:12:55 -08:00
|
|
|
securityAdmin?: PublicKey,
|
2022-11-21 10:34:41 -08:00
|
|
|
testing?: number,
|
|
|
|
version?: number,
|
2023-01-24 08:58:23 -08:00
|
|
|
depositLimitQuote?: BN,
|
2023-02-25 11:34:16 -08:00
|
|
|
feesPayWithMngo?: boolean,
|
|
|
|
feesMngoBonusRate?: number,
|
|
|
|
feesSwapMangoAccount?: PublicKey,
|
|
|
|
feesMngoTokenIndex?: TokenIndex,
|
2023-02-27 07:36:27 -08:00
|
|
|
feesExpiryInterval?: BN,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-08-13 10:54:58 -07:00
|
|
|
.groupEdit(
|
|
|
|
admin ?? null,
|
|
|
|
fastListingAdmin ?? null,
|
2023-01-12 00:12:55 -08:00
|
|
|
securityAdmin ?? null,
|
2022-08-13 10:54:58 -07:00
|
|
|
testing ?? null,
|
|
|
|
version ?? null,
|
2023-01-24 08:58:23 -08:00
|
|
|
depositLimitQuote !== undefined ? depositLimitQuote : null,
|
2023-02-25 11:34:16 -08:00
|
|
|
feesPayWithMngo ?? null,
|
|
|
|
feesMngoBonusRate ?? null,
|
|
|
|
feesSwapMangoAccount ?? null,
|
|
|
|
feesMngoTokenIndex ?? null,
|
2023-02-27 07:36:27 -08:00
|
|
|
feesExpiryInterval ?? null,
|
2022-08-13 10:54:58 -07:00
|
|
|
)
|
2022-08-03 01:25:09 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-08-03 01:25:09 -07:00
|
|
|
}
|
|
|
|
|
2023-01-26 11:27:39 -08:00
|
|
|
public async ixGateSet(
|
2023-01-12 00:12:55 -08:00
|
|
|
group: Group,
|
2023-01-26 11:27:39 -08:00
|
|
|
ixGateParams: IxGateParams,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2023-01-26 11:27:39 -08:00
|
|
|
.ixGateSet(buildIxGate(ixGateParams))
|
2023-01-12 00:12:55 -08:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2023-01-12 00:12:55 -08:00
|
|
|
}
|
|
|
|
|
2023-08-11 10:12:13 -07:00
|
|
|
public async groupClose(group: Group): Promise<MangoSignatureStatus> {
|
2022-06-09 09:27:31 -07:00
|
|
|
const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey;
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-06 05:51:15 -07:00
|
|
|
.groupClose()
|
2022-06-09 09:27:31 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-07-31 00:57:56 -07:00
|
|
|
insuranceVault: group.insuranceVault,
|
2022-06-09 09:27:31 -07:00
|
|
|
admin: adminPk,
|
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-04-08 11:47:12 -07:00
|
|
|
public async getGroup(groupPk: PublicKey): Promise<Group> {
|
2022-05-02 09:26:25 -07:00
|
|
|
const groupAccount = await this.program.account.group.fetch(groupPk);
|
|
|
|
const group = Group.from(groupPk, groupAccount);
|
2023-01-12 11:43:37 -08:00
|
|
|
await group.reloadAll(this);
|
2022-04-08 11:47:12 -07:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2022-08-08 07:31:38 -07:00
|
|
|
public async getGroupsForCreator(creatorPk: PublicKey): Promise<Group[]> {
|
2022-05-27 05:43:53 -07:00
|
|
|
const filters: MemcmpFilter[] = [
|
|
|
|
{
|
|
|
|
memcmp: {
|
2022-08-04 00:28:18 -07:00
|
|
|
bytes: creatorPk.toBase58(),
|
2022-05-27 05:43:53 -07:00
|
|
|
offset: 8,
|
2022-04-07 12:00:08 -07:00
|
|
|
},
|
2022-05-27 05:43:53 -07:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2022-08-13 11:51:09 -07:00
|
|
|
const groups = (await this.program.account.group.all(filters)).map(
|
|
|
|
(tuple) => Group.from(tuple.publicKey, tuple.account),
|
2022-05-27 05:43:53 -07:00
|
|
|
);
|
2022-08-13 11:51:09 -07:00
|
|
|
groups.forEach((group) => group.reloadAll(this));
|
|
|
|
return groups;
|
2022-08-08 01:48:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async getGroupForCreator(
|
|
|
|
creatorPk: PublicKey,
|
2022-08-13 11:51:09 -07:00
|
|
|
groupNum: number,
|
2022-08-08 01:48:05 -07:00
|
|
|
): Promise<Group> {
|
2022-08-13 11:51:09 -07:00
|
|
|
const bbuf = Buffer.alloc(4);
|
|
|
|
bbuf.writeUInt32LE(groupNum);
|
|
|
|
const filters: MemcmpFilter[] = [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: creatorPk.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
2022-08-08 07:31:38 -07:00
|
|
|
},
|
2022-08-13 11:51:09 -07:00
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: bs58.encode(bbuf),
|
|
|
|
offset: 40,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const groups = (await this.program.account.group.all(filters)).map(
|
|
|
|
(tuple) => Group.from(tuple.publicKey, tuple.account),
|
2022-08-08 07:31:38 -07:00
|
|
|
);
|
2022-08-14 04:30:08 -07:00
|
|
|
await groups[0].reloadAll(this);
|
2022-04-07 12:00:08 -07:00
|
|
|
return groups[0];
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-11-04 07:35:40 -07:00
|
|
|
public async getIds(groupPk: PublicKey): Promise<Id | undefined> {
|
|
|
|
switch (this.idsSource) {
|
|
|
|
case 'api':
|
|
|
|
return await Id.fromApi(groupPk);
|
|
|
|
case 'get-program-accounts':
|
|
|
|
return undefined;
|
|
|
|
case 'static':
|
|
|
|
return Id.fromIdsByPk(groupPk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:42:00 -07:00
|
|
|
// Tokens/Banks
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async tokenRegister(
|
2022-04-07 10:42:00 -07:00
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
oraclePk: PublicKey,
|
|
|
|
tokenIndex: number,
|
2022-04-12 07:19:58 -07:00
|
|
|
name: string,
|
2023-08-09 04:55:53 -07:00
|
|
|
params: TokenRegisterParams,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-06-09 09:27:31 -07:00
|
|
|
.tokenRegister(
|
2022-04-07 12:00:08 -07:00
|
|
|
tokenIndex,
|
2022-04-12 07:19:58 -07:00
|
|
|
name,
|
2023-08-09 04:55:53 -07:00
|
|
|
params.oracleConfig,
|
|
|
|
params.interestRateParams,
|
|
|
|
params.loanFeeRate,
|
|
|
|
params.loanOriginationFeeRate,
|
|
|
|
params.maintAssetWeight,
|
|
|
|
params.initAssetWeight,
|
|
|
|
params.maintLiabWeight,
|
|
|
|
params.initLiabWeight,
|
|
|
|
params.liquidationFee,
|
|
|
|
params.stablePriceDelayIntervalSeconds,
|
|
|
|
params.stablePriceDelayGrowthLimit,
|
|
|
|
params.stablePriceGrowthLimit,
|
|
|
|
params.minVaultToDepositsRatio,
|
|
|
|
new BN(params.netBorrowLimitWindowSizeTs),
|
|
|
|
new BN(params.netBorrowLimitPerWindowQuote),
|
|
|
|
params.borrowWeightScaleStartQuote,
|
|
|
|
params.depositWeightScaleStartQuote,
|
|
|
|
params.reduceOnly,
|
|
|
|
params.tokenConditionalSwapTakerFeeRate,
|
|
|
|
params.tokenConditionalSwapMakerFeeRate,
|
2023-08-29 01:03:07 -07:00
|
|
|
params.flashLoanDepositFeeRate,
|
2022-04-07 12:00:08 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 12:00:08 -07:00
|
|
|
mint: mintPk,
|
|
|
|
oracle: oraclePk,
|
2022-05-18 08:16:14 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 12:00:08 -07:00
|
|
|
rent: SYSVAR_RENT_PUBKEY,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 10:42:00 -07:00
|
|
|
}
|
|
|
|
|
2022-08-03 01:25:09 -07:00
|
|
|
public async tokenRegisterTrustless(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
oraclePk: PublicKey,
|
|
|
|
tokenIndex: number,
|
|
|
|
name: string,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-08-03 04:50:49 -07:00
|
|
|
.tokenRegisterTrustless(tokenIndex, name)
|
2022-08-03 01:25:09 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2023-02-28 06:04:28 -08:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-08-03 01:25:09 -07:00
|
|
|
mint: mintPk,
|
|
|
|
oracle: oraclePk,
|
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
rent: SYSVAR_RENT_PUBKEY,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-08-03 01:25:09 -07:00
|
|
|
}
|
|
|
|
|
2022-07-05 10:31:47 -07:00
|
|
|
public async tokenEdit(
|
|
|
|
group: Group,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2023-01-14 05:09:26 -08:00
|
|
|
params: TokenEditParams,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const bank = group.getFirstBankByMint(mintPk);
|
|
|
|
const mintInfo = group.mintInfosMapByTokenIndex.get(bank.tokenIndex)!;
|
2022-07-05 10:31:47 -07:00
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-05 10:31:47 -07:00
|
|
|
.tokenEdit(
|
2023-01-14 05:09:26 -08:00
|
|
|
params.oracle,
|
|
|
|
params.oracleConfig,
|
|
|
|
params.groupInsuranceFund,
|
|
|
|
params.interestRateParams,
|
|
|
|
params.loanFeeRate,
|
|
|
|
params.loanOriginationFeeRate,
|
|
|
|
params.maintAssetWeight,
|
|
|
|
params.initAssetWeight,
|
|
|
|
params.maintLiabWeight,
|
|
|
|
params.initLiabWeight,
|
|
|
|
params.liquidationFee,
|
|
|
|
params.stablePriceDelayIntervalSeconds,
|
|
|
|
params.stablePriceDelayGrowthLimit,
|
|
|
|
params.stablePriceGrowthLimit,
|
|
|
|
params.minVaultToDepositsRatio,
|
|
|
|
params.netBorrowLimitPerWindowQuote !== null
|
|
|
|
? new BN(params.netBorrowLimitPerWindowQuote)
|
2022-12-06 00:34:02 -08:00
|
|
|
: null,
|
2023-01-14 05:09:26 -08:00
|
|
|
params.netBorrowLimitWindowSizeTs !== null
|
|
|
|
? new BN(params.netBorrowLimitWindowSizeTs)
|
2022-12-06 00:34:02 -08:00
|
|
|
: null,
|
2023-01-14 05:09:26 -08:00
|
|
|
params.borrowWeightScaleStartQuote,
|
|
|
|
params.depositWeightScaleStartQuote,
|
|
|
|
params.resetStablePrice ?? false,
|
|
|
|
params.resetNetBorrowLimit ?? false,
|
|
|
|
params.reduceOnly,
|
2023-03-03 01:05:12 -08:00
|
|
|
params.name,
|
2023-04-13 03:44:12 -07:00
|
|
|
params.forceClose,
|
2023-08-08 01:21:13 -07:00
|
|
|
params.tokenConditionalSwapTakerFeeRate,
|
|
|
|
params.tokenConditionalSwapMakerFeeRate,
|
2023-08-29 01:03:07 -07:00
|
|
|
params.flashLoanDepositFeeRate,
|
2022-07-05 10:31:47 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2023-01-14 05:09:26 -08:00
|
|
|
oracle: params.oracle ?? bank.oracle,
|
2022-07-05 10:31:47 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
mintInfo: mintInfo.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts([
|
|
|
|
{
|
|
|
|
pubkey: bank.publicKey,
|
|
|
|
isWritable: true,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta,
|
|
|
|
])
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-07-05 10:31:47 -07:00
|
|
|
}
|
|
|
|
|
2023-04-24 23:12:42 -07:00
|
|
|
public async tokenForceCloseBorrowsWithToken(
|
|
|
|
group: Group,
|
|
|
|
liqor: MangoAccount,
|
|
|
|
liqee: MangoAccount,
|
|
|
|
assetTokenIndex: TokenIndex,
|
|
|
|
liabTokenIndex: TokenIndex,
|
|
|
|
maxLiabTransfer?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-24 23:12:42 -07:00
|
|
|
const assetBank = group.getFirstBankByTokenIndex(assetTokenIndex);
|
|
|
|
const liabBank = group.getFirstBankByTokenIndex(liabTokenIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[liqor, liqee],
|
|
|
|
[assetBank, liabBank],
|
|
|
|
[],
|
|
|
|
);
|
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable:
|
|
|
|
pk.equals(assetBank.publicKey) || pk.equals(liabBank.publicKey)
|
|
|
|
? true
|
|
|
|
: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
);
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.tokenForceCloseBorrowsWithToken(
|
|
|
|
assetTokenIndex,
|
|
|
|
liabTokenIndex,
|
|
|
|
maxLiabTransfer
|
|
|
|
? toNative(maxLiabTransfer, liabBank.mintDecimals)
|
|
|
|
: U64_MAX_BN,
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
liqor: liqor.publicKey,
|
|
|
|
liqorOwner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
liqee: liqee.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(parsedHealthAccounts)
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async tokenDeregister(
|
|
|
|
group: Group,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const bank = group.getFirstBankByMint(mintPk);
|
2022-06-09 09:27:31 -07:00
|
|
|
const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey;
|
2022-06-29 00:11:14 -07:00
|
|
|
|
|
|
|
const dustVaultPk = await getAssociatedTokenAddress(bank.mint, adminPk);
|
|
|
|
const ai = await this.program.provider.connection.getAccountInfo(
|
|
|
|
dustVaultPk,
|
|
|
|
);
|
2022-12-09 03:49:11 -08:00
|
|
|
const preInstructions: TransactionInstruction[] = [];
|
2022-06-29 00:11:14 -07:00
|
|
|
if (!ai) {
|
2022-12-09 03:49:11 -08:00
|
|
|
preInstructions.push(
|
|
|
|
await createAssociatedTokenAccountIdempotentInstruction(
|
2022-06-29 00:11:14 -07:00
|
|
|
adminPk,
|
|
|
|
adminPk,
|
2022-12-09 03:49:11 -08:00
|
|
|
bank.mint,
|
2022-06-29 00:11:14 -07:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-09 03:49:11 -08:00
|
|
|
const ix = await this.program.methods
|
2022-08-25 10:25:56 -07:00
|
|
|
.tokenDeregister()
|
2022-06-09 09:27:31 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: adminPk,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintInfo: group.mintInfosMapByTokenIndex.get(bank.tokenIndex)
|
|
|
|
?.publicKey,
|
2022-06-29 00:11:14 -07:00
|
|
|
dustVault: dustVaultPk,
|
2022-06-09 09:27:31 -07:00
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2022-06-27 02:27:17 -07:00
|
|
|
.remainingAccounts(
|
|
|
|
[bank.publicKey, bank.vault].map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: true, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2022-12-09 03:49:11 -08:00
|
|
|
.instruction();
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
...preInstructions,
|
|
|
|
ix,
|
|
|
|
]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
public async getBanksForGroup(group: Group): Promise<Bank[]> {
|
2022-04-07 12:00:08 -07:00
|
|
|
return (
|
|
|
|
await this.program.account.bank.all([
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 8,
|
2022-04-07 12:00:08 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
])
|
|
|
|
).map((tuple) => Bank.from(tuple.publicKey, tuple.account));
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async getMintInfosForGroup(group: Group): Promise<MintInfo[]> {
|
|
|
|
return (
|
|
|
|
await this.program.account.mintInfo.all([
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
])
|
|
|
|
).map((tuple) => {
|
|
|
|
return MintInfo.from(tuple.publicKey, tuple.account);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async getMintInfoForTokenIndex(
|
|
|
|
group: Group,
|
2022-09-29 06:51:09 -07:00
|
|
|
tokenIndex: TokenIndex,
|
2022-06-09 09:27:31 -07:00
|
|
|
): Promise<MintInfo[]> {
|
|
|
|
const tokenIndexBuf = Buffer.alloc(2);
|
|
|
|
tokenIndexBuf.writeUInt16LE(tokenIndex);
|
|
|
|
return (
|
|
|
|
await this.program.account.mintInfo.all([
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: bs58.encode(tokenIndexBuf),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 40,
|
2022-06-09 09:27:31 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
])
|
|
|
|
).map((tuple) => {
|
|
|
|
return MintInfo.from(tuple.publicKey, tuple.account);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-07 11:05:06 -07:00
|
|
|
// Stub Oracle
|
|
|
|
|
2022-07-06 05:51:15 -07:00
|
|
|
public async stubOracleCreate(
|
2022-04-07 10:42:00 -07:00
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
price: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-06 05:51:15 -07:00
|
|
|
.stubOracleCreate({ val: I80F48.fromNumber(price).getData() })
|
2022-04-07 12:00:08 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-08-04 03:13:36 -07:00
|
|
|
mint: mintPk,
|
2022-05-18 08:16:14 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 10:42:00 -07:00
|
|
|
}
|
|
|
|
|
2022-07-06 05:51:15 -07:00
|
|
|
public async stubOracleClose(
|
2022-06-09 09:27:31 -07:00
|
|
|
group: Group,
|
|
|
|
oracle: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-06 05:51:15 -07:00
|
|
|
.stubOracleClose()
|
2022-06-09 09:27:31 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
oracle: oracle,
|
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-07-06 05:51:15 -07:00
|
|
|
public async stubOracleSet(
|
2022-04-07 10:42:00 -07:00
|
|
|
group: Group,
|
2022-06-18 07:31:28 -07:00
|
|
|
oraclePk: PublicKey,
|
2022-04-07 10:42:00 -07:00
|
|
|
price: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-06 05:51:15 -07:00
|
|
|
.stubOracleSet({ val: I80F48.fromNumber(price).getData() })
|
2022-04-07 12:00:08 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-06-18 07:31:28 -07:00
|
|
|
oracle: oraclePk,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 10:42:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async getStubOracle(
|
|
|
|
group: Group,
|
2022-06-11 04:49:45 -07:00
|
|
|
mintPk?: PublicKey,
|
|
|
|
): Promise<StubOracle[]> {
|
|
|
|
const filters = [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
2022-04-07 12:00:08 -07:00
|
|
|
},
|
2022-06-11 04:49:45 -07:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
if (mintPk) {
|
|
|
|
filters.push({
|
|
|
|
memcmp: {
|
|
|
|
bytes: mintPk.toBase58(),
|
|
|
|
offset: 40,
|
2022-04-07 12:00:08 -07:00
|
|
|
},
|
2022-06-11 04:49:45 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.program.account.stubOracle.all(filters)).map((pa) =>
|
|
|
|
StubOracle.from(pa.publicKey, pa.account),
|
|
|
|
);
|
2022-04-07 10:42:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// MangoAccount
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
public async createMangoAccount(
|
|
|
|
group: Group,
|
2022-08-04 08:35:05 -07:00
|
|
|
accountNumber?: number,
|
2022-04-12 07:19:58 -07:00
|
|
|
name?: string,
|
2022-09-15 00:57:48 -07:00
|
|
|
tokenCount?: number,
|
|
|
|
serum3Count?: number,
|
|
|
|
perpCount?: number,
|
|
|
|
perpOoCount?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-09-15 00:57:48 -07:00
|
|
|
.accountCreate(
|
|
|
|
accountNumber ?? 0,
|
|
|
|
tokenCount ?? 8,
|
2023-08-21 07:26:49 -07:00
|
|
|
serum3Count ?? 4,
|
|
|
|
perpCount ?? 4,
|
2023-02-21 01:40:15 -08:00
|
|
|
perpOoCount ?? 32,
|
2022-09-15 00:57:48 -07:00
|
|
|
name ?? '',
|
|
|
|
)
|
2022-04-07 12:00:08 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-08-04 08:35:05 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-08-15 21:27:06 -07:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
public async expandMangoAccount(
|
2022-09-15 00:57:48 -07:00
|
|
|
group: Group,
|
2023-07-03 05:09:11 -07:00
|
|
|
account: MangoAccount,
|
|
|
|
tokenCount: number,
|
|
|
|
serum3Count: number,
|
|
|
|
perpCount: number,
|
|
|
|
perpOoCount: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const ix = await this.program.methods
|
|
|
|
.accountExpand(tokenCount, serum3Count, perpCount, perpOoCount)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-09-15 00:57:48 -07:00
|
|
|
}
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
public async accountExpandV2(
|
2022-07-25 07:07:53 -07:00
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
2022-08-07 05:16:06 -07:00
|
|
|
tokenCount: number,
|
|
|
|
serum3Count: number,
|
|
|
|
perpCount: number,
|
|
|
|
perpOoCount: number,
|
2023-07-03 05:09:11 -07:00
|
|
|
tokenConditionalSwapCount: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-27 23:26:34 -07:00
|
|
|
const ix = await this.accountExpandV2Ix(
|
2022-09-15 00:57:48 -07:00
|
|
|
group,
|
2023-07-27 23:26:34 -07:00
|
|
|
account,
|
2022-09-15 00:57:48 -07:00
|
|
|
tokenCount,
|
|
|
|
serum3Count,
|
|
|
|
perpCount,
|
|
|
|
perpOoCount,
|
2023-07-27 23:26:34 -07:00
|
|
|
tokenConditionalSwapCount,
|
2022-09-15 00:57:48 -07:00
|
|
|
);
|
2023-07-27 23:26:34 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-09-15 00:57:48 -07:00
|
|
|
}
|
|
|
|
|
2023-07-27 23:26:34 -07:00
|
|
|
public async accountExpandV2Ix(
|
2022-07-25 07:07:53 -07:00
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
2022-08-07 05:16:06 -07:00
|
|
|
tokenCount: number,
|
|
|
|
serum3Count: number,
|
|
|
|
perpCount: number,
|
|
|
|
perpOoCount: number,
|
2023-07-27 23:26:34 -07:00
|
|
|
tokenConditionalSwapCount: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
return await this.program.methods
|
2023-07-03 05:09:11 -07:00
|
|
|
.accountExpandV2(
|
|
|
|
tokenCount,
|
|
|
|
serum3Count,
|
|
|
|
perpCount,
|
|
|
|
perpOoCount,
|
|
|
|
tokenConditionalSwapCount,
|
|
|
|
)
|
2022-07-25 07:07:53 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-07-05 10:31:47 -07:00
|
|
|
public async editMangoAccount(
|
|
|
|
group: Group,
|
2022-07-06 00:56:14 -07:00
|
|
|
mangoAccount: MangoAccount,
|
2022-07-05 10:31:47 -07:00
|
|
|
name?: string,
|
|
|
|
delegate?: PublicKey,
|
2023-08-09 03:05:16 -07:00
|
|
|
temporaryDelegate?: PublicKey,
|
|
|
|
delegateExpiry?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2023-08-09 03:05:16 -07:00
|
|
|
.accountEdit(
|
|
|
|
name ?? null,
|
|
|
|
delegate ?? null,
|
|
|
|
temporaryDelegate ?? null,
|
|
|
|
delegateExpiry ? new BN(delegateExpiry) : null,
|
|
|
|
)
|
2022-07-05 10:31:47 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-07-06 00:56:14 -07:00
|
|
|
account: mangoAccount.publicKey,
|
2022-07-05 10:31:47 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-08-15 21:27:06 -07:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async computeAccountData(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-03 04:55:46 -08:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2023-04-24 05:48:53 -07:00
|
|
|
this.buildHealthRemainingAccounts(group, [mangoAccount], [], []);
|
2023-02-03 04:55:46 -08:00
|
|
|
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.computeAccountData()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-07-05 10:31:47 -07:00
|
|
|
}
|
|
|
|
|
2023-01-12 04:08:10 -08:00
|
|
|
public async toggleMangoAccountFreeze(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
freeze: boolean,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2023-01-12 11:39:46 -08:00
|
|
|
.accountToggleFreeze(freeze)
|
2023-01-12 04:08:10 -08:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2023-01-12 04:08:10 -08:00
|
|
|
}
|
|
|
|
|
2022-12-08 12:53:07 -08:00
|
|
|
public async getMangoAccount(
|
2023-07-03 05:09:11 -07:00
|
|
|
mangoAccountPk: PublicKey,
|
2023-01-19 02:31:54 -08:00
|
|
|
loadSerum3Oo = false,
|
2022-12-08 12:53:07 -08:00
|
|
|
): Promise<MangoAccount> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const mangoAccount = await this.getMangoAccountFromPk(mangoAccountPk);
|
2023-01-19 02:31:54 -08:00
|
|
|
if (loadSerum3Oo) {
|
2023-07-03 05:09:11 -07:00
|
|
|
await mangoAccount?.reloadSerum3OpenOrders(this);
|
2023-01-19 02:31:54 -08:00
|
|
|
}
|
2023-07-03 05:09:11 -07:00
|
|
|
return mangoAccount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async getMangoAccountFromPk(
|
|
|
|
mangoAccountPk: PublicKey,
|
|
|
|
): Promise<MangoAccount> {
|
2023-08-12 10:37:29 -07:00
|
|
|
return this.getMangoAccountFromAi(
|
2023-07-03 05:09:11 -07:00
|
|
|
mangoAccountPk,
|
|
|
|
(await this.program.provider.connection.getAccountInfo(
|
|
|
|
mangoAccountPk,
|
|
|
|
)) as AccountInfo<Buffer>,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-12 10:37:29 -07:00
|
|
|
public getMangoAccountFromAi(
|
2023-07-03 05:09:11 -07:00
|
|
|
mangoAccountPk: PublicKey,
|
|
|
|
ai: AccountInfo<Buffer>,
|
2023-08-12 10:37:29 -07:00
|
|
|
): MangoAccount {
|
2023-07-03 05:09:11 -07:00
|
|
|
const decodedMangoAccount = this.program.coder.accounts.decode(
|
|
|
|
'mangoAccount',
|
|
|
|
ai.data,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Re-encode decoded mango account with v1 layout, this will help identifying
|
|
|
|
// if account is of type v1 or v2
|
|
|
|
// Do whole encoding manually, since anchor uses a buffer of a constant length which is too small
|
|
|
|
const mangoAccountV1Buffer = Buffer.alloc(ai.data.length);
|
|
|
|
const layout =
|
|
|
|
this.program.coder.accounts['accountLayouts'].get('mangoAccount');
|
|
|
|
const discriminatorLen = 8;
|
|
|
|
const v1DataLen = layout.encode(decodedMangoAccount, mangoAccountV1Buffer);
|
|
|
|
const v1Len = discriminatorLen + v1DataLen;
|
|
|
|
|
|
|
|
const tokenConditionalSwaps =
|
|
|
|
ai.data.length > v1Len
|
|
|
|
? (borsh
|
|
|
|
.vec(
|
|
|
|
(this.program as any)._coder.types.typeLayouts.get(
|
|
|
|
'TokenConditionalSwap',
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.decode(
|
|
|
|
ai.data.subarray(
|
|
|
|
v1Len +
|
|
|
|
// This is the padding before tokenConditionalSwaps
|
|
|
|
4,
|
|
|
|
),
|
|
|
|
) as TokenConditionalSwapDto[])
|
|
|
|
: new Array<TokenConditionalSwapDto>();
|
|
|
|
|
|
|
|
return MangoAccount.from(
|
|
|
|
mangoAccountPk,
|
|
|
|
decodedMangoAccount,
|
|
|
|
tokenConditionalSwaps,
|
|
|
|
);
|
2022-09-23 11:39:51 -07:00
|
|
|
}
|
2022-09-27 08:33:51 -07:00
|
|
|
|
2022-09-29 06:51:09 -07:00
|
|
|
public async getMangoAccountWithSlot(
|
|
|
|
mangoAccountPk: PublicKey,
|
2023-01-19 02:31:54 -08:00
|
|
|
loadSerum3Oo = false,
|
2022-09-29 06:51:09 -07:00
|
|
|
): Promise<{ slot: number; value: MangoAccount } | undefined> {
|
2022-09-05 09:31:57 -07:00
|
|
|
const resp =
|
|
|
|
await this.program.provider.connection.getAccountInfoAndContext(
|
|
|
|
mangoAccountPk,
|
|
|
|
);
|
|
|
|
if (!resp?.value) return;
|
2023-07-03 05:09:11 -07:00
|
|
|
const mangoAccount = await this.getMangoAccountFromAi(
|
|
|
|
mangoAccountPk,
|
|
|
|
resp.value,
|
2022-09-05 09:31:57 -07:00
|
|
|
);
|
2023-01-19 02:31:54 -08:00
|
|
|
if (loadSerum3Oo) {
|
|
|
|
await mangoAccount?.reloadSerum3OpenOrders(this);
|
|
|
|
}
|
2022-09-05 09:31:57 -07:00
|
|
|
return { slot: resp.context.slot, value: mangoAccount };
|
|
|
|
}
|
|
|
|
|
2022-08-08 07:31:38 -07:00
|
|
|
public async getMangoAccountForOwner(
|
|
|
|
group: Group,
|
|
|
|
ownerPk: PublicKey,
|
|
|
|
accountNumber: number,
|
2023-01-19 02:31:54 -08:00
|
|
|
loadSerum3Oo = false,
|
2022-08-31 02:41:12 -07:00
|
|
|
): Promise<MangoAccount | undefined> {
|
2023-01-19 02:31:54 -08:00
|
|
|
const mangoAccounts = await this.getMangoAccountsForOwner(
|
|
|
|
group,
|
|
|
|
ownerPk,
|
|
|
|
loadSerum3Oo,
|
|
|
|
);
|
2022-08-31 02:41:12 -07:00
|
|
|
const foundMangoAccount = mangoAccounts.find(
|
2022-08-08 07:31:38 -07:00
|
|
|
(a) => a.accountNum == accountNumber,
|
|
|
|
);
|
2022-08-31 02:41:12 -07:00
|
|
|
|
|
|
|
return foundMangoAccount;
|
2022-08-08 07:31:38 -07:00
|
|
|
}
|
|
|
|
|
2022-08-02 00:38:28 -07:00
|
|
|
public async getMangoAccountsForOwner(
|
2022-04-07 09:58:42 -07:00
|
|
|
group: Group,
|
|
|
|
ownerPk: PublicKey,
|
2023-01-19 02:31:54 -08:00
|
|
|
loadSerum3Oo = false,
|
2022-04-07 09:58:42 -07:00
|
|
|
): Promise<MangoAccount[]> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const discriminatorMemcmp: {
|
|
|
|
offset: number;
|
|
|
|
bytes: string;
|
|
|
|
} = this.program.account.mangoAccount.coder.accounts.memcmp(
|
|
|
|
'mangoAccount',
|
|
|
|
undefined,
|
|
|
|
);
|
|
|
|
|
|
|
|
const accounts = await Promise.all(
|
|
|
|
(
|
|
|
|
await this.program.provider.connection.getProgramAccounts(
|
|
|
|
this.programId,
|
|
|
|
{
|
|
|
|
filters: [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: discriminatorMemcmp.bytes,
|
|
|
|
offset: discriminatorMemcmp.offset,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: ownerPk.toBase58(),
|
|
|
|
offset: 40,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2022-04-07 12:00:08 -07:00
|
|
|
},
|
2023-07-03 05:09:11 -07:00
|
|
|
)
|
|
|
|
).map((account) => {
|
|
|
|
return this.getMangoAccountFromAi(account.pubkey, account.account);
|
|
|
|
}),
|
|
|
|
);
|
2023-01-19 02:31:54 -08:00
|
|
|
|
|
|
|
if (loadSerum3Oo) {
|
|
|
|
await Promise.all(
|
|
|
|
accounts.map(async (a) => await a.reloadSerum3OpenOrders(this)),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return accounts;
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async getMangoAccountsForDelegate(
|
|
|
|
group: Group,
|
|
|
|
delegate: PublicKey,
|
2023-01-19 02:31:54 -08:00
|
|
|
loadSerum3Oo = false,
|
2022-10-07 04:52:04 -07:00
|
|
|
): Promise<MangoAccount[]> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const discriminatorMemcmp: {
|
|
|
|
offset: number;
|
|
|
|
bytes: string;
|
|
|
|
} = this.program.account.mangoAccount.coder.accounts.memcmp(
|
|
|
|
'mangoAccount',
|
|
|
|
undefined,
|
|
|
|
);
|
|
|
|
|
|
|
|
const accounts = await Promise.all(
|
|
|
|
(
|
|
|
|
await this.program.provider.connection.getProgramAccounts(
|
|
|
|
this.programId,
|
|
|
|
{
|
|
|
|
filters: [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: discriminatorMemcmp.bytes,
|
|
|
|
offset: discriminatorMemcmp.offset,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: delegate.toBase58(),
|
|
|
|
offset: 104,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2022-10-07 04:52:04 -07:00
|
|
|
},
|
2023-07-03 05:09:11 -07:00
|
|
|
)
|
|
|
|
).map((account) => {
|
|
|
|
return this.getMangoAccountFromAi(account.pubkey, account.account);
|
|
|
|
}),
|
|
|
|
);
|
2023-01-19 02:31:54 -08:00
|
|
|
|
|
|
|
if (loadSerum3Oo) {
|
|
|
|
await Promise.all(
|
|
|
|
accounts.map(async (a) => await a.reloadSerum3OpenOrders(this)),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return accounts;
|
2022-10-07 04:52:04 -07:00
|
|
|
}
|
|
|
|
|
2023-01-19 02:31:54 -08:00
|
|
|
public async getAllMangoAccounts(
|
|
|
|
group: Group,
|
|
|
|
loadSerum3Oo = false,
|
|
|
|
): Promise<MangoAccount[]> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const discriminatorMemcmp: {
|
|
|
|
offset: number;
|
|
|
|
bytes: string;
|
|
|
|
} = this.program.account.mangoAccount.coder.accounts.memcmp(
|
|
|
|
'mangoAccount',
|
|
|
|
undefined,
|
|
|
|
);
|
|
|
|
|
|
|
|
const accounts = await Promise.all(
|
|
|
|
(
|
|
|
|
await this.program.provider.connection.getProgramAccounts(
|
|
|
|
this.programId,
|
|
|
|
{
|
|
|
|
filters: [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: discriminatorMemcmp.bytes,
|
|
|
|
offset: discriminatorMemcmp.offset,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
|
|
|
offset: 8,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2022-08-13 10:36:09 -07:00
|
|
|
},
|
2023-07-03 05:09:11 -07:00
|
|
|
)
|
|
|
|
).map((account) => {
|
|
|
|
return this.getMangoAccountFromAi(account.pubkey, account.account);
|
|
|
|
}),
|
|
|
|
);
|
2023-01-19 02:31:54 -08:00
|
|
|
|
|
|
|
if (loadSerum3Oo) {
|
2023-07-04 00:26:59 -07:00
|
|
|
const ooPks = accounts
|
|
|
|
.map((a) => a.serum3Active().map((serum3) => serum3.openOrders))
|
|
|
|
.flat();
|
|
|
|
|
|
|
|
const ais: AccountInfo<Buffer>[] = (
|
|
|
|
await Promise.all(
|
|
|
|
chunk(ooPks, 100).map(
|
|
|
|
async (ooPksChunk) =>
|
|
|
|
await this.program.provider.connection.getMultipleAccountsInfo(
|
|
|
|
ooPksChunk,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
).flat();
|
|
|
|
|
|
|
|
if (ooPks.length != ais.length) {
|
|
|
|
throw new Error(`Error in fetch all open orders accounts!`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const serum3OosMapByOo = new Map(
|
|
|
|
Array.from(
|
|
|
|
ais.map((ai, i) => {
|
|
|
|
if (ai == null) {
|
|
|
|
throw new Error(`Undefined AI for open orders ${ooPks[i]}!`);
|
|
|
|
}
|
|
|
|
const oo = OpenOrders.fromAccountInfo(
|
|
|
|
ooPks[i],
|
|
|
|
ai,
|
|
|
|
OPENBOOK_PROGRAM_ID[this.cluster],
|
|
|
|
);
|
|
|
|
return [ooPks[i].toBase58(), oo];
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
accounts.forEach(
|
|
|
|
async (a) => await a.loadSerum3OpenOrders(serum3OosMapByOo),
|
2023-01-19 02:31:54 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return accounts;
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-12-15 12:10:56 -08:00
|
|
|
/**
|
|
|
|
* Note: this ix doesn't settle liabs, reduce open positions, or withdraw tokens to wallet,
|
|
|
|
* it simply closes the account. To close successfully ensure all positions are closed, or
|
|
|
|
* use forceClose flag
|
|
|
|
* @param group
|
|
|
|
* @param mangoAccount
|
|
|
|
* @param forceClose
|
|
|
|
* @returns
|
|
|
|
*/
|
2022-04-08 08:21:49 -07:00
|
|
|
public async closeMangoAccount(
|
2022-07-04 03:09:33 -07:00
|
|
|
group: Group,
|
2022-04-08 08:21:49 -07:00
|
|
|
mangoAccount: MangoAccount,
|
2022-12-15 12:10:56 -08:00
|
|
|
forceClose = false,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-12-15 12:10:56 -08:00
|
|
|
.accountClose(forceClose)
|
2022-04-08 08:21:49 -07:00
|
|
|
.accounts({
|
2022-07-04 03:09:33 -07:00
|
|
|
group: group.publicKey,
|
2022-04-08 08:21:49 -07:00
|
|
|
account: mangoAccount.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-06-09 09:27:31 -07:00
|
|
|
solDestination: mangoAccount.owner,
|
2022-04-08 08:21:49 -07:00
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-09-02 10:50:01 -07:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-08 08:21:49 -07:00
|
|
|
}
|
|
|
|
|
2023-01-12 16:38:02 -08:00
|
|
|
public async emptyAndCloseMangoAccount(
|
2023-01-12 13:34:48 -08:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-24 05:48:53 -07:00
|
|
|
// Work on a deep cloned mango account, since we would deactivating positions
|
|
|
|
// before deactivation reaches on-chain state in order to simplify building a fresh list
|
|
|
|
// of healthRemainingAccounts to each subsequent ix
|
|
|
|
const clonedMangoAccount = cloneDeep(mangoAccount);
|
2023-01-13 04:19:04 -08:00
|
|
|
const instructions: TransactionInstruction[] = [];
|
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
for (const serum3Account of clonedMangoAccount.serum3Active()) {
|
2023-01-13 04:19:04 -08:00
|
|
|
const serum3Market = group.serum3MarketsMapByMarketIndex.get(
|
|
|
|
serum3Account.marketIndex,
|
2023-01-13 14:32:12 -08:00
|
|
|
)!;
|
|
|
|
|
2023-01-13 04:19:04 -08:00
|
|
|
const closeOOIx = await this.serum3CloseOpenOrdersIx(
|
|
|
|
group,
|
2023-04-24 05:48:53 -07:00
|
|
|
clonedMangoAccount,
|
2023-01-13 14:32:12 -08:00
|
|
|
serum3Market.serumMarketExternal,
|
2023-01-13 04:19:04 -08:00
|
|
|
);
|
|
|
|
instructions.push(closeOOIx);
|
2023-04-24 05:48:53 -07:00
|
|
|
serum3Account.marketIndex =
|
|
|
|
Serum3Orders.Serum3MarketIndexUnset as MarketIndex;
|
2023-01-13 04:19:04 -08:00
|
|
|
}
|
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
for (const pp of clonedMangoAccount.perpActive()) {
|
|
|
|
const perpMarketIndex = pp.marketIndex;
|
2023-01-13 14:32:12 -08:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2023-01-13 04:19:04 -08:00
|
|
|
const deactivatingPositionIx = await this.perpDeactivatePositionIx(
|
|
|
|
group,
|
2023-04-24 05:48:53 -07:00
|
|
|
clonedMangoAccount,
|
2023-01-13 14:32:12 -08:00
|
|
|
perpMarketIndex,
|
2023-01-13 04:19:04 -08:00
|
|
|
);
|
|
|
|
instructions.push(deactivatingPositionIx);
|
2023-04-24 05:48:53 -07:00
|
|
|
pp.marketIndex = PerpPosition.PerpMarketIndexUnset as PerpMarketIndex;
|
2023-01-13 04:19:04 -08:00
|
|
|
}
|
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
for (const tp of clonedMangoAccount.tokensActive()) {
|
|
|
|
const bank = group.getFirstBankByTokenIndex(tp.tokenIndex);
|
2023-01-13 14:32:12 -08:00
|
|
|
const withdrawIx = await this.tokenWithdrawNativeIx(
|
|
|
|
group,
|
2023-04-24 05:48:53 -07:00
|
|
|
clonedMangoAccount,
|
2023-01-13 14:32:12 -08:00
|
|
|
bank.mint,
|
|
|
|
U64_MAX_BN,
|
|
|
|
false,
|
|
|
|
);
|
|
|
|
instructions.push(...withdrawIx);
|
2023-04-24 05:48:53 -07:00
|
|
|
tp.tokenIndex = TokenPosition.TokenIndexUnset as TokenIndex;
|
2023-01-13 14:32:12 -08:00
|
|
|
}
|
|
|
|
|
2023-01-13 04:19:04 -08:00
|
|
|
const closeIx = await this.program.methods
|
2023-01-13 14:32:12 -08:00
|
|
|
.accountClose(false)
|
2022-04-08 08:21:49 -07:00
|
|
|
.accounts({
|
2022-07-04 03:09:33 -07:00
|
|
|
group: group.publicKey,
|
2023-04-24 05:48:53 -07:00
|
|
|
account: clonedMangoAccount.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2023-04-24 05:48:53 -07:00
|
|
|
solDestination: clonedMangoAccount.owner,
|
2022-04-08 08:21:49 -07:00
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2023-01-13 04:19:04 -08:00
|
|
|
instructions.push(closeIx);
|
2022-09-02 10:50:01 -07:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, instructions);
|
2022-04-08 08:21:49 -07:00
|
|
|
}
|
|
|
|
|
2023-02-28 03:05:02 -08:00
|
|
|
public async accountBuybackFeesWithMngoIx(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2023-03-20 03:18:11 -07:00
|
|
|
maxBuybackUsd?: number,
|
2023-02-28 03:05:02 -08:00
|
|
|
): Promise<TransactionInstruction> {
|
2023-03-20 03:18:11 -07:00
|
|
|
maxBuybackUsd = maxBuybackUsd ?? mangoAccount.getMaxFeesBuybackUi(group);
|
2023-02-28 03:05:02 -08:00
|
|
|
return await this.program.methods
|
2023-03-20 03:18:11 -07:00
|
|
|
.accountBuybackFeesWithMngo(toNative(maxBuybackUsd, 6))
|
2023-02-28 03:05:02 -08:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
daoAccount: group.buybackFeesSwapMangoAccount,
|
|
|
|
mngoBank: group.getFirstBankForMngo().publicKey,
|
|
|
|
mngoOracle: group.getFirstBankForMngo().oracle,
|
|
|
|
feesBank: group.getFirstBankByTokenIndex(0 as TokenIndex).publicKey,
|
|
|
|
feesOracle: group.getFirstBankByTokenIndex(0 as TokenIndex).oracle,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async accountBuybackFeesWithMngo(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
maxBuyback?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-28 03:05:02 -08:00
|
|
|
const ix = await this.accountBuybackFeesWithMngoIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
maxBuyback,
|
|
|
|
);
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async tokenDeposit(
|
2022-04-07 09:58:42 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2022-04-07 09:58:42 -07:00
|
|
|
amount: number,
|
2023-01-04 00:24:40 -08:00
|
|
|
reduceOnly = false,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-31 02:41:12 -07:00
|
|
|
const decimals = group.getMintDecimals(mintPk);
|
2022-09-30 06:07:43 -07:00
|
|
|
const nativeAmount = toNative(amount, decimals);
|
2022-08-08 07:31:38 -07:00
|
|
|
return await this.tokenDepositNative(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk,
|
2022-08-08 07:31:38 -07:00
|
|
|
nativeAmount,
|
2023-01-04 00:24:40 -08:00
|
|
|
reduceOnly,
|
2022-08-08 07:31:38 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async tokenDepositNative(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2022-09-30 06:07:43 -07:00
|
|
|
nativeAmount: BN,
|
2023-01-04 00:24:40 -08:00
|
|
|
reduceOnly = false,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const bank = group.getFirstBankByMint(mintPk);
|
2022-04-08 03:30:21 -07:00
|
|
|
|
2022-05-25 17:29:06 -07:00
|
|
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk,
|
2022-04-07 09:58:42 -07:00
|
|
|
mangoAccount.owner,
|
|
|
|
);
|
|
|
|
|
2023-08-11 00:35:57 -07:00
|
|
|
let wrappedSolAccount: PublicKey | undefined;
|
2022-05-25 17:29:06 -07:00
|
|
|
let preInstructions: TransactionInstruction[] = [];
|
|
|
|
let postInstructions: TransactionInstruction[] = [];
|
2023-02-21 23:36:59 -08:00
|
|
|
if (mintPk.equals(NATIVE_MINT)) {
|
2023-08-11 00:35:57 -07:00
|
|
|
// Generate a random seed for wrappedSolAccount.
|
|
|
|
const seed = Keypair.generate().publicKey.toBase58().slice(0, 32);
|
|
|
|
// Calculate a publicKey that will be controlled by the `mangoAccount.owner`.
|
|
|
|
wrappedSolAccount = await PublicKey.createWithSeed(
|
|
|
|
mangoAccount.owner,
|
|
|
|
seed,
|
|
|
|
TOKEN_PROGRAM_ID,
|
|
|
|
);
|
|
|
|
|
2022-09-30 06:07:43 -07:00
|
|
|
const lamports = nativeAmount.add(new BN(1e7));
|
2022-05-25 17:29:06 -07:00
|
|
|
|
|
|
|
preInstructions = [
|
2023-08-11 00:35:57 -07:00
|
|
|
SystemProgram.createAccountWithSeed({
|
2022-05-25 17:29:06 -07:00
|
|
|
fromPubkey: mangoAccount.owner,
|
2023-08-11 00:35:57 -07:00
|
|
|
basePubkey: mangoAccount.owner,
|
|
|
|
seed,
|
|
|
|
newAccountPubkey: wrappedSolAccount,
|
2022-09-30 06:07:43 -07:00
|
|
|
lamports: lamports.toNumber(),
|
2022-05-25 17:29:06 -07:00
|
|
|
space: 165,
|
|
|
|
programId: TOKEN_PROGRAM_ID,
|
|
|
|
}),
|
2023-02-21 23:36:59 -08:00
|
|
|
createInitializeAccount3Instruction(
|
2023-08-11 00:35:57 -07:00
|
|
|
wrappedSolAccount,
|
2023-02-21 23:36:59 -08:00
|
|
|
NATIVE_MINT,
|
|
|
|
mangoAccount.owner,
|
|
|
|
),
|
2022-05-25 17:29:06 -07:00
|
|
|
];
|
|
|
|
postInstructions = [
|
2023-02-21 23:36:59 -08:00
|
|
|
createCloseAccountInstruction(
|
2023-08-11 00:35:57 -07:00
|
|
|
wrappedSolAccount,
|
2023-02-21 23:36:59 -08:00
|
|
|
mangoAccount.owner,
|
|
|
|
mangoAccount.owner,
|
|
|
|
),
|
2022-05-25 17:29:06 -07:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2023-04-24 05:48:53 -07:00
|
|
|
this.buildHealthRemainingAccounts(group, [mangoAccount], [bank], []);
|
2022-04-07 09:58:42 -07:00
|
|
|
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2023-01-04 00:24:40 -08:00
|
|
|
.tokenDeposit(new BN(nativeAmount), reduceOnly)
|
2022-04-07 12:00:08 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
2022-09-30 04:21:01 -07:00
|
|
|
owner: mangoAccount.owner,
|
2022-04-07 12:00:08 -07:00
|
|
|
bank: bank.publicKey,
|
|
|
|
vault: bank.vault,
|
2022-08-22 02:02:01 -07:00
|
|
|
oracle: bank.oracle,
|
2023-08-11 00:35:57 -07:00
|
|
|
tokenAccount: wrappedSolAccount ?? tokenAccountPk,
|
2022-08-03 09:05:16 -07:00
|
|
|
tokenAuthority: mangoAccount.owner,
|
2022-04-07 12:00:08 -07:00
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-08-15 21:27:06 -07:00
|
|
|
|
2023-08-11 00:35:57 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
...preInstructions,
|
|
|
|
ix,
|
|
|
|
...postInstructions,
|
|
|
|
]);
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async tokenWithdraw(
|
2022-04-07 09:58:42 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2022-04-07 09:58:42 -07:00
|
|
|
amount: number,
|
|
|
|
allowBorrow: boolean,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-30 06:07:43 -07:00
|
|
|
const nativeAmount = toNative(amount, group.getMintDecimals(mintPk));
|
2023-01-12 16:38:02 -08:00
|
|
|
const ixes = await this.tokenWithdrawNativeIx(
|
2022-08-08 07:31:38 -07:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk,
|
2022-08-08 07:31:38 -07:00
|
|
|
nativeAmount,
|
|
|
|
allowBorrow,
|
2022-04-07 09:58:42 -07:00
|
|
|
);
|
2023-01-12 16:38:02 -08:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixes);
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
2023-01-12 16:38:02 -08:00
|
|
|
public async tokenWithdrawNativeIx(
|
2022-07-04 03:09:33 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
mintPk: PublicKey,
|
2022-09-30 06:07:43 -07:00
|
|
|
nativeAmount: BN,
|
2022-07-04 03:09:33 -07:00
|
|
|
allowBorrow: boolean,
|
2023-01-12 16:38:02 -08:00
|
|
|
): Promise<TransactionInstruction[]> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const bank = group.getFirstBankByMint(mintPk);
|
2022-07-04 03:09:33 -07:00
|
|
|
|
|
|
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
|
|
|
bank.mint,
|
|
|
|
mangoAccount.owner,
|
2023-07-25 04:35:25 -07:00
|
|
|
true,
|
2022-07-04 03:09:33 -07:00
|
|
|
);
|
|
|
|
|
2022-09-01 03:11:31 -07:00
|
|
|
// ensure withdraws don't fail with missing ATAs
|
2022-09-01 02:25:58 -07:00
|
|
|
const preInstructions: TransactionInstruction[] = [
|
|
|
|
await createAssociatedTokenAccountIdempotentInstruction(
|
|
|
|
mangoAccount.owner,
|
|
|
|
mangoAccount.owner,
|
|
|
|
bank.mint,
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
const postInstructions: TransactionInstruction[] = [];
|
2023-02-21 23:36:59 -08:00
|
|
|
if (mintPk.equals(NATIVE_MINT)) {
|
2022-09-01 02:25:58 -07:00
|
|
|
postInstructions.push(
|
2023-02-21 23:36:59 -08:00
|
|
|
createCloseAccountInstruction(
|
|
|
|
tokenAccountPk,
|
|
|
|
mangoAccount.owner,
|
|
|
|
mangoAccount.owner,
|
|
|
|
),
|
2022-09-01 02:25:58 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-04 03:09:33 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2023-04-24 05:48:53 -07:00
|
|
|
this.buildHealthRemainingAccounts(group, [mangoAccount], [bank], [], []);
|
2022-07-04 03:09:33 -07:00
|
|
|
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-07-04 03:09:33 -07:00
|
|
|
.tokenWithdraw(new BN(nativeAmount), allowBorrow)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
2022-10-08 03:48:13 -07:00
|
|
|
owner: mangoAccount.owner,
|
2022-07-04 03:09:33 -07:00
|
|
|
bank: bank.publicKey,
|
|
|
|
vault: bank.vault,
|
2022-08-22 02:02:01 -07:00
|
|
|
oracle: bank.oracle,
|
2022-07-04 03:09:33 -07:00
|
|
|
tokenAccount: tokenAccountPk,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
2023-04-24 05:48:53 -07:00
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
),
|
2022-07-04 03:09:33 -07:00
|
|
|
)
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-08-12 17:15:12 -07:00
|
|
|
|
2023-01-12 16:38:02 -08:00
|
|
|
return [...preInstructions, ix, ...postInstructions];
|
2022-07-04 03:09:33 -07:00
|
|
|
}
|
|
|
|
|
2023-02-02 06:08:57 -08:00
|
|
|
public async tokenWithdrawNative(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
nativeAmount: BN,
|
|
|
|
allowBorrow: boolean,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 06:08:57 -08:00
|
|
|
const ixs = await this.tokenWithdrawNativeIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
mintPk,
|
|
|
|
nativeAmount,
|
|
|
|
allowBorrow,
|
|
|
|
);
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixs);
|
2023-02-02 06:08:57 -08:00
|
|
|
}
|
|
|
|
|
2022-04-07 23:29:35 -07:00
|
|
|
// Serum
|
|
|
|
|
|
|
|
public async serum3RegisterMarket(
|
|
|
|
group: Group,
|
|
|
|
serum3MarketExternalPk: PublicKey,
|
|
|
|
baseBank: Bank,
|
|
|
|
quoteBank: Bank,
|
|
|
|
marketIndex: number,
|
2022-04-12 07:19:58 -07:00
|
|
|
name: string,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-04-12 07:19:58 -07:00
|
|
|
.serum3RegisterMarket(marketIndex, name)
|
2022-04-07 23:29:35 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-12-08 01:16:06 -08:00
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
2022-04-07 23:29:35 -07:00
|
|
|
serumMarketExternal: serum3MarketExternalPk,
|
|
|
|
baseBank: baseBank.publicKey,
|
|
|
|
quoteBank: quoteBank.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 23:29:35 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 23:29:35 -07:00
|
|
|
}
|
|
|
|
|
2023-04-24 23:12:42 -07:00
|
|
|
public async serum3EditMarket(
|
|
|
|
group: Group,
|
|
|
|
serum3MarketIndex: MarketIndex,
|
|
|
|
reduceOnly: boolean | null,
|
|
|
|
forceClose: boolean | null,
|
2023-06-21 06:38:27 -07:00
|
|
|
name: string | null,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-24 23:12:42 -07:00
|
|
|
const serum3Market =
|
|
|
|
group.serum3MarketsMapByMarketIndex.get(serum3MarketIndex);
|
|
|
|
const ix = await this.program.methods
|
2023-06-21 06:38:27 -07:00
|
|
|
.serum3EditMarket(reduceOnly, forceClose, name)
|
2023-04-24 23:12:42 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
market: serum3Market?.publicKey,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
public async serum3deregisterMarket(
|
|
|
|
group: Group,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-31 02:36:44 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-06-09 09:27:31 -07:00
|
|
|
|
2022-09-20 02:41:15 -07:00
|
|
|
const marketIndexBuf = Buffer.alloc(2);
|
|
|
|
marketIndexBuf.writeUInt16LE(serum3Market.marketIndex);
|
|
|
|
const [indexReservation] = await PublicKey.findProgramAddress(
|
|
|
|
[Buffer.from('Serum3Index'), group.publicKey.toBuffer(), marketIndexBuf],
|
|
|
|
this.program.programId,
|
|
|
|
);
|
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-06-09 09:27:31 -07:00
|
|
|
.serum3DeregisterMarket()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
2022-09-20 02:41:15 -07:00
|
|
|
indexReservation,
|
2022-06-09 09:27:31 -07:00
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-06-11 04:49:45 -07:00
|
|
|
public async serum3GetMarkets(
|
2022-04-07 23:29:35 -07:00
|
|
|
group: Group,
|
2022-04-08 03:30:21 -07:00
|
|
|
baseTokenIndex?: number,
|
|
|
|
quoteTokenIndex?: number,
|
2022-04-07 23:29:35 -07:00
|
|
|
): Promise<Serum3Market[]> {
|
|
|
|
const bumpfbuf = Buffer.alloc(1);
|
|
|
|
bumpfbuf.writeUInt8(255);
|
|
|
|
|
2022-04-08 03:30:21 -07:00
|
|
|
const filters: MemcmpFilter[] = [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 8,
|
2022-04-07 23:29:35 -07:00
|
|
|
},
|
2022-04-08 03:30:21 -07:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
if (baseTokenIndex) {
|
|
|
|
const bbuf = Buffer.alloc(2);
|
|
|
|
bbuf.writeUInt16LE(baseTokenIndex);
|
|
|
|
filters.push({
|
|
|
|
memcmp: {
|
|
|
|
bytes: bs58.encode(bbuf),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 40,
|
2022-04-07 23:29:35 -07:00
|
|
|
},
|
2022-04-08 03:30:21 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (quoteTokenIndex) {
|
|
|
|
const qbuf = Buffer.alloc(2);
|
|
|
|
qbuf.writeUInt16LE(quoteTokenIndex);
|
|
|
|
filters.push({
|
|
|
|
memcmp: {
|
|
|
|
bytes: bs58.encode(qbuf),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 42,
|
2022-04-07 23:29:35 -07:00
|
|
|
},
|
2022-04-08 03:30:21 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return (await this.program.account.serum3Market.all(filters)).map((tuple) =>
|
|
|
|
Serum3Market.from(tuple.publicKey, tuple.account),
|
|
|
|
);
|
2022-04-07 23:29:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async serum3CreateOpenOrders(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-09-01 01:48:50 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-01 01:48:50 -07:00
|
|
|
const serum3Market: Serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-04-08 03:30:21 -07:00
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-04-07 23:29:35 -07:00
|
|
|
.serum3CreateOpenOrders()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
|
|
|
serumProgram: serum3Market.serumProgram,
|
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-04-07 23:29:35 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-07 23:29:35 -07:00
|
|
|
}
|
|
|
|
|
2023-01-04 23:30:15 -08:00
|
|
|
public async serum3CreateOpenOrdersIx(
|
2022-06-09 09:27:31 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2023-01-04 23:30:15 -08:00
|
|
|
): 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> {
|
2022-08-31 02:36:44 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-06-09 09:27:31 -07:00
|
|
|
|
|
|
|
return await this.program.methods
|
|
|
|
.serum3CloseOpenOrders()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
|
|
|
serumProgram: serum3Market.serumProgram,
|
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
2023-04-19 09:15:39 -07:00
|
|
|
openOrders: await serum3Market.findOoPda(
|
|
|
|
this.programId,
|
|
|
|
mangoAccount.publicKey,
|
|
|
|
),
|
2022-06-09 09:27:31 -07:00
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-01-04 23:30:15 -08:00
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async serum3CloseOpenOrders(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-01-04 23:30:15 -08:00
|
|
|
const ix = await this.serum3CloseOpenOrdersIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
);
|
|
|
|
|
|
|
|
return await sendTransaction(
|
|
|
|
this.program.provider as AnchorProvider,
|
|
|
|
[ix],
|
|
|
|
group.addressLookupTablesList,
|
|
|
|
{
|
|
|
|
postSendTxCallback: this.postSendTxCallback,
|
|
|
|
},
|
|
|
|
);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2023-04-24 23:12:42 -07:00
|
|
|
public async serum3LiqForceCancelOrders(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
limit?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-24 23:12:42 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
|
|
|
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
|
|
|
const openOrders = await serum3Market.findOoPda(
|
|
|
|
this.programId,
|
|
|
|
mangoAccount.publicKey,
|
|
|
|
);
|
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
[[serum3Market, openOrders]],
|
|
|
|
);
|
|
|
|
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.serum3LiqForceCancelOrders(limit ?? 10)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
openOrders,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
|
|
|
marketBids: serum3MarketExternal.bidsAddress,
|
|
|
|
marketAsks: serum3MarketExternal.asksAddress,
|
|
|
|
marketEventQueue: serum3MarketExternal.decoded.eventQueue,
|
|
|
|
marketBaseVault: serum3MarketExternal.decoded.baseVault,
|
|
|
|
marketQuoteVault: serum3MarketExternal.decoded.quoteVault,
|
|
|
|
marketVaultSigner: await generateSerum3MarketExternalVaultSignerAddress(
|
|
|
|
this.cluster,
|
|
|
|
serum3Market,
|
|
|
|
serum3MarketExternal,
|
|
|
|
),
|
|
|
|
quoteBank: group.getFirstBankByTokenIndex(serum3Market.quoteTokenIndex)
|
|
|
|
.publicKey,
|
|
|
|
quoteVault: group.getFirstBankByTokenIndex(serum3Market.quoteTokenIndex)
|
|
|
|
.vault,
|
|
|
|
baseBank: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
|
|
|
.publicKey,
|
|
|
|
baseVault: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
|
|
|
.vault,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-12-15 01:40:45 -08:00
|
|
|
public async serum3PlaceOrderIx(
|
2022-04-08 03:30:21 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2022-04-08 03:30:21 -07:00
|
|
|
side: Serum3Side,
|
2022-04-08 07:57:37 -07:00
|
|
|
price: number,
|
|
|
|
size: number,
|
2022-04-08 03:30:21 -07:00
|
|
|
selfTradeBehavior: Serum3SelfTradeBehavior,
|
|
|
|
orderType: Serum3OrderType,
|
|
|
|
clientOrderId: number,
|
|
|
|
limit: number,
|
2023-01-04 23:30:15 -08:00
|
|
|
): Promise<TransactionInstruction[]> {
|
|
|
|
const ixs: TransactionInstruction[] = [];
|
2022-08-31 02:36:44 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2023-01-04 23:30:15 -08:00
|
|
|
|
2023-01-07 14:06:35 -08:00
|
|
|
let openOrderPk: PublicKey | undefined = undefined;
|
|
|
|
const banks: Bank[] = [];
|
|
|
|
const openOrdersForMarket: [Serum3Market, PublicKey][] = [];
|
2022-09-29 06:51:09 -07:00
|
|
|
if (!mangoAccount.getSerum3Account(serum3Market.marketIndex)) {
|
2023-01-04 23:30:15 -08:00
|
|
|
const ix = await this.serum3CreateOpenOrdersIx(
|
2022-09-01 01:48:50 -07:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
serum3Market.serumMarketExternal,
|
|
|
|
);
|
2023-01-07 14:06:35 -08:00
|
|
|
ixs.push(ix);
|
|
|
|
openOrderPk = await serum3Market.findOoPda(
|
2023-01-04 23:30:15 -08:00
|
|
|
this.program.programId,
|
|
|
|
mangoAccount.publicKey,
|
2022-04-08 03:30:21 -07:00
|
|
|
);
|
2023-01-07 14:06:35 -08:00
|
|
|
openOrdersForMarket.push([serum3Market, openOrderPk]);
|
2023-01-07 14:23:58 -08:00
|
|
|
const baseTokenIndex = serum3Market.baseTokenIndex;
|
|
|
|
const quoteTokenIndex = serum3Market.quoteTokenIndex;
|
|
|
|
// only include banks if no deposit has been previously made for same token
|
2023-04-25 02:13:40 -07:00
|
|
|
banks.push(group.getFirstBankByTokenIndex(quoteTokenIndex));
|
|
|
|
banks.push(group.getFirstBankByTokenIndex(baseTokenIndex));
|
2023-01-04 23:30:15 -08:00
|
|
|
}
|
2022-04-08 03:30:21 -07:00
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2022-08-31 02:36:44 -07:00
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
2023-01-07 14:06:35 -08:00
|
|
|
banks,
|
2022-08-31 02:36:44 -07:00
|
|
|
[],
|
2023-01-07 14:06:35 -08:00
|
|
|
openOrdersForMarket,
|
2023-01-04 23:30:15 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
|
|
|
const serum3MarketExternalVaultSigner =
|
|
|
|
await generateSerum3MarketExternalVaultSignerAddress(
|
|
|
|
this.cluster,
|
|
|
|
serum3Market,
|
|
|
|
serum3MarketExternal,
|
2022-08-31 02:36:44 -07:00
|
|
|
);
|
2022-04-08 03:30:21 -07:00
|
|
|
|
2022-04-08 07:57:37 -07:00
|
|
|
const limitPrice = serum3MarketExternal.priceNumberToLots(price);
|
|
|
|
const maxBaseQuantity = serum3MarketExternal.baseSizeNumberToLots(size);
|
2022-12-21 01:21:24 -08:00
|
|
|
const isTaker = orderType !== Serum3OrderType.postOnly;
|
2022-12-17 10:01:57 -08:00
|
|
|
const maxQuoteQuantity = new BN(
|
2023-05-07 23:13:50 -07:00
|
|
|
Math.ceil(
|
|
|
|
serum3MarketExternal.decoded.quoteLotSize.toNumber() *
|
|
|
|
(1 + Math.max(serum3Market.getFeeRates(isTaker), 0)) *
|
|
|
|
serum3MarketExternal.baseSizeNumberToLots(size).toNumber() *
|
|
|
|
serum3MarketExternal.priceNumberToLots(price).toNumber(),
|
|
|
|
),
|
2022-12-17 10:01:57 -08:00
|
|
|
);
|
2022-12-17 11:08:48 -08:00
|
|
|
|
2022-09-29 06:51:09 -07:00
|
|
|
const payerTokenIndex = ((): TokenIndex => {
|
2022-08-30 00:55:19 -07:00
|
|
|
if (side == Serum3Side.bid) {
|
|
|
|
return serum3Market.quoteTokenIndex;
|
|
|
|
} else {
|
|
|
|
return serum3Market.baseTokenIndex;
|
|
|
|
}
|
|
|
|
})();
|
2022-04-08 07:57:37 -07:00
|
|
|
|
2022-12-09 04:41:43 -08:00
|
|
|
const payerBank = group.getFirstBankByTokenIndex(payerTokenIndex);
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-04-08 03:30:21 -07:00
|
|
|
.serum3PlaceOrder(
|
|
|
|
side,
|
2022-04-08 07:57:37 -07:00
|
|
|
limitPrice,
|
|
|
|
maxBaseQuantity,
|
|
|
|
maxQuoteQuantity,
|
2022-04-08 03:30:21 -07:00
|
|
|
selfTradeBehavior,
|
|
|
|
orderType,
|
|
|
|
new BN(clientOrderId),
|
|
|
|
limit,
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2023-01-04 23:30:15 -08:00
|
|
|
openOrders:
|
2023-01-07 14:06:35 -08:00
|
|
|
openOrderPk ||
|
2023-01-04 23:30:15 -08:00
|
|
|
mangoAccount.getSerum3Account(serum3Market.marketIndex)?.openOrders,
|
2022-04-08 03:30:21 -07:00
|
|
|
serumMarket: serum3Market.publicKey,
|
2022-12-08 01:16:06 -08:00
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
2022-04-08 03:30:21 -07:00
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
|
|
|
marketBids: serum3MarketExternal.bidsAddress,
|
|
|
|
marketAsks: serum3MarketExternal.asksAddress,
|
|
|
|
marketEventQueue: serum3MarketExternal.decoded.eventQueue,
|
|
|
|
marketRequestQueue: serum3MarketExternal.decoded.requestQueue,
|
|
|
|
marketBaseVault: serum3MarketExternal.decoded.baseVault,
|
|
|
|
marketQuoteVault: serum3MarketExternal.decoded.quoteVault,
|
|
|
|
marketVaultSigner: serum3MarketExternalVaultSigner,
|
2022-12-09 04:41:43 -08:00
|
|
|
payerBank: payerBank.publicKey,
|
|
|
|
payerVault: payerBank.vault,
|
|
|
|
payerOracle: payerBank.oracle,
|
2022-04-08 03:30:21 -07:00
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-09-13 22:44:00 -07:00
|
|
|
|
2023-01-04 23:30:15 -08:00
|
|
|
ixs.push(ix);
|
|
|
|
|
|
|
|
return ixs;
|
2022-12-15 01:40:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public async serum3PlaceOrder(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
side: Serum3Side,
|
|
|
|
price: number,
|
|
|
|
size: number,
|
|
|
|
selfTradeBehavior: Serum3SelfTradeBehavior,
|
|
|
|
orderType: Serum3OrderType,
|
|
|
|
clientOrderId: number,
|
|
|
|
limit: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-01-04 23:30:15 -08:00
|
|
|
const placeOrderIxes = await this.serum3PlaceOrderIx(
|
2022-12-15 01:40:45 -08:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
side,
|
|
|
|
price,
|
|
|
|
size,
|
|
|
|
selfTradeBehavior,
|
|
|
|
orderType,
|
|
|
|
clientOrderId,
|
|
|
|
limit,
|
|
|
|
);
|
2023-04-19 09:15:39 -07:00
|
|
|
|
2023-01-04 23:30:15 -08:00
|
|
|
const settleIx = await this.serum3SettleFundsIx(
|
2022-12-27 15:50:29 -08:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
);
|
|
|
|
|
2023-04-19 09:15:39 -07:00
|
|
|
const ixs = [...placeOrderIxes, settleIx];
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixs);
|
2022-04-08 03:30:21 -07:00
|
|
|
}
|
|
|
|
|
2023-04-19 09:15:39 -07:00
|
|
|
public async serum3CancelAllOrdersIx(
|
2022-06-09 09:27:31 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2022-11-04 08:07:26 -07:00
|
|
|
limit?: number,
|
2023-04-19 09:15:39 -07:00
|
|
|
): Promise<TransactionInstruction> {
|
2022-08-31 02:36:44 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-06-09 09:27:31 -07:00
|
|
|
|
2022-09-29 06:51:09 -07:00
|
|
|
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-06-09 09:27:31 -07:00
|
|
|
|
2023-04-19 09:15:39 -07:00
|
|
|
return await this.program.methods
|
2022-11-04 08:07:26 -07:00
|
|
|
.serum3CancelAllOrders(limit ? limit : 10)
|
2022-06-09 09:27:31 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2023-04-19 09:15:39 -07:00
|
|
|
openOrders: await serum3Market.findOoPda(
|
|
|
|
this.programId,
|
|
|
|
mangoAccount.publicKey,
|
|
|
|
),
|
2022-06-09 09:27:31 -07:00
|
|
|
serumMarket: serum3Market.publicKey,
|
2022-12-08 01:16:06 -08:00
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
2022-06-09 09:27:31 -07:00
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
|
|
|
marketBids: serum3MarketExternal.bidsAddress,
|
|
|
|
marketAsks: serum3MarketExternal.asksAddress,
|
|
|
|
marketEventQueue: serum3MarketExternal.decoded.eventQueue,
|
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2023-04-19 09:15:39 -07:00
|
|
|
}
|
2022-09-13 22:44:00 -07:00
|
|
|
|
2023-04-19 09:15:39 -07:00
|
|
|
public async serum3CancelAllOrders(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
limit?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-19 09:15:39 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.serum3CancelAllOrdersIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
limit,
|
|
|
|
),
|
|
|
|
]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-12-15 04:41:45 -08:00
|
|
|
public async serum3SettleFundsIx(
|
2022-04-08 11:47:12 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2022-12-15 04:41:45 -08:00
|
|
|
): Promise<TransactionInstruction> {
|
2023-03-03 05:04:45 -08:00
|
|
|
if (this.openbookFeesToDao == false) {
|
|
|
|
throw new Error(
|
|
|
|
`openbookFeesToDao is set to false, please use serum3SettleFundsV2Ix`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-15 08:34:56 -07:00
|
|
|
return await this.serum3SettleFundsV2Ix(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
);
|
2022-12-15 04:41:45 -08:00
|
|
|
}
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
public async serum3SettleFundsV2Ix(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
|
|
|
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
|
|
|
|
|
|
|
const [serum3MarketExternalVaultSigner, openOrderPublicKey] =
|
|
|
|
await Promise.all([
|
|
|
|
generateSerum3MarketExternalVaultSignerAddress(
|
|
|
|
this.cluster,
|
|
|
|
serum3Market,
|
|
|
|
serum3MarketExternal,
|
|
|
|
),
|
|
|
|
serum3Market.findOoPda(this.program.programId, mangoAccount.publicKey),
|
|
|
|
]);
|
|
|
|
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.serum3SettleFundsV2(this.openbookFeesToDao)
|
|
|
|
.accounts({
|
|
|
|
v1: {
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
openOrders: openOrderPublicKey,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
|
|
|
marketBaseVault: serum3MarketExternal.decoded.baseVault,
|
|
|
|
marketQuoteVault: serum3MarketExternal.decoded.quoteVault,
|
|
|
|
marketVaultSigner: serum3MarketExternalVaultSigner,
|
|
|
|
quoteBank: group.getFirstBankByTokenIndex(
|
|
|
|
serum3Market.quoteTokenIndex,
|
|
|
|
).publicKey,
|
|
|
|
quoteVault: group.getFirstBankByTokenIndex(
|
|
|
|
serum3Market.quoteTokenIndex,
|
|
|
|
).vault,
|
|
|
|
baseBank: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
|
|
|
.publicKey,
|
|
|
|
baseVault: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
|
|
|
.vault,
|
|
|
|
},
|
|
|
|
v2: {
|
|
|
|
quoteOracle: group.getFirstBankByTokenIndex(
|
|
|
|
serum3Market.quoteTokenIndex,
|
|
|
|
).oracle,
|
|
|
|
baseOracle: group.getFirstBankByTokenIndex(
|
|
|
|
serum3Market.baseTokenIndex,
|
|
|
|
).oracle,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
|
|
|
|
return ix;
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:41:45 -08:00
|
|
|
public async serum3SettleFunds(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-03-03 05:04:45 -08:00
|
|
|
const ix = await this.serum3SettleFundsV2Ix(
|
2022-12-15 04:41:45 -08:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-04-08 11:47:12 -07:00
|
|
|
}
|
|
|
|
|
2022-12-15 01:40:45 -08:00
|
|
|
public async serum3CancelOrderIx(
|
2022-04-08 07:57:37 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk: PublicKey,
|
2022-04-08 07:57:37 -07:00
|
|
|
side: Serum3Side,
|
|
|
|
orderId: BN,
|
2022-12-15 01:40:45 -08:00
|
|
|
): Promise<TransactionInstruction> {
|
2022-08-31 02:36:44 -07:00
|
|
|
const serum3Market = group.serum3MarketsMapByExternal.get(
|
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-04-08 07:57:37 -07:00
|
|
|
|
2022-09-29 06:51:09 -07:00
|
|
|
const serum3MarketExternal = group.serum3ExternalMarketsMap.get(
|
2022-08-31 02:36:44 -07:00
|
|
|
externalMarketPk.toBase58(),
|
|
|
|
)!;
|
2022-06-11 04:49:45 -07:00
|
|
|
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-04-08 07:57:37 -07:00
|
|
|
.serum3CancelOrder(side, orderId)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
2022-09-29 06:51:09 -07:00
|
|
|
openOrders: mangoAccount.getSerum3Account(serum3Market.marketIndex)
|
2022-04-08 07:57:37 -07:00
|
|
|
?.openOrders,
|
|
|
|
serumMarket: serum3Market.publicKey,
|
2022-12-08 01:16:06 -08:00
|
|
|
serumProgram: OPENBOOK_PROGRAM_ID[this.cluster],
|
2022-04-08 07:57:37 -07:00
|
|
|
serumMarketExternal: serum3Market.serumMarketExternal,
|
|
|
|
marketBids: serum3MarketExternal.bidsAddress,
|
|
|
|
marketAsks: serum3MarketExternal.asksAddress,
|
|
|
|
marketEventQueue: serum3MarketExternal.decoded.eventQueue,
|
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-09-13 22:44:00 -07:00
|
|
|
|
2022-12-15 01:40:45 -08:00
|
|
|
return ix;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async serum3CancelOrder(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
side: Serum3Side,
|
|
|
|
orderId: BN,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-12-15 11:06:10 -08:00
|
|
|
const ixes = await Promise.all([
|
|
|
|
this.serum3CancelOrderIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
side,
|
|
|
|
orderId,
|
|
|
|
),
|
2023-03-03 05:04:45 -08:00
|
|
|
this.serum3SettleFundsV2Ix(group, mangoAccount, externalMarketPk),
|
2022-12-15 11:06:10 -08:00
|
|
|
]);
|
2022-12-15 01:40:45 -08:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixes);
|
2022-04-08 07:57:37 -07:00
|
|
|
}
|
|
|
|
|
2022-05-11 04:33:01 -07:00
|
|
|
/// perps
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpCreateMarket(
|
2022-05-11 04:33:01 -07:00
|
|
|
group: Group,
|
|
|
|
oraclePk: PublicKey,
|
|
|
|
perpMarketIndex: number,
|
|
|
|
name: string,
|
2022-11-09 04:25:53 -08:00
|
|
|
oracleConfig: OracleConfigParams,
|
2022-09-21 00:42:45 -07:00
|
|
|
baseDecimals: number,
|
2022-05-11 04:33:01 -07:00
|
|
|
quoteLotSize: number,
|
|
|
|
baseLotSize: number,
|
2023-01-16 07:49:09 -08:00
|
|
|
maintBaseAssetWeight: number,
|
|
|
|
initBaseAssetWeight: number,
|
|
|
|
maintBaseLiabWeight: number,
|
|
|
|
initBaseLiabWeight: number,
|
2023-02-01 07:15:45 -08:00
|
|
|
maintOverallAssetWeight: number,
|
|
|
|
initOverallAssetWeight: number,
|
2023-02-02 00:00:37 -08:00
|
|
|
baseLiquidationFee: number,
|
2022-05-11 04:33:01 -07:00
|
|
|
makerFee: number,
|
|
|
|
takerFee: number,
|
2022-09-22 10:03:45 -07:00
|
|
|
feePenalty: number,
|
2022-05-17 06:06:29 -07:00
|
|
|
minFunding: number,
|
|
|
|
maxFunding: number,
|
|
|
|
impactQuantity: number,
|
2022-09-12 06:25:50 -07:00
|
|
|
groupInsuranceFund: boolean,
|
2022-09-29 03:59:55 -07:00
|
|
|
settleFeeFlat: number,
|
|
|
|
settleFeeAmountThreshold: number,
|
|
|
|
settleFeeFractionLowHealth: number,
|
2022-09-29 05:13:28 -07:00
|
|
|
settleTokenIndex: number,
|
2022-12-02 03:24:11 -08:00
|
|
|
settlePnlLimitFactor: number,
|
|
|
|
settlePnlLimitWindowSize: number,
|
2023-02-02 00:00:37 -08:00
|
|
|
positivePnlLiquidationFee: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-05-11 04:33:01 -07:00
|
|
|
const bids = new Keypair();
|
|
|
|
const asks = new Keypair();
|
|
|
|
const eventQueue = new Keypair();
|
|
|
|
|
2022-09-30 04:33:21 -07:00
|
|
|
const bookSideSize = (this.program as any)._coder.accounts.size(
|
|
|
|
(this.program.account.bookSide as any)._idlAccount,
|
|
|
|
);
|
|
|
|
const eventQueueSize = (this.program as any)._coder.accounts.size(
|
|
|
|
(this.program.account.eventQueue as any)._idlAccount,
|
|
|
|
);
|
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-05-11 04:33:01 -07:00
|
|
|
.perpCreateMarket(
|
|
|
|
perpMarketIndex,
|
|
|
|
name,
|
2022-11-09 04:25:53 -08:00
|
|
|
oracleConfig,
|
2022-09-21 00:42:45 -07:00
|
|
|
baseDecimals,
|
2022-05-11 04:33:01 -07:00
|
|
|
new BN(quoteLotSize),
|
|
|
|
new BN(baseLotSize),
|
2023-01-16 07:49:09 -08:00
|
|
|
maintBaseAssetWeight,
|
|
|
|
initBaseAssetWeight,
|
|
|
|
maintBaseLiabWeight,
|
|
|
|
initBaseLiabWeight,
|
2023-02-01 07:15:45 -08:00
|
|
|
maintOverallAssetWeight,
|
|
|
|
initOverallAssetWeight,
|
2023-02-02 00:00:37 -08:00
|
|
|
baseLiquidationFee,
|
2022-05-11 04:33:01 -07:00
|
|
|
makerFee,
|
|
|
|
takerFee,
|
2022-05-17 06:06:29 -07:00
|
|
|
minFunding,
|
|
|
|
maxFunding,
|
|
|
|
new BN(impactQuantity),
|
2022-09-12 06:25:50 -07:00
|
|
|
groupInsuranceFund,
|
2022-09-27 06:13:53 -07:00
|
|
|
feePenalty,
|
2022-09-29 03:59:55 -07:00
|
|
|
settleFeeFlat,
|
|
|
|
settleFeeAmountThreshold,
|
|
|
|
settleFeeFractionLowHealth,
|
2022-09-29 05:13:28 -07:00
|
|
|
settleTokenIndex,
|
2022-12-02 03:24:11 -08:00
|
|
|
settlePnlLimitFactor,
|
|
|
|
new BN(settlePnlLimitWindowSize),
|
2023-02-02 00:00:37 -08:00
|
|
|
positivePnlLiquidationFee,
|
2022-05-11 04:33:01 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-05-11 04:33:01 -07:00
|
|
|
oracle: oraclePk,
|
|
|
|
bids: bids.publicKey,
|
|
|
|
asks: asks.publicKey,
|
|
|
|
eventQueue: eventQueue.publicKey,
|
2022-05-18 08:16:14 -07:00
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-05-11 04:33:01 -07:00
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
|
|
|
const preInstructions = [
|
|
|
|
// book sides
|
|
|
|
SystemProgram.createAccount({
|
|
|
|
programId: this.program.programId,
|
|
|
|
space: bookSideSize,
|
|
|
|
lamports:
|
|
|
|
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
|
|
|
bookSideSize,
|
|
|
|
),
|
|
|
|
fromPubkey: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
newAccountPubkey: bids.publicKey,
|
|
|
|
}),
|
|
|
|
SystemProgram.createAccount({
|
|
|
|
programId: this.program.programId,
|
|
|
|
space: bookSideSize,
|
|
|
|
lamports:
|
|
|
|
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
|
|
|
bookSideSize,
|
|
|
|
),
|
|
|
|
fromPubkey: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
newAccountPubkey: asks.publicKey,
|
|
|
|
}),
|
|
|
|
// event queue
|
|
|
|
SystemProgram.createAccount({
|
|
|
|
programId: this.program.programId,
|
|
|
|
space: eventQueueSize,
|
|
|
|
lamports:
|
|
|
|
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
|
|
|
eventQueueSize,
|
|
|
|
),
|
|
|
|
fromPubkey: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
newAccountPubkey: eventQueue.publicKey,
|
|
|
|
}),
|
|
|
|
];
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
2023-02-02 05:23:22 -08:00
|
|
|
[...preInstructions, ix],
|
|
|
|
{
|
|
|
|
additionalSigners: [bids, asks, eventQueue],
|
|
|
|
},
|
|
|
|
);
|
2022-05-11 04:33:01 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpEditMarket(
|
2022-07-05 10:31:47 -07:00
|
|
|
group: Group,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-01-14 05:09:26 -08:00
|
|
|
params: PerpEditParams,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-07-05 10:31:47 -07:00
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-07-05 10:31:47 -07:00
|
|
|
.perpEditMarket(
|
2023-01-14 05:09:26 -08:00
|
|
|
params.oracle,
|
|
|
|
params.oracleConfig,
|
|
|
|
params.baseDecimals,
|
2023-01-16 07:49:09 -08:00
|
|
|
params.maintBaseAssetWeight,
|
|
|
|
params.initBaseAssetWeight,
|
|
|
|
params.maintBaseLiabWeight,
|
|
|
|
params.initBaseLiabWeight,
|
2023-02-01 07:15:45 -08:00
|
|
|
params.maintOverallAssetWeight,
|
|
|
|
params.initOverallAssetWeight,
|
2023-02-02 00:00:37 -08:00
|
|
|
params.baseLiquidationFee,
|
2023-01-14 05:09:26 -08:00
|
|
|
params.makerFee,
|
|
|
|
params.takerFee,
|
|
|
|
params.minFunding,
|
|
|
|
params.maxFunding,
|
|
|
|
params.impactQuantity !== null ? new BN(params.impactQuantity) : null,
|
|
|
|
params.groupInsuranceFund,
|
|
|
|
params.feePenalty,
|
|
|
|
params.settleFeeFlat,
|
|
|
|
params.settleFeeAmountThreshold,
|
|
|
|
params.settleFeeFractionLowHealth,
|
|
|
|
params.stablePriceDelayIntervalSeconds,
|
|
|
|
params.stablePriceDelayGrowthLimit,
|
|
|
|
params.stablePriceGrowthLimit,
|
|
|
|
params.settlePnlLimitFactor,
|
|
|
|
params.settlePnlLimitWindowSize !== null
|
|
|
|
? new BN(params.settlePnlLimitWindowSize)
|
2022-12-02 03:24:11 -08:00
|
|
|
: null,
|
2023-01-14 05:09:26 -08:00
|
|
|
params.reduceOnly,
|
2023-01-18 04:27:45 -08:00
|
|
|
params.resetStablePrice ?? false,
|
2023-02-02 00:00:37 -08:00
|
|
|
params.positivePnlLiquidationFee,
|
2023-03-03 01:05:12 -08:00
|
|
|
params.name,
|
2023-04-19 08:42:01 -07:00
|
|
|
params.forceClose,
|
2022-07-05 10:31:47 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
2023-01-14 05:09:26 -08:00
|
|
|
oracle: params.oracle ?? perpMarket.oracle,
|
2022-07-05 10:31:47 -07:00
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-07-05 10:31:47 -07:00
|
|
|
}
|
|
|
|
|
2023-04-24 23:12:42 -07:00
|
|
|
public async perpForceClosePosition(
|
|
|
|
group: Group,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
accountA: MangoAccount,
|
|
|
|
accountB: MangoAccount,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-24 23:12:42 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
|
|
|
|
const ix = await this.program.methods
|
|
|
|
.perpForceClosePosition()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
accountA: accountA.publicKey,
|
|
|
|
accountB: accountB.publicKey,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpCloseMarket(
|
2022-06-09 09:27:31 -07:00
|
|
|
group: Group,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-06-09 09:27:31 -07:00
|
|
|
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-06-09 09:27:31 -07:00
|
|
|
.perpCloseMarket()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
2022-12-07 12:03:28 -08:00
|
|
|
asks: perpMarket.asks,
|
2022-06-09 09:27:31 -07:00
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
solDestination: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-06-09 09:27:31 -07:00
|
|
|
}
|
|
|
|
|
2022-09-21 00:42:45 -07:00
|
|
|
public async perpGetMarkets(group: Group): Promise<PerpMarket[]> {
|
2022-05-11 04:33:01 -07:00
|
|
|
const bumpfbuf = Buffer.alloc(1);
|
|
|
|
bumpfbuf.writeUInt8(255);
|
|
|
|
|
|
|
|
const filters: MemcmpFilter[] = [
|
|
|
|
{
|
|
|
|
memcmp: {
|
|
|
|
bytes: group.publicKey.toBase58(),
|
2022-07-06 00:56:14 -07:00
|
|
|
offset: 8,
|
2022-05-11 04:33:01 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
return (await this.program.account.perpMarket.all(filters)).map((tuple) =>
|
|
|
|
PerpMarket.from(tuple.publicKey, tuple.account),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-01-13 04:19:04 -08:00
|
|
|
public async perpDeactivatePositionIx(
|
2022-09-15 00:57:48 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-01-13 04:19:04 -08:00
|
|
|
): Promise<TransactionInstruction> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-09-15 00:57:48 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2023-04-24 05:48:53 -07:00
|
|
|
this.buildHealthRemainingAccounts(group, [mangoAccount], [], []);
|
2022-09-15 00:57:48 -07:00
|
|
|
return await this.program.methods
|
|
|
|
.perpDeactivatePosition()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2023-01-13 04:19:04 -08:00
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async perpDeactivatePosition(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-01-13 04:19:04 -08:00
|
|
|
const ix = await this.perpDeactivatePositionIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
);
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-09-15 00:57:48 -07:00
|
|
|
}
|
|
|
|
|
2023-07-13 08:10:16 -07:00
|
|
|
public async perpCloseAll(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
slippage = 0.01, // 1%, 100bps
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-13 08:10:16 -07:00
|
|
|
if (mangoAccount.perpActive().length == 0) {
|
|
|
|
throw new Error(`No perp positions found.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mangoAccount.perpActive().length > 8) {
|
|
|
|
// Technically we can fit in 16, 1.6M CU, 100k CU per ix, but lets be conservative
|
|
|
|
throw new Error(
|
|
|
|
`Can't close more than 8 positions in one tx, due to compute usage limit.`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const hrix1 = await this.healthRegionBeginIx(group, mangoAccount);
|
|
|
|
const ixs = await Promise.all(
|
|
|
|
mangoAccount.perpActive().map(async (pa) => {
|
|
|
|
const pm = group.getPerpMarketByMarketIndex(pa.marketIndex);
|
|
|
|
const isLong = pa.basePositionLots.gt(new BN(0));
|
|
|
|
|
|
|
|
return await this.perpPlaceOrderV2Ix(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
pa.marketIndex,
|
|
|
|
isLong ? PerpOrderSide.ask : PerpOrderSide.bid,
|
|
|
|
pm.uiPrice * (isLong ? 1 - slippage : 1 + slippage), // Try to cross the spread to guarantee matching
|
2023-08-27 23:44:13 -07:00
|
|
|
Math.abs(pa.getBasePositionUi(pm) * 1.01), // Send a larger size to ensure full order is closed
|
2023-07-13 08:10:16 -07:00
|
|
|
undefined,
|
|
|
|
Date.now(),
|
|
|
|
PerpOrderType.immediateOrCancel,
|
|
|
|
PerpSelfTradeBehavior.decrementTake,
|
|
|
|
true, // Reduce only
|
|
|
|
undefined,
|
|
|
|
undefined,
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
const hrix2 = await this.healthRegionEndIx(group, mangoAccount);
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
|
|
|
[hrix1, ...ixs, hrix2],
|
|
|
|
{
|
|
|
|
prioritizationFee: true,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-01-25 00:03:35 -08:00
|
|
|
// perpPlaceOrder ix returns an optional, custom order id,
|
|
|
|
// but, since we use a customer tx sender, this method
|
|
|
|
// doesn't return it
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpPlaceOrder(
|
2022-05-11 04:33:01 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-09-23 02:43:26 -07:00
|
|
|
side: PerpOrderSide,
|
2022-06-02 10:30:39 -07:00
|
|
|
price: number,
|
|
|
|
quantity: number,
|
2022-11-21 10:34:41 -08:00
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-06-15 23:05:16 -07:00
|
|
|
const ix = await this.perpPlaceOrderV2Ix(
|
2023-01-02 12:35:39 -08:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
side,
|
|
|
|
price,
|
|
|
|
quantity,
|
|
|
|
maxQuoteQuantity,
|
|
|
|
clientOrderId,
|
|
|
|
orderType,
|
2023-06-15 23:05:16 -07:00
|
|
|
PerpSelfTradeBehavior.decrementTake,
|
2023-01-02 12:35:39 -08:00
|
|
|
reduceOnly,
|
|
|
|
expiryTimestamp,
|
|
|
|
limit,
|
|
|
|
);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-10-07 04:52:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async perpPlaceOrderIx(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
price: number,
|
|
|
|
quantity: number,
|
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
2022-11-09 00:59:34 -08:00
|
|
|
reduceOnly?: boolean,
|
2022-10-07 04:52:04 -07:00
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-06-02 10:30:39 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2022-08-31 02:36:44 -07:00
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
2022-09-21 03:50:10 -07:00
|
|
|
// Settlement token bank, because a position for it may be created
|
2023-02-28 03:05:02 -08:00
|
|
|
[group.getFirstBankForPerpSettlement()],
|
2022-08-31 02:36:44 -07:00
|
|
|
[perpMarket],
|
|
|
|
);
|
2022-10-07 04:52:04 -07:00
|
|
|
return await this.program.methods
|
2022-05-11 04:33:01 -07:00
|
|
|
.perpPlaceOrder(
|
|
|
|
side,
|
2022-09-20 03:57:01 -07:00
|
|
|
perpMarket.uiPriceToLots(price),
|
|
|
|
perpMarket.uiBaseToLots(quantity),
|
|
|
|
maxQuoteQuantity
|
|
|
|
? perpMarket.uiQuoteToLots(maxQuoteQuantity)
|
|
|
|
: I64_MAX_BN,
|
2022-10-07 04:52:04 -07:00
|
|
|
new BN(clientOrderId ? clientOrderId : Date.now()),
|
|
|
|
orderType ? orderType : PerpOrderType.limit,
|
2022-11-09 00:59:34 -08:00
|
|
|
reduceOnly ? reduceOnly : false,
|
2022-10-07 04:52:04 -07:00
|
|
|
new BN(expiryTimestamp ? expiryTimestamp : 0),
|
|
|
|
limit ? limit : 10,
|
2022-05-11 04:33:01 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
2022-12-07 12:03:28 -08:00
|
|
|
bids: perpMarket.bids,
|
2022-05-11 04:33:01 -07:00
|
|
|
asks: perpMarket.asks,
|
2022-11-21 10:34:41 -08:00
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2023-06-15 23:05:16 -07:00
|
|
|
public async perpPlaceOrderV2Ix(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
price: number,
|
|
|
|
quantity: number,
|
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
selfTradeBehavior?: PerpSelfTradeBehavior,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
|
|
|
// Settlement token bank, because a position for it may be created
|
|
|
|
[group.getFirstBankForPerpSettlement()],
|
|
|
|
[perpMarket],
|
|
|
|
);
|
|
|
|
return await this.program.methods
|
|
|
|
.perpPlaceOrderV2(
|
|
|
|
side,
|
|
|
|
perpMarket.uiPriceToLots(price),
|
|
|
|
perpMarket.uiBaseToLots(quantity),
|
|
|
|
maxQuoteQuantity
|
|
|
|
? perpMarket.uiQuoteToLots(maxQuoteQuantity)
|
|
|
|
: I64_MAX_BN,
|
|
|
|
new BN(clientOrderId ? clientOrderId : Date.now()),
|
|
|
|
orderType ?? PerpOrderType.limit,
|
|
|
|
selfTradeBehavior ?? PerpSelfTradeBehavior.decrementTake,
|
|
|
|
reduceOnly ?? false,
|
|
|
|
new BN(expiryTimestamp ? expiryTimestamp : 0),
|
|
|
|
limit ?? 10,
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
|
|
|
asks: perpMarket.asks,
|
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2022-11-21 10:34:41 -08:00
|
|
|
public async perpPlaceOrderPegged(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
priceOffset: number,
|
|
|
|
quantity: number,
|
2023-03-21 15:01:15 -07:00
|
|
|
pegLimit?: number,
|
2022-11-21 10:34:41 -08:00
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-06-15 23:10:52 -07:00
|
|
|
const ix = await this.perpPlaceOrderPeggedV2Ix(
|
2023-01-02 12:35:39 -08:00
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
side,
|
|
|
|
priceOffset,
|
|
|
|
quantity,
|
2023-03-21 15:01:15 -07:00
|
|
|
pegLimit,
|
2023-01-02 12:35:39 -08:00
|
|
|
maxQuoteQuantity,
|
|
|
|
clientOrderId,
|
|
|
|
orderType,
|
2023-06-15 23:05:16 -07:00
|
|
|
PerpSelfTradeBehavior.decrementTake,
|
2023-01-02 12:35:39 -08:00
|
|
|
reduceOnly,
|
|
|
|
expiryTimestamp,
|
|
|
|
limit,
|
|
|
|
);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-11-21 10:34:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public async perpPlaceOrderPeggedIx(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
priceOffset: number,
|
|
|
|
quantity: number,
|
2023-03-21 15:01:15 -07:00
|
|
|
pegLimit?: number,
|
2022-11-21 10:34:41 -08:00
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
|
|
|
// Settlement token bank, because a position for it may be created
|
2023-02-28 03:05:02 -08:00
|
|
|
[group.getFirstBankForPerpSettlement()],
|
2022-11-21 10:34:41 -08:00
|
|
|
[perpMarket],
|
|
|
|
);
|
|
|
|
return await this.program.methods
|
|
|
|
.perpPlaceOrderPegged(
|
|
|
|
side,
|
|
|
|
perpMarket.uiPriceToLots(priceOffset),
|
2023-03-21 15:01:15 -07:00
|
|
|
pegLimit ? perpMarket.uiPriceToLots(pegLimit) : new BN(-1),
|
2022-11-21 10:34:41 -08:00
|
|
|
perpMarket.uiBaseToLots(quantity),
|
|
|
|
maxQuoteQuantity
|
|
|
|
? perpMarket.uiQuoteToLots(maxQuoteQuantity)
|
|
|
|
: I64_MAX_BN,
|
|
|
|
new BN(clientOrderId ?? Date.now()),
|
|
|
|
orderType ? orderType : PerpOrderType.limit,
|
|
|
|
reduceOnly ? reduceOnly : false,
|
2022-12-02 06:48:43 -08:00
|
|
|
new BN(expiryTimestamp ?? 0),
|
2022-11-21 10:34:41 -08:00
|
|
|
limit ? limit : 10,
|
2022-12-01 01:43:47 -08:00
|
|
|
-1,
|
2022-11-21 10:34:41 -08:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
2022-05-11 04:33:01 -07:00
|
|
|
bids: perpMarket.bids,
|
2022-12-07 12:03:28 -08:00
|
|
|
asks: perpMarket.asks,
|
2023-06-15 23:05:16 -07:00
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2023-06-15 23:10:07 -07:00
|
|
|
public async perpPlaceOrderPeggedV2Ix(
|
2023-06-15 23:05:16 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
priceOffset: number,
|
|
|
|
quantity: number,
|
|
|
|
pegLimit?: number,
|
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
selfTradeBehavior?: PerpSelfTradeBehavior,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
|
|
|
// Settlement token bank, because a position for it may be created
|
|
|
|
[group.getFirstBankForPerpSettlement()],
|
|
|
|
[perpMarket],
|
|
|
|
);
|
|
|
|
return await this.program.methods
|
|
|
|
.perpPlaceOrderPeggedV2(
|
|
|
|
side,
|
|
|
|
perpMarket.uiPriceToLots(priceOffset),
|
|
|
|
pegLimit ? perpMarket.uiPriceToLots(pegLimit) : new BN(-1),
|
|
|
|
perpMarket.uiBaseToLots(quantity),
|
|
|
|
maxQuoteQuantity
|
|
|
|
? perpMarket.uiQuoteToLots(maxQuoteQuantity)
|
|
|
|
: I64_MAX_BN,
|
|
|
|
new BN(clientOrderId ?? Date.now()),
|
|
|
|
orderType ?? PerpOrderType.limit,
|
|
|
|
selfTradeBehavior ?? PerpSelfTradeBehavior.decrementTake,
|
|
|
|
reduceOnly ?? false,
|
|
|
|
new BN(expiryTimestamp ?? 0),
|
|
|
|
limit ?? 10,
|
|
|
|
-1,
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
|
|
|
asks: perpMarket.asks,
|
2022-05-11 04:33:01 -07:00
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
oracle: perpMarket.oracle,
|
2022-05-18 08:16:14 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-05-11 04:33:01 -07:00
|
|
|
})
|
2022-06-02 10:30:39 -07:00
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-10-07 04:52:04 -07:00
|
|
|
}
|
2022-09-27 06:13:53 -07:00
|
|
|
|
2023-08-29 22:21:03 -07:00
|
|
|
public async perpCancelOrderByClientOrderIdIx(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
clientOrderId: BN,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
return await this.program.methods
|
|
|
|
.perpCancelOrderByClientOrderId(new BN(clientOrderId))
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
|
|
|
asks: perpMarket.asks,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2022-11-01 10:19:19 -07:00
|
|
|
public async perpCancelOrderIx(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-11-01 10:37:21 -07:00
|
|
|
orderId: BN,
|
2022-11-01 10:19:19 -07:00
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
return await this.program.methods
|
|
|
|
.perpCancelOrder(new BN(orderId))
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
2022-12-07 12:03:28 -08:00
|
|
|
asks: perpMarket.asks,
|
2022-11-01 10:19:19 -07:00
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async perpCancelOrder(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-11-01 10:37:21 -07:00
|
|
|
orderId: BN,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-01-02 12:35:39 -08:00
|
|
|
const ix = await this.perpCancelOrderIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
orderId,
|
|
|
|
);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-11-01 10:19:19 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpCancelAllOrders(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
limit: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-01-02 12:35:39 -08:00
|
|
|
const ix = await this.perpCancelAllOrdersIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
limit,
|
|
|
|
);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-05-11 04:33:01 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpCancelAllOrdersIx(
|
2022-09-20 03:57:01 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-09-20 03:57:01 -07:00
|
|
|
limit: number,
|
2022-10-07 04:52:04 -07:00
|
|
|
): Promise<TransactionInstruction> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-10-07 04:52:04 -07:00
|
|
|
return await this.program.methods
|
2022-09-20 03:57:01 -07:00
|
|
|
.perpCancelAllOrders(limit)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
2022-12-07 12:03:28 -08:00
|
|
|
asks: perpMarket.asks,
|
2022-09-20 03:57:01 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
})
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
2022-09-20 03:57:01 -07:00
|
|
|
}
|
|
|
|
|
2023-07-14 04:19:17 -07:00
|
|
|
async settleAll(
|
2023-07-13 08:10:16 -07:00
|
|
|
client: MangoClient,
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
allMangoAccounts?: MangoAccount[],
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-13 08:10:16 -07:00
|
|
|
if (!allMangoAccounts) {
|
|
|
|
allMangoAccounts = await client.getAllMangoAccounts(group, true);
|
|
|
|
}
|
|
|
|
|
2023-07-14 04:19:17 -07:00
|
|
|
const ixs1 = new Array<TransactionInstruction>();
|
2023-07-13 08:10:16 -07:00
|
|
|
// This is optimistic, since we might find the same opponent candidate for all markets,
|
|
|
|
// and they have might not be able to settle at some point due to safety limits
|
|
|
|
// Future: correct way to do is, to apply the settlement on a copy and then move to next position
|
|
|
|
for (const pa of mangoAccount.perpActive()) {
|
|
|
|
const pm = group.getPerpMarketByMarketIndex(pa.marketIndex);
|
|
|
|
const candidates = await pm.getSettlePnlCandidates(
|
|
|
|
client,
|
|
|
|
group,
|
|
|
|
allMangoAccounts,
|
|
|
|
pa.getUnsettledPnlUi(pm) > 0 ? 'negative' : 'positive',
|
|
|
|
2,
|
|
|
|
);
|
|
|
|
if (candidates.length == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-07-14 04:19:17 -07:00
|
|
|
ixs1.push(
|
|
|
|
// Takes ~130k CU
|
2023-07-13 08:10:16 -07:00
|
|
|
await this.perpSettlePnlIx(
|
|
|
|
group,
|
|
|
|
pa.getUnsettledPnlUi(pm) > 0 ? mangoAccount : candidates[0].account,
|
|
|
|
pa.getUnsettledPnlUi(pm) < 0 ? candidates[0].account : mangoAccount,
|
|
|
|
mangoAccount,
|
|
|
|
pm.perpMarketIndex,
|
|
|
|
),
|
|
|
|
);
|
2023-07-14 04:19:17 -07:00
|
|
|
ixs1.push(
|
|
|
|
// Takes ~20k CU
|
2023-07-13 08:10:16 -07:00
|
|
|
await this.perpSettleFeesIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
pm.perpMarketIndex,
|
|
|
|
undefined,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-07-14 04:19:17 -07:00
|
|
|
const ixs2 = await Promise.all(
|
|
|
|
mangoAccount.serum3Active().map((s) => {
|
|
|
|
const serum3Market = group.getSerum3MarketByMarketIndex(s.marketIndex);
|
|
|
|
// Takes ~65k CU
|
|
|
|
return this.serum3SettleFundsV2Ix(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
serum3Market.serumMarketExternal,
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (
|
|
|
|
mangoAccount.perpActive().length * 150 +
|
|
|
|
mangoAccount.serum3Active().length * 65 >
|
|
|
|
1600
|
|
|
|
) {
|
|
|
|
throw new Error(
|
|
|
|
`Too many perp positions and serum open orders to settle in one tx! Please try settling individually!`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
|
|
|
[...ixs1, ...ixs2],
|
|
|
|
{
|
|
|
|
prioritizationFee: true,
|
|
|
|
},
|
|
|
|
);
|
2023-07-13 08:10:16 -07:00
|
|
|
}
|
|
|
|
|
2023-03-30 01:30:15 -07:00
|
|
|
async perpSettlePnlAndFees(
|
|
|
|
group: Group,
|
|
|
|
profitableAccount: MangoAccount,
|
|
|
|
unprofitableAccount: MangoAccount,
|
|
|
|
accountToSettleFeesFor: MangoAccount,
|
|
|
|
settler: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
maxSettleAmount?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-03-30 01:30:15 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.perpSettlePnlIx(
|
|
|
|
group,
|
|
|
|
profitableAccount,
|
|
|
|
unprofitableAccount,
|
|
|
|
settler,
|
|
|
|
perpMarketIndex,
|
|
|
|
),
|
|
|
|
await this.perpSettleFeesIx(
|
|
|
|
group,
|
|
|
|
accountToSettleFeesFor,
|
|
|
|
perpMarketIndex,
|
|
|
|
maxSettleAmount,
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2022-10-08 03:48:13 -07:00
|
|
|
async perpSettlePnl(
|
|
|
|
group: Group,
|
|
|
|
profitableAccount: MangoAccount,
|
|
|
|
unprofitableAccount: MangoAccount,
|
|
|
|
settler: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-03-30 01:19:00 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.perpSettlePnlIx(
|
|
|
|
group,
|
|
|
|
profitableAccount,
|
|
|
|
unprofitableAccount,
|
|
|
|
settler,
|
|
|
|
perpMarketIndex,
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
async perpSettlePnlIx(
|
|
|
|
group: Group,
|
|
|
|
profitableAccount: MangoAccount,
|
|
|
|
unprofitableAccount: MangoAccount,
|
|
|
|
settler: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
): Promise<TransactionInstruction> {
|
2022-10-08 03:48:13 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[profitableAccount, unprofitableAccount],
|
2023-02-28 03:05:02 -08:00
|
|
|
[group.getFirstBankForPerpSettlement()],
|
2022-10-08 03:48:13 -07:00
|
|
|
[perpMarket],
|
|
|
|
);
|
2022-10-18 12:10:20 -07:00
|
|
|
const bank = group.banksMapByTokenIndex.get(0 as TokenIndex)![0];
|
2023-03-30 01:19:00 -07:00
|
|
|
return await this.program.methods
|
2022-10-08 03:48:13 -07:00
|
|
|
.perpSettlePnl()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
accountA: profitableAccount.publicKey,
|
|
|
|
accountB: unprofitableAccount.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
settleOracle: bank.oracle,
|
|
|
|
settleBank: bank.publicKey,
|
|
|
|
settler: settler.publicKey,
|
2022-12-07 20:50:37 -08:00
|
|
|
settlerOwner: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
2022-10-08 03:48:13 -07:00
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
async perpSettleFees(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2023-03-30 01:19:00 -07:00
|
|
|
maxSettleAmount?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-03-30 01:19:00 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.perpSettleFeesIx(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
perpMarketIndex,
|
|
|
|
maxSettleAmount,
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
async perpSettleFeesIx(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
maxSettleAmount?: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
2022-10-08 03:48:13 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[account], // Account must be unprofitable
|
2023-02-28 03:05:02 -08:00
|
|
|
[group.getFirstBankForPerpSettlement()],
|
2022-10-08 03:48:13 -07:00
|
|
|
[perpMarket],
|
|
|
|
);
|
2022-10-18 12:10:20 -07:00
|
|
|
const bank = group.banksMapByTokenIndex.get(0 as TokenIndex)![0];
|
2023-03-30 01:19:00 -07:00
|
|
|
return await this.program.methods
|
|
|
|
.perpSettleFees(
|
|
|
|
maxSettleAmount ? toNative(maxSettleAmount, 6) : RUST_U64_MAX(),
|
|
|
|
)
|
2022-10-08 03:48:13 -07:00
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
settleOracle: bank.oracle,
|
|
|
|
settleBank: bank.publicKey,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: false, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpConsumeEvents(
|
2022-09-15 00:57:48 -07:00
|
|
|
group: Group,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-09-15 00:57:48 -07:00
|
|
|
accounts: PublicKey[],
|
|
|
|
limit: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-19 08:42:13 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.perpConsumeEventsIx(group, perpMarketIndex, accounts, limit),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async perpConsumeEventsIx(
|
|
|
|
group: Group,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
accounts: PublicKey[],
|
|
|
|
limit: number,
|
|
|
|
): Promise<TransactionInstruction> {
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2023-04-19 08:42:13 -07:00
|
|
|
return await this.program.methods
|
2022-09-15 00:57:48 -07:00
|
|
|
.perpConsumeEvents(new BN(limit))
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
eventQueue: perpMarket.eventQueue,
|
|
|
|
})
|
|
|
|
.remainingAccounts(
|
|
|
|
accounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({ pubkey: pk, isWritable: true, isSigner: false } as AccountMeta),
|
|
|
|
),
|
|
|
|
)
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2022-09-20 03:57:01 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async perpConsumeAllEvents(
|
2022-09-15 00:57:48 -07:00
|
|
|
group: Group,
|
2022-09-29 06:51:09 -07:00
|
|
|
perpMarketIndex: PerpMarketIndex,
|
2022-09-15 00:57:48 -07:00
|
|
|
): Promise<void> {
|
|
|
|
const limit = 8;
|
2022-09-29 06:51:09 -07:00
|
|
|
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
2022-09-15 00:57:48 -07:00
|
|
|
const eventQueue = await perpMarket.loadEventQueue(this);
|
2022-09-27 06:13:53 -07:00
|
|
|
const unconsumedEvents = eventQueue.getUnconsumedEvents();
|
2022-09-15 00:57:48 -07:00
|
|
|
while (unconsumedEvents.length > 0) {
|
|
|
|
const events = unconsumedEvents.splice(0, limit);
|
|
|
|
const accounts = events
|
|
|
|
.map((ev) => {
|
|
|
|
switch (ev.eventType) {
|
2022-09-27 06:13:53 -07:00
|
|
|
case PerpEventQueue.FILL_EVENT_TYPE: {
|
2022-09-15 00:57:48 -07:00
|
|
|
const fill = <FillEvent>ev;
|
|
|
|
return [fill.maker, fill.taker];
|
2022-09-27 06:13:53 -07:00
|
|
|
}
|
|
|
|
case PerpEventQueue.OUT_EVENT_TYPE: {
|
2022-09-15 00:57:48 -07:00
|
|
|
const out = <OutEvent>ev;
|
|
|
|
return [out.owner];
|
2022-09-27 06:13:53 -07:00
|
|
|
}
|
2022-09-15 00:57:48 -07:00
|
|
|
case PerpEventQueue.LIQUIDATE_EVENT_TYPE:
|
|
|
|
return [];
|
|
|
|
default:
|
2022-09-29 06:51:09 -07:00
|
|
|
throw new Error(`Unknown event with eventType ${ev.eventType}!`);
|
2022-09-15 00:57:48 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.flat();
|
|
|
|
|
2022-09-29 06:51:09 -07:00
|
|
|
await this.perpConsumeEvents(group, perpMarketIndex, accounts, limit);
|
2022-09-15 00:57:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-30 12:05:36 -07:00
|
|
|
public async perpUpdateFundingIx(
|
|
|
|
group: Group,
|
|
|
|
perpMarket: PerpMarket,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
return await this.program.methods
|
|
|
|
.perpUpdateFunding()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
perpMarket: perpMarket.publicKey,
|
|
|
|
bids: perpMarket.bids,
|
|
|
|
asks: perpMarket.asks,
|
|
|
|
oracle: perpMarket.oracle,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2022-05-24 11:10:01 -07:00
|
|
|
public async marginTrade({
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
inputMintPk,
|
2022-05-24 11:10:01 -07:00
|
|
|
amountIn,
|
2022-08-17 23:48:45 -07:00
|
|
|
outputMintPk,
|
2022-07-05 20:38:53 -07:00
|
|
|
userDefinedInstructions,
|
2022-12-15 14:22:10 -08:00
|
|
|
userDefinedAlts = [],
|
2022-08-17 03:36:55 -07:00
|
|
|
// margin trade is a general function
|
|
|
|
// set flash_loan_type to FlashLoanType.swap if you desire the transaction to be recorded as a swap
|
|
|
|
flashLoanType,
|
2022-05-24 11:10:01 -07:00
|
|
|
}: {
|
|
|
|
group: Group;
|
|
|
|
mangoAccount: MangoAccount;
|
2022-08-17 23:48:45 -07:00
|
|
|
inputMintPk: PublicKey;
|
2022-05-24 11:10:01 -07:00
|
|
|
amountIn: number;
|
2022-08-17 23:48:45 -07:00
|
|
|
outputMintPk: PublicKey;
|
2022-07-05 20:38:53 -07:00
|
|
|
userDefinedInstructions: TransactionInstruction[];
|
2022-12-15 14:22:10 -08:00
|
|
|
userDefinedAlts: AddressLookupTableAccount[];
|
2022-08-17 03:36:55 -07:00
|
|
|
flashLoanType: FlashLoanType;
|
2023-08-11 10:12:13 -07:00
|
|
|
}): Promise<MangoSignatureStatus> {
|
2023-06-16 07:07:09 -07:00
|
|
|
const isDelegate = (
|
|
|
|
this.program.provider as AnchorProvider
|
|
|
|
).wallet.publicKey.equals(mangoAccount.delegate);
|
|
|
|
const swapExecutingWallet = isDelegate
|
|
|
|
? mangoAccount.delegate
|
|
|
|
: mangoAccount.owner;
|
|
|
|
|
2022-08-17 23:48:45 -07:00
|
|
|
const inputBank: Bank = group.getFirstBankByMint(inputMintPk);
|
|
|
|
const outputBank: Bank = group.getFirstBankByMint(outputMintPk);
|
2022-05-31 18:38:47 -07:00
|
|
|
|
2022-05-24 11:10:01 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
2022-08-04 00:07:32 -07:00
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[mangoAccount],
|
|
|
|
[inputBank, outputBank],
|
2022-08-31 02:36:44 -07:00
|
|
|
[],
|
2022-08-04 00:07:32 -07:00
|
|
|
);
|
2022-06-29 20:36:57 -07:00
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
2022-07-06 21:45:01 -07:00
|
|
|
isWritable: false,
|
2022-06-29 20:36:57 -07:00
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find or create associated token accounts
|
|
|
|
*/
|
2022-08-04 10:42:41 -07:00
|
|
|
const inputTokenAccountPk = await getAssociatedTokenAddress(
|
2022-06-29 20:36:57 -07:00
|
|
|
inputBank.mint,
|
2023-06-16 07:07:09 -07:00
|
|
|
swapExecutingWallet,
|
2023-07-25 04:35:25 -07:00
|
|
|
true,
|
2022-06-29 20:36:57 -07:00
|
|
|
);
|
|
|
|
const inputTokenAccExists =
|
|
|
|
await this.program.provider.connection.getAccountInfo(
|
|
|
|
inputTokenAccountPk,
|
|
|
|
);
|
2022-08-31 02:41:12 -07:00
|
|
|
const preInstructions: TransactionInstruction[] = [];
|
2022-06-29 20:36:57 -07:00
|
|
|
if (!inputTokenAccExists) {
|
|
|
|
preInstructions.push(
|
2022-08-09 15:27:24 -07:00
|
|
|
await createAssociatedTokenAccountIdempotentInstruction(
|
2023-06-16 07:07:09 -07:00
|
|
|
swapExecutingWallet,
|
|
|
|
swapExecutingWallet,
|
2022-06-29 20:36:57 -07:00
|
|
|
inputBank.mint,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-04 10:42:41 -07:00
|
|
|
const outputTokenAccountPk = await getAssociatedTokenAddress(
|
2022-06-29 20:36:57 -07:00
|
|
|
outputBank.mint,
|
2023-06-16 07:07:09 -07:00
|
|
|
swapExecutingWallet,
|
2023-07-25 04:35:25 -07:00
|
|
|
true,
|
2022-06-29 20:36:57 -07:00
|
|
|
);
|
|
|
|
const outputTokenAccExists =
|
|
|
|
await this.program.provider.connection.getAccountInfo(
|
|
|
|
outputTokenAccountPk,
|
|
|
|
);
|
|
|
|
if (!outputTokenAccExists) {
|
|
|
|
preInstructions.push(
|
2022-08-09 15:27:24 -07:00
|
|
|
await createAssociatedTokenAccountIdempotentInstruction(
|
2023-06-16 07:07:09 -07:00
|
|
|
swapExecutingWallet,
|
|
|
|
swapExecutingWallet,
|
2022-06-29 20:36:57 -07:00
|
|
|
outputBank.mint,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-07 10:04:54 -07:00
|
|
|
const inputBankAccount = {
|
2022-06-29 20:36:57 -07:00
|
|
|
pubkey: inputBank.publicKey,
|
2022-07-06 21:45:01 -07:00
|
|
|
isWritable: true,
|
2022-06-29 20:36:57 -07:00
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-07-07 10:04:54 -07:00
|
|
|
const outputBankAccount = {
|
2022-07-06 21:45:01 -07:00
|
|
|
pubkey: outputBank.publicKey,
|
2022-06-29 20:36:57 -07:00
|
|
|
isWritable: true,
|
2022-07-06 21:45:01 -07:00
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-07-07 10:04:54 -07:00
|
|
|
const inputBankVault = {
|
2022-06-29 20:36:57 -07:00
|
|
|
pubkey: inputBank.vault,
|
2022-07-06 21:45:01 -07:00
|
|
|
isWritable: true,
|
2022-06-29 20:36:57 -07:00
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-07-07 10:04:54 -07:00
|
|
|
const outputBankVault = {
|
2022-07-06 21:45:01 -07:00
|
|
|
pubkey: outputBank.vault,
|
2022-06-29 20:36:57 -07:00
|
|
|
isWritable: true,
|
2022-07-06 21:45:01 -07:00
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-07-07 10:04:54 -07:00
|
|
|
const inputATA = {
|
2022-06-29 20:36:57 -07:00
|
|
|
pubkey: inputTokenAccountPk,
|
2022-07-06 21:45:01 -07:00
|
|
|
isWritable: true,
|
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-07-07 10:04:54 -07:00
|
|
|
const outputATA = {
|
2022-07-06 21:45:01 -07:00
|
|
|
pubkey: outputTokenAccountPk,
|
|
|
|
isWritable: false,
|
|
|
|
isSigner: false,
|
2022-06-29 20:36:57 -07:00
|
|
|
};
|
2022-08-16 01:19:15 -07:00
|
|
|
const groupAM = {
|
|
|
|
pubkey: group.publicKey,
|
|
|
|
isWritable: false,
|
|
|
|
isSigner: false,
|
|
|
|
};
|
2022-06-29 20:36:57 -07:00
|
|
|
|
|
|
|
const flashLoanEndIx = await this.program.methods
|
2023-04-14 06:18:02 -07:00
|
|
|
.flashLoanEndV2(2, flashLoanType)
|
2022-06-29 20:36:57 -07:00
|
|
|
.accounts({
|
|
|
|
account: mangoAccount.publicKey,
|
2023-06-16 07:07:09 -07:00
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-06-29 20:36:57 -07:00
|
|
|
})
|
2022-07-06 21:45:01 -07:00
|
|
|
.remainingAccounts([
|
|
|
|
...parsedHealthAccounts,
|
2022-07-07 10:04:54 -07:00
|
|
|
inputBankVault,
|
|
|
|
outputBankVault,
|
|
|
|
inputATA,
|
2022-07-06 21:45:01 -07:00
|
|
|
{
|
|
|
|
isWritable: true,
|
|
|
|
pubkey: outputTokenAccountPk,
|
|
|
|
isSigner: false,
|
|
|
|
},
|
2022-08-16 01:19:15 -07:00
|
|
|
groupAM,
|
2022-07-06 21:45:01 -07:00
|
|
|
])
|
2022-06-29 20:36:57 -07:00
|
|
|
.instruction();
|
|
|
|
|
2022-07-07 13:43:19 -07:00
|
|
|
const flashLoanBeginIx = await this.program.methods
|
2022-08-01 07:55:17 -07:00
|
|
|
.flashLoanBegin([
|
2022-09-30 06:07:43 -07:00
|
|
|
toNative(amountIn, inputBank.mintDecimals),
|
2022-07-06 21:45:01 -07:00
|
|
|
new BN(
|
|
|
|
0,
|
|
|
|
) /* we don't care about borrowing the target amount, this is just a dummy */,
|
|
|
|
])
|
2022-06-29 20:36:57 -07:00
|
|
|
.accounts({
|
2022-11-09 00:35:13 -08:00
|
|
|
account: mangoAccount.publicKey,
|
|
|
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2022-07-06 21:45:01 -07:00
|
|
|
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
2022-06-29 20:36:57 -07:00
|
|
|
})
|
2022-07-06 21:45:01 -07:00
|
|
|
.remainingAccounts([
|
2022-07-07 10:04:54 -07:00
|
|
|
inputBankAccount,
|
|
|
|
outputBankAccount,
|
|
|
|
inputBankVault,
|
|
|
|
outputBankVault,
|
|
|
|
inputATA,
|
|
|
|
outputATA,
|
2022-08-16 01:19:15 -07:00
|
|
|
groupAM,
|
2022-07-06 21:45:01 -07:00
|
|
|
])
|
|
|
|
.instruction();
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
2022-09-27 06:13:53 -07:00
|
|
|
[
|
|
|
|
...preInstructions,
|
|
|
|
flashLoanBeginIx,
|
|
|
|
...userDefinedInstructions.filter((ix) => ix.keys.length > 2),
|
|
|
|
flashLoanEndIx,
|
|
|
|
],
|
2023-02-03 04:55:46 -08:00
|
|
|
{ alts: [...group.addressLookupTablesList, ...userDefinedAlts] },
|
2022-09-27 06:13:53 -07:00
|
|
|
);
|
2022-07-06 21:45:01 -07:00
|
|
|
}
|
2022-08-03 09:05:16 -07:00
|
|
|
|
2023-04-19 08:42:13 -07:00
|
|
|
public async tokenUpdateIndexAndRate(
|
2022-09-29 06:51:09 -07:00
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-04-19 08:42:13 -07:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [
|
|
|
|
await this.tokenUpdateIndexAndRateIx(group, mintPk),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async tokenUpdateIndexAndRateIx(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
): Promise<TransactionInstruction> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const bank = group.getFirstBankByMint(mintPk);
|
|
|
|
const mintInfo = group.mintInfosMapByMint.get(mintPk.toString())!;
|
2022-08-07 05:16:06 -07:00
|
|
|
|
2023-04-19 08:42:13 -07:00
|
|
|
return await this.program.methods
|
2022-08-07 05:16:06 -07:00
|
|
|
.tokenUpdateIndexAndRate()
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
mintInfo: mintInfo.publicKey,
|
|
|
|
oracle: mintInfo.oracle,
|
|
|
|
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
|
|
})
|
2022-08-05 10:11:44 -07:00
|
|
|
.remainingAccounts([
|
|
|
|
{
|
|
|
|
pubkey: bank.publicKey,
|
|
|
|
isWritable: true,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta,
|
|
|
|
])
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2022-08-05 10:11:44 -07:00
|
|
|
}
|
|
|
|
|
2022-08-04 00:07:32 -07:00
|
|
|
/// liquidations
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async liqTokenWithToken(
|
2022-08-04 00:07:32 -07:00
|
|
|
group: Group,
|
|
|
|
liqor: MangoAccount,
|
|
|
|
liqee: MangoAccount,
|
2022-08-17 23:48:45 -07:00
|
|
|
assetMintPk: PublicKey,
|
|
|
|
liabMintPk: PublicKey,
|
2022-08-04 00:07:32 -07:00
|
|
|
maxLiabTransfer: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-08-17 23:48:45 -07:00
|
|
|
const assetBank: Bank = group.getFirstBankByMint(assetMintPk);
|
|
|
|
const liabBank: Bank = group.getFirstBankByMint(liabMintPk);
|
2022-08-04 00:07:32 -07:00
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[liqor, liqee],
|
|
|
|
[assetBank, liabBank],
|
2022-08-31 02:36:44 -07:00
|
|
|
[],
|
2022-08-04 00:07:32 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable:
|
|
|
|
pk.equals(assetBank.publicKey) || pk.equals(liabBank.publicKey)
|
|
|
|
? true
|
|
|
|
: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
2022-08-03 09:05:16 -07:00
|
|
|
);
|
|
|
|
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
2022-08-04 00:07:32 -07:00
|
|
|
.liqTokenWithToken(assetBank.tokenIndex, liabBank.tokenIndex, {
|
|
|
|
val: I80F48.fromNumber(maxLiabTransfer).getData(),
|
|
|
|
})
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
liqor: liqor.publicKey,
|
|
|
|
liqee: liqee.publicKey,
|
2022-08-04 08:35:05 -07:00
|
|
|
liqorOwner: liqor.owner,
|
2022-08-04 00:07:32 -07:00
|
|
|
})
|
|
|
|
.remainingAccounts(parsedHealthAccounts)
|
2022-09-27 06:13:53 -07:00
|
|
|
.instruction();
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-09-27 06:13:53 -07:00
|
|
|
}
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
public async tcsTakeProfitOnDeposit(
|
2023-07-27 23:26:34 -07:00
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
2023-08-07 04:09:19 -07:00
|
|
|
sellBank: Bank,
|
|
|
|
buyBank: Bank,
|
|
|
|
thresholdPriceUi: number,
|
|
|
|
thresholdPriceInSellPerBuyToken: boolean,
|
|
|
|
maxSellUi: number | null,
|
|
|
|
pricePremium: number | null,
|
2023-07-27 23:26:34 -07:00
|
|
|
expiryTimestamp: number | null,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-07 04:09:19 -07:00
|
|
|
if (account.getTokenBalanceUi(sellBank) < 0) {
|
|
|
|
throw new Error(
|
|
|
|
`Only allowed to take profits on deposits! Current balance ${account.getTokenBalanceUi(
|
|
|
|
sellBank,
|
|
|
|
)}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-11 07:46:19 -07:00
|
|
|
if (!thresholdPriceInSellPerBuyToken) {
|
|
|
|
thresholdPriceUi = 1 / thresholdPriceUi;
|
|
|
|
}
|
|
|
|
const thresholdPrice = toNativeSellPerBuyTokenPrice(
|
|
|
|
thresholdPriceUi,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
|
|
|
);
|
|
|
|
const lowerLimit = 0;
|
|
|
|
const upperLimit = thresholdPrice;
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
return await this.tokenConditionalSwapCreate(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
2023-08-11 07:46:19 -07:00
|
|
|
lowerLimit,
|
|
|
|
upperLimit,
|
2023-08-07 04:09:19 -07:00
|
|
|
Number.MAX_SAFE_INTEGER,
|
|
|
|
maxSellUi ?? account.getTokenBalanceUi(sellBank),
|
|
|
|
'TakeProfitOnDeposit',
|
|
|
|
pricePremium,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
expiryTimestamp,
|
2023-09-07 03:40:37 -07:00
|
|
|
thresholdPriceInSellPerBuyToken,
|
2023-07-28 06:32:50 -07:00
|
|
|
);
|
2023-08-07 04:09:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async tcsStopLossOnDeposit(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
sellBank: Bank,
|
|
|
|
buyBank: Bank,
|
|
|
|
thresholdPriceUi: number,
|
|
|
|
thresholdPriceInSellPerBuyToken: boolean,
|
|
|
|
maxSellUi: number | null,
|
|
|
|
pricePremium: number | null,
|
|
|
|
expiryTimestamp: number | null,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-07 04:09:19 -07:00
|
|
|
if (account.getTokenBalanceUi(sellBank) < 0) {
|
|
|
|
throw new Error(
|
|
|
|
`Only allowed to set a stop loss on deposits! Current balance ${account.getTokenBalanceUi(
|
|
|
|
sellBank,
|
|
|
|
)}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-11 07:46:19 -07:00
|
|
|
if (!thresholdPriceInSellPerBuyToken) {
|
|
|
|
thresholdPriceUi = 1 / thresholdPriceUi;
|
|
|
|
}
|
|
|
|
const thresholdPrice = toNativeSellPerBuyTokenPrice(
|
|
|
|
thresholdPriceUi,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
|
|
|
);
|
|
|
|
const lowerLimit = thresholdPrice;
|
|
|
|
const upperLimit = Number.MAX_SAFE_INTEGER;
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
return await this.tokenConditionalSwapCreate(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
2023-08-11 07:46:19 -07:00
|
|
|
lowerLimit,
|
|
|
|
upperLimit,
|
2023-08-07 04:09:19 -07:00
|
|
|
Number.MAX_SAFE_INTEGER,
|
|
|
|
maxSellUi ?? account.getTokenBalanceUi(sellBank),
|
|
|
|
'StopLossOnDeposit',
|
|
|
|
pricePremium,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
expiryTimestamp,
|
2023-09-07 03:40:37 -07:00
|
|
|
thresholdPriceInSellPerBuyToken,
|
2023-08-07 04:09:19 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async tcsTakeProfitOnBorrow(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
sellBank: Bank,
|
|
|
|
buyBank: Bank,
|
|
|
|
thresholdPriceUi: number,
|
|
|
|
thresholdPriceInSellPerBuyToken: boolean,
|
|
|
|
maxBuyUi: number | null,
|
|
|
|
pricePremium: number | null,
|
|
|
|
allowMargin: boolean | null,
|
|
|
|
expiryTimestamp: number | null,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-07 04:09:19 -07:00
|
|
|
if (account.getTokenBalanceUi(buyBank) > 0) {
|
|
|
|
throw new Error(
|
|
|
|
`Only allowed to take profits on borrows! Current balance ${account.getTokenBalanceUi(
|
|
|
|
buyBank,
|
|
|
|
)}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-11 07:46:19 -07:00
|
|
|
if (!thresholdPriceInSellPerBuyToken) {
|
|
|
|
thresholdPriceUi = 1 / thresholdPriceUi;
|
|
|
|
}
|
|
|
|
const thresholdPrice = toNativeSellPerBuyTokenPrice(
|
|
|
|
thresholdPriceUi,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
|
|
|
);
|
|
|
|
const lowerLimit = thresholdPrice;
|
|
|
|
const upperLimit = Number.MAX_SAFE_INTEGER;
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
return await this.tokenConditionalSwapCreate(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
2023-08-11 07:46:19 -07:00
|
|
|
lowerLimit,
|
|
|
|
upperLimit,
|
2023-08-07 04:09:19 -07:00
|
|
|
maxBuyUi ?? -account.getTokenBalanceUi(buyBank),
|
|
|
|
Number.MAX_SAFE_INTEGER,
|
|
|
|
'TakeProfitOnBorrow',
|
|
|
|
pricePremium,
|
|
|
|
false,
|
|
|
|
allowMargin ?? false,
|
|
|
|
expiryTimestamp,
|
2023-09-07 03:40:37 -07:00
|
|
|
thresholdPriceInSellPerBuyToken,
|
2023-08-07 04:09:19 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async tcsStopLossOnBorrow(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
sellBank: Bank,
|
|
|
|
buyBank: Bank,
|
|
|
|
thresholdPriceUi: number,
|
|
|
|
thresholdPriceInSellPerBuyToken: boolean,
|
|
|
|
maxBuyUi: number | null,
|
|
|
|
pricePremium: number | null,
|
|
|
|
allowMargin: boolean | null,
|
|
|
|
expiryTimestamp: number | null,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-07 04:09:19 -07:00
|
|
|
if (account.getTokenBalanceUi(buyBank) > 0) {
|
|
|
|
throw new Error(
|
|
|
|
`Only allowed to set stop loss on borrows! Current balance ${account.getTokenBalanceUi(
|
|
|
|
buyBank,
|
|
|
|
)}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-11 07:46:19 -07:00
|
|
|
if (!thresholdPriceInSellPerBuyToken) {
|
|
|
|
thresholdPriceUi = 1 / thresholdPriceUi;
|
|
|
|
}
|
|
|
|
const thresholdPrice = toNativeSellPerBuyTokenPrice(
|
|
|
|
thresholdPriceUi,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
|
|
|
);
|
|
|
|
const lowerLimit = 0;
|
|
|
|
const upperLimit = thresholdPrice;
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
return await this.tokenConditionalSwapCreate(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
sellBank,
|
|
|
|
buyBank,
|
2023-08-11 07:46:19 -07:00
|
|
|
lowerLimit,
|
|
|
|
upperLimit,
|
2023-08-07 04:09:19 -07:00
|
|
|
maxBuyUi ?? -account.getTokenBalanceUi(buyBank),
|
|
|
|
Number.MAX_SAFE_INTEGER,
|
|
|
|
'StopLossOnBorrow',
|
|
|
|
pricePremium,
|
|
|
|
false,
|
|
|
|
allowMargin ?? false,
|
|
|
|
expiryTimestamp,
|
2023-09-07 03:40:37 -07:00
|
|
|
thresholdPriceInSellPerBuyToken,
|
2023-08-07 04:09:19 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async tokenConditionalSwapCreate(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
sellBank: Bank,
|
|
|
|
buyBank: Bank,
|
2023-08-11 07:46:19 -07:00
|
|
|
lowerLimit: number,
|
|
|
|
upperLimit: number,
|
2023-08-07 04:09:19 -07:00
|
|
|
maxBuyUi: number,
|
|
|
|
maxSellUi: number,
|
|
|
|
tcsIntention:
|
|
|
|
| 'TakeProfitOnDeposit'
|
|
|
|
| 'StopLossOnDeposit'
|
|
|
|
| 'TakeProfitOnBorrow'
|
|
|
|
| 'StopLossOnBorrow'
|
|
|
|
| null,
|
|
|
|
pricePremium: number | null,
|
|
|
|
allowCreatingDeposits: boolean,
|
|
|
|
allowCreatingBorrows: boolean,
|
|
|
|
expiryTimestamp: number | null,
|
2023-09-07 03:40:37 -07:00
|
|
|
displayPriceInSellTokenPerBuyToken: boolean,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-08-25 06:20:00 -07:00
|
|
|
let maxBuy, maxSell, buyAmountInUsd, sellAmountInUsd;
|
|
|
|
if (maxBuyUi == Number.MAX_SAFE_INTEGER) {
|
|
|
|
maxBuy = U64_MAX_BN;
|
|
|
|
} else {
|
|
|
|
buyAmountInUsd = maxBuyUi * buyBank.uiPrice;
|
|
|
|
maxBuy = toNative(maxBuyUi, buyBank.mintDecimals);
|
|
|
|
}
|
|
|
|
if (maxSellUi == Number.MAX_SAFE_INTEGER) {
|
|
|
|
maxSell = U64_MAX_BN;
|
|
|
|
} else {
|
|
|
|
sellAmountInUsd = maxSellUi * sellBank.uiPrice;
|
|
|
|
maxSell = toNative(maxSellUi, sellBank.mintDecimals);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used for computing optimal premium
|
|
|
|
let liqorTcsChunkSizeInUsd = Math.min(buyAmountInUsd, sellAmountInUsd);
|
|
|
|
if (liqorTcsChunkSizeInUsd > 5000) {
|
|
|
|
liqorTcsChunkSizeInUsd = 5000;
|
|
|
|
}
|
|
|
|
// For small TCS swaps, reduce chunk size to 1000 USD
|
|
|
|
else {
|
|
|
|
liqorTcsChunkSizeInUsd = 1000;
|
|
|
|
}
|
2023-08-07 04:09:19 -07:00
|
|
|
|
|
|
|
if (!pricePremium) {
|
2023-08-25 06:20:00 -07:00
|
|
|
if (maxBuy.eq(U64_MAX_BN)) {
|
|
|
|
maxSell.toNumber() * sellBank.uiPrice;
|
|
|
|
}
|
2023-08-07 04:09:19 -07:00
|
|
|
const buyTokenPriceImpact = group.getPriceImpactByTokenIndex(
|
|
|
|
buyBank.tokenIndex,
|
2023-08-25 06:20:00 -07:00
|
|
|
liqorTcsChunkSizeInUsd,
|
2023-08-07 04:09:19 -07:00
|
|
|
);
|
|
|
|
const sellTokenPriceImpact = group.getPriceImpactByTokenIndex(
|
|
|
|
sellBank.tokenIndex,
|
2023-08-25 06:20:00 -07:00
|
|
|
liqorTcsChunkSizeInUsd,
|
2023-08-07 04:09:19 -07:00
|
|
|
);
|
|
|
|
pricePremium =
|
|
|
|
((1 + buyTokenPriceImpact / 100) * (1 + sellTokenPriceImpact / 100) -
|
|
|
|
1) *
|
|
|
|
100;
|
|
|
|
}
|
2023-08-10 04:32:06 -07:00
|
|
|
const pricePremiumRate = pricePremium > 0 ? pricePremium / 100 : 0.03;
|
2023-08-07 04:09:19 -07:00
|
|
|
|
2023-08-10 04:39:39 -07:00
|
|
|
let intention: TokenConditionalSwapIntention;
|
|
|
|
switch (tcsIntention) {
|
|
|
|
case 'StopLossOnBorrow':
|
|
|
|
case 'StopLossOnDeposit':
|
|
|
|
intention = TokenConditionalSwapIntention.stopLoss;
|
|
|
|
break;
|
|
|
|
case 'TakeProfitOnBorrow':
|
|
|
|
case 'TakeProfitOnDeposit':
|
|
|
|
intention = TokenConditionalSwapIntention.takeProfit;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
intention = TokenConditionalSwapIntention.unknown;
|
|
|
|
break;
|
2023-07-27 23:26:34 -07:00
|
|
|
}
|
|
|
|
|
2023-08-10 04:48:28 -07:00
|
|
|
return await this.tokenConditionalSwapCreateRaw(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
buyBank.mint,
|
|
|
|
sellBank.mint,
|
|
|
|
maxBuy,
|
|
|
|
maxSell,
|
|
|
|
expiryTimestamp,
|
|
|
|
lowerLimit,
|
|
|
|
upperLimit,
|
|
|
|
pricePremiumRate,
|
|
|
|
allowCreatingDeposits,
|
|
|
|
allowCreatingBorrows,
|
2023-09-07 03:40:37 -07:00
|
|
|
displayPriceInSellTokenPerBuyToken
|
2023-08-10 04:48:28 -07:00
|
|
|
? TokenConditionalSwapDisplayPriceStyle.sellTokenPerBuyToken
|
|
|
|
: TokenConditionalSwapDisplayPriceStyle.buyTokenPerSellToken,
|
|
|
|
intention,
|
|
|
|
);
|
2023-07-27 23:26:34 -07:00
|
|
|
}
|
|
|
|
|
2023-08-07 04:09:19 -07:00
|
|
|
public async tokenConditionalSwapCreateRaw(
|
2023-07-03 05:09:11 -07:00
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
buyMintPk: PublicKey,
|
|
|
|
sellMintPk: PublicKey,
|
2023-08-10 04:48:28 -07:00
|
|
|
maxBuy: BN,
|
|
|
|
maxSell: BN,
|
2023-07-03 05:09:11 -07:00
|
|
|
expiryTimestamp: number | null,
|
|
|
|
priceLowerLimit: number,
|
|
|
|
priceUpperLimit: number,
|
2023-08-09 04:27:25 -07:00
|
|
|
pricePremiumRate: number,
|
2023-07-03 05:09:11 -07:00
|
|
|
allowCreatingDeposits: boolean,
|
|
|
|
allowCreatingBorrows: boolean,
|
2023-08-03 03:37:01 -07:00
|
|
|
priceDisplayStyle: TokenConditionalSwapDisplayPriceStyle,
|
2023-08-08 09:16:59 -07:00
|
|
|
intention: TokenConditionalSwapIntention,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-03 05:09:11 -07:00
|
|
|
const buyBank: Bank = group.getFirstBankByMint(buyMintPk);
|
|
|
|
const sellBank: Bank = group.getFirstBankByMint(sellMintPk);
|
2023-08-10 04:48:28 -07:00
|
|
|
const tcsIx = await this.program.methods
|
2023-08-03 03:37:01 -07:00
|
|
|
.tokenConditionalSwapCreateV2(
|
2023-08-10 04:48:28 -07:00
|
|
|
maxBuy,
|
|
|
|
maxSell,
|
2023-07-03 05:09:11 -07:00
|
|
|
expiryTimestamp !== null ? new BN(expiryTimestamp) : U64_MAX_BN,
|
|
|
|
priceLowerLimit,
|
|
|
|
priceUpperLimit,
|
2023-08-09 04:27:25 -07:00
|
|
|
pricePremiumRate,
|
2023-07-03 05:09:11 -07:00
|
|
|
allowCreatingDeposits,
|
|
|
|
allowCreatingBorrows,
|
2023-08-03 03:37:01 -07:00
|
|
|
priceDisplayStyle,
|
2023-08-08 09:16:59 -07:00
|
|
|
intention,
|
2023-07-03 05:09:11 -07:00
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
authority: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
buyBank: buyBank.publicKey,
|
|
|
|
sellBank: sellBank.publicKey,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
|
2023-08-10 04:48:28 -07:00
|
|
|
const ixs: TransactionInstruction[] = [];
|
2023-07-27 23:26:34 -07:00
|
|
|
if (account.tokenConditionalSwaps.length == 0) {
|
|
|
|
ixs.push(
|
|
|
|
await this.accountExpandV2Ix(
|
|
|
|
group,
|
|
|
|
account,
|
|
|
|
account.tokens.length,
|
|
|
|
account.serum3.length,
|
|
|
|
account.perps.length,
|
|
|
|
account.perpOpenOrders.length,
|
2023-08-10 04:48:28 -07:00
|
|
|
DEFAULT_TOKEN_CONDITIONAL_SWAP_COUNT,
|
2023-07-27 23:26:34 -07:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2023-08-10 04:48:28 -07:00
|
|
|
ixs.push(tcsIx);
|
2023-07-27 23:26:34 -07:00
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixs);
|
2023-07-03 05:09:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async tokenConditionalSwapCancel(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
2023-07-17 04:14:53 -07:00
|
|
|
tokenConditionalSwapId: BN,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-28 06:57:25 -07:00
|
|
|
const tokenConditionalSwapIndex = account.tokenConditionalSwaps.findIndex(
|
|
|
|
(tcs) => tcs.id.eq(tokenConditionalSwapId),
|
|
|
|
);
|
|
|
|
if (tokenConditionalSwapIndex == -1) {
|
2023-07-17 04:14:53 -07:00
|
|
|
throw new Error('tcs with id not found');
|
|
|
|
}
|
2023-07-28 06:57:25 -07:00
|
|
|
const tcs = account.tokenConditionalSwaps[tokenConditionalSwapIndex];
|
2023-07-17 04:14:53 -07:00
|
|
|
|
|
|
|
const buyBank = group.banksMapByTokenIndex.get(tcs.buyTokenIndex)![0];
|
|
|
|
const sellBank = group.banksMapByTokenIndex.get(tcs.sellTokenIndex)![0];
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
const ix = await this.program.methods
|
|
|
|
.tokenConditionalSwapCancel(
|
|
|
|
tokenConditionalSwapIndex,
|
|
|
|
new BN(tokenConditionalSwapId),
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
authority: (this.program.provider as AnchorProvider).wallet.publicKey,
|
2023-07-17 04:14:53 -07:00
|
|
|
buyBank: buyBank.publicKey,
|
|
|
|
sellBank: sellBank.publicKey,
|
2023-07-03 05:09:11 -07:00
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2023-07-31 05:54:39 -07:00
|
|
|
public async tokenConditionalSwapCancelAll(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-31 05:54:39 -07:00
|
|
|
const ixs = await Promise.all(
|
|
|
|
account.tokenConditionalSwaps
|
|
|
|
.filter((tcs) => tcs.hasData)
|
|
|
|
.map(async (tcs, i) => {
|
|
|
|
const buyBank = group.banksMapByTokenIndex.get(tcs.buyTokenIndex)![0];
|
|
|
|
const sellBank = group.banksMapByTokenIndex.get(
|
|
|
|
tcs.sellTokenIndex,
|
|
|
|
)![0];
|
|
|
|
return await this.program.methods
|
|
|
|
.tokenConditionalSwapCancel(i, new BN(tcs.id))
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
account: account.publicKey,
|
|
|
|
authority: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
buyBank: buyBank.publicKey,
|
|
|
|
sellBank: sellBank.publicKey,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, ixs);
|
|
|
|
}
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
public async tokenConditionalSwapTrigger(
|
|
|
|
group: Group,
|
|
|
|
liqee: MangoAccount,
|
|
|
|
liqor: MangoAccount,
|
2023-07-17 04:14:53 -07:00
|
|
|
tokenConditionalSwapId: BN,
|
2023-07-03 05:09:11 -07:00
|
|
|
maxBuyTokenToLiqee: number,
|
|
|
|
maxSellTokenToLiqor: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-07-28 06:57:25 -07:00
|
|
|
const tokenConditionalSwapIndex = liqee.tokenConditionalSwaps.findIndex(
|
|
|
|
(tcs) => tcs.id.eq(tokenConditionalSwapId),
|
|
|
|
);
|
|
|
|
if (tokenConditionalSwapIndex == -1) {
|
2023-07-17 04:14:53 -07:00
|
|
|
throw new Error('tcs with id not found');
|
|
|
|
}
|
2023-07-28 06:57:25 -07:00
|
|
|
const tcs = liqee.tokenConditionalSwaps[tokenConditionalSwapIndex];
|
2023-07-17 04:14:53 -07:00
|
|
|
|
|
|
|
const buyBank = group.banksMapByTokenIndex.get(tcs.buyTokenIndex)![0];
|
|
|
|
const sellBank = group.banksMapByTokenIndex.get(tcs.sellTokenIndex)![0];
|
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[liqor, liqee],
|
|
|
|
[buyBank, sellBank],
|
|
|
|
[],
|
|
|
|
);
|
|
|
|
|
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable:
|
|
|
|
pk.equals(buyBank.publicKey) || pk.equals(sellBank.publicKey)
|
|
|
|
? true
|
|
|
|
: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
);
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
const ix = await this.program.methods
|
|
|
|
.tokenConditionalSwapTrigger(
|
|
|
|
tokenConditionalSwapIndex,
|
|
|
|
new BN(tokenConditionalSwapId),
|
|
|
|
new BN(maxBuyTokenToLiqee),
|
|
|
|
new BN(maxSellTokenToLiqor),
|
|
|
|
)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
liqee: liqee.publicKey,
|
|
|
|
liqor: liqor.publicKey,
|
|
|
|
liqorAuthority: (this.program.provider as AnchorProvider).wallet
|
|
|
|
.publicKey,
|
|
|
|
})
|
2023-07-17 04:14:53 -07:00
|
|
|
.remainingAccounts(parsedHealthAccounts)
|
2023-07-03 05:09:11 -07:00
|
|
|
.instruction();
|
|
|
|
|
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async altSet(
|
2022-09-29 06:51:09 -07:00
|
|
|
group: Group,
|
|
|
|
addressLookupTable: PublicKey,
|
|
|
|
index: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-09-27 06:13:53 -07:00
|
|
|
const ix = await this.program.methods
|
|
|
|
.altSet(index)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
addressLookupTable,
|
|
|
|
})
|
|
|
|
.instruction();
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-09-27 06:13:53 -07:00
|
|
|
}
|
|
|
|
|
2022-10-07 04:52:04 -07:00
|
|
|
public async altExtend(
|
2022-09-27 06:13:53 -07:00
|
|
|
group: Group,
|
|
|
|
addressLookupTable: PublicKey,
|
|
|
|
index: number,
|
|
|
|
pks: PublicKey[],
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2023-02-02 05:23:22 -08:00
|
|
|
const ix = await this.program.methods
|
2022-09-27 06:13:53 -07:00
|
|
|
.altExtend(index, pks)
|
|
|
|
.accounts({
|
|
|
|
group: group.publicKey,
|
|
|
|
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
payer: (this.program.provider as AnchorProvider).wallet.publicKey,
|
|
|
|
addressLookupTable,
|
|
|
|
})
|
2023-02-02 05:23:22 -08:00
|
|
|
.instruction();
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
|
2022-08-04 00:07:32 -07:00
|
|
|
}
|
2022-06-18 07:31:28 -07:00
|
|
|
|
2022-10-11 00:39:57 -07:00
|
|
|
public async healthRegionBeginIx(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
banks: Bank[] = [],
|
|
|
|
perpMarkets: PerpMarket[] = [],
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[account],
|
|
|
|
[...banks],
|
|
|
|
[...perpMarkets],
|
|
|
|
);
|
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
);
|
|
|
|
|
|
|
|
return await this.program.methods
|
|
|
|
.healthRegionBegin()
|
|
|
|
.accounts({
|
2023-02-02 10:02:30 -08:00
|
|
|
group: group.publicKey,
|
2022-10-11 00:39:57 -07:00
|
|
|
account: account.publicKey,
|
|
|
|
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
|
|
})
|
|
|
|
.remainingAccounts(parsedHealthAccounts)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async healthRegionEndIx(
|
|
|
|
group: Group,
|
|
|
|
account: MangoAccount,
|
|
|
|
banks: Bank[] = [],
|
|
|
|
perpMarkets: PerpMarket[] = [],
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
this.buildHealthRemainingAccounts(
|
|
|
|
group,
|
|
|
|
[account],
|
|
|
|
[...banks],
|
|
|
|
[...perpMarkets],
|
|
|
|
);
|
|
|
|
const parsedHealthAccounts = healthRemainingAccounts.map(
|
|
|
|
(pk) =>
|
|
|
|
({
|
|
|
|
pubkey: pk,
|
|
|
|
isWritable: false,
|
|
|
|
isSigner: false,
|
|
|
|
} as AccountMeta),
|
|
|
|
);
|
|
|
|
|
|
|
|
return await this.program.methods
|
|
|
|
.healthRegionEnd()
|
|
|
|
.accounts({ account: account.publicKey })
|
|
|
|
.remainingAccounts(parsedHealthAccounts)
|
|
|
|
.instruction();
|
|
|
|
}
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
/// static
|
|
|
|
|
2022-06-11 04:49:45 -07:00
|
|
|
static connect(
|
|
|
|
provider: Provider,
|
|
|
|
cluster: Cluster,
|
|
|
|
programId: PublicKey,
|
2022-11-04 07:35:40 -07:00
|
|
|
opts?: MangoClientOptions,
|
2022-06-11 04:49:45 -07:00
|
|
|
): MangoClient {
|
2022-08-04 10:42:41 -07:00
|
|
|
const idl = IDL;
|
2022-03-31 00:10:06 -07:00
|
|
|
|
2022-02-23 02:09:17 -08:00
|
|
|
return new MangoClient(
|
2022-06-11 04:49:45 -07:00
|
|
|
new Program<MangoV4>(idl as MangoV4, programId, provider),
|
|
|
|
programId,
|
|
|
|
cluster,
|
2022-08-12 17:15:12 -07:00
|
|
|
opts,
|
2022-06-21 11:04:21 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-15 23:04:58 -07:00
|
|
|
/**
|
|
|
|
* Connect with defaults,
|
|
|
|
* - random ephemeral keypair,
|
|
|
|
* - fetch ids using gPa
|
|
|
|
* - connects to mainnet-beta
|
|
|
|
* - uses well known program Id
|
|
|
|
* @param clusterUrl
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
static connectDefault(clusterUrl: string): MangoClient {
|
|
|
|
const idl = IDL;
|
|
|
|
|
|
|
|
const options = AnchorProvider.defaultOptions();
|
|
|
|
const connection = new Connection(clusterUrl, options);
|
|
|
|
|
|
|
|
return new MangoClient(
|
|
|
|
new Program<MangoV4>(
|
|
|
|
idl as MangoV4,
|
|
|
|
MANGO_V4_ID['mainnet-beta'],
|
|
|
|
new AnchorProvider(connection, new Wallet(new Keypair()), options),
|
|
|
|
),
|
|
|
|
MANGO_V4_ID['mainnet-beta'],
|
|
|
|
'mainnet-beta' as Cluster,
|
|
|
|
{
|
|
|
|
idsSource: 'get-program-accounts',
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-06-21 11:04:21 -07:00
|
|
|
static connectForGroupName(
|
|
|
|
provider: Provider,
|
|
|
|
groupName: string,
|
|
|
|
): MangoClient {
|
2022-08-04 10:42:41 -07:00
|
|
|
const idl = IDL;
|
2022-06-21 11:04:21 -07:00
|
|
|
|
2022-08-17 23:48:45 -07:00
|
|
|
const id = Id.fromIdsByName(groupName);
|
2022-06-21 11:04:21 -07:00
|
|
|
|
|
|
|
return new MangoClient(
|
|
|
|
new Program<MangoV4>(
|
|
|
|
idl as MangoV4,
|
2022-06-23 06:22:59 -07:00
|
|
|
new PublicKey(id.mangoProgramId),
|
2022-06-21 11:04:21 -07:00
|
|
|
provider,
|
|
|
|
),
|
2022-06-23 06:22:59 -07:00
|
|
|
new PublicKey(id.mangoProgramId),
|
2022-06-21 11:04:21 -07:00
|
|
|
id.cluster,
|
2022-02-23 02:09:17 -08:00
|
|
|
);
|
|
|
|
}
|
2022-04-07 08:16:46 -07:00
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
/**
|
|
|
|
* Builds health remaining accounts.
|
|
|
|
*
|
|
|
|
* For single mango account it builds a list of PublicKeys
|
|
|
|
* which is compatbile with Fixed account retriever.
|
|
|
|
*
|
|
|
|
* For multiple mango accounts it uses same logic as for fixed
|
|
|
|
* but packing all banks, then perp markets, and then serum oo accounts, which
|
|
|
|
* should always be compatible with Scanning account retriever.
|
|
|
|
*
|
|
|
|
* @param group
|
|
|
|
* @param mangoAccounts
|
|
|
|
* @param banks - banks in which new positions might be opened
|
|
|
|
* @param perpMarkets - markets in which new positions might be opened
|
|
|
|
* @param openOrdersForMarket - markets in which new positions might be opened
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
buildHealthRemainingAccounts(
|
2022-08-03 09:05:16 -07:00
|
|
|
group: Group,
|
|
|
|
mangoAccounts: MangoAccount[],
|
2023-04-24 05:48:53 -07:00
|
|
|
// Banks and markets for whom positions don't exist on mango account,
|
|
|
|
// but user would potentially open new positions.
|
2023-01-07 14:06:35 -08:00
|
|
|
banks: Bank[] = [],
|
|
|
|
perpMarkets: PerpMarket[] = [],
|
|
|
|
openOrdersForMarket: [Serum3Market, PublicKey][] = [],
|
2022-08-10 01:15:28 -07:00
|
|
|
): PublicKey[] {
|
2022-04-07 09:58:42 -07:00
|
|
|
const healthRemainingAccounts: PublicKey[] = [];
|
2022-04-07 08:58:20 -07:00
|
|
|
|
2023-04-25 02:13:40 -07:00
|
|
|
const tokenPositionIndices = mangoAccounts
|
|
|
|
.map((mangoAccount) => mangoAccount.tokens.map((t) => t.tokenIndex))
|
|
|
|
.flat();
|
2023-01-07 14:06:35 -08:00
|
|
|
for (const bank of banks) {
|
2023-01-10 07:51:03 -08:00
|
|
|
const tokenPositionExists =
|
2023-01-13 02:23:37 -08:00
|
|
|
tokenPositionIndices.indexOf(bank.tokenIndex) > -1;
|
2023-01-07 14:06:35 -08:00
|
|
|
if (!tokenPositionExists) {
|
2023-01-10 07:51:03 -08:00
|
|
|
const inactiveTokenPosition = tokenPositionIndices.findIndex(
|
|
|
|
(index) => index === TokenPosition.TokenIndexUnset,
|
|
|
|
);
|
2023-01-13 02:23:37 -08:00
|
|
|
if (inactiveTokenPosition != -1) {
|
2023-01-10 07:51:03 -08:00
|
|
|
tokenPositionIndices[inactiveTokenPosition] = bank.tokenIndex;
|
2022-08-10 01:15:28 -07:00
|
|
|
}
|
2022-05-31 18:38:47 -07:00
|
|
|
}
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
2023-04-25 02:13:40 -07:00
|
|
|
const mintInfos = uniq(
|
|
|
|
tokenPositionIndices
|
|
|
|
.filter((tokenIndex) => tokenIndex !== TokenPosition.TokenIndexUnset)
|
|
|
|
.map((tokenIndex) => group.mintInfosMapByTokenIndex.get(tokenIndex)!),
|
|
|
|
(mintInfo) => {
|
|
|
|
mintInfo.tokenIndex;
|
|
|
|
},
|
|
|
|
);
|
2022-06-27 02:27:17 -07:00
|
|
|
healthRemainingAccounts.push(
|
|
|
|
...mintInfos.map((mintInfo) => mintInfo.firstBank()),
|
|
|
|
);
|
2022-04-08 03:30:21 -07:00
|
|
|
healthRemainingAccounts.push(
|
2022-06-11 04:49:45 -07:00
|
|
|
...mintInfos.map((mintInfo) => mintInfo.oracle),
|
2022-04-08 03:30:21 -07:00
|
|
|
);
|
2022-08-31 02:36:44 -07:00
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
// Insert any extra perp markets in the free perp position slots
|
2023-04-25 02:13:40 -07:00
|
|
|
const perpPositionsMarketIndices = mangoAccounts
|
|
|
|
.map((mangoAccount) => mangoAccount.perps.map((p) => p.marketIndex))
|
|
|
|
.flat();
|
2023-01-07 14:06:35 -08:00
|
|
|
for (const perpMarket of perpMarkets) {
|
2023-01-10 07:51:03 -08:00
|
|
|
const perpPositionExists =
|
2023-04-24 05:48:53 -07:00
|
|
|
perpPositionsMarketIndices.indexOf(perpMarket.perpMarketIndex) > -1;
|
2023-01-07 14:06:35 -08:00
|
|
|
if (!perpPositionExists) {
|
2023-04-24 05:48:53 -07:00
|
|
|
const inactivePerpPosition = perpPositionsMarketIndices.findIndex(
|
2023-01-10 07:51:03 -08:00
|
|
|
(perpIdx) => perpIdx === PerpPosition.PerpMarketIndexUnset,
|
|
|
|
);
|
2023-01-13 02:23:37 -08:00
|
|
|
if (inactivePerpPosition != -1) {
|
2023-04-24 05:48:53 -07:00
|
|
|
perpPositionsMarketIndices[inactivePerpPosition] =
|
2023-01-10 07:51:03 -08:00
|
|
|
perpMarket.perpMarketIndex;
|
2022-09-21 03:50:10 -07:00
|
|
|
}
|
2022-08-31 02:36:44 -07:00
|
|
|
}
|
|
|
|
}
|
2023-04-25 02:13:40 -07:00
|
|
|
const allPerpMarkets = uniq(
|
|
|
|
perpPositionsMarketIndices
|
|
|
|
.filter(
|
|
|
|
(perpMarktIndex) =>
|
|
|
|
perpMarktIndex !== PerpPosition.PerpMarketIndexUnset,
|
|
|
|
)
|
|
|
|
.map((perpIdx) => group.getPerpMarketByMarketIndex(perpIdx)!),
|
|
|
|
(pm) => pm.perpMarketIndex,
|
|
|
|
);
|
2022-09-21 03:50:10 -07:00
|
|
|
healthRemainingAccounts.push(
|
|
|
|
...allPerpMarkets.map((perp) => perp.publicKey),
|
|
|
|
);
|
|
|
|
healthRemainingAccounts.push(...allPerpMarkets.map((perp) => perp.oracle));
|
2022-08-31 02:36:44 -07:00
|
|
|
|
2023-04-24 05:48:53 -07:00
|
|
|
// Insert any extra open orders accounts in the cooresponding free serum market slot
|
|
|
|
const serumPositionMarketIndices = mangoAccounts
|
|
|
|
.map((mangoAccount) =>
|
|
|
|
mangoAccount.serum3.map((s) => ({
|
|
|
|
marketIndex: s.marketIndex,
|
|
|
|
openOrders: s.openOrders,
|
|
|
|
})),
|
|
|
|
)
|
|
|
|
.flat();
|
2023-01-07 14:06:35 -08:00
|
|
|
for (const [serum3Market, openOrderPk] of openOrdersForMarket) {
|
2023-01-10 07:51:03 -08:00
|
|
|
const ooPositionExists =
|
2023-04-24 05:48:53 -07:00
|
|
|
serumPositionMarketIndices.findIndex(
|
2023-01-10 07:51:03 -08:00
|
|
|
(i) => i.marketIndex === serum3Market.marketIndex,
|
2023-01-13 02:23:37 -08:00
|
|
|
) > -1;
|
2023-01-07 14:06:35 -08:00
|
|
|
if (!ooPositionExists) {
|
2023-04-24 05:48:53 -07:00
|
|
|
const inactiveSerumPosition = serumPositionMarketIndices.findIndex(
|
2023-01-10 07:51:03 -08:00
|
|
|
(serumPos) =>
|
|
|
|
serumPos.marketIndex === Serum3Orders.Serum3MarketIndexUnset,
|
2023-01-07 14:06:35 -08:00
|
|
|
);
|
2023-01-13 02:23:37 -08:00
|
|
|
if (inactiveSerumPosition != -1) {
|
2023-04-24 05:48:53 -07:00
|
|
|
serumPositionMarketIndices[inactiveSerumPosition].marketIndex =
|
2023-01-13 02:23:37 -08:00
|
|
|
serum3Market.marketIndex;
|
2023-04-24 05:48:53 -07:00
|
|
|
serumPositionMarketIndices[inactiveSerumPosition].openOrders =
|
|
|
|
openOrderPk;
|
2023-01-07 14:06:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 02:36:44 -07:00
|
|
|
healthRemainingAccounts.push(
|
2023-04-24 05:48:53 -07:00
|
|
|
...serumPositionMarketIndices
|
2023-01-10 07:51:03 -08:00
|
|
|
.filter(
|
|
|
|
(serumPosition) =>
|
|
|
|
serumPosition.marketIndex !== Serum3Orders.Serum3MarketIndexUnset,
|
|
|
|
)
|
|
|
|
.map((serumPosition) => serumPosition.openOrders),
|
2022-08-31 02:36:44 -07:00
|
|
|
);
|
|
|
|
|
2022-08-03 09:05:16 -07:00
|
|
|
return healthRemainingAccounts;
|
|
|
|
}
|
2022-12-14 06:15:35 -08:00
|
|
|
|
|
|
|
public async modifyPerpOrder(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
perpMarketIndex: PerpMarketIndex,
|
|
|
|
orderId: BN,
|
|
|
|
side: PerpOrderSide,
|
|
|
|
price: number,
|
|
|
|
quantity: number,
|
|
|
|
maxQuoteQuantity?: number,
|
|
|
|
clientOrderId?: number,
|
|
|
|
orderType?: PerpOrderType,
|
|
|
|
reduceOnly?: boolean,
|
|
|
|
expiryTimestamp?: number,
|
|
|
|
limit?: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-12-14 06:15:35 -08:00
|
|
|
const transactionInstructions: TransactionInstruction[] = [];
|
|
|
|
const [cancelOrderIx, placeOrderIx] = await Promise.all([
|
|
|
|
this.perpCancelOrderIx(group, mangoAccount, perpMarketIndex, orderId),
|
|
|
|
this.perpPlaceOrderIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
perpMarketIndex,
|
|
|
|
side,
|
|
|
|
price,
|
|
|
|
quantity,
|
|
|
|
maxQuoteQuantity,
|
|
|
|
clientOrderId,
|
|
|
|
orderType,
|
|
|
|
reduceOnly,
|
|
|
|
expiryTimestamp,
|
|
|
|
limit,
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
transactionInstructions.push(cancelOrderIx, placeOrderIx);
|
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
2022-12-15 01:40:45 -08:00
|
|
|
transactionInstructions,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
public async modifySerum3Order(
|
|
|
|
group: Group,
|
|
|
|
orderId: BN,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
externalMarketPk: PublicKey,
|
|
|
|
side: Serum3Side,
|
|
|
|
price: number,
|
|
|
|
size: number,
|
|
|
|
selfTradeBehavior: Serum3SelfTradeBehavior,
|
|
|
|
orderType: Serum3OrderType,
|
|
|
|
clientOrderId: number,
|
|
|
|
limit: number,
|
2023-08-11 10:12:13 -07:00
|
|
|
): Promise<MangoSignatureStatus> {
|
2022-12-15 01:40:45 -08:00
|
|
|
const transactionInstructions: TransactionInstruction[] = [];
|
2022-12-15 11:06:10 -08:00
|
|
|
const [cancelOrderIx, settleIx, placeOrderIx] = await Promise.all([
|
2022-12-15 01:40:45 -08:00
|
|
|
this.serum3CancelOrderIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
side,
|
|
|
|
orderId,
|
|
|
|
),
|
2023-03-03 05:04:45 -08:00
|
|
|
this.serum3SettleFundsV2Ix(group, mangoAccount, externalMarketPk),
|
2022-12-15 01:40:45 -08:00
|
|
|
this.serum3PlaceOrderIx(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
externalMarketPk,
|
|
|
|
side,
|
|
|
|
price,
|
|
|
|
size,
|
|
|
|
selfTradeBehavior,
|
|
|
|
orderType,
|
|
|
|
clientOrderId,
|
|
|
|
limit,
|
|
|
|
),
|
|
|
|
]);
|
2023-01-04 23:30:15 -08:00
|
|
|
transactionInstructions.push(cancelOrderIx, settleIx, ...placeOrderIx);
|
2022-12-15 01:40:45 -08:00
|
|
|
|
2023-02-03 04:55:46 -08:00
|
|
|
return await this.sendAndConfirmTransactionForGroup(
|
|
|
|
group,
|
2022-12-14 06:15:35 -08:00
|
|
|
transactionInstructions,
|
|
|
|
);
|
|
|
|
}
|
2023-08-09 14:13:55 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an estimate of a prioritization fee for a set of instructions.
|
|
|
|
*
|
2023-08-11 06:18:41 -07:00
|
|
|
* The estimate is based on the median fees of writable accounts that will be involved in the transaction.
|
2023-08-09 14:13:55 -07:00
|
|
|
*
|
|
|
|
* @param ixs - the instructions that make up the transaction
|
|
|
|
* @returns prioritizationFeeEstimate -- in microLamports
|
|
|
|
*/
|
|
|
|
public async estimatePrioritizationFee(
|
|
|
|
ixs: TransactionInstruction[],
|
|
|
|
): Promise<number> {
|
2023-08-11 06:18:41 -07:00
|
|
|
const writableAccounts = ixs
|
|
|
|
.map((x) => x.keys.filter((a) => a.isWritable).map((k) => k.pubkey))
|
|
|
|
.flat();
|
|
|
|
const uniqueWritableAccounts = uniq(
|
|
|
|
writableAccounts.map((x) => x.toBase58()),
|
|
|
|
)
|
|
|
|
.map((a) => new PublicKey(a))
|
|
|
|
.slice(0, MAX_RECENT_PRIORITY_FEE_ACCOUNTS);
|
|
|
|
|
2023-08-09 14:13:55 -07:00
|
|
|
const priorityFees = await this.connection.getRecentPrioritizationFees({
|
2023-08-11 06:18:41 -07:00
|
|
|
lockedWritableAccounts: uniqueWritableAccounts,
|
2023-08-09 14:13:55 -07:00
|
|
|
});
|
|
|
|
|
2023-08-11 06:18:41 -07:00
|
|
|
if (priorityFees.length < 1) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-08-09 14:13:55 -07:00
|
|
|
// get max priority fee per slot (and sort by slot from old to new)
|
2023-08-11 06:18:41 -07:00
|
|
|
const maxFeeBySlot = mapValues(groupBy(priorityFees, 'slot'), (items) =>
|
|
|
|
maxBy(items, 'prioritizationFee'),
|
2023-08-09 14:13:55 -07:00
|
|
|
);
|
|
|
|
const maximumFees = Object.values(maxFeeBySlot).sort(
|
2023-08-11 06:18:41 -07:00
|
|
|
(a: RecentPrioritizationFees, b: RecentPrioritizationFees) =>
|
|
|
|
a.slot - b.slot,
|
|
|
|
) as RecentPrioritizationFees[];
|
|
|
|
|
|
|
|
// get median of last 20 fees
|
|
|
|
const recentFees = maximumFees.slice(Math.max(maximumFees.length - 20, 0));
|
|
|
|
const mid = Math.floor(recentFees.length / 2);
|
|
|
|
const medianFee =
|
|
|
|
recentFees.length % 2 !== 0
|
|
|
|
? recentFees[mid].prioritizationFee
|
|
|
|
: (recentFees[mid - 1].prioritizationFee +
|
|
|
|
recentFees[mid].prioritizationFee) /
|
|
|
|
2;
|
|
|
|
|
|
|
|
return Math.max(1, Math.ceil(medianFee));
|
2023-08-09 14:13:55 -07:00
|
|
|
}
|
2022-02-23 02:09:17 -08:00
|
|
|
}
|