@layerzerolabs/test-utils-stellar 0.2.122

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.
Files changed (73) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +108 -0
  3. package/dist/4UKUSHEU.js +30 -0
  4. package/dist/4UKUSHEU.js.map +1 -0
  5. package/dist/7DZ4SVEO.js +46 -0
  6. package/dist/7DZ4SVEO.js.map +1 -0
  7. package/dist/CYINPPER.js +293 -0
  8. package/dist/CYINPPER.js.map +1 -0
  9. package/dist/DNRE4ENZ.js +56 -0
  10. package/dist/DNRE4ENZ.js.map +1 -0
  11. package/dist/GAGBEOCZ.js +25 -0
  12. package/dist/GAGBEOCZ.js.map +1 -0
  13. package/dist/PMDPSIA2.js +149 -0
  14. package/dist/PMDPSIA2.js.map +1 -0
  15. package/dist/TYAJMKGY.js +183 -0
  16. package/dist/TYAJMKGY.js.map +1 -0
  17. package/dist/VRABOZPX.js +364 -0
  18. package/dist/VRABOZPX.js.map +1 -0
  19. package/dist/VUOMXK5T.js +6 -0
  20. package/dist/VUOMXK5T.js.map +1 -0
  21. package/dist/ZWKXKZVT.js +394 -0
  22. package/dist/ZWKXKZVT.js.map +1 -0
  23. package/dist/client.d.ts +17 -0
  24. package/dist/client.d.ts.map +1 -0
  25. package/dist/client.js +4 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/deploy.d.ts +43 -0
  28. package/dist/deploy.d.ts.map +1 -0
  29. package/dist/deploy.js +4 -0
  30. package/dist/deploy.js.map +1 -0
  31. package/dist/env.d.ts +45 -0
  32. package/dist/env.d.ts.map +1 -0
  33. package/dist/env.js +5 -0
  34. package/dist/env.js.map +1 -0
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +12 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/localnet-global-setup.d.ts +7 -0
  40. package/dist/localnet-global-setup.d.ts.map +1 -0
  41. package/dist/localnet-global-setup.js +6 -0
  42. package/dist/localnet-global-setup.js.map +1 -0
  43. package/dist/localnet.d.ts +37 -0
  44. package/dist/localnet.d.ts.map +1 -0
  45. package/dist/localnet.js +5 -0
  46. package/dist/localnet.js.map +1 -0
  47. package/dist/protocol-global-setup.d.ts +58 -0
  48. package/dist/protocol-global-setup.d.ts.map +1 -0
  49. package/dist/protocol-global-setup.js +7 -0
  50. package/dist/protocol-global-setup.js.map +1 -0
  51. package/dist/scan.d.ts +59 -0
  52. package/dist/scan.d.ts.map +1 -0
  53. package/dist/scan.js +4 -0
  54. package/dist/scan.js.map +1 -0
  55. package/dist/secp256k1.d.ts +23 -0
  56. package/dist/secp256k1.d.ts.map +1 -0
  57. package/dist/secp256k1.js +4 -0
  58. package/dist/secp256k1.js.map +1 -0
  59. package/dist/utils.d.ts +54 -0
  60. package/dist/utils.d.ts.map +1 -0
  61. package/dist/utils.js +5 -0
  62. package/dist/utils.js.map +1 -0
  63. package/package.json +50 -0
  64. package/src/client.ts +37 -0
  65. package/src/deploy.ts +316 -0
  66. package/src/env.ts +104 -0
  67. package/src/index.ts +40 -0
  68. package/src/localnet-global-setup.ts +30 -0
  69. package/src/localnet.ts +406 -0
  70. package/src/protocol-global-setup.ts +747 -0
  71. package/src/scan.ts +255 -0
  72. package/src/secp256k1.ts +59 -0
  73. package/src/utils.ts +642 -0
@@ -0,0 +1,747 @@
1
+ import type { Keypair } from '@stellar/stellar-sdk';
2
+ import { rpc } from '@stellar/stellar-sdk';
3
+ import { readFileSync } from 'fs';
4
+ import path from 'path';
5
+
6
+ import { createClient } from './client.js';
7
+ import { type DeployableContractClient, deployContract, uploadWasm } from './deploy.js';
8
+ import type { StellarTestEnv } from './env.js';
9
+ import { startStellarLocalnet, stopStellarLocalnet } from './localnet.js';
10
+
11
+ /** Vitest globalSetup context subset (avoids a hard vitest type dependency in src/). */
12
+ export type ProtocolGlobalSetupContext = {
13
+ provide: (key: 'chainA' | 'chainB', value: ChainAddresses) => void;
14
+ };
15
+
16
+ type SigningTransaction = {
17
+ signAndSend: () => Promise<unknown>;
18
+ };
19
+
20
+ /**
21
+ * Generated Soroban clients expose asynchronous methods that construct a signable transaction.
22
+ * Their method names and argument types remain owned by the generated SDK.
23
+ */
24
+ type ProtocolClient = Record<string, (args: object) => Promise<SigningTransaction>>;
25
+
26
+ type ProtocolClientConstructor<TClient extends ProtocolClient> = new (options: {
27
+ contractId: string;
28
+ publicKey: string;
29
+ signTransaction: (tx: string) => Promise<{ signedTxXdr: string; signerAddress: string }>;
30
+ rpcUrl: string;
31
+ networkPassphrase: string;
32
+ allowHttp: boolean;
33
+ }) => TClient;
34
+
35
+ export interface ProtocolContractModule {
36
+ /** The generated SDK Client constructor. Validated at the adapter boundary before use. */
37
+ Client: unknown;
38
+ }
39
+
40
+ /**
41
+ * Protocol contract modules from `@layerzerolabs/lz-v2-stellar-sdk`.
42
+ * Injected by consumers to avoid a workspace cycle (test-utils ↔ sdk).
43
+ */
44
+ export interface ProtocolStackModules {
45
+ endpoint: ProtocolContractModule;
46
+ treasury: ProtocolContractModule;
47
+ uln302: ProtocolContractModule;
48
+ sml: ProtocolContractModule;
49
+ priceFeed: ProtocolContractModule;
50
+ executorFeeLib: ProtocolContractModule;
51
+ dvnFeeLib: ProtocolContractModule;
52
+ dvn: ProtocolContractModule;
53
+ executorHelper: ProtocolContractModule;
54
+ executor: ProtocolContractModule;
55
+ }
56
+
57
+ /**
58
+ * Addresses for a single chain's protocol contracts
59
+ */
60
+ export interface ChainAddresses {
61
+ eid: number;
62
+ endpointV2: string;
63
+ uln302: string;
64
+ sml: string;
65
+ treasury: string;
66
+ executor: string;
67
+ executorHelper: string;
68
+ executorFeeLib: string;
69
+ priceFeed: string;
70
+ dvnFeeLib: string;
71
+ dvn: string;
72
+ }
73
+
74
+ /**
75
+ * Clients for a single chain's protocol contracts
76
+ */
77
+ interface ChainClients {
78
+ endpointClient: ProtocolClient;
79
+ uln302Client: ProtocolClient;
80
+ smlClient: ProtocolClient;
81
+ treasuryClient: ProtocolClient;
82
+ executorClient: ProtocolClient;
83
+ executorHelperClient: ProtocolClient;
84
+ executorFeeLibClient: ProtocolClient;
85
+ priceFeedClient: ProtocolClient;
86
+ dvnFeeLibClient: ProtocolClient;
87
+ dvnClient: ProtocolClient;
88
+ }
89
+
90
+ /**
91
+ * Complete chain setup with addresses and clients
92
+ */
93
+ interface ChainSetup {
94
+ addresses: ChainAddresses;
95
+ clients: ChainClients;
96
+ }
97
+
98
+ interface WasmHashes {
99
+ endpoint: string;
100
+ treasury: string;
101
+ uln302: string;
102
+ sml: string;
103
+ priceFeed: string;
104
+ executorFeeLib: string;
105
+ dvnFeeLib: string;
106
+ dvn: string;
107
+ executorHelper: string;
108
+ executor: string;
109
+ }
110
+
111
+ function getProtocolClientConstructor<TClient extends ProtocolClient>(
112
+ client: unknown,
113
+ moduleName: string,
114
+ ): ProtocolClientConstructor<TClient> {
115
+ if (typeof client !== 'function') {
116
+ throw new TypeError(`${moduleName}.Client must be a generated contract client constructor`);
117
+ }
118
+ return client as ProtocolClientConstructor<TClient>;
119
+ }
120
+
121
+ function getDeployableProtocolClient<TClient extends { options: { contractId: string } }>(
122
+ client: unknown,
123
+ moduleName: string,
124
+ ): DeployableContractClient<TClient> {
125
+ if (
126
+ typeof client !== 'function' ||
127
+ !('deploy' in client) ||
128
+ typeof client.deploy !== 'function'
129
+ ) {
130
+ throw new TypeError(`${moduleName}.Client must expose a deploy method`);
131
+ }
132
+ return client as DeployableContractClient<TClient>;
133
+ }
134
+
135
+ function createProtocolClient(
136
+ env: StellarTestEnv,
137
+ client: unknown,
138
+ contractId: string,
139
+ moduleName: string,
140
+ ): ProtocolClient {
141
+ return createClient(
142
+ env,
143
+ getProtocolClientConstructor<ProtocolClient>(client, moduleName),
144
+ contractId,
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Upload all protocol WASM files once and return their hashes.
150
+ */
151
+ async function uploadAllWasms(env: StellarTestEnv, wasmDir: string): Promise<WasmHashes> {
152
+ const server = new rpc.Server(env.RPC_URL, { allowHttp: true });
153
+
154
+ const wasmFiles = {
155
+ endpoint: 'endpoint_v2.wasm',
156
+ treasury: 'treasury.wasm',
157
+ uln302: 'uln302.wasm',
158
+ sml: 'simple_message_lib.wasm',
159
+ priceFeed: 'price_feed.wasm',
160
+ executorFeeLib: 'executor_fee_lib.wasm',
161
+ dvnFeeLib: 'dvn_fee_lib.wasm',
162
+ dvn: 'dvn.wasm',
163
+ executorHelper: 'executor_helper.wasm',
164
+ executor: 'executor.wasm',
165
+ };
166
+
167
+ const hashes: Record<string, string> = {};
168
+ for (const [name, file] of Object.entries(wasmFiles)) {
169
+ const wasmBuffer = readFileSync(path.join(wasmDir, file));
170
+ console.log(`šŸ“¤ Uploading ${name} WASM (${(wasmBuffer.length / 1024).toFixed(1)} KB)...`);
171
+ hashes[name] = await uploadWasm(env, wasmBuffer, env.DEFAULT_DEPLOYER, server);
172
+ }
173
+
174
+ return hashes as unknown as WasmHashes;
175
+ }
176
+
177
+ /**
178
+ * Deploy all protocol contracts for a single chain using pre-uploaded WASM hashes.
179
+ * The deployer pays gas; contract ownership is always DEFAULT_DEPLOYER.
180
+ */
181
+ async function deployChainContracts(
182
+ env: StellarTestEnv,
183
+ protocol: ProtocolStackModules,
184
+ eid: number,
185
+ chainLabel: string,
186
+ deployer: Keypair,
187
+ wasmDir: string,
188
+ wasmHashes: WasmHashes,
189
+ ): Promise<ChainAddresses> {
190
+ const addresses: ChainAddresses = {
191
+ eid,
192
+ endpointV2: '',
193
+ uln302: '',
194
+ sml: '',
195
+ treasury: '',
196
+ executor: '',
197
+ executorHelper: '',
198
+ executorFeeLib: '',
199
+ priceFeed: '',
200
+ dvnFeeLib: '',
201
+ dvn: '',
202
+ };
203
+
204
+ // 1. Deploy Endpoint
205
+ console.log(`šŸš€ [${chainLabel}] Deploying Endpoint (EID: ${eid})...`);
206
+ const deployedEndpoint = await deployContract<
207
+ ProtocolClient & { options: { contractId: string } }
208
+ >(
209
+ env,
210
+ getDeployableProtocolClient(protocol.endpoint.Client, 'endpoint'),
211
+ path.join(wasmDir, 'endpoint_v2.wasm'),
212
+ {
213
+ eid,
214
+ owner: env.DEFAULT_DEPLOYER.publicKey(),
215
+ native_token: env.NATIVE_TOKEN_ADDRESS,
216
+ },
217
+ deployer,
218
+ { wasmHash: wasmHashes.endpoint },
219
+ );
220
+ addresses.endpointV2 = deployedEndpoint.options.contractId;
221
+ console.log(`āœ… [${chainLabel}] Endpoint deployed:`, addresses.endpointV2);
222
+
223
+ // 2. Deploy Treasury
224
+ console.log(`šŸš€ [${chainLabel}] Deploying Treasury...`);
225
+ const deployedTreasury = await deployContract<
226
+ ProtocolClient & { options: { contractId: string } }
227
+ >(
228
+ env,
229
+ getDeployableProtocolClient(protocol.treasury.Client, 'treasury'),
230
+ path.join(wasmDir, 'treasury.wasm'),
231
+ { owner: env.DEFAULT_DEPLOYER.publicKey() },
232
+ deployer,
233
+ { wasmHash: wasmHashes.treasury },
234
+ );
235
+ addresses.treasury = deployedTreasury.options.contractId;
236
+ console.log(`āœ… [${chainLabel}] Treasury deployed:`, addresses.treasury);
237
+
238
+ // 3. Deploy ULN302
239
+ console.log(`šŸš€ [${chainLabel}] Deploying ULN302...`);
240
+ const deployedUln302 = await deployContract<
241
+ ProtocolClient & { options: { contractId: string } }
242
+ >(
243
+ env,
244
+ getDeployableProtocolClient(protocol.uln302.Client, 'uln302'),
245
+ path.join(wasmDir, 'uln302.wasm'),
246
+ {
247
+ owner: env.DEFAULT_DEPLOYER.publicKey(),
248
+ endpoint: addresses.endpointV2,
249
+ treasury: addresses.treasury,
250
+ },
251
+ deployer,
252
+ { wasmHash: wasmHashes.uln302 },
253
+ );
254
+ addresses.uln302 = deployedUln302.options.contractId;
255
+ console.log(`āœ… [${chainLabel}] ULN302 deployed:`, addresses.uln302);
256
+
257
+ // 4. Deploy SML (SimpleMessageLib)
258
+ console.log(`šŸš€ [${chainLabel}] Deploying SimpleMessageLib...`);
259
+ const deployedSml = await deployContract<ProtocolClient & { options: { contractId: string } }>(
260
+ env,
261
+ getDeployableProtocolClient(protocol.sml.Client, 'sml'),
262
+ path.join(wasmDir, 'simple_message_lib.wasm'),
263
+ {
264
+ owner: env.DEFAULT_DEPLOYER.publicKey(),
265
+ endpoint: addresses.endpointV2,
266
+ fee_recipient: env.DEFAULT_DEPLOYER.publicKey(),
267
+ },
268
+ deployer,
269
+ { wasmHash: wasmHashes.sml },
270
+ );
271
+ addresses.sml = deployedSml.options.contractId;
272
+ console.log(`āœ… [${chainLabel}] SimpleMessageLib deployed:`, addresses.sml);
273
+
274
+ // 5. Deploy Price Feed
275
+ console.log(`šŸš€ [${chainLabel}] Deploying Price Feed...`);
276
+ const deployedPriceFeed = await deployContract<
277
+ ProtocolClient & { options: { contractId: string } }
278
+ >(
279
+ env,
280
+ getDeployableProtocolClient(protocol.priceFeed.Client, 'priceFeed'),
281
+ path.join(wasmDir, 'price_feed.wasm'),
282
+ {
283
+ owner: env.DEFAULT_DEPLOYER.publicKey(),
284
+ price_updater: env.DEFAULT_DEPLOYER.publicKey(),
285
+ },
286
+ deployer,
287
+ { wasmHash: wasmHashes.priceFeed },
288
+ );
289
+ addresses.priceFeed = deployedPriceFeed.options.contractId;
290
+ console.log(`āœ… [${chainLabel}] Price Feed deployed:`, addresses.priceFeed);
291
+
292
+ // 6. Deploy Executor Fee Lib
293
+ console.log(`šŸš€ [${chainLabel}] Deploying Executor Fee Lib...`);
294
+ const deployedExecutorFeeLib = await deployContract<
295
+ ProtocolClient & { options: { contractId: string } }
296
+ >(
297
+ env,
298
+ getDeployableProtocolClient(protocol.executorFeeLib.Client, 'executorFeeLib'),
299
+ path.join(wasmDir, 'executor_fee_lib.wasm'),
300
+ { owner: env.DEFAULT_DEPLOYER.publicKey() },
301
+ deployer,
302
+ { wasmHash: wasmHashes.executorFeeLib },
303
+ );
304
+ addresses.executorFeeLib = deployedExecutorFeeLib.options.contractId;
305
+ console.log(`āœ… [${chainLabel}] Executor Fee Lib deployed:`, addresses.executorFeeLib);
306
+
307
+ // 7. Deploy DVN Fee Lib
308
+ console.log(`šŸš€ [${chainLabel}] Deploying DVN Fee Lib...`);
309
+ const deployedDvnFeeLib = await deployContract<
310
+ ProtocolClient & { options: { contractId: string } }
311
+ >(
312
+ env,
313
+ getDeployableProtocolClient(protocol.dvnFeeLib.Client, 'dvnFeeLib'),
314
+ path.join(wasmDir, 'dvn_fee_lib.wasm'),
315
+ { owner: env.DEFAULT_DEPLOYER.publicKey() },
316
+ deployer,
317
+ { wasmHash: wasmHashes.dvnFeeLib },
318
+ );
319
+ addresses.dvnFeeLib = deployedDvnFeeLib.options.contractId;
320
+ console.log(`āœ… [${chainLabel}] DVN Fee Lib deployed:`, addresses.dvnFeeLib);
321
+
322
+ // 8. Deploy DVN (same signer for both chains)
323
+ console.log(`šŸš€ [${chainLabel}] Deploying DVN...`);
324
+ const deployedDvn = await deployContract<ProtocolClient & { options: { contractId: string } }>(
325
+ env,
326
+ getDeployableProtocolClient(protocol.dvn.Client, 'dvn'),
327
+ path.join(wasmDir, 'dvn.wasm'),
328
+ {
329
+ vid: env.DVN_VID,
330
+ signers: [env.DVN_SIGNER.ethAddress],
331
+ threshold: 1,
332
+ admins: [env.DEFAULT_DEPLOYER.publicKey()],
333
+ supported_msglibs: [addresses.uln302],
334
+ price_feed: addresses.priceFeed,
335
+ default_multiplier_bps: 10000,
336
+ worker_fee_lib: addresses.dvnFeeLib,
337
+ deposit_address: env.DEFAULT_DEPLOYER.publicKey(),
338
+ },
339
+ deployer,
340
+ { wasmHash: wasmHashes.dvn },
341
+ );
342
+ addresses.dvn = deployedDvn.options.contractId;
343
+ console.log(`āœ… [${chainLabel}] DVN deployed:`, addresses.dvn);
344
+
345
+ // 9. Deploy Executor Helper
346
+ console.log(`šŸš€ [${chainLabel}] Deploying Executor Helper...`);
347
+ const deployedExecutorHelper = await deployContract<
348
+ ProtocolClient & { options: { contractId: string } }
349
+ >(
350
+ env,
351
+ getDeployableProtocolClient(protocol.executorHelper.Client, 'executorHelper'),
352
+ path.join(wasmDir, 'executor_helper.wasm'),
353
+ undefined,
354
+ deployer,
355
+ { wasmHash: wasmHashes.executorHelper },
356
+ );
357
+ addresses.executorHelper = deployedExecutorHelper.options.contractId;
358
+ console.log(`āœ… [${chainLabel}] Executor Helper deployed:`, addresses.executorHelper);
359
+
360
+ // 10. Deploy Executor (supports both ULN302 and SML)
361
+ console.log(`šŸš€ [${chainLabel}] Deploying Executor...`);
362
+ const deployedExecutor = await deployContract<
363
+ ProtocolClient & { options: { contractId: string } }
364
+ >(
365
+ env,
366
+ getDeployableProtocolClient(protocol.executor.Client, 'executor'),
367
+ path.join(wasmDir, 'executor.wasm'),
368
+ {
369
+ owner: env.DEFAULT_DEPLOYER.publicKey(),
370
+ endpoint: addresses.endpointV2,
371
+ admins: [env.EXECUTOR_ADMIN.publicKey(), env.DEFAULT_DEPLOYER.publicKey()],
372
+ message_libs: [addresses.uln302, addresses.sml],
373
+ price_feed: addresses.priceFeed,
374
+ default_multiplier_bps: 10000,
375
+ worker_fee_lib: addresses.executorFeeLib,
376
+ deposit_address: env.DEFAULT_DEPLOYER.publicKey(),
377
+ },
378
+ deployer,
379
+ { wasmHash: wasmHashes.executor },
380
+ );
381
+ addresses.executor = deployedExecutor.options.contractId;
382
+ console.log(`āœ… [${chainLabel}] Executor deployed:`, addresses.executor);
383
+
384
+ return addresses;
385
+ }
386
+
387
+ /**
388
+ * Register executor helper and create owner-signed clients for a chain.
389
+ * Must be called sequentially (uses DEFAULT_DEPLOYER for signing).
390
+ */
391
+ async function initChainClients(
392
+ env: StellarTestEnv,
393
+ protocol: ProtocolStackModules,
394
+ addresses: ChainAddresses,
395
+ chainLabel: string,
396
+ ): Promise<ChainSetup> {
397
+ // Register Executor Helper with Executor (needs owner-signed client)
398
+ console.log(`šŸš€ [${chainLabel}] Registering Executor Helper with Executor...`);
399
+ const executorClient = createProtocolClient(
400
+ env,
401
+ protocol.executor.Client,
402
+ addresses.executor,
403
+ 'executor',
404
+ );
405
+ await (
406
+ await executorClient.set_executor_helper({
407
+ helper: addresses.executorHelper,
408
+ allowed_functions: ['execute', 'compose'],
409
+ })
410
+ ).signAndSend();
411
+ console.log(`āœ… [${chainLabel}] Executor Helper registered`);
412
+
413
+ const clients: ChainClients = {
414
+ endpointClient: createProtocolClient(
415
+ env,
416
+ protocol.endpoint.Client,
417
+ addresses.endpointV2,
418
+ 'endpoint',
419
+ ),
420
+ uln302Client: createProtocolClient(env, protocol.uln302.Client, addresses.uln302, 'uln302'),
421
+ smlClient: createProtocolClient(env, protocol.sml.Client, addresses.sml, 'sml'),
422
+ treasuryClient: createProtocolClient(
423
+ env,
424
+ protocol.treasury.Client,
425
+ addresses.treasury,
426
+ 'treasury',
427
+ ),
428
+ executorClient,
429
+ executorHelperClient: createProtocolClient(
430
+ env,
431
+ protocol.executorHelper.Client,
432
+ addresses.executorHelper,
433
+ 'executorHelper',
434
+ ),
435
+ executorFeeLibClient: createProtocolClient(
436
+ env,
437
+ protocol.executorFeeLib.Client,
438
+ addresses.executorFeeLib,
439
+ 'executorFeeLib',
440
+ ),
441
+ priceFeedClient: createProtocolClient(
442
+ env,
443
+ protocol.priceFeed.Client,
444
+ addresses.priceFeed,
445
+ 'priceFeed',
446
+ ),
447
+ dvnFeeLibClient: createProtocolClient(
448
+ env,
449
+ protocol.dvnFeeLib.Client,
450
+ addresses.dvnFeeLib,
451
+ 'dvnFeeLib',
452
+ ),
453
+ dvnClient: createProtocolClient(env, protocol.dvn.Client, addresses.dvn, 'dvn'),
454
+ };
455
+
456
+ return { addresses, clients };
457
+ }
458
+
459
+ /**
460
+ * Wire a single chain's protocol contracts for cross-chain communication
461
+ *
462
+ * @param chain - The chain to wire (this chain)
463
+ * @param otherChain - The other chain (for cross-references)
464
+ * @param chainLabel - Label for logging
465
+ */
466
+ async function wireChainContracts(
467
+ env: StellarTestEnv,
468
+ chain: ChainSetup,
469
+ otherChain: ChainSetup,
470
+ chainLabel: string,
471
+ ): Promise<void> {
472
+ const { addresses, clients } = chain;
473
+ const { endpointClient, uln302Client, priceFeedClient, executorClient, dvnClient } = clients;
474
+
475
+ const thisEid = addresses.eid;
476
+ const otherEid = otherChain.addresses.eid;
477
+
478
+ console.log(
479
+ `\nšŸ”— [${chainLabel}] Wiring protocol contracts (EID: ${thisEid} ↔ ${otherEid})...`,
480
+ );
481
+
482
+ // Register libraries
483
+ await (await endpointClient.register_library({ new_lib: addresses.uln302 })).signAndSend();
484
+ await (await endpointClient.register_library({ new_lib: addresses.sml })).signAndSend();
485
+ console.log(`āœ… [${chainLabel}] Libraries registered (ULN302 + SML)`);
486
+
487
+ // Set ZRO token
488
+ await (await endpointClient.set_zro({ zro: env.ZRO_TOKEN_ADDRESS })).signAndSend();
489
+ console.log(`āœ… [${chainLabel}] ZRO token set`);
490
+
491
+ // ========================================================================
492
+ // Configure for SENDING to the other chain (dst_eid = otherEid)
493
+ // ========================================================================
494
+
495
+ // ULN302 executor config for sending to other chain
496
+ await (
497
+ await uln302Client.set_default_executor_configs({
498
+ params: [
499
+ {
500
+ dst_eid: otherEid,
501
+ config: { executor: addresses.executor, max_message_size: 10000 },
502
+ },
503
+ ],
504
+ })
505
+ ).signAndSend();
506
+ console.log(`āœ… [${chainLabel}] ULN302 executor config set for dst_eid=${otherEid}`);
507
+
508
+ // ULN302 send config: when sending to otherEid, use THIS chain's DVN for fee quoting
509
+ // (The DVN needs dst_config for the destination EID to calculate fees)
510
+ await (
511
+ await uln302Client.set_default_send_uln_configs({
512
+ params: [
513
+ {
514
+ eid: otherEid,
515
+ config: {
516
+ confirmations: 1n,
517
+ required_dvns: [addresses.dvn], // THIS chain's DVN (has dst_config for otherEid)
518
+ optional_dvns: [],
519
+ optional_dvn_threshold: 0,
520
+ },
521
+ },
522
+ ],
523
+ })
524
+ ).signAndSend();
525
+ console.log(
526
+ `āœ… [${chainLabel}] ULN302 send config set for eid=${otherEid} (DVN: ${addresses.dvn})`,
527
+ );
528
+
529
+ // ========================================================================
530
+ // Configure for RECEIVING from the other chain (src_eid = otherEid)
531
+ // ========================================================================
532
+
533
+ // ULN302 receive config: when receiving from otherEid, THIS chain's DVN verifies
534
+ await (
535
+ await uln302Client.set_default_receive_uln_configs({
536
+ params: [
537
+ {
538
+ eid: otherEid,
539
+ config: {
540
+ confirmations: 1n,
541
+ required_dvns: [addresses.dvn], // THIS chain's DVN verifies incoming
542
+ optional_dvns: [],
543
+ optional_dvn_threshold: 0,
544
+ },
545
+ },
546
+ ],
547
+ })
548
+ ).signAndSend();
549
+ console.log(
550
+ `āœ… [${chainLabel}] ULN302 receive config set for eid=${otherEid} (DVN: ${addresses.dvn})`,
551
+ );
552
+
553
+ // Set default send/receive libraries for the other chain
554
+ await (
555
+ await endpointClient.set_default_send_library({
556
+ dst_eid: otherEid,
557
+ new_lib: addresses.uln302,
558
+ })
559
+ ).signAndSend();
560
+ await (
561
+ await endpointClient.set_default_receive_library({
562
+ src_eid: otherEid,
563
+ new_lib: addresses.uln302,
564
+ grace_period: 0n,
565
+ })
566
+ ).signAndSend();
567
+ console.log(`āœ… [${chainLabel}] Default libraries set to ULN302 for eid=${otherEid}`);
568
+
569
+ // ========================================================================
570
+ // Configure Price Feed for both chains
571
+ // ========================================================================
572
+
573
+ await (
574
+ await priceFeedClient.set_price_ratio_denominator({ denominator: 100000000000000000000n })
575
+ ).signAndSend();
576
+ await (
577
+ await priceFeedClient.set_native_token_price_usd({
578
+ price_updater: env.DEFAULT_DEPLOYER.publicKey(),
579
+ native_token_price_usd: 1000000000000000000n,
580
+ })
581
+ ).signAndSend();
582
+
583
+ // Set prices for the other chain
584
+ const NORMALIZED_OTHER_EID = otherEid % 30000;
585
+ await (
586
+ await priceFeedClient.set_price({
587
+ price_updater: env.DEFAULT_DEPLOYER.publicKey(),
588
+ prices: [
589
+ {
590
+ eid: NORMALIZED_OTHER_EID,
591
+ price: {
592
+ gas_per_byte: 1,
593
+ gas_price_in_unit: 1n,
594
+ price_ratio: 100000000000000000000n,
595
+ },
596
+ },
597
+ ],
598
+ })
599
+ ).signAndSend();
600
+ console.log(`āœ… [${chainLabel}] Price Feed configured for eid=${otherEid}`);
601
+
602
+ // ========================================================================
603
+ // Configure Executor for sending to other chain
604
+ // ========================================================================
605
+
606
+ await (
607
+ await executorClient.set_dst_config({
608
+ admin: env.DEFAULT_DEPLOYER.publicKey(),
609
+ params: [
610
+ {
611
+ dst_eid: otherEid,
612
+ dst_config: {
613
+ floor_margin_usd: 0n,
614
+ lz_compose_base_gas: 50000n,
615
+ lz_receive_base_gas: 100000n,
616
+ multiplier_bps: 10000,
617
+ native_cap: 1000000000000n,
618
+ },
619
+ },
620
+ ],
621
+ })
622
+ ).signAndSend();
623
+ console.log(`āœ… [${chainLabel}] Executor configured for dst_eid=${otherEid}`);
624
+
625
+ // ========================================================================
626
+ // Configure DVN for verifying packets going to other chain
627
+ // ========================================================================
628
+
629
+ await (
630
+ await dvnClient.set_dst_config({
631
+ admin: env.DEFAULT_DEPLOYER.publicKey(),
632
+ params: [
633
+ {
634
+ dst_eid: otherEid,
635
+ config: {
636
+ floor_margin_usd: 0n,
637
+ gas: 100000n,
638
+ multiplier_bps: 10000,
639
+ },
640
+ },
641
+ ],
642
+ })
643
+ ).signAndSend();
644
+ console.log(`āœ… [${chainLabel}] DVN configured for dst_eid=${otherEid}`);
645
+
646
+ console.log(`šŸŽ‰ [${chainLabel}] Protocol wiring complete!`);
647
+ }
648
+
649
+ export interface ProtocolStackGlobalSetupOptions {
650
+ /**
651
+ * Directory containing protocol WASM artifacts, or an async resolver.
652
+ * Consumers typically resolve via getFullyQualifiedRepoRootPath.
653
+ */
654
+ wasmDir: string | (() => string | Promise<string>);
655
+ /**
656
+ * Protocol contract modules from `@layerzerolabs/lz-v2-stellar-sdk`.
657
+ * Passed by the consumer to avoid a workspace dependency cycle.
658
+ */
659
+ protocol: ProtocolStackModules;
660
+ }
661
+
662
+ /**
663
+ * Returns a Vitest globalSetup that deploys two protocol stacks and wires them.
664
+ */
665
+ export function createProtocolStackGlobalSetup(
666
+ env: StellarTestEnv,
667
+ options: ProtocolStackGlobalSetupOptions,
668
+ ): (ctx: ProtocolGlobalSetupContext) => Promise<() => Promise<void>> {
669
+ return async function globalSetup({
670
+ provide,
671
+ }: ProtocolGlobalSetupContext): Promise<() => Promise<void>> {
672
+ console.log('\n========================================');
673
+ console.log('🌐 GLOBAL SETUP: Starting Stellar Localnet');
674
+ console.log('========================================\n');
675
+
676
+ await startStellarLocalnet({ env });
677
+
678
+ try {
679
+ const wasmDir =
680
+ typeof options.wasmDir === 'function' ? await options.wasmDir() : options.wasmDir;
681
+
682
+ console.log('\n========================================');
683
+ console.log('šŸ“¤ GLOBAL SETUP: Uploading WASM (once)');
684
+ console.log('========================================\n');
685
+
686
+ const wasmHashes = await uploadAllWasms(env, wasmDir);
687
+
688
+ console.log('\n========================================');
689
+ console.log('šŸ“¦ GLOBAL SETUP: Deploying Protocol Contracts (Two Chains in Parallel)');
690
+ console.log('========================================\n');
691
+
692
+ const { protocol } = options;
693
+
694
+ const [addressesA, addressesB] = await Promise.all([
695
+ deployChainContracts(
696
+ env,
697
+ protocol,
698
+ env.EID_A,
699
+ 'Chain A',
700
+ env.DEFAULT_DEPLOYER,
701
+ wasmDir,
702
+ wasmHashes,
703
+ ),
704
+ deployChainContracts(
705
+ env,
706
+ protocol,
707
+ env.EID_B,
708
+ 'Chain B',
709
+ env.CHAIN_B_DEPLOYER,
710
+ wasmDir,
711
+ wasmHashes,
712
+ ),
713
+ ]);
714
+
715
+ const chainA = await initChainClients(env, protocol, addressesA, 'Chain A');
716
+ const chainB = await initChainClients(env, protocol, addressesB, 'Chain B');
717
+
718
+ console.log('\n========================================');
719
+ console.log('šŸ”— GLOBAL SETUP: Wiring Protocol Contracts (Cross-Chain)');
720
+ console.log('========================================\n');
721
+
722
+ await wireChainContracts(env, chainA, chainB, 'Chain A');
723
+ await wireChainContracts(env, chainB, chainA, 'Chain B');
724
+
725
+ provide('chainA', chainA.addresses);
726
+ provide('chainB', chainB.addresses);
727
+ console.log('\nāœ… Chain addresses provided to tests (in-memory)');
728
+ console.log(' Chain A (EID ' + env.EID_A + '):', chainA.addresses.endpointV2);
729
+ console.log(' Chain B (EID ' + env.EID_B + '):', chainB.addresses.endpointV2);
730
+
731
+ console.log('\n========================================');
732
+ console.log('āœ… GLOBAL SETUP COMPLETE (Two-Chain Cross-Chain Ready)');
733
+ console.log('========================================\n');
734
+ } catch (error) {
735
+ await stopStellarLocalnet({ env });
736
+ throw error;
737
+ }
738
+
739
+ return async () => {
740
+ console.log('\n========================================');
741
+ console.log('šŸ›‘ GLOBAL TEARDOWN: Stopping Stellar Localnet');
742
+ console.log('========================================\n');
743
+
744
+ await stopStellarLocalnet({ env });
745
+ };
746
+ };
747
+ }