@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.
- package/LICENSE +23 -0
- package/README.md +108 -0
- package/dist/4UKUSHEU.js +30 -0
- package/dist/4UKUSHEU.js.map +1 -0
- package/dist/7DZ4SVEO.js +46 -0
- package/dist/7DZ4SVEO.js.map +1 -0
- package/dist/CYINPPER.js +293 -0
- package/dist/CYINPPER.js.map +1 -0
- package/dist/DNRE4ENZ.js +56 -0
- package/dist/DNRE4ENZ.js.map +1 -0
- package/dist/GAGBEOCZ.js +25 -0
- package/dist/GAGBEOCZ.js.map +1 -0
- package/dist/PMDPSIA2.js +149 -0
- package/dist/PMDPSIA2.js.map +1 -0
- package/dist/TYAJMKGY.js +183 -0
- package/dist/TYAJMKGY.js.map +1 -0
- package/dist/VRABOZPX.js +364 -0
- package/dist/VRABOZPX.js.map +1 -0
- package/dist/VUOMXK5T.js +6 -0
- package/dist/VUOMXK5T.js.map +1 -0
- package/dist/ZWKXKZVT.js +394 -0
- package/dist/ZWKXKZVT.js.map +1 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +4 -0
- package/dist/client.js.map +1 -0
- package/dist/deploy.d.ts +43 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +4 -0
- package/dist/deploy.js.map +1 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +5 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/localnet-global-setup.d.ts +7 -0
- package/dist/localnet-global-setup.d.ts.map +1 -0
- package/dist/localnet-global-setup.js +6 -0
- package/dist/localnet-global-setup.js.map +1 -0
- package/dist/localnet.d.ts +37 -0
- package/dist/localnet.d.ts.map +1 -0
- package/dist/localnet.js +5 -0
- package/dist/localnet.js.map +1 -0
- package/dist/protocol-global-setup.d.ts +58 -0
- package/dist/protocol-global-setup.d.ts.map +1 -0
- package/dist/protocol-global-setup.js +7 -0
- package/dist/protocol-global-setup.js.map +1 -0
- package/dist/scan.d.ts +59 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +4 -0
- package/dist/scan.js.map +1 -0
- package/dist/secp256k1.d.ts +23 -0
- package/dist/secp256k1.d.ts.map +1 -0
- package/dist/secp256k1.js +4 -0
- package/dist/secp256k1.js.map +1 -0
- package/dist/utils.d.ts +54 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +5 -0
- package/dist/utils.js.map +1 -0
- package/package.json +50 -0
- package/src/client.ts +37 -0
- package/src/deploy.ts +316 -0
- package/src/env.ts +104 -0
- package/src/index.ts +40 -0
- package/src/localnet-global-setup.ts +30 -0
- package/src/localnet.ts +406 -0
- package/src/protocol-global-setup.ts +747 -0
- package/src/scan.ts +255 -0
- package/src/secp256k1.ts +59 -0
- 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
|
+
}
|