@drift-labs/sdk 2.34.1-beta.5 → 2.34.1-beta.7
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 +4 -2
- package/lib/events/fetchLogs.d.ts +1 -0
- package/lib/events/fetchLogs.js +32 -11
- package/lib/events/parse.d.ts +2 -0
- package/lib/events/parse.js +96 -0
- package/package.json +1 -1
- package/src/events/eventSubscriber.ts +4 -5
- package/src/events/fetchLogs.ts +43 -12
- package/src/events/parse.ts +108 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.34.1-beta.
|
|
1
|
+
2.34.1-beta.7
|
|
@@ -9,6 +9,7 @@ const fetchLogs_1 = require("./fetchLogs");
|
|
|
9
9
|
const webSocketLogProvider_1 = require("./webSocketLogProvider");
|
|
10
10
|
const events_1 = require("events");
|
|
11
11
|
const sort_1 = require("./sort");
|
|
12
|
+
const parse_1 = require("./parse");
|
|
12
13
|
class EventSubscriber {
|
|
13
14
|
constructor(connection, program, options = types_1.DefaultEventSubscriptionOptions) {
|
|
14
15
|
var _a;
|
|
@@ -99,8 +100,9 @@ class EventSubscriber {
|
|
|
99
100
|
parseEventsFromLogs(txSig, slot, logs) {
|
|
100
101
|
const records = [];
|
|
101
102
|
// @ts-ignore
|
|
102
|
-
const
|
|
103
|
-
for (const event of
|
|
103
|
+
const events = (0, parse_1.parseLogs)(this.program, slot, logs);
|
|
104
|
+
for (const event of events) {
|
|
105
|
+
// @ts-ignore
|
|
104
106
|
const expectRecordType = this.eventListMap.has(event.name);
|
|
105
107
|
if (expectRecordType) {
|
|
106
108
|
event.data.txSig = txSig;
|
|
@@ -15,6 +15,7 @@ type FetchLogsResponse = {
|
|
|
15
15
|
mostRecentBlockTime: number | undefined;
|
|
16
16
|
};
|
|
17
17
|
export declare function fetchLogs(connection: Connection, address: PublicKey, finality: Finality, beforeTx?: TransactionSignature, untilTx?: TransactionSignature, limit?: number): Promise<FetchLogsResponse>;
|
|
18
|
+
export declare function fetchTransactionLogs(connection: Connection, signatures: TransactionSignature[], finality: Finality): Promise<Log[]>;
|
|
18
19
|
export declare class LogParser {
|
|
19
20
|
private program;
|
|
20
21
|
constructor(program: Program);
|
package/lib/events/fetchLogs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LogParser = exports.fetchLogs = void 0;
|
|
3
|
+
exports.LogParser = exports.fetchTransactionLogs = exports.fetchLogs = void 0;
|
|
4
|
+
const promiseTimeout_1 = require("../util/promiseTimeout");
|
|
4
5
|
function mapTransactionResponseToLog(transaction) {
|
|
5
6
|
return {
|
|
6
7
|
txSig: transaction.transaction.signatures[0],
|
|
@@ -20,17 +21,8 @@ async function fetchLogs(connection, address, finality, beforeTx, untilTx, limit
|
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
22
23
|
const chunkedSignatures = chunk(filteredSignatures, 100);
|
|
23
|
-
const config = { commitment: finality, maxSupportedTransactionVersion: 0 };
|
|
24
24
|
const transactionLogs = (await Promise.all(chunkedSignatures.map(async (chunk) => {
|
|
25
|
-
|
|
26
|
-
//@ts-ignore
|
|
27
|
-
config);
|
|
28
|
-
return transactions.reduce((logs, transaction) => {
|
|
29
|
-
if (transaction) {
|
|
30
|
-
logs.push(mapTransactionResponseToLog(transaction));
|
|
31
|
-
}
|
|
32
|
-
return logs;
|
|
33
|
-
}, new Array());
|
|
25
|
+
return await fetchTransactionLogs(connection, chunk.map((confirmedSignature) => confirmedSignature.signature), finality);
|
|
34
26
|
}))).flat();
|
|
35
27
|
const earliest = filteredSignatures[0];
|
|
36
28
|
const mostRecent = filteredSignatures[filteredSignatures.length - 1];
|
|
@@ -44,6 +36,35 @@ async function fetchLogs(connection, address, finality, beforeTx, untilTx, limit
|
|
|
44
36
|
};
|
|
45
37
|
}
|
|
46
38
|
exports.fetchLogs = fetchLogs;
|
|
39
|
+
async function fetchTransactionLogs(connection, signatures, finality) {
|
|
40
|
+
const requests = new Array();
|
|
41
|
+
for (const signature of signatures) {
|
|
42
|
+
const args = [
|
|
43
|
+
signature,
|
|
44
|
+
{ commitment: finality, maxSupportedTransactionVersion: 0 },
|
|
45
|
+
];
|
|
46
|
+
requests.push({
|
|
47
|
+
methodName: 'getTransaction',
|
|
48
|
+
args,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const rpcResponses = await (0, promiseTimeout_1.promiseTimeout)(
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
connection._rpcBatchRequest(requests), 10 * 1000 // 10 second timeout
|
|
54
|
+
);
|
|
55
|
+
if (rpcResponses === null) {
|
|
56
|
+
return Promise.reject('RPC request timed out fetching transactions');
|
|
57
|
+
}
|
|
58
|
+
const logs = new Array();
|
|
59
|
+
for (const i in rpcResponses) {
|
|
60
|
+
const rpcResponse = rpcResponses[i];
|
|
61
|
+
if (rpcResponse.result) {
|
|
62
|
+
logs.push(mapTransactionResponseToLog(rpcResponse.result));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return logs;
|
|
66
|
+
}
|
|
67
|
+
exports.fetchTransactionLogs = fetchTransactionLogs;
|
|
47
68
|
function chunk(array, size) {
|
|
48
69
|
return new Array(Math.ceil(array.length / size))
|
|
49
70
|
.fill(null)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseLogs = void 0;
|
|
4
|
+
const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
|
|
5
|
+
const driftProgramStart = `Program ${driftProgramId} invoke`;
|
|
6
|
+
const PROGRAM_LOG = 'Program log: ';
|
|
7
|
+
const PROGRAM_DATA = 'Program data: ';
|
|
8
|
+
const PROGRAM_LOG_START_INDEX = PROGRAM_LOG.length;
|
|
9
|
+
const PROGRAM_DATA_START_INDEX = PROGRAM_DATA.length;
|
|
10
|
+
function parseLogs(program, slot, logs) {
|
|
11
|
+
const events = [];
|
|
12
|
+
const execution = new ExecutionContext();
|
|
13
|
+
for (const log of logs) {
|
|
14
|
+
const [event, newProgram, didPop] = handleLog(execution, log, program);
|
|
15
|
+
if (event) {
|
|
16
|
+
events.push(event);
|
|
17
|
+
}
|
|
18
|
+
if (newProgram) {
|
|
19
|
+
execution.push(newProgram);
|
|
20
|
+
}
|
|
21
|
+
if (didPop) {
|
|
22
|
+
execution.pop();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return events;
|
|
26
|
+
}
|
|
27
|
+
exports.parseLogs = parseLogs;
|
|
28
|
+
function handleLog(execution, log, program) {
|
|
29
|
+
// Executing program is drift program.
|
|
30
|
+
if (execution.stack.length > 0 && execution.program() === driftProgramId) {
|
|
31
|
+
return handleProgramLog(log, program);
|
|
32
|
+
}
|
|
33
|
+
// Executing program is not drift program.
|
|
34
|
+
else {
|
|
35
|
+
return [null, ...handleSystemLog(log)];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Handles logs from *drift* program.
|
|
39
|
+
function handleProgramLog(log, program) {
|
|
40
|
+
// This is a `msg!` log or a `sol_log_data` log.
|
|
41
|
+
if (log.startsWith(PROGRAM_LOG)) {
|
|
42
|
+
const logStr = log.slice(PROGRAM_LOG_START_INDEX);
|
|
43
|
+
const event = program.coder.events.decode(logStr);
|
|
44
|
+
return [event, null, false];
|
|
45
|
+
}
|
|
46
|
+
else if (log.startsWith(PROGRAM_DATA)) {
|
|
47
|
+
const logStr = log.slice(PROGRAM_DATA_START_INDEX);
|
|
48
|
+
const event = program.coder.events.decode(logStr);
|
|
49
|
+
return [event, null, false];
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return [null, ...handleSystemLog(log)];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Handles logs when the current program being executing is *not* drift.
|
|
56
|
+
function handleSystemLog(log) {
|
|
57
|
+
// System component.
|
|
58
|
+
const logStart = log.split(':')[0];
|
|
59
|
+
// Did the program finish executing?
|
|
60
|
+
if (logStart.match(/^Program (.*) success/g) !== null) {
|
|
61
|
+
return [null, true];
|
|
62
|
+
// Recursive call.
|
|
63
|
+
}
|
|
64
|
+
else if (logStart.startsWith(driftProgramStart)) {
|
|
65
|
+
return [driftProgramId, false];
|
|
66
|
+
}
|
|
67
|
+
// CPI call.
|
|
68
|
+
else if (logStart.includes('invoke')) {
|
|
69
|
+
return ['cpi', false]; // Any string will do.
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return [null, false];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Stack frame execution context, allowing one to track what program is
|
|
76
|
+
// executing for a given log.
|
|
77
|
+
class ExecutionContext {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.stack = [];
|
|
80
|
+
}
|
|
81
|
+
program() {
|
|
82
|
+
if (!this.stack.length) {
|
|
83
|
+
throw new Error('Expected the stack to have elements');
|
|
84
|
+
}
|
|
85
|
+
return this.stack[this.stack.length - 1];
|
|
86
|
+
}
|
|
87
|
+
push(newProgram) {
|
|
88
|
+
this.stack.push(newProgram);
|
|
89
|
+
}
|
|
90
|
+
pop() {
|
|
91
|
+
if (!this.stack.length) {
|
|
92
|
+
throw new Error('Expected the stack to have elements');
|
|
93
|
+
}
|
|
94
|
+
this.stack.pop();
|
|
95
|
+
}
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ import { WebSocketLogProvider } from './webSocketLogProvider';
|
|
|
17
17
|
import { EventEmitter } from 'events';
|
|
18
18
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
19
19
|
import { getSortFn } from './sort';
|
|
20
|
+
import { parseLogs } from './parse';
|
|
20
21
|
|
|
21
22
|
export class EventSubscriber {
|
|
22
23
|
private address: PublicKey;
|
|
@@ -166,11 +167,9 @@ export class EventSubscriber {
|
|
|
166
167
|
): WrappedEvents {
|
|
167
168
|
const records = [];
|
|
168
169
|
// @ts-ignore
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
);
|
|
173
|
-
for (const event of eventGenerator) {
|
|
170
|
+
const events = parseLogs(this.program, slot, logs);
|
|
171
|
+
for (const event of events) {
|
|
172
|
+
// @ts-ignore
|
|
174
173
|
const expectRecordType = this.eventListMap.has(event.name);
|
|
175
174
|
if (expectRecordType) {
|
|
176
175
|
event.data.txSig = txSig;
|
package/src/events/fetchLogs.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
VersionedTransactionResponse,
|
|
9
9
|
} from '@solana/web3.js';
|
|
10
10
|
import { WrappedEvents } from './types';
|
|
11
|
+
import { promiseTimeout } from '../util/promiseTimeout';
|
|
11
12
|
|
|
12
13
|
type Log = { txSig: TransactionSignature; slot: number; logs: string[] };
|
|
13
14
|
type FetchLogsResponse = {
|
|
@@ -61,23 +62,14 @@ export async function fetchLogs(
|
|
|
61
62
|
|
|
62
63
|
const chunkedSignatures = chunk(filteredSignatures, 100);
|
|
63
64
|
|
|
64
|
-
const config = { commitment: finality, maxSupportedTransactionVersion: 0 };
|
|
65
|
-
|
|
66
65
|
const transactionLogs = (
|
|
67
66
|
await Promise.all(
|
|
68
67
|
chunkedSignatures.map(async (chunk) => {
|
|
69
|
-
|
|
68
|
+
return await fetchTransactionLogs(
|
|
69
|
+
connection,
|
|
70
70
|
chunk.map((confirmedSignature) => confirmedSignature.signature),
|
|
71
|
-
|
|
72
|
-
config
|
|
71
|
+
finality
|
|
73
72
|
);
|
|
74
|
-
|
|
75
|
-
return transactions.reduce((logs, transaction) => {
|
|
76
|
-
if (transaction) {
|
|
77
|
-
logs.push(mapTransactionResponseToLog(transaction));
|
|
78
|
-
}
|
|
79
|
-
return logs;
|
|
80
|
-
}, new Array<Log>());
|
|
81
73
|
})
|
|
82
74
|
)
|
|
83
75
|
).flat();
|
|
@@ -95,6 +87,45 @@ export async function fetchLogs(
|
|
|
95
87
|
};
|
|
96
88
|
}
|
|
97
89
|
|
|
90
|
+
export async function fetchTransactionLogs(
|
|
91
|
+
connection: Connection,
|
|
92
|
+
signatures: TransactionSignature[],
|
|
93
|
+
finality: Finality
|
|
94
|
+
): Promise<Log[]> {
|
|
95
|
+
const requests = new Array<{ methodName: string; args: any }>();
|
|
96
|
+
for (const signature of signatures) {
|
|
97
|
+
const args = [
|
|
98
|
+
signature,
|
|
99
|
+
{ commitment: finality, maxSupportedTransactionVersion: 0 },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
requests.push({
|
|
103
|
+
methodName: 'getTransaction',
|
|
104
|
+
args,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const rpcResponses: any | null = await promiseTimeout(
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
connection._rpcBatchRequest(requests),
|
|
111
|
+
10 * 1000 // 10 second timeout
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (rpcResponses === null) {
|
|
115
|
+
return Promise.reject('RPC request timed out fetching transactions');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const logs = new Array<Log>();
|
|
119
|
+
for (const i in rpcResponses) {
|
|
120
|
+
const rpcResponse = rpcResponses[i];
|
|
121
|
+
if (rpcResponse.result) {
|
|
122
|
+
logs.push(mapTransactionResponseToLog(rpcResponse.result));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return logs;
|
|
127
|
+
}
|
|
128
|
+
|
|
98
129
|
function chunk<T>(array: readonly T[], size: number): T[][] {
|
|
99
130
|
return new Array(Math.ceil(array.length / size))
|
|
100
131
|
.fill(null)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Program, Event } from '@coral-xyz/anchor';
|
|
2
|
+
|
|
3
|
+
const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
|
|
4
|
+
const driftProgramStart = `Program ${driftProgramId} invoke`;
|
|
5
|
+
const PROGRAM_LOG = 'Program log: ';
|
|
6
|
+
const PROGRAM_DATA = 'Program data: ';
|
|
7
|
+
const PROGRAM_LOG_START_INDEX = PROGRAM_LOG.length;
|
|
8
|
+
const PROGRAM_DATA_START_INDEX = PROGRAM_DATA.length;
|
|
9
|
+
|
|
10
|
+
export function parseLogs(
|
|
11
|
+
program: Program,
|
|
12
|
+
slot: number,
|
|
13
|
+
logs: string[]
|
|
14
|
+
): Event[] {
|
|
15
|
+
const events = [];
|
|
16
|
+
const execution = new ExecutionContext();
|
|
17
|
+
for (const log of logs) {
|
|
18
|
+
const [event, newProgram, didPop] = handleLog(execution, log, program);
|
|
19
|
+
if (event) {
|
|
20
|
+
events.push(event);
|
|
21
|
+
}
|
|
22
|
+
if (newProgram) {
|
|
23
|
+
execution.push(newProgram);
|
|
24
|
+
}
|
|
25
|
+
if (didPop) {
|
|
26
|
+
execution.pop();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return events;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleLog(
|
|
33
|
+
execution: ExecutionContext,
|
|
34
|
+
log: string,
|
|
35
|
+
program: Program
|
|
36
|
+
): [Event | null, string | null, boolean] {
|
|
37
|
+
// Executing program is drift program.
|
|
38
|
+
if (execution.stack.length > 0 && execution.program() === driftProgramId) {
|
|
39
|
+
return handleProgramLog(log, program);
|
|
40
|
+
}
|
|
41
|
+
// Executing program is not drift program.
|
|
42
|
+
else {
|
|
43
|
+
return [null, ...handleSystemLog(log)];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Handles logs from *drift* program.
|
|
48
|
+
function handleProgramLog(
|
|
49
|
+
log: string,
|
|
50
|
+
program: Program
|
|
51
|
+
): [Event | null, string | null, boolean] {
|
|
52
|
+
// This is a `msg!` log or a `sol_log_data` log.
|
|
53
|
+
if (log.startsWith(PROGRAM_LOG)) {
|
|
54
|
+
const logStr = log.slice(PROGRAM_LOG_START_INDEX);
|
|
55
|
+
const event = program.coder.events.decode(logStr);
|
|
56
|
+
return [event, null, false];
|
|
57
|
+
} else if (log.startsWith(PROGRAM_DATA)) {
|
|
58
|
+
const logStr = log.slice(PROGRAM_DATA_START_INDEX);
|
|
59
|
+
const event = program.coder.events.decode(logStr);
|
|
60
|
+
return [event, null, false];
|
|
61
|
+
} else {
|
|
62
|
+
return [null, ...handleSystemLog(log)];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handles logs when the current program being executing is *not* drift.
|
|
67
|
+
function handleSystemLog(log: string): [string | null, boolean] {
|
|
68
|
+
// System component.
|
|
69
|
+
const logStart = log.split(':')[0];
|
|
70
|
+
|
|
71
|
+
// Did the program finish executing?
|
|
72
|
+
if (logStart.match(/^Program (.*) success/g) !== null) {
|
|
73
|
+
return [null, true];
|
|
74
|
+
// Recursive call.
|
|
75
|
+
} else if (logStart.startsWith(driftProgramStart)) {
|
|
76
|
+
return [driftProgramId, false];
|
|
77
|
+
}
|
|
78
|
+
// CPI call.
|
|
79
|
+
else if (logStart.includes('invoke')) {
|
|
80
|
+
return ['cpi', false]; // Any string will do.
|
|
81
|
+
} else {
|
|
82
|
+
return [null, false];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Stack frame execution context, allowing one to track what program is
|
|
87
|
+
// executing for a given log.
|
|
88
|
+
class ExecutionContext {
|
|
89
|
+
stack: string[] = [];
|
|
90
|
+
|
|
91
|
+
program(): string {
|
|
92
|
+
if (!this.stack.length) {
|
|
93
|
+
throw new Error('Expected the stack to have elements');
|
|
94
|
+
}
|
|
95
|
+
return this.stack[this.stack.length - 1];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
push(newProgram: string) {
|
|
99
|
+
this.stack.push(newProgram);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pop() {
|
|
103
|
+
if (!this.stack.length) {
|
|
104
|
+
throw new Error('Expected the stack to have elements');
|
|
105
|
+
}
|
|
106
|
+
this.stack.pop();
|
|
107
|
+
}
|
|
108
|
+
}
|