@drift-labs/sdk 2.34.1-beta.5 → 2.34.1-beta.6

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 CHANGED
@@ -1 +1 @@
1
- 2.34.1-beta.5
1
+ 2.34.1-beta.6
@@ -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 eventGenerator = this.program._events._eventParser.parseLogs(logs, false);
103
- for (const event of eventGenerator) {
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);
@@ -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
- const transactions = await connection.getTransactions(chunk.map((confirmedSignature) => confirmedSignature.signature),
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,36 @@ 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
+ console.log(requests);
52
+ const rpcResponses = await (0, promiseTimeout_1.promiseTimeout)(
53
+ // @ts-ignore
54
+ connection._rpcBatchRequest(requests), 10 * 1000 // 10 second timeout
55
+ );
56
+ if (rpcResponses === null) {
57
+ return Promise.reject('RPC request timed out fetching transactions');
58
+ }
59
+ const logs = new Array();
60
+ for (const i in rpcResponses) {
61
+ const rpcResponse = rpcResponses[i];
62
+ if (rpcResponse.result) {
63
+ logs.push(mapTransactionResponseToLog(rpcResponse.result));
64
+ }
65
+ }
66
+ return logs;
67
+ }
68
+ exports.fetchTransactionLogs = fetchTransactionLogs;
47
69
  function chunk(array, size) {
48
70
  return new Array(Math.ceil(array.length / size))
49
71
  .fill(null)
@@ -0,0 +1,2 @@
1
+ import { Program, Event } from '@coral-xyz/anchor';
2
+ export declare function parseLogs(program: Program, slot: number, logs: string[]): Event[];
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.34.1-beta.5",
3
+ "version": "2.34.1-beta.6",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -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 eventGenerator = this.program._events._eventParser.parseLogs(
170
- logs,
171
- false
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;
@@ -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
- const transactions = await connection.getTransactions(
68
+ return await fetchTransactionLogs(
69
+ connection,
70
70
  chunk.map((confirmedSignature) => confirmedSignature.signature),
71
- //@ts-ignore
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,46 @@ 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
+ console.log(requests);
108
+
109
+ const rpcResponses: any | null = await promiseTimeout(
110
+ // @ts-ignore
111
+ connection._rpcBatchRequest(requests),
112
+ 10 * 1000 // 10 second timeout
113
+ );
114
+
115
+ if (rpcResponses === null) {
116
+ return Promise.reject('RPC request timed out fetching transactions');
117
+ }
118
+
119
+ const logs = new Array<Log>();
120
+ for (const i in rpcResponses) {
121
+ const rpcResponse = rpcResponses[i];
122
+ if (rpcResponse.result) {
123
+ logs.push(mapTransactionResponseToLog(rpcResponse.result));
124
+ }
125
+ }
126
+
127
+ return logs;
128
+ }
129
+
98
130
  function chunk<T>(array: readonly T[], size: number): T[][] {
99
131
  return new Array(Math.ceil(array.length / size))
100
132
  .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
+ }