@drift-labs/sdk 2.74.0-beta.1 → 2.74.0-beta.3
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/blockhashSubscriber/BlockhashSubscriber.d.ts +19 -0
- package/lib/blockhashSubscriber/BlockhashSubscriber.js +69 -0
- package/lib/blockhashSubscriber/index.d.ts +1 -0
- package/lib/blockhashSubscriber/index.js +17 -0
- package/lib/blockhashSubscriber/types.d.ts +7 -0
- package/lib/blockhashSubscriber/types.js +2 -0
- package/lib/events/parse.d.ts +1 -1
- package/lib/events/parse.js +12 -12
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/tx/baseTxSender.d.ts +1 -1
- package/lib/tx/baseTxSender.js +9 -2
- package/lib/tx/fastSingleTxSender.d.ts +1 -1
- package/lib/tx/fastSingleTxSender.js +11 -3
- package/lib/tx/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +101 -0
- package/src/blockhashSubscriber/index.ts +1 -0
- package/src/blockhashSubscriber/types.ts +8 -0
- package/src/events/parse.ts +26 -12
- package/src/index.ts +1 -0
- package/src/tx/baseTxSender.ts +12 -4
- package/src/tx/fastSingleTxSender.ts +13 -5
- package/src/tx/types.ts +2 -1
- package/tests/amm/test.ts +3 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.74.0-beta.
|
|
1
|
+
2.74.0-beta.3
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BlockhashWithExpiryBlockHeight } from '@solana/web3.js';
|
|
2
|
+
import { BlockhashSubscriberConfig } from './types';
|
|
3
|
+
export declare class BlockhashSubscriber {
|
|
4
|
+
private connection;
|
|
5
|
+
private isSubscribed;
|
|
6
|
+
private latestBlockHeight;
|
|
7
|
+
private blockhashes;
|
|
8
|
+
private updateBlockhashIntervalId;
|
|
9
|
+
private commitment;
|
|
10
|
+
private updateIntervalMs;
|
|
11
|
+
constructor(config: BlockhashSubscriberConfig);
|
|
12
|
+
getBlockhashCacheSize(): number;
|
|
13
|
+
getLatestBlockHeight(): number;
|
|
14
|
+
getLatestBlockhash(offset?: number): BlockhashWithExpiryBlockHeight | undefined;
|
|
15
|
+
pruneBlockhashes(): void;
|
|
16
|
+
updateBlockhash(): Promise<void>;
|
|
17
|
+
subscribe(): Promise<void>;
|
|
18
|
+
unsubscribe(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BlockhashSubscriber = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
class BlockhashSubscriber {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
this.isSubscribed = false;
|
|
9
|
+
this.blockhashes = [];
|
|
10
|
+
if (!config.connection && !config.rpcUrl) {
|
|
11
|
+
throw new Error('BlockhashSubscriber requires one of connection or rpcUrl must be provided');
|
|
12
|
+
}
|
|
13
|
+
this.connection = config.connection || new web3_js_1.Connection(config.rpcUrl);
|
|
14
|
+
this.commitment = (_a = config.commitment) !== null && _a !== void 0 ? _a : 'confirmed';
|
|
15
|
+
this.updateIntervalMs = (_b = config.updateIntervalMs) !== null && _b !== void 0 ? _b : 1000;
|
|
16
|
+
}
|
|
17
|
+
getBlockhashCacheSize() {
|
|
18
|
+
return this.blockhashes.length;
|
|
19
|
+
}
|
|
20
|
+
getLatestBlockHeight() {
|
|
21
|
+
return this.latestBlockHeight;
|
|
22
|
+
}
|
|
23
|
+
getLatestBlockhash(offset) {
|
|
24
|
+
if (this.blockhashes.length === 0) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const clampedOffset = Math.max(0, Math.min(this.blockhashes.length - 1, offset !== null && offset !== void 0 ? offset : 0));
|
|
28
|
+
return this.blockhashes[this.blockhashes.length - 1 - clampedOffset];
|
|
29
|
+
}
|
|
30
|
+
pruneBlockhashes() {
|
|
31
|
+
if (this.latestBlockHeight) {
|
|
32
|
+
this.blockhashes = this.blockhashes.filter((blockhash) => blockhash.lastValidBlockHeight > this.latestBlockHeight);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async updateBlockhash() {
|
|
36
|
+
const [resp, lastConfirmedBlockHeight] = await Promise.all([
|
|
37
|
+
this.connection.getLatestBlockhashAndContext({
|
|
38
|
+
commitment: this.commitment,
|
|
39
|
+
}),
|
|
40
|
+
this.connection.getBlockHeight({ commitment: this.commitment }),
|
|
41
|
+
]);
|
|
42
|
+
this.latestBlockHeight = lastConfirmedBlockHeight;
|
|
43
|
+
// avoid caching duplicate blockhashes
|
|
44
|
+
if (this.blockhashes.length > 0) {
|
|
45
|
+
if (resp.value.blockhash ===
|
|
46
|
+
this.blockhashes[this.blockhashes.length - 1].blockhash) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
this.blockhashes.push(resp.value);
|
|
51
|
+
this.pruneBlockhashes();
|
|
52
|
+
}
|
|
53
|
+
async subscribe() {
|
|
54
|
+
if (this.isSubscribed) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.isSubscribed = true;
|
|
58
|
+
await this.updateBlockhash();
|
|
59
|
+
this.updateBlockhashIntervalId = setInterval(this.updateBlockhash.bind(this), this.updateIntervalMs);
|
|
60
|
+
}
|
|
61
|
+
unsubscribe() {
|
|
62
|
+
if (this.updateBlockhashIntervalId) {
|
|
63
|
+
clearInterval(this.updateBlockhashIntervalId);
|
|
64
|
+
this.updateBlockhashIntervalId = undefined;
|
|
65
|
+
}
|
|
66
|
+
this.isSubscribed = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.BlockhashSubscriber = BlockhashSubscriber;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './BlockhashSubscriber';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./BlockhashSubscriber"), exports);
|
package/lib/events/parse.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Program, Event } from '@coral-xyz/anchor';
|
|
2
|
-
export declare function parseLogs(program: Program, logs: string[]): Event[];
|
|
2
|
+
export declare function parseLogs(program: Program, logs: string[], programId?: string): Event[];
|
package/lib/events/parse.js
CHANGED
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseLogs = void 0;
|
|
4
4
|
const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
|
|
5
|
-
const driftProgramStart = `Program ${driftProgramId} invoke`;
|
|
6
5
|
const PROGRAM_LOG = 'Program log: ';
|
|
7
6
|
const PROGRAM_DATA = 'Program data: ';
|
|
8
7
|
const PROGRAM_LOG_START_INDEX = PROGRAM_LOG.length;
|
|
9
8
|
const PROGRAM_DATA_START_INDEX = PROGRAM_DATA.length;
|
|
10
|
-
function parseLogs(program, logs) {
|
|
9
|
+
function parseLogs(program, logs, programId = driftProgramId) {
|
|
11
10
|
const events = [];
|
|
12
11
|
const execution = new ExecutionContext();
|
|
13
12
|
for (const log of logs) {
|
|
14
13
|
if (log.startsWith('Log truncated')) {
|
|
15
14
|
break;
|
|
16
15
|
}
|
|
17
|
-
const [event, newProgram, didPop] = handleLog(execution, log, program);
|
|
16
|
+
const [event, newProgram, didPop] = handleLog(execution, log, program, programId);
|
|
18
17
|
if (event) {
|
|
19
18
|
events.push(event);
|
|
20
19
|
}
|
|
@@ -28,18 +27,18 @@ function parseLogs(program, logs) {
|
|
|
28
27
|
return events;
|
|
29
28
|
}
|
|
30
29
|
exports.parseLogs = parseLogs;
|
|
31
|
-
function handleLog(execution, log, program) {
|
|
30
|
+
function handleLog(execution, log, program, programId = driftProgramId) {
|
|
32
31
|
// Executing program is drift program.
|
|
33
|
-
if (execution.stack.length > 0 && execution.program() ===
|
|
34
|
-
return handleProgramLog(log, program);
|
|
32
|
+
if (execution.stack.length > 0 && execution.program() === programId) {
|
|
33
|
+
return handleProgramLog(log, program, programId);
|
|
35
34
|
}
|
|
36
35
|
// Executing program is not drift program.
|
|
37
36
|
else {
|
|
38
|
-
return [null, ...handleSystemLog(log)];
|
|
37
|
+
return [null, ...handleSystemLog(log, programId)];
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
// Handles logs from *drift* program.
|
|
42
|
-
function handleProgramLog(log, program) {
|
|
41
|
+
function handleProgramLog(log, program, programId = driftProgramId) {
|
|
43
42
|
// This is a `msg!` log or a `sol_log_data` log.
|
|
44
43
|
if (log.startsWith(PROGRAM_LOG)) {
|
|
45
44
|
const logStr = log.slice(PROGRAM_LOG_START_INDEX);
|
|
@@ -52,20 +51,21 @@ function handleProgramLog(log, program) {
|
|
|
52
51
|
return [event, null, false];
|
|
53
52
|
}
|
|
54
53
|
else {
|
|
55
|
-
return [null, ...handleSystemLog(log)];
|
|
54
|
+
return [null, ...handleSystemLog(log, programId)];
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
// Handles logs when the current program being executing is *not* drift.
|
|
59
|
-
function handleSystemLog(log) {
|
|
58
|
+
function handleSystemLog(log, programId = driftProgramId) {
|
|
60
59
|
// System component.
|
|
61
60
|
const logStart = log.split(':')[0];
|
|
61
|
+
const programStart = `Program ${programId} invoke`;
|
|
62
62
|
// Did the program finish executing?
|
|
63
63
|
if (logStart.match(/^Program (.*) success/g) !== null) {
|
|
64
64
|
return [null, true];
|
|
65
65
|
// Recursive call.
|
|
66
66
|
}
|
|
67
|
-
else if (logStart.startsWith(
|
|
68
|
-
return [
|
|
67
|
+
else if (logStart.startsWith(programStart)) {
|
|
68
|
+
return [programId, false];
|
|
69
69
|
}
|
|
70
70
|
// CPI call.
|
|
71
71
|
else if (logStart.includes('invoke')) {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -121,3 +121,4 @@ __exportStar(require("./auctionSubscriber"), exports);
|
|
|
121
121
|
__exportStar(require("./auctionSubscriber/types"), exports);
|
|
122
122
|
__exportStar(require("./memcmp"), exports);
|
|
123
123
|
__exportStar(require("./decode/user"), exports);
|
|
124
|
+
__exportStar(require("./blockhashSubscriber"), exports);
|
package/lib/tx/baseTxSender.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare abstract class BaseTxSender implements TxSender {
|
|
|
22
22
|
});
|
|
23
23
|
send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean, extraConfirmationOptions?: ExtraConfirmationOptions): Promise<TxSigAndSlot>;
|
|
24
24
|
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
|
|
25
|
-
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
25
|
+
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions, blockhash?: string): Promise<VersionedTransaction>;
|
|
26
26
|
sendVersionedTransaction(tx: VersionedTransaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean, extraConfirmationOptions?: ExtraConfirmationOptions): Promise<TxSigAndSlot>;
|
|
27
27
|
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
28
28
|
simulateTransaction(tx: VersionedTransaction): Promise<boolean>;
|
package/lib/tx/baseTxSender.js
CHANGED
|
@@ -47,16 +47,23 @@ class BaseTxSender {
|
|
|
47
47
|
const signedTx = await this.wallet.signTransaction(tx);
|
|
48
48
|
return signedTx;
|
|
49
49
|
}
|
|
50
|
-
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
|
|
50
|
+
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts, blockhash) {
|
|
51
51
|
if (additionalSigners === undefined) {
|
|
52
52
|
additionalSigners = [];
|
|
53
53
|
}
|
|
54
54
|
if (opts === undefined) {
|
|
55
55
|
opts = this.opts;
|
|
56
56
|
}
|
|
57
|
+
let recentBlockhash = '';
|
|
58
|
+
if (blockhash) {
|
|
59
|
+
recentBlockhash = blockhash;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
recentBlockhash = (await this.connection.getLatestBlockhash(opts.preflightCommitment)).blockhash;
|
|
63
|
+
}
|
|
57
64
|
const message = new web3_js_1.TransactionMessage({
|
|
58
65
|
payerKey: this.wallet.publicKey,
|
|
59
|
-
recentBlockhash
|
|
66
|
+
recentBlockhash,
|
|
60
67
|
instructions: ixs,
|
|
61
68
|
}).compileToV0Message(lookupTableAccounts);
|
|
62
69
|
const tx = new web3_js_1.VersionedTransaction(message);
|
|
@@ -27,6 +27,6 @@ export declare class FastSingleTxSender extends BaseTxSender {
|
|
|
27
27
|
});
|
|
28
28
|
startBlockhashRefreshLoop(): void;
|
|
29
29
|
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, _opts: ConfirmOptions): Promise<Transaction>;
|
|
30
|
-
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
30
|
+
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions, blockhash?: string): Promise<VersionedTransaction>;
|
|
31
31
|
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
32
32
|
}
|
|
@@ -52,7 +52,7 @@ class FastSingleTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
52
52
|
const signedTx = await this.wallet.signTransaction(tx);
|
|
53
53
|
return signedTx;
|
|
54
54
|
}
|
|
55
|
-
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
|
|
55
|
+
async getVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts, blockhash) {
|
|
56
56
|
var _a;
|
|
57
57
|
if (additionalSigners === undefined) {
|
|
58
58
|
additionalSigners = [];
|
|
@@ -60,10 +60,18 @@ class FastSingleTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
60
60
|
if (opts === undefined) {
|
|
61
61
|
opts = this.opts;
|
|
62
62
|
}
|
|
63
|
+
let recentBlockhash = '';
|
|
64
|
+
if (blockhash) {
|
|
65
|
+
recentBlockhash = blockhash;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
recentBlockhash =
|
|
69
|
+
(_a = this.recentBlockhash) !== null && _a !== void 0 ? _a : (await this.connection.getLatestBlockhash(opts.preflightCommitment))
|
|
70
|
+
.blockhash;
|
|
71
|
+
}
|
|
63
72
|
const message = new web3_js_1.TransactionMessage({
|
|
64
73
|
payerKey: this.wallet.publicKey,
|
|
65
|
-
recentBlockhash
|
|
66
|
-
.blockhash,
|
|
74
|
+
recentBlockhash,
|
|
67
75
|
instructions: ixs,
|
|
68
76
|
}).compileToV0Message(lookupTableAccounts);
|
|
69
77
|
const tx = new web3_js_1.VersionedTransaction(message);
|
package/lib/tx/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface TxSender {
|
|
|
17
17
|
wallet: IWallet;
|
|
18
18
|
send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean, extraConfirmationOptions?: ExtraConfirmationOptions): Promise<TxSigAndSlot>;
|
|
19
19
|
sendVersionedTransaction(tx: VersionedTransaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean, extraConfirmationOptions?: ExtraConfirmationOptions): Promise<TxSigAndSlot>;
|
|
20
|
-
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<VersionedTransaction>;
|
|
20
|
+
getVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions, blockhash?: string): Promise<VersionedTransaction>;
|
|
21
21
|
sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
|
|
22
22
|
simulateTransaction(tx: VersionedTransaction): Promise<boolean>;
|
|
23
23
|
getTimeoutCount(): number;
|
package/package.json
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BlockhashWithExpiryBlockHeight,
|
|
3
|
+
Commitment,
|
|
4
|
+
Connection,
|
|
5
|
+
} from '@solana/web3.js';
|
|
6
|
+
import { BlockhashSubscriberConfig } from './types';
|
|
7
|
+
|
|
8
|
+
export class BlockhashSubscriber {
|
|
9
|
+
private connection: Connection;
|
|
10
|
+
private isSubscribed = false;
|
|
11
|
+
private latestBlockHeight: number;
|
|
12
|
+
private blockhashes: Array<BlockhashWithExpiryBlockHeight> = [];
|
|
13
|
+
private updateBlockhashIntervalId: NodeJS.Timeout | undefined;
|
|
14
|
+
private commitment: Commitment;
|
|
15
|
+
private updateIntervalMs: number;
|
|
16
|
+
|
|
17
|
+
constructor(config: BlockhashSubscriberConfig) {
|
|
18
|
+
if (!config.connection && !config.rpcUrl) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'BlockhashSubscriber requires one of connection or rpcUrl must be provided'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
this.connection = config.connection || new Connection(config.rpcUrl!);
|
|
24
|
+
this.commitment = config.commitment ?? 'confirmed';
|
|
25
|
+
this.updateIntervalMs = config.updateIntervalMs ?? 1000;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getBlockhashCacheSize(): number {
|
|
29
|
+
return this.blockhashes.length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getLatestBlockHeight(): number {
|
|
33
|
+
return this.latestBlockHeight;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getLatestBlockhash(
|
|
37
|
+
offset?: number
|
|
38
|
+
): BlockhashWithExpiryBlockHeight | undefined {
|
|
39
|
+
if (this.blockhashes.length === 0) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const clampedOffset = Math.max(
|
|
43
|
+
0,
|
|
44
|
+
Math.min(this.blockhashes.length - 1, offset ?? 0)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return this.blockhashes[this.blockhashes.length - 1 - clampedOffset];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pruneBlockhashes() {
|
|
51
|
+
if (this.latestBlockHeight) {
|
|
52
|
+
this.blockhashes = this.blockhashes.filter(
|
|
53
|
+
(blockhash) => blockhash.lastValidBlockHeight > this.latestBlockHeight!
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async updateBlockhash() {
|
|
59
|
+
const [resp, lastConfirmedBlockHeight] = await Promise.all([
|
|
60
|
+
this.connection.getLatestBlockhashAndContext({
|
|
61
|
+
commitment: this.commitment,
|
|
62
|
+
}),
|
|
63
|
+
this.connection.getBlockHeight({ commitment: this.commitment }),
|
|
64
|
+
]);
|
|
65
|
+
this.latestBlockHeight = lastConfirmedBlockHeight;
|
|
66
|
+
|
|
67
|
+
// avoid caching duplicate blockhashes
|
|
68
|
+
if (this.blockhashes.length > 0) {
|
|
69
|
+
if (
|
|
70
|
+
resp.value.blockhash ===
|
|
71
|
+
this.blockhashes[this.blockhashes.length - 1].blockhash
|
|
72
|
+
) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.blockhashes.push(resp.value);
|
|
78
|
+
this.pruneBlockhashes();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async subscribe() {
|
|
82
|
+
if (this.isSubscribed) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.isSubscribed = true;
|
|
86
|
+
|
|
87
|
+
await this.updateBlockhash();
|
|
88
|
+
this.updateBlockhashIntervalId = setInterval(
|
|
89
|
+
this.updateBlockhash.bind(this),
|
|
90
|
+
this.updateIntervalMs
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
unsubscribe() {
|
|
95
|
+
if (this.updateBlockhashIntervalId) {
|
|
96
|
+
clearInterval(this.updateBlockhashIntervalId);
|
|
97
|
+
this.updateBlockhashIntervalId = undefined;
|
|
98
|
+
}
|
|
99
|
+
this.isSubscribed = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './BlockhashSubscriber';
|
package/src/events/parse.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Program, Event } from '@coral-xyz/anchor';
|
|
2
2
|
|
|
3
3
|
const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
|
|
4
|
-
const driftProgramStart = `Program ${driftProgramId} invoke`;
|
|
5
4
|
const PROGRAM_LOG = 'Program log: ';
|
|
6
5
|
const PROGRAM_DATA = 'Program data: ';
|
|
7
6
|
const PROGRAM_LOG_START_INDEX = PROGRAM_LOG.length;
|
|
8
7
|
const PROGRAM_DATA_START_INDEX = PROGRAM_DATA.length;
|
|
9
8
|
|
|
10
|
-
export function parseLogs(
|
|
9
|
+
export function parseLogs(
|
|
10
|
+
program: Program,
|
|
11
|
+
logs: string[],
|
|
12
|
+
programId = driftProgramId
|
|
13
|
+
): Event[] {
|
|
11
14
|
const events = [];
|
|
12
15
|
const execution = new ExecutionContext();
|
|
13
16
|
for (const log of logs) {
|
|
@@ -15,7 +18,12 @@ export function parseLogs(program: Program, logs: string[]): Event[] {
|
|
|
15
18
|
break;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
const [event, newProgram, didPop] = handleLog(
|
|
21
|
+
const [event, newProgram, didPop] = handleLog(
|
|
22
|
+
execution,
|
|
23
|
+
log,
|
|
24
|
+
program,
|
|
25
|
+
programId
|
|
26
|
+
);
|
|
19
27
|
if (event) {
|
|
20
28
|
events.push(event);
|
|
21
29
|
}
|
|
@@ -32,22 +40,24 @@ export function parseLogs(program: Program, logs: string[]): Event[] {
|
|
|
32
40
|
function handleLog(
|
|
33
41
|
execution: ExecutionContext,
|
|
34
42
|
log: string,
|
|
35
|
-
program: Program
|
|
43
|
+
program: Program,
|
|
44
|
+
programId = driftProgramId
|
|
36
45
|
): [Event | null, string | null, boolean] {
|
|
37
46
|
// Executing program is drift program.
|
|
38
|
-
if (execution.stack.length > 0 && execution.program() ===
|
|
39
|
-
return handleProgramLog(log, program);
|
|
47
|
+
if (execution.stack.length > 0 && execution.program() === programId) {
|
|
48
|
+
return handleProgramLog(log, program, programId);
|
|
40
49
|
}
|
|
41
50
|
// Executing program is not drift program.
|
|
42
51
|
else {
|
|
43
|
-
return [null, ...handleSystemLog(log)];
|
|
52
|
+
return [null, ...handleSystemLog(log, programId)];
|
|
44
53
|
}
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
// Handles logs from *drift* program.
|
|
48
57
|
function handleProgramLog(
|
|
49
58
|
log: string,
|
|
50
|
-
program: Program
|
|
59
|
+
program: Program,
|
|
60
|
+
programId = driftProgramId
|
|
51
61
|
): [Event | null, string | null, boolean] {
|
|
52
62
|
// This is a `msg!` log or a `sol_log_data` log.
|
|
53
63
|
if (log.startsWith(PROGRAM_LOG)) {
|
|
@@ -59,21 +69,25 @@ function handleProgramLog(
|
|
|
59
69
|
const event = program.coder.events.decode(logStr);
|
|
60
70
|
return [event, null, false];
|
|
61
71
|
} else {
|
|
62
|
-
return [null, ...handleSystemLog(log)];
|
|
72
|
+
return [null, ...handleSystemLog(log, programId)];
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
// Handles logs when the current program being executing is *not* drift.
|
|
67
|
-
function handleSystemLog(
|
|
77
|
+
function handleSystemLog(
|
|
78
|
+
log: string,
|
|
79
|
+
programId = driftProgramId
|
|
80
|
+
): [string | null, boolean] {
|
|
68
81
|
// System component.
|
|
69
82
|
const logStart = log.split(':')[0];
|
|
83
|
+
const programStart = `Program ${programId} invoke`;
|
|
70
84
|
|
|
71
85
|
// Did the program finish executing?
|
|
72
86
|
if (logStart.match(/^Program (.*) success/g) !== null) {
|
|
73
87
|
return [null, true];
|
|
74
88
|
// Recursive call.
|
|
75
|
-
} else if (logStart.startsWith(
|
|
76
|
-
return [
|
|
89
|
+
} else if (logStart.startsWith(programStart)) {
|
|
90
|
+
return [programId, false];
|
|
77
91
|
}
|
|
78
92
|
// CPI call.
|
|
79
93
|
else if (logStart.includes('invoke')) {
|
package/src/index.ts
CHANGED
package/src/tx/baseTxSender.ts
CHANGED
|
@@ -112,7 +112,8 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
112
112
|
ixs: TransactionInstruction[],
|
|
113
113
|
lookupTableAccounts: AddressLookupTableAccount[],
|
|
114
114
|
additionalSigners?: Array<Signer>,
|
|
115
|
-
opts?: ConfirmOptions
|
|
115
|
+
opts?: ConfirmOptions,
|
|
116
|
+
blockhash?: string
|
|
116
117
|
): Promise<VersionedTransaction> {
|
|
117
118
|
if (additionalSigners === undefined) {
|
|
118
119
|
additionalSigners = [];
|
|
@@ -121,11 +122,18 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
121
122
|
opts = this.opts;
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
let recentBlockhash = '';
|
|
126
|
+
if (blockhash) {
|
|
127
|
+
recentBlockhash = blockhash;
|
|
128
|
+
} else {
|
|
129
|
+
recentBlockhash = (
|
|
130
|
+
await this.connection.getLatestBlockhash(opts.preflightCommitment)
|
|
131
|
+
).blockhash;
|
|
132
|
+
}
|
|
133
|
+
|
|
124
134
|
const message = new TransactionMessage({
|
|
125
135
|
payerKey: this.wallet.publicKey,
|
|
126
|
-
recentBlockhash
|
|
127
|
-
await this.connection.getLatestBlockhash(opts.preflightCommitment)
|
|
128
|
-
).blockhash,
|
|
136
|
+
recentBlockhash,
|
|
129
137
|
instructions: ixs,
|
|
130
138
|
}).compileToV0Message(lookupTableAccounts);
|
|
131
139
|
|
|
@@ -109,7 +109,8 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
109
109
|
ixs: TransactionInstruction[],
|
|
110
110
|
lookupTableAccounts: AddressLookupTableAccount[],
|
|
111
111
|
additionalSigners?: Array<Signer>,
|
|
112
|
-
opts?: ConfirmOptions
|
|
112
|
+
opts?: ConfirmOptions,
|
|
113
|
+
blockhash?: string
|
|
113
114
|
): Promise<VersionedTransaction> {
|
|
114
115
|
if (additionalSigners === undefined) {
|
|
115
116
|
additionalSigners = [];
|
|
@@ -118,12 +119,19 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
118
119
|
opts = this.opts;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
recentBlockhash
|
|
122
|
+
let recentBlockhash = '';
|
|
123
|
+
if (blockhash) {
|
|
124
|
+
recentBlockhash = blockhash;
|
|
125
|
+
} else {
|
|
126
|
+
recentBlockhash =
|
|
124
127
|
this.recentBlockhash ??
|
|
125
128
|
(await this.connection.getLatestBlockhash(opts.preflightCommitment))
|
|
126
|
-
.blockhash
|
|
129
|
+
.blockhash;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const message = new TransactionMessage({
|
|
133
|
+
payerKey: this.wallet.publicKey,
|
|
134
|
+
recentBlockhash,
|
|
127
135
|
instructions: ixs,
|
|
128
136
|
}).compileToV0Message(lookupTableAccounts);
|
|
129
137
|
|
package/src/tx/types.ts
CHANGED
|
@@ -47,7 +47,8 @@ export interface TxSender {
|
|
|
47
47
|
ixs: TransactionInstruction[],
|
|
48
48
|
lookupTableAccounts: AddressLookupTableAccount[],
|
|
49
49
|
additionalSigners?: Array<Signer>,
|
|
50
|
-
opts?: ConfirmOptions
|
|
50
|
+
opts?: ConfirmOptions,
|
|
51
|
+
blockhash?: string
|
|
51
52
|
): Promise<VersionedTransaction>;
|
|
52
53
|
|
|
53
54
|
sendRawTransaction(
|
package/tests/amm/test.ts
CHANGED
|
@@ -1193,7 +1193,9 @@ describe('AMM Tests', () => {
|
|
|
1193
1193
|
};
|
|
1194
1194
|
|
|
1195
1195
|
// good oracle
|
|
1196
|
-
assert(
|
|
1196
|
+
assert(
|
|
1197
|
+
isOracleValid(mockMarket1, oraclePriceData, oracleGuardRails, slot + 5)
|
|
1198
|
+
);
|
|
1197
1199
|
|
|
1198
1200
|
// conf too high
|
|
1199
1201
|
assert(
|