@layerzerolabs/lz-v2-stellar-sdk 0.2.121 → 0.2.123
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/.turbo/turbo-build.log +235 -1
- package/.turbo/turbo-lint.log +8 -0
- package/.turbo/turbo-test.log +41 -1361
- package/dist/generated/dvn.d.ts +2 -2
- package/dist/generated/dvn.js +2 -2
- package/dist/generated/endpoint.d.ts +2 -2
- package/dist/generated/endpoint.js +2 -2
- package/dist/generated/executor.d.ts +3 -3
- package/dist/generated/executor.d.ts.map +1 -1
- package/dist/generated/executor.js +3 -3
- package/dist/generated/executor.js.map +1 -1
- package/dist/generated/price_feed.d.ts +2 -2
- package/dist/generated/price_feed.js +2 -2
- package/dist/generated/sml.d.ts +2 -2
- package/dist/generated/sml.js +2 -2
- package/dist/generated/uln302.d.ts +2 -2
- package/dist/generated/uln302.js +2 -2
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/rust-toolchain.toml +4 -0
- package/src/index.ts +0 -5
- package/test/suites/constants.ts +30 -51
- package/test/suites/deploy.ts +13 -273
- package/test/suites/globalSetup.ts +3 -602
- package/test/upgrader.test.ts +1 -0
- package/test/utils.ts +11 -658
- package/ts-bindings-gen.toml +59 -0
- package/dist/generated/counter.d.ts +0 -1253
- package/dist/generated/counter.d.ts.map +0 -1
- package/dist/generated/counter.js +0 -391
- package/dist/generated/counter.js.map +0 -1
- package/dist/generated/oft.d.ts +0 -1638
- package/dist/generated/oft.d.ts.map +0 -1
- package/dist/generated/oft.js +0 -463
- package/dist/generated/oft.js.map +0 -1
- package/dist/generated/sac_manager.d.ts +0 -567
- package/dist/generated/sac_manager.d.ts.map +0 -1
- package/dist/generated/sac_manager.js +0 -198
- package/dist/generated/sac_manager.js.map +0 -1
- package/src/generated/bml.ts +0 -672
- package/src/generated/counter.ts +0 -1208
- package/src/generated/dvn.ts +0 -1508
- package/src/generated/dvn_fee_lib.ts +0 -595
- package/src/generated/endpoint.ts +0 -1181
- package/src/generated/executor.ts +0 -1521
- package/src/generated/executor_fee_lib.ts +0 -1027
- package/src/generated/executor_helper.ts +0 -833
- package/src/generated/layerzero_view.ts +0 -1119
- package/src/generated/oft.ts +0 -1609
- package/src/generated/price_feed.ts +0 -793
- package/src/generated/sac_manager.ts +0 -555
- package/src/generated/sml.ts +0 -921
- package/src/generated/treasury.ts +0 -887
- package/src/generated/uln302.ts +0 -1224
- package/src/generated/upgrader.ts +0 -276
- package/test/counter-sml.test.ts +0 -480
- package/test/counter-uln.test.ts +0 -584
- package/test/oft-sml.test.ts +0 -750
- package/test/sac-manager.test.ts +0 -464
- package/test/secp256k1.ts +0 -59
- package/test/suites/dummyContractClient.ts +0 -168
- package/test/suites/localnet.ts +0 -192
- package/test/suites/scan.ts +0 -202
- package/turbo.json +0 -15
package/test/suites/localnet.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { BASE_FEE, Operation, rpc, TransactionBuilder } from '@stellar/stellar-sdk';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
CHAIN_B_DEPLOYER,
|
|
6
|
-
DEFAULT_DEPLOYER,
|
|
7
|
-
EXECUTOR_ADMIN,
|
|
8
|
-
JUNK_WALLET,
|
|
9
|
-
NETWORK_PASSPHRASE,
|
|
10
|
-
RPC_URL,
|
|
11
|
-
ZRO_DISTRIBUTOR,
|
|
12
|
-
} from './constants.js';
|
|
13
|
-
import { deployNativeSac, deployZroToken } from './deploy.js';
|
|
14
|
-
|
|
15
|
-
const CONTAINER_NAME = 'stellar-protocol-sdk';
|
|
16
|
-
const ECR_IMAGE = '438003944538.dkr.ecr.us-east-1.amazonaws.com/layerzerolabs/stellar:2026-03-02';
|
|
17
|
-
const CONTAINER_PORT = 8000;
|
|
18
|
-
const HOST_PORT = 8086;
|
|
19
|
-
|
|
20
|
-
// Timeout configuration (in milliseconds)
|
|
21
|
-
const STARTUP_TIMEOUT_MS = 120_000; // 2 minutes
|
|
22
|
-
const REQUEST_TIMEOUT_MS = 5_000;
|
|
23
|
-
const RETRY_INTERVAL_MS = 2_000;
|
|
24
|
-
|
|
25
|
-
export async function startStellarLocalnet(): Promise<void> {
|
|
26
|
-
console.log('🚀 Starting Stellar localnet...');
|
|
27
|
-
|
|
28
|
-
// Remove any existing container and start fresh from the ECR snapshot
|
|
29
|
-
try {
|
|
30
|
-
execSync(`docker rm -f ${CONTAINER_NAME}`, { stdio: 'ignore' });
|
|
31
|
-
} catch {
|
|
32
|
-
// Container didn't exist — ignore
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
execSync(
|
|
36
|
-
`docker run -d --name ${CONTAINER_NAME} -p ${HOST_PORT}:${CONTAINER_PORT} ${ECR_IMAGE}`,
|
|
37
|
-
{ stdio: 'inherit' },
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
console.log('⏳ Waiting for Stellar RPC to be healthy...');
|
|
41
|
-
await waitForRpcHealth();
|
|
42
|
-
console.log('✅ Stellar RPC is healthy');
|
|
43
|
-
|
|
44
|
-
await fundTestAccounts();
|
|
45
|
-
await deployNativeSac();
|
|
46
|
-
await deployZroToken();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Serialization queue for fundAccount — the junk wallet can only have one pending tx at a time.
|
|
50
|
-
// Chaining onto this promise ensures concurrent calls are sequenced correctly.
|
|
51
|
-
let fundAccountQueue: Promise<void> = Promise.resolve();
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Fund a single account from the junk wallet (for ad-hoc test accounts).
|
|
55
|
-
* Calls are serialized to avoid sequence number conflicts on the junk wallet.
|
|
56
|
-
*/
|
|
57
|
-
export function fundAccount(publicKey: string): Promise<void> {
|
|
58
|
-
const result = fundAccountQueue.then(() => fundAccountInternal(publicKey));
|
|
59
|
-
fundAccountQueue = result.catch(() => {}); // keep queue alive if this call fails
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function fundAccountInternal(publicKey: string): Promise<void> {
|
|
64
|
-
const MAX_RETRIES = 5;
|
|
65
|
-
let lastError: unknown;
|
|
66
|
-
|
|
67
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
68
|
-
try {
|
|
69
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
70
|
-
// Re-fetch account on every attempt to get the latest sequence number
|
|
71
|
-
const junkAccount = await server.getAccount(JUNK_WALLET.publicKey());
|
|
72
|
-
|
|
73
|
-
const tx = new TransactionBuilder(junkAccount, {
|
|
74
|
-
fee: BASE_FEE,
|
|
75
|
-
networkPassphrase: NETWORK_PASSPHRASE,
|
|
76
|
-
})
|
|
77
|
-
.addOperation(
|
|
78
|
-
Operation.createAccount({
|
|
79
|
-
destination: publicKey,
|
|
80
|
-
startingBalance: '100',
|
|
81
|
-
}),
|
|
82
|
-
)
|
|
83
|
-
.setTimeout(30)
|
|
84
|
-
.build();
|
|
85
|
-
|
|
86
|
-
tx.sign(JUNK_WALLET);
|
|
87
|
-
|
|
88
|
-
const sendResult = await server.sendTransaction(tx);
|
|
89
|
-
if (sendResult.status !== 'PENDING') {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`Failed to fund account ${publicKey}: ${JSON.stringify(sendResult)}`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const txResult = await server.pollTransaction(sendResult.hash);
|
|
96
|
-
if (txResult.status !== 'SUCCESS') {
|
|
97
|
-
throw new Error(
|
|
98
|
-
`Account funding failed for ${publicKey}: ${JSON.stringify(txResult)}`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return; // success
|
|
103
|
-
} catch (err) {
|
|
104
|
-
lastError = err;
|
|
105
|
-
if (attempt < MAX_RETRIES) {
|
|
106
|
-
await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Failed to fund account ${publicKey} after ${MAX_RETRIES} attempts: ${lastError instanceof Error ? lastError.message : String(lastError)}`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function waitForRpcHealth(): Promise<void> {
|
|
117
|
-
const startTime = Date.now();
|
|
118
|
-
while (Date.now() - startTime < STARTUP_TIMEOUT_MS) {
|
|
119
|
-
try {
|
|
120
|
-
const response = await fetch(RPC_URL, {
|
|
121
|
-
method: 'POST',
|
|
122
|
-
headers: { 'Content-Type': 'application/json' },
|
|
123
|
-
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getHealth' }),
|
|
124
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
125
|
-
});
|
|
126
|
-
const data = (await response.json()) as { result?: { status?: string } };
|
|
127
|
-
if (data.result?.status === 'healthy') return;
|
|
128
|
-
} catch {
|
|
129
|
-
// Not ready yet
|
|
130
|
-
}
|
|
131
|
-
await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS));
|
|
132
|
-
}
|
|
133
|
-
throw new Error(
|
|
134
|
-
`Stellar RPC failed to become healthy within ${STARTUP_TIMEOUT_MS / 1000} seconds`,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async function fundTestAccounts(): Promise<void> {
|
|
139
|
-
console.log('💰 Funding test accounts from junk wallet...');
|
|
140
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
141
|
-
const junkAccount = await server.getAccount(JUNK_WALLET.publicKey());
|
|
142
|
-
|
|
143
|
-
const tx = new TransactionBuilder(junkAccount, {
|
|
144
|
-
fee: BASE_FEE,
|
|
145
|
-
networkPassphrase: NETWORK_PASSPHRASE,
|
|
146
|
-
})
|
|
147
|
-
.addOperation(
|
|
148
|
-
Operation.createAccount({
|
|
149
|
-
destination: DEFAULT_DEPLOYER.publicKey(),
|
|
150
|
-
startingBalance: '2000',
|
|
151
|
-
}),
|
|
152
|
-
)
|
|
153
|
-
.addOperation(
|
|
154
|
-
Operation.createAccount({
|
|
155
|
-
destination: ZRO_DISTRIBUTOR.publicKey(),
|
|
156
|
-
startingBalance: '2000',
|
|
157
|
-
}),
|
|
158
|
-
)
|
|
159
|
-
.addOperation(
|
|
160
|
-
Operation.createAccount({
|
|
161
|
-
destination: EXECUTOR_ADMIN.publicKey(),
|
|
162
|
-
startingBalance: '2000',
|
|
163
|
-
}),
|
|
164
|
-
)
|
|
165
|
-
.addOperation(
|
|
166
|
-
Operation.createAccount({
|
|
167
|
-
destination: CHAIN_B_DEPLOYER.publicKey(),
|
|
168
|
-
startingBalance: '2000',
|
|
169
|
-
}),
|
|
170
|
-
)
|
|
171
|
-
.setTimeout(30)
|
|
172
|
-
.build();
|
|
173
|
-
|
|
174
|
-
tx.sign(JUNK_WALLET);
|
|
175
|
-
|
|
176
|
-
const sendResult = await server.sendTransaction(tx);
|
|
177
|
-
if (sendResult.status !== 'PENDING') {
|
|
178
|
-
throw new Error(`Failed to fund test accounts: ${JSON.stringify(sendResult)}`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const txResult = await server.pollTransaction(sendResult.hash);
|
|
182
|
-
if (txResult.status !== 'SUCCESS') {
|
|
183
|
-
throw new Error(`Account funding transaction failed: ${JSON.stringify(txResult)}`);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
console.log('✅ All test accounts funded');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export async function stopStellarLocalnet(): Promise<void> {
|
|
190
|
-
execSync(`docker rm -f ${CONTAINER_NAME}`, { stdio: 'ignore' });
|
|
191
|
-
console.log('✅ Stellar localnet stopped');
|
|
192
|
-
}
|
package/test/suites/scan.ts
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { rpc, xdr } from '@stellar/stellar-sdk';
|
|
2
|
-
|
|
3
|
-
import { RPC_URL } from './constants.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Event filter for scanning contract events
|
|
7
|
-
*/
|
|
8
|
-
export interface EventFilter {
|
|
9
|
-
contractId: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Parsed contract event
|
|
14
|
-
*/
|
|
15
|
-
export interface ParsedContractEvent {
|
|
16
|
-
type: string;
|
|
17
|
-
contractId: string;
|
|
18
|
-
topics: any[];
|
|
19
|
-
data: any;
|
|
20
|
-
ledger: number;
|
|
21
|
-
txHash: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* PacketSent event structure from endpoint_v2.rs
|
|
26
|
-
*/
|
|
27
|
-
export interface PacketSentEvent {
|
|
28
|
-
encoded_packet: Buffer;
|
|
29
|
-
options: Buffer;
|
|
30
|
-
send_library: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Scan for contract events within a ledger range
|
|
35
|
-
*/
|
|
36
|
-
export async function scanEvents(
|
|
37
|
-
startLedgerSequence: number,
|
|
38
|
-
filter: EventFilter,
|
|
39
|
-
): Promise<ParsedContractEvent[]> {
|
|
40
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
41
|
-
const latestLedger = await server.getLatestLedger();
|
|
42
|
-
|
|
43
|
-
console.log('🔍 Scanning events from ledger', startLedgerSequence);
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
// Wait for at least one new ledger to ensure events are indexed
|
|
47
|
-
let endLedger = latestLedger.sequence;
|
|
48
|
-
if (endLedger <= startLedgerSequence) {
|
|
49
|
-
console.log(' Waiting for next ledger...');
|
|
50
|
-
for (let i = 0; i < 10; i++) {
|
|
51
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
52
|
-
const newLedger = await server.getLatestLedger();
|
|
53
|
-
if (newLedger.sequence > startLedgerSequence) {
|
|
54
|
-
endLedger = newLedger.sequence;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const events = await server.getEvents({
|
|
61
|
-
startLedger: startLedgerSequence,
|
|
62
|
-
filters: [{ type: 'contract', contractIds: [filter.contractId] }],
|
|
63
|
-
endLedger: endLedger,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const parsedEvents: ParsedContractEvent[] = [];
|
|
67
|
-
|
|
68
|
-
for (const event of events.events || []) {
|
|
69
|
-
try {
|
|
70
|
-
const parsedEvent: ParsedContractEvent = {
|
|
71
|
-
type: event.type,
|
|
72
|
-
contractId: event.contractId?.contractId() || '',
|
|
73
|
-
topics: event.topic,
|
|
74
|
-
data: event.value ? xdr.ScVal.fromXDR(event.value.toXDR()) : undefined,
|
|
75
|
-
ledger: event.ledger,
|
|
76
|
-
txHash: event.txHash,
|
|
77
|
-
};
|
|
78
|
-
parsedEvents.push(parsedEvent);
|
|
79
|
-
} catch (e) {
|
|
80
|
-
console.warn('Failed to parse event:', e);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
console.log(`✅ Found ${parsedEvents.length} events`);
|
|
85
|
-
return parsedEvents;
|
|
86
|
-
} catch (error: any) {
|
|
87
|
-
console.error('Error scanning events:', error.message);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Scan for PacketSent events from the Endpoint contract
|
|
94
|
-
*/
|
|
95
|
-
export async function scanPacketSentEvents(
|
|
96
|
-
endpointAddress: string,
|
|
97
|
-
startLedger: number,
|
|
98
|
-
): Promise<PacketSentEvent[]> {
|
|
99
|
-
console.log('📦 Scanning for PacketSent events...');
|
|
100
|
-
const events = await scanEvents(startLedger, {
|
|
101
|
-
contractId: endpointAddress,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Filter for PacketSent events
|
|
105
|
-
// PacketSent event has topics like ["PacketSent"] or similar
|
|
106
|
-
const packetSentEvents: PacketSentEvent[] = [];
|
|
107
|
-
|
|
108
|
-
for (const event of events) {
|
|
109
|
-
try {
|
|
110
|
-
// Check if this is a PacketSent event
|
|
111
|
-
// The first topic should be the event name
|
|
112
|
-
const firstTopic = event.topics[0];
|
|
113
|
-
|
|
114
|
-
// Convert ScVal to string to check event name
|
|
115
|
-
let eventName = '';
|
|
116
|
-
if (firstTopic && firstTopic._switch?.name === 'scvSymbol') {
|
|
117
|
-
eventName = firstTopic.value()?.toString() || '';
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (eventName === 'packet_sent') {
|
|
121
|
-
// Parse the event data
|
|
122
|
-
// PacketSent { encoded_packet, options, send_library }
|
|
123
|
-
const eventData = event.data;
|
|
124
|
-
|
|
125
|
-
// Try to extract fields from the struct
|
|
126
|
-
if (eventData && eventData._switch?.name === 'scvMap') {
|
|
127
|
-
const map = eventData.value();
|
|
128
|
-
const packetEvent: Partial<PacketSentEvent> = {};
|
|
129
|
-
|
|
130
|
-
for (const entry of map || []) {
|
|
131
|
-
const key = entry.key().value()?.toString();
|
|
132
|
-
const value = entry.val();
|
|
133
|
-
|
|
134
|
-
if (key === 'encoded_packet' && value._switch?.name === 'scvBytes') {
|
|
135
|
-
packetEvent.encoded_packet = Buffer.from(value.value());
|
|
136
|
-
} else if (key === 'options' && value._switch?.name === 'scvBytes') {
|
|
137
|
-
packetEvent.options = Buffer.from(value.value());
|
|
138
|
-
} else if (key === 'send_library' && value._switch?.name === 'scvAddress') {
|
|
139
|
-
packetEvent.send_library = value.value()?.toString() || '';
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
packetEvent.encoded_packet &&
|
|
145
|
-
packetEvent.options !== undefined &&
|
|
146
|
-
packetEvent.send_library
|
|
147
|
-
) {
|
|
148
|
-
packetSentEvents.push(packetEvent as PacketSentEvent);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch (e) {
|
|
153
|
-
console.warn('Failed to parse PacketSent event:', e);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
console.log(`✅ Found ${packetSentEvents.length} PacketSent events`);
|
|
158
|
-
return packetSentEvents;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Wait for new ledgers and scan for events
|
|
163
|
-
*/
|
|
164
|
-
export async function waitAndScanEvents(
|
|
165
|
-
startLedger: number,
|
|
166
|
-
contractAddress: string,
|
|
167
|
-
eventName: string,
|
|
168
|
-
maxWaitSeconds: number = 30,
|
|
169
|
-
): Promise<ParsedContractEvent[]> {
|
|
170
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
171
|
-
const endTime = Date.now() + maxWaitSeconds * 1000;
|
|
172
|
-
|
|
173
|
-
console.log(`⏳ Waiting for ${eventName} events...`);
|
|
174
|
-
|
|
175
|
-
while (Date.now() < endTime) {
|
|
176
|
-
const currentLedger = await server.getLatestLedger();
|
|
177
|
-
|
|
178
|
-
if (currentLedger.sequence > startLedger) {
|
|
179
|
-
const events = await scanEvents(startLedger, {
|
|
180
|
-
contractId: contractAddress,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const matchingEvents = events.filter((e) => {
|
|
184
|
-
const firstTopic = e.topics[0];
|
|
185
|
-
if (firstTopic && firstTopic._switch?.name === 'scvSymbol') {
|
|
186
|
-
const name = firstTopic.value()?.toString() || '';
|
|
187
|
-
return name === eventName;
|
|
188
|
-
}
|
|
189
|
-
return false;
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
if (matchingEvents.length > 0) {
|
|
193
|
-
return matchingEvents;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Wait 1 second before checking again
|
|
198
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
throw new Error(`Timeout waiting for ${eventName} events`);
|
|
202
|
-
}
|