@drift-labs/sdk 2.37.1-beta.0 → 2.37.1-beta.2
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/events/eventSubscriber.js +3 -0
- package/lib/events/fetchLogs.js +3 -0
- package/lib/events/types.d.ts +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -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/package.json +2 -2
- package/src/events/eventSubscriber.ts +3 -0
- package/src/events/fetchLogs.ts +3 -0
- package/src/events/types.ts +1 -0
- package/src/index.ts +1 -0
- package/src/marinade/types.ts +70 -70
- package/src/tx/baseTxSender.ts +276 -0
- package/src/tx/fastSingleTxSender.ts +142 -0
- package/src/tx/retryTxSender.ts +9 -235
- package/tests/amm/test.ts +83 -39
- package/tests/dlob/test.ts +19 -17
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.37.1-beta.
|
|
1
|
+
2.37.1-beta.2
|
|
@@ -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/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);
|
|
@@ -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;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { TxSigAndSlot } from './types';
|
|
3
|
+
import { ConfirmOptions, Connection } from '@solana/web3.js';
|
|
4
4
|
import { IWallet } from '../types';
|
|
5
|
+
import { BaseTxSender } from './baseTxSender';
|
|
5
6
|
type ResolveReference = {
|
|
6
7
|
resolve?: () => void;
|
|
7
8
|
};
|
|
8
|
-
export declare class RetryTxSender
|
|
9
|
+
export declare class RetryTxSender extends BaseTxSender {
|
|
9
10
|
connection: Connection;
|
|
10
11
|
wallet: IWallet;
|
|
11
12
|
opts: ConfirmOptions;
|
|
@@ -21,17 +22,7 @@ export declare class RetryTxSender implements TxSender {
|
|
|
21
22
|
retrySleep?: number;
|
|
22
23
|
additionalConnections?: any;
|
|
23
24
|
});
|
|
24
|
-
send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
25
|
-
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
|
|
26
|
-
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
27
|
-
sendVersionedTransaction(tx: VersionedTransaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
28
|
-
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
29
|
-
confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
30
|
-
getTimestamp(): number;
|
|
31
25
|
sleep(reference: ResolveReference): Promise<void>;
|
|
32
|
-
|
|
33
|
-
sendToAdditionalConnections(rawTx: Buffer | Uint8Array, opts: ConfirmOptions): void;
|
|
34
|
-
addAdditionalConnection(newConnection: Connection): void;
|
|
35
|
-
getTimeoutCount(): number;
|
|
26
|
+
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
36
27
|
}
|
|
37
28
|
export {};
|
package/lib/tx/retryTxSender.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.RetryTxSender = void 0;
|
|
7
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
8
4
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
9
|
-
const
|
|
10
|
-
const bs58_1 = __importDefault(require("bs58"));
|
|
5
|
+
const baseTxSender_1 = require("./baseTxSender");
|
|
11
6
|
const DEFAULT_TIMEOUT = 35000;
|
|
12
7
|
const DEFAULT_RETRY = 8000;
|
|
13
|
-
class RetryTxSender {
|
|
8
|
+
class RetryTxSender extends baseTxSender_1.BaseTxSender {
|
|
14
9
|
constructor({ connection, wallet, opts = anchor_1.AnchorProvider.defaultOptions(), timeout = DEFAULT_TIMEOUT, retrySleep = DEFAULT_RETRY, additionalConnections = new Array(), }) {
|
|
10
|
+
super({ connection, wallet, opts, timeout, additionalConnections });
|
|
15
11
|
this.timoutCount = 0;
|
|
16
12
|
this.connection = connection;
|
|
17
13
|
this.wallet = wallet;
|
|
@@ -20,66 +16,11 @@ class RetryTxSender {
|
|
|
20
16
|
this.retrySleep = retrySleep;
|
|
21
17
|
this.additionalConnections = additionalConnections;
|
|
22
18
|
}
|
|
23
|
-
async
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (opts === undefined) {
|
|
28
|
-
opts = this.opts;
|
|
29
|
-
}
|
|
30
|
-
const signedTx = preSigned
|
|
31
|
-
? tx
|
|
32
|
-
: await this.prepareTx(tx, additionalSigners, opts);
|
|
33
|
-
return this.sendRawTransaction(signedTx.serialize(), opts);
|
|
34
|
-
}
|
|
35
|
-
async prepareTx(tx, additionalSigners, opts) {
|
|
36
|
-
tx.feePayer = this.wallet.publicKey;
|
|
37
|
-
tx.recentBlockhash = (await this.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
|
|
38
|
-
additionalSigners
|
|
39
|
-
.filter((s) => s !== undefined)
|
|
40
|
-
.forEach((kp) => {
|
|
41
|
-
tx.partialSign(kp);
|
|
19
|
+
async sleep(reference) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
reference.resolve = resolve;
|
|
22
|
+
setTimeout(resolve, this.retrySleep);
|
|
42
23
|
});
|
|
43
|
-
const signedTx = await this.wallet.signTransaction(tx);
|
|
44
|
-
return signedTx;
|
|
45
|
-
}
|
|
46
|
-
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
|
|
47
|
-
if (additionalSigners === undefined) {
|
|
48
|
-
additionalSigners = [];
|
|
49
|
-
}
|
|
50
|
-
if (opts === undefined) {
|
|
51
|
-
opts = this.opts;
|
|
52
|
-
}
|
|
53
|
-
const message = new web3_js_1.TransactionMessage({
|
|
54
|
-
payerKey: this.wallet.publicKey,
|
|
55
|
-
recentBlockhash: (await this.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash,
|
|
56
|
-
instructions: ixs,
|
|
57
|
-
}).compileToV0Message(lookupTableAccounts);
|
|
58
|
-
const tx = new web3_js_1.VersionedTransaction(message);
|
|
59
|
-
return tx;
|
|
60
|
-
}
|
|
61
|
-
async sendVersionedTransaction(tx, additionalSigners, opts, preSigned) {
|
|
62
|
-
let signedTx;
|
|
63
|
-
if (preSigned) {
|
|
64
|
-
signedTx = tx;
|
|
65
|
-
// @ts-ignore
|
|
66
|
-
}
|
|
67
|
-
else if (this.wallet.payer) {
|
|
68
|
-
// @ts-ignore
|
|
69
|
-
tx.sign((additionalSigners !== null && additionalSigners !== void 0 ? additionalSigners : []).concat(this.wallet.payer));
|
|
70
|
-
signedTx = tx;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
additionalSigners === null || additionalSigners === void 0 ? void 0 : additionalSigners.filter((s) => s !== undefined).forEach((kp) => {
|
|
74
|
-
tx.sign([kp]);
|
|
75
|
-
});
|
|
76
|
-
// @ts-ignore
|
|
77
|
-
signedTx = await this.wallet.signTransaction(tx);
|
|
78
|
-
}
|
|
79
|
-
if (opts === undefined) {
|
|
80
|
-
opts = this.opts;
|
|
81
|
-
}
|
|
82
|
-
return this.sendRawTransaction(signedTx.serialize(), opts);
|
|
83
24
|
}
|
|
84
25
|
async sendRawTransaction(rawTransaction, opts) {
|
|
85
26
|
const startTime = this.getTimestamp();
|
|
@@ -130,97 +71,5 @@ class RetryTxSender {
|
|
|
130
71
|
}
|
|
131
72
|
return { txSig: txid, slot };
|
|
132
73
|
}
|
|
133
|
-
async confirmTransaction(signature, commitment) {
|
|
134
|
-
let decodedSignature;
|
|
135
|
-
try {
|
|
136
|
-
decodedSignature = bs58_1.default.decode(signature);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
throw new Error('signature must be base58 encoded: ' + signature);
|
|
140
|
-
}
|
|
141
|
-
(0, assert_1.default)(decodedSignature.length === 64, 'signature has invalid length');
|
|
142
|
-
const start = Date.now();
|
|
143
|
-
const subscriptionCommitment = commitment || this.opts.commitment;
|
|
144
|
-
const subscriptionIds = new Array();
|
|
145
|
-
const connections = [this.connection, ...this.additionalConnections];
|
|
146
|
-
let response = null;
|
|
147
|
-
const promises = connections.map((connection, i) => {
|
|
148
|
-
let subscriptionId;
|
|
149
|
-
const confirmPromise = new Promise((resolve, reject) => {
|
|
150
|
-
try {
|
|
151
|
-
subscriptionId = connection.onSignature(signature, (result, context) => {
|
|
152
|
-
subscriptionIds[i] = undefined;
|
|
153
|
-
response = {
|
|
154
|
-
context,
|
|
155
|
-
value: result,
|
|
156
|
-
};
|
|
157
|
-
resolve(null);
|
|
158
|
-
}, subscriptionCommitment);
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
reject(err);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
subscriptionIds.push(subscriptionId);
|
|
165
|
-
return confirmPromise;
|
|
166
|
-
});
|
|
167
|
-
try {
|
|
168
|
-
await this.promiseTimeout(promises, this.timeout);
|
|
169
|
-
}
|
|
170
|
-
finally {
|
|
171
|
-
for (const [i, subscriptionId] of subscriptionIds.entries()) {
|
|
172
|
-
if (subscriptionId) {
|
|
173
|
-
connections[i].removeSignatureListener(subscriptionId);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (response === null) {
|
|
178
|
-
this.timoutCount += 1;
|
|
179
|
-
const duration = (Date.now() - start) / 1000;
|
|
180
|
-
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.`);
|
|
181
|
-
}
|
|
182
|
-
return response;
|
|
183
|
-
}
|
|
184
|
-
getTimestamp() {
|
|
185
|
-
return new Date().getTime();
|
|
186
|
-
}
|
|
187
|
-
async sleep(reference) {
|
|
188
|
-
return new Promise((resolve) => {
|
|
189
|
-
reference.resolve = resolve;
|
|
190
|
-
setTimeout(resolve, this.retrySleep);
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
promiseTimeout(promises, timeoutMs) {
|
|
194
|
-
let timeoutId;
|
|
195
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
196
|
-
timeoutId = setTimeout(() => resolve(null), timeoutMs);
|
|
197
|
-
});
|
|
198
|
-
return Promise.race([...promises, timeoutPromise]).then((result) => {
|
|
199
|
-
clearTimeout(timeoutId);
|
|
200
|
-
return result;
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
sendToAdditionalConnections(rawTx, opts) {
|
|
204
|
-
this.additionalConnections.map((connection) => {
|
|
205
|
-
connection.sendRawTransaction(rawTx, opts).catch((e) => {
|
|
206
|
-
console.error(
|
|
207
|
-
// @ts-ignore
|
|
208
|
-
`error sending tx to additional connection ${connection._rpcEndpoint}`);
|
|
209
|
-
console.error(e);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
addAdditionalConnection(newConnection) {
|
|
214
|
-
const alreadyUsingConnection = this.additionalConnections.filter((connection) => {
|
|
215
|
-
// @ts-ignore
|
|
216
|
-
return connection._rpcEndpoint === newConnection.rpcEndpoint;
|
|
217
|
-
}).length > 0;
|
|
218
|
-
if (!alreadyUsingConnection) {
|
|
219
|
-
this.additionalConnections.push(newConnection);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
getTimeoutCount() {
|
|
223
|
-
return this.timoutCount;
|
|
224
|
-
}
|
|
225
74
|
}
|
|
226
75
|
exports.RetryTxSender = RetryTxSender;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.37.1-beta.
|
|
3
|
+
"version": "2.37.1-beta.2",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"author": "crispheaney",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"eslint-plugin-prettier": "^3.4.0",
|
|
56
56
|
"lodash": "^4.17.21",
|
|
57
57
|
"mocha": "^10.0.0",
|
|
58
|
-
"prettier": "^
|
|
58
|
+
"prettier": "^3.0.1",
|
|
59
59
|
"ts-node": "^10.8.0",
|
|
60
60
|
"typescript": "^4.9.5"
|
|
61
61
|
},
|
|
@@ -168,6 +168,7 @@ export class EventSubscriber {
|
|
|
168
168
|
const records = [];
|
|
169
169
|
// @ts-ignore
|
|
170
170
|
const events = parseLogs(this.program, slot, logs);
|
|
171
|
+
let runningEventIndex = 0;
|
|
171
172
|
for (const event of events) {
|
|
172
173
|
// @ts-ignore
|
|
173
174
|
const expectRecordType = this.eventListMap.has(event.name);
|
|
@@ -175,8 +176,10 @@ export class EventSubscriber {
|
|
|
175
176
|
event.data.txSig = txSig;
|
|
176
177
|
event.data.slot = slot;
|
|
177
178
|
event.data.eventType = event.name;
|
|
179
|
+
event.data.txSigIndex = runningEventIndex;
|
|
178
180
|
records.push(event.data);
|
|
179
181
|
}
|
|
182
|
+
runningEventIndex++;
|
|
180
183
|
}
|
|
181
184
|
return records;
|
|
182
185
|
}
|
package/src/events/fetchLogs.ts
CHANGED
|
@@ -155,11 +155,14 @@ export class LogParser {
|
|
|
155
155
|
event.logs,
|
|
156
156
|
false
|
|
157
157
|
);
|
|
158
|
+
let runningEventIndex = 0;
|
|
158
159
|
for (const eventLog of eventGenerator) {
|
|
159
160
|
eventLog.data.txSig = event.txSig;
|
|
160
161
|
eventLog.data.slot = event.slot;
|
|
161
162
|
eventLog.data.eventType = eventLog.name;
|
|
163
|
+
eventLog.data.txSigIndex = runningEventIndex;
|
|
162
164
|
records.push(eventLog.data);
|
|
165
|
+
runningEventIndex++;
|
|
163
166
|
}
|
|
164
167
|
return records;
|
|
165
168
|
}
|
package/src/events/types.ts
CHANGED
|
@@ -64,6 +64,7 @@ export type EventSubscriptionOrderDirection = 'asc' | 'desc';
|
|
|
64
64
|
export type Event<T> = T & {
|
|
65
65
|
txSig: TransactionSignature;
|
|
66
66
|
slot: number;
|
|
67
|
+
txSigIndex: number; // Unique index for each event inside a tx
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
export type WrappedEvent<Type extends EventType> = EventMap[Type] & {
|