@drift-labs/sdk 2.37.1-beta.0 → 2.37.1-beta.10
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/VERSION +1 -1
- package/lib/adminClient.d.ts +1 -0
- package/lib/adminClient.js +10 -0
- package/lib/constants/perpMarkets.js +20 -0
- package/lib/events/eventSubscriber.js +3 -0
- package/lib/events/fetchLogs.js +3 -0
- package/lib/events/types.d.ts +1 -0
- package/lib/idl/drift.json +73 -8
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/math/auction.js +1 -1
- package/lib/orderSubscriber/OrderSubscriber.d.ts +5 -1
- package/lib/orderSubscriber/OrderSubscriber.js +10 -0
- package/lib/orderSubscriber/types.d.ts +5 -0
- package/lib/tx/baseTxSender.d.ts +30 -0
- package/lib/tx/baseTxSender.js +176 -0
- package/lib/tx/fastSingleTxSender.d.ts +27 -0
- package/lib/tx/fastSingleTxSender.js +83 -0
- package/lib/tx/retryTxSender.d.ts +5 -14
- package/lib/tx/retryTxSender.js +7 -158
- package/lib/types.d.ts +18 -0
- package/lib/types.js +1 -0
- package/lib/user.d.ts +10 -1
- package/lib/user.js +259 -61
- package/package.json +2 -2
- package/src/adminClient.ts +18 -0
- package/src/constants/perpMarkets.ts +20 -0
- package/src/events/eventSubscriber.ts +3 -0
- package/src/events/fetchLogs.ts +3 -0
- package/src/events/types.ts +1 -0
- package/src/idl/drift.json +73 -8
- package/src/index.ts +1 -0
- package/src/marinade/types.ts +70 -70
- package/src/math/auction.ts +1 -1
- package/src/orderSubscriber/OrderSubscriber.ts +19 -2
- package/src/orderSubscriber/types.ts +11 -0
- package/src/tx/baseTxSender.ts +276 -0
- package/src/tx/fastSingleTxSender.ts +142 -0
- package/src/tx/retryTxSender.ts +9 -235
- package/src/types.ts +19 -0
- package/src/user.ts +441 -101
- package/tests/amm/test.ts +83 -39
- package/tests/dlob/helpers.ts +2 -0
- package/tests/dlob/test.ts +19 -17
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.37.1-beta.
|
|
1
|
+
2.37.1-beta.10
|
package/lib/adminClient.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare class AdminClient extends DriftClient {
|
|
|
26
26
|
updateAmmJitIntensity(perpMarketIndex: number, ammJitIntensity: number): Promise<TransactionSignature>;
|
|
27
27
|
updatePerpMarketName(perpMarketIndex: number, name: string): Promise<TransactionSignature>;
|
|
28
28
|
updateSpotMarketName(spotMarketIndex: number, name: string): Promise<TransactionSignature>;
|
|
29
|
+
updatePerpMarketPerLpBase(perpMarketIndex: number, perLpBase: number): Promise<TransactionSignature>;
|
|
29
30
|
updatePerpMarketMaxSpread(perpMarketIndex: number, maxSpread: number): Promise<TransactionSignature>;
|
|
30
31
|
updatePerpFeeStructure(feeStructure: FeeStructure): Promise<TransactionSignature>;
|
|
31
32
|
updateSpotFeeStructure(feeStructure: FeeStructure): Promise<TransactionSignature>;
|
package/lib/adminClient.js
CHANGED
|
@@ -365,6 +365,16 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
365
365
|
},
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
|
+
async updatePerpMarketPerLpBase(perpMarketIndex, perLpBase) {
|
|
369
|
+
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, perpMarketIndex);
|
|
370
|
+
return await this.program.rpc.updatePerpMarketPerLpBase(perLpBase, {
|
|
371
|
+
accounts: {
|
|
372
|
+
admin: this.wallet.publicKey,
|
|
373
|
+
state: await this.getStatePublicKey(),
|
|
374
|
+
perpMarket: perpMarketPublicKey,
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
368
378
|
async updatePerpMarketMaxSpread(perpMarketIndex, maxSpread) {
|
|
369
379
|
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, perpMarketIndex);
|
|
370
380
|
return await this.program.rpc.updatePerpMarketMaxSpread(maxSpread, {
|
|
@@ -144,6 +144,16 @@ exports.DevnetPerpMarkets = [
|
|
|
144
144
|
launchTs: 1689270550000,
|
|
145
145
|
oracleSource: __1.OracleSource.PYTH,
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
fullName: 'HNT',
|
|
149
|
+
category: ['IoT'],
|
|
150
|
+
symbol: 'HNT-PERP',
|
|
151
|
+
baseAssetSymbol: 'HNT',
|
|
152
|
+
marketIndex: 14,
|
|
153
|
+
oracle: new web3_js_1.PublicKey('6Eg8YdfFJQF2HHonzPUBSCCmyUEhrStg9VBLK957sBe6'),
|
|
154
|
+
launchTs: 16922949550000,
|
|
155
|
+
oracleSource: __1.OracleSource.PYTH,
|
|
156
|
+
},
|
|
147
157
|
];
|
|
148
158
|
exports.MainnetPerpMarkets = [
|
|
149
159
|
{
|
|
@@ -286,6 +296,16 @@ exports.MainnetPerpMarkets = [
|
|
|
286
296
|
launchTs: 1689270550000,
|
|
287
297
|
oracleSource: __1.OracleSource.PYTH,
|
|
288
298
|
},
|
|
299
|
+
{
|
|
300
|
+
fullName: 'HNT',
|
|
301
|
+
category: ['IoT'],
|
|
302
|
+
symbol: 'HNT-PERP',
|
|
303
|
+
baseAssetSymbol: 'HNT',
|
|
304
|
+
marketIndex: 14,
|
|
305
|
+
oracle: new web3_js_1.PublicKey('7moA1i5vQUpfDwSpK6Pw9s56ahB7WFGidtbL2ujWrVvm'),
|
|
306
|
+
launchTs: 16922949550000,
|
|
307
|
+
oracleSource: __1.OracleSource.PYTH,
|
|
308
|
+
},
|
|
289
309
|
];
|
|
290
310
|
exports.PerpMarkets = {
|
|
291
311
|
devnet: exports.DevnetPerpMarkets,
|
|
@@ -101,6 +101,7 @@ class EventSubscriber {
|
|
|
101
101
|
const records = [];
|
|
102
102
|
// @ts-ignore
|
|
103
103
|
const events = (0, parse_1.parseLogs)(this.program, slot, logs);
|
|
104
|
+
let runningEventIndex = 0;
|
|
104
105
|
for (const event of events) {
|
|
105
106
|
// @ts-ignore
|
|
106
107
|
const expectRecordType = this.eventListMap.has(event.name);
|
|
@@ -108,8 +109,10 @@ class EventSubscriber {
|
|
|
108
109
|
event.data.txSig = txSig;
|
|
109
110
|
event.data.slot = slot;
|
|
110
111
|
event.data.eventType = event.name;
|
|
112
|
+
event.data.txSigIndex = runningEventIndex;
|
|
111
113
|
records.push(event.data);
|
|
112
114
|
}
|
|
115
|
+
runningEventIndex++;
|
|
113
116
|
}
|
|
114
117
|
return records;
|
|
115
118
|
}
|
package/lib/events/fetchLogs.js
CHANGED
|
@@ -83,11 +83,14 @@ class LogParser {
|
|
|
83
83
|
const records = [];
|
|
84
84
|
// @ts-ignore
|
|
85
85
|
const eventGenerator = this.program._events._eventParser.parseLogs(event.logs, false);
|
|
86
|
+
let runningEventIndex = 0;
|
|
86
87
|
for (const eventLog of eventGenerator) {
|
|
87
88
|
eventLog.data.txSig = event.txSig;
|
|
88
89
|
eventLog.data.slot = event.slot;
|
|
89
90
|
eventLog.data.eventType = eventLog.name;
|
|
91
|
+
eventLog.data.txSigIndex = runningEventIndex;
|
|
90
92
|
records.push(eventLog.data);
|
|
93
|
+
runningEventIndex++;
|
|
91
94
|
}
|
|
92
95
|
return records;
|
|
93
96
|
}
|
package/lib/events/types.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type EventSubscriptionOrderDirection = 'asc' | 'desc';
|
|
|
17
17
|
export type Event<T> = T & {
|
|
18
18
|
txSig: TransactionSignature;
|
|
19
19
|
slot: number;
|
|
20
|
+
txSigIndex: number;
|
|
20
21
|
};
|
|
21
22
|
export type WrappedEvent<Type extends EventType> = EventMap[Type] & {
|
|
22
23
|
eventType: Type;
|
package/lib/idl/drift.json
CHANGED
|
@@ -1122,6 +1122,31 @@
|
|
|
1122
1122
|
}
|
|
1123
1123
|
]
|
|
1124
1124
|
},
|
|
1125
|
+
{
|
|
1126
|
+
"name": "updateUserReduceOnly",
|
|
1127
|
+
"accounts": [
|
|
1128
|
+
{
|
|
1129
|
+
"name": "user",
|
|
1130
|
+
"isMut": true,
|
|
1131
|
+
"isSigner": false
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
"name": "authority",
|
|
1135
|
+
"isMut": false,
|
|
1136
|
+
"isSigner": true
|
|
1137
|
+
}
|
|
1138
|
+
],
|
|
1139
|
+
"args": [
|
|
1140
|
+
{
|
|
1141
|
+
"name": "subAccountId",
|
|
1142
|
+
"type": "u16"
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
"name": "reduceOnly",
|
|
1146
|
+
"type": "bool"
|
|
1147
|
+
}
|
|
1148
|
+
]
|
|
1149
|
+
},
|
|
1125
1150
|
{
|
|
1126
1151
|
"name": "deleteUser",
|
|
1127
1152
|
"accounts": [
|
|
@@ -3857,6 +3882,32 @@
|
|
|
3857
3882
|
}
|
|
3858
3883
|
]
|
|
3859
3884
|
},
|
|
3885
|
+
{
|
|
3886
|
+
"name": "updatePerpMarketPerLpBase",
|
|
3887
|
+
"accounts": [
|
|
3888
|
+
{
|
|
3889
|
+
"name": "admin",
|
|
3890
|
+
"isMut": false,
|
|
3891
|
+
"isSigner": true
|
|
3892
|
+
},
|
|
3893
|
+
{
|
|
3894
|
+
"name": "state",
|
|
3895
|
+
"isMut": false,
|
|
3896
|
+
"isSigner": false
|
|
3897
|
+
},
|
|
3898
|
+
{
|
|
3899
|
+
"name": "perpMarket",
|
|
3900
|
+
"isMut": true,
|
|
3901
|
+
"isSigner": false
|
|
3902
|
+
}
|
|
3903
|
+
],
|
|
3904
|
+
"args": [
|
|
3905
|
+
{
|
|
3906
|
+
"name": "perLpBase",
|
|
3907
|
+
"type": "i8"
|
|
3908
|
+
}
|
|
3909
|
+
]
|
|
3910
|
+
},
|
|
3860
3911
|
{
|
|
3861
3912
|
"name": "updateLpCooldownTime",
|
|
3862
3913
|
"accounts": [
|
|
@@ -6953,9 +7004,20 @@
|
|
|
6953
7004
|
],
|
|
6954
7005
|
"type": "i32"
|
|
6955
7006
|
},
|
|
7007
|
+
{
|
|
7008
|
+
"name": "perLpBase",
|
|
7009
|
+
"docs": [
|
|
7010
|
+
"expo for unit of per_lp, base 10 (if per_lp_base=X, then per_lp unit is 10^X)"
|
|
7011
|
+
],
|
|
7012
|
+
"type": "i8"
|
|
7013
|
+
},
|
|
6956
7014
|
{
|
|
6957
7015
|
"name": "padding1",
|
|
6958
|
-
"type": "
|
|
7016
|
+
"type": "u8"
|
|
7017
|
+
},
|
|
7018
|
+
{
|
|
7019
|
+
"name": "padding2",
|
|
7020
|
+
"type": "u16"
|
|
6959
7021
|
},
|
|
6960
7022
|
{
|
|
6961
7023
|
"name": "totalFeeEarnedPerLp",
|
|
@@ -7422,13 +7484,8 @@
|
|
|
7422
7484
|
"type": "u8"
|
|
7423
7485
|
},
|
|
7424
7486
|
{
|
|
7425
|
-
"name": "
|
|
7426
|
-
"type":
|
|
7427
|
-
"array": [
|
|
7428
|
-
"u8",
|
|
7429
|
-
1
|
|
7430
|
-
]
|
|
7431
|
-
}
|
|
7487
|
+
"name": "perLpBase",
|
|
7488
|
+
"type": "i8"
|
|
7432
7489
|
}
|
|
7433
7490
|
]
|
|
7434
7491
|
}
|
|
@@ -8314,6 +8371,9 @@
|
|
|
8314
8371
|
},
|
|
8315
8372
|
{
|
|
8316
8373
|
"name": "Bankrupt"
|
|
8374
|
+
},
|
|
8375
|
+
{
|
|
8376
|
+
"name": "ReduceOnly"
|
|
8317
8377
|
}
|
|
8318
8378
|
]
|
|
8319
8379
|
}
|
|
@@ -10681,6 +10741,11 @@
|
|
|
10681
10741
|
"code": 6253,
|
|
10682
10742
|
"name": "CantUpdatePerpBidAskTwap",
|
|
10683
10743
|
"msg": "CantUpdatePerpBidAskTwap"
|
|
10744
|
+
},
|
|
10745
|
+
{
|
|
10746
|
+
"code": 6254,
|
|
10747
|
+
"name": "UserReduceOnly",
|
|
10748
|
+
"msg": "UserReduceOnly"
|
|
10684
10749
|
}
|
|
10685
10750
|
]
|
|
10686
10751
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export * from './serum/serumSubscriber';
|
|
|
60
60
|
export * from './serum/serumFulfillmentConfigMap';
|
|
61
61
|
export * from './phoenix/phoenixSubscriber';
|
|
62
62
|
export * from './phoenix/phoenixFulfillmentConfigMap';
|
|
63
|
+
export * from './tx/fastSingleTxSender';
|
|
63
64
|
export * from './tx/retryTxSender';
|
|
64
65
|
export * from './tx/types';
|
|
65
66
|
export * from './util/computeUnits';
|
package/lib/index.js
CHANGED
|
@@ -83,6 +83,7 @@ __exportStar(require("./serum/serumSubscriber"), exports);
|
|
|
83
83
|
__exportStar(require("./serum/serumFulfillmentConfigMap"), exports);
|
|
84
84
|
__exportStar(require("./phoenix/phoenixSubscriber"), exports);
|
|
85
85
|
__exportStar(require("./phoenix/phoenixFulfillmentConfigMap"), exports);
|
|
86
|
+
__exportStar(require("./tx/fastSingleTxSender"), exports);
|
|
86
87
|
__exportStar(require("./tx/retryTxSender"), exports);
|
|
87
88
|
__exportStar(require("./tx/types"), exports);
|
|
88
89
|
__exportStar(require("./util/computeUnits"), exports);
|
package/lib/math/auction.js
CHANGED
|
@@ -64,7 +64,7 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
|
|
|
64
64
|
const deltaDenominator = new _1.BN(order.auctionDuration);
|
|
65
65
|
const deltaNumerator = _1.BN.min(slotsElapsed, deltaDenominator);
|
|
66
66
|
if (deltaDenominator.eq(_1.ZERO)) {
|
|
67
|
-
return
|
|
67
|
+
return oraclePrice.add(order.auctionEndPrice);
|
|
68
68
|
}
|
|
69
69
|
let priceOffsetDelta;
|
|
70
70
|
if ((0, types_1.isVariant)(order.direction, 'long')) {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { DriftClient } from '../driftClient';
|
|
3
4
|
import { UserAccount } from '../types';
|
|
4
5
|
import { Buffer } from 'buffer';
|
|
5
6
|
import { DLOB } from '../dlob/DLOB';
|
|
6
|
-
import { OrderSubscriberConfig } from './types';
|
|
7
|
+
import { OrderSubscriberConfig, OrderSubscriberEvents } from './types';
|
|
7
8
|
import { PollingSubscription } from './PollingSubscription';
|
|
8
9
|
import { WebsocketSubscription } from './WebsocketSubscription';
|
|
10
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
9
12
|
export declare class OrderSubscriber {
|
|
10
13
|
driftClient: DriftClient;
|
|
11
14
|
usersAccounts: Map<string, {
|
|
@@ -13,6 +16,7 @@ export declare class OrderSubscriber {
|
|
|
13
16
|
userAccount: UserAccount;
|
|
14
17
|
}>;
|
|
15
18
|
subscription: PollingSubscription | WebsocketSubscription;
|
|
19
|
+
eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
|
|
16
20
|
fetchPromise?: Promise<void>;
|
|
17
21
|
fetchPromiseResolver: () => void;
|
|
18
22
|
constructor(config: OrderSubscriberConfig);
|
|
@@ -7,6 +7,7 @@ const buffer_1 = require("buffer");
|
|
|
7
7
|
const DLOB_1 = require("../dlob/DLOB");
|
|
8
8
|
const PollingSubscription_1 = require("./PollingSubscription");
|
|
9
9
|
const WebsocketSubscription_1 = require("./WebsocketSubscription");
|
|
10
|
+
const events_1 = require("events");
|
|
10
11
|
class OrderSubscriber {
|
|
11
12
|
constructor(config) {
|
|
12
13
|
this.usersAccounts = new Map();
|
|
@@ -23,6 +24,7 @@ class OrderSubscriber {
|
|
|
23
24
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
24
25
|
});
|
|
25
26
|
}
|
|
27
|
+
this.eventEmitter = new events_1.EventEmitter();
|
|
26
28
|
}
|
|
27
29
|
async subscribe() {
|
|
28
30
|
await this.subscription.subscribe();
|
|
@@ -75,6 +77,14 @@ class OrderSubscriber {
|
|
|
75
77
|
const slotAndUserAccount = this.usersAccounts.get(key);
|
|
76
78
|
if (!slotAndUserAccount || slotAndUserAccount.slot < slot) {
|
|
77
79
|
const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
|
|
80
|
+
const newOrders = userAccount.orders.filter((order) => {
|
|
81
|
+
var _a;
|
|
82
|
+
return order.slot.toNumber() > ((_a = slotAndUserAccount === null || slotAndUserAccount === void 0 ? void 0 : slotAndUserAccount.slot) !== null && _a !== void 0 ? _a : 0) &&
|
|
83
|
+
order.slot.toNumber() <= slot;
|
|
84
|
+
});
|
|
85
|
+
if (newOrders.length > 0) {
|
|
86
|
+
this.eventEmitter.emit('onUpdate', userAccount, newOrders, new web3_js_1.PublicKey(key), slot);
|
|
87
|
+
}
|
|
78
88
|
if (userAccount.hasOpenOrder) {
|
|
79
89
|
this.usersAccounts.set(key, { slot, userAccount });
|
|
80
90
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { Order, UserAccount } from '../types';
|
|
1
3
|
import { DriftClient } from '../driftClient';
|
|
2
4
|
export type OrderSubscriberConfig = {
|
|
3
5
|
driftClient: DriftClient;
|
|
@@ -9,3 +11,6 @@ export type OrderSubscriberConfig = {
|
|
|
9
11
|
skipInitialLoad?: boolean;
|
|
10
12
|
};
|
|
11
13
|
};
|
|
14
|
+
export interface OrderSubscriberEvents {
|
|
15
|
+
onUpdate: (account: UserAccount, updatedOrders: Order[], pubkey: PublicKey, slot: number) => void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { TxSender, TxSigAndSlot } from './types';
|
|
3
|
+
import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection, VersionedTransaction, TransactionInstruction, AddressLookupTableAccount } from '@solana/web3.js';
|
|
4
|
+
import { IWallet } from '../types';
|
|
5
|
+
export declare abstract class BaseTxSender implements TxSender {
|
|
6
|
+
connection: Connection;
|
|
7
|
+
wallet: IWallet;
|
|
8
|
+
opts: ConfirmOptions;
|
|
9
|
+
timeout: number;
|
|
10
|
+
additionalConnections: Connection[];
|
|
11
|
+
timeoutCount: number;
|
|
12
|
+
constructor({ connection, wallet, opts, timeout, additionalConnections, }: {
|
|
13
|
+
connection: Connection;
|
|
14
|
+
wallet: IWallet;
|
|
15
|
+
opts?: ConfirmOptions;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
additionalConnections?: any;
|
|
18
|
+
});
|
|
19
|
+
send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
20
|
+
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
|
|
21
|
+
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
22
|
+
sendVersionedTransaction(tx: VersionedTransaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
23
|
+
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
24
|
+
confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
25
|
+
getTimestamp(): number;
|
|
26
|
+
promiseTimeout<T>(promises: Promise<T>[], timeoutMs: number): Promise<T | null>;
|
|
27
|
+
sendToAdditionalConnections(rawTx: Buffer | Uint8Array, opts: ConfirmOptions): void;
|
|
28
|
+
addAdditionalConnection(newConnection: Connection): void;
|
|
29
|
+
getTimeoutCount(): number;
|
|
30
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BaseTxSender = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
9
|
+
const assert_1 = __importDefault(require("assert"));
|
|
10
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
11
|
+
const DEFAULT_TIMEOUT = 35000;
|
|
12
|
+
class BaseTxSender {
|
|
13
|
+
constructor({ connection, wallet, opts = anchor_1.AnchorProvider.defaultOptions(), timeout = DEFAULT_TIMEOUT, additionalConnections = new Array(), }) {
|
|
14
|
+
this.timeoutCount = 0;
|
|
15
|
+
this.connection = connection;
|
|
16
|
+
this.wallet = wallet;
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
this.timeout = timeout;
|
|
19
|
+
this.additionalConnections = additionalConnections;
|
|
20
|
+
}
|
|
21
|
+
async send(tx, additionalSigners, opts, preSigned) {
|
|
22
|
+
if (additionalSigners === undefined) {
|
|
23
|
+
additionalSigners = [];
|
|
24
|
+
}
|
|
25
|
+
if (opts === undefined) {
|
|
26
|
+
opts = this.opts;
|
|
27
|
+
}
|
|
28
|
+
const signedTx = preSigned
|
|
29
|
+
? tx
|
|
30
|
+
: await this.prepareTx(tx, additionalSigners, opts);
|
|
31
|
+
return this.sendRawTransaction(signedTx.serialize(), opts);
|
|
32
|
+
}
|
|
33
|
+
async prepareTx(tx, additionalSigners, opts) {
|
|
34
|
+
tx.feePayer = this.wallet.publicKey;
|
|
35
|
+
tx.recentBlockhash = (await this.connection.getLatestBlockhash(opts.preflightCommitment)).blockhash;
|
|
36
|
+
additionalSigners
|
|
37
|
+
.filter((s) => s !== undefined)
|
|
38
|
+
.forEach((kp) => {
|
|
39
|
+
tx.partialSign(kp);
|
|
40
|
+
});
|
|
41
|
+
const signedTx = await this.wallet.signTransaction(tx);
|
|
42
|
+
return signedTx;
|
|
43
|
+
}
|
|
44
|
+
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
|
|
45
|
+
if (additionalSigners === undefined) {
|
|
46
|
+
additionalSigners = [];
|
|
47
|
+
}
|
|
48
|
+
if (opts === undefined) {
|
|
49
|
+
opts = this.opts;
|
|
50
|
+
}
|
|
51
|
+
const message = new web3_js_1.TransactionMessage({
|
|
52
|
+
payerKey: this.wallet.publicKey,
|
|
53
|
+
recentBlockhash: (await this.connection.getLatestBlockhash(opts.preflightCommitment)).blockhash,
|
|
54
|
+
instructions: ixs,
|
|
55
|
+
}).compileToV0Message(lookupTableAccounts);
|
|
56
|
+
const tx = new web3_js_1.VersionedTransaction(message);
|
|
57
|
+
return tx;
|
|
58
|
+
}
|
|
59
|
+
async sendVersionedTransaction(tx, additionalSigners, opts, preSigned) {
|
|
60
|
+
let signedTx;
|
|
61
|
+
if (preSigned) {
|
|
62
|
+
signedTx = tx;
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
}
|
|
65
|
+
else if (this.wallet.payer) {
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
tx.sign((additionalSigners !== null && additionalSigners !== void 0 ? additionalSigners : []).concat(this.wallet.payer));
|
|
68
|
+
signedTx = tx;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
additionalSigners === null || additionalSigners === void 0 ? void 0 : additionalSigners.filter((s) => s !== undefined).forEach((kp) => {
|
|
72
|
+
tx.sign([kp]);
|
|
73
|
+
});
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
signedTx = await this.wallet.signTransaction(tx);
|
|
76
|
+
}
|
|
77
|
+
if (opts === undefined) {
|
|
78
|
+
opts = this.opts;
|
|
79
|
+
}
|
|
80
|
+
return this.sendRawTransaction(signedTx.serialize(), opts);
|
|
81
|
+
}
|
|
82
|
+
async sendRawTransaction(
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
84
|
+
rawTransaction,
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
|
+
opts) {
|
|
87
|
+
throw new Error('Must be implemented by subclass');
|
|
88
|
+
}
|
|
89
|
+
async confirmTransaction(signature, commitment) {
|
|
90
|
+
let decodedSignature;
|
|
91
|
+
try {
|
|
92
|
+
decodedSignature = bs58_1.default.decode(signature);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
throw new Error('signature must be base58 encoded: ' + signature);
|
|
96
|
+
}
|
|
97
|
+
(0, assert_1.default)(decodedSignature.length === 64, 'signature has invalid length');
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
const subscriptionCommitment = commitment || this.opts.commitment;
|
|
100
|
+
const subscriptionIds = new Array();
|
|
101
|
+
const connections = [this.connection, ...this.additionalConnections];
|
|
102
|
+
let response = null;
|
|
103
|
+
const promises = connections.map((connection, i) => {
|
|
104
|
+
let subscriptionId;
|
|
105
|
+
const confirmPromise = new Promise((resolve, reject) => {
|
|
106
|
+
try {
|
|
107
|
+
subscriptionId = connection.onSignature(signature, (result, context) => {
|
|
108
|
+
subscriptionIds[i] = undefined;
|
|
109
|
+
response = {
|
|
110
|
+
context,
|
|
111
|
+
value: result,
|
|
112
|
+
};
|
|
113
|
+
resolve(null);
|
|
114
|
+
}, subscriptionCommitment);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
reject(err);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
subscriptionIds.push(subscriptionId);
|
|
121
|
+
return confirmPromise;
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
await this.promiseTimeout(promises, this.timeout);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
for (const [i, subscriptionId] of subscriptionIds.entries()) {
|
|
128
|
+
if (subscriptionId) {
|
|
129
|
+
connections[i].removeSignatureListener(subscriptionId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (response === null) {
|
|
134
|
+
this.timeoutCount += 1;
|
|
135
|
+
const duration = (Date.now() - start) / 1000;
|
|
136
|
+
throw new Error(`Transaction was not confirmed in ${duration.toFixed(2)} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`);
|
|
137
|
+
}
|
|
138
|
+
return response;
|
|
139
|
+
}
|
|
140
|
+
getTimestamp() {
|
|
141
|
+
return new Date().getTime();
|
|
142
|
+
}
|
|
143
|
+
promiseTimeout(promises, timeoutMs) {
|
|
144
|
+
let timeoutId;
|
|
145
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
146
|
+
timeoutId = setTimeout(() => resolve(null), timeoutMs);
|
|
147
|
+
});
|
|
148
|
+
return Promise.race([...promises, timeoutPromise]).then((result) => {
|
|
149
|
+
clearTimeout(timeoutId);
|
|
150
|
+
return result;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
sendToAdditionalConnections(rawTx, opts) {
|
|
154
|
+
this.additionalConnections.map((connection) => {
|
|
155
|
+
connection.sendRawTransaction(rawTx, opts).catch((e) => {
|
|
156
|
+
console.error(
|
|
157
|
+
// @ts-ignore
|
|
158
|
+
`error sending tx to additional connection ${connection._rpcEndpoint}`);
|
|
159
|
+
console.error(e);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
addAdditionalConnection(newConnection) {
|
|
164
|
+
const alreadyUsingConnection = this.additionalConnections.filter((connection) => {
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
return connection._rpcEndpoint === newConnection.rpcEndpoint;
|
|
167
|
+
}).length > 0;
|
|
168
|
+
if (!alreadyUsingConnection) {
|
|
169
|
+
this.additionalConnections.push(newConnection);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
getTimeoutCount() {
|
|
173
|
+
return this.timeoutCount;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.BaseTxSender = BaseTxSender;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { TxSigAndSlot } from './types';
|
|
3
|
+
import { ConfirmOptions, Signer, Transaction, Connection, VersionedTransaction, TransactionInstruction, AddressLookupTableAccount } from '@solana/web3.js';
|
|
4
|
+
import { IWallet } from '../types';
|
|
5
|
+
import { BaseTxSender } from './baseTxSender';
|
|
6
|
+
export declare class FastSingleTxSender extends BaseTxSender {
|
|
7
|
+
connection: Connection;
|
|
8
|
+
wallet: IWallet;
|
|
9
|
+
opts: ConfirmOptions;
|
|
10
|
+
timeout: number;
|
|
11
|
+
blockhashRefreshInterval: number;
|
|
12
|
+
additionalConnections: Connection[];
|
|
13
|
+
timoutCount: number;
|
|
14
|
+
recentBlockhash: string;
|
|
15
|
+
constructor({ connection, wallet, opts, timeout, blockhashRefreshInterval, additionalConnections, }: {
|
|
16
|
+
connection: Connection;
|
|
17
|
+
wallet: IWallet;
|
|
18
|
+
opts?: ConfirmOptions;
|
|
19
|
+
timeout?: number;
|
|
20
|
+
blockhashRefreshInterval?: number;
|
|
21
|
+
additionalConnections?: any;
|
|
22
|
+
});
|
|
23
|
+
startBlockhashRefreshLoop(): void;
|
|
24
|
+
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
|
|
25
|
+
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
26
|
+
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FastSingleTxSender = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
6
|
+
const baseTxSender_1 = require("./baseTxSender");
|
|
7
|
+
const DEFAULT_TIMEOUT = 35000;
|
|
8
|
+
const DEFAULT_BLOCKHASH_REFRESH = 10000;
|
|
9
|
+
class FastSingleTxSender extends baseTxSender_1.BaseTxSender {
|
|
10
|
+
constructor({ connection, wallet, opts = anchor_1.AnchorProvider.defaultOptions(), timeout = DEFAULT_TIMEOUT, blockhashRefreshInterval = DEFAULT_BLOCKHASH_REFRESH, additionalConnections = new Array(), }) {
|
|
11
|
+
super({ connection, wallet, opts, timeout, additionalConnections });
|
|
12
|
+
this.timoutCount = 0;
|
|
13
|
+
this.connection = connection;
|
|
14
|
+
this.wallet = wallet;
|
|
15
|
+
this.opts = opts;
|
|
16
|
+
this.timeout = timeout;
|
|
17
|
+
this.blockhashRefreshInterval = blockhashRefreshInterval;
|
|
18
|
+
this.additionalConnections = additionalConnections;
|
|
19
|
+
this.startBlockhashRefreshLoop();
|
|
20
|
+
}
|
|
21
|
+
startBlockhashRefreshLoop() {
|
|
22
|
+
setInterval(async () => {
|
|
23
|
+
this.recentBlockhash = (await this.connection.getLatestBlockhash(this.opts)).blockhash;
|
|
24
|
+
}, this.blockhashRefreshInterval);
|
|
25
|
+
}
|
|
26
|
+
async prepareTx(tx, additionalSigners, opts) {
|
|
27
|
+
var _a;
|
|
28
|
+
tx.feePayer = this.wallet.publicKey;
|
|
29
|
+
tx.recentBlockhash =
|
|
30
|
+
(_a = this.recentBlockhash) !== null && _a !== void 0 ? _a : (await this.connection.getLatestBlockhash(opts.preflightCommitment))
|
|
31
|
+
.blockhash;
|
|
32
|
+
additionalSigners
|
|
33
|
+
.filter((s) => s !== undefined)
|
|
34
|
+
.forEach((kp) => {
|
|
35
|
+
tx.partialSign(kp);
|
|
36
|
+
});
|
|
37
|
+
const signedTx = await this.wallet.signTransaction(tx);
|
|
38
|
+
return signedTx;
|
|
39
|
+
}
|
|
40
|
+
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
|
|
41
|
+
var _a;
|
|
42
|
+
if (additionalSigners === undefined) {
|
|
43
|
+
additionalSigners = [];
|
|
44
|
+
}
|
|
45
|
+
if (opts === undefined) {
|
|
46
|
+
opts = this.opts;
|
|
47
|
+
}
|
|
48
|
+
const message = new web3_js_1.TransactionMessage({
|
|
49
|
+
payerKey: this.wallet.publicKey,
|
|
50
|
+
recentBlockhash: (_a = this.recentBlockhash) !== null && _a !== void 0 ? _a : (await this.connection.getLatestBlockhash(opts.preflightCommitment))
|
|
51
|
+
.blockhash,
|
|
52
|
+
instructions: ixs,
|
|
53
|
+
}).compileToV0Message(lookupTableAccounts);
|
|
54
|
+
const tx = new web3_js_1.VersionedTransaction(message);
|
|
55
|
+
return tx;
|
|
56
|
+
}
|
|
57
|
+
async sendRawTransaction(rawTransaction, opts) {
|
|
58
|
+
let txid;
|
|
59
|
+
try {
|
|
60
|
+
txid = await this.connection.sendRawTransaction(rawTransaction, opts);
|
|
61
|
+
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(e);
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
this.connection.sendRawTransaction(rawTransaction, opts).catch((e) => {
|
|
68
|
+
console.error(e);
|
|
69
|
+
});
|
|
70
|
+
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
71
|
+
let slot;
|
|
72
|
+
try {
|
|
73
|
+
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
74
|
+
slot = result.context.slot;
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
console.error(e);
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
return { txSig: txid, slot };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.FastSingleTxSender = FastSingleTxSender;
|