@drift-labs/sdk 2.96.0-beta.9 → 2.97.0-beta.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 +3 -0
- package/VERSION +1 -1
- package/bun.lockb +0 -0
- package/lib/accounts/pollingDriftClientAccountSubscriber.d.ts +5 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +24 -1
- package/lib/accounts/types.d.ts +5 -0
- package/lib/accounts/types.js +7 -1
- package/lib/accounts/utils.d.ts +7 -0
- package/lib/accounts/utils.js +33 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +5 -4
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +24 -1
- package/lib/config.d.ts +6 -1
- package/lib/config.js +10 -1
- package/lib/constants/perpMarkets.js +33 -1
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/constants/txConstants.d.ts +1 -0
- package/lib/constants/txConstants.js +4 -0
- package/lib/driftClient.d.ts +36 -8
- package/lib/driftClient.js +166 -43
- package/lib/driftClientConfig.d.ts +3 -0
- package/lib/events/types.js +1 -5
- package/lib/idl/drift.json +170 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/orderParams.js +8 -8
- package/lib/orderSubscriber/OrderSubscriber.js +1 -6
- package/lib/tokenFaucet.js +2 -1
- package/lib/tx/baseTxSender.d.ts +0 -1
- package/lib/tx/baseTxSender.js +8 -26
- package/lib/tx/fastSingleTxSender.js +2 -2
- package/lib/tx/forwardOnlyTxSender.js +2 -2
- package/lib/tx/reportTransactionError.d.ts +20 -0
- package/lib/tx/reportTransactionError.js +103 -0
- package/lib/tx/retryTxSender.js +2 -2
- package/lib/tx/txHandler.js +10 -7
- package/lib/tx/whileValidTxSender.d.ts +4 -5
- package/lib/tx/whileValidTxSender.js +16 -17
- package/lib/types.d.ts +22 -1
- package/lib/types.js +6 -1
- package/lib/util/TransactionConfirmationManager.d.ts +16 -0
- package/lib/util/TransactionConfirmationManager.js +174 -0
- package/package.json +4 -3
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +41 -5
- package/src/accounts/types.ts +6 -0
- package/src/accounts/utils.ts +42 -0
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +40 -5
- package/src/config.ts +17 -1
- package/src/constants/perpMarkets.ts +35 -1
- package/src/constants/spotMarkets.ts +11 -0
- package/src/constants/txConstants.ts +1 -0
- package/src/driftClient.ts +346 -53
- package/src/driftClientConfig.ts +3 -0
- package/src/events/types.ts +1 -5
- package/src/idl/drift.json +170 -2
- package/src/index.ts +1 -0
- package/src/orderParams.ts +20 -12
- package/src/orderSubscriber/OrderSubscriber.ts +2 -5
- package/src/tokenFaucet.ts +2 -2
- package/src/tx/baseTxSender.ts +10 -32
- package/src/tx/fastSingleTxSender.ts +2 -2
- package/src/tx/forwardOnlyTxSender.ts +2 -2
- package/src/tx/reportTransactionError.ts +159 -0
- package/src/tx/retryTxSender.ts +2 -2
- package/src/tx/txHandler.ts +8 -2
- package/src/tx/whileValidTxSender.ts +18 -27
- package/src/types.ts +31 -1
- package/src/util/TransactionConfirmationManager.ts +292 -0
- package/tests/ci/verifyConstants.ts +13 -0
- package/tests/tx/TransactionConfirmationManager.test.ts +305 -0
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import { TxSigAndSlot } from './types';
|
|
1
|
+
import { ConfirmationStrategy, TxSigAndSlot } from './types';
|
|
2
2
|
import {
|
|
3
|
-
Commitment,
|
|
4
3
|
ConfirmOptions,
|
|
5
4
|
Connection,
|
|
5
|
+
SendTransactionError,
|
|
6
6
|
Signer,
|
|
7
7
|
Transaction,
|
|
8
8
|
VersionedTransaction,
|
|
9
9
|
} from '@solana/web3.js';
|
|
10
|
-
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
11
10
|
import { BaseTxSender } from './baseTxSender';
|
|
12
11
|
import bs58 from 'bs58';
|
|
13
12
|
import { TxHandler } from './txHandler';
|
|
14
13
|
import { IWallet } from '../types';
|
|
14
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
15
15
|
|
|
16
16
|
const DEFAULT_RETRY = 2000;
|
|
17
17
|
|
|
18
|
-
const VALID_BLOCK_HEIGHT_OFFSET = -150; // This is a bit of weirdness but the lastValidBlockHeight value returned from connection.getLatestBlockhash is always 300 blocks ahead of the current block, even though the transaction actually expires after 150 blocks. This accounts for that so that we can at least accuractely estimate the transaction expiry.
|
|
19
|
-
|
|
20
18
|
type ResolveReference = {
|
|
21
19
|
resolve?: () => void;
|
|
22
20
|
};
|
|
@@ -33,7 +31,6 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
33
31
|
string,
|
|
34
32
|
{ blockhash: string; lastValidBlockHeight: number }
|
|
35
33
|
>();
|
|
36
|
-
blockhashCommitment: Commitment;
|
|
37
34
|
|
|
38
35
|
useBlockHeightOffset = true;
|
|
39
36
|
|
|
@@ -62,11 +59,11 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
62
59
|
public constructor({
|
|
63
60
|
connection,
|
|
64
61
|
wallet,
|
|
65
|
-
opts = { ...
|
|
62
|
+
opts = { ...DEFAULT_CONFIRMATION_OPTS, maxRetries: 0 },
|
|
66
63
|
retrySleep = DEFAULT_RETRY,
|
|
67
64
|
additionalConnections = new Array<Connection>(),
|
|
65
|
+
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
68
66
|
additionalTxSenderCallbacks = [],
|
|
69
|
-
blockhashCommitment = 'finalized',
|
|
70
67
|
txHandler,
|
|
71
68
|
trackTxLandRate,
|
|
72
69
|
txLandRateLookbackWindowMinutes,
|
|
@@ -78,7 +75,7 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
78
75
|
retrySleep?: number;
|
|
79
76
|
additionalConnections?;
|
|
80
77
|
additionalTxSenderCallbacks?: ((base58EncodedTx: string) => void)[];
|
|
81
|
-
|
|
78
|
+
confirmationStrategy?: ConfirmationStrategy;
|
|
82
79
|
txHandler?: TxHandler;
|
|
83
80
|
trackTxLandRate?: boolean;
|
|
84
81
|
txLandRateLookbackWindowMinutes?: number;
|
|
@@ -93,10 +90,10 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
93
90
|
txHandler,
|
|
94
91
|
trackTxLandRate,
|
|
95
92
|
txLandRateLookbackWindowMinutes,
|
|
93
|
+
confirmationStrategy,
|
|
96
94
|
landRateToFeeFunc,
|
|
97
95
|
});
|
|
98
96
|
this.retrySleep = retrySleep;
|
|
99
|
-
this.blockhashCommitment = blockhashCommitment;
|
|
100
97
|
|
|
101
98
|
this.checkAndSetUseBlockHeightOffset();
|
|
102
99
|
}
|
|
@@ -139,7 +136,7 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
139
136
|
|
|
140
137
|
// handle subclass-specific side effects
|
|
141
138
|
const txSig = bs58.encode(
|
|
142
|
-
signedTx
|
|
139
|
+
signedTx?.signature || signedTx.signatures[0]?.signature
|
|
143
140
|
);
|
|
144
141
|
this.untilValid.set(txSig, latestBlockhash);
|
|
145
142
|
|
|
@@ -234,27 +231,21 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
234
231
|
|
|
235
232
|
let slot: number;
|
|
236
233
|
try {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
const result = await this.connection.confirmTransaction(
|
|
240
|
-
{
|
|
241
|
-
signature: txid,
|
|
242
|
-
blockhash,
|
|
243
|
-
lastValidBlockHeight: this.useBlockHeightOffset
|
|
244
|
-
? lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET
|
|
245
|
-
: lastValidBlockHeight,
|
|
246
|
-
},
|
|
247
|
-
opts?.commitment
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
if (!result) {
|
|
251
|
-
throw new Error(`Couldn't get signature status for txid: ${txid}`);
|
|
252
|
-
}
|
|
234
|
+
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
253
235
|
|
|
254
236
|
this.txSigCache?.set(txid, true);
|
|
255
237
|
|
|
256
238
|
await this.checkConfirmationResultForError(txid, result.value);
|
|
257
239
|
|
|
240
|
+
if (result?.value?.err) {
|
|
241
|
+
// Fallback error handling if there's a problem reporting the error in checkConfirmationResultForError
|
|
242
|
+
throw new SendTransactionError({
|
|
243
|
+
action: 'send',
|
|
244
|
+
signature: txid,
|
|
245
|
+
transactionMessage: `Transaction Failed`,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
258
249
|
slot = result.context.slot;
|
|
259
250
|
// eslint-disable-next-line no-useless-catch
|
|
260
251
|
} catch (e) {
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Keypair,
|
|
3
|
+
PublicKey,
|
|
4
|
+
Transaction,
|
|
5
|
+
VersionedTransaction,
|
|
6
|
+
} from '@solana/web3.js';
|
|
2
7
|
import { BN, ZERO } from '.';
|
|
3
8
|
|
|
4
9
|
// Utility type which lets you denote record with values of type A mapped to a record with the same keys but values of type B
|
|
@@ -1051,6 +1056,24 @@ export const DefaultOrderParams: OrderParams = {
|
|
|
1051
1056
|
auctionEndPrice: null,
|
|
1052
1057
|
};
|
|
1053
1058
|
|
|
1059
|
+
export type SwiftServerMessage = {
|
|
1060
|
+
slot: BN;
|
|
1061
|
+
swiftOrderSignature: Uint8Array;
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
export type SwiftOrderParamsMessage = {
|
|
1065
|
+
swiftOrderParams: OptionalOrderParams;
|
|
1066
|
+
expectedOrderId: number;
|
|
1067
|
+
subAccountId: number;
|
|
1068
|
+
takeProfitOrderParams: SwiftTriggerOrderParams | null;
|
|
1069
|
+
stopLossOrderParams: SwiftTriggerOrderParams | null;
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
export type SwiftTriggerOrderParams = {
|
|
1073
|
+
triggerPrice: BN;
|
|
1074
|
+
baseAssetAmount: BN;
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1054
1077
|
export type MakerInfo = {
|
|
1055
1078
|
maker: PublicKey;
|
|
1056
1079
|
makerStats: PublicKey;
|
|
@@ -1070,6 +1093,11 @@ export type ReferrerInfo = {
|
|
|
1070
1093
|
referrerStats: PublicKey;
|
|
1071
1094
|
};
|
|
1072
1095
|
|
|
1096
|
+
export enum PlaceAndTakeOrderSuccessCondition {
|
|
1097
|
+
PartialFill = 1,
|
|
1098
|
+
FullFill = 2,
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1073
1101
|
type ExactType<T> = Pick<T, keyof T>;
|
|
1074
1102
|
|
|
1075
1103
|
export type BaseTxParams = ExactType<{
|
|
@@ -1097,6 +1125,7 @@ export interface IWallet {
|
|
|
1097
1125
|
signTransaction(tx: Transaction): Promise<Transaction>;
|
|
1098
1126
|
signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;
|
|
1099
1127
|
publicKey: PublicKey;
|
|
1128
|
+
payer?: Keypair;
|
|
1100
1129
|
}
|
|
1101
1130
|
export interface IVersionedWallet {
|
|
1102
1131
|
signVersionedTransaction(
|
|
@@ -1106,6 +1135,7 @@ export interface IVersionedWallet {
|
|
|
1106
1135
|
txs: VersionedTransaction[]
|
|
1107
1136
|
): Promise<VersionedTransaction[]>;
|
|
1108
1137
|
publicKey: PublicKey;
|
|
1138
|
+
payer?: Keypair;
|
|
1109
1139
|
}
|
|
1110
1140
|
|
|
1111
1141
|
export type FeeStructure = {
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientSubscriptionId,
|
|
3
|
+
Connection,
|
|
4
|
+
Context,
|
|
5
|
+
RpcResponseAndContext,
|
|
6
|
+
SignatureResult,
|
|
7
|
+
SignatureStatus,
|
|
8
|
+
TransactionConfirmationStatus,
|
|
9
|
+
} from '@solana/web3.js';
|
|
10
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
11
|
+
import { TxSendError } from '..';
|
|
12
|
+
import { NOT_CONFIRMED_ERROR_CODE } from '../constants/txConstants';
|
|
13
|
+
import {
|
|
14
|
+
getTransactionErrorFromTxSig,
|
|
15
|
+
throwTransactionError,
|
|
16
|
+
} from '../tx/reportTransactionError';
|
|
17
|
+
import { promiseTimeout } from './promiseTimeout';
|
|
18
|
+
|
|
19
|
+
type ResolveReference = {
|
|
20
|
+
resolve?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const confirmationStatusValues: Record<TransactionConfirmationStatus, number> =
|
|
24
|
+
{
|
|
25
|
+
processed: 0,
|
|
26
|
+
confirmed: 1,
|
|
27
|
+
finalized: 2,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
interface TransactionConfirmationRequest {
|
|
31
|
+
txSig: string;
|
|
32
|
+
desiredConfirmationStatus: TransactionConfirmationStatus;
|
|
33
|
+
timeout: number;
|
|
34
|
+
pollInterval: number;
|
|
35
|
+
searchTransactionHistory: boolean;
|
|
36
|
+
startTime: number;
|
|
37
|
+
resolve: (status: SignatureStatus) => void;
|
|
38
|
+
reject: (error: Error) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Class to await for transaction confirmations in an optimised manner. It tracks a shared list of all pending transactions and fetches them in bulk in a shared RPC request whenever they have an "overlapping" polling interval. E.g. tx1 with an interval of 200ms and tx2 with an interval of 300ms (if sent at the same time) will be fetched together at at 600ms, 1200ms, 1800ms, etc.
|
|
43
|
+
*/
|
|
44
|
+
export class TransactionConfirmationManager {
|
|
45
|
+
private connection: Connection;
|
|
46
|
+
private pendingConfirmations: Map<string, TransactionConfirmationRequest> =
|
|
47
|
+
new Map();
|
|
48
|
+
private intervalId: NodeJS.Timeout | null = null;
|
|
49
|
+
|
|
50
|
+
constructor(connection: Connection) {
|
|
51
|
+
this.connection = connection;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async confirmTransactionWebSocket(
|
|
55
|
+
txSig: string,
|
|
56
|
+
timeout = 30000,
|
|
57
|
+
desiredConfirmationStatus = DEFAULT_CONFIRMATION_OPTS.commitment as TransactionConfirmationStatus
|
|
58
|
+
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
const subscriptionCommitment =
|
|
61
|
+
desiredConfirmationStatus || DEFAULT_CONFIRMATION_OPTS.commitment;
|
|
62
|
+
|
|
63
|
+
let response: RpcResponseAndContext<SignatureResult> | null = null;
|
|
64
|
+
|
|
65
|
+
let subscriptionId: ClientSubscriptionId;
|
|
66
|
+
|
|
67
|
+
const confirmationPromise = new Promise((resolve, reject) => {
|
|
68
|
+
try {
|
|
69
|
+
subscriptionId = this.connection.onSignature(
|
|
70
|
+
txSig,
|
|
71
|
+
(result: SignatureResult, context: Context) => {
|
|
72
|
+
response = {
|
|
73
|
+
context,
|
|
74
|
+
value: result,
|
|
75
|
+
};
|
|
76
|
+
resolve(null);
|
|
77
|
+
},
|
|
78
|
+
subscriptionCommitment
|
|
79
|
+
);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
reject(err);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// We do a one-shot confirmation check just in case the transaction is ALREADY confirmed when we create the websocket confirmation .. We want to run this concurrently with the onSignature subscription. If this returns true then we can return early as the transaction has already been confirmed.
|
|
86
|
+
const oneShotConfirmationPromise = this.connection.getSignatureStatuses([
|
|
87
|
+
txSig,
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
const resolveReference: ResolveReference = {};
|
|
91
|
+
|
|
92
|
+
// This is the promise we are waiting on to resolve the overall confirmation. It will resolve the faster of a positive oneShot confirmation, or the websocket confirmation, or the timeout.
|
|
93
|
+
const overallWaitingForConfirmationPromise = new Promise<void>(
|
|
94
|
+
(resolve) => {
|
|
95
|
+
resolveReference.resolve = resolve;
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Await for the one shot confirmation and resolve the waiting promise if we get a positive confirmation result
|
|
100
|
+
oneShotConfirmationPromise.then(
|
|
101
|
+
async (oneShotResponse) => {
|
|
102
|
+
if (!oneShotResponse || !oneShotResponse?.value?.[0]) return;
|
|
103
|
+
|
|
104
|
+
const resultValue = oneShotResponse.value[0];
|
|
105
|
+
|
|
106
|
+
if (resultValue.err) {
|
|
107
|
+
await throwTransactionError(txSig, this.connection);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
this.checkStatusMatchesDesiredConfirmationStatus(
|
|
112
|
+
resultValue,
|
|
113
|
+
desiredConfirmationStatus
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
response = {
|
|
117
|
+
context: oneShotResponse.context,
|
|
118
|
+
value: oneShotResponse.value[0],
|
|
119
|
+
};
|
|
120
|
+
resolveReference.resolve?.();
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
(onRejected) => {
|
|
124
|
+
throw onRejected;
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Await for the websocket confirmation with the configured timeout
|
|
129
|
+
promiseTimeout(confirmationPromise, timeout).then(
|
|
130
|
+
() => {
|
|
131
|
+
resolveReference.resolve?.();
|
|
132
|
+
},
|
|
133
|
+
(onRejected) => {
|
|
134
|
+
throw onRejected;
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await overallWaitingForConfirmationPromise;
|
|
140
|
+
} finally {
|
|
141
|
+
if (subscriptionId !== undefined) {
|
|
142
|
+
this.connection.removeSignatureListener(subscriptionId);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const duration = (Date.now() - start) / 1000;
|
|
147
|
+
|
|
148
|
+
if (response === null) {
|
|
149
|
+
throw new TxSendError(
|
|
150
|
+
`Transaction was not confirmed in ${duration.toFixed(
|
|
151
|
+
2
|
|
152
|
+
)} seconds. It is unknown if it succeeded or failed. Check signature ${txSig} using the Solana Explorer or CLI tools.`,
|
|
153
|
+
NOT_CONFIRMED_ERROR_CODE
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async confirmTransactionPolling(
|
|
161
|
+
txSig: string,
|
|
162
|
+
desiredConfirmationStatus = DEFAULT_CONFIRMATION_OPTS.commitment as TransactionConfirmationStatus,
|
|
163
|
+
timeout = 30000,
|
|
164
|
+
pollInterval = 1000,
|
|
165
|
+
searchTransactionHistory = false
|
|
166
|
+
): Promise<SignatureStatus> {
|
|
167
|
+
// Interval must be > 400ms and a multiple of 100ms
|
|
168
|
+
if (pollInterval < 400 || pollInterval % 100 !== 0) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'Transaction confirmation polling interval must be at least 400ms and a multiple of 100ms'
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
this.pendingConfirmations.set(txSig, {
|
|
176
|
+
txSig,
|
|
177
|
+
desiredConfirmationStatus,
|
|
178
|
+
timeout,
|
|
179
|
+
pollInterval,
|
|
180
|
+
searchTransactionHistory,
|
|
181
|
+
startTime: Date.now(),
|
|
182
|
+
resolve,
|
|
183
|
+
reject,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!this.intervalId) {
|
|
187
|
+
this.startConfirmationLoop();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private startConfirmationLoop() {
|
|
193
|
+
this.intervalId = setInterval(() => this.checkPendingConfirmations(), 100);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async checkPendingConfirmations() {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
const transactionsToCheck: TransactionConfirmationRequest[] = [];
|
|
199
|
+
|
|
200
|
+
for (const [txSig, request] of this.pendingConfirmations.entries()) {
|
|
201
|
+
if (now - request.startTime >= request.timeout) {
|
|
202
|
+
request.reject(
|
|
203
|
+
new Error(
|
|
204
|
+
`Transaction confirmation timeout after ${request.timeout}ms`
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
this.pendingConfirmations.delete(txSig);
|
|
208
|
+
} else if ((now - request.startTime) % request.pollInterval < 100) {
|
|
209
|
+
transactionsToCheck.push(request);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (transactionsToCheck.length > 0) {
|
|
214
|
+
await this.checkTransactionStatuses(transactionsToCheck);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.pendingConfirmations.size === 0 && this.intervalId) {
|
|
218
|
+
clearInterval(this.intervalId);
|
|
219
|
+
this.intervalId = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private checkStatusMatchesDesiredConfirmationStatus(
|
|
224
|
+
status: SignatureStatus,
|
|
225
|
+
desiredConfirmationStatus: TransactionConfirmationStatus
|
|
226
|
+
): boolean {
|
|
227
|
+
if (
|
|
228
|
+
status.confirmationStatus &&
|
|
229
|
+
confirmationStatusValues[status.confirmationStatus] >=
|
|
230
|
+
confirmationStatusValues[desiredConfirmationStatus]
|
|
231
|
+
) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async checkTransactionStatuses(
|
|
239
|
+
requests: TransactionConfirmationRequest[]
|
|
240
|
+
) {
|
|
241
|
+
const txSigs = requests.map((request) => request.txSig);
|
|
242
|
+
const { value: statuses } = await this.connection.getSignatureStatuses(
|
|
243
|
+
txSigs,
|
|
244
|
+
{
|
|
245
|
+
searchTransactionHistory: requests.some(
|
|
246
|
+
(req) => req.searchTransactionHistory
|
|
247
|
+
),
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (!statuses || statuses.length !== txSigs.length) {
|
|
252
|
+
throw new Error('Failed to get signature statuses');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
for (let i = 0; i < statuses.length; i++) {
|
|
256
|
+
const status = statuses[i];
|
|
257
|
+
const request = requests[i];
|
|
258
|
+
|
|
259
|
+
if (status === null) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (status.err) {
|
|
264
|
+
this.pendingConfirmations.delete(request.txSig);
|
|
265
|
+
request.reject(
|
|
266
|
+
await getTransactionErrorFromTxSig(request.txSig, this.connection)
|
|
267
|
+
);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (
|
|
272
|
+
confirmationStatusValues[status.confirmationStatus] === undefined ||
|
|
273
|
+
confirmationStatusValues[request.desiredConfirmationStatus] ===
|
|
274
|
+
undefined
|
|
275
|
+
) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Invalid confirmation status when awaiting confirmation: ${status.confirmationStatus}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (
|
|
282
|
+
this.checkStatusMatchesDesiredConfirmationStatus(
|
|
283
|
+
status,
|
|
284
|
+
request.desiredConfirmationStatus
|
|
285
|
+
)
|
|
286
|
+
) {
|
|
287
|
+
request.resolve(status);
|
|
288
|
+
this.pendingConfirmations.delete(request.txSig);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
MainnetPerpMarkets,
|
|
7
7
|
BulkAccountLoader,
|
|
8
8
|
getVariant,
|
|
9
|
+
isOneOfVariant,
|
|
9
10
|
} from '../../src';
|
|
10
11
|
import { Connection, Keypair } from '@solana/web3.js';
|
|
11
12
|
import { Wallet } from '@coral-xyz/anchor';
|
|
@@ -128,6 +129,12 @@ describe('Verify Constants', function () {
|
|
|
128
129
|
market.marketIndex
|
|
129
130
|
} oracle ${market.oracle.toBase58()}`
|
|
130
131
|
);
|
|
132
|
+
|
|
133
|
+
if (isOneOfVariant(market.oracleSource, ['pythPull', 'pyth1KPull', 'pyth1MPull', 'pythStableCoinPull'])) {
|
|
134
|
+
if (!correspondingConfigMarket.pythFeedId) {
|
|
135
|
+
assert(false, `spot market ${market.marketIndex} missing feed id`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
const perpMarkets = mainnetDriftClient.getPerpMarketAccounts();
|
|
@@ -177,6 +184,12 @@ describe('Verify Constants', function () {
|
|
|
177
184
|
market.marketIndex
|
|
178
185
|
} oracle ${market.amm.oracle.toBase58()}`
|
|
179
186
|
);
|
|
187
|
+
|
|
188
|
+
if (isOneOfVariant(market.amm.oracleSource, ['pythPull', 'pyth1KPull', 'pyth1MPull', 'pythStableCoinPull'])) {
|
|
189
|
+
if (!correspondingConfigMarket.pythFeedId) {
|
|
190
|
+
assert(false, `perp market ${market.marketIndex} missing feed id`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
180
193
|
}
|
|
181
194
|
});
|
|
182
195
|
|