@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
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.96.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -554,7 +554,7 @@
|
|
|
554
554
|
}
|
|
555
555
|
},
|
|
556
556
|
{
|
|
557
|
-
"name": "
|
|
557
|
+
"name": "successCondition",
|
|
558
558
|
"type": {
|
|
559
559
|
"option": "u32"
|
|
560
560
|
}
|
|
@@ -608,6 +608,60 @@
|
|
|
608
608
|
}
|
|
609
609
|
]
|
|
610
610
|
},
|
|
611
|
+
{
|
|
612
|
+
"name": "placeSwiftTakerOrder",
|
|
613
|
+
"accounts": [
|
|
614
|
+
{
|
|
615
|
+
"name": "state",
|
|
616
|
+
"isMut": false,
|
|
617
|
+
"isSigner": false
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
"name": "user",
|
|
621
|
+
"isMut": true,
|
|
622
|
+
"isSigner": false
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
"name": "userStats",
|
|
626
|
+
"isMut": true,
|
|
627
|
+
"isSigner": false
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"name": "authority",
|
|
631
|
+
"isMut": false,
|
|
632
|
+
"isSigner": true
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
"name": "ixSysvar",
|
|
636
|
+
"isMut": false,
|
|
637
|
+
"isSigner": false,
|
|
638
|
+
"docs": [
|
|
639
|
+
"the supplied Sysvar could be anything else.",
|
|
640
|
+
"The Instruction Sysvar has not been implemented",
|
|
641
|
+
"in the Anchor framework yet, so this is the safe approach."
|
|
642
|
+
]
|
|
643
|
+
}
|
|
644
|
+
],
|
|
645
|
+
"args": [
|
|
646
|
+
{
|
|
647
|
+
"name": "swiftMessageBytes",
|
|
648
|
+
"type": "bytes"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
"name": "swiftOrderParamsMessageBytes",
|
|
652
|
+
"type": "bytes"
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"name": "swiftMessageSignature",
|
|
656
|
+
"type": {
|
|
657
|
+
"array": [
|
|
658
|
+
"u8",
|
|
659
|
+
64
|
|
660
|
+
]
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
]
|
|
664
|
+
},
|
|
611
665
|
{
|
|
612
666
|
"name": "placeSpotOrder",
|
|
613
667
|
"accounts": [
|
|
@@ -8050,6 +8104,81 @@
|
|
|
8050
8104
|
]
|
|
8051
8105
|
}
|
|
8052
8106
|
},
|
|
8107
|
+
{
|
|
8108
|
+
"name": "SwiftServerMessage",
|
|
8109
|
+
"type": {
|
|
8110
|
+
"kind": "struct",
|
|
8111
|
+
"fields": [
|
|
8112
|
+
{
|
|
8113
|
+
"name": "swiftOrderSignature",
|
|
8114
|
+
"type": {
|
|
8115
|
+
"array": [
|
|
8116
|
+
"u8",
|
|
8117
|
+
64
|
|
8118
|
+
]
|
|
8119
|
+
}
|
|
8120
|
+
},
|
|
8121
|
+
{
|
|
8122
|
+
"name": "slot",
|
|
8123
|
+
"type": "u64"
|
|
8124
|
+
}
|
|
8125
|
+
]
|
|
8126
|
+
}
|
|
8127
|
+
},
|
|
8128
|
+
{
|
|
8129
|
+
"name": "SwiftOrderParamsMessage",
|
|
8130
|
+
"type": {
|
|
8131
|
+
"kind": "struct",
|
|
8132
|
+
"fields": [
|
|
8133
|
+
{
|
|
8134
|
+
"name": "swiftOrderParams",
|
|
8135
|
+
"type": {
|
|
8136
|
+
"defined": "OrderParams"
|
|
8137
|
+
}
|
|
8138
|
+
},
|
|
8139
|
+
{
|
|
8140
|
+
"name": "expectedOrderId",
|
|
8141
|
+
"type": "i32"
|
|
8142
|
+
},
|
|
8143
|
+
{
|
|
8144
|
+
"name": "subAccountId",
|
|
8145
|
+
"type": "u16"
|
|
8146
|
+
},
|
|
8147
|
+
{
|
|
8148
|
+
"name": "takeProfitOrderParams",
|
|
8149
|
+
"type": {
|
|
8150
|
+
"option": {
|
|
8151
|
+
"defined": "SwiftTriggerOrderParams"
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
},
|
|
8155
|
+
{
|
|
8156
|
+
"name": "stopLossOrderParams",
|
|
8157
|
+
"type": {
|
|
8158
|
+
"option": {
|
|
8159
|
+
"defined": "SwiftTriggerOrderParams"
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
}
|
|
8163
|
+
]
|
|
8164
|
+
}
|
|
8165
|
+
},
|
|
8166
|
+
{
|
|
8167
|
+
"name": "SwiftTriggerOrderParams",
|
|
8168
|
+
"type": {
|
|
8169
|
+
"kind": "struct",
|
|
8170
|
+
"fields": [
|
|
8171
|
+
{
|
|
8172
|
+
"name": "triggerPrice",
|
|
8173
|
+
"type": "u64"
|
|
8174
|
+
},
|
|
8175
|
+
{
|
|
8176
|
+
"name": "baseAssetAmount",
|
|
8177
|
+
"type": "u64"
|
|
8178
|
+
}
|
|
8179
|
+
]
|
|
8180
|
+
}
|
|
8181
|
+
},
|
|
8053
8182
|
{
|
|
8054
8183
|
"name": "ModifyOrderParams",
|
|
8055
8184
|
"type": {
|
|
@@ -10122,6 +10251,20 @@
|
|
|
10122
10251
|
]
|
|
10123
10252
|
}
|
|
10124
10253
|
},
|
|
10254
|
+
{
|
|
10255
|
+
"name": "PlaceAndTakeOrderSuccessCondition",
|
|
10256
|
+
"type": {
|
|
10257
|
+
"kind": "enum",
|
|
10258
|
+
"variants": [
|
|
10259
|
+
{
|
|
10260
|
+
"name": "PartialFill"
|
|
10261
|
+
},
|
|
10262
|
+
{
|
|
10263
|
+
"name": "FullFill"
|
|
10264
|
+
}
|
|
10265
|
+
]
|
|
10266
|
+
}
|
|
10267
|
+
},
|
|
10125
10268
|
{
|
|
10126
10269
|
"name": "PerpOperation",
|
|
10127
10270
|
"type": {
|
|
@@ -12958,6 +13101,31 @@
|
|
|
12958
13101
|
"code": 6284,
|
|
12959
13102
|
"name": "InvalidPredictionMarketOrder",
|
|
12960
13103
|
"msg": "Invalid prediction market order"
|
|
13104
|
+
},
|
|
13105
|
+
{
|
|
13106
|
+
"code": 6285,
|
|
13107
|
+
"name": "InvalidVerificationIxIndex",
|
|
13108
|
+
"msg": "Ed25519 Ix must be before place and make swift order ix"
|
|
13109
|
+
},
|
|
13110
|
+
{
|
|
13111
|
+
"code": 6286,
|
|
13112
|
+
"name": "SigVerificationFailed",
|
|
13113
|
+
"msg": "Swift message verificaiton failed"
|
|
13114
|
+
},
|
|
13115
|
+
{
|
|
13116
|
+
"code": 6287,
|
|
13117
|
+
"name": "MismatchedSwiftOrderParamsMarketIndex",
|
|
13118
|
+
"msg": "Market index mismatched b/w taker and maker swift order params"
|
|
13119
|
+
},
|
|
13120
|
+
{
|
|
13121
|
+
"code": 6288,
|
|
13122
|
+
"name": "InvalidSwiftOrderParam",
|
|
13123
|
+
"msg": "Swift only available for market/oracle perp orders"
|
|
13124
|
+
},
|
|
13125
|
+
{
|
|
13126
|
+
"code": 6289,
|
|
13127
|
+
"name": "PlaceAndTakeOrderSuccessConditionFailed",
|
|
13128
|
+
"msg": "Place and take order success condition failed"
|
|
12961
13129
|
}
|
|
12962
13130
|
],
|
|
12963
13131
|
"metadata": {
|
package/src/index.ts
CHANGED
|
@@ -108,6 +108,7 @@ export * from './memcmp';
|
|
|
108
108
|
export * from './decode/user';
|
|
109
109
|
export * from './blockhashSubscriber';
|
|
110
110
|
export * from './util/chainClock';
|
|
111
|
+
export * from './util/TransactionConfirmationManager';
|
|
111
112
|
export * from './clock/clockSubscriber';
|
|
112
113
|
|
|
113
114
|
export { BN, PublicKey, pyth };
|
package/src/orderParams.ts
CHANGED
|
@@ -10,9 +10,11 @@ import { BN } from '@coral-xyz/anchor';
|
|
|
10
10
|
export function getLimitOrderParams(
|
|
11
11
|
params: Omit<OptionalOrderParams, 'orderType'> & { price: BN }
|
|
12
12
|
): OptionalOrderParams {
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return getOrderParams(
|
|
14
|
+
Object.assign({}, params, {
|
|
15
|
+
orderType: OrderType.LIMIT,
|
|
16
|
+
})
|
|
17
|
+
);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function getTriggerMarketOrderParams(
|
|
@@ -21,9 +23,11 @@ export function getTriggerMarketOrderParams(
|
|
|
21
23
|
triggerPrice: BN;
|
|
22
24
|
}
|
|
23
25
|
): OptionalOrderParams {
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
return getOrderParams(
|
|
27
|
+
Object.assign({}, params, {
|
|
28
|
+
orderType: OrderType.TRIGGER_MARKET,
|
|
29
|
+
})
|
|
30
|
+
);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export function getTriggerLimitOrderParams(
|
|
@@ -33,17 +37,21 @@ export function getTriggerLimitOrderParams(
|
|
|
33
37
|
price: BN;
|
|
34
38
|
}
|
|
35
39
|
): OptionalOrderParams {
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
return getOrderParams(
|
|
41
|
+
Object.assign({}, params, {
|
|
42
|
+
orderType: OrderType.TRIGGER_LIMIT,
|
|
43
|
+
})
|
|
44
|
+
);
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
export function getMarketOrderParams(
|
|
42
48
|
params: Omit<OptionalOrderParams, 'orderType'>
|
|
43
49
|
): OptionalOrderParams {
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
return getOrderParams(
|
|
51
|
+
Object.assign({}, params, {
|
|
52
|
+
orderType: OrderType.MARKET,
|
|
53
|
+
})
|
|
54
|
+
);
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
/**
|
|
@@ -209,11 +209,8 @@ export class OrderSubscriber {
|
|
|
209
209
|
dataType
|
|
210
210
|
);
|
|
211
211
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
} else {
|
|
215
|
-
this.usersAccounts.delete(key);
|
|
216
|
-
}
|
|
212
|
+
|
|
213
|
+
this.usersAccounts.set(key, { slot, userAccount });
|
|
217
214
|
}
|
|
218
215
|
}
|
|
219
216
|
|
package/src/tokenFaucet.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
TransactionInstruction,
|
|
17
17
|
TransactionSignature,
|
|
18
18
|
} from '@solana/web3.js';
|
|
19
|
-
import { BN } from '.';
|
|
19
|
+
import { BN, DEFAULT_CONFIRMATION_OPTS } from '.';
|
|
20
20
|
import tokenFaucet from './idl/token_faucet.json';
|
|
21
21
|
import { IWallet } from './types';
|
|
22
22
|
import { BankrunContextWrapper } from './bankrun/bankrunConnection';
|
|
@@ -41,7 +41,7 @@ export class TokenFaucet {
|
|
|
41
41
|
this.connection = connection;
|
|
42
42
|
this.context = context;
|
|
43
43
|
this.wallet = wallet;
|
|
44
|
-
this.opts = opts ||
|
|
44
|
+
this.opts = opts || DEFAULT_CONFIRMATION_OPTS;
|
|
45
45
|
// @ts-ignore
|
|
46
46
|
const provider = new AnchorProvider(
|
|
47
47
|
context ? context.connection.toConnection() : this.connection,
|
package/src/tx/baseTxSender.ts
CHANGED
|
@@ -15,22 +15,22 @@ import {
|
|
|
15
15
|
TransactionSignature,
|
|
16
16
|
Connection,
|
|
17
17
|
VersionedTransaction,
|
|
18
|
-
SendTransactionError,
|
|
19
18
|
TransactionInstruction,
|
|
20
19
|
AddressLookupTableAccount,
|
|
21
20
|
BlockhashWithExpiryBlockHeight,
|
|
22
21
|
} from '@solana/web3.js';
|
|
23
|
-
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
24
22
|
import assert from 'assert';
|
|
25
23
|
import bs58 from 'bs58';
|
|
26
24
|
import { TxHandler } from './txHandler';
|
|
27
25
|
import { IWallet } from '../types';
|
|
28
26
|
import NodeCache from 'node-cache';
|
|
27
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
28
|
+
import { NOT_CONFIRMED_ERROR_CODE } from '../constants/txConstants';
|
|
29
|
+
import { throwTransactionError } from './reportTransactionError';
|
|
29
30
|
|
|
30
31
|
const BASELINE_TX_LAND_RATE = 0.9;
|
|
31
32
|
const DEFAULT_TIMEOUT = 35000;
|
|
32
33
|
const DEFAULT_TX_LAND_RATE_LOOKBACK_WINDOW_MINUTES = 10;
|
|
33
|
-
const NOT_CONFIRMED_ERROR_CODE = -1001;
|
|
34
34
|
|
|
35
35
|
export abstract class BaseTxSender implements TxSender {
|
|
36
36
|
connection: Connection;
|
|
@@ -54,7 +54,7 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
54
54
|
public constructor({
|
|
55
55
|
connection,
|
|
56
56
|
wallet,
|
|
57
|
-
opts =
|
|
57
|
+
opts = DEFAULT_CONFIRMATION_OPTS,
|
|
58
58
|
timeout = DEFAULT_TIMEOUT,
|
|
59
59
|
additionalConnections = new Array<Connection>(),
|
|
60
60
|
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
@@ -406,40 +406,18 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
406
406
|
public async checkConfirmationResultForError(
|
|
407
407
|
txSig: string,
|
|
408
408
|
result: SignatureResult
|
|
409
|
-
) {
|
|
409
|
+
): Promise<void> {
|
|
410
410
|
if (result.err) {
|
|
411
|
-
await
|
|
411
|
+
await throwTransactionError(
|
|
412
|
+
txSig,
|
|
413
|
+
this.connection,
|
|
414
|
+
this.opts?.commitment
|
|
415
|
+
);
|
|
412
416
|
}
|
|
413
417
|
|
|
414
418
|
return;
|
|
415
419
|
}
|
|
416
420
|
|
|
417
|
-
public async reportTransactionError(txSig: string) {
|
|
418
|
-
const transactionResult = await this.connection.getTransaction(txSig, {
|
|
419
|
-
maxSupportedTransactionVersion: 0,
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
if (!transactionResult?.meta?.err) {
|
|
423
|
-
return undefined;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const logs = transactionResult.meta.logMessages;
|
|
427
|
-
|
|
428
|
-
const lastLog = logs[logs.length - 1];
|
|
429
|
-
|
|
430
|
-
const friendlyMessage = lastLog?.match(/(failed:) (.+)/)?.[2];
|
|
431
|
-
|
|
432
|
-
// @ts-ignore
|
|
433
|
-
throw new SendTransactionError({
|
|
434
|
-
action: 'send',
|
|
435
|
-
signature: txSig,
|
|
436
|
-
transactionMessage: `Transaction Failed${
|
|
437
|
-
friendlyMessage ? `: ${friendlyMessage}` : ''
|
|
438
|
-
}`,
|
|
439
|
-
logs,
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
421
|
public getTxLandRate(): number {
|
|
444
422
|
if (!this.trackTxLandRate) {
|
|
445
423
|
console.warn(
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
Commitment,
|
|
7
7
|
BlockhashWithExpiryBlockHeight,
|
|
8
8
|
} from '@solana/web3.js';
|
|
9
|
-
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
10
9
|
import { BaseTxSender } from './baseTxSender';
|
|
11
10
|
import { TxHandler } from './txHandler';
|
|
12
11
|
import { IWallet } from '../types';
|
|
12
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
13
13
|
|
|
14
14
|
const DEFAULT_TIMEOUT = 35000;
|
|
15
15
|
const DEFAULT_BLOCKHASH_REFRESH = 10000;
|
|
@@ -31,7 +31,7 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
31
31
|
public constructor({
|
|
32
32
|
connection,
|
|
33
33
|
wallet,
|
|
34
|
-
opts = { ...
|
|
34
|
+
opts = { ...DEFAULT_CONFIRMATION_OPTS, maxRetries: 0 },
|
|
35
35
|
timeout = DEFAULT_TIMEOUT,
|
|
36
36
|
blockhashRefreshInterval = DEFAULT_BLOCKHASH_REFRESH,
|
|
37
37
|
additionalConnections = new Array<Connection>(),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
2
1
|
import {
|
|
3
2
|
ConfirmOptions,
|
|
4
3
|
Connection,
|
|
@@ -9,6 +8,7 @@ import { BaseTxSender } from './baseTxSender';
|
|
|
9
8
|
import { ConfirmationStrategy, TxSigAndSlot } from './types';
|
|
10
9
|
import { TxHandler } from './txHandler';
|
|
11
10
|
import { IWallet } from '../types';
|
|
11
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
12
12
|
|
|
13
13
|
const DEFAULT_TIMEOUT = 35000;
|
|
14
14
|
const DEFAULT_RETRY = 5000;
|
|
@@ -29,7 +29,7 @@ export class ForwardOnlyTxSender extends BaseTxSender {
|
|
|
29
29
|
public constructor({
|
|
30
30
|
connection,
|
|
31
31
|
wallet,
|
|
32
|
-
opts = { ...
|
|
32
|
+
opts = { ...DEFAULT_CONFIRMATION_OPTS, maxRetries: 0 },
|
|
33
33
|
timeout = DEFAULT_TIMEOUT,
|
|
34
34
|
retrySleep = DEFAULT_RETRY,
|
|
35
35
|
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Commitment,
|
|
3
|
+
Connection,
|
|
4
|
+
Finality,
|
|
5
|
+
SendTransactionError,
|
|
6
|
+
VersionedTransactionResponse,
|
|
7
|
+
} from '@solana/web3.js';
|
|
8
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The new getTransaction method expects a Finality type instead of a Commitment type. The only options for Finality are 'confirmed' and 'finalized'.
|
|
12
|
+
* @param commitment
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
const commitmentToFinality = (commitment: Commitment): Finality => {
|
|
16
|
+
switch (commitment) {
|
|
17
|
+
case 'confirmed':
|
|
18
|
+
return 'confirmed';
|
|
19
|
+
case 'finalized':
|
|
20
|
+
return 'finalized';
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Invalid commitment when reporting transaction error. The commitment must be 'confirmed' or 'finalized' but was given '${commitment}'. If you're using this commitment for a specific reason, you may need to roll your own logic here.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getTransactionResult = async (
|
|
29
|
+
txSig: string,
|
|
30
|
+
connection: Connection,
|
|
31
|
+
commitment?: Commitment
|
|
32
|
+
): Promise<VersionedTransactionResponse> => {
|
|
33
|
+
const finality = commitmentToFinality(
|
|
34
|
+
commitment || connection.commitment || DEFAULT_CONFIRMATION_OPTS.commitment
|
|
35
|
+
);
|
|
36
|
+
return await connection.getTransaction(txSig, {
|
|
37
|
+
maxSupportedTransactionVersion: 0,
|
|
38
|
+
commitment: finality,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getTransactionResultWithRetry = async (
|
|
43
|
+
txSig: string,
|
|
44
|
+
connection: Connection,
|
|
45
|
+
commitment?: Commitment
|
|
46
|
+
): Promise<VersionedTransactionResponse> => {
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
|
|
49
|
+
const retryTimeout = 3_000; // Timeout after 3 seconds
|
|
50
|
+
const retryInterval = 800; // Retry with 800ms interval
|
|
51
|
+
const retryCount = 3; // Retry 3 times
|
|
52
|
+
|
|
53
|
+
let currentCount = 0;
|
|
54
|
+
let transactionResult = await getTransactionResult(
|
|
55
|
+
txSig,
|
|
56
|
+
connection,
|
|
57
|
+
commitment
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Retry 3 times or until timeout as long as we don't have a result yet
|
|
61
|
+
while (
|
|
62
|
+
!transactionResult &&
|
|
63
|
+
Date.now() - start < retryTimeout &&
|
|
64
|
+
currentCount < retryCount
|
|
65
|
+
) {
|
|
66
|
+
// Sleep for 1 second :: Do this first so that we don't run the first loop immediately after the initial fetch above
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, retryInterval));
|
|
68
|
+
|
|
69
|
+
transactionResult = await getTransactionResult(
|
|
70
|
+
txSig,
|
|
71
|
+
connection,
|
|
72
|
+
commitment
|
|
73
|
+
);
|
|
74
|
+
currentCount++;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return transactionResult;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* THROWS if there is an error
|
|
82
|
+
*
|
|
83
|
+
* Should only be used for a txSig that is confirmed has an error. There is a race-condition where sometimes the transaction is not instantly available to fetch after the confirmation has already failed with an error, so this method has retry logic which we don't want to do wastefully. This method will throw a generic error if it can't get the transaction result after a retry period.
|
|
84
|
+
* @param txSig
|
|
85
|
+
* @param connection
|
|
86
|
+
* @returns
|
|
87
|
+
*/
|
|
88
|
+
export const throwTransactionError = async (
|
|
89
|
+
txSig: string,
|
|
90
|
+
connection: Connection,
|
|
91
|
+
commitment?: Commitment
|
|
92
|
+
): Promise<void> => {
|
|
93
|
+
const err = await getTransactionErrorFromTxSig(txSig, connection, commitment);
|
|
94
|
+
|
|
95
|
+
if (err) {
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* RETURNS an error if there is one
|
|
104
|
+
*
|
|
105
|
+
* Should only be used for a txSig that is confirmed has an error. There is a race-condition where sometimes the transaction is not instantly available to fetch after the confirmation has already failed with an error, so this method has retry logic which we don't want to do wastefully. This method will throw a generic error if it can't get the transaction result after a retry period.
|
|
106
|
+
* @param txSig
|
|
107
|
+
* @param connection
|
|
108
|
+
* @returns
|
|
109
|
+
*/
|
|
110
|
+
export const getTransactionErrorFromTxSig = async (
|
|
111
|
+
txSig: string,
|
|
112
|
+
connection: Connection,
|
|
113
|
+
commitment?: Commitment
|
|
114
|
+
): Promise<SendTransactionError> => {
|
|
115
|
+
const transactionResult = await getTransactionResultWithRetry(
|
|
116
|
+
txSig,
|
|
117
|
+
connection,
|
|
118
|
+
commitment
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (!transactionResult) {
|
|
122
|
+
// Throw a generic error because we couldn't get the transaction result for the given txSig
|
|
123
|
+
return new SendTransactionError({
|
|
124
|
+
action: 'send',
|
|
125
|
+
signature: txSig,
|
|
126
|
+
transactionMessage: `Transaction Failed`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!transactionResult?.meta?.err) {
|
|
131
|
+
// Assume that the transaction was successful and we are here erroneously because we have a result with no error
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return getTransactionError(transactionResult);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const getTransactionError = (
|
|
139
|
+
transactionResult: VersionedTransactionResponse
|
|
140
|
+
): SendTransactionError => {
|
|
141
|
+
if (!transactionResult?.meta?.err) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const logs = transactionResult?.meta?.logMessages ?? ['No logs'];
|
|
146
|
+
|
|
147
|
+
const lastLog = logs[logs.length - 1];
|
|
148
|
+
|
|
149
|
+
const friendlyMessage = lastLog?.match(/(failed:) (.+)/)?.[2];
|
|
150
|
+
|
|
151
|
+
return new SendTransactionError({
|
|
152
|
+
action: 'send',
|
|
153
|
+
signature: transactionResult?.transaction?.signatures?.[0],
|
|
154
|
+
transactionMessage: `Transaction Failed${
|
|
155
|
+
friendlyMessage ? `: ${friendlyMessage}` : ''
|
|
156
|
+
}`,
|
|
157
|
+
logs,
|
|
158
|
+
});
|
|
159
|
+
};
|
package/src/tx/retryTxSender.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ConfirmationStrategy, TxSigAndSlot } from './types';
|
|
2
2
|
import { ConfirmOptions, Connection } from '@solana/web3.js';
|
|
3
|
-
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
4
3
|
import { BaseTxSender } from './baseTxSender';
|
|
5
4
|
import { TxHandler } from './txHandler';
|
|
6
5
|
import { IWallet } from '../types';
|
|
6
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
7
7
|
|
|
8
8
|
const DEFAULT_TIMEOUT = 35000;
|
|
9
9
|
const DEFAULT_RETRY = 2000;
|
|
@@ -24,7 +24,7 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
24
24
|
public constructor({
|
|
25
25
|
connection,
|
|
26
26
|
wallet,
|
|
27
|
-
opts = { ...
|
|
27
|
+
opts = { ...DEFAULT_CONFIRMATION_OPTS, maxRetries: 0 },
|
|
28
28
|
timeout = DEFAULT_TIMEOUT,
|
|
29
29
|
retrySleep = DEFAULT_RETRY,
|
|
30
30
|
additionalConnections = new Array<Connection>(),
|
package/src/tx/txHandler.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { CachedBlockhashFetcher } from './blockhashFetcher/cachedBlockhashFetche
|
|
|
29
29
|
import { BaseBlockhashFetcher } from './blockhashFetcher/baseBlockhashFetcher';
|
|
30
30
|
import { BlockhashFetcher } from './blockhashFetcher/types';
|
|
31
31
|
import { isVersionedTransaction } from './utils';
|
|
32
|
+
import { DEFAULT_CONFIRMATION_OPTS } from '../config';
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Explanation for SIGNATURE_BLOCK_AND_EXPIRY:
|
|
@@ -81,7 +82,8 @@ export class TxHandler {
|
|
|
81
82
|
private preSignedCb?: () => void;
|
|
82
83
|
private onSignedCb?: (txSigs: DriftClientMetricsEvents['txSigned']) => void;
|
|
83
84
|
|
|
84
|
-
private blockhashCommitment: Commitment =
|
|
85
|
+
private blockhashCommitment: Commitment =
|
|
86
|
+
DEFAULT_CONFIRMATION_OPTS.commitment;
|
|
85
87
|
private blockHashFetcher: BlockhashFetcher;
|
|
86
88
|
|
|
87
89
|
constructor(props: {
|
|
@@ -98,6 +100,11 @@ export class TxHandler {
|
|
|
98
100
|
this.connection = props.connection;
|
|
99
101
|
this.wallet = props.wallet;
|
|
100
102
|
this.confirmationOptions = props.confirmationOptions;
|
|
103
|
+
this.blockhashCommitment =
|
|
104
|
+
props.confirmationOptions?.preflightCommitment ??
|
|
105
|
+
props?.connection?.commitment ??
|
|
106
|
+
this.blockhashCommitment ??
|
|
107
|
+
'confirmed';
|
|
101
108
|
|
|
102
109
|
this.blockHashFetcher = props?.config?.blockhashCachingEnabled
|
|
103
110
|
? new CachedBlockhashFetcher(
|
|
@@ -536,7 +543,6 @@ export class TxHandler {
|
|
|
536
543
|
}
|
|
537
544
|
} else {
|
|
538
545
|
const marketLookupTable = await fetchMarketLookupTableAccount();
|
|
539
|
-
|
|
540
546
|
lookupTables = lookupTables
|
|
541
547
|
? [...lookupTables, marketLookupTable]
|
|
542
548
|
: [marketLookupTable];
|