@provablehq/aleo-bridge-sdk 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,3882 @@
1
+ import {
2
+ BridgeError,
3
+ DEFAULT_SOLANA_RPC_URL,
4
+ aleoAddressToBytes32,
5
+ aleoProgramAddress,
6
+ buildTransferRemoteInstruction,
7
+ buildXReserveDepositPayload,
8
+ buildXReserveHookData,
9
+ bytes32ToAleoAddress,
10
+ calculateXReserveDepositNonce,
11
+ calculateXReserveMessageHash,
12
+ createSolanaClient,
13
+ evmAddressToXReserveBytes32,
14
+ loadKit,
15
+ quoteIgpGasPayment,
16
+ solanaCustom,
17
+ solanaHttp,
18
+ solanaKeyPair,
19
+ solanaWallet,
20
+ xReserveDepositNonceFromPayload,
21
+ xReserveHexToAleoBytes
22
+ } from "./chunk-OU6GVGG7.js";
23
+
24
+ // src/connections/resolve.ts
25
+ function resolve(registry, clients, chainId, family) {
26
+ const chain = registry.chains.find((entry) => entry.id === chainId);
27
+ if (!chain) throw new BridgeError(`Unknown bridge chain: "${chainId}"`);
28
+ const client = clients[chainId];
29
+ if (!client) throw new BridgeError(`No client is configured for chain "${chainId}"`);
30
+ if (client.family !== chain.family || client.family !== family) {
31
+ throw new BridgeError(`Client "${chainId}" has family "${client.family}"; the registry declares "${chain.family}"`);
32
+ }
33
+ return client;
34
+ }
35
+ function requireEvmClient(registry, clients, chainId) {
36
+ return resolve(registry, clients, chainId, "evm");
37
+ }
38
+ function requireEvmClientWithWallet(registry, clients, chainId, action) {
39
+ const client = requireEvmClient(registry, clients, chainId);
40
+ if (!client.walletClient) throw new BridgeError(`EVM wallet client is required to ${action} on chain "${chainId}"`);
41
+ return client;
42
+ }
43
+ function requireSolanaClient(registry, clients, chainId) {
44
+ return resolve(registry, clients, chainId, "solana");
45
+ }
46
+ function requireSolanaClientWithWallet(registry, clients, chainId, action) {
47
+ const client = requireSolanaClient(registry, clients, chainId);
48
+ if (!client.walletClient) throw new BridgeError(`Solana wallet client is required to ${action} on chain "${chainId}"`);
49
+ return client;
50
+ }
51
+ function requireAleoClient(registry, clients, chainId) {
52
+ return resolve(registry, clients, chainId, "aleo");
53
+ }
54
+ function requireAleoClientWithWallet(registry, clients, chainId, action) {
55
+ const client = requireAleoClient(registry, clients, chainId);
56
+ if (!client.walletClient) throw new BridgeError(`Aleo wallet client is required to ${action} on chain "${chainId}"`);
57
+ return client;
58
+ }
59
+
60
+ // src/protocols/hyperlane/aleo.ts
61
+ import { parsePlaintextValue, readContract } from "@provablehq/veil-core";
62
+
63
+ // src/utils/hyperlane.ts
64
+ import bs58 from "bs58";
65
+ import { getAddress, hexToBytes, isAddress, padHex } from "viem";
66
+ function littleEndianU128(bytes) {
67
+ let value = 0n;
68
+ for (let index = 0; index < bytes.length; index++) {
69
+ value |= BigInt(bytes[index]) << BigInt(index * 8);
70
+ }
71
+ return value;
72
+ }
73
+ function evmAddressToAleoHyperlaneRecipient(address) {
74
+ if (!isAddress(address)) throw new BridgeError(`Invalid Ethereum Hyperlane recipient: ${address}`);
75
+ const recipient = hexToBytes(padHex(getAddress(address), { size: 32 }));
76
+ return [
77
+ littleEndianU128(recipient.slice(0, 16)),
78
+ littleEndianU128(recipient.slice(16, 32))
79
+ ];
80
+ }
81
+ function solanaAddressToAleoHyperlaneRecipient(address) {
82
+ try {
83
+ const recipient = bs58.decode(address);
84
+ if (recipient.length !== 32) throw new Error("invalid public key width");
85
+ return [
86
+ littleEndianU128(recipient.slice(0, 16)),
87
+ littleEndianU128(recipient.slice(16, 32))
88
+ ];
89
+ } catch (cause) {
90
+ throw new BridgeError(`Invalid Solana Hyperlane recipient: ${address}`, { cause });
91
+ }
92
+ }
93
+
94
+ // src/utils/units.ts
95
+ function parseDecimalAmount(amount, decimals) {
96
+ const match = /^(\d+)(?:\.(\d+))?$/.exec(amount.trim());
97
+ if (!match) {
98
+ throw new BridgeError(`Invalid decimal amount "${amount}"`);
99
+ }
100
+ const whole = match[1];
101
+ const frac = match[2] ?? "";
102
+ if (frac.length > decimals) {
103
+ throw new BridgeError(
104
+ `Amount "${amount}" has ${frac.length} fractional digits but the asset supports ${decimals}`
105
+ );
106
+ }
107
+ return BigInt(whole + frac.padEnd(decimals, "0"));
108
+ }
109
+ function formatDecimalAmount(amount, decimals) {
110
+ if (amount < 0n) throw new BridgeError("Atomic amount must be non-negative");
111
+ if (!Number.isSafeInteger(decimals) || decimals < 0) {
112
+ throw new BridgeError("Asset decimals must be a non-negative safe integer");
113
+ }
114
+ if (decimals === 0) return amount.toString();
115
+ const digits = amount.toString().padStart(decimals + 1, "0");
116
+ const whole = digits.slice(0, -decimals);
117
+ const fraction = digits.slice(-decimals).replace(/0+$/, "");
118
+ return fraction ? `${whole}.${fraction}` : whole;
119
+ }
120
+
121
+ // src/protocols/hyperlane/aleo.ts
122
+ var MAX_U64 = (1n << 64n) - 1n;
123
+ var GAS_QUOTE_SCALE = 10000000000n;
124
+ var ZERO_GAS_LIMIT_FALLBACK = 50000n;
125
+ var PLACEHOLDER_FIELDS = [
126
+ "aleoTokenType",
127
+ "aleoTokenOwner",
128
+ "aleoIsm",
129
+ "aleoHook",
130
+ "aleoTokenId",
131
+ "aleoMailboxDefaultHook",
132
+ "aleoMailboxRequiredHook",
133
+ "aleoRemoteRouterRecipient",
134
+ "aleoRemoteRouterGas",
135
+ "aleoRecipient",
136
+ "aleoAllowanceSpender0",
137
+ "aleoAllowanceAmount0",
138
+ "aleoAllowanceSpender1",
139
+ "aleoAllowanceAmount1",
140
+ "aleoAllowanceSpender2",
141
+ "aleoAllowanceAmount2",
142
+ "aleoAllowanceSpender3",
143
+ "aleoAllowanceAmount3"
144
+ ];
145
+ var APP_METADATA_FIELDS = /* @__PURE__ */ new Set([
146
+ "aleoTokenType",
147
+ "aleoTokenOwner",
148
+ "aleoIsm",
149
+ "aleoHook",
150
+ "aleoTokenId"
151
+ ]);
152
+ var MAILBOX_STATE_FIELDS = /* @__PURE__ */ new Set([
153
+ "aleoMailboxDefaultHook",
154
+ "aleoMailboxRequiredHook"
155
+ ]);
156
+ var REMOTE_ROUTER_FIELDS = /* @__PURE__ */ new Set([
157
+ "aleoRemoteRouterRecipient",
158
+ "aleoRemoteRouterGas"
159
+ ]);
160
+ var ALLOWANCE_SPENDER_FIELDS = /* @__PURE__ */ new Set([
161
+ "aleoAllowanceSpender0",
162
+ "aleoAllowanceSpender1",
163
+ "aleoAllowanceSpender2",
164
+ "aleoAllowanceSpender3"
165
+ ]);
166
+ var UNUSED_ALLOWANCE_AMOUNT_FIELDS = /* @__PURE__ */ new Set([
167
+ "aleoAllowanceAmount1",
168
+ "aleoAllowanceAmount2",
169
+ "aleoAllowanceAmount3"
170
+ ]);
171
+ function metadataString(route2, key) {
172
+ const value = route2.metadata?.[key];
173
+ if (typeof value !== "string" || value.length === 0) throw new BridgeError(`Hyperlane route metadata ${key} is missing: ${route2.id}`);
174
+ return value;
175
+ }
176
+ function metadataNumber(route2, key) {
177
+ const value = route2.metadata?.[key];
178
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new BridgeError(`Hyperlane route metadata ${key} is invalid: ${route2.id}`);
179
+ return value;
180
+ }
181
+ function optionalMetadataNumber(route2, key, fallback) {
182
+ return route2.metadata?.[key] == null ? fallback : metadataNumber(route2, key);
183
+ }
184
+ function validatedRoute(registry, params) {
185
+ const { plan } = params;
186
+ if (plan.protocol !== "hyperlane" || plan.route.protocol !== "hyperlane") throw new BridgeError("Aleo transfer_remote requires a Hyperlane transfer plan");
187
+ if (plan.registryVersion !== registry.version) throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
188
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
189
+ if (!route2 || route2.protocol !== "hyperlane") throw new BridgeError(`Hyperlane route is not configured: ${plan.route.id}`);
190
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
191
+ const sourceChain = registry.chains.find((chain) => chain.id === plan.sourceAsset.chainId);
192
+ if (sourceChain?.family !== "aleo") throw new BridgeError("transfer_remote requires an Aleo source asset");
193
+ const destinationChain = registry.chains.find((chain) => chain.id === plan.destinationAsset.chainId);
194
+ if (!destinationChain) throw new BridgeError(`Destination chain is not configured: ${plan.destinationAsset.chainId}`);
195
+ const program = metadataString(route2, "aleoRouterProgram");
196
+ if (!program.endsWith(".aleo")) throw new BridgeError(`Aleo Warp Route program is invalid: ${route2.id}`);
197
+ return { route: route2, program, destinationChain };
198
+ }
199
+ function allowance(route2, index, amountOverride) {
200
+ const amount = amountOverride ?? metadataString(route2, `aleoAllowanceAmount${index}`);
201
+ return `{ spender: ${metadataString(route2, `aleoAllowanceSpender${index}`)}, amount: ${amount}u64 }`;
202
+ }
203
+ function gasConfigBigint(config, field, routeId) {
204
+ const value = config[field];
205
+ if (typeof value !== "bigint" || value < 0n) throw new BridgeError(`Hyperlane gas configuration field ${field} is invalid: ${routeId}`);
206
+ return value;
207
+ }
208
+ async function quote(registry, client, params) {
209
+ const route2 = registry.routes.find((entry) => entry.id === params.routeId);
210
+ if (!route2 || route2.protocol !== "hyperlane") throw new BridgeError(`Hyperlane route is not configured: ${params.routeId}`);
211
+ const sourceAsset = registry.assets.find((asset) => asset.id === route2.sourceAssetId);
212
+ const sourceChain = registry.chains.find((chain) => chain.id === sourceAsset?.chainId);
213
+ if (sourceChain?.family !== "aleo") throw new BridgeError(`Hyperlane gas quotes require an Aleo source asset: ${params.routeId}`);
214
+ const hookManager = metadataString(route2, "aleoHookManagerProgram");
215
+ const igp = metadataString(route2, "aleoMailboxDefaultHook");
216
+ const destination = metadataNumber(route2, "aleoDestinationDomain");
217
+ const gasLimitMetadata = BigInt(metadataString(route2, "aleoRemoteRouterGas"));
218
+ const literal = await readContract(client, {
219
+ programId: hookManager,
220
+ mapping: "destination_gas_configs",
221
+ key: `{ igp: ${igp}, destination: ${destination}u32 }`
222
+ });
223
+ if (literal == null) throw new BridgeError(`Hyperlane destination gas configuration is missing on chain: ${params.routeId}`);
224
+ const config = parsePlaintextValue(literal);
225
+ if (typeof config !== "object" || Array.isArray(config)) throw new BridgeError(`Hyperlane destination gas configuration is malformed: ${params.routeId}`);
226
+ const gasOverhead = gasConfigBigint(config, "gas_overhead", route2.id);
227
+ const exchangeRate = gasConfigBigint(config, "exchange_rate", route2.id);
228
+ const gasPrice = gasConfigBigint(config, "gas_price", route2.id);
229
+ if (exchangeRate === 0n || gasPrice === 0n) throw new BridgeError(`Hyperlane destination gas configuration is unpriced: ${params.routeId}`);
230
+ const gasLimit = gasLimitMetadata === 0n ? ZERO_GAS_LIMIT_FALLBACK : gasLimitMetadata;
231
+ const paymentMicrocredits = (gasLimit + gasOverhead) * gasPrice * exchangeRate / GAS_QUOTE_SCALE;
232
+ if (paymentMicrocredits <= 0n || paymentMicrocredits > MAX_U64) {
233
+ throw new BridgeError(`Hyperlane hook payment does not fit a positive u64: ${paymentMicrocredits}`);
234
+ }
235
+ return {
236
+ routeId: route2.id,
237
+ gasLimit,
238
+ gasOverhead,
239
+ gasPrice,
240
+ exchangeRate,
241
+ paymentMicrocredits,
242
+ // A public quote cannot authorize the program execution needed to price
243
+ // its Aleo network fee. Keep the absent total explicit so callers do not
244
+ // mistake the Hyperlane hook payment for their complete balance need.
245
+ executionFeeMicrocredits: null,
246
+ totalMicrocredits: null
247
+ };
248
+ }
249
+ function buildTransferRemoteCall(registry, params) {
250
+ if (params.mode != null && params.mode !== "caller" && params.mode !== "signer") {
251
+ throw new BridgeError(`Unsupported Aleo Hyperlane transfer mode: ${String(params.mode)}`);
252
+ }
253
+ const gasPayment = params.gasPaymentMicrocredits;
254
+ if (gasPayment != null && (gasPayment <= 0n || gasPayment > MAX_U64)) {
255
+ throw new BridgeError(`gasPaymentMicrocredits must be a positive u64: ${gasPayment}`);
256
+ }
257
+ const { route: route2, program, destinationChain } = validatedRoute(registry, params);
258
+ const amountAtomic = parseDecimalAmount(params.plan.amountIn, params.plan.sourceAsset.decimals);
259
+ const destination = metadataNumber(route2, "aleoDestinationDomain");
260
+ const localDecimals = optionalMetadataNumber(route2, "aleoLocalDecimals", params.plan.sourceAsset.decimals);
261
+ const remoteDecimals = optionalMetadataNumber(route2, "aleoRemoteDecimals", params.plan.destinationAsset.decimals);
262
+ const appMetadata = `{ token_type: ${metadataString(route2, "aleoTokenType")}u8, token_owner: ${metadataString(route2, "aleoTokenOwner")}, ism: ${metadataString(route2, "aleoIsm")}, hook: ${metadataString(route2, "aleoHook")}, token_id: ${metadataString(route2, "aleoTokenId")}, local_decimals: ${localDecimals}u8, remote_decimals: ${remoteDecimals}u8 }`;
263
+ const mailboxState = `{ default_hook: ${metadataString(route2, "aleoMailboxDefaultHook")}, required_hook: ${metadataString(route2, "aleoMailboxRequiredHook")} }`;
264
+ const remoteRouter = `{ domain: ${destination}u32, recipient: ${metadataString(route2, "aleoRemoteRouterRecipient")}, gas: ${metadataString(route2, "aleoRemoteRouterGas")}u128 }`;
265
+ const recipientLimbs = destinationChain.family === "evm" ? evmAddressToAleoHyperlaneRecipient(params.plan.recipient) : destinationChain.family === "solana" ? solanaAddressToAleoHyperlaneRecipient(params.plan.recipient) : void 0;
266
+ const recipient = recipientLimbs ? `[${recipientLimbs[0]}u128, ${recipientLimbs[1]}u128]` : metadataString(route2, "aleoRecipient");
267
+ const allowances = `[${[0, 1, 2, 3].map((index) => allowance(route2, index, index === 0 ? gasPayment?.toString() : void 0)).join(", ")}]`;
268
+ const usesPlaceholderConfiguration = route2.metadata?.aleoPlaceholderConfiguration === true;
269
+ let placeholderFields = route2.metadata?.aleoAppMetadataVerified === true ? PLACEHOLDER_FIELDS.filter((field) => !APP_METADATA_FIELDS.has(field)) : PLACEHOLDER_FIELDS;
270
+ if (route2.metadata?.aleoMailboxStateVerified === true) {
271
+ placeholderFields = placeholderFields.filter((field) => !MAILBOX_STATE_FIELDS.has(field));
272
+ }
273
+ if (route2.metadata?.aleoRemoteRouterVerified === true) {
274
+ placeholderFields = placeholderFields.filter((field) => !REMOTE_ROUTER_FIELDS.has(field));
275
+ }
276
+ if (route2.metadata?.aleoAllowanceSpendersVerified === true) {
277
+ placeholderFields = placeholderFields.filter((field) => !ALLOWANCE_SPENDER_FIELDS.has(field));
278
+ }
279
+ if (route2.metadata?.aleoUnusedAllowancesVerified === true) {
280
+ placeholderFields = placeholderFields.filter((field) => !UNUSED_ALLOWANCE_AMOUNT_FIELDS.has(field));
281
+ }
282
+ if (recipientLimbs) {
283
+ placeholderFields = placeholderFields.filter((field) => field !== "aleoRecipient");
284
+ }
285
+ if (gasPayment != null) {
286
+ placeholderFields = placeholderFields.filter((field) => field !== "aleoAllowanceAmount0");
287
+ }
288
+ const functionName = params.mode === "signer" ? "transfer_remote_as_signer" : "transfer_remote";
289
+ return {
290
+ routeId: route2.id,
291
+ program,
292
+ function: functionName,
293
+ inputs: [
294
+ appMetadata,
295
+ mailboxState,
296
+ remoteRouter,
297
+ `${destination}u32`,
298
+ recipient,
299
+ `${amountAtomic}u128`,
300
+ allowances
301
+ ],
302
+ amountAtomic,
303
+ usesPlaceholderConfiguration,
304
+ placeholderFields: usesPlaceholderConfiguration ? placeholderFields : gasPayment == null ? ["aleoAllowanceAmount0"] : []
305
+ };
306
+ }
307
+ async function execute(registry, client, params) {
308
+ const call = buildTransferRemoteCall(registry, params);
309
+ if (call.usesPlaceholderConfiguration) {
310
+ throw new BridgeError(`Aleo Hyperlane route contains non-executable placeholder configuration: ${call.routeId}`);
311
+ }
312
+ const route2 = registry.routes.find((entry) => entry.id === call.routeId);
313
+ if (route2?.availability !== "active") {
314
+ throw new BridgeError(`Aleo Hyperlane route is not active: ${call.routeId}`);
315
+ }
316
+ if (params.gasPaymentMicrocredits == null) {
317
+ throw new BridgeError(`Aleo Hyperlane execution requires a live hook gas payment; call quote first: ${call.routeId}`);
318
+ }
319
+ const transactionId = await client.executeTransaction({
320
+ program: call.program,
321
+ function: call.function,
322
+ inputs: call.inputs,
323
+ privateFee: params.privateFee ?? false,
324
+ onProgress: async (event) => {
325
+ await params.onProgress?.(event);
326
+ if (event.type === "transaction-prepared") await params.onPrepared?.(event.transaction);
327
+ }
328
+ });
329
+ if (!transactionId) throw new BridgeError("Aleo wallet returned an empty Hyperlane transaction id");
330
+ const receipt = {
331
+ id: transactionId,
332
+ protocol: "hyperlane",
333
+ status: "SOURCE_CONFIRMING",
334
+ sourceTxId: transactionId,
335
+ protocolState: { routeId: call.routeId, sourceProgram: call.program, sourceFunction: call.function }
336
+ };
337
+ await params.onSubmitted?.(receipt);
338
+ return {
339
+ transactionId,
340
+ receipt
341
+ };
342
+ }
343
+
344
+ // src/protocols/hyperlane/evm.ts
345
+ import {
346
+ decodeEventLog,
347
+ decodeFunctionResult,
348
+ encodeFunctionData,
349
+ getAddress as getAddress2,
350
+ isAddress as isAddress2,
351
+ isHash,
352
+ parseAbi,
353
+ zeroAddress
354
+ } from "viem";
355
+ var WARP_ROUTE_ABI = parseAbi([
356
+ "function quoteTransferRemote(uint32 destination, bytes32 recipient, uint256 amount) view returns ((address token, uint256 amount)[] quotes)",
357
+ "function transferRemote(uint32 destination, bytes32 recipient, uint256 amount) payable returns (bytes32 messageId)",
358
+ "event SentTransferRemote(uint32 indexed destination, bytes32 indexed recipient, uint256 amount)"
359
+ ]);
360
+ var ERC20_ABI = parseAbi([
361
+ "function allowance(address owner, address spender) view returns (uint256)",
362
+ "function approve(address spender, uint256 amount) returns (bool)"
363
+ ]);
364
+ var DISPATCH_ID_ABI = parseAbi(["event DispatchId(bytes32 indexed messageId)"]);
365
+ function isHexOfBytes(value, bytes) {
366
+ return new RegExp(`^0x[0-9a-fA-F]{${bytes * 2}}$`).test(value);
367
+ }
368
+ function routeMetadata(registry, plan) {
369
+ if (plan.protocol !== "hyperlane" || plan.route.protocol !== "hyperlane") {
370
+ throw new BridgeError("Ethereum Hyperlane actions require a Hyperlane transfer plan");
371
+ }
372
+ if (plan.registryVersion !== registry.version) {
373
+ throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
374
+ }
375
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
376
+ if (!route2 || route2.protocol !== "hyperlane") {
377
+ throw new BridgeError(`Hyperlane route is not present in the configured registry: ${plan.route.id}`);
378
+ }
379
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) {
380
+ throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
381
+ }
382
+ if (route2.availability !== "active") {
383
+ throw new BridgeError(`Hyperlane route is not executable: ${route2.id}`);
384
+ }
385
+ const metadata2 = route2.metadata;
386
+ if (!metadata2) throw new BridgeError(`Hyperlane route metadata is missing: ${plan.route.id}`);
387
+ const routerAddress = metadata2.routerAddress;
388
+ const sourceChainId = metadata2.sourceChainId;
389
+ const destinationDomain = metadata2.destinationDomain;
390
+ const routerType = metadata2.routerType;
391
+ const tokenAddress = metadata2.tokenAddress;
392
+ const destinationRouter = metadata2.destinationRouter;
393
+ const mailboxAddress = metadata2.mailboxAddress;
394
+ const interchainGasPaymaster = metadata2.interchainGasPaymaster;
395
+ const interchainSecurityModule = metadata2.interchainSecurityModule;
396
+ const registryCommit = metadata2.registryCommit;
397
+ if (typeof routerAddress !== "string" || !isAddress2(routerAddress)) {
398
+ throw new BridgeError(`Hyperlane route has an invalid routerAddress: ${plan.route.id}`);
399
+ }
400
+ if (!Number.isInteger(sourceChainId) || typeof sourceChainId !== "number" || sourceChainId <= 0) {
401
+ throw new BridgeError(`Hyperlane route has an invalid sourceChainId: ${plan.route.id}`);
402
+ }
403
+ if (!Number.isInteger(destinationDomain) || typeof destinationDomain !== "number" || destinationDomain < 0 || destinationDomain > 4294967295) {
404
+ throw new BridgeError(`Hyperlane route has an invalid destinationDomain: ${plan.route.id}`);
405
+ }
406
+ if (routerType !== "native" && routerType !== "collateral") {
407
+ throw new BridgeError(`Hyperlane route has an invalid routerType: ${plan.route.id}`);
408
+ }
409
+ if (routerType === "collateral" && (typeof tokenAddress !== "string" || !isAddress2(tokenAddress))) {
410
+ throw new BridgeError(`Collateral Hyperlane route has an invalid tokenAddress: ${plan.route.id}`);
411
+ }
412
+ if (typeof destinationRouter !== "string" || destinationRouter.length === 0) {
413
+ throw new BridgeError(`Hyperlane route has an invalid destinationRouter: ${plan.route.id}`);
414
+ }
415
+ if (typeof mailboxAddress !== "string" || !isAddress2(mailboxAddress)) {
416
+ throw new BridgeError(`Hyperlane route has an invalid mailboxAddress: ${plan.route.id}`);
417
+ }
418
+ if (typeof interchainGasPaymaster !== "string" || !isAddress2(interchainGasPaymaster)) {
419
+ throw new BridgeError(`Hyperlane route has an invalid interchainGasPaymaster: ${plan.route.id}`);
420
+ }
421
+ if (typeof interchainSecurityModule !== "string" || !isAddress2(interchainSecurityModule)) {
422
+ throw new BridgeError(`Hyperlane route has an invalid interchainSecurityModule: ${plan.route.id}`);
423
+ }
424
+ if (typeof registryCommit !== "string" || !/^[0-9a-f]{40}$/i.test(registryCommit)) {
425
+ throw new BridgeError(`Hyperlane route has an invalid registryCommit: ${plan.route.id}`);
426
+ }
427
+ return {
428
+ routerAddress: getAddress2(routerAddress),
429
+ sourceChainId,
430
+ destinationDomain,
431
+ routerType,
432
+ ...typeof tokenAddress === "string" && isAddress2(tokenAddress) ? { tokenAddress: getAddress2(tokenAddress) } : {},
433
+ destinationRouter,
434
+ mailboxAddress: getAddress2(mailboxAddress),
435
+ interchainGasPaymaster: getAddress2(interchainGasPaymaster),
436
+ interchainSecurityModule: getAddress2(interchainSecurityModule),
437
+ registryCommit,
438
+ requiresApprovalReset: metadata2.requiresApprovalReset === true
439
+ };
440
+ }
441
+ function validateRecipient(recipientBytes32) {
442
+ if (!isHexOfBytes(recipientBytes32, 32)) {
443
+ throw new BridgeError("Hyperlane recipientBytes32 must contain exactly 32 bytes");
444
+ }
445
+ }
446
+ async function rpcCall(client, to, data) {
447
+ const result = await client.publicClient.call({ to, data });
448
+ if (typeof result !== "string" || !result.startsWith("0x")) {
449
+ throw new BridgeError("EVM public client returned an invalid eth_call result");
450
+ }
451
+ return result;
452
+ }
453
+ async function assertChain(client, expectedChainId) {
454
+ const actual = await client.publicClient.getChainId();
455
+ if (actual !== expectedChainId) {
456
+ throw new BridgeError(`EVM wallet is connected to chain ${actual}; expected ${expectedChainId}`);
457
+ }
458
+ }
459
+ async function resolveAccount(client, plan) {
460
+ const account2 = await client.walletClient.getAddress();
461
+ if (!isAddress2(account2)) throw new BridgeError("EVM wallet client account is invalid");
462
+ const normalized = getAddress2(account2);
463
+ if (plan.sender && (!isAddress2(plan.sender) || getAddress2(plan.sender) !== normalized)) {
464
+ throw new BridgeError(`Prepared sender ${plan.sender} does not match connected account ${normalized}`);
465
+ }
466
+ return normalized;
467
+ }
468
+ async function sendTransaction(client, chainId, transaction) {
469
+ const result = await client.walletClient.sendTransaction({
470
+ chainId,
471
+ from: transaction.from,
472
+ to: transaction.to,
473
+ data: transaction.data,
474
+ ...transaction.value ? { value: BigInt(transaction.value) } : {}
475
+ });
476
+ if (!isHash(result)) {
477
+ throw new BridgeError("EVM wallet client returned an invalid transaction hash");
478
+ }
479
+ return result;
480
+ }
481
+ async function waitForReceipt(client, hash, timeoutMs, pollingIntervalMs) {
482
+ const deadline = Date.now() + timeoutMs;
483
+ do {
484
+ const result = await client.publicClient.getTransactionReceipt(hash);
485
+ if (result != null && typeof result === "object") return result;
486
+ if (Date.now() >= deadline) return void 0;
487
+ await new Promise((resolve2) => setTimeout(resolve2, pollingIntervalMs));
488
+ } while (true);
489
+ }
490
+ function assertSuccessfulReceipt(receipt, hash) {
491
+ if (receipt.status === "reverted") throw new BridgeError(`EVM transaction reverted: ${hash}`);
492
+ }
493
+ function messageIdFromReceipt(receipt) {
494
+ for (const log of receipt.logs ?? []) {
495
+ try {
496
+ const signature = log.topics[0];
497
+ if (!signature) continue;
498
+ const decoded = decodeEventLog({
499
+ abi: DISPATCH_ID_ABI,
500
+ data: log.data,
501
+ topics: [signature, ...log.topics.slice(1)],
502
+ strict: false
503
+ });
504
+ const messageId = decoded.args.messageId;
505
+ if (decoded.eventName === "DispatchId" && messageId && isHash(messageId)) return messageId;
506
+ } catch {
507
+ }
508
+ }
509
+ return void 0;
510
+ }
511
+ async function quote2(registry, client, params) {
512
+ validateRecipient(params.recipientBytes32);
513
+ const metadata2 = routeMetadata(registry, params.plan);
514
+ await assertChain(client, metadata2.sourceChainId);
515
+ const sourceAsset = registry.assets.find((asset) => asset.id === params.plan.sourceAsset.id);
516
+ const amountAtomic = parseDecimalAmount(params.plan.amountIn, sourceAsset.decimals);
517
+ const data = encodeFunctionData({
518
+ abi: WARP_ROUTE_ABI,
519
+ functionName: "quoteTransferRemote",
520
+ args: [metadata2.destinationDomain, params.recipientBytes32, amountAtomic]
521
+ });
522
+ const encoded = await rpcCall(client, metadata2.routerAddress, data);
523
+ const quotes = decodeFunctionResult({
524
+ abi: WARP_ROUTE_ABI,
525
+ functionName: "quoteTransferRemote",
526
+ data: encoded
527
+ });
528
+ const nativeValueAtomic = quotes.filter((quote6) => getAddress2(quote6.token) === zeroAddress).reduce((sum, quote6) => sum + quote6.amount, 0n);
529
+ if (metadata2.routerType === "native") {
530
+ if (nativeValueAtomic < amountAtomic) {
531
+ throw new BridgeError("Native Hyperlane quote does not cover the transfer amount");
532
+ }
533
+ return {
534
+ routeId: params.plan.route.id,
535
+ routerAddress: metadata2.routerAddress,
536
+ sourceChainId: metadata2.sourceChainId,
537
+ destinationDomain: metadata2.destinationDomain,
538
+ recipientBytes32: params.recipientBytes32,
539
+ amountAtomic,
540
+ nativeValueAtomic,
541
+ nativeFeeAtomic: nativeValueAtomic - amountAtomic
542
+ };
543
+ }
544
+ const tokenAddress = metadata2.tokenAddress;
545
+ const tokenAmountAtomic = quotes.filter((quote6) => getAddress2(quote6.token) === tokenAddress).reduce((sum, quote6) => sum + quote6.amount, 0n);
546
+ if (tokenAmountAtomic < amountAtomic) {
547
+ throw new BridgeError("Collateral Hyperlane quote does not cover the transfer amount");
548
+ }
549
+ return {
550
+ routeId: params.plan.route.id,
551
+ routerAddress: metadata2.routerAddress,
552
+ sourceChainId: metadata2.sourceChainId,
553
+ destinationDomain: metadata2.destinationDomain,
554
+ recipientBytes32: params.recipientBytes32,
555
+ amountAtomic,
556
+ nativeValueAtomic,
557
+ nativeFeeAtomic: nativeValueAtomic,
558
+ tokenAmountAtomic,
559
+ tokenAddress
560
+ };
561
+ }
562
+ function executionReceipt(plan, status, id, quote6, approvalTxIds, sourceSender, sourceTxId, messageId) {
563
+ return {
564
+ id,
565
+ protocol: "hyperlane",
566
+ status,
567
+ ...sourceTxId ? { sourceTxId } : {},
568
+ ...messageId ? { messageId } : {},
569
+ protocolState: {
570
+ routeId: plan.route.id,
571
+ approvalTxIds: [...approvalTxIds],
572
+ sourceSender,
573
+ recipientBytes32: quote6.recipientBytes32,
574
+ destinationDomain: quote6.destinationDomain,
575
+ nativeValueAtomic: quote6.nativeValueAtomic.toString(),
576
+ amountAtomic: quote6.amountAtomic.toString()
577
+ }
578
+ };
579
+ }
580
+ function checkpointApprovalIds(receipt) {
581
+ const ids = receipt.protocolState.approvalTxIds;
582
+ if (!Array.isArray(ids) || ids.some((id) => typeof id !== "string" || !isHash(id))) {
583
+ throw new BridgeError("Hyperlane checkpoint contains invalid approval transaction ids");
584
+ }
585
+ return [...ids];
586
+ }
587
+ function validateCheckpoint(registry, plan, metadata2, recipientBytes32, receipt) {
588
+ const state = receipt.protocolState;
589
+ const sourceAsset = registry.assets.find((asset) => asset.id === plan.sourceAsset.id);
590
+ if (!sourceAsset) throw new BridgeError(`Hyperlane source asset is not present in the configured registry: ${plan.sourceAsset.id}`);
591
+ const amountAtomic = parseDecimalAmount(plan.amountIn, sourceAsset.decimals).toString();
592
+ if (receipt.protocol !== "hyperlane" || state.routeId !== plan.route.id || state.destinationDomain !== metadata2.destinationDomain || state.amountAtomic !== amountAtomic || typeof state.recipientBytes32 !== "string" || state.recipientBytes32.toLowerCase() !== recipientBytes32.toLowerCase()) {
593
+ throw new BridgeError("Hyperlane checkpoint does not match the prepared transfer");
594
+ }
595
+ }
596
+ async function approvalScanBlock(client, approvalTxIds) {
597
+ let blockNumber;
598
+ for (const approvalTxId of approvalTxIds) {
599
+ const receipt = await client.publicClient.getTransactionReceipt(approvalTxId);
600
+ if (!receipt) continue;
601
+ assertSuccessfulReceipt(receipt, approvalTxId);
602
+ if (typeof receipt.blockNumber === "bigint" && (blockNumber === void 0 || receipt.blockNumber > blockNumber)) {
603
+ blockNumber = receipt.blockNumber;
604
+ }
605
+ }
606
+ return blockNumber;
607
+ }
608
+ async function recoverDispatchFromHistory(client, metadata2, recipientBytes32, receipt, approvalTxIds, options) {
609
+ const sourceSender = receipt.protocolState.sourceSender;
610
+ if (typeof sourceSender !== "string" || !isAddress2(sourceSender)) {
611
+ if (options.required) throw new BridgeError("Cannot safely resume Hyperlane without the source account used by the approval");
612
+ return void 0;
613
+ }
614
+ const fromBlock = await approvalScanBlock(client, approvalTxIds);
615
+ if (fromBlock === void 0) {
616
+ if (options.required) {
617
+ throw new BridgeError("Cannot safely resume Hyperlane because no confirmed approval block is available for source history verification");
618
+ }
619
+ return void 0;
620
+ }
621
+ const amountAtomic = receipt.protocolState.amountAtomic;
622
+ if (typeof amountAtomic !== "string" || !/^\d+$/.test(amountAtomic)) {
623
+ throw new BridgeError("Hyperlane checkpoint contains an invalid source amount");
624
+ }
625
+ const logs = await client.publicClient.getLogs({ address: metadata2.routerAddress, fromBlock });
626
+ const candidates = /* @__PURE__ */ new Set();
627
+ for (const log of logs) {
628
+ try {
629
+ const decoded = decodeEventLog({
630
+ abi: WARP_ROUTE_ABI,
631
+ data: log.data,
632
+ topics: log.topics
633
+ });
634
+ if (decoded.eventName === "SentTransferRemote" && decoded.args.destination === metadata2.destinationDomain && decoded.args.recipient.toLowerCase() === recipientBytes32.toLowerCase() && decoded.args.amount === BigInt(amountAtomic)) {
635
+ candidates.add(log.transactionHash);
636
+ }
637
+ } catch {
638
+ }
639
+ }
640
+ const matches = [];
641
+ for (const transactionHash of candidates) {
642
+ const [transaction, sourceReceipt] = await Promise.all([
643
+ client.publicClient.getTransaction(transactionHash),
644
+ client.publicClient.getTransactionReceipt(transactionHash)
645
+ ]);
646
+ if (!transaction || !sourceReceipt || getAddress2(transaction.from) !== getAddress2(sourceSender) || !transaction.to || getAddress2(transaction.to) !== metadata2.routerAddress) continue;
647
+ assertSuccessfulReceipt(sourceReceipt, transactionHash);
648
+ const messageId = messageIdFromReceipt(sourceReceipt);
649
+ matches.push({
650
+ ...receipt,
651
+ id: messageId ?? transactionHash,
652
+ status: "DELIVERY_PENDING",
653
+ sourceTxId: transactionHash,
654
+ ...messageId ? { messageId } : {}
655
+ });
656
+ }
657
+ if (matches.length > 1) {
658
+ throw new BridgeError("Multiple matching Hyperlane dispatches were found; recovery cannot safely choose one source transaction");
659
+ }
660
+ return matches[0];
661
+ }
662
+ async function getSourceStatus(registry, client, plan, recipientBytes32, receipt) {
663
+ const metadata2 = routeMetadata(registry, plan);
664
+ await assertChain(client, metadata2.sourceChainId);
665
+ validateCheckpoint(registry, plan, metadata2, recipientBytes32, receipt);
666
+ if (receipt.status !== "SOURCE_CONFIRMING") {
667
+ throw new BridgeError("Hyperlane source status requires a source-confirming receipt");
668
+ }
669
+ const sourceTxId = receipt.sourceTxId;
670
+ if (!sourceTxId || !isHash(sourceTxId)) {
671
+ throw new BridgeError("Hyperlane checkpoint is missing the source transaction id");
672
+ }
673
+ const sourceReceipt = await client.publicClient.getTransactionReceipt(sourceTxId);
674
+ if (!sourceReceipt) return receipt;
675
+ assertSuccessfulReceipt(sourceReceipt, sourceTxId);
676
+ const messageId = messageIdFromReceipt(sourceReceipt);
677
+ return {
678
+ ...receipt,
679
+ id: messageId ?? sourceTxId,
680
+ status: "DELIVERY_PENDING",
681
+ ...messageId ? { messageId } : {}
682
+ };
683
+ }
684
+ async function recoverSourceCheckpoint(registry, client, plan, recipientBytes32, checkpoint) {
685
+ if (checkpoint.version !== 1 || checkpoint.intent.bridgeProtocol !== "hyperlane" || checkpoint.route.id !== plan.route.id) {
686
+ throw new BridgeError("Bridge checkpoint does not match the prepared route");
687
+ }
688
+ const metadata2 = routeMetadata(registry, plan);
689
+ const sourceAsset = registry.assets.find((asset) => asset.id === plan.sourceAsset.id);
690
+ if (!sourceAsset) throw new BridgeError(`Hyperlane source asset is not present in the configured registry: ${plan.sourceAsset.id}`);
691
+ const approvals = [...checkpoint.source?.approvalTransactionIds ?? []];
692
+ if (approvals.some((id) => !isHash(id))) throw new BridgeError("Bridge checkpoint contains an invalid approval transaction id");
693
+ const approvalTxIds = approvals;
694
+ const protocolState = {
695
+ routeId: plan.route.id,
696
+ approvalTxIds,
697
+ recipientBytes32,
698
+ destinationDomain: metadata2.destinationDomain,
699
+ nativeValueAtomic: "0",
700
+ amountAtomic: parseDecimalAmount(plan.amountIn, sourceAsset.decimals).toString(),
701
+ ...plan.sender && isAddress2(plan.sender) ? { sourceSender: getAddress2(plan.sender) } : {}
702
+ };
703
+ if (!checkpoint.source?.transactionId) {
704
+ const approvalTxId = approvalTxIds.at(-1);
705
+ if (!approvalTxId) throw new BridgeError("Bridge checkpoint contains no submitted transaction");
706
+ const pending2 = {
707
+ id: approvalTxId,
708
+ protocol: "hyperlane",
709
+ status: "SOURCE_APPROVAL_PENDING",
710
+ protocolState
711
+ };
712
+ const approvalReceipt = await client.publicClient.getTransactionReceipt(approvalTxId);
713
+ if (!approvalReceipt) return pending2;
714
+ assertSuccessfulReceipt(approvalReceipt, approvalTxId);
715
+ const recovered = await recoverDispatchFromHistory(
716
+ client,
717
+ metadata2,
718
+ recipientBytes32,
719
+ pending2,
720
+ approvalTxIds,
721
+ { required: false }
722
+ );
723
+ if (recovered) return recovered;
724
+ return { ...pending2, status: "SOURCE_SUBMISSION_PENDING" };
725
+ }
726
+ if (!isHash(checkpoint.source.transactionId)) throw new BridgeError("Bridge checkpoint contains an invalid source transaction id");
727
+ const pending = {
728
+ id: checkpoint.source.transactionId,
729
+ protocol: "hyperlane",
730
+ status: "SOURCE_CONFIRMING",
731
+ sourceTxId: checkpoint.source.transactionId,
732
+ protocolState
733
+ };
734
+ const observed = await getSourceStatus(registry, client, plan, recipientBytes32, pending);
735
+ if (observed !== pending) return observed;
736
+ return await recoverDispatchFromHistory(
737
+ client,
738
+ metadata2,
739
+ recipientBytes32,
740
+ pending,
741
+ approvalTxIds,
742
+ { required: false }
743
+ ) ?? observed;
744
+ }
745
+ async function execute2(registry, client, params) {
746
+ const pollingIntervalMs = params.pollingIntervalMs ?? 1e3;
747
+ const confirmationTimeoutMs = params.confirmationTimeoutMs ?? 12e4;
748
+ if (!Number.isFinite(pollingIntervalMs) || pollingIntervalMs < 0) {
749
+ throw new BridgeError("pollingIntervalMs must be a non-negative finite number");
750
+ }
751
+ if (!Number.isFinite(confirmationTimeoutMs) || confirmationTimeoutMs < 0) {
752
+ throw new BridgeError("confirmationTimeoutMs must be a non-negative finite number");
753
+ }
754
+ const metadata2 = routeMetadata(registry, params.plan);
755
+ await assertChain(client, metadata2.sourceChainId);
756
+ let approvalTxIds = [];
757
+ if (params.resume) {
758
+ validateCheckpoint(registry, params.plan, metadata2, params.recipientBytes32, params.resume);
759
+ approvalTxIds = checkpointApprovalIds(params.resume);
760
+ if (params.resume.status === "DELIVERY_PENDING") {
761
+ return { approvalTxIds, receipt: params.resume };
762
+ }
763
+ if (params.resume.status === "SOURCE_CONFIRMING") {
764
+ const sourceTxId2 = params.resume.sourceTxId;
765
+ if (!sourceTxId2 || !isHash(sourceTxId2)) throw new BridgeError("Hyperlane checkpoint is missing the source transaction id");
766
+ const sourceReceipt2 = await waitForReceipt(client, sourceTxId2, confirmationTimeoutMs, pollingIntervalMs);
767
+ if (!sourceReceipt2) return { approvalTxIds, receipt: params.resume };
768
+ assertSuccessfulReceipt(sourceReceipt2, sourceTxId2);
769
+ const messageId2 = messageIdFromReceipt(sourceReceipt2);
770
+ return {
771
+ approvalTxIds,
772
+ receipt: {
773
+ ...params.resume,
774
+ id: messageId2 ?? sourceTxId2,
775
+ status: "DELIVERY_PENDING",
776
+ ...messageId2 ? { messageId: messageId2 } : {}
777
+ }
778
+ };
779
+ }
780
+ if (params.resume.status === "SOURCE_SUBMISSION_PENDING") {
781
+ const recovered = await recoverDispatchFromHistory(
782
+ client,
783
+ metadata2,
784
+ params.recipientBytes32,
785
+ params.resume,
786
+ approvalTxIds,
787
+ { required: true }
788
+ );
789
+ if (recovered) return { approvalTxIds, receipt: recovered };
790
+ } else if (params.resume.status === "SOURCE_APPROVAL_PENDING") {
791
+ const approvalTxId = approvalTxIds.at(-1);
792
+ if (!approvalTxId) throw new BridgeError("Hyperlane checkpoint is missing the approval transaction id");
793
+ const approvalReceipt = await waitForReceipt(client, approvalTxId, confirmationTimeoutMs, pollingIntervalMs);
794
+ if (!approvalReceipt) return { approvalTxIds, receipt: params.resume };
795
+ assertSuccessfulReceipt(approvalReceipt, approvalTxId);
796
+ } else {
797
+ throw new BridgeError(`Unsupported Hyperlane resume status: ${params.resume.status}`);
798
+ }
799
+ }
800
+ const transferQuote = await quote2(registry, client, params);
801
+ const account2 = await resolveAccount(client, params.plan);
802
+ if (metadata2.routerType === "collateral") {
803
+ const allowanceData = encodeFunctionData({
804
+ abi: ERC20_ABI,
805
+ functionName: "allowance",
806
+ args: [account2, metadata2.routerAddress]
807
+ });
808
+ const allowanceResult = await rpcCall(client, metadata2.tokenAddress, allowanceData);
809
+ const allowance2 = decodeFunctionResult({
810
+ abi: ERC20_ABI,
811
+ functionName: "allowance",
812
+ data: allowanceResult
813
+ });
814
+ const required = transferQuote.tokenAmountAtomic;
815
+ const approveAndConfirm = async (amount) => {
816
+ const data = encodeFunctionData({
817
+ abi: ERC20_ABI,
818
+ functionName: "approve",
819
+ args: [metadata2.routerAddress, amount]
820
+ });
821
+ const hash = await sendTransaction(client, metadata2.sourceChainId, { from: account2, to: metadata2.tokenAddress, data });
822
+ approvalTxIds.push(hash);
823
+ const checkpoint2 = executionReceipt(params.plan, "SOURCE_APPROVAL_PENDING", hash, transferQuote, approvalTxIds, account2);
824
+ await params.onSubmitted?.(checkpoint2);
825
+ const receipt = await waitForReceipt(client, hash, confirmationTimeoutMs, pollingIntervalMs);
826
+ if (!receipt) return false;
827
+ assertSuccessfulReceipt(receipt, hash);
828
+ return true;
829
+ };
830
+ if (allowance2 < required) {
831
+ if (allowance2 > 0n && metadata2.requiresApprovalReset) {
832
+ if (!await approveAndConfirm(0n)) {
833
+ return {
834
+ approvalTxIds,
835
+ receipt: executionReceipt(params.plan, "SOURCE_APPROVAL_PENDING", approvalTxIds.at(-1), transferQuote, approvalTxIds, account2)
836
+ };
837
+ }
838
+ }
839
+ if (!await approveAndConfirm(required)) {
840
+ return {
841
+ approvalTxIds,
842
+ receipt: executionReceipt(params.plan, "SOURCE_APPROVAL_PENDING", approvalTxIds.at(-1), transferQuote, approvalTxIds, account2)
843
+ };
844
+ }
845
+ }
846
+ }
847
+ const transferData = encodeFunctionData({
848
+ abi: WARP_ROUTE_ABI,
849
+ functionName: "transferRemote",
850
+ args: [transferQuote.destinationDomain, transferQuote.recipientBytes32, transferQuote.amountAtomic]
851
+ });
852
+ const sourceTxId = await sendTransaction(client, metadata2.sourceChainId, {
853
+ from: account2,
854
+ to: transferQuote.routerAddress,
855
+ data: transferData,
856
+ value: `0x${transferQuote.nativeValueAtomic.toString(16)}`
857
+ });
858
+ const checkpoint = executionReceipt(params.plan, "SOURCE_CONFIRMING", sourceTxId, transferQuote, approvalTxIds, account2, sourceTxId);
859
+ await params.onSubmitted?.(checkpoint);
860
+ const sourceReceipt = await waitForReceipt(
861
+ client,
862
+ sourceTxId,
863
+ confirmationTimeoutMs,
864
+ pollingIntervalMs
865
+ );
866
+ if (!sourceReceipt) {
867
+ return {
868
+ approvalTxIds,
869
+ receipt: checkpoint
870
+ };
871
+ }
872
+ assertSuccessfulReceipt(sourceReceipt, sourceTxId);
873
+ const messageId = messageIdFromReceipt(sourceReceipt);
874
+ return {
875
+ approvalTxIds,
876
+ receipt: executionReceipt(
877
+ params.plan,
878
+ "DELIVERY_PENDING",
879
+ messageId ?? sourceTxId,
880
+ transferQuote,
881
+ approvalTxIds,
882
+ account2,
883
+ sourceTxId,
884
+ messageId
885
+ )
886
+ };
887
+ }
888
+
889
+ // src/solana/extractHyperlaneMessageId.ts
890
+ var DISPATCHED_MESSAGE_LOG_PATTERN = /Dispatched message to \d+, ID (0x[0-9a-fA-F]{64})/;
891
+ function extractSolanaHyperlaneMessageId(logs) {
892
+ if (!logs) return void 0;
893
+ for (const line of logs) {
894
+ const match = DISPATCHED_MESSAGE_LOG_PATTERN.exec(line);
895
+ if (match) return match[1];
896
+ }
897
+ return void 0;
898
+ }
899
+
900
+ // src/protocols/hyperlane/solanaMetadata.ts
901
+ var SOLANA_PUBKEY = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
902
+ function requirePubkey(value, field, routeId) {
903
+ if (typeof value !== "string" || !SOLANA_PUBKEY.test(value)) {
904
+ throw new BridgeError(`Solana Hyperlane route has an invalid ${field}: ${routeId}`);
905
+ }
906
+ return value;
907
+ }
908
+ function solanaRouteMetadata(registry, plan) {
909
+ if (plan.protocol !== "hyperlane" || plan.route.protocol !== "hyperlane") {
910
+ throw new BridgeError("Solana Hyperlane actions require a Hyperlane transfer plan");
911
+ }
912
+ if (plan.registryVersion !== registry.version) {
913
+ throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
914
+ }
915
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
916
+ if (!route2 || route2.protocol !== "hyperlane") {
917
+ throw new BridgeError(`Hyperlane route is not present in the configured registry: ${plan.route.id}`);
918
+ }
919
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) {
920
+ throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
921
+ }
922
+ if (route2.availability !== "active") {
923
+ throw new BridgeError(`Hyperlane route is not executable: ${route2.id}`);
924
+ }
925
+ const metadata2 = route2.metadata;
926
+ if (!metadata2) throw new BridgeError(`Solana Hyperlane route metadata is missing: ${plan.route.id}`);
927
+ const routeId = plan.route.id;
928
+ const warpProgramAddress = requirePubkey(metadata2.warpProgramAddress, "warpProgramAddress", routeId);
929
+ const tokenPda = requirePubkey(metadata2.tokenPda, "tokenPda", routeId);
930
+ const nativeCollateralPda = requirePubkey(metadata2.nativeCollateralPda, "nativeCollateralPda", routeId);
931
+ const dispatchAuthorityPda = requirePubkey(metadata2.dispatchAuthorityPda, "dispatchAuthorityPda", routeId);
932
+ const mailboxProgramAddress = requirePubkey(metadata2.mailboxProgramAddress, "mailboxProgramAddress", routeId);
933
+ const mailboxOutboxPda = requirePubkey(metadata2.mailboxOutboxPda, "mailboxOutboxPda", routeId);
934
+ const igpProgramAddress = requirePubkey(metadata2.igpProgramAddress, "igpProgramAddress", routeId);
935
+ const igpProgramDataPda = requirePubkey(metadata2.igpProgramDataPda, "igpProgramDataPda", routeId);
936
+ const igpAccount = requirePubkey(metadata2.igpAccount, "igpAccount", routeId);
937
+ const splNoopProgramAddress = requirePubkey(metadata2.splNoopProgramAddress, "splNoopProgramAddress", routeId);
938
+ const igpOverheadAccountRaw = metadata2.igpOverheadAccount;
939
+ const igpOverheadAccount = igpOverheadAccountRaw == null ? void 0 : requirePubkey(igpOverheadAccountRaw, "igpOverheadAccount", routeId);
940
+ const destinationDomain = metadata2.destinationDomain;
941
+ if (typeof destinationDomain !== "number" || !Number.isInteger(destinationDomain) || destinationDomain < 0 || destinationDomain > 4294967295) {
942
+ throw new BridgeError(`Solana Hyperlane route has an invalid destinationDomain: ${routeId}`);
943
+ }
944
+ const destinationGasAmount = metadata2.destinationGasAmount;
945
+ if (typeof destinationGasAmount !== "string" || !/^\d+$/.test(destinationGasAmount)) {
946
+ throw new BridgeError(`Solana Hyperlane route has an invalid destinationGasAmount: ${routeId}`);
947
+ }
948
+ const registryCommit = metadata2.registryCommit;
949
+ if (typeof registryCommit !== "string" || !/^[0-9a-f]{40}$/i.test(registryCommit)) {
950
+ throw new BridgeError(`Solana Hyperlane route has an invalid registryCommit: ${routeId}`);
951
+ }
952
+ const solanaReviewedAt = metadata2.solanaReviewedAt;
953
+ if (typeof solanaReviewedAt !== "string" || Number.isNaN(Date.parse(solanaReviewedAt))) {
954
+ throw new BridgeError(`Solana Hyperlane route has an invalid solanaReviewedAt: ${routeId}`);
955
+ }
956
+ const solanaConfigSource = metadata2.solanaConfigSource;
957
+ if (typeof solanaConfigSource !== "string" || solanaConfigSource.length === 0) {
958
+ throw new BridgeError(`Solana Hyperlane route has an invalid solanaConfigSource: ${routeId}`);
959
+ }
960
+ return {
961
+ warpProgramAddress,
962
+ tokenPda,
963
+ nativeCollateralPda,
964
+ dispatchAuthorityPda,
965
+ mailboxProgramAddress,
966
+ mailboxOutboxPda,
967
+ igpProgramAddress,
968
+ igpProgramDataPda,
969
+ igpAccount,
970
+ ...igpOverheadAccount == null ? {} : { igpOverheadAccount },
971
+ splNoopProgramAddress,
972
+ destinationDomain,
973
+ destinationGasAmount,
974
+ registryCommit,
975
+ solanaReviewedAt,
976
+ solanaConfigSource
977
+ };
978
+ }
979
+
980
+ // src/protocols/hyperlane/solana.ts
981
+ var GAS_PAYMENT_ACCOUNT_DATA_LENGTH = 141;
982
+ var DISPATCHED_MESSAGE_ACCOUNT_DATA_LENGTH = 194;
983
+ var SOLANA_HYPERLANE_COMPUTE_UNIT_LIMIT = 4e5;
984
+ function accountRole(kit, account2) {
985
+ if (account2.signer && account2.writable) return kit.AccountRole.WRITABLE_SIGNER;
986
+ if (account2.signer) return kit.AccountRole.READONLY_SIGNER;
987
+ if (account2.writable) return kit.AccountRole.WRITABLE;
988
+ return kit.AccountRole.READONLY;
989
+ }
990
+ async function quote3(registry, client, params) {
991
+ const metadata2 = solanaRouteMetadata(registry, params.plan);
992
+ const rpc = client.publicClient;
993
+ const amountLamports = parseDecimalAmount(params.plan.amountIn, params.plan.sourceAsset.decimals);
994
+ const igpAccountData = await rpc.getAccountData(metadata2.igpAccount);
995
+ if (!igpAccountData) throw new BridgeError(`Solana IGP account does not exist: ${metadata2.igpAccount}`);
996
+ const igpPaymentLamports = quoteIgpGasPayment({
997
+ igpAccountData,
998
+ destinationDomain: metadata2.destinationDomain,
999
+ gasAmount: BigInt(metadata2.destinationGasAmount)
1000
+ });
1001
+ if (!params.plan.sender) throw new BridgeError("Solana sender is required to quote the transaction fee");
1002
+ const kit = await loadKit();
1003
+ const uniqueMessageSigner = await kit.generateKeyPairSigner();
1004
+ const built = await buildTransferRemoteInstruction({
1005
+ metadata: metadata2,
1006
+ senderAddress: params.plan.sender,
1007
+ uniqueMessageAddress: uniqueMessageSigner.address,
1008
+ recipientAleoAddress: params.plan.recipient,
1009
+ amountLamports
1010
+ });
1011
+ const { blockhash, lastValidBlockHeight } = await rpc.getLatestBlockhash();
1012
+ const message = kit.pipe(
1013
+ kit.createTransactionMessage({ version: 0 }),
1014
+ (transaction) => kit.setTransactionMessageFeePayer(kit.address(params.plan.sender), transaction),
1015
+ (transaction) => kit.setTransactionMessageLifetimeUsingBlockhash({ blockhash: kit.blockhash(blockhash), lastValidBlockHeight }, transaction),
1016
+ (transaction) => kit.setTransactionMessageComputeUnitLimit(SOLANA_HYPERLANE_COMPUTE_UNIT_LIMIT, transaction),
1017
+ (transaction) => kit.appendTransactionMessageInstruction({
1018
+ programAddress: kit.address(built.programAddress),
1019
+ accounts: built.accounts.map((account2) => ({ address: kit.address(account2.address), role: accountRole(kit, account2) })),
1020
+ data: built.data
1021
+ }, transaction)
1022
+ );
1023
+ const compiled = kit.compileTransaction(message);
1024
+ const [networkFeeLamports, gasPaymentRent, dispatchedMessageRent, senderRent] = await Promise.all([
1025
+ rpc.getFeeForMessage(new Uint8Array(compiled.messageBytes)),
1026
+ rpc.getMinimumBalanceForRentExemption(GAS_PAYMENT_ACCOUNT_DATA_LENGTH),
1027
+ rpc.getMinimumBalanceForRentExemption(DISPATCHED_MESSAGE_ACCOUNT_DATA_LENGTH),
1028
+ rpc.getMinimumBalanceForRentExemption(0)
1029
+ ]);
1030
+ const rentLamports = gasPaymentRent + dispatchedMessageRent + senderRent;
1031
+ return {
1032
+ routeId: params.plan.route.id,
1033
+ amountLamports,
1034
+ igpPaymentLamports,
1035
+ networkFeeLamports,
1036
+ rentLamports,
1037
+ totalLamports: amountLamports + igpPaymentLamports + networkFeeLamports + rentLamports
1038
+ };
1039
+ }
1040
+ async function pollForConfirmation(rpc, signature, pollingIntervalMs, confirmationTimeoutMs, blockhash) {
1041
+ const deadline = Date.now() + confirmationTimeoutMs;
1042
+ do {
1043
+ let status;
1044
+ try {
1045
+ status = await rpc.getSignatureStatus(signature);
1046
+ } catch {
1047
+ status = null;
1048
+ }
1049
+ if (status === "failed") {
1050
+ throw new BridgeError(`Solana Hyperlane transfer failed on-chain: ${signature}`);
1051
+ }
1052
+ if (status === "confirmed" || status === "finalized") return status;
1053
+ try {
1054
+ if (!await rpc.isBlockhashValid(blockhash)) return "expired";
1055
+ } catch {
1056
+ }
1057
+ if (Date.now() >= deadline) return void 0;
1058
+ await new Promise((resolve2) => setTimeout(resolve2, pollingIntervalMs));
1059
+ } while (true);
1060
+ }
1061
+ function buildReceipt(status, signature, routeId, metadata2, uniqueMessageAddress, quote6, blockhash, lastValidBlockHeight, messageId, blockhashExpired = false) {
1062
+ return {
1063
+ id: messageId ?? signature,
1064
+ protocol: "hyperlane",
1065
+ status,
1066
+ sourceTxId: signature,
1067
+ ...messageId ? { messageId } : {},
1068
+ protocolState: {
1069
+ routeId,
1070
+ signature,
1071
+ uniqueMessageAddress,
1072
+ destinationDomain: metadata2.destinationDomain,
1073
+ quotedLamports: quote6.totalLamports.toString(),
1074
+ blockhash,
1075
+ lastValidBlockHeight: lastValidBlockHeight.toString(),
1076
+ ...blockhashExpired ? { blockhashExpired: true } : {},
1077
+ // The transaction confirmed but the Mailbox dispatch log line was
1078
+ // absent or unparsable — note it rather than throwing.
1079
+ ...status === "DELIVERY_PENDING" && !messageId ? { messageIdUnavailable: true } : {}
1080
+ }
1081
+ };
1082
+ }
1083
+ async function getSourceStatus2(client, receipt) {
1084
+ if (receipt.protocol !== "hyperlane" || receipt.status !== "SOURCE_CONFIRMING" || !receipt.sourceTxId) {
1085
+ throw new BridgeError("Solana Hyperlane source status requires a source-confirming receipt");
1086
+ }
1087
+ const status = await client.publicClient.getSignatureStatus(receipt.sourceTxId);
1088
+ if (status == null) {
1089
+ const { blockhash, lastValidBlockHeight } = receipt.protocolState;
1090
+ if (blockhash === void 0 && lastValidBlockHeight === void 0) return receipt;
1091
+ if (typeof blockhash !== "string" || !blockhash || typeof lastValidBlockHeight !== "string" || !/^\d+$/.test(lastValidBlockHeight)) {
1092
+ throw new BridgeError("Solana Hyperlane source receipt has an invalid blockhash lifetime");
1093
+ }
1094
+ try {
1095
+ if (!await client.publicClient.isBlockhashValid(blockhash)) {
1096
+ return {
1097
+ ...receipt,
1098
+ status: "EXPIRED",
1099
+ protocolState: {
1100
+ ...receipt.protocolState,
1101
+ blockhashExpired: true,
1102
+ sourceError: `Solana transaction expired before confirmation: ${receipt.sourceTxId}`
1103
+ }
1104
+ };
1105
+ }
1106
+ } catch {
1107
+ return receipt;
1108
+ }
1109
+ return receipt;
1110
+ }
1111
+ if (status === "processed") return receipt;
1112
+ if (status === "failed") throw new BridgeError(`Solana Hyperlane transfer failed on-chain: ${receipt.sourceTxId}`);
1113
+ const messageId = extractSolanaHyperlaneMessageId(await client.publicClient.getTransactionLogs(receipt.sourceTxId));
1114
+ return {
1115
+ ...receipt,
1116
+ id: messageId ?? receipt.sourceTxId,
1117
+ status: "DELIVERY_PENDING",
1118
+ ...messageId ? { messageId } : {},
1119
+ protocolState: {
1120
+ ...receipt.protocolState,
1121
+ ...!messageId ? { messageIdUnavailable: true } : {}
1122
+ }
1123
+ };
1124
+ }
1125
+ async function execute3(registry, client, params) {
1126
+ const rpc = client.publicClient;
1127
+ const walletClient = client.walletClient;
1128
+ const requestedPollingIntervalMs = params.pollingIntervalMs ?? 1e3;
1129
+ const confirmationTimeoutMs = params.confirmationTimeoutMs ?? 12e4;
1130
+ if (!Number.isFinite(requestedPollingIntervalMs) || requestedPollingIntervalMs < 0) {
1131
+ throw new BridgeError("pollingIntervalMs must be a non-negative finite number");
1132
+ }
1133
+ if (!Number.isFinite(confirmationTimeoutMs) || confirmationTimeoutMs < 0) {
1134
+ throw new BridgeError("confirmationTimeoutMs must be a non-negative finite number");
1135
+ }
1136
+ const pollingIntervalMs = Math.max(requestedPollingIntervalMs, 100);
1137
+ const metadata2 = solanaRouteMetadata(registry, params.plan);
1138
+ if (params.resume) {
1139
+ const receipt = params.resume;
1140
+ const state = receipt.protocolState;
1141
+ if (receipt.protocol !== "hyperlane" || receipt.status !== "SOURCE_CONFIRMING" && receipt.status !== "DELIVERY_PENDING" || typeof receipt.sourceTxId !== "string" || state.routeId !== params.plan.route.id || state.destinationDomain !== metadata2.destinationDomain) {
1142
+ throw new BridgeError("Solana Hyperlane resume receipt does not match the prepared route");
1143
+ }
1144
+ if (receipt.status === "DELIVERY_PENDING" || state.blockhashExpired === true) {
1145
+ return { receipt };
1146
+ }
1147
+ if (typeof state.blockhash !== "string" || typeof state.lastValidBlockHeight !== "string" || !/^\d+$/.test(state.lastValidBlockHeight)) {
1148
+ throw new BridgeError("Solana Hyperlane resume receipt is missing its blockhash lifetime");
1149
+ }
1150
+ const signature2 = receipt.sourceTxId;
1151
+ try {
1152
+ const confirmation = await pollForConfirmation(
1153
+ client.publicClient,
1154
+ signature2,
1155
+ pollingIntervalMs,
1156
+ confirmationTimeoutMs,
1157
+ state.blockhash
1158
+ );
1159
+ if (!confirmation) return { receipt };
1160
+ if (confirmation === "expired") {
1161
+ return { receipt: { ...receipt, protocolState: { ...state, blockhashExpired: true } } };
1162
+ }
1163
+ const messageId = extractSolanaHyperlaneMessageId(await client.publicClient.getTransactionLogs(signature2));
1164
+ return {
1165
+ receipt: {
1166
+ ...receipt,
1167
+ id: messageId ?? signature2,
1168
+ status: "DELIVERY_PENDING",
1169
+ ...messageId ? { messageId } : {},
1170
+ protocolState: {
1171
+ ...state,
1172
+ ...!messageId ? { messageIdUnavailable: true } : {}
1173
+ }
1174
+ }
1175
+ };
1176
+ } catch (error) {
1177
+ if (error instanceof BridgeError && error.message.includes(signature2)) throw error;
1178
+ const message2 = error instanceof Error ? error.message : String(error);
1179
+ throw new BridgeError(`Solana Hyperlane transfer ${signature2} was submitted, but confirmation failed: ${message2}`);
1180
+ }
1181
+ }
1182
+ const senderAddress = await walletClient.getAddress();
1183
+ if (params.plan.sender && params.plan.sender !== senderAddress) {
1184
+ throw new BridgeError(`Prepared sender ${params.plan.sender} does not match connected account ${senderAddress}`);
1185
+ }
1186
+ const transferQuote = await quote3(registry, client, { plan: { ...params.plan, sender: senderAddress } });
1187
+ const requiredLamports = transferQuote.totalLamports;
1188
+ const balance = await rpc.getBalance(senderAddress);
1189
+ if (balance < requiredLamports) {
1190
+ throw new BridgeError(
1191
+ `Insufficient Solana balance for this Hyperlane transfer: balance ${balance} lamports, required ${requiredLamports} lamports (amount ${transferQuote.amountLamports} + gas ${transferQuote.igpPaymentLamports + transferQuote.networkFeeLamports} + rent ${transferQuote.rentLamports})`
1192
+ );
1193
+ }
1194
+ const kit = await loadKit();
1195
+ const uniqueMessageSigner = await kit.generateKeyPairSigner();
1196
+ const built = await buildTransferRemoteInstruction({
1197
+ metadata: metadata2,
1198
+ senderAddress,
1199
+ uniqueMessageAddress: uniqueMessageSigner.address,
1200
+ recipientAleoAddress: params.plan.recipient,
1201
+ amountLamports: transferQuote.amountLamports
1202
+ });
1203
+ const instruction = {
1204
+ programAddress: kit.address(built.programAddress),
1205
+ accounts: built.accounts.map((account2) => ({
1206
+ address: kit.address(account2.address),
1207
+ role: accountRole(kit, account2)
1208
+ })),
1209
+ data: built.data
1210
+ };
1211
+ const { blockhash, lastValidBlockHeight } = await rpc.getLatestBlockhash();
1212
+ const message = kit.pipe(
1213
+ kit.createTransactionMessage({ version: 0 }),
1214
+ (tx) => kit.setTransactionMessageFeePayer(kit.address(senderAddress), tx),
1215
+ (tx) => kit.setTransactionMessageLifetimeUsingBlockhash(
1216
+ { blockhash: kit.blockhash(blockhash), lastValidBlockHeight },
1217
+ tx
1218
+ ),
1219
+ (tx) => kit.setTransactionMessageComputeUnitLimit(SOLANA_HYPERLANE_COMPUTE_UNIT_LIMIT, tx),
1220
+ (tx) => kit.appendTransactionMessageInstruction(instruction, tx)
1221
+ );
1222
+ const compiledTransaction = kit.compileTransaction(message);
1223
+ const signedTransaction = await kit.partiallySignTransaction(
1224
+ [uniqueMessageSigner.keyPair],
1225
+ compiledTransaction
1226
+ );
1227
+ const wireTransaction = new Uint8Array(kit.getTransactionEncoder().encode(signedTransaction));
1228
+ const { signature } = await walletClient.sendTransaction(wireTransaction);
1229
+ const submittedReceipt = buildReceipt(
1230
+ "SOURCE_CONFIRMING",
1231
+ signature,
1232
+ params.plan.route.id,
1233
+ metadata2,
1234
+ uniqueMessageSigner.address,
1235
+ transferQuote,
1236
+ blockhash,
1237
+ lastValidBlockHeight
1238
+ );
1239
+ await params.onSubmitted?.(submittedReceipt);
1240
+ try {
1241
+ const confirmation = await pollForConfirmation(rpc, signature, pollingIntervalMs, confirmationTimeoutMs, blockhash);
1242
+ if (!confirmation) {
1243
+ return {
1244
+ receipt: submittedReceipt
1245
+ };
1246
+ }
1247
+ if (confirmation === "expired") {
1248
+ return {
1249
+ receipt: buildReceipt("SOURCE_CONFIRMING", signature, params.plan.route.id, metadata2, uniqueMessageSigner.address, transferQuote, blockhash, lastValidBlockHeight, void 0, true)
1250
+ };
1251
+ }
1252
+ const logs = await rpc.getTransactionLogs(signature);
1253
+ const messageId = extractSolanaHyperlaneMessageId(logs);
1254
+ return {
1255
+ receipt: buildReceipt("DELIVERY_PENDING", signature, params.plan.route.id, metadata2, uniqueMessageSigner.address, transferQuote, blockhash, lastValidBlockHeight, messageId)
1256
+ };
1257
+ } catch (error) {
1258
+ if (error instanceof BridgeError && error.message.includes(signature)) throw error;
1259
+ const message2 = error instanceof Error ? error.message : String(error);
1260
+ throw new BridgeError(`Solana Hyperlane transfer ${signature} failed after broadcast: ${message2}`, { cause: error });
1261
+ }
1262
+ }
1263
+
1264
+ // src/protocols/xreserve/aleoToEvm.ts
1265
+ var ETHEREUM_DESTINATION_DOMAIN = 0;
1266
+ function validatedRoute2(registry, params) {
1267
+ const { plan } = params;
1268
+ if (plan.protocol !== "xreserve" || plan.route.protocol !== "xreserve") throw new BridgeError("USDCx burn requires an xReserve transfer plan");
1269
+ if (plan.registryVersion !== registry.version) throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
1270
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
1271
+ if (!route2 || route2.protocol !== "xreserve" || route2.availability !== "active") throw new BridgeError(`xReserve route is not executable: ${plan.route.id}`);
1272
+ const sourceChain = registry.chains.find((chain) => chain.id === plan.sourceAsset.chainId);
1273
+ const destinationChain = registry.chains.find((chain) => chain.id === plan.destinationAsset.chainId);
1274
+ if (sourceChain?.family !== "aleo" || destinationChain?.family !== "evm") throw new BridgeError("USDCx burn action requires an Aleo-to-Ethereum route");
1275
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
1276
+ const bridgeProgram = route2.metadata?.bridgeProgram;
1277
+ const wrapperProgram = route2.metadata?.wrapperProgram;
1278
+ const tokenProgram = route2.metadata?.remoteToken;
1279
+ const nativeDomain = route2.metadata?.ethereumDestinationDomain;
1280
+ const withdrawalFee = route2.metadata?.withdrawalFeeAtomic;
1281
+ if (typeof bridgeProgram !== "string" || !bridgeProgram.endsWith(".aleo")) throw new BridgeError(`xReserve bridge program is invalid: ${route2.id}`);
1282
+ if (typeof wrapperProgram !== "string" || !wrapperProgram.endsWith(".aleo")) throw new BridgeError(`xReserve wrapper program is invalid: ${route2.id}`);
1283
+ if (typeof tokenProgram !== "string" || !tokenProgram.endsWith(".aleo")) throw new BridgeError(`xReserve token program is invalid: ${route2.id}`);
1284
+ if (typeof withdrawalFee !== "string" || !/^\d+$/.test(withdrawalFee)) throw new BridgeError(`xReserve withdrawal fee is invalid: ${route2.id}`);
1285
+ if (nativeDomain !== ETHEREUM_DESTINATION_DOMAIN) throw new BridgeError(`xReserve Ethereum destination domain must be ${ETHEREUM_DESTINATION_DOMAIN}: ${route2.id}`);
1286
+ return { route: route2, bridgeProgram, wrapperProgram, tokenProgram, nativeDomain, withdrawalFeeAtomic: BigInt(withdrawalFee) };
1287
+ }
1288
+ function assertPrivateInputs(userRecord, merkleProof, tokenProgram) {
1289
+ if (userRecord == null) throw new BridgeError("private_burn requires a USDCx userRecord input");
1290
+ if (typeof userRecord === "object") {
1291
+ if (userRecord.type !== "record" || userRecord.program !== tokenProgram || userRecord.recordname !== "Token") {
1292
+ throw new BridgeError(`private_burn record requests must select ${tokenProgram}/Token`);
1293
+ }
1294
+ }
1295
+ if (typeof merkleProof !== "string" || !merkleProof.startsWith("[") || !merkleProof.endsWith("]")) {
1296
+ throw new BridgeError("private_burn requires an encoded [MerkleProof; 2] Aleo literal");
1297
+ }
1298
+ }
1299
+ function buildBurnCall(registry, params) {
1300
+ const deployment = validatedRoute2(registry, params);
1301
+ const mode = params.mode ?? "private";
1302
+ if (mode !== "public-as-signer" && mode !== "public" && mode !== "private") throw new BridgeError(`Unsupported USDCx burn mode: ${String(mode)}`);
1303
+ const amountAtomic = parseDecimalAmount(params.plan.amountIn, params.plan.sourceAsset.decimals);
1304
+ if (amountAtomic <= 0n) throw new BridgeError("USDCx burn amount must be greater than zero");
1305
+ if (amountAtomic <= deployment.withdrawalFeeAtomic) {
1306
+ const fee = formatDecimalAmount(deployment.withdrawalFeeAtomic, params.plan.sourceAsset.decimals);
1307
+ throw new BridgeError(`USDCx burn amount must exceed the ${fee} ${params.plan.sourceAsset.symbol} withdrawal fee`);
1308
+ }
1309
+ const nativeRecipientBytes32 = evmAddressToXReserveBytes32(params.plan.recipient);
1310
+ const amount = `${amountAtomic}u128`;
1311
+ const nativeDomain = `${deployment.nativeDomain}u32`;
1312
+ const nativeRecipient = xReserveHexToAleoBytes(nativeRecipientBytes32, 32);
1313
+ if (mode === "private") {
1314
+ assertPrivateInputs(params.userRecord, params.merkleProof, deployment.tokenProgram);
1315
+ return {
1316
+ routeId: deployment.route.id,
1317
+ mode,
1318
+ program: deployment.wrapperProgram,
1319
+ function: "private_burn",
1320
+ inputs: [params.userRecord, amount, nativeDomain, nativeRecipient, params.merkleProof],
1321
+ amountAtomic,
1322
+ nativeDomain: deployment.nativeDomain,
1323
+ nativeRecipientBytes32
1324
+ };
1325
+ }
1326
+ return {
1327
+ routeId: deployment.route.id,
1328
+ mode,
1329
+ program: deployment.bridgeProgram,
1330
+ function: mode === "public" ? "burn_public" : "burn_public_as_signer",
1331
+ inputs: [amount, nativeDomain, nativeRecipient],
1332
+ amountAtomic,
1333
+ nativeDomain: deployment.nativeDomain,
1334
+ nativeRecipientBytes32
1335
+ };
1336
+ }
1337
+ async function execute4(registry, client, params) {
1338
+ const call = buildBurnCall(registry, params);
1339
+ const transactionId = await client.executeTransaction({
1340
+ program: call.program,
1341
+ function: call.function,
1342
+ inputs: call.inputs,
1343
+ privateFee: params.privateFee ?? false,
1344
+ onProgress: async (event) => {
1345
+ await params.onProgress?.(event);
1346
+ if (event.type === "transaction-prepared") await params.onPrepared?.(event.transaction);
1347
+ }
1348
+ });
1349
+ if (!transactionId) throw new BridgeError("Aleo wallet returned an empty burn transaction id");
1350
+ const receipt = {
1351
+ id: transactionId,
1352
+ protocol: "xreserve",
1353
+ status: "SOURCE_CONFIRMING",
1354
+ sourceTxId: transactionId,
1355
+ protocolState: {
1356
+ routeId: call.routeId,
1357
+ burnMode: call.mode,
1358
+ amountAtomic: call.amountAtomic.toString(),
1359
+ nativeDomain: call.nativeDomain,
1360
+ nativeRecipientBytes32: call.nativeRecipientBytes32,
1361
+ sourceProgram: call.program,
1362
+ sourceFunction: call.function,
1363
+ forwardingService: "aleo-burn-attestation"
1364
+ }
1365
+ };
1366
+ await params.onSubmitted?.(receipt);
1367
+ return { transactionId, receipt };
1368
+ }
1369
+
1370
+ // src/protocols/xreserve/evmToAleo.ts
1371
+ import {
1372
+ decodeEventLog as decodeEventLog2,
1373
+ decodeFunctionResult as decodeFunctionResult2,
1374
+ encodeFunctionData as encodeFunctionData2,
1375
+ getAddress as getAddress3,
1376
+ isAddress as isAddress3,
1377
+ isHash as isHash2,
1378
+ isHex,
1379
+ parseAbi as parseAbi2
1380
+ } from "viem";
1381
+ var ERC20_ABI2 = parseAbi2([
1382
+ "function balanceOf(address owner) view returns (uint256)",
1383
+ "function allowance(address owner, address spender) view returns (uint256)",
1384
+ "function approve(address spender, uint256 amount) returns (bool)"
1385
+ ]);
1386
+ var XRESERVE_ABI = parseAbi2([
1387
+ "function depositToRemote(uint256 value, uint32 remoteDomain, bytes32 remoteRecipient, address localToken, uint256 maxFee, bytes hookData)",
1388
+ "event DepositedToRemote(address indexed localToken, uint256 value, address indexed localDepositor, bytes32 indexed remoteRecipient, uint32 remoteDomain, bytes32 remoteToken, uint256 maxFee, bytes hookData)"
1389
+ ]);
1390
+ function metadata(registry, plan) {
1391
+ if (plan.protocol !== "xreserve" || plan.route.protocol !== "xreserve") throw new BridgeError("xReserve actions require an xReserve transfer plan");
1392
+ if (plan.registryVersion !== registry.version) throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
1393
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
1394
+ if (!route2 || route2.availability !== "active") throw new BridgeError(`xReserve route is not executable: ${plan.route.id}`);
1395
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
1396
+ const sourceChain = registry.chains.find((chain) => chain.id === plan.sourceAsset.chainId);
1397
+ if (sourceChain?.family !== "evm" || plan.destinationAsset.chainId !== (route2.environment === "mainnet" ? "aleo" : "aleo-testnet")) {
1398
+ throw new BridgeError("This action supports Ethereum-to-Aleo xReserve deposits only");
1399
+ }
1400
+ const raw = route2.metadata ?? {};
1401
+ const xReserveContract = raw.xReserveContract;
1402
+ const sourceChainId = raw.sourceChainId;
1403
+ const sourceDomain = raw.sourceDomain;
1404
+ const remoteDomain = raw.remoteDomain;
1405
+ const remoteTokenBytes32 = raw.remoteTokenBytes32;
1406
+ const minimumAmountAtomic = raw.minimumAmountAtomic;
1407
+ const maxFeeAtomic = raw.maxFeeAtomic;
1408
+ const bridgeProgram = raw.bridgeProgram;
1409
+ const wrapperProgram = raw.wrapperProgram;
1410
+ const attestationBaseUrl = raw.attestationBaseUrl;
1411
+ if (typeof xReserveContract !== "string" || !isAddress3(xReserveContract)) throw new BridgeError(`xReserve contract is invalid: ${route2.id}`);
1412
+ if (typeof sourceChainId !== "number" || !Number.isSafeInteger(sourceChainId) || sourceChainId <= 0) throw new BridgeError(`xReserve sourceChainId is invalid: ${route2.id}`);
1413
+ if (typeof sourceDomain !== "number" || !Number.isInteger(sourceDomain) || sourceDomain < 0) throw new BridgeError(`xReserve sourceDomain is invalid: ${route2.id}`);
1414
+ if (typeof remoteDomain !== "number" || !Number.isInteger(remoteDomain) || remoteDomain < 0) throw new BridgeError(`xReserve remoteDomain is invalid: ${route2.id}`);
1415
+ if (typeof remoteTokenBytes32 !== "string" || !/^0x[0-9a-f]{64}$/i.test(remoteTokenBytes32)) throw new BridgeError(`xReserve remote token is invalid: ${route2.id}`);
1416
+ if (typeof minimumAmountAtomic !== "string" || !/^\d+$/.test(minimumAmountAtomic)) throw new BridgeError(`xReserve minimum amount is invalid: ${route2.id}`);
1417
+ if (typeof maxFeeAtomic !== "string" || !/^\d+$/.test(maxFeeAtomic)) throw new BridgeError(`xReserve max fee is invalid: ${route2.id}`);
1418
+ if (typeof bridgeProgram !== "string" || !bridgeProgram.endsWith(".aleo")) throw new BridgeError(`xReserve bridge program is invalid: ${route2.id}`);
1419
+ if (typeof wrapperProgram !== "string" || !wrapperProgram.endsWith(".aleo")) throw new BridgeError(`xReserve wrapper program is invalid: ${route2.id}`);
1420
+ if (typeof attestationBaseUrl !== "string" || !attestationBaseUrl.startsWith("https://")) throw new BridgeError(`xReserve attestation URL is invalid: ${route2.id}`);
1421
+ return { xReserveContract: getAddress3(xReserveContract), sourceChainId, sourceDomain, remoteDomain, remoteTokenBytes32, minimumAmountAtomic: BigInt(minimumAmountAtomic), maxFeeAtomic: BigInt(maxFeeAtomic), bridgeProgram, wrapperProgram, attestationBaseUrl };
1422
+ }
1423
+ async function assertChain2(client, expected) {
1424
+ const chain = await client.publicClient.getChainId();
1425
+ if (chain !== expected) throw new BridgeError(`EVM client is connected to chain ${chain}; expected ${expected}`);
1426
+ }
1427
+ async function observedAccount(client, plan, receipt) {
1428
+ const saved = receipt?.protocolState.sourceSender;
1429
+ const candidate = typeof saved === "string" ? saved : plan.sender;
1430
+ if (candidate && isAddress3(candidate)) return getAddress3(candidate);
1431
+ if (client.walletClient) return account(client, plan);
1432
+ throw new BridgeError("Read-only EVM access requires the prepared sender address");
1433
+ }
1434
+ async function account(client, plan) {
1435
+ const value = await client.walletClient.getAddress();
1436
+ if (typeof value !== "string" || !isAddress3(value)) throw new BridgeError("EVM wallet client has no connected account");
1437
+ const resolved = getAddress3(value);
1438
+ if (plan.sender && (!isAddress3(plan.sender) || getAddress3(plan.sender) !== resolved)) throw new BridgeError(`Prepared sender ${plan.sender} does not match connected account ${resolved}`);
1439
+ return resolved;
1440
+ }
1441
+ async function callUint(client, to, data, functionName) {
1442
+ const result = await client.publicClient.call({ to, data });
1443
+ if (typeof result !== "string" || !isHex(result)) throw new BridgeError("EVM public client returned an invalid contract result");
1444
+ return decodeFunctionResult2({ abi: ERC20_ABI2, functionName, data: result });
1445
+ }
1446
+ async function send(client, chainId, transaction) {
1447
+ const hash = await client.walletClient.sendTransaction({ chainId, ...transaction });
1448
+ if (typeof hash !== "string" || !isHash2(hash)) throw new BridgeError("EVM wallet client returned an invalid transaction hash");
1449
+ return hash;
1450
+ }
1451
+ async function wait(client, hash, timeout, interval) {
1452
+ const deadline = Date.now() + timeout;
1453
+ do {
1454
+ const result = await client.publicClient.getTransactionReceipt(hash);
1455
+ if (result && typeof result === "object") return result;
1456
+ if (Date.now() >= deadline) return void 0;
1457
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
1458
+ } while (true);
1459
+ }
1460
+ function successful(receipt, hash) {
1461
+ if (receipt.status === "reverted") throw new BridgeError(`EVM transaction reverted: ${hash}`);
1462
+ }
1463
+ async function quote4(registry, client, params) {
1464
+ const route2 = metadata(registry, params.plan);
1465
+ await assertChain2(client, route2.sourceChainId);
1466
+ const owner = await observedAccount(client, params.plan);
1467
+ const token = params.plan.sourceAsset.locator?.value;
1468
+ if (params.plan.sourceAsset.locator?.kind !== "evm-contract" || !token || !isAddress3(token)) throw new BridgeError("xReserve source token contract is missing");
1469
+ const amountAtomic = parseDecimalAmount(params.plan.amountIn, params.plan.sourceAsset.decimals);
1470
+ if (amountAtomic < route2.minimumAmountAtomic) throw new BridgeError(`xReserve minimum deposit is ${route2.minimumAmountAtomic} atomic units`);
1471
+ const environment = params.plan.route.environment;
1472
+ const hookData = await buildXReserveHookData(
1473
+ params.plan.mintMode,
1474
+ params.plan.recipient,
1475
+ environment,
1476
+ params.privateMintSecretNonce ?? "0scalar"
1477
+ );
1478
+ const recipient = params.plan.mintMode === "private" ? await aleoProgramAddress(route2.wrapperProgram, environment) : params.plan.recipient;
1479
+ const remoteRecipientBytes32 = aleoAddressToBytes32(recipient);
1480
+ const balanceData = encodeFunctionData2({ abi: ERC20_ABI2, functionName: "balanceOf", args: [owner] });
1481
+ const allowanceData = encodeFunctionData2({ abi: ERC20_ABI2, functionName: "allowance", args: [owner, route2.xReserveContract] });
1482
+ const [balanceAtomic, allowanceAtomic] = await Promise.all([
1483
+ callUint(client, getAddress3(token), balanceData, "balanceOf"),
1484
+ callUint(client, getAddress3(token), allowanceData, "allowance")
1485
+ ]);
1486
+ if (balanceAtomic < amountAtomic) throw new BridgeError(`Insufficient ${params.plan.sourceAsset.symbol} balance`);
1487
+ return { routeId: params.plan.route.id, xReserveContract: route2.xReserveContract, tokenAddress: getAddress3(token), sourceChainId: route2.sourceChainId, remoteDomain: route2.remoteDomain, remoteRecipientBytes32, amountAtomic, maxFeeAtomic: route2.maxFeeAtomic, hookData, balanceAtomic, allowanceAtomic, approvalRequired: allowanceAtomic < amountAtomic };
1488
+ }
1489
+ function pendingReceipt(plan, status, id, approvalTxIds, quote6, sourceSender, sourceTxId) {
1490
+ return { id, protocol: "xreserve", status, ...sourceTxId ? { sourceTxId } : {}, protocolState: { routeId: plan.route.id, approvalTxIds, sourceSender, mintMode: plan.mintMode, intendedRecipient: plan.recipient, xReserveContract: quote6.xReserveContract, tokenAddress: quote6.tokenAddress, sourceChainId: quote6.sourceChainId, remoteDomain: quote6.remoteDomain, remoteRecipientBytes32: quote6.remoteRecipientBytes32, hookData: quote6.hookData, amountAtomic: quote6.amountAtomic.toString(), maxFeeAtomic: quote6.maxFeeAtomic.toString() } };
1491
+ }
1492
+ function resumeQuote(plan, receipt) {
1493
+ const state = receipt.protocolState;
1494
+ if (receipt.protocol !== "xreserve" || state.routeId !== plan.route.id) {
1495
+ throw new BridgeError("Checkpoint does not match the prepared xReserve route");
1496
+ }
1497
+ if (state.mintMode !== plan.mintMode || state.intendedRecipient !== plan.recipient) {
1498
+ throw new BridgeError("Checkpoint does not match the prepared xReserve recipient");
1499
+ }
1500
+ if (typeof state.xReserveContract !== "string" || !isAddress3(state.xReserveContract) || typeof state.tokenAddress !== "string" || !isAddress3(state.tokenAddress) || typeof state.sourceChainId !== "number" || typeof state.remoteDomain !== "number" || typeof state.remoteRecipientBytes32 !== "string" || !isHex(state.remoteRecipientBytes32) || typeof state.hookData !== "string" || !isHex(state.hookData) || typeof state.amountAtomic !== "string" || !/^\d+$/.test(state.amountAtomic) || typeof state.maxFeeAtomic !== "string" || !/^\d+$/.test(state.maxFeeAtomic)) {
1501
+ throw new BridgeError("Checkpoint contains invalid xReserve submission state");
1502
+ }
1503
+ return {
1504
+ routeId: plan.route.id,
1505
+ xReserveContract: getAddress3(state.xReserveContract),
1506
+ tokenAddress: getAddress3(state.tokenAddress),
1507
+ sourceChainId: state.sourceChainId,
1508
+ remoteDomain: state.remoteDomain,
1509
+ remoteRecipientBytes32: state.remoteRecipientBytes32,
1510
+ amountAtomic: BigInt(state.amountAtomic),
1511
+ maxFeeAtomic: BigInt(state.maxFeeAtomic),
1512
+ hookData: state.hookData,
1513
+ balanceAtomic: 0n,
1514
+ allowanceAtomic: 0n,
1515
+ approvalRequired: false
1516
+ };
1517
+ }
1518
+ function approvalIds(receipt) {
1519
+ const ids = receipt.protocolState.approvalTxIds;
1520
+ if (!Array.isArray(ids) || ids.some((id) => typeof id !== "string" || !isHash2(id))) {
1521
+ throw new BridgeError("Checkpoint contains invalid xReserve approval transaction ids");
1522
+ }
1523
+ return ids;
1524
+ }
1525
+ function confirmedDepositReceipt(plan, route2, quote6, owner, approvalTxIds, sourceTxId, receipt) {
1526
+ successful(receipt, sourceTxId);
1527
+ let matched;
1528
+ for (const log of receipt.logs ?? []) {
1529
+ if (log.address && getAddress3(log.address) !== route2.xReserveContract) continue;
1530
+ try {
1531
+ const decoded = decodeEventLog2({ abi: XRESERVE_ABI, data: log.data, topics: log.topics });
1532
+ if (decoded.eventName === "DepositedToRemote") matched = { log, args: decoded.args };
1533
+ } catch {
1534
+ }
1535
+ }
1536
+ if (!matched) throw new BridgeError("Confirmed receipt does not contain a valid DepositedToRemote event");
1537
+ const { log: eventLog, args } = matched;
1538
+ if (getAddress3(args.localToken) !== quote6.tokenAddress || getAddress3(args.localDepositor) !== owner || args.value !== quote6.amountAtomic || args.remoteDomain !== route2.remoteDomain || args.remoteRecipient.toLowerCase() !== quote6.remoteRecipientBytes32.toLowerCase() || args.remoteToken.toLowerCase() !== route2.remoteTokenBytes32.toLowerCase() || args.maxFee !== route2.maxFeeAtomic || args.hookData.toLowerCase() !== quote6.hookData.toLowerCase()) throw new BridgeError("DepositedToRemote event does not match the prepared transfer");
1539
+ const rawIndex = eventLog.logIndex;
1540
+ const logIndex = typeof rawIndex === "string" ? Number(BigInt(rawIndex)) : rawIndex;
1541
+ if (!Number.isSafeInteger(logIndex) || logIndex == null || logIndex < 0) throw new BridgeError("DepositedToRemote log index is missing or invalid");
1542
+ const nonce = calculateXReserveDepositNonce(route2.sourceDomain, sourceTxId, logIndex);
1543
+ const payload = buildXReserveDepositPayload({ amount: args.value, remoteDomain: args.remoteDomain, remoteToken: args.remoteToken, remoteRecipient: args.remoteRecipient, localToken: args.localToken, depositor: args.localDepositor, maxFee: args.maxFee, nonce, hookData: args.hookData });
1544
+ const messageHash = calculateXReserveMessageHash(payload);
1545
+ return { id: messageHash, protocol: "xreserve", status: "ATTESTATION_PENDING", sourceTxId, protocolState: { ...pendingReceipt(plan, "ATTESTATION_PENDING", messageHash, approvalTxIds, quote6, owner, sourceTxId).protocolState, sourceDomain: route2.sourceDomain, remoteDomain: route2.remoteDomain, depositLogIndex: logIndex, nonce, payload, messageHash, bridgeProgram: route2.bridgeProgram, wrapperProgram: route2.wrapperProgram } };
1546
+ }
1547
+ async function approvalScanBlock2(client, approvalTxIds) {
1548
+ let blockNumber;
1549
+ for (const approvalTxId of approvalTxIds) {
1550
+ const receipt = await client.publicClient.getTransactionReceipt(approvalTxId);
1551
+ if (!receipt) continue;
1552
+ successful(receipt, approvalTxId);
1553
+ if (typeof receipt.blockNumber === "bigint" && (blockNumber === void 0 || receipt.blockNumber > blockNumber)) {
1554
+ blockNumber = receipt.blockNumber;
1555
+ }
1556
+ }
1557
+ return blockNumber;
1558
+ }
1559
+ async function recoverConfirmedDepositFromHistory(plan, route2, client, quote6, owner, approvalTxIds, options) {
1560
+ const fromBlock = await approvalScanBlock2(client, approvalTxIds);
1561
+ if (fromBlock === void 0) {
1562
+ if (options.required) {
1563
+ throw new BridgeError("Cannot safely resume xReserve because no confirmed approval block is available for source history verification");
1564
+ }
1565
+ return void 0;
1566
+ }
1567
+ const logs = await client.publicClient.getLogs({ address: route2.xReserveContract, fromBlock });
1568
+ const transactionHashes = [...new Set(logs.map((log) => log.transactionHash))];
1569
+ const matches = [];
1570
+ for (const transactionHash of transactionHashes) {
1571
+ const receipt = await client.publicClient.getTransactionReceipt(transactionHash);
1572
+ if (!receipt) continue;
1573
+ try {
1574
+ matches.push(confirmedDepositReceipt(plan, route2, quote6, owner, approvalTxIds, transactionHash, receipt));
1575
+ } catch (error) {
1576
+ if (!(error instanceof BridgeError)) throw error;
1577
+ }
1578
+ }
1579
+ if (matches.length > 1) {
1580
+ throw new BridgeError("Multiple matching xReserve deposits were found; recovery cannot safely choose one source transaction");
1581
+ }
1582
+ return matches[0];
1583
+ }
1584
+ async function getSourceStatus3(registry, client, plan, receipt) {
1585
+ if (receipt.status !== "SOURCE_CONFIRMING") {
1586
+ throw new BridgeError("xReserve source status requires a source-confirming receipt");
1587
+ }
1588
+ const route2 = metadata(registry, plan);
1589
+ const owner = await observedAccount(client, plan, receipt);
1590
+ const transferQuote = resumeQuote(plan, receipt);
1591
+ const approvalTxIds = approvalIds(receipt);
1592
+ const sourceTxId = receipt.sourceTxId;
1593
+ if (!sourceTxId || !isHash2(sourceTxId)) {
1594
+ throw new BridgeError("Checkpoint is missing the xReserve source transaction id");
1595
+ }
1596
+ const sourceReceipt = await client.publicClient.getTransactionReceipt(sourceTxId);
1597
+ if (!sourceReceipt) return receipt;
1598
+ return confirmedDepositReceipt(plan, route2, transferQuote, owner, approvalTxIds, sourceTxId, sourceReceipt);
1599
+ }
1600
+ async function recoverSourceCheckpoint2(registry, client, plan, checkpoint) {
1601
+ if (checkpoint.version !== 1 || checkpoint.intent.bridgeProtocol !== "xreserve" || checkpoint.route.id !== plan.route.id) {
1602
+ throw new BridgeError("Bridge checkpoint does not match the prepared route");
1603
+ }
1604
+ const route2 = metadata(registry, plan);
1605
+ await assertChain2(client, route2.sourceChainId);
1606
+ const owner = await observedAccount(client, plan);
1607
+ const token = plan.sourceAsset.locator?.value;
1608
+ if (plan.sourceAsset.locator?.kind !== "evm-contract" || !token || !isAddress3(token)) {
1609
+ throw new BridgeError("xReserve source token contract is missing");
1610
+ }
1611
+ const amountAtomic = parseDecimalAmount(plan.amountIn, plan.sourceAsset.decimals);
1612
+ const storedHookData = checkpoint.source?.hookData;
1613
+ if (storedHookData !== void 0 && (!isHex(storedHookData, { strict: true }) || storedHookData.length !== 132)) {
1614
+ throw new BridgeError("Bridge checkpoint contains invalid xReserve hook data");
1615
+ }
1616
+ const hookData = storedHookData ?? await buildXReserveHookData(
1617
+ plan.mintMode,
1618
+ plan.recipient,
1619
+ plan.route.environment,
1620
+ "0scalar"
1621
+ );
1622
+ const recipient = plan.mintMode === "private" ? await aleoProgramAddress(route2.wrapperProgram, plan.route.environment) : plan.recipient;
1623
+ const quote6 = {
1624
+ routeId: plan.route.id,
1625
+ xReserveContract: route2.xReserveContract,
1626
+ tokenAddress: getAddress3(token),
1627
+ sourceChainId: route2.sourceChainId,
1628
+ remoteDomain: route2.remoteDomain,
1629
+ remoteRecipientBytes32: aleoAddressToBytes32(recipient),
1630
+ amountAtomic,
1631
+ maxFeeAtomic: route2.maxFeeAtomic,
1632
+ hookData,
1633
+ balanceAtomic: 0n,
1634
+ allowanceAtomic: 0n,
1635
+ approvalRequired: false
1636
+ };
1637
+ const approvals = [...checkpoint.source?.approvalTransactionIds ?? []];
1638
+ if (approvals.some((id) => !isHash2(id))) {
1639
+ throw new BridgeError("Bridge checkpoint contains an invalid approval transaction id");
1640
+ }
1641
+ const approvalTxIds = approvals;
1642
+ if (!checkpoint.source?.transactionId) {
1643
+ const approvalTxId = approvalTxIds.at(-1);
1644
+ if (!approvalTxId) throw new BridgeError("Bridge checkpoint contains no submitted transaction");
1645
+ const pending2 = pendingReceipt(plan, "SOURCE_APPROVAL_PENDING", approvalTxId, approvalTxIds, quote6, owner);
1646
+ const approvalReceipt = await client.publicClient.getTransactionReceipt(approvalTxId);
1647
+ if (!approvalReceipt) return pending2;
1648
+ successful(approvalReceipt, approvalTxId);
1649
+ const recovered = await recoverConfirmedDepositFromHistory(
1650
+ plan,
1651
+ route2,
1652
+ client,
1653
+ quote6,
1654
+ owner,
1655
+ approvalTxIds,
1656
+ { required: false }
1657
+ );
1658
+ if (recovered) return recovered;
1659
+ return { ...pending2, status: "SOURCE_SUBMISSION_PENDING" };
1660
+ }
1661
+ if (!isHash2(checkpoint.source.transactionId)) {
1662
+ throw new BridgeError("Bridge checkpoint contains an invalid source transaction id");
1663
+ }
1664
+ const pending = pendingReceipt(
1665
+ plan,
1666
+ "SOURCE_CONFIRMING",
1667
+ checkpoint.source.transactionId,
1668
+ approvalTxIds,
1669
+ quote6,
1670
+ owner,
1671
+ checkpoint.source.transactionId
1672
+ );
1673
+ const observed = await getSourceStatus3(registry, client, plan, pending);
1674
+ if (observed !== pending) return observed;
1675
+ return await recoverConfirmedDepositFromHistory(
1676
+ plan,
1677
+ route2,
1678
+ client,
1679
+ quote6,
1680
+ owner,
1681
+ approvalTxIds,
1682
+ { required: false }
1683
+ ) ?? observed;
1684
+ }
1685
+ async function execute5(registry, client, params) {
1686
+ const pollingIntervalMs = params.pollingIntervalMs ?? 1e3;
1687
+ const confirmationTimeoutMs = params.confirmationTimeoutMs ?? 12e4;
1688
+ if (!Number.isFinite(pollingIntervalMs) || pollingIntervalMs < 0 || !Number.isFinite(confirmationTimeoutMs) || confirmationTimeoutMs < 0) throw new BridgeError("Receipt polling controls must be non-negative finite numbers");
1689
+ const route2 = metadata(registry, params.plan);
1690
+ const owner = await account(client, params.plan);
1691
+ let transferQuote;
1692
+ let approvalTxIds = [];
1693
+ if (params.resume?.status === "ATTESTATION_PENDING") {
1694
+ resumeQuote(params.plan, params.resume);
1695
+ return { approvalTxIds: approvalIds(params.resume), receipt: params.resume };
1696
+ }
1697
+ if (params.resume?.status === "SOURCE_CONFIRMING") {
1698
+ transferQuote = resumeQuote(params.plan, params.resume);
1699
+ approvalTxIds = approvalIds(params.resume);
1700
+ const sourceTxId2 = params.resume.sourceTxId;
1701
+ if (!sourceTxId2 || !isHash2(sourceTxId2)) throw new BridgeError("Checkpoint is missing the xReserve source transaction id");
1702
+ const receipt2 = await wait(client, sourceTxId2, confirmationTimeoutMs, pollingIntervalMs);
1703
+ if (!receipt2) return { approvalTxIds, receipt: params.resume };
1704
+ return { approvalTxIds, receipt: confirmedDepositReceipt(params.plan, route2, transferQuote, owner, approvalTxIds, sourceTxId2, receipt2) };
1705
+ }
1706
+ if (params.resume?.status === "SOURCE_SUBMISSION_PENDING") {
1707
+ const checkpointQuote = resumeQuote(params.plan, params.resume);
1708
+ approvalTxIds = approvalIds(params.resume);
1709
+ const recovered = await recoverConfirmedDepositFromHistory(
1710
+ params.plan,
1711
+ route2,
1712
+ client,
1713
+ checkpointQuote,
1714
+ owner,
1715
+ approvalTxIds,
1716
+ { required: true }
1717
+ );
1718
+ if (recovered) return { approvalTxIds, receipt: recovered };
1719
+ transferQuote = await quote4(registry, client, params);
1720
+ if (transferQuote.hookData.toLowerCase() !== checkpointQuote.hookData.toLowerCase()) {
1721
+ throw new BridgeError("Private mint secret nonce does not match the checkpointed approval");
1722
+ }
1723
+ if (transferQuote.approvalRequired) {
1724
+ throw new BridgeError("The recovered xReserve approval allowance is no longer available. Inspect source history before starting another transfer.");
1725
+ }
1726
+ } else if (params.resume?.status === "SOURCE_APPROVAL_PENDING") {
1727
+ resumeQuote(params.plan, params.resume);
1728
+ approvalTxIds = approvalIds(params.resume);
1729
+ const approvalTxId = params.resume.id;
1730
+ if (!isHash2(approvalTxId)) throw new BridgeError("Checkpoint is missing the xReserve approval transaction id");
1731
+ const receipt2 = await wait(client, approvalTxId, confirmationTimeoutMs, pollingIntervalMs);
1732
+ if (!receipt2) return { approvalTxIds, receipt: params.resume };
1733
+ successful(receipt2, approvalTxId);
1734
+ transferQuote = await quote4(registry, client, params);
1735
+ } else if (params.resume) {
1736
+ throw new BridgeError(`Unsupported xReserve resume status: ${params.resume.status}`);
1737
+ } else {
1738
+ transferQuote = await quote4(registry, client, params);
1739
+ }
1740
+ if (transferQuote.approvalRequired) {
1741
+ const data2 = encodeFunctionData2({ abi: ERC20_ABI2, functionName: "approve", args: [route2.xReserveContract, transferQuote.amountAtomic] });
1742
+ const hash = await send(client, route2.sourceChainId, { from: owner, to: transferQuote.tokenAddress, data: data2 });
1743
+ approvalTxIds.push(hash);
1744
+ const submitted2 = pendingReceipt(params.plan, "SOURCE_APPROVAL_PENDING", hash, approvalTxIds, transferQuote, owner);
1745
+ await params.onSubmitted?.(submitted2);
1746
+ const receipt2 = await wait(client, hash, confirmationTimeoutMs, pollingIntervalMs);
1747
+ if (!receipt2) return { approvalTxIds, receipt: submitted2 };
1748
+ successful(receipt2, hash);
1749
+ }
1750
+ const data = encodeFunctionData2({ abi: XRESERVE_ABI, functionName: "depositToRemote", args: [transferQuote.amountAtomic, route2.remoteDomain, transferQuote.remoteRecipientBytes32, transferQuote.tokenAddress, route2.maxFeeAtomic, transferQuote.hookData] });
1751
+ const sourceTxId = await send(client, route2.sourceChainId, { from: owner, to: route2.xReserveContract, data });
1752
+ const submitted = pendingReceipt(params.plan, "SOURCE_CONFIRMING", sourceTxId, approvalTxIds, transferQuote, owner, sourceTxId);
1753
+ await params.onSubmitted?.(submitted);
1754
+ const receipt = await wait(client, sourceTxId, confirmationTimeoutMs, pollingIntervalMs);
1755
+ if (!receipt) return { approvalTxIds, receipt: submitted };
1756
+ return { approvalTxIds, receipt: confirmedDepositReceipt(params.plan, route2, transferQuote, owner, approvalTxIds, sourceTxId, receipt) };
1757
+ }
1758
+ async function getAttestation(registry, transport, params) {
1759
+ const route2 = registry.routes.find((entry) => entry.id === params.routeId);
1760
+ if (!route2) throw new BridgeError(`Unknown bridge route: ${params.routeId}`);
1761
+ if (route2.protocol !== "xreserve" || route2.availability !== "active") throw new BridgeError(`xReserve route is not executable: ${params.routeId}`);
1762
+ const attestationBaseUrl = route2.metadata?.attestationBaseUrl;
1763
+ if (typeof attestationBaseUrl !== "string" || !attestationBaseUrl.startsWith("https://")) throw new BridgeError(`xReserve attestation URL is invalid: ${params.routeId}`);
1764
+ const response = await transport(`${attestationBaseUrl}/${params.messageHash}`, params.signal ? { signal: params.signal } : void 0);
1765
+ if (response.status === 404) return { status: "pending", messageHash: params.messageHash };
1766
+ if (!response.ok) throw new BridgeError(`Circle attester request failed with HTTP ${response.status}`);
1767
+ const body = await response.json();
1768
+ const value = body.attestation;
1769
+ if (!value || typeof value.payload !== "string" || !isHex(value.payload) || typeof value.attestation !== "string" || !isHex(value.attestation) || typeof value.messageHash !== "string" || !isHash2(value.messageHash) || value.messageHash.toLowerCase() !== params.messageHash.toLowerCase()) throw new BridgeError("Circle attester returned an invalid response");
1770
+ if (calculateXReserveMessageHash(value.payload) !== params.messageHash) throw new BridgeError("Circle attestation payload does not match the requested message hash");
1771
+ return { status: "complete", messageHash: params.messageHash, payload: value.payload, attestation: value.attestation };
1772
+ }
1773
+ async function complete(registry, client, params) {
1774
+ const { plan, deposit, attestation } = params;
1775
+ if (plan.protocol !== "xreserve" || plan.route.protocol !== "xreserve" || plan.mintMode !== "private") {
1776
+ throw new BridgeError("private_mint requires a private xReserve transfer plan");
1777
+ }
1778
+ if (plan.registryVersion !== registry.version) throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
1779
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
1780
+ if (!route2 || route2.protocol !== "xreserve" || route2.availability !== "active") throw new BridgeError(`xReserve route is not executable: ${plan.route.id}`);
1781
+ const wrapperProgram = route2.metadata?.wrapperProgram;
1782
+ if (typeof wrapperProgram !== "string" || !wrapperProgram.endsWith(".aleo")) throw new BridgeError(`xReserve wrapper program is invalid: ${plan.route.id}`);
1783
+ if (deposit.protocol !== "xreserve" || deposit.status !== "ATTESTATION_PENDING") throw new BridgeError("Private mint requires a confirmed xReserve deposit awaiting attestation");
1784
+ if (attestation.status !== "complete") throw new BridgeError("Private mint requires a completed Circle attestation");
1785
+ const depositPayload = deposit.protocolState.payload;
1786
+ const depositHash = deposit.protocolState.messageHash;
1787
+ const intendedRecipient = deposit.protocolState.intendedRecipient;
1788
+ const mintMode = deposit.protocolState.mintMode;
1789
+ if (typeof depositPayload !== "string" || !isHex(depositPayload, { strict: true })) throw new BridgeError("Deposit receipt is missing the canonical xReserve payload");
1790
+ if (typeof depositHash !== "string" || !isHash2(depositHash)) throw new BridgeError("Deposit receipt is missing the Circle message hash");
1791
+ if (typeof intendedRecipient !== "string" || intendedRecipient !== plan.recipient || mintMode !== "private") throw new BridgeError("Deposit receipt does not match the private mint plan");
1792
+ if (attestation.payload.toLowerCase() !== depositPayload.toLowerCase() || attestation.messageHash.toLowerCase() !== depositHash.toLowerCase()) throw new BridgeError("Circle attestation does not match the confirmed deposit");
1793
+ if (calculateXReserveMessageHash(attestation.payload) !== attestation.messageHash) throw new BridgeError("Circle attestation payload has an invalid message hash");
1794
+ const secretNonce = params.privateMintSecretNonce ?? "0scalar";
1795
+ const expectedHookData = await buildXReserveHookData("private", plan.recipient, route2.environment, secretNonce);
1796
+ const attestedHookData = `0x${attestation.payload.slice(-130)}`;
1797
+ if (attestedHookData.toLowerCase() !== expectedHookData.toLowerCase()) {
1798
+ throw new BridgeError("Private mint secret nonce and recipient do not match the attested hook data");
1799
+ }
1800
+ const transactionId = await client.executeTransaction({
1801
+ program: wrapperProgram,
1802
+ function: "private_mint",
1803
+ inputs: [
1804
+ xReserveHexToAleoBytes(attestation.payload, 305),
1805
+ xReserveHexToAleoBytes(attestation.attestation, 65),
1806
+ xReserveHexToAleoBytes(attestation.messageHash, 32),
1807
+ secretNonce,
1808
+ plan.recipient
1809
+ ],
1810
+ privateFee: params.privateFee ?? false,
1811
+ onProgress: async (event) => {
1812
+ await params.onProgress?.(event);
1813
+ if (event.type === "transaction-prepared") await params.onPrepared?.(event.transaction);
1814
+ }
1815
+ });
1816
+ if (!transactionId) throw new BridgeError("Aleo wallet returned an empty private mint transaction id");
1817
+ const receipt = {
1818
+ ...deposit,
1819
+ status: "DESTINATION_CONFIRMING",
1820
+ destinationTxId: transactionId,
1821
+ protocolState: {
1822
+ ...deposit.protocolState,
1823
+ attestation: attestation.attestation,
1824
+ destinationProgram: wrapperProgram,
1825
+ destinationFunction: "private_mint",
1826
+ secretNonce
1827
+ }
1828
+ };
1829
+ await params.onSubmitted?.(receipt);
1830
+ return { transactionId, receipt };
1831
+ }
1832
+
1833
+ // src/actions/internal/resolveTransferRoute.ts
1834
+ function resolveTransferRoute(registry, plan) {
1835
+ if (plan.registryVersion !== registry.version) {
1836
+ throw new BridgeError(`Transfer plan uses registry ${plan.registryVersion}; expected ${registry.version}`);
1837
+ }
1838
+ const route2 = registry.routes.find((entry) => entry.id === plan.route.id);
1839
+ if (!route2 || route2.protocol !== plan.protocol || plan.route.protocol !== plan.protocol) {
1840
+ throw new BridgeError(`Transfer plan route does not match the configured registry: ${plan.route.id}`);
1841
+ }
1842
+ if (route2.sourceAssetId !== plan.sourceAsset.id || route2.destinationAssetId !== plan.destinationAsset.id) {
1843
+ throw new BridgeError(`Transfer plan assets do not match configured route: ${route2.id}`);
1844
+ }
1845
+ const sourceAsset = registry.assets.find((asset) => asset.id === route2.sourceAssetId);
1846
+ const destinationAsset = registry.assets.find((asset) => asset.id === route2.destinationAssetId);
1847
+ if (!sourceAsset || sourceAsset.chainId !== plan.sourceAsset.chainId || !destinationAsset || destinationAsset.chainId !== plan.destinationAsset.chainId) {
1848
+ throw new BridgeError(`Transfer plan asset chains do not match configured route: ${route2.id}`);
1849
+ }
1850
+ const sourceChain = registry.chains.find((chain) => chain.id === sourceAsset.chainId);
1851
+ const destinationChain = registry.chains.find((chain) => chain.id === destinationAsset.chainId);
1852
+ if (!sourceChain || !destinationChain) {
1853
+ throw new BridgeError(`Transfer plan references an unknown chain: ${route2.id}`);
1854
+ }
1855
+ return { route: route2, sourceAsset, destinationAsset, sourceChain, destinationChain };
1856
+ }
1857
+
1858
+ // src/actions/createBridgeCheckpoint.ts
1859
+ function createBridgeCheckpoint(plan, receipt) {
1860
+ if (receipt.protocol !== plan.protocol || receipt.protocolState.routeId !== plan.route.id) {
1861
+ throw new BridgeError("Bridge receipt does not match the prepared route");
1862
+ }
1863
+ const rawApprovals = receipt.protocolState.approvalTxIds;
1864
+ if (rawApprovals !== void 0 && (!Array.isArray(rawApprovals) || rawApprovals.some((value) => typeof value !== "string"))) {
1865
+ throw new BridgeError("Bridge receipt contains invalid approval transaction identifiers");
1866
+ }
1867
+ const approvals = [...rawApprovals ?? []];
1868
+ const sourceSender = receipt.protocolState.sourceSender;
1869
+ if (sourceSender !== void 0 && typeof sourceSender !== "string") {
1870
+ throw new BridgeError("Bridge receipt contains an invalid source sender");
1871
+ }
1872
+ const sender = plan.sender ?? sourceSender;
1873
+ const preparedTransaction = receipt.protocolState.preparedTransaction;
1874
+ if (preparedTransaction !== void 0 && (typeof preparedTransaction !== "string" || !preparedTransaction)) {
1875
+ throw new BridgeError("Bridge receipt contains an invalid prepared transaction");
1876
+ }
1877
+ const preparedDestinationTransaction = receipt.protocolState.preparedDestinationTransaction;
1878
+ if (preparedDestinationTransaction !== void 0 && (typeof preparedDestinationTransaction !== "string" || !preparedDestinationTransaction)) {
1879
+ throw new BridgeError("Bridge receipt contains an invalid prepared destination transaction");
1880
+ }
1881
+ const blockhash = receipt.protocolState.blockhash;
1882
+ const lastValidBlockHeight = receipt.protocolState.lastValidBlockHeight;
1883
+ if ((blockhash !== void 0 || lastValidBlockHeight !== void 0) && (typeof blockhash !== "string" || !blockhash || typeof lastValidBlockHeight !== "string" || !/^\d+$/.test(lastValidBlockHeight))) {
1884
+ throw new BridgeError("Bridge receipt contains an invalid Solana blockhash lifetime");
1885
+ }
1886
+ const source = approvals.length > 0 || receipt.sourceTxId || preparedTransaction ? {
1887
+ ...approvals.length > 0 ? { approvalTransactionIds: approvals } : {},
1888
+ ...receipt.sourceTxId ? { transactionId: receipt.sourceTxId } : {},
1889
+ ...typeof receipt.protocolState.hookData === "string" ? { hookData: receipt.protocolState.hookData } : {},
1890
+ ...typeof blockhash === "string" && typeof lastValidBlockHeight === "string" ? { blockhash, lastValidBlockHeight } : {},
1891
+ ...typeof preparedTransaction === "string" ? { preparedTransaction: { transactionId: receipt.id, serializedTransaction: preparedTransaction } } : {}
1892
+ } : void 0;
1893
+ const balanceBeforeAtomic = receipt.protocolState.destinationBalanceBeforeAtomic;
1894
+ const expectedIncreaseAtomic = receipt.protocolState.expectedDestinationIncreaseAtomic;
1895
+ if ((balanceBeforeAtomic !== void 0 || expectedIncreaseAtomic !== void 0) && (typeof balanceBeforeAtomic !== "string" || !/^\d+$/.test(balanceBeforeAtomic) || typeof expectedIncreaseAtomic !== "string" || !/^\d+$/.test(expectedIncreaseAtomic))) {
1896
+ throw new BridgeError("Bridge receipt contains invalid destination balance verification state");
1897
+ }
1898
+ return {
1899
+ version: 1,
1900
+ intent: {
1901
+ source: { chain: plan.sourceAsset.chainId, asset: plan.sourceAsset.key },
1902
+ destination: { chain: plan.destinationAsset.chainId, asset: plan.destinationAsset.key },
1903
+ bridgeProtocol: plan.protocol,
1904
+ amount: plan.amountIn,
1905
+ recipient: plan.recipient,
1906
+ ...sender ? { sender } : {},
1907
+ ...plan.destinationAsset.locator?.kind === "aleo-program" ? { mintMode: plan.mintMode } : {}
1908
+ },
1909
+ route: { id: plan.route.id, registryVersion: plan.registryVersion },
1910
+ ...source ? { source } : {},
1911
+ ...receipt.destinationTxId || typeof preparedDestinationTransaction === "string" ? {
1912
+ destination: {
1913
+ ...receipt.destinationTxId ? { transactionId: receipt.destinationTxId } : {},
1914
+ ...typeof preparedDestinationTransaction === "string" ? { preparedTransaction: { transactionId: receipt.id, serializedTransaction: preparedDestinationTransaction } } : {}
1915
+ }
1916
+ } : {},
1917
+ ...typeof balanceBeforeAtomic === "string" && typeof expectedIncreaseAtomic === "string" ? { deliveryVerification: { balanceBeforeAtomic, expectedIncreaseAtomic } } : {}
1918
+ };
1919
+ }
1920
+
1921
+ // src/actions/internal/readDestinationBalance.ts
1922
+ import { decodeFunctionResult as decodeFunctionResult3, encodeFunctionData as encodeFunctionData3, getAddress as getAddress4, parseAbi as parseAbi3 } from "viem";
1923
+ var ERC20_BALANCE_ABI = parseAbi3(["function balanceOf(address owner) view returns (uint256)"]);
1924
+ async function readDestinationBalance(registry, clients, plan) {
1925
+ const chain = registry.chains.find((candidate) => candidate.id === plan.destinationAsset.chainId);
1926
+ if (!chain) throw new BridgeError(`Unknown destination chain: ${plan.destinationAsset.chainId}`);
1927
+ if (!clients[chain.id]) return void 0;
1928
+ if (chain.family === "evm") {
1929
+ const recipient = getAddress4(plan.recipient);
1930
+ const client = requireEvmClient(registry, clients, chain.id).publicClient;
1931
+ if (plan.destinationAsset.locator?.kind === "native") return client.getBalance(recipient);
1932
+ if (plan.destinationAsset.locator?.kind !== "evm-contract") return void 0;
1933
+ const data = encodeFunctionData3({
1934
+ abi: ERC20_BALANCE_ABI,
1935
+ functionName: "balanceOf",
1936
+ args: [recipient]
1937
+ });
1938
+ const result = await client.call({ to: getAddress4(plan.destinationAsset.locator.value), data });
1939
+ return decodeFunctionResult3({ abi: ERC20_BALANCE_ABI, functionName: "balanceOf", data: result });
1940
+ }
1941
+ if (chain.family === "solana" && plan.destinationAsset.locator?.kind === "native") {
1942
+ return requireSolanaClient(registry, clients, chain.id).publicClient.getBalance(plan.recipient);
1943
+ }
1944
+ return void 0;
1945
+ }
1946
+
1947
+ // src/actions/execute.ts
1948
+ function withDeliveryVerification(receipt, verification) {
1949
+ return verification ? { ...receipt, protocolState: { ...receipt.protocolState, ...verification } } : receipt;
1950
+ }
1951
+ function submissionCheckpoint(params) {
1952
+ return params.onCheckpoint ? async (receipt) => {
1953
+ await params.onCheckpoint?.(createBridgeCheckpoint(params.plan, receipt));
1954
+ } : void 0;
1955
+ }
1956
+ function preparedAleoCheckpoint(params, verification) {
1957
+ return params.onCheckpoint ? async (transaction) => {
1958
+ await params.onCheckpoint?.(createBridgeCheckpoint(params.plan, {
1959
+ id: transaction.id,
1960
+ protocol: params.plan.protocol,
1961
+ status: "SOURCE_SUBMISSION_PENDING",
1962
+ protocolState: {
1963
+ routeId: params.plan.route.id,
1964
+ preparedTransaction: JSON.stringify(transaction),
1965
+ ...verification
1966
+ }
1967
+ }));
1968
+ } : void 0;
1969
+ }
1970
+ function aleoHyperlaneMode(mode) {
1971
+ if (mode == null) return void 0;
1972
+ if (mode === "caller" || mode === "signer") return mode;
1973
+ throw new BridgeError(`Aleo Hyperlane does not support execution mode "${mode}"`);
1974
+ }
1975
+ function xReserveBurnMode(mode) {
1976
+ if (mode == null) return void 0;
1977
+ if (mode === "private" || mode === "public" || mode === "public-as-signer") return mode;
1978
+ throw new BridgeError(`Aleo xReserve does not support execution mode "${mode}"`);
1979
+ }
1980
+ async function execute6(registry, clients, params) {
1981
+ const chain = resolveTransferRoute(registry, params.plan).sourceChain;
1982
+ const chainId = chain.id;
1983
+ const onSubmitted = submissionCheckpoint(params);
1984
+ if (params.plan.protocol === "hyperlane" && chain.family === "evm") {
1985
+ const execution = await execute2(
1986
+ registry,
1987
+ requireEvmClientWithWallet(registry, clients, chainId, "execute Hyperlane transfer"),
1988
+ {
1989
+ plan: params.plan,
1990
+ recipientBytes32: aleoAddressToBytes32(params.plan.recipient),
1991
+ pollingIntervalMs: params.pollingIntervalMs,
1992
+ confirmationTimeoutMs: params.confirmationTimeoutMs,
1993
+ onSubmitted
1994
+ }
1995
+ );
1996
+ return { kind: "evm-hyperlane", ...execution };
1997
+ }
1998
+ if (params.plan.protocol === "hyperlane" && chain.family === "solana") {
1999
+ const execution = await execute3(
2000
+ registry,
2001
+ requireSolanaClientWithWallet(registry, clients, chainId, "execute Hyperlane transfer"),
2002
+ {
2003
+ plan: params.plan,
2004
+ pollingIntervalMs: params.pollingIntervalMs,
2005
+ confirmationTimeoutMs: params.confirmationTimeoutMs,
2006
+ onSubmitted
2007
+ }
2008
+ );
2009
+ return { kind: "solana-hyperlane", ...execution };
2010
+ }
2011
+ if (params.plan.protocol === "hyperlane" && chain.family === "aleo") {
2012
+ const client = requireAleoClientWithWallet(registry, clients, chainId, "execute Hyperlane transfer");
2013
+ const destinationBalanceBefore = await readDestinationBalance(registry, clients, params.plan);
2014
+ const verification = destinationBalanceBefore === void 0 ? void 0 : {
2015
+ destinationBalanceBeforeAtomic: destinationBalanceBefore.toString(),
2016
+ expectedDestinationIncreaseAtomic: parseDecimalAmount(
2017
+ params.plan.amountIn,
2018
+ params.plan.destinationAsset.decimals
2019
+ ).toString()
2020
+ };
2021
+ const onAleoSubmitted = params.onCheckpoint ? async (receipt) => {
2022
+ await params.onCheckpoint?.(createBridgeCheckpoint(
2023
+ params.plan,
2024
+ withDeliveryVerification(receipt, verification)
2025
+ ));
2026
+ } : void 0;
2027
+ const gasPaymentMicrocredits = params.gasPaymentMicrocredits ?? (await quote(
2028
+ registry,
2029
+ requireAleoClient(registry, clients, chainId).publicClient,
2030
+ { routeId: params.plan.route.id }
2031
+ )).paymentMicrocredits;
2032
+ const execution = await execute(registry, client.walletClient, {
2033
+ plan: params.plan,
2034
+ mode: aleoHyperlaneMode(params.mode),
2035
+ privateFee: params.privateFee,
2036
+ gasPaymentMicrocredits,
2037
+ onSubmitted: onAleoSubmitted,
2038
+ onProgress: params.onProgress,
2039
+ onPrepared: preparedAleoCheckpoint(params, verification)
2040
+ });
2041
+ return {
2042
+ kind: "aleo-hyperlane",
2043
+ ...execution,
2044
+ receipt: withDeliveryVerification(execution.receipt, verification)
2045
+ };
2046
+ }
2047
+ if (params.plan.protocol === "xreserve" && chain.family === "evm") {
2048
+ const execution = await execute5(
2049
+ registry,
2050
+ requireEvmClientWithWallet(registry, clients, chainId, "execute xReserve transfer"),
2051
+ {
2052
+ plan: params.plan,
2053
+ pollingIntervalMs: params.pollingIntervalMs,
2054
+ confirmationTimeoutMs: params.confirmationTimeoutMs,
2055
+ onSubmitted,
2056
+ privateMintSecretNonce: params.privateMintSecretNonce
2057
+ }
2058
+ );
2059
+ return { kind: "evm-xreserve", ...execution };
2060
+ }
2061
+ if (params.plan.protocol === "xreserve" && chain.family === "aleo") {
2062
+ const execution = await execute4(
2063
+ registry,
2064
+ requireAleoClientWithWallet(registry, clients, chainId, "execute xReserve burn").walletClient,
2065
+ {
2066
+ plan: params.plan,
2067
+ mode: xReserveBurnMode(params.mode),
2068
+ userRecord: params.userRecord,
2069
+ merkleProof: params.merkleProof,
2070
+ privateFee: params.privateFee,
2071
+ onSubmitted,
2072
+ onProgress: params.onProgress,
2073
+ onPrepared: preparedAleoCheckpoint(params)
2074
+ }
2075
+ );
2076
+ return { kind: "aleo-xreserve", ...execution };
2077
+ }
2078
+ throw new BridgeError(`Unsupported ${params.plan.protocol} source chain family: ${chain.family}`);
2079
+ }
2080
+
2081
+ // src/actions/prepare.ts
2082
+ function executor(chain) {
2083
+ if (chain.family === "aleo") return "aleo-wallet";
2084
+ if (chain.family === "evm") return "evm-wallet";
2085
+ return "solana-wallet";
2086
+ }
2087
+ function xreserveSteps(route2, source, destination, sourceChain, destinationChain, mintMode) {
2088
+ if (sourceChain.family === "evm" && destinationChain.family === "aleo") {
2089
+ return [
2090
+ { key: "source-approval", kind: "approve", chainId: source.chainId, executor: "evm-wallet", description: `Approve the Circle xReserve contract to spend ${source.symbol}.`, irreversible: false },
2091
+ { key: "source-deposit", kind: "deposit", chainId: source.chainId, executor: "evm-wallet", description: `Deposit ${source.symbol} into Circle xReserve for Aleo.`, irreversible: true },
2092
+ { key: "deposit-attestation", kind: "wait-attestation", executor: "protocol", description: "Wait for Circle to attest the confirmed reserve deposit.", irreversible: false },
2093
+ { key: "destination-mint", kind: "mint", chainId: destination.chainId, executor: mintMode === "private" ? "aleo-wallet" : "protocol", description: mintMode === "private" ? `Submit private_mint to mint attested ${destination.symbol} through the shielded wrapper.` : `Wait for attested ${destination.symbol} to mint as an Aleo ${mintMode === "record" ? "record" : "public balance"}.`, irreversible: false }
2094
+ ];
2095
+ }
2096
+ if (sourceChain.family === "aleo" && destinationChain.family === "evm") {
2097
+ return [
2098
+ { key: "source-burn", kind: "burn", chainId: source.chainId, executor: "aleo-wallet", description: `Burn ${source.symbol} and create the xReserve withdrawal intent; private burn is the default.`, irreversible: true },
2099
+ { key: "withdrawal-attestation", kind: "wait-attestation", executor: "protocol", description: "Wait for the Aleo burn attestation service to forward the accepted burn to Circle.", irreversible: false },
2100
+ { key: "destination-withdrawal", kind: "withdraw", chainId: destination.chainId, executor: "protocol", description: `Wait for Circle to release ${destination.symbol} to the native recipient.`, irreversible: false },
2101
+ { key: "destination-confirmation", kind: "confirm-delivery", chainId: destination.chainId, executor: "protocol", description: "Confirm the destination USDC balance change.", irreversible: false }
2102
+ ];
2103
+ }
2104
+ throw new BridgeError(`Unsupported xReserve route direction: ${route2.id}`);
2105
+ }
2106
+ function hyperlaneSteps(source, destination, sourceChain, destinationChain) {
2107
+ const steps = [];
2108
+ if (source.kind === "token" && sourceChain.family !== "aleo") {
2109
+ steps.push({
2110
+ key: "source-approval",
2111
+ kind: "approve",
2112
+ chainId: source.chainId,
2113
+ executor: executor(sourceChain),
2114
+ description: `Approve the Hyperlane Warp Route to spend ${source.symbol}.`,
2115
+ irreversible: false
2116
+ });
2117
+ }
2118
+ steps.push(
2119
+ { key: "source-dispatch", kind: "dispatch", chainId: source.chainId, executor: executor(sourceChain), description: `Dispatch ${source.symbol} through its Hyperlane Warp Route.`, irreversible: true },
2120
+ { key: "message-delivery", kind: "wait-delivery", executor: "protocol", description: "Wait for the Hyperlane message to be relayed and processed.", irreversible: false },
2121
+ { key: "destination-confirmation", kind: "confirm-delivery", chainId: destination.chainId, executor: "protocol", description: `Confirm relayer delivery of ${destination.symbol} on the destination chain.`, irreversible: false }
2122
+ );
2123
+ return steps;
2124
+ }
2125
+ function prepare(registry, params) {
2126
+ const sourceAsset = registry.assets.find((asset) => asset.chainId === params.source.chain && asset.key === params.source.asset);
2127
+ if (!sourceAsset) throw new BridgeError(`Unknown source asset ${params.source.asset} on ${params.source.chain}`);
2128
+ const destinationAsset = registry.assets.find((asset) => asset.chainId === params.destination.chain && asset.key === params.destination.asset);
2129
+ if (!destinationAsset) throw new BridgeError(`Unknown destination asset ${params.destination.asset} on ${params.destination.chain}`);
2130
+ const routes2 = registry.routes.filter((entry) => entry.sourceAssetId === sourceAsset.id && entry.destinationAssetId === destinationAsset.id && entry.availability !== "disabled" && (params.bridgeProtocol == null || entry.protocol === params.bridgeProtocol));
2131
+ if (routes2.length === 0) {
2132
+ throw new BridgeError(`No bridge route from ${params.source.chain}/${params.source.asset} to ${params.destination.chain}/${params.destination.asset}`);
2133
+ }
2134
+ if (routes2.length > 1) {
2135
+ throw new BridgeError(`Multiple bridge routes match ${params.source.chain}/${params.source.asset} to ${params.destination.chain}/${params.destination.asset}; specify bridgeProtocol`);
2136
+ }
2137
+ const route2 = routes2[0];
2138
+ const sourceChain = registry.chains.find((chain) => chain.id === sourceAsset.chainId);
2139
+ const destinationChain = registry.chains.find((chain) => chain.id === destinationAsset.chainId);
2140
+ if (params.privateRecipient === true && params.mintMode != null && params.mintMode !== "private") {
2141
+ throw new BridgeError("privateRecipient conflicts with the selected mintMode");
2142
+ }
2143
+ const mintMode = params.mintMode ?? (params.privateRecipient === true ? "private" : "public");
2144
+ if ((params.mintMode != null || params.privateRecipient === true) && destinationChain.family !== "aleo") {
2145
+ throw new BridgeError("Aleo mint mode is only valid when the destination chain is Aleo");
2146
+ }
2147
+ if (route2.protocol !== "xreserve" && mintMode !== "public") {
2148
+ throw new BridgeError("record and private mint modes are only supported by xReserve routes");
2149
+ }
2150
+ const atomic = parseDecimalAmount(params.amount, sourceAsset.decimals);
2151
+ if (atomic <= 0n) throw new BridgeError("Bridge transfer amount must be greater than zero");
2152
+ parseDecimalAmount(params.amount, destinationAsset.decimals);
2153
+ if (destinationAsset.addressValidationRegex) {
2154
+ const regex = new RegExp(destinationAsset.addressValidationRegex);
2155
+ if (!regex.test(params.recipient)) {
2156
+ throw new BridgeError(`Recipient does not match ${destinationAsset.chainId} address format`);
2157
+ }
2158
+ }
2159
+ const steps = route2.protocol === "xreserve" ? xreserveSteps(route2, sourceAsset, destinationAsset, sourceChain, destinationChain, mintMode) : hyperlaneSteps(sourceAsset, destinationAsset, sourceChain, destinationChain);
2160
+ const fees = [];
2161
+ return {
2162
+ registryVersion: registry.version,
2163
+ protocol: route2.protocol,
2164
+ route: route2,
2165
+ sourceAsset,
2166
+ destinationAsset,
2167
+ amountIn: params.amount,
2168
+ // Both current protocols preserve display units before live fee deduction.
2169
+ // Omit a promise about net output until fee quoting is implemented.
2170
+ recipient: params.recipient,
2171
+ ...params.sender == null ? {} : { sender: params.sender },
2172
+ mintMode,
2173
+ privateRecipient: mintMode === "private",
2174
+ fees,
2175
+ steps
2176
+ };
2177
+ }
2178
+
2179
+ // src/actions/quote.ts
2180
+ async function quote5(registry, clients, params) {
2181
+ const plan = prepare(registry, params);
2182
+ const protocolParams = {
2183
+ plan,
2184
+ privateMintSecretNonce: params.privateMintSecretNonce
2185
+ };
2186
+ const chain = resolveTransferRoute(registry, plan).sourceChain;
2187
+ const chainId = chain.id;
2188
+ if (plan.protocol === "hyperlane" && chain.family === "evm") {
2189
+ const client = requireEvmClient(registry, clients, chainId);
2190
+ const quote6 = await quote2(registry, client, {
2191
+ plan,
2192
+ recipientBytes32: aleoAddressToBytes32(plan.recipient)
2193
+ });
2194
+ return { kind: "evm-hyperlane", plan, ...quote6 };
2195
+ }
2196
+ if (plan.protocol === "hyperlane" && chain.family === "solana") {
2197
+ const quote6 = await quote3(
2198
+ registry,
2199
+ requireSolanaClient(registry, clients, chainId),
2200
+ protocolParams
2201
+ );
2202
+ return { kind: "solana-hyperlane", plan, ...quote6 };
2203
+ }
2204
+ if (plan.protocol === "hyperlane" && chain.family === "aleo") {
2205
+ const quote6 = await quote(
2206
+ registry,
2207
+ requireAleoClient(registry, clients, chainId).publicClient,
2208
+ { routeId: plan.route.id }
2209
+ );
2210
+ return { kind: "aleo-hyperlane", plan, ...quote6 };
2211
+ }
2212
+ if (plan.protocol === "xreserve" && chain.family === "evm") {
2213
+ const quote6 = await quote4(
2214
+ registry,
2215
+ requireEvmClient(registry, clients, chainId),
2216
+ protocolParams
2217
+ );
2218
+ return { kind: "evm-xreserve", plan, ...quote6 };
2219
+ }
2220
+ if (plan.protocol === "xreserve" && chain.family === "aleo") {
2221
+ const rawFee = plan.route.metadata?.withdrawalFeeAtomic;
2222
+ if (typeof rawFee !== "string" || !/^\d+$/.test(rawFee)) {
2223
+ throw new BridgeError(`xReserve withdrawal fee is missing or invalid: ${plan.route.id}`);
2224
+ }
2225
+ const feeAtomic = BigInt(rawFee);
2226
+ const amountAtomic = parseDecimalAmount(plan.amountIn, plan.sourceAsset.decimals);
2227
+ const formattedFee = formatDecimalAmount(feeAtomic, plan.sourceAsset.decimals);
2228
+ if (amountAtomic <= feeAtomic) {
2229
+ throw new BridgeError(`xReserve burn amount must exceed the ${formattedFee} ${plan.sourceAsset.symbol} withdrawal fee`);
2230
+ }
2231
+ const amountOutAtomic = amountAtomic - feeAtomic;
2232
+ return {
2233
+ kind: "aleo-xreserve",
2234
+ plan,
2235
+ routeId: plan.route.id,
2236
+ protocol: "xreserve",
2237
+ amountIn: plan.amountIn,
2238
+ // xReserve preserves the displayed denomination across USDC and USDCx.
2239
+ // The remaining atomic amount is still expressed in source units here;
2240
+ // formatting it with destination decimals would change its value.
2241
+ amountOut: formatDecimalAmount(amountOutAtomic, plan.sourceAsset.decimals),
2242
+ fees: [
2243
+ ...plan.fees,
2244
+ {
2245
+ kind: "protocol",
2246
+ chainId,
2247
+ assetId: plan.sourceAsset.id,
2248
+ amount: formattedFee,
2249
+ estimated: false
2250
+ }
2251
+ ],
2252
+ status: "not-queried"
2253
+ };
2254
+ }
2255
+ throw new BridgeError(`Unsupported ${plan.protocol} source chain family: ${chain.family}`);
2256
+ }
2257
+
2258
+ // src/actions/complete.ts
2259
+ import { classifyBroadcastError, DuplicateTransactionError } from "@provablehq/veil-core";
2260
+ import { isHash as isHash3, isHex as isHex2 } from "viem";
2261
+ async function complete2(registry, clients, params) {
2262
+ let plan;
2263
+ let receipt;
2264
+ if (params.progress) {
2265
+ if (params.progress.next !== "complete") {
2266
+ throw new BridgeError("Bridge progress has no destination action to complete");
2267
+ }
2268
+ plan = params.progress.plan;
2269
+ receipt = params.progress.receipt;
2270
+ } else if (params.plan && params.receipt) {
2271
+ plan = params.plan;
2272
+ receipt = params.receipt;
2273
+ } else {
2274
+ throw new BridgeError("Bridge completion requires recovered progress");
2275
+ }
2276
+ const route2 = resolveTransferRoute(registry, plan);
2277
+ if (receipt.status !== "DESTINATION_ACTION_REQUIRED" || receipt.nextAction?.kind !== "xreserve-private-mint" || receipt.nextAction.chainId !== route2.destinationChain.id) {
2278
+ throw new BridgeError("Bridge receipt has no supported destination action ready");
2279
+ }
2280
+ if (route2.route.protocol !== "xreserve" || route2.sourceChain.family !== "evm" || route2.destinationChain.family !== "aleo") {
2281
+ throw new BridgeError("Destination completion is not implemented for this bridge route");
2282
+ }
2283
+ const payload = receipt.protocolState.payload;
2284
+ const messageHash = receipt.protocolState.messageHash;
2285
+ const attestation = receipt.protocolState.attestation;
2286
+ if (typeof payload !== "string" || !isHex2(payload, { strict: true }) || typeof messageHash !== "string" || !isHash3(messageHash) || typeof attestation !== "string" || !isHex2(attestation, { strict: true })) {
2287
+ throw new BridgeError("Ready xReserve receipt is missing its validated Circle attestation");
2288
+ }
2289
+ const preparedTransaction = receipt.protocolState.preparedDestinationTransaction;
2290
+ if (preparedTransaction !== void 0) {
2291
+ if (typeof preparedTransaction !== "string" || !preparedTransaction) {
2292
+ throw new BridgeError("Prepared Aleo destination recovery is missing its serialized transaction");
2293
+ }
2294
+ let decoded;
2295
+ try {
2296
+ decoded = JSON.parse(preparedTransaction);
2297
+ } catch (error) {
2298
+ throw new BridgeError("Prepared Aleo destination recovery contains an invalid serialized transaction", { cause: error });
2299
+ }
2300
+ const transactionId = decoded && typeof decoded === "object" ? decoded.id : void 0;
2301
+ if (typeof transactionId !== "string" || transactionId !== receipt.id) {
2302
+ throw new BridgeError("Prepared Aleo destination recovery transaction id does not match its payload");
2303
+ }
2304
+ try {
2305
+ const submittedId = await requireAleoClient(
2306
+ registry,
2307
+ clients,
2308
+ route2.destinationChain.id
2309
+ ).publicClient.request({
2310
+ method: "sendTransaction",
2311
+ params: { transaction: preparedTransaction }
2312
+ });
2313
+ if (submittedId !== transactionId) {
2314
+ throw new BridgeError(`Aleo node returned transaction id ${submittedId}; expected ${transactionId}`);
2315
+ }
2316
+ } catch (error) {
2317
+ if (error instanceof BridgeError) throw error;
2318
+ const classified = classifyBroadcastError(error, transactionId);
2319
+ if (!(classified instanceof DuplicateTransactionError)) throw classified;
2320
+ }
2321
+ const { nextAction: _nextAction2, ...ready } = receipt;
2322
+ const submitted = {
2323
+ ...ready,
2324
+ status: "DESTINATION_CONFIRMING",
2325
+ destinationTxId: transactionId,
2326
+ protocolState: {
2327
+ ...ready.protocolState,
2328
+ preparedDestinationTransaction: void 0
2329
+ }
2330
+ };
2331
+ await params.onCheckpoint?.(createBridgeCheckpoint(plan, submitted));
2332
+ return { kind: "aleo-xreserve", transactionId, receipt: submitted };
2333
+ }
2334
+ const { nextAction: _nextAction, ...deposit } = receipt;
2335
+ const result = await complete(
2336
+ registry,
2337
+ requireAleoClientWithWallet(registry, clients, route2.destinationChain.id, "complete xReserve private mint").walletClient,
2338
+ {
2339
+ plan,
2340
+ deposit: { ...deposit, status: "ATTESTATION_PENDING" },
2341
+ attestation: { status: "complete", payload, messageHash, attestation },
2342
+ privateFee: params.privateFee,
2343
+ onProgress: params.onProgress,
2344
+ onPrepared: params.onCheckpoint ? async (transaction) => params.onCheckpoint?.(createBridgeCheckpoint(plan, {
2345
+ ...receipt,
2346
+ id: transaction.id,
2347
+ protocolState: {
2348
+ ...receipt.protocolState,
2349
+ preparedDestinationTransaction: JSON.stringify(transaction)
2350
+ }
2351
+ })) : void 0,
2352
+ privateMintSecretNonce: params.privateMintSecretNonce,
2353
+ onSubmitted: params.onCheckpoint ? async (submitted) => params.onCheckpoint?.(createBridgeCheckpoint(plan, submitted)) : void 0
2354
+ }
2355
+ );
2356
+ return { kind: "aleo-xreserve", ...result };
2357
+ }
2358
+
2359
+ // src/actions/getStatus.ts
2360
+ import { transactionStatus } from "@provablehq/veil-core";
2361
+ import { isHash as isHash6 } from "viem";
2362
+
2363
+ // src/utils/hyperlaneDelivery.ts
2364
+ import { readContract as readContract2 } from "@provablehq/veil-core";
2365
+ import { decodeFunctionResult as decodeFunctionResult4, encodeFunctionData as encodeFunctionData4, getAddress as getAddress5, hexToBytes as hexToBytes2, isAddress as isAddress4, isHash as isHash4, parseAbi as parseAbi4 } from "viem";
2366
+ var EVM_MAILBOX_ABI = parseAbi4(["function delivered(bytes32 id) view returns (bool)"]);
2367
+ function littleEndianU1282(bytes) {
2368
+ let value = 0n;
2369
+ for (let index = 0; index < bytes.length; index++) {
2370
+ value |= BigInt(bytes[index]) << BigInt(index * 8);
2371
+ }
2372
+ return value;
2373
+ }
2374
+ function aleoDeliveryKey(messageId) {
2375
+ const bytes = hexToBytes2(messageId);
2376
+ const first = littleEndianU1282(bytes.slice(0, 16));
2377
+ const second = littleEndianU1282(bytes.slice(16, 32));
2378
+ return `{ id: [${first}u128, ${second}u128] }`;
2379
+ }
2380
+ async function readHyperlaneDelivery(client, params) {
2381
+ if (!isHash4(params.messageId)) throw new BridgeError("Hyperlane delivery requires a 32-byte message id");
2382
+ if (client.family === "aleo") {
2383
+ if (!params.mailbox.endsWith(".aleo")) throw new BridgeError(`Invalid Aleo Hyperlane mailbox program: ${params.mailbox}`);
2384
+ return await readContract2(client.publicClient, {
2385
+ programId: params.mailbox,
2386
+ mapping: "deliveries",
2387
+ key: aleoDeliveryKey(params.messageId)
2388
+ }) !== null;
2389
+ }
2390
+ if (!isAddress4(params.mailbox)) throw new BridgeError(`Invalid EVM Hyperlane mailbox address: ${params.mailbox}`);
2391
+ const mailbox = getAddress5(params.mailbox);
2392
+ const data = encodeFunctionData4({
2393
+ abi: EVM_MAILBOX_ABI,
2394
+ functionName: "delivered",
2395
+ args: [params.messageId]
2396
+ });
2397
+ const result = await client.publicClient.call({ to: mailbox, data });
2398
+ return decodeFunctionResult4({ abi: EVM_MAILBOX_ABI, functionName: "delivered", data: result });
2399
+ }
2400
+
2401
+ // src/utils/xreserveDelivery.ts
2402
+ import { readContract as readContract3 } from "@provablehq/veil-core";
2403
+ import { isHash as isHash5 } from "viem";
2404
+ async function readXReserveDelivery(client, params) {
2405
+ if (!params.bridgeProgram.endsWith(".aleo")) {
2406
+ throw new BridgeError(`Invalid Aleo xReserve bridge program: ${params.bridgeProgram}`);
2407
+ }
2408
+ if (!isHash5(params.nonce)) throw new BridgeError("xReserve delivery requires a 32-byte deposit nonce");
2409
+ const value = await readContract3(client.publicClient, {
2410
+ programId: params.bridgeProgram,
2411
+ mapping: "nullifier",
2412
+ key: xReserveHexToAleoBytes(params.nonce, 32)
2413
+ });
2414
+ if (value === null) return false;
2415
+ if (typeof value !== "string") throw new BridgeError("Aleo xReserve bridge returned an invalid nullifier value");
2416
+ return value.trim() === "true";
2417
+ }
2418
+
2419
+ // src/actions/getStatus.ts
2420
+ function withoutNextAction(receipt) {
2421
+ const { nextAction: _nextAction, ...rest } = receipt;
2422
+ return rest;
2423
+ }
2424
+ async function getStatus(registry, clients, client, params) {
2425
+ const route2 = resolveTransferRoute(registry, params.plan);
2426
+ const receipt = params.receipt;
2427
+ if (receipt.protocol !== params.plan.protocol || receipt.protocolState.routeId !== params.plan.route.id) {
2428
+ throw new BridgeError("Bridge receipt does not match the prepared route");
2429
+ }
2430
+ if (receipt.status === "COMPLETED" || receipt.status === "FAILED" || receipt.status === "EXPIRED") {
2431
+ return receipt;
2432
+ }
2433
+ if (receipt.status === "SOURCE_APPROVAL_PENDING" && route2.sourceChain.family === "evm") {
2434
+ if (!isHash6(receipt.id)) throw new BridgeError("Bridge receipt is missing its EVM approval transaction id");
2435
+ const evm = requireEvmClient(registry, clients, route2.sourceChain.id);
2436
+ const result = await evm.publicClient.getTransactionReceipt(receipt.id);
2437
+ if (!result) return receipt;
2438
+ if (result.status === "reverted") {
2439
+ return {
2440
+ ...withoutNextAction(receipt),
2441
+ status: "FAILED",
2442
+ protocolState: { ...receipt.protocolState, sourceError: `EVM approval transaction reverted: ${receipt.id}` }
2443
+ };
2444
+ }
2445
+ return { ...receipt, status: "SOURCE_SUBMISSION_PENDING" };
2446
+ }
2447
+ if (receipt.status === "SOURCE_CONFIRMING" && route2.sourceChain.family === "aleo") {
2448
+ const transactionId = receipt.sourceTxId;
2449
+ if (!transactionId) throw new BridgeError("Bridge receipt is missing its Aleo source transaction id");
2450
+ const aleo = requireAleoClient(registry, clients, route2.sourceChain.id);
2451
+ const result = await transactionStatus(aleo.publicClient, { transactionId });
2452
+ if (result.status === "accepted") {
2453
+ return { ...withoutNextAction(receipt), status: "DELIVERY_PENDING" };
2454
+ }
2455
+ if (result.status === "rejected") {
2456
+ return {
2457
+ ...withoutNextAction(receipt),
2458
+ status: "FAILED",
2459
+ protocolState: { ...receipt.protocolState, sourceError: result.error ?? "Aleo transaction was rejected" }
2460
+ };
2461
+ }
2462
+ return receipt;
2463
+ }
2464
+ if (receipt.status === "SOURCE_CONFIRMING" && route2.route.protocol === "hyperlane" && route2.sourceChain.family === "evm") {
2465
+ return getSourceStatus(
2466
+ registry,
2467
+ requireEvmClient(registry, clients, route2.sourceChain.id),
2468
+ params.plan,
2469
+ aleoAddressToBytes32(params.plan.recipient),
2470
+ receipt
2471
+ );
2472
+ }
2473
+ if (receipt.status === "SOURCE_CONFIRMING" && route2.route.protocol === "hyperlane" && route2.sourceChain.family === "solana") {
2474
+ return getSourceStatus2(
2475
+ requireSolanaClient(registry, clients, route2.sourceChain.id),
2476
+ receipt
2477
+ );
2478
+ }
2479
+ if (receipt.status === "DELIVERY_PENDING" && route2.route.protocol === "hyperlane" && receipt.messageId && (route2.destinationChain.family === "aleo" || route2.destinationChain.family === "evm")) {
2480
+ const destinationClient = route2.destinationChain.family === "aleo" ? requireAleoClient(registry, clients, route2.destinationChain.id) : requireEvmClient(registry, clients, route2.destinationChain.id);
2481
+ const mailbox = route2.destinationChain.family === "aleo" ? route2.route.metadata?.aleoMailboxProgram : route2.route.metadata?.mailboxAddress;
2482
+ if (typeof mailbox !== "string" || !mailbox) {
2483
+ throw new BridgeError(`Hyperlane destination mailbox is not configured for ${route2.route.id}`);
2484
+ }
2485
+ const delivered = await readHyperlaneDelivery(destinationClient, { messageId: receipt.messageId, mailbox });
2486
+ if (!delivered) return receipt;
2487
+ return { ...withoutNextAction(receipt), status: "COMPLETED" };
2488
+ }
2489
+ if (receipt.status === "DELIVERY_PENDING" && route2.route.protocol === "hyperlane" && route2.sourceChain.family === "aleo") {
2490
+ const before = receipt.protocolState.destinationBalanceBeforeAtomic;
2491
+ const expected = receipt.protocolState.expectedDestinationIncreaseAtomic;
2492
+ if (typeof before !== "string" || !/^\d+$/.test(before) || typeof expected !== "string" || !/^\d+$/.test(expected)) {
2493
+ return receipt;
2494
+ }
2495
+ const current = await readDestinationBalance(registry, clients, params.plan);
2496
+ if (current === void 0) {
2497
+ throw new BridgeError(`No supported destination balance verifier is configured for ${route2.destinationChain.id}`);
2498
+ }
2499
+ if (current < BigInt(before) + BigInt(expected)) return receipt;
2500
+ return { ...withoutNextAction(receipt), status: "COMPLETED" };
2501
+ }
2502
+ if (route2.route.protocol === "hyperlane") {
2503
+ return receipt;
2504
+ }
2505
+ if (receipt.status === "DELIVERY_PENDING" && route2.route.protocol === "xreserve" && route2.sourceChain.family === "aleo" && route2.destinationChain.family === "evm") {
2506
+ return receipt;
2507
+ }
2508
+ if (route2.route.protocol !== "xreserve" || route2.sourceChain.family !== "evm" || route2.destinationChain.family !== "aleo") {
2509
+ throw new BridgeError("Status refresh is not implemented for this bridge route");
2510
+ }
2511
+ const shouldCheckDelivery = receipt.status === "ATTESTATION_PENDING" || receipt.status === "DELIVERY_PENDING" || receipt.status === "DESTINATION_ACTION_REQUIRED";
2512
+ if (receipt.status === "DELIVERY_PENDING" && !clients[route2.destinationChain.id]) {
2513
+ throw new BridgeError(`An Aleo client is required to verify xReserve delivery on chain "${route2.destinationChain.id}"`);
2514
+ }
2515
+ if (shouldCheckDelivery && clients[route2.destinationChain.id]) {
2516
+ const storedNonce = receipt.protocolState.nonce;
2517
+ const payload = receipt.protocolState.payload;
2518
+ const nonce = typeof storedNonce === "string" ? storedNonce : typeof payload === "string" ? xReserveDepositNonceFromPayload(payload) : void 0;
2519
+ const bridgeProgram = receipt.protocolState.bridgeProgram ?? route2.route.metadata?.bridgeProgram;
2520
+ if (typeof nonce === "string" && typeof bridgeProgram === "string") {
2521
+ const delivered = await readXReserveDelivery(
2522
+ requireAleoClient(registry, clients, route2.destinationChain.id),
2523
+ { bridgeProgram, nonce }
2524
+ );
2525
+ if (delivered) return { ...withoutNextAction(receipt), status: "COMPLETED" };
2526
+ }
2527
+ }
2528
+ if (receipt.status === "SOURCE_CONFIRMING") {
2529
+ return getSourceStatus3(
2530
+ registry,
2531
+ requireEvmClient(registry, clients, route2.sourceChain.id),
2532
+ params.plan,
2533
+ receipt
2534
+ );
2535
+ }
2536
+ if (receipt.status === "ATTESTATION_PENDING") {
2537
+ const messageHash = receipt.protocolState.messageHash;
2538
+ if (typeof messageHash !== "string" || !isHash6(messageHash)) {
2539
+ throw new BridgeError("xReserve receipt is missing its Circle message hash");
2540
+ }
2541
+ const attestation = await getAttestation(registry, client, {
2542
+ routeId: params.plan.route.id,
2543
+ messageHash,
2544
+ signal: params.signal
2545
+ });
2546
+ if (attestation.status === "pending") return receipt;
2547
+ if (params.plan.mintMode !== "private") {
2548
+ return { ...receipt, status: "DELIVERY_PENDING", protocolState: { ...receipt.protocolState, attestation: attestation.attestation } };
2549
+ }
2550
+ return {
2551
+ ...receipt,
2552
+ status: "DESTINATION_ACTION_REQUIRED",
2553
+ nextAction: { kind: "xreserve-private-mint", chainId: route2.destinationChain.id },
2554
+ protocolState: { ...receipt.protocolState, attestation: attestation.attestation }
2555
+ };
2556
+ }
2557
+ if (receipt.status === "DESTINATION_ACTION_REQUIRED") return receipt;
2558
+ if (receipt.status === "DESTINATION_CONFIRMING") {
2559
+ const transactionId = receipt.destinationTxId;
2560
+ if (!transactionId) throw new BridgeError("xReserve receipt is missing its Aleo destination transaction id");
2561
+ const aleo = requireAleoClient(registry, clients, route2.destinationChain.id);
2562
+ const result = await transactionStatus(aleo.publicClient, { transactionId });
2563
+ if (result.status === "accepted") {
2564
+ return { ...withoutNextAction(receipt), status: "COMPLETED" };
2565
+ }
2566
+ if (result.status === "rejected") {
2567
+ return {
2568
+ ...withoutNextAction(receipt),
2569
+ status: "FAILED",
2570
+ protocolState: { ...receipt.protocolState, destinationError: result.error ?? "Aleo transaction was rejected" }
2571
+ };
2572
+ }
2573
+ return receipt;
2574
+ }
2575
+ return receipt;
2576
+ }
2577
+
2578
+ // src/actions/internal/toBridgeProgress.ts
2579
+ function failureMessage(receipt) {
2580
+ const state = receipt.protocolState;
2581
+ const message = state.destinationError ?? state.sourceError;
2582
+ return typeof message === "string" ? message : `Bridge transfer ended in ${receipt.status}`;
2583
+ }
2584
+ function toBridgeProgress(plan, receipt) {
2585
+ if (receipt.status === "SOURCE_SUBMISSION_PENDING") return { next: "resume", plan, receipt };
2586
+ if (receipt.status === "DESTINATION_ACTION_REQUIRED") return { next: "complete", plan, receipt };
2587
+ if (receipt.status === "COMPLETED") return { next: "done", plan, receipt };
2588
+ if (receipt.status === "FAILED" || receipt.status === "EXPIRED") {
2589
+ return { next: "failed", plan, receipt, error: failureMessage(receipt) };
2590
+ }
2591
+ return { next: "wait", plan, receipt };
2592
+ }
2593
+
2594
+ // src/actions/recover.ts
2595
+ async function recover(registry, clients, client, params) {
2596
+ const checkpoint = params.checkpoint;
2597
+ if (checkpoint.version !== 1 || !checkpoint.intent || !checkpoint.route) {
2598
+ throw new BridgeError("Bridge checkpoint format is invalid or unsupported");
2599
+ }
2600
+ const plan = prepare(registry, checkpoint.intent);
2601
+ const route2 = resolveTransferRoute(registry, plan);
2602
+ if (checkpoint.version !== 1 || checkpoint.route.id !== plan.route.id || checkpoint.route.registryVersion !== plan.registryVersion) {
2603
+ throw new BridgeError("Bridge checkpoint does not match the prepared route");
2604
+ }
2605
+ let receipt;
2606
+ if (route2.sourceChain.family === "aleo") {
2607
+ const deliveryVerification = checkpoint.deliveryVerification ? {
2608
+ destinationBalanceBeforeAtomic: checkpoint.deliveryVerification.balanceBeforeAtomic,
2609
+ expectedDestinationIncreaseAtomic: checkpoint.deliveryVerification.expectedIncreaseAtomic
2610
+ } : {};
2611
+ const prepared = checkpoint.source?.preparedTransaction;
2612
+ if (prepared && !checkpoint.source?.transactionId) {
2613
+ if (checkpoint.destination || (checkpoint.source?.approvalTransactionIds?.length ?? 0) > 0) {
2614
+ throw new BridgeError("Bridge checkpoint contains transactions that are invalid for a prepared Aleo source route");
2615
+ }
2616
+ let decoded;
2617
+ try {
2618
+ decoded = JSON.parse(prepared.serializedTransaction);
2619
+ } catch (error) {
2620
+ throw new BridgeError("Bridge checkpoint contains an invalid prepared Aleo transaction", { cause: error });
2621
+ }
2622
+ if (!decoded || typeof decoded !== "object" || decoded.id !== prepared.transactionId) {
2623
+ throw new BridgeError("Bridge checkpoint prepared Aleo transaction id does not match its payload");
2624
+ }
2625
+ return toBridgeProgress(plan, {
2626
+ id: prepared.transactionId,
2627
+ protocol: plan.protocol,
2628
+ status: "SOURCE_SUBMISSION_PENDING",
2629
+ protocolState: {
2630
+ routeId: checkpoint.route.id,
2631
+ preparedTransaction: prepared.serializedTransaction,
2632
+ ...deliveryVerification
2633
+ }
2634
+ });
2635
+ }
2636
+ if (!checkpoint.source?.transactionId) {
2637
+ throw new BridgeError("Bridge checkpoint contains no submitted source transaction");
2638
+ }
2639
+ if (checkpoint.destination || (checkpoint.source.approvalTransactionIds?.length ?? 0) > 0) {
2640
+ throw new BridgeError("Bridge checkpoint contains transactions that are invalid for an Aleo source route");
2641
+ }
2642
+ receipt = await getStatus(registry, clients, client, {
2643
+ plan,
2644
+ receipt: {
2645
+ id: checkpoint.source.transactionId,
2646
+ protocol: plan.protocol,
2647
+ status: "SOURCE_CONFIRMING",
2648
+ sourceTxId: checkpoint.source.transactionId,
2649
+ protocolState: { routeId: checkpoint.route.id, ...deliveryVerification }
2650
+ },
2651
+ signal: params.signal
2652
+ });
2653
+ return toBridgeProgress(plan, receipt);
2654
+ }
2655
+ if (route2.sourceChain.family === "solana") {
2656
+ if (!checkpoint.source?.transactionId) {
2657
+ throw new BridgeError("Bridge checkpoint contains no submitted source transaction");
2658
+ }
2659
+ if (checkpoint.destination || (checkpoint.source.approvalTransactionIds?.length ?? 0) > 0) {
2660
+ throw new BridgeError("Bridge checkpoint contains transactions that are invalid for a Solana source route");
2661
+ }
2662
+ const { blockhash, lastValidBlockHeight } = checkpoint.source;
2663
+ if ((blockhash !== void 0 || lastValidBlockHeight !== void 0) && (typeof blockhash !== "string" || !blockhash || typeof lastValidBlockHeight !== "string" || !/^\d+$/.test(lastValidBlockHeight))) {
2664
+ throw new BridgeError("Bridge checkpoint contains an invalid Solana blockhash lifetime");
2665
+ }
2666
+ receipt = await getStatus(registry, clients, client, {
2667
+ plan,
2668
+ receipt: {
2669
+ id: checkpoint.source.transactionId,
2670
+ protocol: plan.protocol,
2671
+ status: "SOURCE_CONFIRMING",
2672
+ sourceTxId: checkpoint.source.transactionId,
2673
+ protocolState: {
2674
+ routeId: checkpoint.route.id,
2675
+ ...typeof blockhash === "string" && typeof lastValidBlockHeight === "string" ? { blockhash, lastValidBlockHeight } : {}
2676
+ }
2677
+ },
2678
+ signal: params.signal
2679
+ });
2680
+ return toBridgeProgress(plan, receipt);
2681
+ }
2682
+ if (route2.route.protocol === "hyperlane" && route2.sourceChain.family === "evm") {
2683
+ if (checkpoint.destination) {
2684
+ throw new BridgeError("Bridge checkpoint contains a destination transaction that is invalid for this Hyperlane route");
2685
+ }
2686
+ receipt = await recoverSourceCheckpoint(
2687
+ registry,
2688
+ requireEvmClient(registry, clients, route2.sourceChain.id),
2689
+ plan,
2690
+ aleoAddressToBytes32(plan.recipient),
2691
+ checkpoint
2692
+ );
2693
+ return toBridgeProgress(plan, receipt);
2694
+ }
2695
+ if (route2.route.protocol !== "xreserve" || route2.sourceChain.family !== "evm" || route2.destinationChain.family !== "aleo") {
2696
+ throw new BridgeError("Bridge checkpoint recovery is not implemented for this route");
2697
+ }
2698
+ receipt = await recoverSourceCheckpoint2(
2699
+ registry,
2700
+ requireEvmClient(registry, clients, route2.sourceChain.id),
2701
+ plan,
2702
+ checkpoint
2703
+ );
2704
+ const preparedDestination = checkpoint.destination?.preparedTransaction;
2705
+ if (preparedDestination && checkpoint.destination?.transactionId) {
2706
+ throw new BridgeError("Bridge checkpoint cannot contain both prepared and submitted destination transactions");
2707
+ }
2708
+ if (preparedDestination) {
2709
+ let decoded;
2710
+ try {
2711
+ decoded = JSON.parse(preparedDestination.serializedTransaction);
2712
+ } catch (error) {
2713
+ throw new BridgeError("Bridge checkpoint contains an invalid prepared Aleo destination transaction", { cause: error });
2714
+ }
2715
+ if (!decoded || typeof decoded !== "object" || decoded.id !== preparedDestination.transactionId) {
2716
+ throw new BridgeError("Bridge checkpoint prepared Aleo destination transaction id does not match its payload");
2717
+ }
2718
+ }
2719
+ if (checkpoint.destination?.transactionId) {
2720
+ receipt = {
2721
+ ...receipt,
2722
+ status: "DESTINATION_CONFIRMING",
2723
+ destinationTxId: checkpoint.destination.transactionId
2724
+ };
2725
+ }
2726
+ if (receipt.status === "ATTESTATION_PENDING" || receipt.status === "DESTINATION_CONFIRMING") {
2727
+ receipt = await getStatus(registry, clients, client, {
2728
+ plan,
2729
+ receipt,
2730
+ signal: params.signal
2731
+ });
2732
+ }
2733
+ if (preparedDestination) {
2734
+ if (receipt.status !== "DESTINATION_ACTION_REQUIRED") {
2735
+ throw new BridgeError("Prepared destination transaction is no longer valid for the recovered bridge state");
2736
+ }
2737
+ receipt = {
2738
+ ...receipt,
2739
+ id: preparedDestination.transactionId,
2740
+ protocolState: {
2741
+ ...receipt.protocolState,
2742
+ preparedDestinationTransaction: preparedDestination.serializedTransaction
2743
+ }
2744
+ };
2745
+ }
2746
+ return toBridgeProgress(plan, receipt);
2747
+ }
2748
+
2749
+ // src/actions/resume.ts
2750
+ import { classifyBroadcastError as classifyBroadcastError2, DuplicateTransactionError as DuplicateTransactionError2 } from "@provablehq/veil-core";
2751
+ async function resume(registry, clients, params) {
2752
+ const { plan, receipt } = params.progress;
2753
+ if (params.progress.next !== "resume" || receipt.status !== "SOURCE_SUBMISSION_PENDING") {
2754
+ throw new BridgeError("Bridge progress has no source submission to resume");
2755
+ }
2756
+ const route2 = resolveTransferRoute(registry, plan);
2757
+ const onSubmitted = params.onCheckpoint ? async (value) => params.onCheckpoint?.(createBridgeCheckpoint(plan, value)) : void 0;
2758
+ if (route2.sourceChain.family === "aleo") {
2759
+ const serializedTransaction = receipt.protocolState.preparedTransaction;
2760
+ if (typeof serializedTransaction !== "string" || !serializedTransaction) {
2761
+ throw new BridgeError("Prepared Aleo recovery is missing its serialized transaction");
2762
+ }
2763
+ let decoded;
2764
+ try {
2765
+ decoded = JSON.parse(serializedTransaction);
2766
+ } catch (error) {
2767
+ throw new BridgeError("Prepared Aleo recovery contains an invalid serialized transaction", { cause: error });
2768
+ }
2769
+ const transactionId = decoded && typeof decoded === "object" ? decoded.id : void 0;
2770
+ if (typeof transactionId !== "string" || transactionId !== receipt.id) {
2771
+ throw new BridgeError("Prepared Aleo recovery transaction id does not match its payload");
2772
+ }
2773
+ try {
2774
+ const submittedId = await requireAleoClient(
2775
+ registry,
2776
+ clients,
2777
+ route2.sourceChain.id
2778
+ ).publicClient.request({
2779
+ method: "sendTransaction",
2780
+ params: { transaction: serializedTransaction }
2781
+ });
2782
+ if (submittedId !== transactionId) {
2783
+ throw new BridgeError(`Aleo node returned transaction id ${submittedId}; expected ${transactionId}`);
2784
+ }
2785
+ } catch (error) {
2786
+ if (error instanceof BridgeError) throw error;
2787
+ const classified = classifyBroadcastError2(error, transactionId);
2788
+ if (!(classified instanceof DuplicateTransactionError2)) throw classified;
2789
+ }
2790
+ const submitted = {
2791
+ id: transactionId,
2792
+ protocol: plan.protocol,
2793
+ status: "SOURCE_CONFIRMING",
2794
+ sourceTxId: transactionId,
2795
+ protocolState: {
2796
+ routeId: plan.route.id,
2797
+ ...typeof receipt.protocolState.destinationBalanceBeforeAtomic === "string" ? { destinationBalanceBeforeAtomic: receipt.protocolState.destinationBalanceBeforeAtomic } : {},
2798
+ ...typeof receipt.protocolState.expectedDestinationIncreaseAtomic === "string" ? { expectedDestinationIncreaseAtomic: receipt.protocolState.expectedDestinationIncreaseAtomic } : {}
2799
+ }
2800
+ };
2801
+ await onSubmitted?.(submitted);
2802
+ return plan.protocol === "hyperlane" ? { kind: "aleo-hyperlane", transactionId, receipt: submitted } : { kind: "aleo-xreserve", transactionId, receipt: submitted };
2803
+ }
2804
+ if (route2.route.protocol === "xreserve" && route2.sourceChain.family === "evm") {
2805
+ const execution = await execute5(
2806
+ registry,
2807
+ requireEvmClientWithWallet(registry, clients, route2.sourceChain.id, "resume xReserve transfer"),
2808
+ {
2809
+ plan,
2810
+ resume: receipt,
2811
+ pollingIntervalMs: params.pollingIntervalMs,
2812
+ confirmationTimeoutMs: params.confirmationTimeoutMs,
2813
+ onSubmitted,
2814
+ privateMintSecretNonce: params.privateMintSecretNonce
2815
+ }
2816
+ );
2817
+ return { kind: "evm-xreserve", ...execution };
2818
+ }
2819
+ if (route2.route.protocol === "hyperlane" && route2.sourceChain.family === "evm") {
2820
+ const execution = await execute2(
2821
+ registry,
2822
+ requireEvmClientWithWallet(registry, clients, route2.sourceChain.id, "resume Hyperlane transfer"),
2823
+ {
2824
+ plan,
2825
+ recipientBytes32: aleoAddressToBytes32(plan.recipient),
2826
+ resume: receipt,
2827
+ pollingIntervalMs: params.pollingIntervalMs,
2828
+ confirmationTimeoutMs: params.confirmationTimeoutMs,
2829
+ onSubmitted
2830
+ }
2831
+ );
2832
+ return { kind: "evm-hyperlane", ...execution };
2833
+ }
2834
+ throw new BridgeError("Source resumption is not implemented for this bridge route");
2835
+ }
2836
+
2837
+ // src/actions/wait.ts
2838
+ var CALLER_BOUNDARIES = [
2839
+ "SOURCE_SUBMISSION_PENDING",
2840
+ "DESTINATION_ACTION_REQUIRED",
2841
+ "COMPLETED",
2842
+ "FAILED",
2843
+ "EXPIRED"
2844
+ ];
2845
+ async function wait2(registry, clients, client, params) {
2846
+ const { plan, receipt } = params.progress;
2847
+ if (params.until?.length === 0) throw new BridgeError("wait requires at least one target status when until is provided");
2848
+ resolveTransferRoute(registry, plan);
2849
+ if (receipt.protocol !== plan.protocol || receipt.protocolState.routeId !== plan.route.id) {
2850
+ throw new BridgeError("Bridge progress does not match its reconstructed plan");
2851
+ }
2852
+ const current = toBridgeProgress(plan, receipt);
2853
+ const until = [.../* @__PURE__ */ new Set([...CALLER_BOUNDARIES, ...params.until ?? []])];
2854
+ if (current.next !== "wait" || until.includes(receipt.status)) return current;
2855
+ const requestedInterval = params.pollingIntervalMs ?? 15e3;
2856
+ const timeoutMs = params.timeoutMs ?? 20 * 6e4;
2857
+ if (!Number.isFinite(requestedInterval) || requestedInterval < 0 || !Number.isFinite(timeoutMs) || timeoutMs < 0) {
2858
+ throw new BridgeError("Status polling controls must be non-negative finite numbers");
2859
+ }
2860
+ const interval = requestedInterval === 0 ? 0 : Math.max(100, requestedInterval);
2861
+ const deadline = Date.now() + timeoutMs;
2862
+ let updated = receipt;
2863
+ while (true) {
2864
+ if (params.signal?.aborted) throw new BridgeError("Bridge status polling was cancelled");
2865
+ const next = await getStatus(registry, clients, client, { plan, receipt: updated, signal: params.signal });
2866
+ if (next !== updated) await params.onUpdate?.(toBridgeProgress(plan, next));
2867
+ updated = next;
2868
+ if (until.includes(updated.status)) return toBridgeProgress(plan, updated);
2869
+ if (Date.now() >= deadline) throw new BridgeError(`Bridge status polling timed out in state ${updated.status}`);
2870
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
2871
+ }
2872
+ }
2873
+
2874
+ // src/actions/internal/aleoPrivacy.ts
2875
+ var EMPTY_PROOF = `{ siblings: [${Array(16).fill("0field").join(", ")}], leaf_index: 1u32 }`;
2876
+ var EMPTY_MERKLE_PROOF_PAIR = `[${EMPTY_PROOF}, ${EMPTY_PROOF}]`;
2877
+ function resolvePrivacyAsset(registry, endpoint, operation) {
2878
+ const asset = registry.assets.find((entry) => entry.chainId === endpoint.chain && entry.key === endpoint.asset);
2879
+ if (!asset) throw new BridgeError(`Unknown bridge asset: "${endpoint.chain}/${endpoint.asset}"`);
2880
+ const chain = registry.chains.find((entry) => entry.id === asset.chainId);
2881
+ if (chain?.family !== "aleo" || !asset.privacy) {
2882
+ throw new BridgeError(`Bridge asset "${asset.id}" does not support ${operation}`);
2883
+ }
2884
+ return asset;
2885
+ }
2886
+ function privacyAmount(asset, amount, operation) {
2887
+ const amountAtomic = parseDecimalAmount(amount, asset.decimals);
2888
+ if (amountAtomic <= 0n) throw new BridgeError(`${operation} amount must be greater than zero`);
2889
+ return { amountAtomic, literal: `${amountAtomic}u128` };
2890
+ }
2891
+ function privacyRecord(program, amount) {
2892
+ return {
2893
+ type: "record",
2894
+ program,
2895
+ recordname: "Token",
2896
+ filters: { amount: { gte: amount } }
2897
+ };
2898
+ }
2899
+
2900
+ // src/actions/shield.ts
2901
+ async function shield(registry, clients, params) {
2902
+ const asset = resolvePrivacyAsset(registry, params.asset, "shielding");
2903
+ const { amountAtomic, literal } = privacyAmount(asset, params.amount, "Shielding");
2904
+ const { walletClient } = requireAleoClientWithWallet(registry, clients, asset.chainId, `shield ${asset.symbol}`);
2905
+ const inputs = asset.privacy.kind === "arc22" ? [params.recipient ?? { type: "address", label: `${asset.symbol} private recipient` }, literal] : [literal];
2906
+ const transactionId = await walletClient.executeTransaction({
2907
+ program: asset.privacy.program,
2908
+ function: asset.privacy.kind === "arc22" ? "transfer_public_to_private" : "shield",
2909
+ inputs,
2910
+ privateFee: params.privateFee ?? false,
2911
+ onProgress: async (event) => {
2912
+ await params.onProgress?.(event);
2913
+ if (event.type === "transaction-prepared") await params.onPrepared?.(event.transaction);
2914
+ }
2915
+ });
2916
+ if (!transactionId) throw new BridgeError(`Aleo wallet returned an empty ${asset.symbol} shield transaction id`);
2917
+ return { transactionId, assetId: asset.id, amount: params.amount, amountAtomic };
2918
+ }
2919
+
2920
+ // src/actions/unshield.ts
2921
+ async function unshield(registry, clients, params) {
2922
+ const asset = resolvePrivacyAsset(registry, params.asset, "unshielding");
2923
+ const { amountAtomic, literal } = privacyAmount(asset, params.amount, "Unshielding");
2924
+ const { walletClient } = requireAleoClientWithWallet(registry, clients, asset.chainId, `unshield ${asset.symbol}`);
2925
+ const record = params.record ?? privacyRecord(asset.privacy.program, literal);
2926
+ const inputs = asset.privacy.kind === "arc22" ? [
2927
+ params.recipient ?? { type: "address", label: `${asset.symbol} public recipient` },
2928
+ literal,
2929
+ record,
2930
+ params.merkleProof ?? EMPTY_MERKLE_PROOF_PAIR
2931
+ ] : [record, literal];
2932
+ const transactionId = await walletClient.executeTransaction({
2933
+ program: asset.privacy.program,
2934
+ function: asset.privacy.kind === "arc22" ? "transfer_private_to_public" : "unshield",
2935
+ inputs,
2936
+ privateFee: params.privateFee ?? false,
2937
+ onProgress: async (event) => {
2938
+ await params.onProgress?.(event);
2939
+ if (event.type === "transaction-prepared") await params.onPrepared?.(event.transaction);
2940
+ }
2941
+ });
2942
+ if (!transactionId) throw new BridgeError(`Aleo wallet returned an empty ${asset.symbol} unshield transaction id`);
2943
+ return { transactionId, assetId: asset.id, amount: params.amount, amountAtomic };
2944
+ }
2945
+
2946
+ // src/clients/decorators/bridge.ts
2947
+ function bridgeActions(config) {
2948
+ return {
2949
+ // Every closure injects the same validated route catalog and
2950
+ // registry-keyed clients, preventing per-action configuration drift.
2951
+ quote: async (params) => quote5(config.registry, config.clients, params),
2952
+ execute: async (params) => execute6(config.registry, config.clients, params),
2953
+ getStatus: async (params) => getStatus(config.registry, config.clients, config.fetch, params),
2954
+ complete: async (params) => complete2(config.registry, config.clients, params),
2955
+ recover: async (params) => recover(config.registry, config.clients, config.fetch, params),
2956
+ resume: async (params) => resume(config.registry, config.clients, params),
2957
+ wait: async (params) => wait2(config.registry, config.clients, config.fetch, params),
2958
+ shield: async (params) => shield(config.registry, config.clients, params),
2959
+ unshield: async (params) => unshield(config.registry, config.clients, params)
2960
+ };
2961
+ }
2962
+
2963
+ // src/registry/default.ts
2964
+ var EVM_ADDRESS = "^0x[0-9a-fA-F]{40}$";
2965
+ var SOLANA_ADDRESS = "^[1-9A-HJ-NP-Za-km-z]{32,44}$";
2966
+ var ALEO_ADDRESS = "^aleo1[0-9a-z]{58}$";
2967
+ var chains = [
2968
+ { id: "aleo", displayName: "Aleo", family: "aleo", environment: "mainnet", nativeCurrencySymbol: "ALEO", protocolDomains: { xreserve: 10002, hyperlane: 1634493807 } },
2969
+ { id: "ethereum", displayName: "Ethereum", family: "evm", environment: "mainnet", nativeCurrencySymbol: "ETH", protocolDomains: { xreserve: 0, hyperlane: 1 } },
2970
+ { id: "solana", displayName: "Solana", family: "solana", environment: "mainnet", nativeCurrencySymbol: "SOL", protocolDomains: { hyperlane: 1399811149 } },
2971
+ { id: "base", displayName: "Base", family: "evm", environment: "mainnet", nativeCurrencySymbol: "ETH" },
2972
+ { id: "hyperevm", displayName: "HyperEVM", family: "evm", environment: "mainnet", nativeCurrencySymbol: "HYPE" },
2973
+ { id: "aleo-testnet", displayName: "Aleo Testnet", family: "aleo", environment: "testnet", nativeCurrencySymbol: "ALEO", protocolDomains: { xreserve: 10002, hyperlane: 1617853565 } },
2974
+ { id: "sepolia", displayName: "Ethereum Sepolia", family: "evm", environment: "testnet", nativeCurrencySymbol: "ETH", protocolDomains: { hyperlane: 11155111 } }
2975
+ ];
2976
+ var assets = [
2977
+ { id: "aleo/aleo", key: "aleo", chainId: "aleo", symbol: "ALEO", name: "Aleo", decimals: 6, kind: "native", locator: { kind: "aleo-program", value: "credits.aleo" }, addressValidationRegex: ALEO_ADDRESS },
2978
+ { id: "aleo/usdcx", key: "usdcx", chainId: "aleo", symbol: "USDCx", name: "USDCx", decimals: 6, kind: "token", locator: { kind: "aleo-program", value: "usdcx_stablecoin.aleo" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc22", program: "usdcx_stablecoin.aleo" } },
2979
+ { id: "aleo/eth", key: "eth", chainId: "aleo", symbol: "ETH", name: "Hyperlane ETH", decimals: 18, kind: "token", locator: { kind: "aleo-program", value: "hyp_warp_token_eth_v2.aleo", tokenId: "aleo1t7f29tq9qng2lfvrkpcuvu59jn24hrmzqdyqfn6p0u5p80npfvqqecmkj8" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc20", program: "arc20_eth.aleo" } },
2980
+ { id: "aleo/wbtc", key: "wbtc", chainId: "aleo", symbol: "WBTC", name: "Hyperlane WBTC", decimals: 8, kind: "token", locator: { kind: "aleo-program", value: "hyp_warp_token_wbtc_v2.aleo", tokenId: "aleo1240fsvz2dhmj0cdtt8mc0yc8um9fmu236rqcl2qnlj9703hd2vpsdwyrtf" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc20", program: "arc20_wbtc.aleo" } },
2981
+ { id: "aleo/usdt", key: "usdt", chainId: "aleo", symbol: "USDT", name: "Hyperlane USDT", decimals: 6, kind: "token", locator: { kind: "aleo-program", value: "hyp_warp_token_usdt_v2.aleo", tokenId: "aleo18yynfz0lrfx0tund540vy2z7gju7ekgqsueg5jgu28mpm2z42ufq7qua8y" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc20", program: "arc20_usdt.aleo" } },
2982
+ { id: "aleo/sol", key: "sol", chainId: "aleo", symbol: "SOL", name: "Hyperlane SOL", decimals: 9, kind: "token", locator: { kind: "aleo-program", value: "hyp_warp_token_sol_v2.aleo", tokenId: "aleo1aa0zt0vg9uwknekpqeefkvad55swp7833wc5crp2prv0lm4djuxs5r7k6v" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc20", program: "arc20_sol.aleo" } },
2983
+ { id: "aleo/usad", key: "usad", chainId: "aleo", symbol: "USAD", name: "USAD", decimals: 6, kind: "token", locator: { kind: "aleo-program", value: "usad_stablecoin.aleo" }, addressValidationRegex: ALEO_ADDRESS },
2984
+ { id: "ethereum/usdc", key: "usdc", chainId: "ethereum", symbol: "USDC", name: "USD Coin", decimals: 6, kind: "token", locator: { kind: "evm-contract", value: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" }, addressValidationRegex: EVM_ADDRESS },
2985
+ { id: "ethereum/eth", key: "eth", chainId: "ethereum", symbol: "ETH", name: "Ether", decimals: 18, kind: "native", locator: { kind: "native", value: "ETH" }, addressValidationRegex: EVM_ADDRESS },
2986
+ { id: "ethereum/wbtc", key: "wbtc", chainId: "ethereum", symbol: "WBTC", name: "Wrapped Bitcoin", decimals: 8, kind: "token", locator: { kind: "evm-contract", value: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" }, addressValidationRegex: EVM_ADDRESS },
2987
+ { id: "ethereum/usdt", key: "usdt", chainId: "ethereum", symbol: "USDT", name: "Tether USD", decimals: 6, kind: "token", locator: { kind: "evm-contract", value: "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, addressValidationRegex: EVM_ADDRESS },
2988
+ { id: "ethereum/aleo", key: "aleo", chainId: "ethereum", symbol: "ALEO", name: "Hyperlane ALEO", decimals: 6, kind: "token", addressValidationRegex: EVM_ADDRESS },
2989
+ { id: "ethereum/usad", key: "usad", chainId: "ethereum", symbol: "USAD", name: "USAD route collateral", decimals: 6, kind: "token", addressValidationRegex: EVM_ADDRESS },
2990
+ { id: "solana/sol", key: "sol", chainId: "solana", symbol: "SOL", name: "Solana", decimals: 9, kind: "native", locator: { kind: "native", value: "SOL" }, addressValidationRegex: SOLANA_ADDRESS },
2991
+ { id: "solana/aleo", key: "aleo", chainId: "solana", symbol: "ALEO", name: "Hyperlane ALEO", decimals: 6, kind: "token", addressValidationRegex: SOLANA_ADDRESS },
2992
+ { id: "base/aleo", key: "aleo", chainId: "base", symbol: "ALEO", name: "Hyperlane ALEO", decimals: 6, kind: "token", addressValidationRegex: EVM_ADDRESS },
2993
+ { id: "hyperevm/aleo", key: "aleo", chainId: "hyperevm", symbol: "ALEO", name: "Hyperlane ALEO", decimals: 6, kind: "token", addressValidationRegex: EVM_ADDRESS },
2994
+ { id: "aleo-testnet/usdcx", key: "usdcx", chainId: "aleo-testnet", symbol: "USDCx", name: "Testnet USDCx", decimals: 6, kind: "token", locator: { kind: "aleo-program", value: "test_usdcx_stablecoin.aleo" }, addressValidationRegex: ALEO_ADDRESS, privacy: { kind: "arc22", program: "test_usdcx_stablecoin.aleo" } },
2995
+ { id: "sepolia/usdc", key: "usdc", chainId: "sepolia", symbol: "USDC", name: "Testnet USD Coin", decimals: 6, kind: "token", locator: { kind: "evm-contract", value: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" }, addressValidationRegex: EVM_ADDRESS }
2996
+ ];
2997
+ var XRESERVE_SOURCE = "https://developers.circle.com/xreserve/references/supported-blockchains-and-domains";
2998
+ var HYPERLANE_REGISTRY_COMMIT = "2621c16f2db1ccb46643265c110dac5ca2c7c51a";
2999
+ var HYPERLANE_SOURCE = `https://github.com/hyperlane-xyz/hyperlane-registry/tree/${HYPERLANE_REGISTRY_COMMIT}/deployments/warp_routes`;
3000
+ var ALEO_ETH_PROGRAM_SOURCE = "https://explorer.provable.com/program/hyp_warp_token_eth_v2.aleo";
3001
+ var ALEO_ETH_APP_METADATA_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_eth_v2.aleo/mapping/app_metadata/true";
3002
+ var ALEO_ETH_REMOTE_ROUTER_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_eth_v2.aleo/mapping/remote_routers/1u32";
3003
+ var ALEO_ETH_SAMPLE_TRANSFER_SOURCE = "https://explorer.provable.com/transaction/at1vu0yckkms887zkl3qz7plnncd56jtf5zeal4uj2808upsjkusy8q7yp9v8";
3004
+ var ALEO_WBTC_PROGRAM_SOURCE = "https://explorer.provable.com/program/hyp_warp_token_wbtc_v2.aleo";
3005
+ var ALEO_WBTC_APP_METADATA_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_wbtc_v2.aleo/mapping/app_metadata/true";
3006
+ var ALEO_WBTC_REMOTE_ROUTER_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_wbtc_v2.aleo/mapping/remote_routers/1u32";
3007
+ var ALEO_USDT_PROGRAM_SOURCE = "https://explorer.provable.com/program/hyp_warp_token_usdt_v2.aleo";
3008
+ var ALEO_USDT_APP_METADATA_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_usdt_v2.aleo/mapping/app_metadata/true";
3009
+ var ALEO_USDT_ETHEREUM_REMOTE_ROUTER_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_usdt_v2.aleo/mapping/remote_routers/1u32";
3010
+ var ALEO_USDT_SAMPLE_TRANSFER_SOURCE = "https://explorer.provable.com/transaction/at19caeeee8v3xc4kfwen4tx89f0tnggrpjp0anrhq2ca3y82xr9q8qyz8a9r";
3011
+ var ALEO_USDT_HYPERLANE_CONFIG_SOURCE = "https://github.com/hyperlane-xyz/hyperlane-registry/blob/418056e21734d26a7d14692e0ec5e902cc9e86bf/deployments/warp_routes/USDT/aleo-config.yaml";
3012
+ var ALEO_SOL_PROGRAM_SOURCE = "https://explorer.provable.com/program/hyp_warp_token_sol_v2.aleo";
3013
+ var ALEO_SOL_APP_METADATA_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_sol_v2.aleo/mapping/app_metadata/true";
3014
+ var ALEO_SOL_REMOTE_ROUTER_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_warp_token_sol_v2.aleo/mapping/remote_routers/1399811149u32";
3015
+ var ALEO_SOL_HYPERLANE_CONFIG_SOURCE = "https://github.com/hyperlane-xyz/hyperlane-registry/blob/418056e21734d26a7d14692e0ec5e902cc9e86bf/deployments/warp_routes/SOL/aleo-config.yaml";
3016
+ var ALEO_MAILBOX_PROGRAM_SOURCE = "https://explorer.provable.com/program/hyp_mailbox.aleo";
3017
+ var ALEO_MAILBOX_METADATA_SOURCE = "https://api.explorer.provable.com/v2/mainnet/program/hyp_mailbox.aleo/mapping/mailbox/true";
3018
+ var ETHEREUM_HYPERLANE_COMMON = {
3019
+ sourceChainId: 1,
3020
+ destinationDomain: 1634493807,
3021
+ mailboxAddress: "0xc005dc82818d67AF737725bD4bf75435d065D239",
3022
+ interchainGasPaymaster: "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611",
3023
+ interchainSecurityModule: "0x0000000000000000000000000000000000000000",
3024
+ registryCommit: HYPERLANE_REGISTRY_COMMIT
3025
+ };
3026
+ var ETH_HYPERLANE_METADATA = {
3027
+ ...ETHEREUM_HYPERLANE_COMMON,
3028
+ routerAddress: "0x38D447694f5c1f773ae3132cf93bF30B7Ec1Fa5A",
3029
+ routerType: "native",
3030
+ destinationRouter: "hyp_warp_token_eth_v2.aleo/aleo1t7f29tq9qng2lfvrkpcuvu59jn24hrmzqdyqfn6p0u5p80npfvqqecmkj8"
3031
+ };
3032
+ var WBTC_HYPERLANE_METADATA = {
3033
+ ...ETHEREUM_HYPERLANE_COMMON,
3034
+ routerAddress: "0x20CDC85778b732073F7EecEF3DF25c0d310f8772",
3035
+ routerType: "collateral",
3036
+ tokenAddress: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
3037
+ destinationRouter: "hyp_warp_token_wbtc_v2.aleo/aleo1240fsvz2dhmj0cdtt8mc0yc8um9fmu236rqcl2qnlj9703hd2vpsdwyrtf"
3038
+ };
3039
+ var USDT_HYPERLANE_METADATA = {
3040
+ ...ETHEREUM_HYPERLANE_COMMON,
3041
+ routerAddress: "0x3C2064D78e4578E8F936E3db42aEF044E33FBF31",
3042
+ routerType: "collateral",
3043
+ tokenAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
3044
+ destinationRouter: "hyp_warp_token_usdt_v2.aleo/aleo18yynfz0lrfx0tund540vy2z7gju7ekgqsueg5jgu28mpm2z42ufq7qua8y",
3045
+ requiresApprovalReset: true
3046
+ };
3047
+ var ALEO_PLACEHOLDER_ADDRESS = "aleo1kypwp5m7qtk9mwazgcpg0tq8aal23mnrvwfvug65qgcg9xvsrqgspyjm6n";
3048
+ var ALEO_PLACEHOLDER_BYTES32 = `[${Array.from({ length: 32 }, () => "0u8").join(", ")}]`;
3049
+ var ALEO_MAILBOX_METADATA = {
3050
+ aleoMailboxStateVerified: true,
3051
+ aleoHookManagerProgram: "hyp_hook_manager.aleo",
3052
+ aleoHookManagerProgramSource: "https://explorer.provable.com/program/hyp_hook_manager.aleo",
3053
+ aleoMailboxProgram: "hyp_mailbox.aleo",
3054
+ aleoMailboxProgramEdition: 0,
3055
+ aleoMailboxProgramSource: ALEO_MAILBOX_PROGRAM_SOURCE,
3056
+ aleoMailboxMetadataSource: ALEO_MAILBOX_METADATA_SOURCE,
3057
+ aleoMailboxMetadataReviewedAt: "2026-08-17",
3058
+ aleoMailboxLocalDomain: 1634493807,
3059
+ aleoMailboxObservedNonce: 170,
3060
+ aleoMailboxObservedProcessCount: 291,
3061
+ aleoMailboxDefaultIsm: "aleo1yvf5kcsdgnescqq2lar83mms79yh3ugvc3y0mdnlgvx4lyh5zugqr9hptk",
3062
+ aleoMailboxDefaultHook: "aleo194tz0jmyq8rd9htvnqppqw4jqerk2p2zd8plzn3sxl06wcgsm5pq9fka74",
3063
+ aleoMailboxRequiredHook: "aleo1yxevh9qgxehej46j7vueplwjcpfdfml2dje3ey4ukzknx7wzasgqnxgq82",
3064
+ aleoMailboxDispatchProxy: "aleo1sge9kmjzs3d8fqrscy4hwn7vf9vw4jcxe877lv0m2w8hay78lsxsqg975s",
3065
+ aleoMailboxOwner: "aleo1ypf8xgvz560ukw25hufj3d77gx69pdcy70nssdfdxd97j80d7cqs98d7x8"
3066
+ };
3067
+ function aleoHyperlanePlaceholders(program, destinationDomain) {
3068
+ return {
3069
+ aleoRouterProgram: program,
3070
+ aleoDestinationDomain: destinationDomain,
3071
+ aleoPlaceholderConfiguration: true,
3072
+ aleoTokenType: "0",
3073
+ aleoTokenOwner: ALEO_PLACEHOLDER_ADDRESS,
3074
+ aleoIsm: ALEO_PLACEHOLDER_ADDRESS,
3075
+ aleoHook: ALEO_PLACEHOLDER_ADDRESS,
3076
+ aleoTokenId: "0field",
3077
+ aleoRemoteRouterRecipient: ALEO_PLACEHOLDER_BYTES32,
3078
+ aleoRemoteRouterGas: "0",
3079
+ aleoRecipient: "[0u128, 0u128]",
3080
+ aleoAllowanceSpender0: ALEO_PLACEHOLDER_ADDRESS,
3081
+ aleoAllowanceAmount0: "0",
3082
+ aleoAllowanceSpender1: ALEO_PLACEHOLDER_ADDRESS,
3083
+ aleoAllowanceAmount1: "0",
3084
+ aleoAllowanceSpender2: ALEO_PLACEHOLDER_ADDRESS,
3085
+ aleoAllowanceAmount2: "0",
3086
+ aleoAllowanceSpender3: ALEO_PLACEHOLDER_ADDRESS,
3087
+ aleoAllowanceAmount3: "0",
3088
+ ...ALEO_MAILBOX_METADATA
3089
+ };
3090
+ }
3091
+ var ALEO_WBTC_APP_METADATA = {
3092
+ aleoAppMetadataVerified: true,
3093
+ aleoProgramSource: ALEO_WBTC_PROGRAM_SOURCE,
3094
+ aleoAppMetadataSource: ALEO_WBTC_APP_METADATA_SOURCE,
3095
+ aleoAppMetadataReviewedAt: "2026-08-17",
3096
+ aleoProgramEdition: 0,
3097
+ aleoTokenType: "1",
3098
+ aleoTokenOwner: "aleo14jauje2a5sncm9u5t3mt6qqv3eq2hatkddskccs0dvsy35a0x58q0d6f95",
3099
+ aleoIsm: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3100
+ aleoHook: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3101
+ aleoTokenId: "1505227928464760254508513036497943623956572091841806589002910775534260084309field",
3102
+ aleoLocalDecimals: 8,
3103
+ aleoRemoteDecimals: 8
3104
+ };
3105
+ var ALEO_ETH_APP_METADATA = {
3106
+ aleoAppMetadataVerified: true,
3107
+ aleoProgramSource: ALEO_ETH_PROGRAM_SOURCE,
3108
+ aleoAppMetadataSource: ALEO_ETH_APP_METADATA_SOURCE,
3109
+ aleoAppMetadataReviewedAt: "2026-08-17",
3110
+ aleoProgramEdition: 0,
3111
+ aleoTokenType: "1",
3112
+ aleoTokenOwner: "aleo1wq6f6qdqya44avznygz5hae40u3mjg64w0r93a4qfu4utpf8cg9q566f4r",
3113
+ aleoIsm: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3114
+ aleoHook: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3115
+ aleoTokenId: "133188123661477349522757068766864658505569365361420630212878794317749195359field",
3116
+ aleoLocalDecimals: 18,
3117
+ aleoRemoteDecimals: 18
3118
+ };
3119
+ var ALEO_USDT_APP_METADATA = {
3120
+ aleoAppMetadataVerified: true,
3121
+ aleoProgramSource: ALEO_USDT_PROGRAM_SOURCE,
3122
+ aleoAppMetadataSource: ALEO_USDT_APP_METADATA_SOURCE,
3123
+ aleoAppMetadataReviewedAt: "2026-08-17",
3124
+ aleoProgramEdition: 1,
3125
+ aleoTokenType: "1",
3126
+ aleoTokenOwner: "aleo1l3gwacmjruxryy9c7c4fn0acyzprf29hucrvthw7f63lpyhd5y9srydq8z",
3127
+ aleoIsm: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3128
+ aleoHook: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3129
+ aleoTokenId: "8295938150000417034830036849466229528602563851235385582732969109393809606969field",
3130
+ aleoLocalDecimals: 6,
3131
+ aleoRemoteDecimals: 18,
3132
+ aleoScale: "1000000000000",
3133
+ aleoHyperlaneConfigSource: ALEO_USDT_HYPERLANE_CONFIG_SOURCE
3134
+ };
3135
+ var ALEO_SOL_APP_METADATA = {
3136
+ aleoAppMetadataVerified: true,
3137
+ aleoProgramSource: ALEO_SOL_PROGRAM_SOURCE,
3138
+ aleoAppMetadataSource: ALEO_SOL_APP_METADATA_SOURCE,
3139
+ aleoAppMetadataReviewedAt: "2026-08-17",
3140
+ aleoProgramEdition: 0,
3141
+ aleoTokenType: "1",
3142
+ aleoTokenOwner: "aleo1wr8rfr4ggedjxtg5e23s38zqkgy2j05uc9l8t4akjp5zcw3levpswkwk45",
3143
+ aleoIsm: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3144
+ aleoHook: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3145
+ aleoTokenId: "6148061383892805373029428966764338809222769879628268522058032128225601478383field",
3146
+ aleoLocalDecimals: 9,
3147
+ aleoRemoteDecimals: 9,
3148
+ aleoHyperlaneConfigSource: ALEO_SOL_HYPERLANE_CONFIG_SOURCE
3149
+ };
3150
+ var ALEO_ETH_REMOTE_ROUTER = {
3151
+ aleoRemoteRouterVerified: true,
3152
+ aleoRemoteRouterSource: ALEO_ETH_REMOTE_ROUTER_SOURCE,
3153
+ aleoRemoteRouterReviewedAt: "2026-08-17",
3154
+ aleoSampleTransferSource: ALEO_ETH_SAMPLE_TRANSFER_SOURCE,
3155
+ aleoDestinationDomain: 1,
3156
+ aleoRemoteRouterEvmAddress: "0x38D447694f5c1f773ae3132cf93bF30B7Ec1Fa5A",
3157
+ aleoRemoteRouterRecipient: "[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 56u8, 212u8, 71u8, 105u8, 79u8, 92u8, 31u8, 119u8, 58u8, 227u8, 19u8, 44u8, 249u8, 59u8, 243u8, 11u8, 126u8, 193u8, 250u8, 90u8]",
3158
+ aleoRemoteRouterGas: "44000",
3159
+ aleoAllowanceSpendersVerified: true,
3160
+ aleoUnusedAllowancesVerified: true,
3161
+ aleoAllowanceSpender0: "aleo194tz0jmyq8rd9htvnqppqw4jqerk2p2zd8plzn3sxl06wcgsm5pq9fka74",
3162
+ aleoAllowanceSpender1: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3163
+ aleoAllowanceSpender2: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3164
+ aleoAllowanceSpender3: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3165
+ aleoAllowanceAmount1: "0",
3166
+ aleoAllowanceAmount2: "0",
3167
+ aleoAllowanceAmount3: "0"
3168
+ };
3169
+ var ALEO_WBTC_REMOTE_ROUTER = {
3170
+ aleoRemoteRouterVerified: true,
3171
+ aleoRemoteRouterSource: ALEO_WBTC_REMOTE_ROUTER_SOURCE,
3172
+ aleoRemoteRouterReviewedAt: "2026-08-17",
3173
+ aleoDestinationDomain: 1,
3174
+ aleoRemoteRouterEvmAddress: "0x20CDC85778b732073F7EecEF3DF25c0d310f8772",
3175
+ aleoRemoteRouterRecipient: "[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 32u8, 205u8, 200u8, 87u8, 120u8, 183u8, 50u8, 7u8, 63u8, 126u8, 236u8, 239u8, 61u8, 242u8, 92u8, 13u8, 49u8, 15u8, 135u8, 114u8]",
3176
+ aleoRemoteRouterGas: "68000",
3177
+ aleoAllowanceSpendersVerified: true,
3178
+ aleoUnusedAllowancesVerified: true,
3179
+ aleoAllowanceSpender0: "aleo194tz0jmyq8rd9htvnqppqw4jqerk2p2zd8plzn3sxl06wcgsm5pq9fka74",
3180
+ aleoAllowanceSpender1: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3181
+ aleoAllowanceSpender2: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3182
+ aleoAllowanceSpender3: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3183
+ aleoAllowanceAmount1: "0",
3184
+ aleoAllowanceAmount2: "0",
3185
+ aleoAllowanceAmount3: "0"
3186
+ };
3187
+ var ALEO_USDT_ETHEREUM_REMOTE_ROUTER = {
3188
+ aleoRemoteRouterVerified: true,
3189
+ aleoRemoteRouterSource: ALEO_USDT_ETHEREUM_REMOTE_ROUTER_SOURCE,
3190
+ aleoRemoteRouterReviewedAt: "2026-08-17",
3191
+ aleoSampleTransferSource: ALEO_USDT_SAMPLE_TRANSFER_SOURCE,
3192
+ aleoSampleTransferDestinationDomain: 56,
3193
+ aleoDestinationDomain: 1,
3194
+ aleoRemoteRouterEvmAddress: "0x3C2064D78e4578E8F936E3db42aEF044E33FBF31",
3195
+ aleoRemoteRouterRecipient: "[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 60u8, 32u8, 100u8, 215u8, 142u8, 69u8, 120u8, 232u8, 249u8, 54u8, 227u8, 219u8, 66u8, 174u8, 240u8, 68u8, 227u8, 63u8, 191u8, 49u8]",
3196
+ aleoRemoteRouterGas: "68000",
3197
+ aleoAllowanceSpendersVerified: true,
3198
+ aleoUnusedAllowancesVerified: true,
3199
+ aleoAllowanceSpender0: "aleo194tz0jmyq8rd9htvnqppqw4jqerk2p2zd8plzn3sxl06wcgsm5pq9fka74",
3200
+ aleoAllowanceSpender1: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3201
+ aleoAllowanceSpender2: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3202
+ aleoAllowanceSpender3: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3203
+ aleoAllowanceAmount1: "0",
3204
+ aleoAllowanceAmount2: "0",
3205
+ aleoAllowanceAmount3: "0"
3206
+ };
3207
+ var ALEO_SOL_REMOTE_ROUTER = {
3208
+ aleoRemoteRouterVerified: true,
3209
+ aleoRemoteRouterSource: ALEO_SOL_REMOTE_ROUTER_SOURCE,
3210
+ aleoRemoteRouterReviewedAt: "2026-08-17",
3211
+ aleoSampleTransitionId: "au15fg39h53h55tkj0nexrme3k6pvgxngxapcyajdhf06jcg3cyeugq5kd7hg",
3212
+ aleoDestinationDomain: 1399811149,
3213
+ aleoRemoteRouterSolanaAddress: "8YGT2pZwyZe94qBpGzWfY2TMEVcwaQ1bXAE7YAgpUaM7",
3214
+ aleoRemoteRouterRecipient: "[112u8, 4u8, 72u8, 22u8, 219u8, 143u8, 68u8, 202u8, 21u8, 197u8, 236u8, 182u8, 198u8, 142u8, 52u8, 96u8, 142u8, 38u8, 51u8, 113u8, 116u8, 143u8, 96u8, 123u8, 104u8, 126u8, 97u8, 73u8, 7u8, 6u8, 211u8, 122u8]",
3215
+ aleoRemoteRouterGas: "300000",
3216
+ aleoAllowanceSpendersVerified: true,
3217
+ aleoUnusedAllowancesVerified: true,
3218
+ aleoAllowanceSpender0: "aleo194tz0jmyq8rd9htvnqppqw4jqerk2p2zd8plzn3sxl06wcgsm5pq9fka74",
3219
+ aleoAllowanceSpender1: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3220
+ aleoAllowanceSpender2: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3221
+ aleoAllowanceSpender3: "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc",
3222
+ aleoAllowanceAmount1: "0",
3223
+ aleoAllowanceAmount2: "0",
3224
+ aleoAllowanceAmount3: "0"
3225
+ };
3226
+ var ALEO_WITHDRAWAL_ACTIVATION = {
3227
+ aleoPlaceholderConfiguration: false,
3228
+ aleoWithdrawalReviewedAt: "2026-08-26"
3229
+ };
3230
+ var SOLANA_SOL_DEPOSIT_METADATA = {
3231
+ warpProgramAddress: "8YGT2pZwyZe94qBpGzWfY2TMEVcwaQ1bXAE7YAgpUaM7",
3232
+ tokenPda: "JDkpV5CsSbhyGhHhirC5DjGPTcuKWUVHtBZ5MFsgu3ZW",
3233
+ nativeCollateralPda: "8HY3hxmnrWwqEmcdwkSnfN9wEQFUkyiwZvU1vMbnXgbC",
3234
+ dispatchAuthorityPda: "ATDttjggAZKyS19kcV6Rn56oMi49gDprZGckRou9vkkY",
3235
+ mailboxProgramAddress: "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi",
3236
+ mailboxOutboxPda: "BvZpTuYLAR77mPhH4GtvwEWUTs53GQqkgBNuXpCePVNk",
3237
+ igpProgramAddress: "BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv",
3238
+ igpProgramDataPda: "8Cv4PHJ6Cf3xY7dse7wYeZKtuQv9SAN6ujt5w22a2uho",
3239
+ igpAccount: "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M",
3240
+ igpOverheadAccount: "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
3241
+ splNoopProgramAddress: "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
3242
+ destinationDomain: 1634493807,
3243
+ destinationGasAmount: "464000",
3244
+ registryCommit: "418056e21734d26a7d14692e0ec5e902cc9e86bf",
3245
+ solanaReviewedAt: "2026-08-31",
3246
+ solanaConfigSource: ALEO_SOL_HYPERLANE_CONFIG_SOURCE
3247
+ };
3248
+ function route(id, protocol, environment, sourceAssetId, destinationAssetId, availability, deploymentId, metadata2) {
3249
+ return {
3250
+ id,
3251
+ protocol,
3252
+ environment,
3253
+ sourceAssetId,
3254
+ destinationAssetId,
3255
+ availability,
3256
+ deploymentId,
3257
+ source: protocol === "xreserve" ? XRESERVE_SOURCE : HYPERLANE_SOURCE,
3258
+ ...metadata2 == null ? {} : { metadata: metadata2 }
3259
+ };
3260
+ }
3261
+ function pair(protocol, environment, left, right, availability, deploymentId, metadata2, reverseAvailability = availability) {
3262
+ return [
3263
+ route(`${protocol}:${left}->${right}`, protocol, environment, left, right, availability, deploymentId, metadata2),
3264
+ route(`${protocol}:${right}->${left}`, protocol, environment, right, left, reverseAvailability, deploymentId, metadata2)
3265
+ ];
3266
+ }
3267
+ var routes = [
3268
+ ...pair("xreserve", "mainnet", "ethereum/usdc", "aleo/usdcx", "active", "xreserve-usdcx-aleo", {
3269
+ xReserveContract: "0x8888888199b2Df864bf678259607d6D5EBb4e3Ce",
3270
+ sourceChainId: 1,
3271
+ sourceDomain: 0,
3272
+ ethereumDestinationDomain: 0,
3273
+ arcDestinationDomain: 26,
3274
+ remoteDomain: 10002,
3275
+ remoteToken: "usdcx_stablecoin.aleo",
3276
+ remoteTokenBytes32: "0x11ea7dab1d29d5f61500582c63e98c42e1165f9ba050ea9d0c6af9f871987711",
3277
+ minimumAmountAtomic: "2000000",
3278
+ withdrawalFeeAtomic: "2000000",
3279
+ maxFeeAtomic: "100000",
3280
+ bridgeProgram: "usdcx_bridge_v2.aleo",
3281
+ wrapperProgram: "shielded_usdcx_wrapper.aleo",
3282
+ attestationBaseUrl: "https://xreserve-api.circle.com/v1/attestations"
3283
+ }, "active"),
3284
+ ...pair("xreserve", "testnet", "sepolia/usdc", "aleo-testnet/usdcx", "active", "xreserve-usdcx-aleo-testnet", {
3285
+ xReserveContract: "0x008888878f94C0d87defdf0B07f46B93C1934442",
3286
+ sourceChainId: 11155111,
3287
+ sourceDomain: 0,
3288
+ ethereumDestinationDomain: 0,
3289
+ arcDestinationDomain: 26,
3290
+ remoteDomain: 10002,
3291
+ remoteToken: "test_usdcx_stablecoin.aleo",
3292
+ remoteTokenBytes32: "0xb143ed52c774cd1d4a519d0e796f15916be5a9e1d45edcd9852dd23f68f53401",
3293
+ minimumAmountAtomic: "2000000",
3294
+ withdrawalFeeAtomic: "2000000",
3295
+ maxFeeAtomic: "100000",
3296
+ bridgeProgram: "test_usdcx_bridge_v2.aleo",
3297
+ wrapperProgram: "shielded_usdcx_wrapper.aleo",
3298
+ attestationBaseUrl: "https://xreserve-api-testnet.circle.com/v1/attestations"
3299
+ }, "active"),
3300
+ route("hyperlane:ethereum/eth->aleo/eth", "hyperlane", "mainnet", "ethereum/eth", "aleo/eth", "active", "ETH/aleo", { ...ETH_HYPERLANE_METADATA, ...ALEO_MAILBOX_METADATA }),
3301
+ route("hyperlane:aleo/eth->ethereum/eth", "hyperlane", "mainnet", "aleo/eth", "ethereum/eth", "active", "ETH/aleo", { ...ETH_HYPERLANE_METADATA, ...aleoHyperlanePlaceholders("hyp_warp_token_eth_v2.aleo", 1), ...ALEO_ETH_APP_METADATA, ...ALEO_ETH_REMOTE_ROUTER, ...ALEO_WITHDRAWAL_ACTIVATION }),
3302
+ route("hyperlane:ethereum/wbtc->aleo/wbtc", "hyperlane", "mainnet", "ethereum/wbtc", "aleo/wbtc", "active", "WBTC/aleo", { ...WBTC_HYPERLANE_METADATA, ...ALEO_MAILBOX_METADATA }),
3303
+ route("hyperlane:aleo/wbtc->ethereum/wbtc", "hyperlane", "mainnet", "aleo/wbtc", "ethereum/wbtc", "active", "WBTC/aleo", { ...WBTC_HYPERLANE_METADATA, ...aleoHyperlanePlaceholders("hyp_warp_token_wbtc_v2.aleo", 1), ...ALEO_WBTC_APP_METADATA, ...ALEO_WBTC_REMOTE_ROUTER, ...ALEO_WITHDRAWAL_ACTIVATION }),
3304
+ route("hyperlane:ethereum/usdt->aleo/usdt", "hyperlane", "mainnet", "ethereum/usdt", "aleo/usdt", "active", "USDT/aleo", { ...USDT_HYPERLANE_METADATA, ...ALEO_MAILBOX_METADATA }),
3305
+ route("hyperlane:aleo/usdt->ethereum/usdt", "hyperlane", "mainnet", "aleo/usdt", "ethereum/usdt", "active", "USDT/aleo", { ...USDT_HYPERLANE_METADATA, ...aleoHyperlanePlaceholders("hyp_warp_token_usdt_v2.aleo", 1), ...ALEO_USDT_APP_METADATA, ...ALEO_USDT_ETHEREUM_REMOTE_ROUTER, ...ALEO_WITHDRAWAL_ACTIVATION }),
3306
+ route("hyperlane:solana/sol->aleo/sol", "hyperlane", "mainnet", "solana/sol", "aleo/sol", "active", "SOL/aleo", { ...SOLANA_SOL_DEPOSIT_METADATA, ...ALEO_MAILBOX_METADATA }),
3307
+ route("hyperlane:aleo/sol->solana/sol", "hyperlane", "mainnet", "aleo/sol", "solana/sol", "active", "SOL/aleo", { ...aleoHyperlanePlaceholders("hyp_warp_token_sol_v2.aleo", 1399811149), ...ALEO_SOL_APP_METADATA, ...ALEO_SOL_REMOTE_ROUTER, ...ALEO_WITHDRAWAL_ACTIVATION }),
3308
+ ...pair("hyperlane", "mainnet", "aleo/aleo", "ethereum/aleo", "metadata-required", "ALEO/aleo", ALEO_MAILBOX_METADATA),
3309
+ ...pair("hyperlane", "mainnet", "aleo/aleo", "solana/aleo", "metadata-required", "ALEO/aleo", ALEO_MAILBOX_METADATA),
3310
+ ...pair("hyperlane", "mainnet", "aleo/aleo", "base/aleo", "metadata-required", "ALEO/aleo", ALEO_MAILBOX_METADATA),
3311
+ ...pair("hyperlane", "mainnet", "aleo/aleo", "hyperevm/aleo", "metadata-required", "ALEO/aleo", ALEO_MAILBOX_METADATA),
3312
+ route("hyperlane:ethereum/usad->aleo/usad", "hyperlane", "mainnet", "ethereum/usad", "aleo/usad", "metadata-required", "USAD/aleo", ALEO_MAILBOX_METADATA),
3313
+ route("hyperlane:aleo/usad->ethereum/usad", "hyperlane", "mainnet", "aleo/usad", "ethereum/usad", "metadata-required", "USAD/aleo", aleoHyperlanePlaceholders("hyp_warp_token_usad_v2.aleo", 1))
3314
+ ];
3315
+ var DEFAULT_BRIDGE_REGISTRY = Object.freeze({
3316
+ version: "2026-08-31.solana-deposits.1",
3317
+ chains: Object.freeze(chains),
3318
+ assets: Object.freeze(assets),
3319
+ routes: Object.freeze(routes),
3320
+ sources: Object.freeze([XRESERVE_SOURCE, HYPERLANE_SOURCE]),
3321
+ getAssets(params = {}) {
3322
+ const chains2 = new Map(this.chains.map((chain) => [chain.id, chain]));
3323
+ const chainId = params.chainId?.toLowerCase();
3324
+ const symbol = params.symbol?.toLowerCase();
3325
+ return this.assets.filter((asset) => {
3326
+ const chain = chains2.get(asset.chainId);
3327
+ return (params.environment == null || chain?.environment === params.environment) && (chainId == null || asset.chainId.toLowerCase() === chainId) && (symbol == null || asset.symbol.toLowerCase() === symbol);
3328
+ });
3329
+ },
3330
+ getRoutes(params = {}) {
3331
+ const assets2 = new Map(this.assets.map((asset) => [asset.id, asset]));
3332
+ const sourceChainId = params.sourceChainId?.toLowerCase();
3333
+ const destinationChainId = params.destinationChainId?.toLowerCase();
3334
+ const symbol = params.symbol?.toLowerCase();
3335
+ return this.routes.filter((route2) => {
3336
+ const source = assets2.get(route2.sourceAssetId);
3337
+ const destination = assets2.get(route2.destinationAssetId);
3338
+ return (params.includeUnavailable === true || route2.availability !== "disabled") && (params.environment == null || route2.environment === params.environment) && (params.protocol == null || route2.protocol === params.protocol) && (sourceChainId == null || source.chainId.toLowerCase() === sourceChainId) && (destinationChainId == null || destination.chainId.toLowerCase() === destinationChainId) && (symbol == null || source.symbol.toLowerCase() === symbol || destination.symbol.toLowerCase() === symbol);
3339
+ });
3340
+ }
3341
+ });
3342
+
3343
+ // src/registry/validate.ts
3344
+ var REQUIRED_SOLANA_HYPERLANE_METADATA_FIELDS = [
3345
+ "warpProgramAddress",
3346
+ "tokenPda",
3347
+ "nativeCollateralPda",
3348
+ "dispatchAuthorityPda",
3349
+ "mailboxProgramAddress",
3350
+ "mailboxOutboxPda",
3351
+ "igpProgramAddress",
3352
+ "igpProgramDataPda",
3353
+ "igpAccount",
3354
+ "splNoopProgramAddress",
3355
+ "destinationDomain",
3356
+ "destinationGasAmount",
3357
+ "registryCommit",
3358
+ "solanaReviewedAt",
3359
+ "solanaConfigSource"
3360
+ ];
3361
+ function hasCompleteSolanaHyperlaneMetadata(metadata2) {
3362
+ if (!metadata2) return false;
3363
+ return REQUIRED_SOLANA_HYPERLANE_METADATA_FIELDS.every((field) => {
3364
+ const value = metadata2[field];
3365
+ return field === "destinationDomain" ? typeof value === "number" : typeof value === "string" && value.length > 0;
3366
+ });
3367
+ }
3368
+ function validateBridgeRegistry(registry) {
3369
+ if (!registry.version.trim()) throw new BridgeError("Bridge registry version must not be empty");
3370
+ const chainIds = /* @__PURE__ */ new Set();
3371
+ for (const chain of registry.chains) {
3372
+ if (chainIds.has(chain.id)) throw new BridgeError(`Duplicate bridge chain id: ${chain.id}`);
3373
+ chainIds.add(chain.id);
3374
+ }
3375
+ const assetIds = /* @__PURE__ */ new Set();
3376
+ const assetKeys = /* @__PURE__ */ new Set();
3377
+ for (const asset of registry.assets) {
3378
+ if (assetIds.has(asset.id)) throw new BridgeError(`Duplicate bridge asset id: ${asset.id}`);
3379
+ if (!chainIds.has(asset.chainId)) {
3380
+ throw new BridgeError(`Bridge asset ${asset.id} references unknown chain ${asset.chainId}`);
3381
+ }
3382
+ if (!asset.key.trim()) throw new BridgeError(`Bridge asset ${asset.id} has an empty key`);
3383
+ const scopedKey = `${asset.chainId}/${asset.key}`;
3384
+ if (assetKeys.has(scopedKey)) throw new BridgeError(`Duplicate bridge asset key: ${scopedKey}`);
3385
+ if (!Number.isInteger(asset.decimals) || asset.decimals < 0) {
3386
+ throw new BridgeError(`Bridge asset ${asset.id} has invalid decimals ${asset.decimals}`);
3387
+ }
3388
+ if (asset.addressValidationRegex) {
3389
+ try {
3390
+ new RegExp(asset.addressValidationRegex);
3391
+ } catch (cause) {
3392
+ throw new BridgeError(`Bridge asset ${asset.id} has an invalid address validation regex`, {
3393
+ cause
3394
+ });
3395
+ }
3396
+ }
3397
+ if (asset.privacy) {
3398
+ const chain = registry.chains.find((entry) => entry.id === asset.chainId);
3399
+ if (chain?.family !== "aleo") {
3400
+ throw new BridgeError(`Bridge asset ${asset.id} declares a privacy capability on a non-Aleo chain`);
3401
+ }
3402
+ if (!asset.privacy.program.trim()) {
3403
+ throw new BridgeError(`Bridge asset ${asset.id} has an empty privacy program`);
3404
+ }
3405
+ if (asset.privacy.kind !== "arc20" && asset.privacy.kind !== "arc22") {
3406
+ throw new BridgeError(`Bridge asset ${asset.id} has an unsupported privacy capability kind`);
3407
+ }
3408
+ }
3409
+ assetIds.add(asset.id);
3410
+ assetKeys.add(scopedKey);
3411
+ }
3412
+ const routeIds = /* @__PURE__ */ new Set();
3413
+ for (const route2 of registry.routes) {
3414
+ if (routeIds.has(route2.id)) throw new BridgeError(`Duplicate bridge route id: ${route2.id}`);
3415
+ if (!assetIds.has(route2.sourceAssetId)) {
3416
+ throw new BridgeError(`Bridge route ${route2.id} references unknown source asset ${route2.sourceAssetId}`);
3417
+ }
3418
+ if (!assetIds.has(route2.destinationAssetId)) {
3419
+ throw new BridgeError(`Bridge route ${route2.id} references unknown destination asset ${route2.destinationAssetId}`);
3420
+ }
3421
+ const source = registry.assets.find((asset) => asset.id === route2.sourceAssetId);
3422
+ const destination = registry.assets.find((asset) => asset.id === route2.destinationAssetId);
3423
+ const sourceChain = registry.chains.find((chain) => chain.id === source.chainId);
3424
+ const destinationChain = registry.chains.find((chain) => chain.id === destination.chainId);
3425
+ if (sourceChain.environment !== route2.environment || destinationChain.environment !== route2.environment) {
3426
+ throw new BridgeError(`Bridge route ${route2.id} crosses registry environments`);
3427
+ }
3428
+ if (route2.protocol === "hyperlane" && route2.availability === "active" && sourceChain.family === "solana" && !hasCompleteSolanaHyperlaneMetadata(route2.metadata)) {
3429
+ throw new BridgeError(`Bridge route ${route2.id} is active but missing required Solana Hyperlane metadata`);
3430
+ }
3431
+ routeIds.add(route2.id);
3432
+ }
3433
+ return registry;
3434
+ }
3435
+
3436
+ // src/clients/createBridgeClient.ts
3437
+ function createBridgeClient(config = {}) {
3438
+ const environment = config.environment ?? "mainnet";
3439
+ const registry = validateBridgeRegistry(config.registry ?? DEFAULT_BRIDGE_REGISTRY);
3440
+ const fetch = config.fetch ?? globalThis.fetch;
3441
+ const clients = config.clients ?? {};
3442
+ return {
3443
+ key: config.key ?? "bridge",
3444
+ name: config.name ?? "Bridge Client",
3445
+ environment,
3446
+ registry,
3447
+ ...bridgeActions({ registry, clients, fetch })
3448
+ };
3449
+ }
3450
+
3451
+ // src/connections/evm.ts
3452
+ import {
3453
+ createPublicClient,
3454
+ createWalletClient,
3455
+ custom,
3456
+ defineChain,
3457
+ getAddress as getAddress6,
3458
+ http
3459
+ } from "viem";
3460
+ import { privateKeyToAccount } from "viem/accounts";
3461
+ function evmHttp(url, options = {}) {
3462
+ return { type: "http", url, fetch: options.fetch };
3463
+ }
3464
+ function evmCustom(request) {
3465
+ return { type: "custom", request };
3466
+ }
3467
+ function evmProvider(provider, options = {}) {
3468
+ return { type: "provider", provider, account: options.account };
3469
+ }
3470
+ function evmPrivateKey(privateKey) {
3471
+ return { type: "local", account: privateKeyToAccount(privateKey) };
3472
+ }
3473
+ function evmLocalAccount(account2) {
3474
+ return { type: "local", account: account2 };
3475
+ }
3476
+ function createEvmClient(config) {
3477
+ if (config.transport && config.publicClient) {
3478
+ throw new BridgeError("EVM client accepts either transport or publicClient, not both");
3479
+ }
3480
+ if (config.account && config.walletClient) {
3481
+ throw new BridgeError("EVM client accepts either account or walletClient, not both");
3482
+ }
3483
+ if (!config.transport && !config.publicClient && !config.account && !config.walletClient) {
3484
+ throw new BridgeError("EVM client requires a public or wallet capability");
3485
+ }
3486
+ if (config.account?.type === "local" && !config.transport && !config.publicClient) {
3487
+ throw new BridgeError("Local EVM accounts require an EVM transport or public client");
3488
+ }
3489
+ return materializeEvmClient(config, globalThis.fetch);
3490
+ }
3491
+ function transportFor(transport, defaultFetch) {
3492
+ if (transport.type === "custom") return custom({ request: transport.request });
3493
+ return http(transport.url, { fetchFn: transport.fetch ?? defaultFetch });
3494
+ }
3495
+ function normalizePublicClient(client) {
3496
+ return {
3497
+ getChainId: () => client.getChainId(),
3498
+ getBalance: (address) => client.getBalance({ address }),
3499
+ call: async (params) => (await client.call(params)).data ?? "0x",
3500
+ getTransactionReceipt: async (hash) => {
3501
+ try {
3502
+ return await client.getTransactionReceipt({ hash });
3503
+ } catch (error) {
3504
+ if (error instanceof Error && error.name === "TransactionReceiptNotFoundError") return null;
3505
+ throw error;
3506
+ }
3507
+ },
3508
+ getLogs: async (params) => client.getLogs(params),
3509
+ getTransaction: async (hash) => {
3510
+ try {
3511
+ return await client.getTransaction({ hash });
3512
+ } catch (error) {
3513
+ if (error instanceof Error && error.name === "TransactionNotFoundError") return null;
3514
+ throw error;
3515
+ }
3516
+ }
3517
+ };
3518
+ }
3519
+ function normalizeWalletClient(client) {
3520
+ const resolveAddress = async () => {
3521
+ if (client.account) return client.account.address;
3522
+ const [address] = await client.getAddresses();
3523
+ if (!address) throw new BridgeError("EVM wallet has no connected account");
3524
+ return address;
3525
+ };
3526
+ return {
3527
+ getAddress: resolveAddress,
3528
+ sendTransaction: async ({ chainId, from, ...transaction }) => {
3529
+ const currentChainId = await client.getChainId();
3530
+ if (currentChainId !== chainId) {
3531
+ throw new BridgeError(`EVM wallet is connected to chain ${currentChainId}; expected ${chainId}`);
3532
+ }
3533
+ const account2 = await resolveAddress();
3534
+ if (from && getAddress6(from) !== getAddress6(account2)) {
3535
+ throw new BridgeError(`EVM transaction sender ${from} does not match connected account ${account2}`);
3536
+ }
3537
+ return client.sendTransaction({
3538
+ ...transaction,
3539
+ account: client.account ?? account2,
3540
+ // Viem treats an omitted chain as a request to validate against a
3541
+ // configured chain and throws when an account-only client has none.
3542
+ // The adapter already compared getChainId() with the requested id.
3543
+ chain: client.chain ?? null
3544
+ });
3545
+ }
3546
+ };
3547
+ }
3548
+ function materializeEvmClient(config, defaultFetch) {
3549
+ const transport = config.transport ? transportFor(config.transport, defaultFetch) : void 0;
3550
+ const viemPublic = config.publicClient ?? (transport ? createPublicClient({ transport }) : void 0);
3551
+ let publicClient = viemPublic ? normalizePublicClient(viemPublic) : void 0;
3552
+ let walletClient = config.walletClient ? normalizeWalletClient(config.walletClient) : void 0;
3553
+ if (!publicClient && config.walletClient) {
3554
+ const request = config.walletClient.request;
3555
+ publicClient = normalizePublicClient(createPublicClient({ transport: custom({ request }) }));
3556
+ }
3557
+ if (config.account?.type === "provider") {
3558
+ const provider = config.account.provider;
3559
+ if (!publicClient) publicClient = normalizePublicClient(createPublicClient({ transport: custom(provider) }));
3560
+ const account2 = config.account.account;
3561
+ walletClient = {
3562
+ getAddress: async () => {
3563
+ if (account2) return account2;
3564
+ const addresses = await provider.request({ method: "eth_accounts" });
3565
+ const address = Array.isArray(addresses) ? addresses[0] : void 0;
3566
+ if (typeof address !== "string") throw new BridgeError("EVM wallet has no connected account");
3567
+ return address;
3568
+ },
3569
+ sendTransaction: async ({ chainId, from, to, data, value }) => {
3570
+ const current = await provider.request({ method: "eth_chainId" });
3571
+ if (typeof current !== "string" || Number.parseInt(current, 16) !== chainId) {
3572
+ throw new BridgeError(`EVM wallet is connected to chain ${String(current)}; expected ${chainId}`);
3573
+ }
3574
+ const sender = from ?? await walletClient.getAddress();
3575
+ const hash = await provider.request({
3576
+ method: "eth_sendTransaction",
3577
+ params: [{ from: sender, to, data, ...value === void 0 ? {} : { value: `0x${value.toString(16)}` } }]
3578
+ });
3579
+ if (typeof hash !== "string") throw new BridgeError("EVM wallet returned an invalid transaction hash");
3580
+ return hash;
3581
+ }
3582
+ };
3583
+ } else if (config.account?.type === "local") {
3584
+ if (!viemPublic) throw new BridgeError("Local EVM accounts require an EVM transport or public client");
3585
+ const localAccount = config.account.account;
3586
+ walletClient = {
3587
+ getAddress: async () => localAccount.address,
3588
+ sendTransaction: async ({ chainId, from, ...transaction }) => {
3589
+ const currentChainId = await viemPublic.getChainId();
3590
+ if (currentChainId !== chainId) {
3591
+ throw new BridgeError(`EVM transport is connected to chain ${currentChainId}; expected ${chainId}`);
3592
+ }
3593
+ if (from && getAddress6(from) !== getAddress6(localAccount.address)) {
3594
+ throw new BridgeError(`EVM transaction sender ${from} does not match local account ${localAccount.address}`);
3595
+ }
3596
+ const chain = viemPublic.chain ?? defineChain({
3597
+ id: chainId,
3598
+ name: `EVM chain ${chainId}`,
3599
+ nativeCurrency: { name: "Native token", symbol: "ETH", decimals: 18 },
3600
+ rpcUrls: { default: { http: [] } }
3601
+ });
3602
+ const localWallet = createWalletClient({
3603
+ account: localAccount,
3604
+ chain,
3605
+ transport: custom({ request: viemPublic.request }, { retryCount: 0 })
3606
+ });
3607
+ return localWallet.sendTransaction({ ...transaction, account: localAccount, chain });
3608
+ }
3609
+ };
3610
+ }
3611
+ if (!publicClient) throw new BridgeError("EVM client could not materialize a public client");
3612
+ return { family: "evm", publicClient, walletClient };
3613
+ }
3614
+
3615
+ // src/connections/aleo.ts
3616
+ function aleoWallet(client) {
3617
+ return client;
3618
+ }
3619
+ function createAleoClient(config) {
3620
+ if (!config.publicClient) throw new BridgeError("Aleo client requires a public client");
3621
+ return { family: "aleo", publicClient: config.publicClient, walletClient: config.account };
3622
+ }
3623
+
3624
+ // src/protocols/index.ts
3625
+ function splitRegistry(params) {
3626
+ const { registry = DEFAULT_BRIDGE_REGISTRY, ...rest } = params;
3627
+ return [registry, rest];
3628
+ }
3629
+ var hyperlane = {
3630
+ aleo: {
3631
+ /**
3632
+ * Calculates the Hyperlane relayer payment for a transfer leaving Aleo.
3633
+ *
3634
+ * Reads the current gas oracle on Aleo without requesting a signature or
3635
+ * moving funds. The payment can change before the transfer is submitted.
3636
+ *
3637
+ * @param client Aleo network access used to read the current gas price and exchange rate.
3638
+ * @param params Route selected for the transfer and optional replacement bridge deployments.
3639
+ * @returns Destination gas requirements and the exact payment in Aleo microcredits.
3640
+ * @throws BridgeError When the route is unavailable or its gas configuration is invalid.
3641
+ * @example const result = await hyperlane.aleo.quote(client, { routeId })
3642
+ */
3643
+ quote(client, params) {
3644
+ const [registry, actionParams] = splitRegistry(params);
3645
+ return quote(registry, client, actionParams);
3646
+ },
3647
+ /**
3648
+ * Begins an Aleo-to-Ethereum or Aleo-to-Solana transfer through Hyperlane.
3649
+ *
3650
+ * The Aleo wallet proves, signs, and submits the source transaction, which
3651
+ * commits the asset and incurs an Aleo network fee.
3652
+ *
3653
+ * @param client Aleo wallet that authorizes and submits the source transaction.
3654
+ * @param params Route, assets, amount, recipient, gas payment, and optional replacement bridge deployments.
3655
+ * @returns The Aleo transaction identifier and state needed to follow delivery.
3656
+ * @throws BridgeError When the transfer is unavailable, its payment is invalid, or wallet submission fails.
3657
+ * @example const result = await hyperlane.aleo.execute(client, { plan, gasPaymentMicrocredits })
3658
+ */
3659
+ execute(client, params) {
3660
+ const [registry, actionParams] = splitRegistry(params);
3661
+ return execute(registry, client, actionParams);
3662
+ }
3663
+ },
3664
+ evm: {
3665
+ /**
3666
+ * Calculates the funds required for a Hyperlane transfer leaving an EVM chain.
3667
+ *
3668
+ * Reads the deployed router without requesting a wallet signature or moving
3669
+ * funds. The quoted network payment can change before submission.
3670
+ *
3671
+ * @param client EVM network access used to read the selected Hyperlane router.
3672
+ * @param params Route, assets, amount, encoded Aleo recipient, and optional replacement bridge deployments.
3673
+ * @returns Source token amount and native network payment required by the router.
3674
+ * @throws BridgeError When the route is unavailable or the router returns invalid values.
3675
+ * @example const result = await hyperlane.evm.quote(client, { plan, recipientBytes32 })
3676
+ */
3677
+ quote(client, params) {
3678
+ const [registry, actionParams] = splitRegistry(params);
3679
+ return quote2(registry, client, actionParams);
3680
+ },
3681
+ /**
3682
+ * Begins an EVM-to-Aleo transfer through Hyperlane.
3683
+ *
3684
+ * An ERC-20 transfer may first request token approval. The wallet then
3685
+ * submits the source dispatch, which commits funds and incurs network fees.
3686
+ *
3687
+ * @param client EVM network and wallet access used to authorize and submit the transfer.
3688
+ * @param params Route, assets, amount, encoded Aleo recipient, confirmation controls, and optional replacement bridge deployments.
3689
+ * @returns Submitted approval identifiers and state needed to follow delivery.
3690
+ * @throws BridgeError When the transfer is unavailable, wallet authorization fails, funds are insufficient, or submission fails.
3691
+ * @example const result = await hyperlane.evm.execute(client, { plan, recipientBytes32 })
3692
+ */
3693
+ execute(client, params) {
3694
+ const [registry, actionParams] = splitRegistry(params);
3695
+ return execute2(registry, client, actionParams);
3696
+ }
3697
+ },
3698
+ solana: {
3699
+ /**
3700
+ * Calculates the SOL required for a Solana-to-Aleo Hyperlane transfer.
3701
+ *
3702
+ * Reads current gas, transaction fee, and rent requirements without
3703
+ * requesting a wallet signature or moving funds.
3704
+ *
3705
+ * @param client Solana network access used to read account, fee, and rent values.
3706
+ * @param params Route, amount, recipient, and optional replacement bridge deployments.
3707
+ * @returns Transfer amount, relayer payment, network fee, rent, and total required lamports.
3708
+ * @throws BridgeError When the route is unavailable or Solana returns invalid account or fee data.
3709
+ * @example const result = await hyperlane.solana.quote(client, { plan })
3710
+ */
3711
+ quote(client, params) {
3712
+ const [registry, actionParams] = splitRegistry(params);
3713
+ return quote3(registry, client, actionParams);
3714
+ },
3715
+ /**
3716
+ * Begins a Solana-to-Aleo transfer through Hyperlane.
3717
+ *
3718
+ * The Solana wallet signs and submits the source transaction, which commits
3719
+ * SOL and incurs the relayer payment, network fee, and account rent.
3720
+ *
3721
+ * @param client Solana network and wallet access used to authorize and submit the transfer.
3722
+ * @param params Route, amount, recipient, confirmation controls, and optional replacement bridge deployments.
3723
+ * @returns The Solana signature and state needed to follow delivery.
3724
+ * @throws BridgeError When the route is unavailable, funds are insufficient, wallet authorization fails, or submission fails.
3725
+ * @example const result = await hyperlane.solana.execute(client, { plan })
3726
+ */
3727
+ execute(client, params) {
3728
+ const [registry, actionParams] = splitRegistry(params);
3729
+ return execute3(registry, client, actionParams);
3730
+ }
3731
+ }
3732
+ };
3733
+ var xreserve = {
3734
+ evmToAleo: {
3735
+ /**
3736
+ * Calculates the USDC and token approval required for an xReserve transfer to Aleo.
3737
+ *
3738
+ * Reads the prepared sender's USDC balance and existing xReserve allowance
3739
+ * without requesting a signature or moving funds. When the plan omits a
3740
+ * sender, the client resolves it from its optional wallet capability.
3741
+ *
3742
+ * @param client Ethereum network access, plus a wallet when the plan does not identify the source account.
3743
+ * @param params Route, amount, Aleo recipient, privacy preference, and optional replacement bridge deployments.
3744
+ * @returns Deposit amount, maximum provider fee, balance, allowance, and whether approval is required.
3745
+ * @throws BridgeError When the route is unavailable, the account lacks funds, or Ethereum returns invalid state.
3746
+ * @example const result = await xreserve.evmToAleo.quote(client, { plan })
3747
+ */
3748
+ quote(client, params) {
3749
+ const [registry, actionParams] = splitRegistry(params);
3750
+ return quote4(registry, client, actionParams);
3751
+ },
3752
+ /**
3753
+ * Begins a USDC-to-USDCx transfer from Ethereum to Aleo through xReserve.
3754
+ *
3755
+ * The wallet may first approve USDC spending, then submits the reserve
3756
+ * deposit that commits funds and incurs Ethereum network fees.
3757
+ *
3758
+ * @param client Ethereum network and wallet access used to authorize and submit the deposit.
3759
+ * @param params Route, amount, Aleo recipient, privacy preference, confirmation controls, and optional replacement bridge deployments.
3760
+ * @returns Submitted approval identifiers and state needed to obtain Circle's attestation and follow delivery.
3761
+ * @throws BridgeError When the route is unavailable, funds are insufficient, wallet authorization fails, or submission fails.
3762
+ * @example const result = await xreserve.evmToAleo.execute(client, { plan })
3763
+ */
3764
+ execute(client, params) {
3765
+ const [registry, actionParams] = splitRegistry(params);
3766
+ return execute5(registry, client, actionParams);
3767
+ },
3768
+ /**
3769
+ * Checks whether Circle has attested one confirmed xReserve deposit.
3770
+ *
3771
+ * Contacts Circle once and does not request a wallet signature, submit a
3772
+ * transaction, or move funds.
3773
+ *
3774
+ * @param client HTTP access used to contact Circle's attestation service.
3775
+ * @param params Deposit message hash, route, cancellation signal, and optional replacement bridge deployments.
3776
+ * @returns Whether the attestation is pending or the signed attestation is ready.
3777
+ * @throws BridgeError When Circle returns an invalid response.
3778
+ * @example const result = await xreserve.evmToAleo.getAttestation(fetch, { routeId, messageHash })
3779
+ */
3780
+ getAttestation(client, params) {
3781
+ const [registry, actionParams] = splitRegistry(params);
3782
+ return getAttestation(registry, client, actionParams);
3783
+ },
3784
+ /**
3785
+ * Delivers a private USDCx record after Circle attests an Ethereum deposit.
3786
+ *
3787
+ * The Aleo wallet proves, signs, and submits the private mint, which incurs
3788
+ * an Aleo network fee. The source deposit is not repeated.
3789
+ *
3790
+ * @param client Aleo wallet that authorizes and submits the private mint.
3791
+ * @param params Transfer details, attested deposit, private mint secret, recovery callback, and optional replacement bridge deployments.
3792
+ * @returns The Aleo transaction identifier and state needed to confirm private delivery.
3793
+ * @throws BridgeError When the attestation or private mint secret is invalid, wallet authorization fails, or submission fails.
3794
+ * @example const result = await xreserve.evmToAleo.complete(client, { plan, deposit, attestation })
3795
+ */
3796
+ complete(client, params) {
3797
+ const [registry, actionParams] = splitRegistry(params);
3798
+ return complete(registry, client, actionParams);
3799
+ }
3800
+ },
3801
+ aleoToEvm: {
3802
+ /**
3803
+ * Begins a USDCx-to-USDC transfer from Aleo to Ethereum through xReserve.
3804
+ *
3805
+ * The Aleo wallet proves, signs, and submits a burn that commits USDCx and
3806
+ * incurs an Aleo network fee. The provider completes Ethereum delivery
3807
+ * without another wallet authorization.
3808
+ *
3809
+ * @param client Aleo wallet that authorizes and submits the USDCx burn.
3810
+ * @param params Route, amount, Ethereum recipient, public or private funding preference, and optional replacement bridge deployments.
3811
+ * @returns The Aleo transaction identifier and state needed to follow provider-managed delivery.
3812
+ * @throws BridgeError When the route or private funding inputs are invalid, wallet authorization fails, or submission fails.
3813
+ * @example const result = await xreserve.aleoToEvm.execute(client, { plan, mode: 'public' })
3814
+ */
3815
+ execute(client, params) {
3816
+ const [registry, actionParams] = splitRegistry(params);
3817
+ return execute4(registry, client, actionParams);
3818
+ }
3819
+ }
3820
+ };
3821
+
3822
+ // src/builders/buildXReserveBurnCall.ts
3823
+ function buildXReserveBurnCall(registry, params) {
3824
+ return buildBurnCall(registry, params);
3825
+ }
3826
+
3827
+ // src/builders/buildAleoHyperlaneTransferRemoteCall.ts
3828
+ function buildAleoHyperlaneTransferRemoteCall(registry, params) {
3829
+ return buildTransferRemoteCall(registry, params);
3830
+ }
3831
+ export {
3832
+ BridgeError,
3833
+ DEFAULT_BRIDGE_REGISTRY,
3834
+ DEFAULT_SOLANA_RPC_URL,
3835
+ aleoAddressToBytes32,
3836
+ aleoProgramAddress,
3837
+ aleoWallet,
3838
+ bridgeActions,
3839
+ buildAleoHyperlaneTransferRemoteCall,
3840
+ buildXReserveBurnCall,
3841
+ buildXReserveDepositPayload,
3842
+ buildXReserveHookData,
3843
+ bytes32ToAleoAddress,
3844
+ calculateXReserveDepositNonce,
3845
+ calculateXReserveMessageHash,
3846
+ complete2 as complete,
3847
+ createAleoClient,
3848
+ createBridgeCheckpoint,
3849
+ createBridgeClient,
3850
+ createEvmClient,
3851
+ createSolanaClient,
3852
+ evmAddressToAleoHyperlaneRecipient,
3853
+ evmAddressToXReserveBytes32,
3854
+ evmCustom,
3855
+ evmHttp,
3856
+ evmLocalAccount,
3857
+ evmPrivateKey,
3858
+ evmProvider,
3859
+ execute6 as execute,
3860
+ formatDecimalAmount,
3861
+ getStatus,
3862
+ hyperlane,
3863
+ parseDecimalAmount,
3864
+ quote5 as quote,
3865
+ readHyperlaneDelivery,
3866
+ readXReserveDelivery,
3867
+ recover,
3868
+ resume,
3869
+ shield,
3870
+ solanaAddressToAleoHyperlaneRecipient,
3871
+ solanaCustom,
3872
+ solanaHttp,
3873
+ solanaKeyPair,
3874
+ solanaWallet,
3875
+ unshield,
3876
+ validateBridgeRegistry,
3877
+ wait2 as wait,
3878
+ xReserveDepositNonceFromPayload,
3879
+ xReserveHexToAleoBytes,
3880
+ xreserve
3881
+ };
3882
+ //# sourceMappingURL=index.js.map