@drift-labs/sdk 0.2.0-temp.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -29
- package/lib/accounts/bulkAccountLoader.d.ts +2 -0
- package/lib/accounts/bulkAccountLoader.js +36 -29
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.d.ts +2 -2
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
- package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
- package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
- package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
- package/lib/accounts/types.d.ts +26 -15
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
- package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.d.ts +15 -9
- package/lib/addresses/pda.js +73 -35
- package/lib/adminClient.d.ts +65 -0
- package/lib/adminClient.js +637 -0
- package/lib/config.d.ts +10 -10
- package/lib/config.js +26 -22
- package/lib/constants/numericConstants.d.ts +29 -12
- package/lib/constants/numericConstants.js +40 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +20 -9
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +63 -0
- package/lib/dlob/DLOB.d.ts +82 -0
- package/lib/dlob/DLOB.js +694 -0
- package/lib/dlob/DLOBNode.d.ts +54 -0
- package/lib/dlob/DLOBNode.js +77 -0
- package/lib/dlob/NodeList.d.ts +27 -0
- package/lib/dlob/NodeList.js +144 -0
- package/lib/driftClient.d.ts +234 -0
- package/lib/driftClient.js +2108 -0
- package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
- package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
- package/lib/events/eventList.d.ts +0 -1
- package/lib/events/eventList.js +0 -7
- package/lib/events/eventSubscriber.d.ts +5 -2
- package/lib/events/eventSubscriber.js +25 -11
- package/lib/events/fetchLogs.d.ts +13 -2
- package/lib/events/fetchLogs.js +45 -14
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +7 -3
- package/lib/events/sort.js +8 -11
- package/lib/events/txEventCache.d.ts +0 -2
- package/lib/events/txEventCache.js +0 -14
- package/lib/events/types.d.ts +11 -3
- package/lib/events/types.js +8 -0
- package/lib/events/webSocketLogProvider.js +1 -1
- package/lib/examples/makeTradeExample.js +30 -18
- package/lib/factory/bigNum.d.ts +8 -4
- package/lib/factory/bigNum.js +109 -19
- package/lib/idl/drift.json +8392 -0
- package/lib/index.d.ts +29 -12
- package/lib/index.js +29 -12
- package/lib/math/amm.d.ts +9 -6
- package/lib/math/amm.js +91 -38
- package/lib/math/conversion.js +1 -1
- package/lib/math/exchangeStatus.d.ts +4 -0
- package/lib/math/exchangeStatus.js +18 -0
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +23 -21
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +82 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +70 -10
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +16 -6
- package/lib/math/orders.js +97 -17
- package/lib/math/position.d.ts +27 -13
- package/lib/math/position.js +91 -37
- package/lib/math/repeg.js +17 -8
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +192 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +27 -31
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +4 -4
- package/lib/orderParams.js +12 -4
- package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
- package/lib/serum/serumFulfillmentConfigMap.js +17 -0
- package/lib/serum/serumSubscriber.d.ts +27 -0
- package/lib/serum/serumSubscriber.js +56 -0
- package/lib/serum/types.d.ts +11 -0
- package/{src/oracles → lib/serum}/types.js +0 -0
- package/lib/tokenFaucet.d.ts +1 -0
- package/lib/tokenFaucet.js +23 -12
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/types.d.ts +631 -222
- package/lib/types.js +137 -24
- package/lib/user.d.ts +228 -0
- package/lib/user.js +959 -0
- package/lib/userConfig.d.ts +14 -0
- package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
- package/lib/userMap/userMap.d.ts +41 -0
- package/lib/userMap/userMap.js +85 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/userName.d.ts +1 -0
- package/lib/userName.js +3 -2
- package/lib/userStats.d.ts +18 -0
- package/lib/userStats.js +49 -0
- package/lib/userStatsConfig.d.ts +14 -0
- package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +12 -5
- package/src/accounts/bulkAccountLoader.ts +44 -34
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +2 -3
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
- package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
- package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
- package/src/accounts/types.ts +35 -15
- package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +3 -4
- package/src/addresses/pda.ts +105 -33
- package/src/adminClient.ts +1207 -0
- package/src/config.ts +41 -34
- package/src/constants/numericConstants.ts +59 -26
- package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
- package/src/constants/spotMarkets.ts +83 -0
- package/src/dlob/DLOB.ts +1120 -0
- package/src/dlob/DLOBNode.ts +155 -0
- package/src/dlob/NodeList.ts +195 -0
- package/src/driftClient.ts +3594 -0
- package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
- package/src/events/eventList.ts +1 -8
- package/src/events/eventSubscriber.ts +36 -14
- package/src/events/fetchLogs.ts +60 -15
- package/src/events/pollingLogProvider.ts +11 -3
- package/src/events/sort.ts +11 -15
- package/src/events/txEventCache.ts +0 -16
- package/src/events/types.ts +27 -2
- package/src/events/webSocketLogProvider.ts +1 -1
- package/src/examples/makeTradeExample.js +152 -75
- package/src/examples/makeTradeExample.ts +44 -28
- package/src/factory/bigNum.ts +150 -22
- package/src/idl/drift.json +8392 -0
- package/src/idl/pyth.json +98 -2
- package/src/index.ts +29 -12
- package/src/math/amm.ts +161 -48
- package/src/math/conversion.ts +2 -2
- package/src/math/exchangeStatus.ts +31 -0
- package/src/math/funding.ts +41 -31
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +133 -0
- package/src/math/market.ts +143 -14
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +168 -26
- package/src/math/position.ts +133 -59
- package/src/math/repeg.ts +19 -9
- package/src/math/spotBalance.ts +319 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +33 -37
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +16 -8
- package/src/serum/serumFulfillmentConfigMap.ts +26 -0
- package/src/serum/serumSubscriber.ts +99 -0
- package/src/serum/types.ts +13 -0
- package/src/tokenFaucet.ts +38 -15
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.ts +2 -1
- package/src/types.ts +594 -195
- package/src/user.ts +1599 -0
- package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
- package/src/userMap/userMap.ts +124 -0
- package/src/userMap/userStatsMap.ts +108 -0
- package/src/userName.ts +2 -1
- package/src/userStats.ts +75 -0
- package/src/userStatsConfig.ts +18 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/tests/bn/test.ts +46 -11
- package/tests/dlob/helpers.ts +619 -0
- package/tests/dlob/test.ts +4586 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
- package/lib/admin.d.ts +0 -44
- package/lib/admin.js +0 -438
- package/lib/clearingHouse.d.ts +0 -146
- package/lib/clearingHouse.js +0 -1154
- package/lib/clearingHouseUser.d.ts +0 -187
- package/lib/clearingHouseUser.js +0 -634
- package/lib/clearingHouseUserConfig.d.ts +0 -14
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -41
- package/lib/constants/markets.d.ts +0 -19
- package/lib/idl/clearing_house.json +0 -4464
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/accounts/bulkAccountLoader.js +0 -197
- package/src/accounts/bulkUserSubscription.js +0 -33
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
- package/src/accounts/pollingOracleSubscriber.js +0 -93
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
- package/src/accounts/pollingUserAccountSubscriber.js +0 -132
- package/src/accounts/types.js +0 -10
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -93
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
- package/src/addresses/marketAddresses.js +0 -26
- package/src/admin.js +0 -517
- package/src/admin.ts +0 -730
- package/src/clearingHouse.ts +0 -1828
- package/src/clearingHouseUser.ts +0 -978
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/config.js +0 -67
- package/src/constants/banks.js +0 -42
- package/src/constants/banks.ts +0 -50
- package/src/constants/markets.js +0 -42
- package/src/constants/numericConstants.js +0 -41
- package/src/events/eventSubscriber.js +0 -139
- package/src/events/fetchLogs.js +0 -50
- package/src/events/pollingLogProvider.js +0 -64
- package/src/events/sort.js +0 -44
- package/src/events/txEventCache.js +0 -71
- package/src/events/types.js +0 -20
- package/src/events/webSocketLogProvider.js +0 -41
- package/src/factory/bigNum.js +0 -390
- package/src/factory/oracleClient.js +0 -20
- package/src/idl/clearing_house.json +0 -4464
- package/src/index.js +0 -69
- package/src/math/amm.js +0 -369
- package/src/math/auction.js +0 -42
- package/src/math/bankBalance.ts +0 -112
- package/src/math/conversion.js +0 -11
- package/src/math/funding.js +0 -248
- package/src/math/oracles.js +0 -26
- package/src/math/repeg.js +0 -128
- package/src/math/state.js +0 -15
- package/src/math/state.ts +0 -14
- package/src/math/trade.js +0 -253
- package/src/math/utils.js +0 -26
- package/src/math/utils.js.map +0 -1
- package/src/mockUSDCFaucet.js +0 -280
- package/src/oracles/oracleClientCache.js +0 -19
- package/src/oracles/pythClient.js +0 -46
- package/src/oracles/quoteAssetOracleClient.js +0 -32
- package/src/oracles/switchboardClient.js +0 -69
- package/src/orderParams.js +0 -20
- package/src/orders.ts +0 -245
- package/src/slot/SlotSubscriber.js +0 -39
- package/src/tokenFaucet.js +0 -189
- package/src/types.js +0 -125
- package/src/userName.js +0 -20
- package/src/wallet.js +0 -35
|
@@ -0,0 +1,3594 @@
|
|
|
1
|
+
import { AnchorProvider, BN, Idl, Program } from '@project-serum/anchor';
|
|
2
|
+
import bs58 from 'bs58';
|
|
3
|
+
import {
|
|
4
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
5
|
+
Token,
|
|
6
|
+
TOKEN_PROGRAM_ID,
|
|
7
|
+
} from '@solana/spl-token';
|
|
8
|
+
import {
|
|
9
|
+
StateAccount,
|
|
10
|
+
IWallet,
|
|
11
|
+
PositionDirection,
|
|
12
|
+
UserAccount,
|
|
13
|
+
PerpMarketAccount,
|
|
14
|
+
OrderParams,
|
|
15
|
+
Order,
|
|
16
|
+
SpotMarketAccount,
|
|
17
|
+
SpotPosition,
|
|
18
|
+
MakerInfo,
|
|
19
|
+
TakerInfo,
|
|
20
|
+
OptionalOrderParams,
|
|
21
|
+
DefaultOrderParams,
|
|
22
|
+
OrderType,
|
|
23
|
+
ReferrerInfo,
|
|
24
|
+
MarketType,
|
|
25
|
+
SerumV3FulfillmentConfigAccount,
|
|
26
|
+
isVariant,
|
|
27
|
+
} from './types';
|
|
28
|
+
import * as anchor from '@project-serum/anchor';
|
|
29
|
+
import driftIDL from './idl/drift.json';
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
Connection,
|
|
33
|
+
PublicKey,
|
|
34
|
+
TransactionSignature,
|
|
35
|
+
ConfirmOptions,
|
|
36
|
+
Transaction,
|
|
37
|
+
TransactionInstruction,
|
|
38
|
+
AccountMeta,
|
|
39
|
+
Keypair,
|
|
40
|
+
LAMPORTS_PER_SOL,
|
|
41
|
+
Signer,
|
|
42
|
+
SystemProgram,
|
|
43
|
+
ComputeBudgetProgram,
|
|
44
|
+
} from '@solana/web3.js';
|
|
45
|
+
|
|
46
|
+
import { TokenFaucet } from './tokenFaucet';
|
|
47
|
+
import { EventEmitter } from 'events';
|
|
48
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
49
|
+
import {
|
|
50
|
+
getDriftSignerPublicKey,
|
|
51
|
+
getDriftStateAccountPublicKey,
|
|
52
|
+
getInsuranceFundStakeAccountPublicKey,
|
|
53
|
+
getPerpMarketPublicKey,
|
|
54
|
+
getSerumFulfillmentConfigPublicKey,
|
|
55
|
+
getSerumSignerPublicKey,
|
|
56
|
+
getSpotMarketPublicKey,
|
|
57
|
+
getUserAccountPublicKey,
|
|
58
|
+
getUserAccountPublicKeySync,
|
|
59
|
+
getUserStatsAccountPublicKey,
|
|
60
|
+
} from './addresses/pda';
|
|
61
|
+
import {
|
|
62
|
+
DriftClientAccountSubscriber,
|
|
63
|
+
DriftClientAccountEvents,
|
|
64
|
+
DataAndSlot,
|
|
65
|
+
} from './accounts/types';
|
|
66
|
+
import { TxSender } from './tx/types';
|
|
67
|
+
import { wrapInTx } from './tx/utils';
|
|
68
|
+
import { QUOTE_SPOT_MARKET_INDEX, ZERO } from './constants/numericConstants';
|
|
69
|
+
import { findDirectionToClose, positionIsAvailable } from './math/position';
|
|
70
|
+
import { getTokenAmount } from './math/spotBalance';
|
|
71
|
+
import { DEFAULT_USER_NAME, encodeName } from './userName';
|
|
72
|
+
import { OraclePriceData } from './oracles/types';
|
|
73
|
+
import { DriftClientConfig } from './driftClientConfig';
|
|
74
|
+
import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber';
|
|
75
|
+
import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber';
|
|
76
|
+
import { RetryTxSender } from './tx/retryTxSender';
|
|
77
|
+
import { User } from './user';
|
|
78
|
+
import { UserSubscriptionConfig } from './userConfig';
|
|
79
|
+
import { getMarketsAndOraclesForSubscription } from './config';
|
|
80
|
+
import { WRAPPED_SOL_MINT } from './constants/spotMarkets';
|
|
81
|
+
import { UserStats } from './userStats';
|
|
82
|
+
import { isSpotPositionAvailable } from './math/spotPosition';
|
|
83
|
+
|
|
84
|
+
type RemainingAccountParams = {
|
|
85
|
+
userAccounts: UserAccount[];
|
|
86
|
+
writablePerpMarketIndexes?: number[];
|
|
87
|
+
writableSpotMarketIndexes?: number[];
|
|
88
|
+
readablePerpMarketIndex?: number;
|
|
89
|
+
readableSpotMarketIndexes?: number[];
|
|
90
|
+
useMarketLastSlotCache?: boolean;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* # DriftClient
|
|
95
|
+
* This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
|
|
96
|
+
*/
|
|
97
|
+
export class DriftClient {
|
|
98
|
+
connection: Connection;
|
|
99
|
+
wallet: IWallet;
|
|
100
|
+
public program: Program;
|
|
101
|
+
provider: AnchorProvider;
|
|
102
|
+
opts?: ConfirmOptions;
|
|
103
|
+
users = new Map<number, User>();
|
|
104
|
+
userStats?: UserStats;
|
|
105
|
+
activeSubAccountId: number;
|
|
106
|
+
userAccountSubscriptionConfig: UserSubscriptionConfig;
|
|
107
|
+
accountSubscriber: DriftClientAccountSubscriber;
|
|
108
|
+
eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
|
|
109
|
+
_isSubscribed = false;
|
|
110
|
+
txSender: TxSender;
|
|
111
|
+
perpMarketLastSlotCache = new Map<number, number>();
|
|
112
|
+
spotMarketLastSlotCache = new Map<number, number>();
|
|
113
|
+
authority: PublicKey;
|
|
114
|
+
|
|
115
|
+
public get isSubscribed() {
|
|
116
|
+
return this._isSubscribed && this.accountSubscriber.isSubscribed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public set isSubscribed(val: boolean) {
|
|
120
|
+
this._isSubscribed = val;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public constructor(config: DriftClientConfig) {
|
|
124
|
+
this.connection = config.connection;
|
|
125
|
+
this.wallet = config.wallet;
|
|
126
|
+
this.opts = config.opts || AnchorProvider.defaultOptions();
|
|
127
|
+
this.provider = new AnchorProvider(
|
|
128
|
+
config.connection,
|
|
129
|
+
config.wallet,
|
|
130
|
+
this.opts
|
|
131
|
+
);
|
|
132
|
+
this.program = new Program(
|
|
133
|
+
driftIDL as Idl,
|
|
134
|
+
config.programID,
|
|
135
|
+
this.provider
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
this.authority = config.authority ?? this.wallet.publicKey;
|
|
139
|
+
const subAccountIds = config.subAccountIds ?? [0];
|
|
140
|
+
this.activeSubAccountId = config.activeSubAccountId ?? subAccountIds[0];
|
|
141
|
+
this.userAccountSubscriptionConfig =
|
|
142
|
+
config.accountSubscription?.type === 'polling'
|
|
143
|
+
? {
|
|
144
|
+
type: 'polling',
|
|
145
|
+
accountLoader: config.accountSubscription.accountLoader,
|
|
146
|
+
}
|
|
147
|
+
: {
|
|
148
|
+
type: 'websocket',
|
|
149
|
+
};
|
|
150
|
+
this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
|
|
151
|
+
if (config.userStats) {
|
|
152
|
+
this.userStats = new UserStats({
|
|
153
|
+
driftClient: this,
|
|
154
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
155
|
+
this.program.programId,
|
|
156
|
+
this.authority
|
|
157
|
+
),
|
|
158
|
+
accountSubscription: this.userAccountSubscriptionConfig,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let perpMarketIndexes = config.perpMarketIndexes;
|
|
163
|
+
let spotMarketIndexes = config.spotMarketIndexes;
|
|
164
|
+
let oracleInfos = config.oracleInfos;
|
|
165
|
+
if (config.env) {
|
|
166
|
+
const {
|
|
167
|
+
perpMarketIndexes: envPerpMarketIndexes,
|
|
168
|
+
spotMarketIndexes: envSpotMarketIndexes,
|
|
169
|
+
oracleInfos: envOralceInfos,
|
|
170
|
+
} = getMarketsAndOraclesForSubscription(config.env);
|
|
171
|
+
perpMarketIndexes = perpMarketIndexes
|
|
172
|
+
? perpMarketIndexes
|
|
173
|
+
: envPerpMarketIndexes;
|
|
174
|
+
spotMarketIndexes = spotMarketIndexes
|
|
175
|
+
? spotMarketIndexes
|
|
176
|
+
: envSpotMarketIndexes;
|
|
177
|
+
oracleInfos = oracleInfos ? oracleInfos : envOralceInfos;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (config.accountSubscription?.type === 'polling') {
|
|
181
|
+
this.accountSubscriber = new PollingDriftClientAccountSubscriber(
|
|
182
|
+
this.program,
|
|
183
|
+
config.accountSubscription.accountLoader,
|
|
184
|
+
perpMarketIndexes ?? [],
|
|
185
|
+
spotMarketIndexes ?? [],
|
|
186
|
+
oracleInfos ?? []
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
this.accountSubscriber = new WebSocketDriftClientAccountSubscriber(
|
|
190
|
+
this.program,
|
|
191
|
+
config.perpMarketIndexes ?? [],
|
|
192
|
+
config.spotMarketIndexes ?? [],
|
|
193
|
+
config.oracleInfos ?? []
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
|
197
|
+
this.txSender = new RetryTxSender(
|
|
198
|
+
this.provider,
|
|
199
|
+
config.txSenderConfig?.timeout,
|
|
200
|
+
config.txSenderConfig?.retrySleep,
|
|
201
|
+
config.txSenderConfig?.additionalConnections
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
createUsers(
|
|
206
|
+
subAccountIds: number[],
|
|
207
|
+
accountSubscriptionConfig: UserSubscriptionConfig
|
|
208
|
+
): void {
|
|
209
|
+
for (const subAccountId of subAccountIds) {
|
|
210
|
+
const user = this.createUser(subAccountId, accountSubscriptionConfig);
|
|
211
|
+
this.users.set(subAccountId, user);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
createUser(
|
|
216
|
+
subAccountId: number,
|
|
217
|
+
accountSubscriptionConfig: UserSubscriptionConfig
|
|
218
|
+
): User {
|
|
219
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
220
|
+
this.program.programId,
|
|
221
|
+
this.authority,
|
|
222
|
+
subAccountId
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
return new User({
|
|
226
|
+
driftClient: this,
|
|
227
|
+
userAccountPublicKey,
|
|
228
|
+
accountSubscription: accountSubscriptionConfig,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public async subscribe(): Promise<boolean> {
|
|
233
|
+
let subscribePromises = this.subscribeUsers().concat(
|
|
234
|
+
this.accountSubscriber.subscribe()
|
|
235
|
+
);
|
|
236
|
+
if (this.userStats !== undefined) {
|
|
237
|
+
subscribePromises = subscribePromises.concat(this.userStats.subscribe());
|
|
238
|
+
}
|
|
239
|
+
this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
|
|
240
|
+
(success, prevSuccess) => success && prevSuccess
|
|
241
|
+
);
|
|
242
|
+
return this.isSubscribed;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
subscribeUsers(): Promise<boolean>[] {
|
|
246
|
+
return [...this.users.values()].map((user) => user.subscribe());
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Forces the accountSubscriber to fetch account updates from rpc
|
|
251
|
+
*/
|
|
252
|
+
public async fetchAccounts(): Promise<void> {
|
|
253
|
+
let promises = [...this.users.values()]
|
|
254
|
+
.map((user) => user.fetchAccounts())
|
|
255
|
+
.concat(this.accountSubscriber.fetch());
|
|
256
|
+
if (this.userStats) {
|
|
257
|
+
promises = promises.concat(this.userStats.fetchAccounts());
|
|
258
|
+
}
|
|
259
|
+
await Promise.all(promises);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public async unsubscribe(): Promise<void> {
|
|
263
|
+
let unsubscribePromises = this.unsubscribeUsers().concat(
|
|
264
|
+
this.accountSubscriber.unsubscribe()
|
|
265
|
+
);
|
|
266
|
+
if (this.userStats !== undefined) {
|
|
267
|
+
unsubscribePromises = unsubscribePromises.concat(
|
|
268
|
+
this.userStats.unsubscribe()
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
await Promise.all(unsubscribePromises);
|
|
272
|
+
this.isSubscribed = false;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
unsubscribeUsers(): Promise<void>[] {
|
|
276
|
+
return [...this.users.values()].map((user) => user.unsubscribe());
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
statePublicKey?: PublicKey;
|
|
280
|
+
public async getStatePublicKey(): Promise<PublicKey> {
|
|
281
|
+
if (this.statePublicKey) {
|
|
282
|
+
return this.statePublicKey;
|
|
283
|
+
}
|
|
284
|
+
this.statePublicKey = await getDriftStateAccountPublicKey(
|
|
285
|
+
this.program.programId
|
|
286
|
+
);
|
|
287
|
+
return this.statePublicKey;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
signerPublicKey?: PublicKey;
|
|
291
|
+
public getSignerPublicKey(): PublicKey {
|
|
292
|
+
if (this.signerPublicKey) {
|
|
293
|
+
return this.signerPublicKey;
|
|
294
|
+
}
|
|
295
|
+
this.signerPublicKey = getDriftSignerPublicKey(this.program.programId);
|
|
296
|
+
return this.signerPublicKey;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public getStateAccount(): StateAccount {
|
|
300
|
+
return this.accountSubscriber.getStateAccountAndSlot().data;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
public getPerpMarketAccount(
|
|
304
|
+
marketIndex: number
|
|
305
|
+
): PerpMarketAccount | undefined {
|
|
306
|
+
return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
public getPerpMarketAccounts(): PerpMarketAccount[] {
|
|
310
|
+
return this.accountSubscriber
|
|
311
|
+
.getMarketAccountsAndSlots()
|
|
312
|
+
.map((value) => value.data);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public getSpotMarketAccount(
|
|
316
|
+
marketIndex: number
|
|
317
|
+
): SpotMarketAccount | undefined {
|
|
318
|
+
return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public getSpotMarketAccounts(): SpotMarketAccount[] {
|
|
322
|
+
return this.accountSubscriber
|
|
323
|
+
.getSpotMarketAccountsAndSlots()
|
|
324
|
+
.map((value) => value.data);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public getQuoteSpotMarketAccount(): SpotMarketAccount {
|
|
328
|
+
return this.accountSubscriber.getSpotMarketAccountAndSlot(
|
|
329
|
+
QUOTE_SPOT_MARKET_INDEX
|
|
330
|
+
).data;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public getOraclePriceDataAndSlot(
|
|
334
|
+
oraclePublicKey: PublicKey
|
|
335
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
336
|
+
return this.accountSubscriber.getOraclePriceDataAndSlot(oraclePublicKey);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
public async getSerumV3FulfillmentConfig(
|
|
340
|
+
serumMarket: PublicKey
|
|
341
|
+
): Promise<SerumV3FulfillmentConfigAccount> {
|
|
342
|
+
const address = await getSerumFulfillmentConfigPublicKey(
|
|
343
|
+
this.program.programId,
|
|
344
|
+
serumMarket
|
|
345
|
+
);
|
|
346
|
+
return (await this.program.account.serumV3FulfillmentConfig.fetch(
|
|
347
|
+
address
|
|
348
|
+
)) as SerumV3FulfillmentConfigAccount;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Update the wallet to use for drift transactions and linked user account
|
|
353
|
+
* @param newWallet
|
|
354
|
+
* @param subAccountIds
|
|
355
|
+
* @param activeSubAccountId
|
|
356
|
+
*/
|
|
357
|
+
public async updateWallet(
|
|
358
|
+
newWallet: IWallet,
|
|
359
|
+
subAccountIds = [0],
|
|
360
|
+
activeSubAccountId = 0
|
|
361
|
+
): Promise<void> {
|
|
362
|
+
const newProvider = new AnchorProvider(
|
|
363
|
+
this.connection,
|
|
364
|
+
newWallet,
|
|
365
|
+
this.opts
|
|
366
|
+
);
|
|
367
|
+
const newProgram = new Program(
|
|
368
|
+
driftIDL as Idl,
|
|
369
|
+
this.program.programId,
|
|
370
|
+
newProvider
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// Update provider for txSender with new wallet details
|
|
374
|
+
this.txSender.provider = newProvider;
|
|
375
|
+
|
|
376
|
+
this.wallet = newWallet;
|
|
377
|
+
this.provider = newProvider;
|
|
378
|
+
this.program = newProgram;
|
|
379
|
+
this.authority = newWallet.publicKey;
|
|
380
|
+
|
|
381
|
+
if (this.isSubscribed) {
|
|
382
|
+
await Promise.all(this.unsubscribeUsers());
|
|
383
|
+
|
|
384
|
+
if (this.userStats) {
|
|
385
|
+
await this.userStats.unsubscribe();
|
|
386
|
+
|
|
387
|
+
this.userStats = new UserStats({
|
|
388
|
+
driftClient: this,
|
|
389
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
390
|
+
this.program.programId,
|
|
391
|
+
this.authority
|
|
392
|
+
),
|
|
393
|
+
accountSubscription: this.userAccountSubscriptionConfig,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
this.users.clear();
|
|
398
|
+
this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
|
|
399
|
+
if (this.isSubscribed) {
|
|
400
|
+
await Promise.all(this.subscribeUsers());
|
|
401
|
+
|
|
402
|
+
if (this.userStats) {
|
|
403
|
+
await this.userStats.subscribe();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.activeSubAccountId = activeSubAccountId;
|
|
408
|
+
this.userStatsAccountPublicKey = undefined;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
public async switchActiveUser(subAccountId: number): Promise<void> {
|
|
412
|
+
this.activeSubAccountId = subAccountId;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public async addUser(subAccountId: number): Promise<void> {
|
|
416
|
+
if (this.users.has(subAccountId)) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const user = this.createUser(
|
|
421
|
+
subAccountId,
|
|
422
|
+
this.userAccountSubscriptionConfig
|
|
423
|
+
);
|
|
424
|
+
await user.subscribe();
|
|
425
|
+
this.users.set(subAccountId, user);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
public async initializeUserAccount(
|
|
429
|
+
subAccountId = 0,
|
|
430
|
+
name = DEFAULT_USER_NAME,
|
|
431
|
+
referrerInfo?: ReferrerInfo
|
|
432
|
+
): Promise<[TransactionSignature, PublicKey]> {
|
|
433
|
+
const [userAccountPublicKey, initializeUserAccountIx] =
|
|
434
|
+
await this.getInitializeUserInstructions(
|
|
435
|
+
subAccountId,
|
|
436
|
+
name,
|
|
437
|
+
referrerInfo
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const tx = new Transaction();
|
|
441
|
+
if (subAccountId === 0) {
|
|
442
|
+
if (
|
|
443
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
444
|
+
) {
|
|
445
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
tx.add(initializeUserAccountIx);
|
|
449
|
+
const { txSig } = await this.txSender.send(tx, [], this.opts);
|
|
450
|
+
return [txSig, userAccountPublicKey];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async getInitializeUserInstructions(
|
|
454
|
+
subAccountId = 0,
|
|
455
|
+
name = DEFAULT_USER_NAME,
|
|
456
|
+
referrerInfo?: ReferrerInfo
|
|
457
|
+
): Promise<[PublicKey, TransactionInstruction]> {
|
|
458
|
+
const userAccountPublicKey = await getUserAccountPublicKey(
|
|
459
|
+
this.program.programId,
|
|
460
|
+
this.wallet.publicKey,
|
|
461
|
+
subAccountId
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const remainingAccounts = new Array<AccountMeta>();
|
|
465
|
+
if (referrerInfo !== undefined) {
|
|
466
|
+
remainingAccounts.push({
|
|
467
|
+
pubkey: referrerInfo.referrer,
|
|
468
|
+
isWritable: true,
|
|
469
|
+
isSigner: false,
|
|
470
|
+
});
|
|
471
|
+
remainingAccounts.push({
|
|
472
|
+
pubkey: referrerInfo.referrerStats,
|
|
473
|
+
isWritable: true,
|
|
474
|
+
isSigner: false,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const state = this.getStateAccount();
|
|
479
|
+
if (!state.whitelistMint.equals(PublicKey.default)) {
|
|
480
|
+
const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
|
|
481
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
482
|
+
TOKEN_PROGRAM_ID,
|
|
483
|
+
state.whitelistMint,
|
|
484
|
+
this.wallet.publicKey
|
|
485
|
+
);
|
|
486
|
+
remainingAccounts.push({
|
|
487
|
+
pubkey: associatedTokenPublicKey,
|
|
488
|
+
isWritable: false,
|
|
489
|
+
isSigner: false,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const nameBuffer = encodeName(name);
|
|
494
|
+
const initializeUserAccountIx =
|
|
495
|
+
await this.program.instruction.initializeUser(subAccountId, nameBuffer, {
|
|
496
|
+
accounts: {
|
|
497
|
+
user: userAccountPublicKey,
|
|
498
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
499
|
+
authority: this.wallet.publicKey,
|
|
500
|
+
payer: this.wallet.publicKey,
|
|
501
|
+
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
502
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
503
|
+
state: await this.getStatePublicKey(),
|
|
504
|
+
},
|
|
505
|
+
remainingAccounts,
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
return [userAccountPublicKey, initializeUserAccountIx];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async getInitializeUserStatsIx(): Promise<TransactionInstruction> {
|
|
512
|
+
return await this.program.instruction.initializeUserStats({
|
|
513
|
+
accounts: {
|
|
514
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
515
|
+
authority: this.wallet.publicKey,
|
|
516
|
+
payer: this.wallet.publicKey,
|
|
517
|
+
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
518
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
519
|
+
state: await this.getStatePublicKey(),
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
public async updateUserName(
|
|
525
|
+
name: string,
|
|
526
|
+
subAccountId = 0
|
|
527
|
+
): Promise<TransactionSignature> {
|
|
528
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
529
|
+
this.program.programId,
|
|
530
|
+
this.wallet.publicKey,
|
|
531
|
+
subAccountId
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
const nameBuffer = encodeName(name);
|
|
535
|
+
return await this.program.rpc.updateUserName(subAccountId, nameBuffer, {
|
|
536
|
+
accounts: {
|
|
537
|
+
user: userAccountPublicKey,
|
|
538
|
+
authority: this.wallet.publicKey,
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
public async updateUserCustomMarginRatio(
|
|
544
|
+
marginRatio: number,
|
|
545
|
+
subAccountId = 0
|
|
546
|
+
): Promise<TransactionSignature> {
|
|
547
|
+
return await this.program.rpc.updateUserCustomMarginRatio(
|
|
548
|
+
subAccountId,
|
|
549
|
+
marginRatio,
|
|
550
|
+
{
|
|
551
|
+
accounts: {
|
|
552
|
+
user: await this.getUserAccountPublicKey(),
|
|
553
|
+
authority: this.wallet.publicKey,
|
|
554
|
+
},
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
public async updateUserDelegate(
|
|
560
|
+
delegate: PublicKey,
|
|
561
|
+
subAccountId = 0
|
|
562
|
+
): Promise<TransactionSignature> {
|
|
563
|
+
return await this.program.rpc.updateUserDelegate(subAccountId, delegate, {
|
|
564
|
+
accounts: {
|
|
565
|
+
user: await this.getUserAccountPublicKey(),
|
|
566
|
+
authority: this.wallet.publicKey,
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
public async getUserAccountsForDelegate(
|
|
572
|
+
delegate: PublicKey
|
|
573
|
+
): Promise<UserAccount[]> {
|
|
574
|
+
const programAccounts = await this.program.account.user.all([
|
|
575
|
+
{
|
|
576
|
+
memcmp: {
|
|
577
|
+
offset: 40,
|
|
578
|
+
/** data to match, as base-58 encoded string and limited to less than 129 bytes */
|
|
579
|
+
bytes: bs58.encode(delegate.toBuffer()),
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
]);
|
|
583
|
+
|
|
584
|
+
return programAccounts.map(
|
|
585
|
+
(programAccount) => programAccount.account as UserAccount
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
public async getUserAccountsForAuthority(
|
|
590
|
+
authority: PublicKey
|
|
591
|
+
): Promise<UserAccount[]> {
|
|
592
|
+
const programAccounts = await this.program.account.user.all([
|
|
593
|
+
{
|
|
594
|
+
memcmp: {
|
|
595
|
+
offset: 8,
|
|
596
|
+
/** data to match, as base-58 encoded string and limited to less than 129 bytes */
|
|
597
|
+
bytes: bs58.encode(authority.toBuffer()),
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
]);
|
|
601
|
+
|
|
602
|
+
return programAccounts.map(
|
|
603
|
+
(programAccount) => programAccount.account as UserAccount
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
public async deleteUser(subAccountId = 0): Promise<TransactionSignature> {
|
|
608
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
609
|
+
this.program.programId,
|
|
610
|
+
this.wallet.publicKey,
|
|
611
|
+
subAccountId
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
const txSig = await this.program.rpc.deleteUser({
|
|
615
|
+
accounts: {
|
|
616
|
+
user: userAccountPublicKey,
|
|
617
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
618
|
+
authority: this.wallet.publicKey,
|
|
619
|
+
state: await this.getStatePublicKey(),
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
await this.users.get(subAccountId)?.unsubscribe();
|
|
624
|
+
this.users.delete(subAccountId);
|
|
625
|
+
|
|
626
|
+
return txSig;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
public getUser(subAccountId?: number): User {
|
|
630
|
+
subAccountId = subAccountId ?? this.activeSubAccountId;
|
|
631
|
+
if (!this.users.has(subAccountId)) {
|
|
632
|
+
throw new Error(`Clearing House has no user for user id ${subAccountId}`);
|
|
633
|
+
}
|
|
634
|
+
return this.users.get(subAccountId);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
public getUsers(): User[] {
|
|
638
|
+
return [...this.users.values()];
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
public getUserStats(): UserStats {
|
|
642
|
+
return this.userStats;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
userStatsAccountPublicKey: PublicKey;
|
|
646
|
+
public getUserStatsAccountPublicKey(): PublicKey {
|
|
647
|
+
if (this.userStatsAccountPublicKey) {
|
|
648
|
+
return this.userStatsAccountPublicKey;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
|
|
652
|
+
this.program.programId,
|
|
653
|
+
this.authority
|
|
654
|
+
);
|
|
655
|
+
return this.userStatsAccountPublicKey;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
public async getUserAccountPublicKey(): Promise<PublicKey> {
|
|
659
|
+
return this.getUser().userAccountPublicKey;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
public getUserAccount(subAccountId?: number): UserAccount | undefined {
|
|
663
|
+
return this.getUser(subAccountId).getUserAccount();
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
public getUserAccountAndSlot(
|
|
667
|
+
subAccountId?: number
|
|
668
|
+
): DataAndSlot<UserAccount> | undefined {
|
|
669
|
+
return this.getUser(subAccountId).getUserAccountAndSlot();
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
public getSpotPosition(marketIndex: number): SpotPosition | undefined {
|
|
673
|
+
return this.getUserAccount().spotPositions.find(
|
|
674
|
+
(spotPosition) => spotPosition.marketIndex === marketIndex
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
public getQuoteAssetTokenAmount(): BN {
|
|
679
|
+
const spotMarket = this.getSpotMarketAccount(QUOTE_SPOT_MARKET_INDEX);
|
|
680
|
+
const spotPosition = this.getSpotPosition(QUOTE_SPOT_MARKET_INDEX);
|
|
681
|
+
return getTokenAmount(
|
|
682
|
+
spotPosition.scaledBalance,
|
|
683
|
+
spotMarket,
|
|
684
|
+
spotPosition.balanceType
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
public getTokenAmount(marketIndex: number): BN {
|
|
689
|
+
const spotPosition = this.getSpotPosition(marketIndex);
|
|
690
|
+
if (spotPosition === undefined) {
|
|
691
|
+
return ZERO;
|
|
692
|
+
}
|
|
693
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
694
|
+
return getTokenAmount(
|
|
695
|
+
spotPosition.scaledBalance,
|
|
696
|
+
spotMarket,
|
|
697
|
+
spotPosition.balanceType
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
|
|
702
|
+
const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
|
|
703
|
+
this.getRemainingAccountMapsForUsers(params.userAccounts);
|
|
704
|
+
|
|
705
|
+
if (params.useMarketLastSlotCache) {
|
|
706
|
+
const lastUserSlot = this.getUserAccountAndSlot()?.slot;
|
|
707
|
+
for (const [
|
|
708
|
+
marketIndex,
|
|
709
|
+
slot,
|
|
710
|
+
] of this.perpMarketLastSlotCache.entries()) {
|
|
711
|
+
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
712
|
+
// otherwise remove from slot
|
|
713
|
+
if (slot > lastUserSlot) {
|
|
714
|
+
const marketAccount = this.getPerpMarketAccount(marketIndex);
|
|
715
|
+
perpMarketAccountMap.set(marketIndex, {
|
|
716
|
+
pubkey: marketAccount.pubkey,
|
|
717
|
+
isSigner: false,
|
|
718
|
+
isWritable: false,
|
|
719
|
+
});
|
|
720
|
+
oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
|
|
721
|
+
pubkey: marketAccount.amm.oracle,
|
|
722
|
+
isSigner: false,
|
|
723
|
+
isWritable: false,
|
|
724
|
+
});
|
|
725
|
+
} else {
|
|
726
|
+
this.perpMarketLastSlotCache.delete(marketIndex);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
for (const [
|
|
731
|
+
marketIndex,
|
|
732
|
+
slot,
|
|
733
|
+
] of this.spotMarketLastSlotCache.entries()) {
|
|
734
|
+
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
735
|
+
// otherwise remove from slot
|
|
736
|
+
if (slot > lastUserSlot) {
|
|
737
|
+
const marketAccount = this.getSpotMarketAccount(marketIndex);
|
|
738
|
+
spotMarketAccountMap.set(marketIndex, {
|
|
739
|
+
pubkey: marketAccount.pubkey,
|
|
740
|
+
isSigner: false,
|
|
741
|
+
isWritable: false,
|
|
742
|
+
});
|
|
743
|
+
if (!marketAccount.oracle.equals(PublicKey.default)) {
|
|
744
|
+
oracleAccountMap.set(marketAccount.oracle.toString(), {
|
|
745
|
+
pubkey: marketAccount.oracle,
|
|
746
|
+
isSigner: false,
|
|
747
|
+
isWritable: false,
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
} else {
|
|
751
|
+
this.spotMarketLastSlotCache.delete(marketIndex);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (params.readablePerpMarketIndex !== undefined) {
|
|
757
|
+
const marketAccount = this.getPerpMarketAccount(
|
|
758
|
+
params.readablePerpMarketIndex
|
|
759
|
+
);
|
|
760
|
+
perpMarketAccountMap.set(params.readablePerpMarketIndex, {
|
|
761
|
+
pubkey: marketAccount.pubkey,
|
|
762
|
+
isSigner: false,
|
|
763
|
+
isWritable: false,
|
|
764
|
+
});
|
|
765
|
+
oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
|
|
766
|
+
pubkey: marketAccount.amm.oracle,
|
|
767
|
+
isSigner: false,
|
|
768
|
+
isWritable: false,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (params.writablePerpMarketIndexes !== undefined) {
|
|
773
|
+
for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
|
|
774
|
+
const marketAccount = this.getPerpMarketAccount(
|
|
775
|
+
writablePerpMarketIndex
|
|
776
|
+
);
|
|
777
|
+
perpMarketAccountMap.set(writablePerpMarketIndex, {
|
|
778
|
+
pubkey: marketAccount.pubkey,
|
|
779
|
+
isSigner: false,
|
|
780
|
+
isWritable: true,
|
|
781
|
+
});
|
|
782
|
+
oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
|
|
783
|
+
pubkey: marketAccount.amm.oracle,
|
|
784
|
+
isSigner: false,
|
|
785
|
+
isWritable: false,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (params.readableSpotMarketIndexes !== undefined) {
|
|
791
|
+
for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
|
|
792
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
793
|
+
readableSpotMarketIndex
|
|
794
|
+
);
|
|
795
|
+
spotMarketAccountMap.set(readableSpotMarketIndex, {
|
|
796
|
+
pubkey: spotMarketAccount.pubkey,
|
|
797
|
+
isSigner: false,
|
|
798
|
+
isWritable: false,
|
|
799
|
+
});
|
|
800
|
+
if (spotMarketAccount.marketIndex !== 0) {
|
|
801
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
802
|
+
pubkey: spotMarketAccount.oracle,
|
|
803
|
+
isSigner: false,
|
|
804
|
+
isWritable: false,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (params.writableSpotMarketIndexes !== undefined) {
|
|
811
|
+
for (const writableSpotMarketIndex of params.writableSpotMarketIndexes) {
|
|
812
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
813
|
+
writableSpotMarketIndex
|
|
814
|
+
);
|
|
815
|
+
spotMarketAccountMap.set(spotMarketAccount.marketIndex, {
|
|
816
|
+
pubkey: spotMarketAccount.pubkey,
|
|
817
|
+
isSigner: false,
|
|
818
|
+
isWritable: true,
|
|
819
|
+
});
|
|
820
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
821
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
822
|
+
pubkey: spotMarketAccount.oracle,
|
|
823
|
+
isSigner: false,
|
|
824
|
+
isWritable: false,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return [
|
|
831
|
+
...oracleAccountMap.values(),
|
|
832
|
+
...spotMarketAccountMap.values(),
|
|
833
|
+
...perpMarketAccountMap.values(),
|
|
834
|
+
];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): {
|
|
838
|
+
oracleAccountMap: Map<string, AccountMeta>;
|
|
839
|
+
spotMarketAccountMap: Map<number, AccountMeta>;
|
|
840
|
+
perpMarketAccountMap: Map<number, AccountMeta>;
|
|
841
|
+
} {
|
|
842
|
+
const oracleAccountMap = new Map<string, AccountMeta>();
|
|
843
|
+
const spotMarketAccountMap = new Map<number, AccountMeta>();
|
|
844
|
+
const perpMarketAccountMap = new Map<number, AccountMeta>();
|
|
845
|
+
|
|
846
|
+
for (const userAccount of userAccounts) {
|
|
847
|
+
for (const spotPosition of userAccount.spotPositions) {
|
|
848
|
+
if (!isSpotPositionAvailable(spotPosition)) {
|
|
849
|
+
const spotMarket = this.getSpotMarketAccount(
|
|
850
|
+
spotPosition.marketIndex
|
|
851
|
+
);
|
|
852
|
+
spotMarketAccountMap.set(spotPosition.marketIndex, {
|
|
853
|
+
pubkey: spotMarket.pubkey,
|
|
854
|
+
isSigner: false,
|
|
855
|
+
isWritable: false,
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
if (!spotMarket.oracle.equals(PublicKey.default)) {
|
|
859
|
+
oracleAccountMap.set(spotMarket.oracle.toString(), {
|
|
860
|
+
pubkey: spotMarket.oracle,
|
|
861
|
+
isSigner: false,
|
|
862
|
+
isWritable: false,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (
|
|
867
|
+
!spotPosition.openAsks.eq(ZERO) ||
|
|
868
|
+
!spotPosition.openBids.eq(ZERO)
|
|
869
|
+
) {
|
|
870
|
+
spotMarketAccountMap.set(spotPosition.marketIndex, {
|
|
871
|
+
pubkey: this.getQuoteSpotMarketAccount().pubkey,
|
|
872
|
+
isSigner: false,
|
|
873
|
+
isWritable: false,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
for (const position of userAccount.perpPositions) {
|
|
879
|
+
if (!positionIsAvailable(position)) {
|
|
880
|
+
const market = this.getPerpMarketAccount(position.marketIndex);
|
|
881
|
+
perpMarketAccountMap.set(position.marketIndex, {
|
|
882
|
+
pubkey: market.pubkey,
|
|
883
|
+
isWritable: false,
|
|
884
|
+
isSigner: false,
|
|
885
|
+
});
|
|
886
|
+
oracleAccountMap.set(market.amm.oracle.toString(), {
|
|
887
|
+
pubkey: market.amm.oracle,
|
|
888
|
+
isWritable: false,
|
|
889
|
+
isSigner: false,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return {
|
|
896
|
+
oracleAccountMap,
|
|
897
|
+
spotMarketAccountMap,
|
|
898
|
+
perpMarketAccountMap,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
public getOrder(orderId: number): Order | undefined {
|
|
903
|
+
return this.getUserAccount()?.orders.find(
|
|
904
|
+
(order) => order.orderId === orderId
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
public getOrderByUserId(userOrderId: number): Order | undefined {
|
|
909
|
+
return this.getUserAccount()?.orders.find(
|
|
910
|
+
(order) => order.userOrderId === userOrderId
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
public async deposit(
|
|
915
|
+
amount: BN,
|
|
916
|
+
marketIndex: number,
|
|
917
|
+
collateralAccountPublicKey: PublicKey,
|
|
918
|
+
subAccountId?: number,
|
|
919
|
+
reduceOnly = false
|
|
920
|
+
): Promise<TransactionSignature> {
|
|
921
|
+
const tx = new Transaction();
|
|
922
|
+
tx.add(
|
|
923
|
+
ComputeBudgetProgram.requestUnits({
|
|
924
|
+
units: 600_000,
|
|
925
|
+
additionalFee: 0,
|
|
926
|
+
})
|
|
927
|
+
);
|
|
928
|
+
|
|
929
|
+
const additionalSigners: Array<Signer> = [];
|
|
930
|
+
|
|
931
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
932
|
+
|
|
933
|
+
const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
|
|
934
|
+
|
|
935
|
+
const authority = this.authority;
|
|
936
|
+
|
|
937
|
+
const createWSOLTokenAccount =
|
|
938
|
+
isSolMarket && collateralAccountPublicKey.equals(authority);
|
|
939
|
+
|
|
940
|
+
if (createWSOLTokenAccount) {
|
|
941
|
+
const { ixs, signers, pubkey } =
|
|
942
|
+
await this.getWrappedSolAccountCreationIxs(amount, true);
|
|
943
|
+
|
|
944
|
+
collateralAccountPublicKey = pubkey;
|
|
945
|
+
|
|
946
|
+
ixs.forEach((ix) => {
|
|
947
|
+
tx.add(ix);
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
signers.forEach((signer) => additionalSigners.push(signer));
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const depositCollateralIx = await this.getDepositInstruction(
|
|
954
|
+
amount,
|
|
955
|
+
marketIndex,
|
|
956
|
+
collateralAccountPublicKey,
|
|
957
|
+
subAccountId,
|
|
958
|
+
reduceOnly,
|
|
959
|
+
true
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
tx.add(depositCollateralIx);
|
|
963
|
+
|
|
964
|
+
// Close the wrapped sol account at the end of the transaction
|
|
965
|
+
if (createWSOLTokenAccount) {
|
|
966
|
+
tx.add(
|
|
967
|
+
Token.createCloseAccountInstruction(
|
|
968
|
+
TOKEN_PROGRAM_ID,
|
|
969
|
+
collateralAccountPublicKey,
|
|
970
|
+
authority,
|
|
971
|
+
authority,
|
|
972
|
+
[]
|
|
973
|
+
)
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const { txSig, slot } = await this.txSender.send(
|
|
978
|
+
tx,
|
|
979
|
+
additionalSigners,
|
|
980
|
+
this.opts
|
|
981
|
+
);
|
|
982
|
+
this.spotMarketLastSlotCache.set(marketIndex, slot);
|
|
983
|
+
return txSig;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
async getDepositInstruction(
|
|
987
|
+
amount: BN,
|
|
988
|
+
marketIndex: number,
|
|
989
|
+
userTokenAccount: PublicKey,
|
|
990
|
+
subAccountId?: number,
|
|
991
|
+
reduceOnly = false,
|
|
992
|
+
userInitialized = true
|
|
993
|
+
): Promise<TransactionInstruction> {
|
|
994
|
+
const userAccountPublicKey = subAccountId
|
|
995
|
+
? await getUserAccountPublicKey(
|
|
996
|
+
this.program.programId,
|
|
997
|
+
this.authority,
|
|
998
|
+
subAccountId
|
|
999
|
+
)
|
|
1000
|
+
: await this.getUserAccountPublicKey();
|
|
1001
|
+
|
|
1002
|
+
let remainingAccounts = [];
|
|
1003
|
+
if (userInitialized) {
|
|
1004
|
+
remainingAccounts = this.getRemainingAccounts({
|
|
1005
|
+
userAccounts: [this.getUserAccount()],
|
|
1006
|
+
useMarketLastSlotCache: true,
|
|
1007
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
1008
|
+
});
|
|
1009
|
+
} else {
|
|
1010
|
+
remainingAccounts = this.getRemainingAccounts({
|
|
1011
|
+
userAccounts: [],
|
|
1012
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
1017
|
+
|
|
1018
|
+
return await this.program.instruction.deposit(
|
|
1019
|
+
marketIndex,
|
|
1020
|
+
amount,
|
|
1021
|
+
reduceOnly,
|
|
1022
|
+
{
|
|
1023
|
+
accounts: {
|
|
1024
|
+
state: await this.getStatePublicKey(),
|
|
1025
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
1026
|
+
spotMarketVault: spotMarketAccount.vault,
|
|
1027
|
+
user: userAccountPublicKey,
|
|
1028
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
1029
|
+
userTokenAccount: userTokenAccount,
|
|
1030
|
+
authority: this.wallet.publicKey,
|
|
1031
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
1032
|
+
},
|
|
1033
|
+
remainingAccounts,
|
|
1034
|
+
}
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private async checkIfAccountExists(account: PublicKey): Promise<boolean> {
|
|
1039
|
+
try {
|
|
1040
|
+
const accountInfo = await this.connection.getAccountInfo(account);
|
|
1041
|
+
return accountInfo != null;
|
|
1042
|
+
} catch (e) {
|
|
1043
|
+
// Doesn't already exist
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
private async getWrappedSolAccountCreationIxs(
|
|
1049
|
+
amount: BN,
|
|
1050
|
+
isDeposit?: boolean
|
|
1051
|
+
): Promise<{
|
|
1052
|
+
ixs: anchor.web3.TransactionInstruction[];
|
|
1053
|
+
signers: Signer[];
|
|
1054
|
+
pubkey: PublicKey;
|
|
1055
|
+
}> {
|
|
1056
|
+
const wrappedSolAccount = new Keypair();
|
|
1057
|
+
|
|
1058
|
+
const result = {
|
|
1059
|
+
ixs: [],
|
|
1060
|
+
signers: [],
|
|
1061
|
+
pubkey: wrappedSolAccount.publicKey,
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
const rentSpaceLamports = new BN(LAMPORTS_PER_SOL / 100);
|
|
1065
|
+
|
|
1066
|
+
const lamports = isDeposit
|
|
1067
|
+
? amount.add(rentSpaceLamports)
|
|
1068
|
+
: rentSpaceLamports;
|
|
1069
|
+
|
|
1070
|
+
const authority = this.wallet.publicKey;
|
|
1071
|
+
|
|
1072
|
+
result.ixs.push(
|
|
1073
|
+
SystemProgram.createAccount({
|
|
1074
|
+
fromPubkey: authority,
|
|
1075
|
+
newAccountPubkey: wrappedSolAccount.publicKey,
|
|
1076
|
+
lamports: lamports.toNumber(),
|
|
1077
|
+
space: 165,
|
|
1078
|
+
programId: TOKEN_PROGRAM_ID,
|
|
1079
|
+
})
|
|
1080
|
+
);
|
|
1081
|
+
|
|
1082
|
+
result.ixs.push(
|
|
1083
|
+
Token.createInitAccountInstruction(
|
|
1084
|
+
TOKEN_PROGRAM_ID,
|
|
1085
|
+
WRAPPED_SOL_MINT,
|
|
1086
|
+
wrappedSolAccount.publicKey,
|
|
1087
|
+
authority
|
|
1088
|
+
)
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
result.signers.push(wrappedSolAccount);
|
|
1092
|
+
|
|
1093
|
+
return result;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
public getAssociatedTokenAccountCreationIx(
|
|
1097
|
+
tokenMintAddress: PublicKey,
|
|
1098
|
+
associatedTokenAddress: PublicKey
|
|
1099
|
+
): anchor.web3.TransactionInstruction {
|
|
1100
|
+
const createAssociatedAccountIx =
|
|
1101
|
+
Token.createAssociatedTokenAccountInstruction(
|
|
1102
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
1103
|
+
TOKEN_PROGRAM_ID,
|
|
1104
|
+
tokenMintAddress,
|
|
1105
|
+
associatedTokenAddress,
|
|
1106
|
+
this.wallet.publicKey,
|
|
1107
|
+
this.wallet.publicKey
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
return createAssociatedAccountIx;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Creates the Clearing House User account for a user, and deposits some initial collateral
|
|
1115
|
+
* @param amount
|
|
1116
|
+
* @param userTokenAccount
|
|
1117
|
+
* @param marketIndex
|
|
1118
|
+
* @param subAccountId
|
|
1119
|
+
* @param name
|
|
1120
|
+
* @param fromSubAccountId
|
|
1121
|
+
* @returns
|
|
1122
|
+
*/
|
|
1123
|
+
public async initializeUserAccountAndDepositCollateral(
|
|
1124
|
+
amount: BN,
|
|
1125
|
+
userTokenAccount: PublicKey,
|
|
1126
|
+
marketIndex = 0,
|
|
1127
|
+
subAccountId = 0,
|
|
1128
|
+
name = DEFAULT_USER_NAME,
|
|
1129
|
+
fromSubAccountId?: number,
|
|
1130
|
+
referrerInfo?: ReferrerInfo
|
|
1131
|
+
): Promise<[TransactionSignature, PublicKey]> {
|
|
1132
|
+
const [userAccountPublicKey, initializeUserAccountIx] =
|
|
1133
|
+
await this.getInitializeUserInstructions(
|
|
1134
|
+
subAccountId,
|
|
1135
|
+
name,
|
|
1136
|
+
referrerInfo
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
const additionalSigners: Array<Signer> = [];
|
|
1140
|
+
|
|
1141
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
1142
|
+
|
|
1143
|
+
const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
|
|
1144
|
+
|
|
1145
|
+
const tx = new Transaction();
|
|
1146
|
+
|
|
1147
|
+
const authority = this.wallet.publicKey;
|
|
1148
|
+
|
|
1149
|
+
const createWSOLTokenAccount =
|
|
1150
|
+
isSolMarket && userTokenAccount.equals(authority);
|
|
1151
|
+
|
|
1152
|
+
if (createWSOLTokenAccount) {
|
|
1153
|
+
const {
|
|
1154
|
+
ixs: startIxs,
|
|
1155
|
+
signers,
|
|
1156
|
+
pubkey,
|
|
1157
|
+
} = await this.getWrappedSolAccountCreationIxs(amount, true);
|
|
1158
|
+
|
|
1159
|
+
userTokenAccount = pubkey;
|
|
1160
|
+
|
|
1161
|
+
startIxs.forEach((ix) => {
|
|
1162
|
+
tx.add(ix);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
signers.forEach((signer) => additionalSigners.push(signer));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const depositCollateralIx =
|
|
1169
|
+
fromSubAccountId != null
|
|
1170
|
+
? await this.getTransferDepositIx(
|
|
1171
|
+
amount,
|
|
1172
|
+
marketIndex,
|
|
1173
|
+
fromSubAccountId,
|
|
1174
|
+
subAccountId
|
|
1175
|
+
)
|
|
1176
|
+
: await this.getDepositInstruction(
|
|
1177
|
+
amount,
|
|
1178
|
+
marketIndex,
|
|
1179
|
+
userTokenAccount,
|
|
1180
|
+
subAccountId,
|
|
1181
|
+
false,
|
|
1182
|
+
false
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
if (subAccountId === 0) {
|
|
1186
|
+
if (
|
|
1187
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
1188
|
+
) {
|
|
1189
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
1193
|
+
|
|
1194
|
+
// Close the wrapped sol account at the end of the transaction
|
|
1195
|
+
if (createWSOLTokenAccount) {
|
|
1196
|
+
tx.add(
|
|
1197
|
+
Token.createCloseAccountInstruction(
|
|
1198
|
+
TOKEN_PROGRAM_ID,
|
|
1199
|
+
userTokenAccount,
|
|
1200
|
+
authority,
|
|
1201
|
+
authority,
|
|
1202
|
+
[]
|
|
1203
|
+
)
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1208
|
+
tx,
|
|
1209
|
+
additionalSigners,
|
|
1210
|
+
this.opts
|
|
1211
|
+
);
|
|
1212
|
+
this.spotMarketLastSlotCache.set(marketIndex, slot);
|
|
1213
|
+
return [txSig, userAccountPublicKey];
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
public async initializeUserAccountForDevnet(
|
|
1217
|
+
subAccountId = 0,
|
|
1218
|
+
name = DEFAULT_USER_NAME,
|
|
1219
|
+
marketIndex: number,
|
|
1220
|
+
tokenFaucet: TokenFaucet,
|
|
1221
|
+
amount: BN,
|
|
1222
|
+
referrerInfo?: ReferrerInfo
|
|
1223
|
+
): Promise<[TransactionSignature, PublicKey]> {
|
|
1224
|
+
const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] =
|
|
1225
|
+
await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(
|
|
1226
|
+
this.wallet.publicKey,
|
|
1227
|
+
amount
|
|
1228
|
+
);
|
|
1229
|
+
|
|
1230
|
+
const [userAccountPublicKey, initializeUserAccountIx] =
|
|
1231
|
+
await this.getInitializeUserInstructions(
|
|
1232
|
+
subAccountId,
|
|
1233
|
+
name,
|
|
1234
|
+
referrerInfo
|
|
1235
|
+
);
|
|
1236
|
+
|
|
1237
|
+
const depositCollateralIx = await this.getDepositInstruction(
|
|
1238
|
+
amount,
|
|
1239
|
+
marketIndex,
|
|
1240
|
+
associateTokenPublicKey,
|
|
1241
|
+
subAccountId,
|
|
1242
|
+
false,
|
|
1243
|
+
false
|
|
1244
|
+
);
|
|
1245
|
+
|
|
1246
|
+
const tx = new Transaction().add(createAssociatedAccountIx).add(mintToIx);
|
|
1247
|
+
|
|
1248
|
+
if (subAccountId === 0) {
|
|
1249
|
+
if (
|
|
1250
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
1251
|
+
) {
|
|
1252
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
1256
|
+
|
|
1257
|
+
const txSig = await this.program.provider.sendAndConfirm(tx, []);
|
|
1258
|
+
|
|
1259
|
+
return [txSig, userAccountPublicKey];
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
public async withdraw(
|
|
1263
|
+
amount: BN,
|
|
1264
|
+
marketIndex: number,
|
|
1265
|
+
userTokenAccount: PublicKey,
|
|
1266
|
+
reduceOnly = false
|
|
1267
|
+
): Promise<TransactionSignature> {
|
|
1268
|
+
const tx = new Transaction();
|
|
1269
|
+
tx.add(
|
|
1270
|
+
ComputeBudgetProgram.requestUnits({
|
|
1271
|
+
units: 600_000,
|
|
1272
|
+
additionalFee: 0,
|
|
1273
|
+
})
|
|
1274
|
+
);
|
|
1275
|
+
|
|
1276
|
+
const additionalSigners: Array<Signer> = [];
|
|
1277
|
+
|
|
1278
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
1279
|
+
|
|
1280
|
+
const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
|
|
1281
|
+
|
|
1282
|
+
const authority = this.wallet.publicKey;
|
|
1283
|
+
|
|
1284
|
+
const createWSOLTokenAccount =
|
|
1285
|
+
isSolMarket && userTokenAccount.equals(authority);
|
|
1286
|
+
|
|
1287
|
+
if (createWSOLTokenAccount) {
|
|
1288
|
+
const { ixs, signers, pubkey } =
|
|
1289
|
+
await this.getWrappedSolAccountCreationIxs(amount, false);
|
|
1290
|
+
|
|
1291
|
+
userTokenAccount = pubkey;
|
|
1292
|
+
|
|
1293
|
+
ixs.forEach((ix) => {
|
|
1294
|
+
tx.add(ix);
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
signers.forEach((signer) => additionalSigners.push(signer));
|
|
1298
|
+
} else {
|
|
1299
|
+
const accountExists = await this.checkIfAccountExists(userTokenAccount);
|
|
1300
|
+
|
|
1301
|
+
if (!accountExists) {
|
|
1302
|
+
const createAssociatedTokenAccountIx =
|
|
1303
|
+
this.getAssociatedTokenAccountCreationIx(
|
|
1304
|
+
spotMarketAccount.mint,
|
|
1305
|
+
userTokenAccount
|
|
1306
|
+
);
|
|
1307
|
+
|
|
1308
|
+
tx.add(createAssociatedTokenAccountIx);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const withdrawCollateral = await this.getWithdrawIx(
|
|
1313
|
+
amount,
|
|
1314
|
+
spotMarketAccount.marketIndex,
|
|
1315
|
+
userTokenAccount,
|
|
1316
|
+
reduceOnly
|
|
1317
|
+
);
|
|
1318
|
+
|
|
1319
|
+
tx.add(withdrawCollateral);
|
|
1320
|
+
|
|
1321
|
+
// Close the wrapped sol account at the end of the transaction
|
|
1322
|
+
if (createWSOLTokenAccount) {
|
|
1323
|
+
tx.add(
|
|
1324
|
+
Token.createCloseAccountInstruction(
|
|
1325
|
+
TOKEN_PROGRAM_ID,
|
|
1326
|
+
userTokenAccount,
|
|
1327
|
+
authority,
|
|
1328
|
+
authority,
|
|
1329
|
+
[]
|
|
1330
|
+
)
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1335
|
+
tx,
|
|
1336
|
+
additionalSigners,
|
|
1337
|
+
this.opts
|
|
1338
|
+
);
|
|
1339
|
+
this.spotMarketLastSlotCache.set(marketIndex, slot);
|
|
1340
|
+
return txSig;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
public async getWithdrawIx(
|
|
1344
|
+
amount: BN,
|
|
1345
|
+
marketIndex: number,
|
|
1346
|
+
userTokenAccount: PublicKey,
|
|
1347
|
+
reduceOnly = false
|
|
1348
|
+
): Promise<TransactionInstruction> {
|
|
1349
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1350
|
+
|
|
1351
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1352
|
+
userAccounts: [this.getUserAccount()],
|
|
1353
|
+
useMarketLastSlotCache: true,
|
|
1354
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
1355
|
+
readableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
1359
|
+
|
|
1360
|
+
return await this.program.instruction.withdraw(
|
|
1361
|
+
marketIndex,
|
|
1362
|
+
amount,
|
|
1363
|
+
reduceOnly,
|
|
1364
|
+
{
|
|
1365
|
+
accounts: {
|
|
1366
|
+
state: await this.getStatePublicKey(),
|
|
1367
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
1368
|
+
spotMarketVault: spotMarketAccount.vault,
|
|
1369
|
+
driftSigner: this.getSignerPublicKey(),
|
|
1370
|
+
user: userAccountPublicKey,
|
|
1371
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
1372
|
+
userTokenAccount: userTokenAccount,
|
|
1373
|
+
authority: this.wallet.publicKey,
|
|
1374
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
1375
|
+
},
|
|
1376
|
+
remainingAccounts,
|
|
1377
|
+
}
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
public async transferDeposit(
|
|
1382
|
+
amount: BN,
|
|
1383
|
+
marketIndex: number,
|
|
1384
|
+
fromSubAccountId: number,
|
|
1385
|
+
toSubAccountId: number
|
|
1386
|
+
): Promise<TransactionSignature> {
|
|
1387
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1388
|
+
wrapInTx(
|
|
1389
|
+
await this.getTransferDepositIx(
|
|
1390
|
+
amount,
|
|
1391
|
+
marketIndex,
|
|
1392
|
+
fromSubAccountId,
|
|
1393
|
+
toSubAccountId
|
|
1394
|
+
)
|
|
1395
|
+
),
|
|
1396
|
+
[],
|
|
1397
|
+
this.opts
|
|
1398
|
+
);
|
|
1399
|
+
if (
|
|
1400
|
+
fromSubAccountId === this.activeSubAccountId ||
|
|
1401
|
+
toSubAccountId === this.activeSubAccountId
|
|
1402
|
+
) {
|
|
1403
|
+
this.spotMarketLastSlotCache.set(marketIndex, slot);
|
|
1404
|
+
}
|
|
1405
|
+
return txSig;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
public async getTransferDepositIx(
|
|
1409
|
+
amount: BN,
|
|
1410
|
+
marketIndex: number,
|
|
1411
|
+
fromSubAccountId: number,
|
|
1412
|
+
toSubAccountId: number
|
|
1413
|
+
): Promise<TransactionInstruction> {
|
|
1414
|
+
const fromUser = await getUserAccountPublicKey(
|
|
1415
|
+
this.program.programId,
|
|
1416
|
+
this.wallet.publicKey,
|
|
1417
|
+
fromSubAccountId
|
|
1418
|
+
);
|
|
1419
|
+
const toUser = await getUserAccountPublicKey(
|
|
1420
|
+
this.program.programId,
|
|
1421
|
+
this.wallet.publicKey,
|
|
1422
|
+
toSubAccountId
|
|
1423
|
+
);
|
|
1424
|
+
|
|
1425
|
+
let remainingAccounts;
|
|
1426
|
+
if (this.users.has(fromSubAccountId)) {
|
|
1427
|
+
remainingAccounts = this.getRemainingAccounts({
|
|
1428
|
+
userAccounts: [this.users.get(fromSubAccountId).getUserAccount()],
|
|
1429
|
+
useMarketLastSlotCache: true,
|
|
1430
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
1431
|
+
});
|
|
1432
|
+
} else {
|
|
1433
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
1434
|
+
this.program.programId,
|
|
1435
|
+
this.authority,
|
|
1436
|
+
fromSubAccountId
|
|
1437
|
+
);
|
|
1438
|
+
|
|
1439
|
+
const fromUserAccount = (await this.program.account.user.fetch(
|
|
1440
|
+
userAccountPublicKey
|
|
1441
|
+
)) as UserAccount;
|
|
1442
|
+
remainingAccounts = this.getRemainingAccounts({
|
|
1443
|
+
userAccounts: [fromUserAccount],
|
|
1444
|
+
useMarketLastSlotCache: true,
|
|
1445
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
return await this.program.instruction.transferDeposit(marketIndex, amount, {
|
|
1450
|
+
accounts: {
|
|
1451
|
+
authority: this.wallet.publicKey,
|
|
1452
|
+
fromUser,
|
|
1453
|
+
toUser,
|
|
1454
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
1455
|
+
state: await this.getStatePublicKey(),
|
|
1456
|
+
spotMarketVault: this.getSpotMarketAccount(marketIndex).vault,
|
|
1457
|
+
},
|
|
1458
|
+
remainingAccounts,
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
public async updateSpotMarketCumulativeInterest(
|
|
1463
|
+
marketIndex: number
|
|
1464
|
+
): Promise<TransactionSignature> {
|
|
1465
|
+
const { txSig } = await this.txSender.send(
|
|
1466
|
+
wrapInTx(await this.updateSpotMarketCumulativeInterestIx(marketIndex)),
|
|
1467
|
+
[],
|
|
1468
|
+
this.opts
|
|
1469
|
+
);
|
|
1470
|
+
return txSig;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
public async updateSpotMarketCumulativeInterestIx(
|
|
1474
|
+
marketIndex: number
|
|
1475
|
+
): Promise<TransactionInstruction> {
|
|
1476
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
1477
|
+
return await this.program.instruction.updateSpotMarketCumulativeInterest({
|
|
1478
|
+
accounts: {
|
|
1479
|
+
state: await this.getStatePublicKey(),
|
|
1480
|
+
spotMarket: spotMarket.pubkey,
|
|
1481
|
+
oracle: spotMarket.oracle,
|
|
1482
|
+
},
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
public async settleLP(
|
|
1487
|
+
settleeUserAccountPublicKey: PublicKey,
|
|
1488
|
+
marketIndex: number
|
|
1489
|
+
): Promise<TransactionSignature> {
|
|
1490
|
+
const { txSig } = await this.txSender.send(
|
|
1491
|
+
wrapInTx(await this.settleLPIx(settleeUserAccountPublicKey, marketIndex)),
|
|
1492
|
+
[],
|
|
1493
|
+
this.opts
|
|
1494
|
+
);
|
|
1495
|
+
return txSig;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
public async settleLPIx(
|
|
1499
|
+
settleeUserAccountPublicKey: PublicKey,
|
|
1500
|
+
marketIndex: number
|
|
1501
|
+
): Promise<TransactionInstruction> {
|
|
1502
|
+
const settleeUserAccount = (await this.program.account.user.fetch(
|
|
1503
|
+
settleeUserAccountPublicKey
|
|
1504
|
+
)) as UserAccount;
|
|
1505
|
+
|
|
1506
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1507
|
+
userAccounts: [settleeUserAccount],
|
|
1508
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
return this.program.instruction.settleLp(marketIndex, {
|
|
1512
|
+
accounts: {
|
|
1513
|
+
state: await this.getStatePublicKey(),
|
|
1514
|
+
user: settleeUserAccountPublicKey,
|
|
1515
|
+
},
|
|
1516
|
+
remainingAccounts: remainingAccounts,
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
public async removePerpLpShares(
|
|
1521
|
+
marketIndex: number,
|
|
1522
|
+
sharesToBurn?: BN
|
|
1523
|
+
): Promise<TransactionSignature> {
|
|
1524
|
+
const { txSig } = await this.txSender.send(
|
|
1525
|
+
wrapInTx(await this.getRemovePerpLpSharesIx(marketIndex, sharesToBurn)),
|
|
1526
|
+
[],
|
|
1527
|
+
this.opts
|
|
1528
|
+
);
|
|
1529
|
+
return txSig;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
public async removePerpLpSharesInExpiringMarket(
|
|
1533
|
+
marketIndex: number,
|
|
1534
|
+
userAccountPublicKey: PublicKey,
|
|
1535
|
+
sharesToBurn?: BN
|
|
1536
|
+
): Promise<TransactionSignature> {
|
|
1537
|
+
const { txSig } = await this.txSender.send(
|
|
1538
|
+
wrapInTx(
|
|
1539
|
+
await this.getRemovePerpLpSharesInExpiringMarket(
|
|
1540
|
+
marketIndex,
|
|
1541
|
+
userAccountPublicKey,
|
|
1542
|
+
sharesToBurn
|
|
1543
|
+
)
|
|
1544
|
+
),
|
|
1545
|
+
[],
|
|
1546
|
+
this.opts
|
|
1547
|
+
);
|
|
1548
|
+
return txSig;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
public async getRemovePerpLpSharesInExpiringMarket(
|
|
1552
|
+
marketIndex: number,
|
|
1553
|
+
userAccountPublicKey: PublicKey,
|
|
1554
|
+
sharesToBurn?: BN
|
|
1555
|
+
): Promise<TransactionInstruction> {
|
|
1556
|
+
const userAccount = (await this.program.account.user.fetch(
|
|
1557
|
+
userAccountPublicKey
|
|
1558
|
+
)) as UserAccount;
|
|
1559
|
+
|
|
1560
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1561
|
+
userAccounts: [userAccount],
|
|
1562
|
+
useMarketLastSlotCache: true,
|
|
1563
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
if (sharesToBurn == undefined) {
|
|
1567
|
+
const perpPosition = userAccount.perpPositions.filter(
|
|
1568
|
+
(position) => position.marketIndex === marketIndex
|
|
1569
|
+
)[0];
|
|
1570
|
+
sharesToBurn = perpPosition.lpShares;
|
|
1571
|
+
console.log('burning lp shares:', sharesToBurn.toString());
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
return this.program.instruction.removePerpLpSharesInExpiringMarket(
|
|
1575
|
+
sharesToBurn,
|
|
1576
|
+
marketIndex,
|
|
1577
|
+
{
|
|
1578
|
+
accounts: {
|
|
1579
|
+
state: await this.getStatePublicKey(),
|
|
1580
|
+
user: userAccountPublicKey,
|
|
1581
|
+
},
|
|
1582
|
+
remainingAccounts: remainingAccounts,
|
|
1583
|
+
}
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
public async getRemovePerpLpSharesIx(
|
|
1588
|
+
marketIndex: number,
|
|
1589
|
+
sharesToBurn?: BN
|
|
1590
|
+
): Promise<TransactionInstruction> {
|
|
1591
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1592
|
+
|
|
1593
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1594
|
+
userAccounts: [this.getUserAccount()],
|
|
1595
|
+
useMarketLastSlotCache: true,
|
|
1596
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
if (sharesToBurn == undefined) {
|
|
1600
|
+
const userAccount = this.getUserAccount();
|
|
1601
|
+
const perpPosition = userAccount.perpPositions.filter(
|
|
1602
|
+
(position) => position.marketIndex === marketIndex
|
|
1603
|
+
)[0];
|
|
1604
|
+
sharesToBurn = perpPosition.lpShares;
|
|
1605
|
+
console.log('burning lp shares:', sharesToBurn.toString());
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
return this.program.instruction.removePerpLpShares(
|
|
1609
|
+
sharesToBurn,
|
|
1610
|
+
marketIndex,
|
|
1611
|
+
{
|
|
1612
|
+
accounts: {
|
|
1613
|
+
state: await this.getStatePublicKey(),
|
|
1614
|
+
user: userAccountPublicKey,
|
|
1615
|
+
authority: this.wallet.publicKey,
|
|
1616
|
+
},
|
|
1617
|
+
remainingAccounts: remainingAccounts,
|
|
1618
|
+
}
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
public async addPerpLpShares(
|
|
1623
|
+
amount: BN,
|
|
1624
|
+
marketIndex: number
|
|
1625
|
+
): Promise<TransactionSignature> {
|
|
1626
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1627
|
+
wrapInTx(await this.getAddPerpLpSharesIx(amount, marketIndex)),
|
|
1628
|
+
[],
|
|
1629
|
+
this.opts
|
|
1630
|
+
);
|
|
1631
|
+
this.perpMarketLastSlotCache.set(marketIndex, slot);
|
|
1632
|
+
return txSig;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
public async getAddPerpLpSharesIx(
|
|
1636
|
+
amount: BN,
|
|
1637
|
+
marketIndex: number
|
|
1638
|
+
): Promise<TransactionInstruction> {
|
|
1639
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1640
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1641
|
+
userAccounts: [this.getUserAccount()],
|
|
1642
|
+
useMarketLastSlotCache: true,
|
|
1643
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
return this.program.instruction.addPerpLpShares(amount, marketIndex, {
|
|
1647
|
+
accounts: {
|
|
1648
|
+
state: await this.getStatePublicKey(),
|
|
1649
|
+
user: userAccountPublicKey,
|
|
1650
|
+
authority: this.wallet.publicKey,
|
|
1651
|
+
},
|
|
1652
|
+
remainingAccounts: remainingAccounts,
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
public async openPosition(
|
|
1657
|
+
direction: PositionDirection,
|
|
1658
|
+
amount: BN,
|
|
1659
|
+
marketIndex: number,
|
|
1660
|
+
limitPrice?: BN
|
|
1661
|
+
): Promise<TransactionSignature> {
|
|
1662
|
+
return await this.placeAndTakePerpOrder({
|
|
1663
|
+
orderType: OrderType.MARKET,
|
|
1664
|
+
marketIndex,
|
|
1665
|
+
direction,
|
|
1666
|
+
baseAssetAmount: amount,
|
|
1667
|
+
price: limitPrice,
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
public async sendSignedTx(tx: Transaction): Promise<TransactionSignature> {
|
|
1672
|
+
const { txSig } = await this.txSender.send(tx, undefined, this.opts, true);
|
|
1673
|
+
|
|
1674
|
+
return txSig;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Sends a market order and returns a signed tx which can fill the order against the vamm, which the caller can use to fill their own order if required.
|
|
1679
|
+
* @param orderParams
|
|
1680
|
+
* @param userAccountPublicKey
|
|
1681
|
+
* @param userAccount
|
|
1682
|
+
* @returns
|
|
1683
|
+
*/
|
|
1684
|
+
public async sendMarketOrderAndGetSignedFillTx(
|
|
1685
|
+
orderParams: OptionalOrderParams,
|
|
1686
|
+
userAccountPublicKey: PublicKey,
|
|
1687
|
+
userAccount: UserAccount
|
|
1688
|
+
): Promise<{ txSig: TransactionSignature; signedFillTx: Transaction }> {
|
|
1689
|
+
const marketIndex = orderParams.marketIndex;
|
|
1690
|
+
const orderId = userAccount.nextOrderId;
|
|
1691
|
+
|
|
1692
|
+
const marketOrderTx = wrapInTx(await this.getPlacePerpOrderIx(orderParams));
|
|
1693
|
+
const fillTx = wrapInTx(
|
|
1694
|
+
await this.getFillPerpOrderIx(userAccountPublicKey, userAccount, {
|
|
1695
|
+
orderId,
|
|
1696
|
+
marketIndex,
|
|
1697
|
+
})
|
|
1698
|
+
);
|
|
1699
|
+
|
|
1700
|
+
// Apply the latest blockhash to the txs so that we can sign before sending them
|
|
1701
|
+
const currentBlockHash = (
|
|
1702
|
+
await this.connection.getLatestBlockhash('finalized')
|
|
1703
|
+
).blockhash;
|
|
1704
|
+
marketOrderTx.recentBlockhash = currentBlockHash;
|
|
1705
|
+
fillTx.recentBlockhash = currentBlockHash;
|
|
1706
|
+
|
|
1707
|
+
marketOrderTx.feePayer = userAccount.authority;
|
|
1708
|
+
fillTx.feePayer = userAccount.authority;
|
|
1709
|
+
|
|
1710
|
+
const [signedMarketOrderTx, signedFillTx] =
|
|
1711
|
+
await this.provider.wallet.signAllTransactions([marketOrderTx, fillTx]);
|
|
1712
|
+
|
|
1713
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1714
|
+
signedMarketOrderTx,
|
|
1715
|
+
[],
|
|
1716
|
+
this.opts,
|
|
1717
|
+
true
|
|
1718
|
+
);
|
|
1719
|
+
|
|
1720
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
1721
|
+
|
|
1722
|
+
return { txSig, signedFillTx };
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
public async placePerpOrder(
|
|
1726
|
+
orderParams: OptionalOrderParams
|
|
1727
|
+
): Promise<TransactionSignature> {
|
|
1728
|
+
const { txSig, slot } = await this.txSender.send(
|
|
1729
|
+
wrapInTx(await this.getPlacePerpOrderIx(orderParams)),
|
|
1730
|
+
[],
|
|
1731
|
+
this.opts
|
|
1732
|
+
);
|
|
1733
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
1734
|
+
return txSig;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
getOrderParams(
|
|
1738
|
+
optionalOrderParams: OptionalOrderParams,
|
|
1739
|
+
marketType: MarketType
|
|
1740
|
+
): OrderParams {
|
|
1741
|
+
return Object.assign({}, DefaultOrderParams, optionalOrderParams, {
|
|
1742
|
+
marketType,
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
public async getPlacePerpOrderIx(
|
|
1747
|
+
orderParams: OptionalOrderParams
|
|
1748
|
+
): Promise<TransactionInstruction> {
|
|
1749
|
+
orderParams = this.getOrderParams(orderParams, MarketType.PERP);
|
|
1750
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1751
|
+
|
|
1752
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1753
|
+
userAccounts: [this.getUserAccount()],
|
|
1754
|
+
useMarketLastSlotCache: true,
|
|
1755
|
+
readablePerpMarketIndex: orderParams.marketIndex,
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
return await this.program.instruction.placePerpOrder(orderParams, {
|
|
1759
|
+
accounts: {
|
|
1760
|
+
state: await this.getStatePublicKey(),
|
|
1761
|
+
user: userAccountPublicKey,
|
|
1762
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
1763
|
+
authority: this.wallet.publicKey,
|
|
1764
|
+
},
|
|
1765
|
+
remainingAccounts,
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
public async updateAMMs(
|
|
1770
|
+
marketIndexes: number[]
|
|
1771
|
+
): Promise<TransactionSignature> {
|
|
1772
|
+
const { txSig } = await this.txSender.send(
|
|
1773
|
+
wrapInTx(await this.getUpdateAMMsIx(marketIndexes)),
|
|
1774
|
+
[],
|
|
1775
|
+
this.opts
|
|
1776
|
+
);
|
|
1777
|
+
return txSig;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
public async getUpdateAMMsIx(
|
|
1781
|
+
marketIndexes: number[]
|
|
1782
|
+
): Promise<TransactionInstruction> {
|
|
1783
|
+
for (let i = marketIndexes.length; i < 5; i++) {
|
|
1784
|
+
marketIndexes.push(100);
|
|
1785
|
+
}
|
|
1786
|
+
const marketAccountInfos = [];
|
|
1787
|
+
const oracleAccountInfos = [];
|
|
1788
|
+
for (const marketIndex of marketIndexes) {
|
|
1789
|
+
if (marketIndex !== 100) {
|
|
1790
|
+
const market = this.getPerpMarketAccount(marketIndex);
|
|
1791
|
+
marketAccountInfos.push({
|
|
1792
|
+
pubkey: market.pubkey,
|
|
1793
|
+
isWritable: true,
|
|
1794
|
+
isSigner: false,
|
|
1795
|
+
});
|
|
1796
|
+
oracleAccountInfos.push({
|
|
1797
|
+
pubkey: market.amm.oracle,
|
|
1798
|
+
isWritable: false,
|
|
1799
|
+
isSigner: false,
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
const remainingAccounts = oracleAccountInfos.concat(marketAccountInfos);
|
|
1804
|
+
|
|
1805
|
+
return await this.program.instruction.updateAmms(marketIndexes, {
|
|
1806
|
+
accounts: {
|
|
1807
|
+
state: await this.getStatePublicKey(),
|
|
1808
|
+
authority: this.wallet.publicKey,
|
|
1809
|
+
},
|
|
1810
|
+
remainingAccounts,
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
public async settleExpiredMarket(
|
|
1815
|
+
marketIndex: number
|
|
1816
|
+
): Promise<TransactionSignature> {
|
|
1817
|
+
const { txSig } = await this.txSender.send(
|
|
1818
|
+
wrapInTx(await this.getSettleExpiredMarketIx(marketIndex)),
|
|
1819
|
+
[],
|
|
1820
|
+
this.opts
|
|
1821
|
+
);
|
|
1822
|
+
return txSig;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
public async getSettleExpiredMarketIx(
|
|
1826
|
+
marketIndex: number
|
|
1827
|
+
): Promise<TransactionInstruction> {
|
|
1828
|
+
const marketAccountInfos = [];
|
|
1829
|
+
const oracleAccountInfos = [];
|
|
1830
|
+
const spotMarketAccountInfos = [];
|
|
1831
|
+
const market = this.getPerpMarketAccount(marketIndex);
|
|
1832
|
+
marketAccountInfos.push({
|
|
1833
|
+
pubkey: market.pubkey,
|
|
1834
|
+
isWritable: true,
|
|
1835
|
+
isSigner: false,
|
|
1836
|
+
});
|
|
1837
|
+
oracleAccountInfos.push({
|
|
1838
|
+
pubkey: market.amm.oracle,
|
|
1839
|
+
isWritable: false,
|
|
1840
|
+
isSigner: false,
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1843
|
+
spotMarketAccountInfos.push({
|
|
1844
|
+
pubkey: this.getSpotMarketAccount(QUOTE_SPOT_MARKET_INDEX).pubkey,
|
|
1845
|
+
isSigner: false,
|
|
1846
|
+
isWritable: true,
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
const remainingAccounts = oracleAccountInfos
|
|
1850
|
+
.concat(spotMarketAccountInfos)
|
|
1851
|
+
.concat(marketAccountInfos);
|
|
1852
|
+
|
|
1853
|
+
return await this.program.instruction.settleExpiredMarket(marketIndex, {
|
|
1854
|
+
accounts: {
|
|
1855
|
+
state: await this.getStatePublicKey(),
|
|
1856
|
+
authority: this.wallet.publicKey,
|
|
1857
|
+
},
|
|
1858
|
+
remainingAccounts,
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
public async settleExpiredMarketPoolsToRevenuePool(
|
|
1863
|
+
perpMarketIndex: number
|
|
1864
|
+
): Promise<TransactionSignature> {
|
|
1865
|
+
const perpMarketPublicKey = await getPerpMarketPublicKey(
|
|
1866
|
+
this.program.programId,
|
|
1867
|
+
perpMarketIndex
|
|
1868
|
+
);
|
|
1869
|
+
|
|
1870
|
+
const spotMarketPublicKey = await getSpotMarketPublicKey(
|
|
1871
|
+
this.program.programId,
|
|
1872
|
+
QUOTE_SPOT_MARKET_INDEX
|
|
1873
|
+
);
|
|
1874
|
+
|
|
1875
|
+
const ix =
|
|
1876
|
+
await this.program.instruction.settleExpiredMarketPoolsToRevenuePool({
|
|
1877
|
+
accounts: {
|
|
1878
|
+
state: await this.getStatePublicKey(),
|
|
1879
|
+
admin: this.wallet.publicKey,
|
|
1880
|
+
spotMarket: spotMarketPublicKey,
|
|
1881
|
+
perpMarket: perpMarketPublicKey,
|
|
1882
|
+
},
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1885
|
+
const { txSig } = await this.txSender.send(wrapInTx(ix), [], this.opts);
|
|
1886
|
+
|
|
1887
|
+
return txSig;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
public async cancelOrder(orderId?: number): Promise<TransactionSignature> {
|
|
1891
|
+
const { txSig } = await this.txSender.send(
|
|
1892
|
+
wrapInTx(await this.getCancelOrderIx(orderId)),
|
|
1893
|
+
[],
|
|
1894
|
+
this.opts
|
|
1895
|
+
);
|
|
1896
|
+
return txSig;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
public async getCancelOrderIx(
|
|
1900
|
+
orderId?: number
|
|
1901
|
+
): Promise<TransactionInstruction> {
|
|
1902
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1903
|
+
|
|
1904
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1905
|
+
userAccounts: [this.getUserAccount()],
|
|
1906
|
+
useMarketLastSlotCache: true,
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
return await this.program.instruction.cancelOrder(orderId ?? null, {
|
|
1910
|
+
accounts: {
|
|
1911
|
+
state: await this.getStatePublicKey(),
|
|
1912
|
+
user: userAccountPublicKey,
|
|
1913
|
+
authority: this.wallet.publicKey,
|
|
1914
|
+
},
|
|
1915
|
+
remainingAccounts,
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
public async cancelOrderByUserId(
|
|
1920
|
+
userOrderId: number
|
|
1921
|
+
): Promise<TransactionSignature> {
|
|
1922
|
+
const { txSig } = await this.txSender.send(
|
|
1923
|
+
wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId)),
|
|
1924
|
+
[],
|
|
1925
|
+
this.opts
|
|
1926
|
+
);
|
|
1927
|
+
return txSig;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
public async getCancelOrderByUserIdIx(
|
|
1931
|
+
userOrderId: number
|
|
1932
|
+
): Promise<TransactionInstruction> {
|
|
1933
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1934
|
+
|
|
1935
|
+
const order = this.getOrderByUserId(userOrderId);
|
|
1936
|
+
const oracle = this.getPerpMarketAccount(order.marketIndex).amm.oracle;
|
|
1937
|
+
|
|
1938
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1939
|
+
userAccounts: [this.getUserAccount()],
|
|
1940
|
+
useMarketLastSlotCache: true,
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
return await this.program.instruction.cancelOrderByUserId(userOrderId, {
|
|
1944
|
+
accounts: {
|
|
1945
|
+
state: await this.getStatePublicKey(),
|
|
1946
|
+
user: userAccountPublicKey,
|
|
1947
|
+
authority: this.wallet.publicKey,
|
|
1948
|
+
oracle,
|
|
1949
|
+
},
|
|
1950
|
+
remainingAccounts,
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
public async cancelOrders(
|
|
1955
|
+
marketType?: MarketType,
|
|
1956
|
+
marketIndex?: number,
|
|
1957
|
+
direction?: PositionDirection
|
|
1958
|
+
): Promise<TransactionSignature> {
|
|
1959
|
+
const { txSig } = await this.txSender.send(
|
|
1960
|
+
wrapInTx(
|
|
1961
|
+
await this.getCancelOrdersIx(marketType, marketIndex, direction)
|
|
1962
|
+
),
|
|
1963
|
+
[],
|
|
1964
|
+
this.opts
|
|
1965
|
+
);
|
|
1966
|
+
return txSig;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
public async getCancelOrdersIx(
|
|
1970
|
+
marketType: MarketType | null,
|
|
1971
|
+
marketIndex: number | null,
|
|
1972
|
+
direction: PositionDirection | null
|
|
1973
|
+
): Promise<TransactionInstruction> {
|
|
1974
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
1975
|
+
|
|
1976
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
1977
|
+
userAccounts: [this.getUserAccount()],
|
|
1978
|
+
useMarketLastSlotCache: true,
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
return await this.program.instruction.cancelOrders(
|
|
1982
|
+
marketType ?? null,
|
|
1983
|
+
marketIndex ?? null,
|
|
1984
|
+
direction ?? null,
|
|
1985
|
+
{
|
|
1986
|
+
accounts: {
|
|
1987
|
+
state: await this.getStatePublicKey(),
|
|
1988
|
+
user: userAccountPublicKey,
|
|
1989
|
+
authority: this.wallet.publicKey,
|
|
1990
|
+
},
|
|
1991
|
+
remainingAccounts,
|
|
1992
|
+
}
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
public async fillPerpOrder(
|
|
1997
|
+
userAccountPublicKey: PublicKey,
|
|
1998
|
+
user: UserAccount,
|
|
1999
|
+
order?: Pick<Order, 'marketIndex' | 'orderId'>,
|
|
2000
|
+
makerInfo?: MakerInfo,
|
|
2001
|
+
referrerInfo?: ReferrerInfo
|
|
2002
|
+
): Promise<TransactionSignature> {
|
|
2003
|
+
const { txSig } = await this.txSender.send(
|
|
2004
|
+
wrapInTx(
|
|
2005
|
+
await this.getFillPerpOrderIx(
|
|
2006
|
+
userAccountPublicKey,
|
|
2007
|
+
user,
|
|
2008
|
+
order,
|
|
2009
|
+
makerInfo,
|
|
2010
|
+
referrerInfo
|
|
2011
|
+
)
|
|
2012
|
+
),
|
|
2013
|
+
[],
|
|
2014
|
+
this.opts
|
|
2015
|
+
);
|
|
2016
|
+
return txSig;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
public async getFillPerpOrderIx(
|
|
2020
|
+
userAccountPublicKey: PublicKey,
|
|
2021
|
+
userAccount: UserAccount,
|
|
2022
|
+
order: Pick<Order, 'marketIndex' | 'orderId'>,
|
|
2023
|
+
makerInfo?: MakerInfo,
|
|
2024
|
+
referrerInfo?: ReferrerInfo
|
|
2025
|
+
): Promise<TransactionInstruction> {
|
|
2026
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
2027
|
+
this.program.programId,
|
|
2028
|
+
userAccount.authority
|
|
2029
|
+
);
|
|
2030
|
+
|
|
2031
|
+
const fillerPublicKey = await this.getUserAccountPublicKey();
|
|
2032
|
+
const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2033
|
+
|
|
2034
|
+
const marketIndex = order
|
|
2035
|
+
? order.marketIndex
|
|
2036
|
+
: userAccount.orders.find(
|
|
2037
|
+
(order) => order.orderId === userAccount.nextOrderId - 1
|
|
2038
|
+
).marketIndex;
|
|
2039
|
+
|
|
2040
|
+
const userAccounts = [userAccount];
|
|
2041
|
+
if (makerInfo !== undefined) {
|
|
2042
|
+
userAccounts.push(makerInfo.makerUserAccount);
|
|
2043
|
+
}
|
|
2044
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2045
|
+
userAccounts,
|
|
2046
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
if (makerInfo) {
|
|
2050
|
+
remainingAccounts.push({
|
|
2051
|
+
pubkey: makerInfo.maker,
|
|
2052
|
+
isWritable: true,
|
|
2053
|
+
isSigner: false,
|
|
2054
|
+
});
|
|
2055
|
+
remainingAccounts.push({
|
|
2056
|
+
pubkey: makerInfo.makerStats,
|
|
2057
|
+
isWritable: true,
|
|
2058
|
+
isSigner: false,
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
if (referrerInfo) {
|
|
2063
|
+
remainingAccounts.push({
|
|
2064
|
+
pubkey: referrerInfo.referrer,
|
|
2065
|
+
isWritable: true,
|
|
2066
|
+
isSigner: false,
|
|
2067
|
+
});
|
|
2068
|
+
remainingAccounts.push({
|
|
2069
|
+
pubkey: referrerInfo.referrerStats,
|
|
2070
|
+
isWritable: true,
|
|
2071
|
+
isSigner: false,
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
const orderId = order.orderId;
|
|
2076
|
+
const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
|
|
2077
|
+
|
|
2078
|
+
return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
|
|
2079
|
+
accounts: {
|
|
2080
|
+
state: await this.getStatePublicKey(),
|
|
2081
|
+
filler: fillerPublicKey,
|
|
2082
|
+
fillerStats: fillerStatsPublicKey,
|
|
2083
|
+
user: userAccountPublicKey,
|
|
2084
|
+
userStats: userStatsPublicKey,
|
|
2085
|
+
authority: this.wallet.publicKey,
|
|
2086
|
+
},
|
|
2087
|
+
remainingAccounts,
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
public async placeSpotOrder(
|
|
2092
|
+
orderParams: OptionalOrderParams
|
|
2093
|
+
): Promise<TransactionSignature> {
|
|
2094
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2095
|
+
wrapInTx(await this.getPlaceSpotOrderIx(orderParams)),
|
|
2096
|
+
[],
|
|
2097
|
+
this.opts
|
|
2098
|
+
);
|
|
2099
|
+
this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2100
|
+
this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
|
|
2101
|
+
return txSig;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
public async getPlaceSpotOrderIx(
|
|
2105
|
+
orderParams: OptionalOrderParams
|
|
2106
|
+
): Promise<TransactionInstruction> {
|
|
2107
|
+
orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
|
|
2108
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2109
|
+
|
|
2110
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2111
|
+
userAccounts: [this.getUserAccount()],
|
|
2112
|
+
useMarketLastSlotCache: true,
|
|
2113
|
+
readableSpotMarketIndexes: [
|
|
2114
|
+
orderParams.marketIndex,
|
|
2115
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
2116
|
+
],
|
|
2117
|
+
});
|
|
2118
|
+
|
|
2119
|
+
return await this.program.instruction.placeSpotOrder(orderParams, {
|
|
2120
|
+
accounts: {
|
|
2121
|
+
state: await this.getStatePublicKey(),
|
|
2122
|
+
user: userAccountPublicKey,
|
|
2123
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
2124
|
+
authority: this.wallet.publicKey,
|
|
2125
|
+
},
|
|
2126
|
+
remainingAccounts,
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
public async fillSpotOrder(
|
|
2131
|
+
userAccountPublicKey: PublicKey,
|
|
2132
|
+
user: UserAccount,
|
|
2133
|
+
order?: Order,
|
|
2134
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2135
|
+
makerInfo?: MakerInfo,
|
|
2136
|
+
referrerInfo?: ReferrerInfo
|
|
2137
|
+
): Promise<TransactionSignature> {
|
|
2138
|
+
const { txSig } = await this.txSender.send(
|
|
2139
|
+
wrapInTx(
|
|
2140
|
+
await this.getFillSpotOrderIx(
|
|
2141
|
+
userAccountPublicKey,
|
|
2142
|
+
user,
|
|
2143
|
+
order,
|
|
2144
|
+
fulfillmentConfig,
|
|
2145
|
+
makerInfo,
|
|
2146
|
+
referrerInfo
|
|
2147
|
+
)
|
|
2148
|
+
),
|
|
2149
|
+
[],
|
|
2150
|
+
this.opts
|
|
2151
|
+
);
|
|
2152
|
+
return txSig;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
public async getFillSpotOrderIx(
|
|
2156
|
+
userAccountPublicKey: PublicKey,
|
|
2157
|
+
userAccount: UserAccount,
|
|
2158
|
+
order?: Order,
|
|
2159
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2160
|
+
makerInfo?: MakerInfo,
|
|
2161
|
+
referrerInfo?: ReferrerInfo
|
|
2162
|
+
): Promise<TransactionInstruction> {
|
|
2163
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
2164
|
+
this.program.programId,
|
|
2165
|
+
userAccount.authority
|
|
2166
|
+
);
|
|
2167
|
+
|
|
2168
|
+
const fillerPublicKey = await this.getUserAccountPublicKey();
|
|
2169
|
+
const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2170
|
+
|
|
2171
|
+
const marketIndex = order
|
|
2172
|
+
? order.marketIndex
|
|
2173
|
+
: userAccount.orders.find(
|
|
2174
|
+
(order) => order.orderId === userAccount.nextOrderId - 1
|
|
2175
|
+
).marketIndex;
|
|
2176
|
+
|
|
2177
|
+
const userAccounts = [userAccount];
|
|
2178
|
+
if (makerInfo !== undefined) {
|
|
2179
|
+
userAccounts.push(makerInfo.makerUserAccount);
|
|
2180
|
+
}
|
|
2181
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2182
|
+
userAccounts,
|
|
2183
|
+
writableSpotMarketIndexes: [marketIndex, QUOTE_SPOT_MARKET_INDEX],
|
|
2184
|
+
});
|
|
2185
|
+
|
|
2186
|
+
if (makerInfo) {
|
|
2187
|
+
remainingAccounts.push({
|
|
2188
|
+
pubkey: makerInfo.maker,
|
|
2189
|
+
isWritable: true,
|
|
2190
|
+
isSigner: false,
|
|
2191
|
+
});
|
|
2192
|
+
remainingAccounts.push({
|
|
2193
|
+
pubkey: makerInfo.makerStats,
|
|
2194
|
+
isWritable: true,
|
|
2195
|
+
isSigner: false,
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
if (referrerInfo) {
|
|
2200
|
+
remainingAccounts.push({
|
|
2201
|
+
pubkey: referrerInfo.referrer,
|
|
2202
|
+
isWritable: true,
|
|
2203
|
+
isSigner: false,
|
|
2204
|
+
});
|
|
2205
|
+
remainingAccounts.push({
|
|
2206
|
+
pubkey: referrerInfo.referrerStats,
|
|
2207
|
+
isWritable: true,
|
|
2208
|
+
isSigner: false,
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
const orderId = order.orderId;
|
|
2213
|
+
const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
|
|
2214
|
+
|
|
2215
|
+
this.addSpotFulfillmentAccounts(
|
|
2216
|
+
marketIndex,
|
|
2217
|
+
remainingAccounts,
|
|
2218
|
+
fulfillmentConfig
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2221
|
+
return await this.program.instruction.fillSpotOrder(
|
|
2222
|
+
orderId,
|
|
2223
|
+
fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
|
|
2224
|
+
makerOrderId,
|
|
2225
|
+
{
|
|
2226
|
+
accounts: {
|
|
2227
|
+
state: await this.getStatePublicKey(),
|
|
2228
|
+
filler: fillerPublicKey,
|
|
2229
|
+
fillerStats: fillerStatsPublicKey,
|
|
2230
|
+
user: userAccountPublicKey,
|
|
2231
|
+
userStats: userStatsPublicKey,
|
|
2232
|
+
authority: this.wallet.publicKey,
|
|
2233
|
+
},
|
|
2234
|
+
remainingAccounts,
|
|
2235
|
+
}
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
addSpotFulfillmentAccounts(
|
|
2240
|
+
marketIndex: number,
|
|
2241
|
+
remainingAccounts: AccountMeta[],
|
|
2242
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount
|
|
2243
|
+
) {
|
|
2244
|
+
if (fulfillmentConfig) {
|
|
2245
|
+
this.addSerumRemainingAccounts(
|
|
2246
|
+
marketIndex,
|
|
2247
|
+
remainingAccounts,
|
|
2248
|
+
fulfillmentConfig
|
|
2249
|
+
);
|
|
2250
|
+
} else {
|
|
2251
|
+
remainingAccounts.push({
|
|
2252
|
+
pubkey: this.getSpotMarketAccount(marketIndex).vault,
|
|
2253
|
+
isWritable: false,
|
|
2254
|
+
isSigner: false,
|
|
2255
|
+
});
|
|
2256
|
+
remainingAccounts.push({
|
|
2257
|
+
pubkey: this.getQuoteSpotMarketAccount().vault,
|
|
2258
|
+
isWritable: false,
|
|
2259
|
+
isSigner: false,
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
addSerumRemainingAccounts(
|
|
2265
|
+
marketIndex: number,
|
|
2266
|
+
remainingAccounts: AccountMeta[],
|
|
2267
|
+
fulfillmentConfig: SerumV3FulfillmentConfigAccount
|
|
2268
|
+
) {
|
|
2269
|
+
remainingAccounts.push({
|
|
2270
|
+
pubkey: fulfillmentConfig.pubkey,
|
|
2271
|
+
isWritable: false,
|
|
2272
|
+
isSigner: false,
|
|
2273
|
+
});
|
|
2274
|
+
remainingAccounts.push({
|
|
2275
|
+
pubkey: fulfillmentConfig.serumProgramId,
|
|
2276
|
+
isWritable: false,
|
|
2277
|
+
isSigner: false,
|
|
2278
|
+
});
|
|
2279
|
+
remainingAccounts.push({
|
|
2280
|
+
pubkey: fulfillmentConfig.serumMarket,
|
|
2281
|
+
isWritable: true,
|
|
2282
|
+
isSigner: false,
|
|
2283
|
+
});
|
|
2284
|
+
remainingAccounts.push({
|
|
2285
|
+
pubkey: fulfillmentConfig.serumRequestQueue,
|
|
2286
|
+
isWritable: true,
|
|
2287
|
+
isSigner: false,
|
|
2288
|
+
});
|
|
2289
|
+
remainingAccounts.push({
|
|
2290
|
+
pubkey: fulfillmentConfig.serumEventQueue,
|
|
2291
|
+
isWritable: true,
|
|
2292
|
+
isSigner: false,
|
|
2293
|
+
});
|
|
2294
|
+
remainingAccounts.push({
|
|
2295
|
+
pubkey: fulfillmentConfig.serumBids,
|
|
2296
|
+
isWritable: true,
|
|
2297
|
+
isSigner: false,
|
|
2298
|
+
});
|
|
2299
|
+
remainingAccounts.push({
|
|
2300
|
+
pubkey: fulfillmentConfig.serumAsks,
|
|
2301
|
+
isWritable: true,
|
|
2302
|
+
isSigner: false,
|
|
2303
|
+
});
|
|
2304
|
+
remainingAccounts.push({
|
|
2305
|
+
pubkey: fulfillmentConfig.serumBaseVault,
|
|
2306
|
+
isWritable: true,
|
|
2307
|
+
isSigner: false,
|
|
2308
|
+
});
|
|
2309
|
+
remainingAccounts.push({
|
|
2310
|
+
pubkey: fulfillmentConfig.serumQuoteVault,
|
|
2311
|
+
isWritable: true,
|
|
2312
|
+
isSigner: false,
|
|
2313
|
+
});
|
|
2314
|
+
remainingAccounts.push({
|
|
2315
|
+
pubkey: fulfillmentConfig.serumOpenOrders,
|
|
2316
|
+
isWritable: true,
|
|
2317
|
+
isSigner: false,
|
|
2318
|
+
});
|
|
2319
|
+
remainingAccounts.push({
|
|
2320
|
+
pubkey: getSerumSignerPublicKey(
|
|
2321
|
+
fulfillmentConfig.serumProgramId,
|
|
2322
|
+
fulfillmentConfig.serumMarket,
|
|
2323
|
+
fulfillmentConfig.serumSignerNonce
|
|
2324
|
+
),
|
|
2325
|
+
isWritable: false,
|
|
2326
|
+
isSigner: false,
|
|
2327
|
+
});
|
|
2328
|
+
remainingAccounts.push({
|
|
2329
|
+
pubkey: this.getSignerPublicKey(),
|
|
2330
|
+
isWritable: false,
|
|
2331
|
+
isSigner: false,
|
|
2332
|
+
});
|
|
2333
|
+
remainingAccounts.push({
|
|
2334
|
+
pubkey: TOKEN_PROGRAM_ID,
|
|
2335
|
+
isWritable: false,
|
|
2336
|
+
isSigner: false,
|
|
2337
|
+
});
|
|
2338
|
+
remainingAccounts.push({
|
|
2339
|
+
pubkey: this.getSpotMarketAccount(marketIndex).vault,
|
|
2340
|
+
isWritable: true,
|
|
2341
|
+
isSigner: false,
|
|
2342
|
+
});
|
|
2343
|
+
remainingAccounts.push({
|
|
2344
|
+
pubkey: this.getQuoteSpotMarketAccount().vault,
|
|
2345
|
+
isWritable: true,
|
|
2346
|
+
isSigner: false,
|
|
2347
|
+
});
|
|
2348
|
+
remainingAccounts.push({
|
|
2349
|
+
pubkey: this.getStateAccount().srmVault,
|
|
2350
|
+
isWritable: false,
|
|
2351
|
+
isSigner: false,
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
public async triggerOrder(
|
|
2356
|
+
userAccountPublicKey: PublicKey,
|
|
2357
|
+
user: UserAccount,
|
|
2358
|
+
order: Order
|
|
2359
|
+
): Promise<TransactionSignature> {
|
|
2360
|
+
const { txSig } = await this.txSender.send(
|
|
2361
|
+
wrapInTx(await this.getTriggerOrderIx(userAccountPublicKey, user, order)),
|
|
2362
|
+
[],
|
|
2363
|
+
this.opts
|
|
2364
|
+
);
|
|
2365
|
+
return txSig;
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
public async getTriggerOrderIx(
|
|
2369
|
+
userAccountPublicKey: PublicKey,
|
|
2370
|
+
userAccount: UserAccount,
|
|
2371
|
+
order: Order
|
|
2372
|
+
): Promise<TransactionInstruction> {
|
|
2373
|
+
const fillerPublicKey = await this.getUserAccountPublicKey();
|
|
2374
|
+
|
|
2375
|
+
let remainingAccountsParams;
|
|
2376
|
+
if (isVariant(order.marketType, 'perp')) {
|
|
2377
|
+
remainingAccountsParams = {
|
|
2378
|
+
userAccounts: [userAccount],
|
|
2379
|
+
writablePerpMarketIndexes: [order.marketIndex],
|
|
2380
|
+
};
|
|
2381
|
+
} else {
|
|
2382
|
+
remainingAccountsParams = {
|
|
2383
|
+
userAccounts: [userAccount],
|
|
2384
|
+
writableSpotMarketIndexes: [order.marketIndex, QUOTE_SPOT_MARKET_INDEX],
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
const remainingAccounts = this.getRemainingAccounts(
|
|
2389
|
+
remainingAccountsParams
|
|
2390
|
+
);
|
|
2391
|
+
|
|
2392
|
+
const orderId = order.orderId;
|
|
2393
|
+
return await this.program.instruction.triggerOrder(orderId, {
|
|
2394
|
+
accounts: {
|
|
2395
|
+
state: await this.getStatePublicKey(),
|
|
2396
|
+
filler: fillerPublicKey,
|
|
2397
|
+
user: userAccountPublicKey,
|
|
2398
|
+
authority: this.wallet.publicKey,
|
|
2399
|
+
},
|
|
2400
|
+
remainingAccounts,
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
public async placeAndTakePerpOrder(
|
|
2405
|
+
orderParams: OptionalOrderParams,
|
|
2406
|
+
makerInfo?: MakerInfo,
|
|
2407
|
+
referrerInfo?: ReferrerInfo
|
|
2408
|
+
): Promise<TransactionSignature> {
|
|
2409
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2410
|
+
wrapInTx(
|
|
2411
|
+
await this.getPlaceAndTakePerpOrderIx(
|
|
2412
|
+
orderParams,
|
|
2413
|
+
makerInfo,
|
|
2414
|
+
referrerInfo
|
|
2415
|
+
)
|
|
2416
|
+
),
|
|
2417
|
+
[],
|
|
2418
|
+
this.opts
|
|
2419
|
+
);
|
|
2420
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2421
|
+
return txSig;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
public async getPlaceAndTakePerpOrderIx(
|
|
2425
|
+
orderParams: OptionalOrderParams,
|
|
2426
|
+
makerInfo?: MakerInfo,
|
|
2427
|
+
referrerInfo?: ReferrerInfo
|
|
2428
|
+
): Promise<TransactionInstruction> {
|
|
2429
|
+
orderParams = this.getOrderParams(orderParams, MarketType.PERP);
|
|
2430
|
+
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
2431
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2432
|
+
|
|
2433
|
+
const userAccounts = [this.getUserAccount()];
|
|
2434
|
+
if (makerInfo !== undefined) {
|
|
2435
|
+
userAccounts.push(makerInfo.makerUserAccount);
|
|
2436
|
+
}
|
|
2437
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2438
|
+
userAccounts,
|
|
2439
|
+
useMarketLastSlotCache: true,
|
|
2440
|
+
writablePerpMarketIndexes: [orderParams.marketIndex],
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2443
|
+
let makerOrderId = null;
|
|
2444
|
+
if (makerInfo) {
|
|
2445
|
+
makerOrderId = makerInfo.order.orderId;
|
|
2446
|
+
remainingAccounts.push({
|
|
2447
|
+
pubkey: makerInfo.maker,
|
|
2448
|
+
isSigner: false,
|
|
2449
|
+
isWritable: true,
|
|
2450
|
+
});
|
|
2451
|
+
remainingAccounts.push({
|
|
2452
|
+
pubkey: makerInfo.makerStats,
|
|
2453
|
+
isSigner: false,
|
|
2454
|
+
isWritable: true,
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
if (referrerInfo) {
|
|
2459
|
+
remainingAccounts.push({
|
|
2460
|
+
pubkey: referrerInfo.referrer,
|
|
2461
|
+
isWritable: true,
|
|
2462
|
+
isSigner: false,
|
|
2463
|
+
});
|
|
2464
|
+
remainingAccounts.push({
|
|
2465
|
+
pubkey: referrerInfo.referrerStats,
|
|
2466
|
+
isWritable: true,
|
|
2467
|
+
isSigner: false,
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
return await this.program.instruction.placeAndTakePerpOrder(
|
|
2472
|
+
orderParams,
|
|
2473
|
+
makerOrderId,
|
|
2474
|
+
{
|
|
2475
|
+
accounts: {
|
|
2476
|
+
state: await this.getStatePublicKey(),
|
|
2477
|
+
user: userAccountPublicKey,
|
|
2478
|
+
userStats: userStatsPublicKey,
|
|
2479
|
+
authority: this.wallet.publicKey,
|
|
2480
|
+
},
|
|
2481
|
+
remainingAccounts,
|
|
2482
|
+
}
|
|
2483
|
+
);
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
public async placeAndMakePerpOrder(
|
|
2487
|
+
orderParams: OptionalOrderParams,
|
|
2488
|
+
takerInfo: TakerInfo,
|
|
2489
|
+
referrerInfo?: ReferrerInfo
|
|
2490
|
+
): Promise<TransactionSignature> {
|
|
2491
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2492
|
+
wrapInTx(
|
|
2493
|
+
await this.getPlaceAndMakePerpOrderIx(
|
|
2494
|
+
orderParams,
|
|
2495
|
+
takerInfo,
|
|
2496
|
+
referrerInfo
|
|
2497
|
+
)
|
|
2498
|
+
),
|
|
2499
|
+
[],
|
|
2500
|
+
this.opts
|
|
2501
|
+
);
|
|
2502
|
+
|
|
2503
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2504
|
+
|
|
2505
|
+
return txSig;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
public async getPlaceAndMakePerpOrderIx(
|
|
2509
|
+
orderParams: OptionalOrderParams,
|
|
2510
|
+
takerInfo: TakerInfo,
|
|
2511
|
+
referrerInfo?: ReferrerInfo
|
|
2512
|
+
): Promise<TransactionInstruction> {
|
|
2513
|
+
orderParams = this.getOrderParams(orderParams, MarketType.PERP);
|
|
2514
|
+
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2515
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2516
|
+
|
|
2517
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2518
|
+
userAccounts: [this.getUserAccount(), takerInfo.takerUserAccount],
|
|
2519
|
+
useMarketLastSlotCache: true,
|
|
2520
|
+
writablePerpMarketIndexes: [orderParams.marketIndex],
|
|
2521
|
+
});
|
|
2522
|
+
|
|
2523
|
+
if (referrerInfo) {
|
|
2524
|
+
remainingAccounts.push({
|
|
2525
|
+
pubkey: referrerInfo.referrer,
|
|
2526
|
+
isWritable: true,
|
|
2527
|
+
isSigner: false,
|
|
2528
|
+
});
|
|
2529
|
+
remainingAccounts.push({
|
|
2530
|
+
pubkey: referrerInfo.referrerStats,
|
|
2531
|
+
isWritable: true,
|
|
2532
|
+
isSigner: false,
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
const takerOrderId = takerInfo.order.orderId;
|
|
2537
|
+
return await this.program.instruction.placeAndMakePerpOrder(
|
|
2538
|
+
orderParams,
|
|
2539
|
+
takerOrderId,
|
|
2540
|
+
{
|
|
2541
|
+
accounts: {
|
|
2542
|
+
state: await this.getStatePublicKey(),
|
|
2543
|
+
user: userAccountPublicKey,
|
|
2544
|
+
userStats: userStatsPublicKey,
|
|
2545
|
+
taker: takerInfo.taker,
|
|
2546
|
+
takerStats: takerInfo.takerStats,
|
|
2547
|
+
authority: this.wallet.publicKey,
|
|
2548
|
+
},
|
|
2549
|
+
remainingAccounts,
|
|
2550
|
+
}
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
public async placeAndTakeSpotOrder(
|
|
2555
|
+
orderParams: OptionalOrderParams,
|
|
2556
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2557
|
+
makerInfo?: MakerInfo,
|
|
2558
|
+
referrerInfo?: ReferrerInfo
|
|
2559
|
+
): Promise<TransactionSignature> {
|
|
2560
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2561
|
+
wrapInTx(
|
|
2562
|
+
await this.getPlaceAndTakeSpotOrderIx(
|
|
2563
|
+
orderParams,
|
|
2564
|
+
fulfillmentConfig,
|
|
2565
|
+
makerInfo,
|
|
2566
|
+
referrerInfo
|
|
2567
|
+
)
|
|
2568
|
+
),
|
|
2569
|
+
[],
|
|
2570
|
+
this.opts
|
|
2571
|
+
);
|
|
2572
|
+
this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2573
|
+
this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
|
|
2574
|
+
return txSig;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
public async getPlaceAndTakeSpotOrderIx(
|
|
2578
|
+
orderParams: OptionalOrderParams,
|
|
2579
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2580
|
+
makerInfo?: MakerInfo,
|
|
2581
|
+
referrerInfo?: ReferrerInfo
|
|
2582
|
+
): Promise<TransactionInstruction> {
|
|
2583
|
+
orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
|
|
2584
|
+
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
2585
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2586
|
+
|
|
2587
|
+
const userAccounts = [this.getUserAccount()];
|
|
2588
|
+
if (makerInfo !== undefined) {
|
|
2589
|
+
userAccounts.push(makerInfo.makerUserAccount);
|
|
2590
|
+
}
|
|
2591
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2592
|
+
userAccounts,
|
|
2593
|
+
useMarketLastSlotCache: true,
|
|
2594
|
+
writableSpotMarketIndexes: [
|
|
2595
|
+
orderParams.marketIndex,
|
|
2596
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
2597
|
+
],
|
|
2598
|
+
});
|
|
2599
|
+
|
|
2600
|
+
let makerOrderId = null;
|
|
2601
|
+
if (makerInfo) {
|
|
2602
|
+
makerOrderId = makerInfo.order.orderId;
|
|
2603
|
+
remainingAccounts.push({
|
|
2604
|
+
pubkey: makerInfo.maker,
|
|
2605
|
+
isSigner: false,
|
|
2606
|
+
isWritable: true,
|
|
2607
|
+
});
|
|
2608
|
+
remainingAccounts.push({
|
|
2609
|
+
pubkey: makerInfo.makerStats,
|
|
2610
|
+
isSigner: false,
|
|
2611
|
+
isWritable: true,
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
if (referrerInfo) {
|
|
2616
|
+
remainingAccounts.push({
|
|
2617
|
+
pubkey: referrerInfo.referrer,
|
|
2618
|
+
isWritable: true,
|
|
2619
|
+
isSigner: false,
|
|
2620
|
+
});
|
|
2621
|
+
remainingAccounts.push({
|
|
2622
|
+
pubkey: referrerInfo.referrerStats,
|
|
2623
|
+
isWritable: true,
|
|
2624
|
+
isSigner: false,
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
this.addSpotFulfillmentAccounts(
|
|
2629
|
+
orderParams.marketIndex,
|
|
2630
|
+
remainingAccounts,
|
|
2631
|
+
fulfillmentConfig
|
|
2632
|
+
);
|
|
2633
|
+
|
|
2634
|
+
return await this.program.instruction.placeAndTakeSpotOrder(
|
|
2635
|
+
orderParams,
|
|
2636
|
+
fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
|
|
2637
|
+
makerOrderId,
|
|
2638
|
+
{
|
|
2639
|
+
accounts: {
|
|
2640
|
+
state: await this.getStatePublicKey(),
|
|
2641
|
+
user: userAccountPublicKey,
|
|
2642
|
+
userStats: userStatsPublicKey,
|
|
2643
|
+
authority: this.wallet.publicKey,
|
|
2644
|
+
},
|
|
2645
|
+
remainingAccounts,
|
|
2646
|
+
}
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
public async placeAndMakeSpotOrder(
|
|
2651
|
+
orderParams: OptionalOrderParams,
|
|
2652
|
+
takerInfo: TakerInfo,
|
|
2653
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2654
|
+
referrerInfo?: ReferrerInfo
|
|
2655
|
+
): Promise<TransactionSignature> {
|
|
2656
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2657
|
+
wrapInTx(
|
|
2658
|
+
await this.getPlaceAndMakeSpotOrderIx(
|
|
2659
|
+
orderParams,
|
|
2660
|
+
takerInfo,
|
|
2661
|
+
fulfillmentConfig,
|
|
2662
|
+
referrerInfo
|
|
2663
|
+
)
|
|
2664
|
+
),
|
|
2665
|
+
[],
|
|
2666
|
+
this.opts
|
|
2667
|
+
);
|
|
2668
|
+
this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2669
|
+
this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
|
|
2670
|
+
return txSig;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
public async getPlaceAndMakeSpotOrderIx(
|
|
2674
|
+
orderParams: OptionalOrderParams,
|
|
2675
|
+
takerInfo: TakerInfo,
|
|
2676
|
+
fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
|
|
2677
|
+
referrerInfo?: ReferrerInfo
|
|
2678
|
+
): Promise<TransactionInstruction> {
|
|
2679
|
+
orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
|
|
2680
|
+
const userStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2681
|
+
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
2682
|
+
|
|
2683
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2684
|
+
userAccounts: [this.getUserAccount(), takerInfo.takerUserAccount],
|
|
2685
|
+
useMarketLastSlotCache: true,
|
|
2686
|
+
writableSpotMarketIndexes: [
|
|
2687
|
+
orderParams.marketIndex,
|
|
2688
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
2689
|
+
],
|
|
2690
|
+
});
|
|
2691
|
+
|
|
2692
|
+
if (referrerInfo) {
|
|
2693
|
+
remainingAccounts.push({
|
|
2694
|
+
pubkey: referrerInfo.referrer,
|
|
2695
|
+
isWritable: true,
|
|
2696
|
+
isSigner: false,
|
|
2697
|
+
});
|
|
2698
|
+
remainingAccounts.push({
|
|
2699
|
+
pubkey: referrerInfo.referrerStats,
|
|
2700
|
+
isWritable: true,
|
|
2701
|
+
isSigner: false,
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
this.addSpotFulfillmentAccounts(
|
|
2706
|
+
orderParams.marketIndex,
|
|
2707
|
+
remainingAccounts,
|
|
2708
|
+
fulfillmentConfig
|
|
2709
|
+
);
|
|
2710
|
+
|
|
2711
|
+
const takerOrderId = takerInfo.order.orderId;
|
|
2712
|
+
return await this.program.instruction.placeAndMakeSpotOrder(
|
|
2713
|
+
orderParams,
|
|
2714
|
+
takerOrderId,
|
|
2715
|
+
fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
|
|
2716
|
+
{
|
|
2717
|
+
accounts: {
|
|
2718
|
+
state: await this.getStatePublicKey(),
|
|
2719
|
+
user: userAccountPublicKey,
|
|
2720
|
+
userStats: userStatsPublicKey,
|
|
2721
|
+
taker: takerInfo.taker,
|
|
2722
|
+
takerStats: takerInfo.takerStats,
|
|
2723
|
+
authority: this.wallet.publicKey,
|
|
2724
|
+
},
|
|
2725
|
+
remainingAccounts,
|
|
2726
|
+
}
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
/**
|
|
2731
|
+
* Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
|
|
2732
|
+
* @param marketIndex
|
|
2733
|
+
* @returns
|
|
2734
|
+
*/
|
|
2735
|
+
public async closePosition(
|
|
2736
|
+
marketIndex: number,
|
|
2737
|
+
limitPrice?: BN
|
|
2738
|
+
): Promise<TransactionSignature> {
|
|
2739
|
+
const userPosition = this.getUser().getPerpPosition(marketIndex);
|
|
2740
|
+
if (!userPosition) {
|
|
2741
|
+
throw Error(`No position in market ${marketIndex.toString()}`);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
return await this.placeAndTakePerpOrder({
|
|
2745
|
+
orderType: OrderType.MARKET,
|
|
2746
|
+
marketIndex,
|
|
2747
|
+
direction: findDirectionToClose(userPosition),
|
|
2748
|
+
baseAssetAmount: userPosition.baseAssetAmount.abs(),
|
|
2749
|
+
reduceOnly: true,
|
|
2750
|
+
price: limitPrice,
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Modifies an open order by closing it and replacing it with a new order.
|
|
2756
|
+
* @param orderId: The open order to modify
|
|
2757
|
+
* @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
|
|
2758
|
+
* @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
|
|
2759
|
+
* @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
|
|
2760
|
+
* @returns
|
|
2761
|
+
*/
|
|
2762
|
+
public async modifyPerpOrder(
|
|
2763
|
+
orderId: number,
|
|
2764
|
+
newBaseAmount?: BN,
|
|
2765
|
+
newLimitPrice?: BN,
|
|
2766
|
+
newOraclePriceOffset?: number
|
|
2767
|
+
): Promise<TransactionSignature> {
|
|
2768
|
+
if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
|
|
2769
|
+
throw new Error(
|
|
2770
|
+
`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
const openOrder = this.getUser().getOrder(orderId);
|
|
2775
|
+
if (!openOrder) {
|
|
2776
|
+
throw new Error(`No open order with id ${orderId.toString()}`);
|
|
2777
|
+
}
|
|
2778
|
+
const cancelOrderIx = await this.getCancelOrderIx(orderId);
|
|
2779
|
+
|
|
2780
|
+
const newOrderParams: OptionalOrderParams = {
|
|
2781
|
+
orderType: openOrder.orderType,
|
|
2782
|
+
marketType: openOrder.marketType,
|
|
2783
|
+
direction: openOrder.direction,
|
|
2784
|
+
baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
|
|
2785
|
+
price: newLimitPrice || openOrder.price,
|
|
2786
|
+
marketIndex: openOrder.marketIndex,
|
|
2787
|
+
reduceOnly: openOrder.reduceOnly,
|
|
2788
|
+
postOnly: openOrder.postOnly,
|
|
2789
|
+
immediateOrCancel: openOrder.immediateOrCancel,
|
|
2790
|
+
triggerPrice: openOrder.triggerPrice,
|
|
2791
|
+
triggerCondition: openOrder.triggerCondition,
|
|
2792
|
+
oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
|
|
2793
|
+
auctionDuration: openOrder.auctionDuration,
|
|
2794
|
+
maxTs: openOrder.maxTs,
|
|
2795
|
+
auctionStartPrice: openOrder.auctionStartPrice,
|
|
2796
|
+
auctionEndPrice: openOrder.auctionEndPrice,
|
|
2797
|
+
};
|
|
2798
|
+
const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
|
|
2799
|
+
|
|
2800
|
+
const tx = new Transaction();
|
|
2801
|
+
tx.add(
|
|
2802
|
+
ComputeBudgetProgram.requestUnits({
|
|
2803
|
+
units: 1_000_000,
|
|
2804
|
+
additionalFee: 0,
|
|
2805
|
+
})
|
|
2806
|
+
);
|
|
2807
|
+
tx.add(cancelOrderIx);
|
|
2808
|
+
tx.add(placeOrderIx);
|
|
2809
|
+
const { txSig, slot } = await this.txSender.send(tx, [], this.opts);
|
|
2810
|
+
this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
|
|
2811
|
+
return txSig;
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
public async settlePNLs(
|
|
2815
|
+
users: {
|
|
2816
|
+
settleeUserAccountPublicKey: PublicKey;
|
|
2817
|
+
settleeUserAccount: UserAccount;
|
|
2818
|
+
}[],
|
|
2819
|
+
marketIndex: number
|
|
2820
|
+
): Promise<TransactionSignature> {
|
|
2821
|
+
const ixs = [];
|
|
2822
|
+
for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
|
|
2823
|
+
ixs.push(
|
|
2824
|
+
await this.settlePNLIx(
|
|
2825
|
+
settleeUserAccountPublicKey,
|
|
2826
|
+
settleeUserAccount,
|
|
2827
|
+
marketIndex
|
|
2828
|
+
)
|
|
2829
|
+
);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
const tx = new Transaction()
|
|
2833
|
+
.add(
|
|
2834
|
+
ComputeBudgetProgram.requestUnits({
|
|
2835
|
+
units: 1_000_000,
|
|
2836
|
+
additionalFee: 0,
|
|
2837
|
+
})
|
|
2838
|
+
)
|
|
2839
|
+
.add(...ixs);
|
|
2840
|
+
|
|
2841
|
+
const { txSig } = await this.txSender.send(tx, [], this.opts);
|
|
2842
|
+
return txSig;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
public async settlePNL(
|
|
2846
|
+
settleeUserAccountPublicKey: PublicKey,
|
|
2847
|
+
settleeUserAccount: UserAccount,
|
|
2848
|
+
marketIndex: number
|
|
2849
|
+
): Promise<TransactionSignature> {
|
|
2850
|
+
const { txSig } = await this.txSender.send(
|
|
2851
|
+
wrapInTx(
|
|
2852
|
+
await this.settlePNLIx(
|
|
2853
|
+
settleeUserAccountPublicKey,
|
|
2854
|
+
settleeUserAccount,
|
|
2855
|
+
marketIndex
|
|
2856
|
+
)
|
|
2857
|
+
),
|
|
2858
|
+
[],
|
|
2859
|
+
this.opts
|
|
2860
|
+
);
|
|
2861
|
+
return txSig;
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
public async settlePNLIx(
|
|
2865
|
+
settleeUserAccountPublicKey: PublicKey,
|
|
2866
|
+
settleeUserAccount: UserAccount,
|
|
2867
|
+
marketIndex: number
|
|
2868
|
+
): Promise<TransactionInstruction> {
|
|
2869
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2870
|
+
userAccounts: [settleeUserAccount],
|
|
2871
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
2872
|
+
writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
|
|
2873
|
+
});
|
|
2874
|
+
|
|
2875
|
+
return await this.program.instruction.settlePnl(marketIndex, {
|
|
2876
|
+
accounts: {
|
|
2877
|
+
state: await this.getStatePublicKey(),
|
|
2878
|
+
authority: this.wallet.publicKey,
|
|
2879
|
+
user: settleeUserAccountPublicKey,
|
|
2880
|
+
spotMarketVault: this.getQuoteSpotMarketAccount().vault,
|
|
2881
|
+
},
|
|
2882
|
+
remainingAccounts: remainingAccounts,
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
public async liquidatePerp(
|
|
2887
|
+
userAccountPublicKey: PublicKey,
|
|
2888
|
+
userAccount: UserAccount,
|
|
2889
|
+
marketIndex: number,
|
|
2890
|
+
maxBaseAssetAmount: BN,
|
|
2891
|
+
limitPrice?: BN
|
|
2892
|
+
): Promise<TransactionSignature> {
|
|
2893
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2894
|
+
wrapInTx(
|
|
2895
|
+
await this.getLiquidatePerpIx(
|
|
2896
|
+
userAccountPublicKey,
|
|
2897
|
+
userAccount,
|
|
2898
|
+
marketIndex,
|
|
2899
|
+
maxBaseAssetAmount,
|
|
2900
|
+
limitPrice
|
|
2901
|
+
)
|
|
2902
|
+
),
|
|
2903
|
+
[],
|
|
2904
|
+
this.opts
|
|
2905
|
+
);
|
|
2906
|
+
this.perpMarketLastSlotCache.set(marketIndex, slot);
|
|
2907
|
+
return txSig;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
public async getLiquidatePerpIx(
|
|
2911
|
+
userAccountPublicKey: PublicKey,
|
|
2912
|
+
userAccount: UserAccount,
|
|
2913
|
+
marketIndex: number,
|
|
2914
|
+
maxBaseAssetAmount: BN,
|
|
2915
|
+
limitPrice?: BN
|
|
2916
|
+
): Promise<TransactionInstruction> {
|
|
2917
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
2918
|
+
this.program.programId,
|
|
2919
|
+
userAccount.authority
|
|
2920
|
+
);
|
|
2921
|
+
|
|
2922
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
2923
|
+
const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
|
|
2924
|
+
|
|
2925
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2926
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
2927
|
+
useMarketLastSlotCache: true,
|
|
2928
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2931
|
+
return await this.program.instruction.liquidatePerp(
|
|
2932
|
+
marketIndex,
|
|
2933
|
+
maxBaseAssetAmount,
|
|
2934
|
+
limitPrice ?? null,
|
|
2935
|
+
{
|
|
2936
|
+
accounts: {
|
|
2937
|
+
state: await this.getStatePublicKey(),
|
|
2938
|
+
authority: this.wallet.publicKey,
|
|
2939
|
+
user: userAccountPublicKey,
|
|
2940
|
+
userStats: userStatsPublicKey,
|
|
2941
|
+
liquidator: liquidatorPublicKey,
|
|
2942
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
2943
|
+
},
|
|
2944
|
+
remainingAccounts: remainingAccounts,
|
|
2945
|
+
}
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
public async liquidateSpot(
|
|
2950
|
+
userAccountPublicKey: PublicKey,
|
|
2951
|
+
userAccount: UserAccount,
|
|
2952
|
+
assetMarketIndex: number,
|
|
2953
|
+
liabilityMarketIndex: number,
|
|
2954
|
+
maxLiabilityTransfer: BN,
|
|
2955
|
+
limitPrice?: BN
|
|
2956
|
+
): Promise<TransactionSignature> {
|
|
2957
|
+
const { txSig, slot } = await this.txSender.send(
|
|
2958
|
+
wrapInTx(
|
|
2959
|
+
await this.getLiquidateSpotIx(
|
|
2960
|
+
userAccountPublicKey,
|
|
2961
|
+
userAccount,
|
|
2962
|
+
assetMarketIndex,
|
|
2963
|
+
liabilityMarketIndex,
|
|
2964
|
+
maxLiabilityTransfer,
|
|
2965
|
+
limitPrice
|
|
2966
|
+
)
|
|
2967
|
+
),
|
|
2968
|
+
[],
|
|
2969
|
+
this.opts
|
|
2970
|
+
);
|
|
2971
|
+
this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
|
|
2972
|
+
this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
|
|
2973
|
+
return txSig;
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
public async getLiquidateSpotIx(
|
|
2977
|
+
userAccountPublicKey: PublicKey,
|
|
2978
|
+
userAccount: UserAccount,
|
|
2979
|
+
assetMarketIndex: number,
|
|
2980
|
+
liabilityMarketIndex: number,
|
|
2981
|
+
maxLiabilityTransfer: BN,
|
|
2982
|
+
limitPrice?: BN
|
|
2983
|
+
): Promise<TransactionInstruction> {
|
|
2984
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
2985
|
+
this.program.programId,
|
|
2986
|
+
userAccount.authority
|
|
2987
|
+
);
|
|
2988
|
+
|
|
2989
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
2990
|
+
const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
2991
|
+
|
|
2992
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
2993
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
2994
|
+
useMarketLastSlotCache: true,
|
|
2995
|
+
writableSpotMarketIndexes: [liabilityMarketIndex, assetMarketIndex],
|
|
2996
|
+
});
|
|
2997
|
+
|
|
2998
|
+
return await this.program.instruction.liquidateSpot(
|
|
2999
|
+
assetMarketIndex,
|
|
3000
|
+
liabilityMarketIndex,
|
|
3001
|
+
maxLiabilityTransfer,
|
|
3002
|
+
limitPrice || null,
|
|
3003
|
+
{
|
|
3004
|
+
accounts: {
|
|
3005
|
+
state: await this.getStatePublicKey(),
|
|
3006
|
+
authority: this.wallet.publicKey,
|
|
3007
|
+
user: userAccountPublicKey,
|
|
3008
|
+
userStats: userStatsPublicKey,
|
|
3009
|
+
liquidator: liquidatorPublicKey,
|
|
3010
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
3011
|
+
},
|
|
3012
|
+
remainingAccounts: remainingAccounts,
|
|
3013
|
+
}
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
public async liquidateBorrowForPerpPnl(
|
|
3018
|
+
userAccountPublicKey: PublicKey,
|
|
3019
|
+
userAccount: UserAccount,
|
|
3020
|
+
perpMarketIndex: number,
|
|
3021
|
+
liabilityMarketIndex: number,
|
|
3022
|
+
maxLiabilityTransfer: BN,
|
|
3023
|
+
limitPrice?: BN
|
|
3024
|
+
): Promise<TransactionSignature> {
|
|
3025
|
+
const { txSig, slot } = await this.txSender.send(
|
|
3026
|
+
wrapInTx(
|
|
3027
|
+
await this.getLiquidateBorrowForPerpPnlIx(
|
|
3028
|
+
userAccountPublicKey,
|
|
3029
|
+
userAccount,
|
|
3030
|
+
perpMarketIndex,
|
|
3031
|
+
liabilityMarketIndex,
|
|
3032
|
+
maxLiabilityTransfer,
|
|
3033
|
+
limitPrice
|
|
3034
|
+
)
|
|
3035
|
+
),
|
|
3036
|
+
[],
|
|
3037
|
+
this.opts
|
|
3038
|
+
);
|
|
3039
|
+
this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
|
|
3040
|
+
this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
|
|
3041
|
+
return txSig;
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
public async getLiquidateBorrowForPerpPnlIx(
|
|
3045
|
+
userAccountPublicKey: PublicKey,
|
|
3046
|
+
userAccount: UserAccount,
|
|
3047
|
+
perpMarketIndex: number,
|
|
3048
|
+
liabilityMarketIndex: number,
|
|
3049
|
+
maxLiabilityTransfer: BN,
|
|
3050
|
+
limitPrice?: BN
|
|
3051
|
+
): Promise<TransactionInstruction> {
|
|
3052
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
3053
|
+
this.program.programId,
|
|
3054
|
+
userAccount.authority
|
|
3055
|
+
);
|
|
3056
|
+
|
|
3057
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
3058
|
+
const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
3059
|
+
|
|
3060
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3061
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
3062
|
+
writablePerpMarketIndexes: [perpMarketIndex],
|
|
3063
|
+
writableSpotMarketIndexes: [liabilityMarketIndex],
|
|
3064
|
+
});
|
|
3065
|
+
|
|
3066
|
+
return await this.program.instruction.liquidateBorrowForPerpPnl(
|
|
3067
|
+
perpMarketIndex,
|
|
3068
|
+
liabilityMarketIndex,
|
|
3069
|
+
maxLiabilityTransfer,
|
|
3070
|
+
limitPrice || null,
|
|
3071
|
+
{
|
|
3072
|
+
accounts: {
|
|
3073
|
+
state: await this.getStatePublicKey(),
|
|
3074
|
+
authority: this.wallet.publicKey,
|
|
3075
|
+
user: userAccountPublicKey,
|
|
3076
|
+
userStats: userStatsPublicKey,
|
|
3077
|
+
liquidator: liquidatorPublicKey,
|
|
3078
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
3079
|
+
},
|
|
3080
|
+
remainingAccounts: remainingAccounts,
|
|
3081
|
+
}
|
|
3082
|
+
);
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
public async liquidatePerpPnlForDeposit(
|
|
3086
|
+
userAccountPublicKey: PublicKey,
|
|
3087
|
+
userAccount: UserAccount,
|
|
3088
|
+
perpMarketIndex: number,
|
|
3089
|
+
assetMarketIndex: number,
|
|
3090
|
+
maxPnlTransfer: BN,
|
|
3091
|
+
limitPrice?: BN
|
|
3092
|
+
): Promise<TransactionSignature> {
|
|
3093
|
+
const { txSig, slot } = await this.txSender.send(
|
|
3094
|
+
wrapInTx(
|
|
3095
|
+
await this.getLiquidatePerpPnlForDepositIx(
|
|
3096
|
+
userAccountPublicKey,
|
|
3097
|
+
userAccount,
|
|
3098
|
+
perpMarketIndex,
|
|
3099
|
+
assetMarketIndex,
|
|
3100
|
+
maxPnlTransfer,
|
|
3101
|
+
limitPrice
|
|
3102
|
+
)
|
|
3103
|
+
),
|
|
3104
|
+
[],
|
|
3105
|
+
this.opts
|
|
3106
|
+
);
|
|
3107
|
+
this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
|
|
3108
|
+
this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
|
|
3109
|
+
return txSig;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
public async getLiquidatePerpPnlForDepositIx(
|
|
3113
|
+
userAccountPublicKey: PublicKey,
|
|
3114
|
+
userAccount: UserAccount,
|
|
3115
|
+
perpMarketIndex: number,
|
|
3116
|
+
assetMarketIndex: number,
|
|
3117
|
+
maxPnlTransfer: BN,
|
|
3118
|
+
limitPrice?: BN
|
|
3119
|
+
): Promise<TransactionInstruction> {
|
|
3120
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
3121
|
+
this.program.programId,
|
|
3122
|
+
userAccount.authority
|
|
3123
|
+
);
|
|
3124
|
+
|
|
3125
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
3126
|
+
const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
3127
|
+
|
|
3128
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3129
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
3130
|
+
writablePerpMarketIndexes: [perpMarketIndex],
|
|
3131
|
+
writableSpotMarketIndexes: [assetMarketIndex],
|
|
3132
|
+
});
|
|
3133
|
+
|
|
3134
|
+
return await this.program.instruction.liquidatePerpPnlForDeposit(
|
|
3135
|
+
perpMarketIndex,
|
|
3136
|
+
assetMarketIndex,
|
|
3137
|
+
maxPnlTransfer,
|
|
3138
|
+
limitPrice || null,
|
|
3139
|
+
{
|
|
3140
|
+
accounts: {
|
|
3141
|
+
state: await this.getStatePublicKey(),
|
|
3142
|
+
authority: this.wallet.publicKey,
|
|
3143
|
+
user: userAccountPublicKey,
|
|
3144
|
+
userStats: userStatsPublicKey,
|
|
3145
|
+
liquidator: liquidatorPublicKey,
|
|
3146
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
3147
|
+
},
|
|
3148
|
+
remainingAccounts: remainingAccounts,
|
|
3149
|
+
}
|
|
3150
|
+
);
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
public async resolvePerpBankruptcy(
|
|
3154
|
+
userAccountPublicKey: PublicKey,
|
|
3155
|
+
userAccount: UserAccount,
|
|
3156
|
+
marketIndex: number
|
|
3157
|
+
): Promise<TransactionSignature> {
|
|
3158
|
+
const { txSig } = await this.txSender.send(
|
|
3159
|
+
wrapInTx(
|
|
3160
|
+
await this.getResolvePerpBankruptcyIx(
|
|
3161
|
+
userAccountPublicKey,
|
|
3162
|
+
userAccount,
|
|
3163
|
+
marketIndex
|
|
3164
|
+
)
|
|
3165
|
+
),
|
|
3166
|
+
[],
|
|
3167
|
+
this.opts
|
|
3168
|
+
);
|
|
3169
|
+
return txSig;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
public async getResolvePerpBankruptcyIx(
|
|
3173
|
+
userAccountPublicKey: PublicKey,
|
|
3174
|
+
userAccount: UserAccount,
|
|
3175
|
+
marketIndex: number
|
|
3176
|
+
): Promise<TransactionInstruction> {
|
|
3177
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
3178
|
+
this.program.programId,
|
|
3179
|
+
userAccount.authority
|
|
3180
|
+
);
|
|
3181
|
+
|
|
3182
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
3183
|
+
const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
3184
|
+
|
|
3185
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3186
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
3187
|
+
writablePerpMarketIndexes: [marketIndex],
|
|
3188
|
+
writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
|
|
3189
|
+
});
|
|
3190
|
+
|
|
3191
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
3192
|
+
|
|
3193
|
+
return await this.program.instruction.resolvePerpBankruptcy(
|
|
3194
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
3195
|
+
marketIndex,
|
|
3196
|
+
{
|
|
3197
|
+
accounts: {
|
|
3198
|
+
state: await this.getStatePublicKey(),
|
|
3199
|
+
authority: this.wallet.publicKey,
|
|
3200
|
+
user: userAccountPublicKey,
|
|
3201
|
+
userStats: userStatsPublicKey,
|
|
3202
|
+
liquidator: liquidatorPublicKey,
|
|
3203
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
3204
|
+
spotMarketVault: spotMarket.vault,
|
|
3205
|
+
insuranceFundVault: spotMarket.insuranceFund.vault,
|
|
3206
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3207
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3208
|
+
},
|
|
3209
|
+
remainingAccounts: remainingAccounts,
|
|
3210
|
+
}
|
|
3211
|
+
);
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
public async resolveSpotBankruptcy(
|
|
3215
|
+
userAccountPublicKey: PublicKey,
|
|
3216
|
+
userAccount: UserAccount,
|
|
3217
|
+
marketIndex: number
|
|
3218
|
+
): Promise<TransactionSignature> {
|
|
3219
|
+
const { txSig } = await this.txSender.send(
|
|
3220
|
+
wrapInTx(
|
|
3221
|
+
await this.getResolveSpotBankruptcyIx(
|
|
3222
|
+
userAccountPublicKey,
|
|
3223
|
+
userAccount,
|
|
3224
|
+
marketIndex
|
|
3225
|
+
)
|
|
3226
|
+
),
|
|
3227
|
+
[],
|
|
3228
|
+
this.opts
|
|
3229
|
+
);
|
|
3230
|
+
return txSig;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
public async getResolveSpotBankruptcyIx(
|
|
3234
|
+
userAccountPublicKey: PublicKey,
|
|
3235
|
+
userAccount: UserAccount,
|
|
3236
|
+
marketIndex: number
|
|
3237
|
+
): Promise<TransactionInstruction> {
|
|
3238
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
3239
|
+
this.program.programId,
|
|
3240
|
+
userAccount.authority
|
|
3241
|
+
);
|
|
3242
|
+
|
|
3243
|
+
const liquidatorPublicKey = await this.getUserAccountPublicKey();
|
|
3244
|
+
const liquidatorStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
3245
|
+
|
|
3246
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3247
|
+
userAccounts: [this.getUserAccount(), userAccount],
|
|
3248
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3249
|
+
});
|
|
3250
|
+
|
|
3251
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
3252
|
+
|
|
3253
|
+
return await this.program.instruction.resolveSpotBankruptcy(marketIndex, {
|
|
3254
|
+
accounts: {
|
|
3255
|
+
state: await this.getStatePublicKey(),
|
|
3256
|
+
authority: this.wallet.publicKey,
|
|
3257
|
+
user: userAccountPublicKey,
|
|
3258
|
+
userStats: userStatsPublicKey,
|
|
3259
|
+
liquidatorStats: liquidatorStatsPublicKey,
|
|
3260
|
+
liquidator: liquidatorPublicKey,
|
|
3261
|
+
spotMarketVault: spotMarket.vault,
|
|
3262
|
+
insuranceFundVault: spotMarket.insuranceFund.vault,
|
|
3263
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3264
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3265
|
+
},
|
|
3266
|
+
remainingAccounts: remainingAccounts,
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
public async updateFundingRate(
|
|
3271
|
+
perpMarketIndex: number,
|
|
3272
|
+
oracle: PublicKey
|
|
3273
|
+
): Promise<TransactionSignature> {
|
|
3274
|
+
const { txSig } = await this.txSender.send(
|
|
3275
|
+
wrapInTx(await this.getUpdateFundingRateIx(perpMarketIndex, oracle)),
|
|
3276
|
+
[],
|
|
3277
|
+
this.opts
|
|
3278
|
+
);
|
|
3279
|
+
return txSig;
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
public async getUpdateFundingRateIx(
|
|
3283
|
+
perpMarketIndex: number,
|
|
3284
|
+
oracle: PublicKey
|
|
3285
|
+
): Promise<TransactionInstruction> {
|
|
3286
|
+
const perpMarketPublicKey = await getPerpMarketPublicKey(
|
|
3287
|
+
this.program.programId,
|
|
3288
|
+
perpMarketIndex
|
|
3289
|
+
);
|
|
3290
|
+
return await this.program.instruction.updateFundingRate(perpMarketIndex, {
|
|
3291
|
+
accounts: {
|
|
3292
|
+
state: await this.getStatePublicKey(),
|
|
3293
|
+
perpMarket: perpMarketPublicKey,
|
|
3294
|
+
oracle: oracle,
|
|
3295
|
+
},
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
public async settleFundingPayment(
|
|
3300
|
+
userAccountPublicKey: PublicKey
|
|
3301
|
+
): Promise<TransactionSignature> {
|
|
3302
|
+
const { txSig } = await this.txSender.send(
|
|
3303
|
+
wrapInTx(await this.getSettleFundingPaymentIx(userAccountPublicKey)),
|
|
3304
|
+
[],
|
|
3305
|
+
this.opts
|
|
3306
|
+
);
|
|
3307
|
+
return txSig;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
public async getSettleFundingPaymentIx(
|
|
3311
|
+
userAccountPublicKey: PublicKey
|
|
3312
|
+
): Promise<TransactionInstruction> {
|
|
3313
|
+
const userAccount = (await this.program.account.user.fetch(
|
|
3314
|
+
userAccountPublicKey
|
|
3315
|
+
)) as UserAccount;
|
|
3316
|
+
|
|
3317
|
+
const writablePerpMarketIndexes = [];
|
|
3318
|
+
for (const position of userAccount.perpPositions) {
|
|
3319
|
+
if (!positionIsAvailable(position)) {
|
|
3320
|
+
writablePerpMarketIndexes.push(position.marketIndex);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3325
|
+
userAccounts: [userAccount],
|
|
3326
|
+
writablePerpMarketIndexes,
|
|
3327
|
+
});
|
|
3328
|
+
|
|
3329
|
+
return await this.program.instruction.settleFundingPayment({
|
|
3330
|
+
accounts: {
|
|
3331
|
+
state: await this.getStatePublicKey(),
|
|
3332
|
+
user: userAccountPublicKey,
|
|
3333
|
+
},
|
|
3334
|
+
remainingAccounts,
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
public triggerEvent(eventName: keyof DriftClientAccountEvents, data?: any) {
|
|
3339
|
+
this.eventEmitter.emit(eventName, data);
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
public getOracleDataForPerpMarket(marketIndex: number): OraclePriceData {
|
|
3343
|
+
const oracleKey = this.getPerpMarketAccount(marketIndex).amm.oracle;
|
|
3344
|
+
const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
|
|
3345
|
+
|
|
3346
|
+
return oracleData;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
public getOracleDataForSpotMarket(marketIndex: number): OraclePriceData {
|
|
3350
|
+
const oracleKey = this.getSpotMarketAccount(marketIndex).oracle;
|
|
3351
|
+
const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
|
|
3352
|
+
|
|
3353
|
+
return oracleData;
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
public async initializeInsuranceFundStake(
|
|
3357
|
+
marketIndex: number
|
|
3358
|
+
): Promise<TransactionSignature> {
|
|
3359
|
+
const { txSig } = await this.txSender.send(
|
|
3360
|
+
wrapInTx(await this.getInitializeInsuranceFundStakeIx(marketIndex)),
|
|
3361
|
+
[],
|
|
3362
|
+
this.opts
|
|
3363
|
+
);
|
|
3364
|
+
return txSig;
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
public async getInitializeInsuranceFundStakeIx(
|
|
3368
|
+
marketIndex: number
|
|
3369
|
+
): Promise<TransactionInstruction> {
|
|
3370
|
+
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3371
|
+
this.program.programId,
|
|
3372
|
+
this.wallet.publicKey,
|
|
3373
|
+
marketIndex
|
|
3374
|
+
);
|
|
3375
|
+
|
|
3376
|
+
return await this.program.instruction.initializeInsuranceFundStake(
|
|
3377
|
+
marketIndex,
|
|
3378
|
+
{
|
|
3379
|
+
accounts: {
|
|
3380
|
+
insuranceFundStake: ifStakeAccountPublicKey,
|
|
3381
|
+
spotMarket: this.getSpotMarketAccount(marketIndex).pubkey,
|
|
3382
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
3383
|
+
authority: this.wallet.publicKey,
|
|
3384
|
+
payer: this.wallet.publicKey,
|
|
3385
|
+
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
3386
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
3387
|
+
state: await this.getStatePublicKey(),
|
|
3388
|
+
},
|
|
3389
|
+
}
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
public async addInsuranceFundStake(
|
|
3394
|
+
marketIndex: number,
|
|
3395
|
+
amount: BN,
|
|
3396
|
+
collateralAccountPublicKey: PublicKey
|
|
3397
|
+
): Promise<TransactionSignature> {
|
|
3398
|
+
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
3399
|
+
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3400
|
+
this.program.programId,
|
|
3401
|
+
this.wallet.publicKey,
|
|
3402
|
+
marketIndex
|
|
3403
|
+
);
|
|
3404
|
+
|
|
3405
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3406
|
+
userAccounts: [this.getUserAccount()],
|
|
3407
|
+
useMarketLastSlotCache: true,
|
|
3408
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3409
|
+
});
|
|
3410
|
+
|
|
3411
|
+
return await this.program.rpc.addInsuranceFundStake(marketIndex, amount, {
|
|
3412
|
+
accounts: {
|
|
3413
|
+
state: await this.getStatePublicKey(),
|
|
3414
|
+
spotMarket: spotMarket.pubkey,
|
|
3415
|
+
insuranceFundStake: ifStakeAccountPublicKey,
|
|
3416
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
3417
|
+
authority: this.wallet.publicKey,
|
|
3418
|
+
spotMarketVault: spotMarket.vault,
|
|
3419
|
+
insuranceFundVault: spotMarket.insuranceFund.vault,
|
|
3420
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3421
|
+
userTokenAccount: collateralAccountPublicKey,
|
|
3422
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3423
|
+
},
|
|
3424
|
+
remainingAccounts,
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
public async requestRemoveInsuranceFundStake(
|
|
3429
|
+
marketIndex: number,
|
|
3430
|
+
amount: BN
|
|
3431
|
+
): Promise<TransactionSignature> {
|
|
3432
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3433
|
+
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3434
|
+
this.program.programId,
|
|
3435
|
+
this.wallet.publicKey,
|
|
3436
|
+
marketIndex
|
|
3437
|
+
);
|
|
3438
|
+
|
|
3439
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3440
|
+
userAccounts: [this.getUserAccount()],
|
|
3441
|
+
useMarketLastSlotCache: true,
|
|
3442
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3443
|
+
});
|
|
3444
|
+
|
|
3445
|
+
return await this.program.rpc.requestRemoveInsuranceFundStake(
|
|
3446
|
+
marketIndex,
|
|
3447
|
+
amount,
|
|
3448
|
+
{
|
|
3449
|
+
accounts: {
|
|
3450
|
+
state: await this.getStatePublicKey(),
|
|
3451
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
3452
|
+
insuranceFundStake: ifStakeAccountPublicKey,
|
|
3453
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
3454
|
+
authority: this.wallet.publicKey,
|
|
3455
|
+
insuranceFundVault: spotMarketAccount.insuranceFund.vault,
|
|
3456
|
+
},
|
|
3457
|
+
remainingAccounts,
|
|
3458
|
+
}
|
|
3459
|
+
);
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
public async cancelRequestRemoveInsuranceFundStake(
|
|
3463
|
+
marketIndex: number
|
|
3464
|
+
): Promise<TransactionSignature> {
|
|
3465
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3466
|
+
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3467
|
+
this.program.programId,
|
|
3468
|
+
this.wallet.publicKey,
|
|
3469
|
+
marketIndex
|
|
3470
|
+
);
|
|
3471
|
+
|
|
3472
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3473
|
+
userAccounts: [this.getUserAccount()],
|
|
3474
|
+
useMarketLastSlotCache: true,
|
|
3475
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3476
|
+
});
|
|
3477
|
+
|
|
3478
|
+
return await this.program.rpc.cancelRequestRemoveInsuranceFundStake(
|
|
3479
|
+
marketIndex,
|
|
3480
|
+
{
|
|
3481
|
+
accounts: {
|
|
3482
|
+
state: await this.getStatePublicKey(),
|
|
3483
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
3484
|
+
insuranceFundStake: ifStakeAccountPublicKey,
|
|
3485
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
3486
|
+
authority: this.wallet.publicKey,
|
|
3487
|
+
insuranceFundVault: spotMarketAccount.insuranceFund.vault,
|
|
3488
|
+
},
|
|
3489
|
+
remainingAccounts,
|
|
3490
|
+
}
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
public async removeInsuranceFundStake(
|
|
3495
|
+
marketIndex: number,
|
|
3496
|
+
collateralAccountPublicKey: PublicKey
|
|
3497
|
+
): Promise<TransactionSignature> {
|
|
3498
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3499
|
+
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3500
|
+
this.program.programId,
|
|
3501
|
+
this.wallet.publicKey,
|
|
3502
|
+
marketIndex
|
|
3503
|
+
);
|
|
3504
|
+
|
|
3505
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3506
|
+
userAccounts: [this.getUserAccount()],
|
|
3507
|
+
useMarketLastSlotCache: true,
|
|
3508
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3509
|
+
});
|
|
3510
|
+
|
|
3511
|
+
return await this.program.rpc.removeInsuranceFundStake(marketIndex, {
|
|
3512
|
+
accounts: {
|
|
3513
|
+
state: await this.getStatePublicKey(),
|
|
3514
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
3515
|
+
insuranceFundStake: ifStakeAccountPublicKey,
|
|
3516
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
3517
|
+
authority: this.wallet.publicKey,
|
|
3518
|
+
insuranceFundVault: spotMarketAccount.insuranceFund.vault,
|
|
3519
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3520
|
+
userTokenAccount: collateralAccountPublicKey,
|
|
3521
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3522
|
+
},
|
|
3523
|
+
remainingAccounts,
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
public async settleRevenueToInsuranceFund(
|
|
3528
|
+
marketIndex: number
|
|
3529
|
+
): Promise<TransactionSignature> {
|
|
3530
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3531
|
+
|
|
3532
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3533
|
+
userAccounts: [this.getUserAccount()],
|
|
3534
|
+
useMarketLastSlotCache: true,
|
|
3535
|
+
writableSpotMarketIndexes: [marketIndex],
|
|
3536
|
+
});
|
|
3537
|
+
|
|
3538
|
+
return await this.program.rpc.settleRevenueToInsuranceFund(marketIndex, {
|
|
3539
|
+
accounts: {
|
|
3540
|
+
state: await this.getStatePublicKey(),
|
|
3541
|
+
spotMarket: spotMarketAccount.pubkey,
|
|
3542
|
+
spotMarketVault: spotMarketAccount.vault,
|
|
3543
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3544
|
+
insuranceFundVault: spotMarketAccount.insuranceFund.vault,
|
|
3545
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3546
|
+
},
|
|
3547
|
+
remainingAccounts,
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
public async resolvePerpPnlDeficit(
|
|
3552
|
+
spotMarketIndex: number,
|
|
3553
|
+
perpMarketIndex: number
|
|
3554
|
+
): Promise<TransactionSignature> {
|
|
3555
|
+
const { txSig } = await this.txSender.send(
|
|
3556
|
+
wrapInTx(
|
|
3557
|
+
await this.getResolvePerpPnlDeficitIx(spotMarketIndex, perpMarketIndex)
|
|
3558
|
+
),
|
|
3559
|
+
[],
|
|
3560
|
+
this.opts
|
|
3561
|
+
);
|
|
3562
|
+
return txSig;
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
public async getResolvePerpPnlDeficitIx(
|
|
3566
|
+
spotMarketIndex: number,
|
|
3567
|
+
perpMarketIndex: number
|
|
3568
|
+
): Promise<TransactionInstruction> {
|
|
3569
|
+
const remainingAccounts = this.getRemainingAccounts({
|
|
3570
|
+
userAccounts: [this.getUserAccount()],
|
|
3571
|
+
useMarketLastSlotCache: true,
|
|
3572
|
+
writablePerpMarketIndexes: [perpMarketIndex],
|
|
3573
|
+
writableSpotMarketIndexes: [spotMarketIndex],
|
|
3574
|
+
});
|
|
3575
|
+
|
|
3576
|
+
const spotMarket = this.getSpotMarketAccount(spotMarketIndex);
|
|
3577
|
+
|
|
3578
|
+
return await this.program.instruction.resolvePerpPnlDeficit(
|
|
3579
|
+
spotMarketIndex,
|
|
3580
|
+
perpMarketIndex,
|
|
3581
|
+
{
|
|
3582
|
+
accounts: {
|
|
3583
|
+
state: await this.getStatePublicKey(),
|
|
3584
|
+
authority: this.wallet.publicKey,
|
|
3585
|
+
spotMarketVault: spotMarket.vault,
|
|
3586
|
+
insuranceFundVault: spotMarket.insuranceFund.vault,
|
|
3587
|
+
driftSigner: this.getSignerPublicKey(),
|
|
3588
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3589
|
+
},
|
|
3590
|
+
remainingAccounts: remainingAccounts,
|
|
3591
|
+
}
|
|
3592
|
+
);
|
|
3593
|
+
}
|
|
3594
|
+
}
|