@drift-labs/sdk 2.34.1-beta.4 → 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 +1 -1
- package/lib/driftClient.js +5 -2
- package/lib/events/eventSubscriber.js +4 -2
- package/lib/events/fetchLogs.d.ts +1 -0
- package/lib/events/fetchLogs.js +33 -11
- package/lib/events/parse.d.ts +2 -0
- package/lib/events/parse.js +96 -0
- package/package.json +1 -1
- package/src/driftClient.ts +21 -17
- package/src/events/eventSubscriber.ts +4 -5
- package/src/events/fetchLogs.ts +44 -12
- package/src/events/parse.ts +108 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.34.1-beta.
|
|
1
|
+
2.34.1-beta.6
|
package/lib/driftClient.js
CHANGED
|
@@ -1041,7 +1041,10 @@ class DriftClient {
|
|
|
1041
1041
|
}));
|
|
1042
1042
|
}
|
|
1043
1043
|
const authority = this.wallet.publicKey;
|
|
1044
|
-
const
|
|
1044
|
+
const isFromSubaccount = fromSubAccountId !== null &&
|
|
1045
|
+
fromSubAccountId !== undefined &&
|
|
1046
|
+
!isNaN(fromSubAccountId);
|
|
1047
|
+
const createWSOLTokenAccount = isSolMarket && userTokenAccount.equals(authority) && !isFromSubaccount;
|
|
1045
1048
|
if (createWSOLTokenAccount) {
|
|
1046
1049
|
const { ixs: startIxs, signers, pubkey, } = await this.getWrappedSolAccountCreationIxs(amount, true);
|
|
1047
1050
|
userTokenAccount = pubkey;
|
|
@@ -1050,7 +1053,7 @@ class DriftClient {
|
|
|
1050
1053
|
});
|
|
1051
1054
|
signers.forEach((signer) => additionalSigners.push(signer));
|
|
1052
1055
|
}
|
|
1053
|
-
const depositCollateralIx =
|
|
1056
|
+
const depositCollateralIx = isFromSubaccount
|
|
1054
1057
|
? await this.getTransferDepositIx(amount, marketIndex, fromSubAccountId, subAccountId)
|
|
1055
1058
|
: await this.getDepositInstruction(amount, marketIndex, userTokenAccount, subAccountId, false, false);
|
|
1056
1059
|
if (subAccountId === 0) {
|
|
@@ -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,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,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
package/src/driftClient.ts
CHANGED
|
@@ -1782,8 +1782,13 @@ export class DriftClient {
|
|
|
1782
1782
|
|
|
1783
1783
|
const authority = this.wallet.publicKey;
|
|
1784
1784
|
|
|
1785
|
+
const isFromSubaccount =
|
|
1786
|
+
fromSubAccountId !== null &&
|
|
1787
|
+
fromSubAccountId !== undefined &&
|
|
1788
|
+
!isNaN(fromSubAccountId);
|
|
1789
|
+
|
|
1785
1790
|
const createWSOLTokenAccount =
|
|
1786
|
-
isSolMarket && userTokenAccount.equals(authority);
|
|
1791
|
+
isSolMarket && userTokenAccount.equals(authority) && !isFromSubaccount;
|
|
1787
1792
|
|
|
1788
1793
|
if (createWSOLTokenAccount) {
|
|
1789
1794
|
const {
|
|
@@ -1801,22 +1806,21 @@ export class DriftClient {
|
|
|
1801
1806
|
signers.forEach((signer) => additionalSigners.push(signer));
|
|
1802
1807
|
}
|
|
1803
1808
|
|
|
1804
|
-
const depositCollateralIx =
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
);
|
|
1809
|
+
const depositCollateralIx = isFromSubaccount
|
|
1810
|
+
? await this.getTransferDepositIx(
|
|
1811
|
+
amount,
|
|
1812
|
+
marketIndex,
|
|
1813
|
+
fromSubAccountId,
|
|
1814
|
+
subAccountId
|
|
1815
|
+
)
|
|
1816
|
+
: await this.getDepositInstruction(
|
|
1817
|
+
amount,
|
|
1818
|
+
marketIndex,
|
|
1819
|
+
userTokenAccount,
|
|
1820
|
+
subAccountId,
|
|
1821
|
+
false,
|
|
1822
|
+
false
|
|
1823
|
+
);
|
|
1820
1824
|
|
|
1821
1825
|
if (subAccountId === 0) {
|
|
1822
1826
|
if (
|
|
@@ -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,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
|
+
}
|