@provablehq/aleo-bridge-sdk 0.8.0-rc.0 → 0.11.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/README.md CHANGED
@@ -1,325 +1,391 @@
1
1
  # @provablehq/aleo-bridge-sdk
2
2
 
3
- A protocol-oriented bridge client for Aleo. USDCx transfers use Circle
4
- xReserve. ETH, WBTC, USDT, SOL, ALEO, and USAD transfers use Hyperlane Warp Routes.
3
+ Moves assets between Aleo, Ethereum, and Solana through reviewed Hyperlane and
4
+ Circle xReserve deployments.
5
+
6
+ The package supports browser wallets and local keys. It does not choose a
7
+ wallet, store transfer progress, or submit a second transaction after an
8
+ interruption without caller authorization.
9
+
10
+ > This package is published as a preview. It versions in lockstep with the
11
+ > `@provablehq/veil-*` packages, but its API is subject to breaking changes
12
+ > between minor releases.
13
+
14
+ ## Supported transfers
15
+
16
+ | Source | Destination | Asset received | Provider |
17
+ | --- | --- | --- | --- |
18
+ | Ethereum ETH | Aleo | ETH | Hyperlane |
19
+ | Aleo ETH | Ethereum | ETH | Hyperlane |
20
+ | Ethereum WBTC | Aleo | WBTC | Hyperlane |
21
+ | Aleo WBTC | Ethereum | WBTC | Hyperlane |
22
+ | Ethereum USDT | Aleo | USDT | Hyperlane |
23
+ | Aleo USDT | Ethereum | USDT | Hyperlane |
24
+ | Solana SOL | Aleo | SOL | Hyperlane |
25
+ | Aleo SOL | Solana | SOL | Hyperlane |
26
+ | Ethereum USDC | Aleo | USDCx | Circle xReserve |
27
+ | Aleo USDCx | Ethereum | USDC | Circle xReserve |
28
+
29
+ The registry also contains incomplete ALEO and USAD Hyperlane entries for
30
+ deployment discovery. Those entries are marked `metadata-required` and cannot
31
+ be quoted or executed. Solana routes currently support native SOL, not USDC or
32
+ other SPL tokens.
33
+
34
+ ## Create a browser client
35
+
36
+ A browser application supplies public network access and the wallet accounts
37
+ that may authorize transfers. Public clients read balances, fees, and
38
+ transaction status. Wallet clients request signatures only when a fund-moving
39
+ action runs.
5
40
 
6
- The package is in preview and is not published to npm. It provides reviewed
7
- route discovery, non-fund-moving transfer plans, and injected-wallet execution
8
- for Ethereum-to-Aleo xReserve USDC deposits and Hyperlane routes carrying ETH,
9
- WBTC, and USDT. Aleo-origin and Solana execution paths remain under development.
41
+ ```ts
42
+ import {
43
+ createAleoClient,
44
+ createBridgeClient,
45
+ createEvmClient,
46
+ createSolanaClient,
47
+ evmHttp,
48
+ evmProvider,
49
+ solanaHttp,
50
+ solanaWallet,
51
+ } from '@provablehq/aleo-bridge-sdk'
52
+
53
+ const bridge = createBridgeClient({
54
+ environment: 'mainnet',
55
+ clients: {
56
+ ethereum: createEvmClient({
57
+ transport: evmHttp(ethereumRpcUrl),
58
+ account: evmProvider(window.ethereum),
59
+ }),
60
+ solana: createSolanaClient({
61
+ transport: solanaHttp(solanaRpcUrl),
62
+ account: solanaWallet({
63
+ wallet,
64
+ account: wallet.accounts[0],
65
+ chain: 'solana:mainnet',
66
+ }),
67
+ }),
68
+ aleo: createAleoClient({
69
+ publicClient: aleoPublicClient,
70
+ account: aleoWalletClient,
71
+ }),
72
+ },
73
+ })
74
+ ```
75
+
76
+ An EIP-1193 provider, such as `window.ethereum`, can supply both EVM reads and
77
+ wallet requests when `transport` is omitted. A separate transport keeps public
78
+ reads independent from the wallet provider. Solana always requires a public
79
+ transport because Wallet Standard accounts authorize transactions but do not
80
+ provide general RPC access.
10
81
 
11
- ## Current API
82
+ An existing viem wallet client can be passed as
83
+ `createEvmClient({ walletClient })`. Add `publicClient` when reads and receipt
84
+ polling should use a different viem client.
85
+
86
+ ## Create a local-key client
87
+
88
+ A bot or server can use local EVM and Solana keys through the supplied account
89
+ adapters. An Aleo local account comes from `@provablehq/veil-aleo-sdk`, which
90
+ supports delegated or local proving.
12
91
 
13
92
  ```ts
14
- import { createBridgeClient } from '@provablehq/aleo-bridge-sdk'
93
+ import {
94
+ createAleoClient,
95
+ createBridgeClient,
96
+ createEvmClient,
97
+ createSolanaClient,
98
+ evmHttp,
99
+ evmPrivateKey,
100
+ solanaHttp,
101
+ solanaKeyPair,
102
+ } from '@provablehq/aleo-bridge-sdk'
103
+ import { loadNetwork } from '@provablehq/veil-aleo-sdk'
104
+
105
+ const aleoNetwork = await loadNetwork('mainnet')
106
+ const {
107
+ publicClient: aleoPublicClient,
108
+ walletClient: aleoWalletClient,
109
+ } = aleoNetwork.createAleoClient({
110
+ privateKey: aleoPrivateKey,
111
+ provingMode: 'delegated',
112
+ })
113
+
114
+ const bridge = createBridgeClient({
115
+ environment: 'mainnet',
116
+ clients: {
117
+ ethereum: createEvmClient({
118
+ transport: evmHttp(ethereumRpcUrl),
119
+ account: evmPrivateKey(evmPrivateKey),
120
+ }),
121
+ solana: createSolanaClient({
122
+ transport: solanaHttp(solanaRpcUrl),
123
+ account: solanaKeyPair(solanaSecretKeyBytes),
124
+ }),
125
+ aleo: createAleoClient({
126
+ publicClient: aleoPublicClient,
127
+ account: aleoWalletClient,
128
+ }),
129
+ },
130
+ })
131
+ ```
15
132
 
16
- const bridge = createBridgeClient({ environment: 'mainnet' })
133
+ Local EVM and Solana accounts sign inside the caller's process and broadcast
134
+ through their configured transports. The bridge client never receives the raw
135
+ key after the account adapter is created.
17
136
 
18
- const routes = bridge.getRoutes({
19
- protocol: 'xreserve',
137
+ ## Find supported assets and routes
138
+
139
+ The registry is the reviewed catalog bundled with the package. Reading it does
140
+ not contact a network or request a wallet signature.
141
+
142
+ ```ts
143
+ const assets = bridge.registry.getAssets({
144
+ environment: bridge.environment,
145
+ chainId: 'aleo',
146
+ })
147
+
148
+ const routes = bridge.registry.getRoutes({
149
+ environment: bridge.environment,
20
150
  sourceChainId: 'ethereum',
21
151
  destinationChainId: 'aleo',
22
152
  })
153
+ ```
23
154
 
24
- const plan = bridge.prepareTransfer({
25
- routeId: routes[0]!.id,
26
- amount: '25',
27
- recipient: aleoAddress,
28
- })
155
+ Applications select assets by chain and asset names. They do not construct
156
+ encoded route strings or copy contract addresses into transfer requests.
157
+ `getRoutes` can also return `metadata-required` entries; check `availability`
158
+ before presenting a route as executable.
29
159
 
30
- plan.steps
31
- // approve → deposit → wait-attestation → mint
32
- ```
160
+ ## Move an asset across chains
33
161
 
34
- `prepareTransfer` is pure and local. It validates the route, amount precision,
35
- and recipient format, then identifies every execution step and the first
36
- irreversible operation. It does not query live fees, sign, submit, or move
37
- funds.
162
+ Every transfer follows the same caller lifecycle:
38
163
 
39
- ## Ethereum xReserve execution
164
+ 1. `quote` checks that the requested transfer is supported and reports current
165
+ costs that can be known before submission.
166
+ 2. `execute` asks the source wallet to authorize the required source-chain
167
+ transactions.
168
+ 3. `wait` follows the submitted transfer until it finishes, fails, or requires
169
+ another wallet authorization.
170
+ 4. `resume` or `complete` runs only when `progress.next` requests that action.
40
171
 
41
- The xReserve action derives the Aleo wire recipient and 65-byte hook from the
42
- plan. Select `public`, `record`, or `private`; the deprecated
43
- `privateRecipient: true` option remains an alias for `mintMode: 'private'`.
172
+ ### 1. Quote the transfer
44
173
 
45
- ```ts
46
- const bridge = createBridgeClient({
47
- environment: 'testnet',
48
- executors: { evm: injectedProvider },
49
- xReserveHttpTransport: (url, init) => fetch(url, init),
50
- })
174
+ The caller supplies the source asset, destination asset, amount, recipient, and
175
+ optional provider. The result reports route-specific fees, balance or approval
176
+ requirements where available, and the plan that must be passed to execution.
177
+ Quoting can read networks and providers, but it does not request a signature or
178
+ move funds.
51
179
 
52
- const plan = bridge.prepareTransfer({
53
- routeId: 'xreserve:sepolia/usdc->aleo-testnet/usdcx',
54
- amount: '25',
180
+ ```ts
181
+ const quote = await bridge.quote({
182
+ source: { chain: 'ethereum', asset: 'wbtc' },
183
+ destination: { chain: 'aleo', asset: 'wbtc' },
184
+ bridgeProtocol: 'hyperlane',
185
+ amount: '0.001',
186
+ sender: ethereumAddress,
55
187
  recipient: aleoAddress,
56
- sender: connectedEthereumAccount,
57
- mintMode: 'private',
58
- privateMintSecretNonce: '7scalar', // Optional; defaults to 0scalar.
59
188
  })
60
189
 
61
- const quote = await bridge.quoteEvmXReserveTransfer({ plan })
62
- const execution = await bridge.executeEvmXReserveTransfer({ plan })
63
- const attestation = await bridge.getXReserveAttestation({
64
- routeId: plan.route.id,
65
- messageHash: execution.receipt.id as `0x${string}`,
66
- })
190
+ if (quote.kind !== 'evm-hyperlane') {
191
+ throw new Error(`Unexpected quote kind: ${quote.kind}`)
192
+ }
193
+
194
+ console.log(quote.amountAtomic)
195
+ console.log(quote.nativeFeeAtomic)
67
196
  ```
68
197
 
69
- Execution reads USDC balance and allowance, submits an exact-amount approval
70
- when needed, waits for confirmation, and calls the nonpayable
71
- `depositToRemote`. It then validates `DepositedToRemote`, derives Circle's
72
- deposit nonce, builds the canonical 305-byte payload, and returns
73
- `ATTESTATION_PENDING` with the message hash and resumable protocol state.
198
+ `quote.plan` identifies the exact route, amount, recipient, and reviewed
199
+ deployment that produced the quote. Keep this value unchanged for execution.
74
200
 
75
- Private mode lazily loads the optional `@provablehq/sdk` peer dependency. It
76
- commits the intended recipient with BHP256 and directs the xReserve deposit to
77
- `shielded_usdcx_wrapper.aleo`. Public and record modes do not load Aleo WASM.
78
- Circle attestation HTTP access is injected so browser, Node, and React Native
79
- applications can supply their own fetch-compatible transport.
201
+ ### 2. Authorize the source transfer
80
202
 
81
- Public and record USDCx destination mints are protocol-driven. Only private
82
- USDCx minting requires a second user transaction on Aleo. Once Circle returns a
83
- completed attestation, submit the wrapper call through a Veil wallet client:
203
+ Execution may request more than one wallet transaction. An ERC-20 route can
204
+ require an approval before its bridge deposit. The result contains the latest
205
+ receipt and every transaction identifier already submitted.
84
206
 
85
207
  ```ts
86
- const bridge = createBridgeClient({
87
- environment: 'testnet',
88
- executors: {
89
- evm: injectedEvmProvider,
90
- aleo: aleoWalletClient,
208
+ const execution = await bridge.execute({
209
+ plan: quote.plan,
210
+ onCheckpoint(checkpoint) {
211
+ saveCheckpoint(checkpoint)
91
212
  },
92
- xReserveHttpTransport: (url, init) => fetch(url, init),
93
213
  })
214
+ ```
215
+
216
+ Once a source transaction has been submitted, do not call `execute` again for
217
+ the same transfer. Use the returned receipt while the application remains open,
218
+ or recover from the latest checkpoint after an interruption.
94
219
 
95
- const mint = await bridge.executeXReservePrivateMint({
96
- plan,
97
- deposit: execution.receipt,
98
- attestation,
220
+ ### 3. Follow the transfer
221
+
222
+ `wait` reads source confirmation, provider processing, and destination delivery
223
+ where the route exposes verifiable evidence. It does not request another
224
+ signature or submit a transaction.
225
+
226
+ ```ts
227
+ let progress = await bridge.wait({
228
+ progress: {
229
+ next: 'wait',
230
+ plan: quote.plan,
231
+ receipt: execution.receipt,
232
+ },
99
233
  })
100
234
  ```
101
235
 
102
- This calls `shielded_usdcx_wrapper.aleo/private_mint` with the 305-byte payload,
103
- 65-byte Circle signature, 32-byte message hash, the plan's secret scalar nonce,
104
- and intended Aleo recipient. The wrapper reproduces the recipient commitment, mints publicly to
105
- its own program address, and transfers the amount to the recipient as a record.
236
+ The `next` field is the only value an application needs to select the next
237
+ lifecycle action:
106
238
 
107
- ## Ethereum Hyperlane execution
239
+ | `progress.next` | Caller action |
240
+ | --- | --- |
241
+ | `done` | Show completion. No further wallet action is required. |
242
+ | `failed` | Show the reported failure. Do not repeat a transaction that already succeeded. |
243
+ | `wait` | Call `wait` again when polling stopped at an application-selected status. |
244
+ | `resume` | Ask the source wallet to submit the remaining source operation. |
245
+ | `complete` | Ask the Aleo recipient to authorize a private USDCx mint. |
108
246
 
109
- Pass an EIP-1193-compatible provider from MetaMask, Phantom, or another injected
110
- wallet. The bridge never receives the wallet's private key.
247
+ `resume` is used when work such as an ERC-20 approval succeeded but the source
248
+ deposit was not submitted. It does not repeat the confirmed approval.
111
249
 
112
250
  ```ts
113
- import { createBridgeClient, type EvmBridgeExecutor } from '@provablehq/aleo-bridge-sdk'
251
+ if (progress.next === 'resume') {
252
+ const resumed = await bridge.resume({ progress })
253
+ progress = await bridge.wait({
254
+ progress: {
255
+ next: 'wait',
256
+ plan: progress.plan,
257
+ receipt: resumed.receipt,
258
+ },
259
+ })
260
+ }
261
+ ```
114
262
 
115
- const bridge = createBridgeClient({
116
- environment: 'mainnet',
117
- executors: {
118
- evm: injectedProvider as EvmBridgeExecutor,
119
- },
120
- })
263
+ `complete` applies only to an Ethereum USDC deposit that selected a private
264
+ USDCx mint. Circle first attests the deposit. The Aleo recipient then authorizes
265
+ one destination transaction that creates the private record.
121
266
 
122
- const plan = bridge.prepareTransfer({
123
- routeId: 'hyperlane:ethereum/wbtc->aleo/wbtc',
124
- amount: '0.001',
125
- recipient: aleoAddress,
126
- sender: connectedEthereumAccount,
127
- })
267
+ ```ts
268
+ if (progress.next === 'complete') {
269
+ const destination = await bridge.complete({
270
+ progress,
271
+ privateMintSecretNonce,
272
+ })
273
+ progress = await bridge.wait({
274
+ progress: {
275
+ next: 'wait',
276
+ plan: progress.plan,
277
+ receipt: destination.receipt,
278
+ },
279
+ })
280
+ }
281
+ ```
128
282
 
129
- const quote = await bridge.quoteEvmHyperlaneTransfer({
130
- plan,
131
- recipientBytes32: encodedAleoRecipient,
132
- })
283
+ ## Recover after an interruption
133
284
 
134
- const execution = await bridge.executeEvmHyperlaneTransfer({
135
- plan,
136
- recipientBytes32: encodedAleoRecipient,
285
+ A checkpoint contains the public transfer intent and transaction identifiers
286
+ needed to find the transfer again. It excludes private keys, Aleo record
287
+ plaintext, proofs, and private-mint secret nonces.
288
+
289
+ The SDK calls `onCheckpoint` at supported submission boundaries. The callback
290
+ does not imply a storage system. A browser can use IndexedDB or local storage;
291
+ a server can use a database or file. Applications that stay open can keep the
292
+ receipt in memory and omit the callback.
293
+
294
+ ```ts
295
+ await bridge.execute({
296
+ plan: quote.plan,
297
+ onCheckpoint(checkpoint) {
298
+ localStorage.setItem('bridge-checkpoint', JSON.stringify(checkpoint))
299
+ },
137
300
  })
138
301
  ```
139
302
 
140
- `quoteEvmHyperlaneTransfer` calls the route's `quoteTransferRemote` function and
141
- returns atomic native payment and token-allowance requirements. It does not sign
142
- or submit a transaction.
303
+ After a restart, `recover` reconstructs the plan and checks existing network or
304
+ provider state. It never signs, submits, or repeats a transaction.
143
305
 
144
- `executeEvmHyperlaneTransfer` requotes immediately before submission. For WBTC
145
- and USDT it reads the current ERC-20 allowance, submits `approve` only when the
146
- allowance is insufficient, waits for confirmation, and then submits
147
- `transferRemote`. USDT's non-zero allowance is reset to zero before setting a
148
- new value. Native ETH routes skip approval and send the quoted total as
149
- `msg.value`.
306
+ ```ts
307
+ const checkpoint = JSON.parse(localStorage.getItem('bridge-checkpoint')!)
308
+ let progress = await bridge.recover({ checkpoint })
309
+
310
+ if (progress.next === 'wait') {
311
+ progress = await bridge.wait({ progress })
312
+ }
313
+ ```
150
314
 
151
- The wire recipient is currently explicit. `recipientBytes32` MUST be the exact
152
- 32-byte Aleo recipient encoding accepted by the enrolled Hyperlane router; it is
153
- validated for width but is not derived from `plan.recipient` yet.
315
+ The application then handles `progress.next` by the same table above. A private
316
+ mint nonce must be stored separately because it is intentionally absent from
317
+ the checkpoint.
154
318
 
155
- Receipt timeouts return `SOURCE_APPROVAL_PENDING` or `SOURCE_CONFIRMING` with the
156
- submitted transaction identifiers. A timeout does not report the transaction as
157
- failed. A confirmed dispatch returns `DELIVERY_PENDING` and includes the
158
- Hyperlane message id when the Mailbox `DispatchId` event is present.
319
+ ## Use private assets on Aleo
159
320
 
160
- Inbound Hyperlane Aleo minting is performed by the Hyperlane relayer. The user
161
- submits only the source-chain approval and dispatch transactions; no Aleo wallet
162
- transaction is requested for Hyperlane delivery.
321
+ Hyperlane routes mint wrapped assets into public Aleo balances and spend public
322
+ balances when bridging out of Aleo. Shielding and unshielding let the same asset
323
+ move between that public balance and a private Aleo record.
163
324
 
164
- ## Aleo USDCx burns
325
+ ### Unshield before bridging out through Hyperlane
165
326
 
166
- USDCx burns submit one Aleo transaction. The Aleo-operated burn attestation
167
- service observes accepted burns and forwards them to Circle; the bridge client
168
- does not submit a second attestation or Ethereum withdrawal transaction.
327
+ An outbound Hyperlane transfer cannot spend a private record directly. Convert
328
+ the amount into the account's public balance before quoting and executing the
329
+ bridge transfer.
169
330
 
170
331
  ```ts
171
- const plan = bridge.prepareTransfer({
172
- routeId: 'xreserve:aleo/usdcx->ethereum/usdc',
173
- amount: '25',
174
- recipient: ethereumRecipient,
332
+ const conversion = await bridge.unshield({
333
+ asset: { chain: 'aleo', asset: 'sol' },
334
+ amount: '0.01',
175
335
  })
176
336
 
177
- const burn = await bridge.executeXReserveBurn({
178
- plan,
179
- userRecord,
180
- merkleProof,
181
- // Default: private
182
- })
337
+ console.log(conversion.transactionId)
183
338
  ```
184
339
 
185
- Private burning is the default. Three transition modes remain available:
340
+ The Aleo wallet selects a sufficient record when it supports wallet-side record
341
+ requests. A local-key caller must supply the encoded record because a local
342
+ account cannot resolve a wallet-side record request. Wait for the Aleo
343
+ transaction to be accepted before spending the resulting public balance.
186
344
 
187
- - `private` calls `shielded_usdcx_wrapper.aleo/private_burn` and requires a
188
- USDCx `Token` record input plus an encoded `[MerkleProof; 2]` literal.
189
- - `public` calls `burn_public` for public or program-owned balances.
190
- - `public-as-signer` calls `burn_public_as_signer` when the public balance must
191
- be proven to belong to the EOA signer.
345
+ Private USDCx can be burned directly by the xReserve private withdrawal flow.
346
+ It does not need to be unshielded first.
192
347
 
193
- Ethereum's native destination domain is pinned to `0u32`; its address is
194
- left-padded to `[u8; 32]`. ARC domain `26u32` is recorded in the registry but is
195
- not selectable through the Ethereum route. Pause, freeze-list, and mutable
196
- minimum/maximum burn checks execute atomically in the deployed Aleo program.
348
+ ### Shield an asset for private use on Aleo
197
349
 
198
- ## Registry
199
-
200
- `DEFAULT_BRIDGE_REGISTRY` is a versioned snapshot of chains, chain-specific
201
- assets, and directional routes. Applications can pass a reviewed replacement:
350
+ After a Hyperlane transfer arrives, its Aleo balance is public. Convert any
351
+ amount that should be held or spent privately into a record owned by the Aleo
352
+ account.
202
353
 
203
354
  ```ts
204
- const bridge = createBridgeClient({
205
- environment: 'testnet',
206
- registry: companyReviewedRegistry,
355
+ const conversion = await bridge.shield({
356
+ asset: { chain: 'aleo', asset: 'sol' },
357
+ amount: '0.01',
207
358
  })
359
+
360
+ console.log(conversion.transactionId)
208
361
  ```
209
362
 
210
- xReserve entries include Circle's published Ethereum/Sepolia USDC contracts,
211
- Aleo domain, and USDCx program identifiers. Ethereum-to-Aleo ETH, WBTC, and USDT
212
- routes pin router, domain, ISM, token, Mailbox, and gas-payment metadata to
213
- Hyperlane Registry commit `2621c16f2db1ccb46643265c110dac5ca2c7c51a` and are
214
- active. Reverse and other Hyperlane routes remain `metadata-required` until
215
- their deployment metadata is complete and reviewed.
216
-
217
- ### Aleo-origin Hyperlane placeholders
218
-
219
- The Aleo-origin ETH, WBTC, USDT, SOL, and USAD routes expose the complete
220
- `transfer_remote` call shape for these programs:
221
-
222
- - `hyp_warp_token_eth_v2.aleo`
223
- - `hyp_warp_token_wbtc_v2.aleo`
224
- - `hyp_warp_token_usdt_v2.aleo`
225
- - `hyp_warp_token_sol_v2.aleo`
226
- - `hyp_warp_token_usad_v2.aleo`
227
-
228
- **These routes contain dummy development values and are not executable.** They
229
- remain `metadata-required`, carry `aleoPlaceholderConfiguration: true`, and
230
- `executeAleoHyperlaneTransferRemote` throws before calling the wallet. Use
231
- `buildAleoHyperlaneTransferRemoteCall` only to inspect and integrate the ABI
232
- until the configuration below has been reviewed and replaced.
233
-
234
- Except for the verified ETH, WBTC, USDT, and SOL route data described below,
235
- the current dummy values are:
236
-
237
- - `token_type`: `0u8`; `token_id`: `0field`
238
- - `token_owner`, `ism`, `hook`, and all four allowance spenders: the same
239
- development-only Aleo address
240
- - remote-router recipient: 32 zero bytes; remote-router gas: `0u128`
241
- - destination recipient limbs are derived from the Ethereum or Solana address
242
- in the transfer plan
243
- - all four credit allowance amounts: `0u64`
244
-
245
- The WBTC route is partially populated from mainnet
246
- [`hyp_warp_token_wbtc_v2.aleo`](https://explorer.provable.com/program/hyp_warp_token_wbtc_v2.aleo),
247
- edition `0`. Its `app_metadata[true]` token type, owner, ISM, hook, token ID,
248
- and `8u8` local/remote decimals are verified and are not placeholders. Its
249
- first hook allowance amount remains unresolved, so the route is still
250
- non-executable.
251
-
252
- The WBTC Ethereum remote router is also verified: domain `1u32`, recipient
253
- `0x20CDC85778b732073F7EecEF3DF25c0d310f8772` left-padded to `[u8; 32]`, and
254
- gas `68000u128`. `transfer_remote_as_signer` is selectable with
255
- `mode: 'signer'`. Its allowance spender positions and three unused zero amounts
256
- match the reviewed call shape. The first hook allowance amount remains dynamic;
257
- the observed `9138947u64` applies only to the sample transaction and is not
258
- stored as a route-wide cap.
259
-
260
- The ETH route is populated from current mainnet app metadata and the reviewed
261
- [`transfer_remote_as_signer` transaction](https://explorer.provable.com/transaction/at1vu0yckkms887zkl3qz7plnncd56jtf5zeal4uj2808upsjkusy8q7yp9v8).
262
- Its Ethereum router is `0x38D447694f5c1f773ae3132cf93bF30B7Ec1Fa5A`,
263
- left-padded to `[u8; 32]`, with domain `1u32` and gas `44000u128`. The
264
- transaction's `8174147u64` first allowance is an observed dispatch quote and is
265
- not stored as a route-wide cap. As with WBTC, only the first hook allowance
266
- amount remains unresolved. Ethereum recipient limbs are derived from
267
- `plan.recipient`.
268
-
269
- The USDT route uses current edition `1` app metadata and the verified Ethereum
270
- remote router at domain `1u32`: `0x3C2064D78e4578E8F936E3db42aEF044E33FBF31`
271
- with gas `68000u128`. The reviewed signer transaction targets BSC domain `56`,
272
- so it validates the shared allowance layout but is not used as the Ethereum
273
- router source. Its `1994463u64` first allowance is transaction-specific. The
274
- official Hyperlane route config records Aleo and Ethereum USDT as 6-decimal
275
- assets with a `1000000000000` scale; the Aleo program's app metadata must still
276
- be passed exactly as `local_decimals: 6u8, remote_decimals: 18u8`. The builder
277
- therefore reads these contract metadata decimals instead of inferring both from
278
- the endpoint assets.
279
-
280
- The SOL route uses verified edition `0` app metadata with 9 local and remote
281
- decimals. Its Solana destination is Hyperlane domain `1399811149u32`, router
282
- `8YGT2pZwyZe94qBpGzWfY2TMEVcwaQ1bXAE7YAgpUaM7`, and gas `300000u128`. The
283
- reviewed signer transition confirms the shared allowance layout; its
284
- `7661056u64` first allowance is transaction-specific and is not stored as a
285
- route-wide value. The Aleo SOL asset locator now points to the v2 warp program
286
- and token identifier from the pinned Hyperlane route configuration.
287
-
288
- All Aleo Warp Routes share the verified mainnet
289
- [`hyp_mailbox.aleo`](https://explorer.provable.com/program/hyp_mailbox.aleo)
290
- mailbox configuration, edition `0`. The `transfer_remote` input now uses its
291
- `default_hook` and `required_hook`. The registry also records the local domain,
292
- default ISM, dispatch proxy, owner, and the nonce/process count observed during
293
- the 2026-08-17 review. The nonce and process count are mutable observations and
294
- are not transaction inputs.
295
-
296
- Before enabling submission, replace and verify every field still reported by
297
- `placeholderFields` for that route. Implement the dynamic hook credit quote for
298
- ETH, WBTC, USDT, and SOL. Then remove `aleoPlaceholderConfiguration` and change
299
- the route availability to `active` in a reviewed registry snapshot.
300
-
301
- ## Exports
302
-
303
- - `createBridgeClient`
304
- - `getAssets` and `getRoutes`
305
- - `prepareTransfer`
306
- - `quoteEvmHyperlaneTransfer` and `executeEvmHyperlaneTransfer`
307
- - `quoteEvmXReserveTransfer`, `executeEvmXReserveTransfer`, and `getXReserveAttestation`
308
- - `executeXReservePrivateMint`
309
- - `buildXReserveBurnCall` and `executeXReserveBurn`
310
- - `buildAleoHyperlaneTransferRemoteCall` and `executeAleoHyperlaneTransferRemote`
311
- - Aleo address, xReserve hook, nonce, payload, and message-hash utilities
312
- - Ethereum and Solana Hyperlane recipient serialization for Aleo-origin transfers
313
- - `DEFAULT_BRIDGE_REGISTRY` and `validateBridgeRegistry`
314
- - Protocol-neutral asset, route, plan, fee, step, status, and receipt types
315
- - `createBridgeAgentTools` from `/agent`
316
- - `createBridgeMcpServer` from `/mcp`
317
-
318
- The agent and MCP surfaces expose discovery and planning only. They do not expose
319
- fund-moving wallet actions.
320
-
321
- ## Next implementation phases
322
-
323
- 1. Add protocol delivery tracking for relayer-driven xReserve and Hyperlane mints.
324
- 2. Replace and review the Aleo-origin Hyperlane placeholders, then add destination confirmation.
325
- 3. Add injected Solana execution and gated protocol testnets.
363
+ Shielding and unshielding each submit an Aleo transaction and incur an Aleo
364
+ transaction fee. They are separate from bridge delivery. A failed privacy
365
+ conversion does not repeat or reverse the completed cross-chain transfer.
366
+
367
+ The default registry supports these conversions for wrapped ETH, WBTC, USDT,
368
+ and SOL through their ARC-20 programs, and for USDCx through its ARC-22
369
+ transfers. The current USDCx default uses the empty freeze-list proof. Supply a
370
+ current proof after the deployed freeze-list tree is populated.
371
+
372
+ ## Complete examples
373
+
374
+ The [bridge tutorial](../../examples/bridge/README.md) explains configuration,
375
+ safe read-only runs, mainnet authorization, checkpoints, and each provider's
376
+ observable completion boundary.
377
+
378
+ | Transfer | Example |
379
+ | --- | --- |
380
+ | Ethereum ETH → Aleo ETH | [`eth-to-aleo.ts`](../../examples/bridge/eth-to-aleo.ts) |
381
+ | Ethereum WBTC → Aleo WBTC | [`wbtc-to-aleo.ts`](../../examples/bridge/wbtc-to-aleo.ts) |
382
+ | Aleo ETH → Ethereum ETH | [`eth-to-ethereum.ts`](../../examples/bridge/eth-to-ethereum.ts) |
383
+ | Aleo WBTC → Ethereum WBTC | [`wbtc-to-ethereum.ts`](../../examples/bridge/wbtc-to-ethereum.ts) |
384
+ | Aleo USDT → Ethereum USDT | [`usdt-to-ethereum.ts`](../../examples/bridge/usdt-to-ethereum.ts) |
385
+ | Solana SOL → Aleo SOL | [`sol-to-aleo.ts`](../../examples/bridge/sol-to-aleo.ts) |
386
+ | Aleo SOL → Solana SOL | [`sol-to-solana.ts`](../../examples/bridge/sol-to-solana.ts) |
387
+ | Ethereum USDC → Aleo USDCx | [`usdc-to-usdcx.ts`](../../examples/bridge/usdc-to-usdcx.ts) |
388
+ | Aleo USDCx → Ethereum USDC | [`usdcx-to-usdc.ts`](../../examples/bridge/usdcx-to-usdc.ts) |
389
+
390
+ Each script quotes mainnet state and exits without submitting by default. The
391
+ script prints the exact acknowledgement required to authorize real funds.