@provablehq/aleo-bridge-sdk 0.9.0-rc.1 → 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,334 +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. The reviewed Aleo-origin ETH, WBTC, USDT, and SOL withdrawal routes are
215
- also active (reviewed 2026-08-26); other Hyperlane routes remain
216
- `metadata-required` until their deployment metadata is complete and reviewed.
217
-
218
- ### Aleo-origin Hyperlane withdrawals
219
-
220
- The Aleo-origin ETH, WBTC, USDT, SOL, and USAD routes expose the complete
221
- `transfer_remote` call shape for these programs:
222
-
223
- - `hyp_warp_token_eth_v2.aleo`
224
- - `hyp_warp_token_wbtc_v2.aleo`
225
- - `hyp_warp_token_usdt_v2.aleo`
226
- - `hyp_warp_token_sol_v2.aleo`
227
- - `hyp_warp_token_usad_v2.aleo`
228
-
229
- The ETH, WBTC, USDT, and SOL routes are active. Every static field is reviewed;
230
- the one dynamic value — the interchain gas paymaster's hook payment — is read
231
- live from `hyp_hook_manager.aleo/destination_gas_configs` by
232
- `quoteAleoHyperlaneGasPayment` and passed to execution as
233
- `gasPaymentMicrocredits`. The on-chain hook asserts the payment exactly equals
234
- its own recomputed quote, so a stale quote aborts at finalization without
235
- moving funds, and `executeAleoHyperlaneTransferRemote` throws before wallet
236
- access when no quote is supplied.
237
-
238
- **The USAD route contains dummy development values and is not executable.** It
239
- remains `metadata-required`, carries `aleoPlaceholderConfiguration: true`, and
240
- `executeAleoHyperlaneTransferRemote` throws before calling the wallet. Use
241
- `buildAleoHyperlaneTransferRemoteCall` only to inspect and integrate its ABI
242
- until the configuration has been reviewed and replaced.
243
-
244
- For USAD, the current dummy values are:
245
-
246
- - `token_type`: `0u8`; `token_id`: `0field`
247
- - `token_owner`, `ism`, `hook`, and all four allowance spenders: the same
248
- development-only Aleo address
249
- - remote-router recipient: 32 zero bytes; remote-router gas: `0u128`
250
- - destination recipient limbs are derived from the Ethereum or Solana address
251
- in the transfer plan
252
- - all four credit allowance amounts: `0u64`
253
-
254
- The WBTC route is partially populated from mainnet
255
- [`hyp_warp_token_wbtc_v2.aleo`](https://explorer.provable.com/program/hyp_warp_token_wbtc_v2.aleo),
256
- edition `0`. Its `app_metadata[true]` token type, owner, ISM, hook, token ID,
257
- and `8u8` local/remote decimals are verified and are not placeholders. Its
258
- first hook allowance amount is the live gas quote supplied at execution time.
259
-
260
- The WBTC Ethereum remote router is also verified: domain `1u32`, recipient
261
- `0x20CDC85778b732073F7EecEF3DF25c0d310f8772` left-padded to `[u8; 32]`, and
262
- gas `68000u128`. `transfer_remote_as_signer` is selectable with
263
- `mode: 'signer'`. Its allowance spender positions and three unused zero amounts
264
- match the reviewed call shape. The first hook allowance amount remains dynamic;
265
- the observed `9138947u64` applies only to the sample transaction and is not
266
- stored as a route-wide cap.
267
-
268
- The ETH route is populated from current mainnet app metadata and the reviewed
269
- [`transfer_remote_as_signer` transaction](https://explorer.provable.com/transaction/at1vu0yckkms887zkl3qz7plnncd56jtf5zeal4uj2808upsjkusy8q7yp9v8).
270
- Its Ethereum router is `0x38D447694f5c1f773ae3132cf93bF30B7Ec1Fa5A`,
271
- left-padded to `[u8; 32]`, with domain `1u32` and gas `44000u128`. The
272
- transaction's `8174147u64` first allowance is an observed dispatch quote and is
273
- not stored as a route-wide cap. As with WBTC, the first hook allowance amount
274
- is quoted live at execution time. Ethereum recipient limbs are derived from
275
- `plan.recipient`.
276
-
277
- The USDT route uses current edition `1` app metadata and the verified Ethereum
278
- remote router at domain `1u32`: `0x3C2064D78e4578E8F936E3db42aEF044E33FBF31`
279
- with gas `68000u128`. The reviewed signer transaction targets BSC domain `56`,
280
- so it validates the shared allowance layout but is not used as the Ethereum
281
- router source. Its `1994463u64` first allowance is transaction-specific. The
282
- official Hyperlane route config records Aleo and Ethereum USDT as 6-decimal
283
- assets with a `1000000000000` scale; the Aleo program's app metadata must still
284
- be passed exactly as `local_decimals: 6u8, remote_decimals: 18u8`. The builder
285
- therefore reads these contract metadata decimals instead of inferring both from
286
- the endpoint assets.
287
-
288
- The SOL route uses verified edition `0` app metadata with 9 local and remote
289
- decimals. Its Solana destination is Hyperlane domain `1399811149u32`, router
290
- `8YGT2pZwyZe94qBpGzWfY2TMEVcwaQ1bXAE7YAgpUaM7`, and gas `300000u128`. The
291
- reviewed signer transition confirms the shared allowance layout; its
292
- `7661056u64` first allowance is transaction-specific and is not stored as a
293
- route-wide value. The Aleo SOL asset locator now points to the v2 warp program
294
- and token identifier from the pinned Hyperlane route configuration.
295
-
296
- All Aleo Warp Routes share the verified mainnet
297
- [`hyp_mailbox.aleo`](https://explorer.provable.com/program/hyp_mailbox.aleo)
298
- mailbox configuration, edition `0`. The `transfer_remote` input now uses its
299
- `default_hook` and `required_hook`. The registry also records the local domain,
300
- default ISM, dispatch proxy, owner, and the nonce/process count observed during
301
- the 2026-08-17 review. The nonce and process count are mutable observations and
302
- are not transaction inputs.
303
-
304
- Before enabling USAD submission, replace and verify every field still reported
305
- by `placeholderFields` for that route. Then remove
306
- `aleoPlaceholderConfiguration` and change the route availability to `active` in
307
- a reviewed registry snapshot, as the 2026-08-26 review did for ETH, WBTC, USDT,
308
- and SOL.
309
-
310
- ## Exports
311
-
312
- - `createBridgeClient`
313
- - `getAssets` and `getRoutes`
314
- - `prepareTransfer`
315
- - `quoteEvmHyperlaneTransfer` and `executeEvmHyperlaneTransfer`
316
- - `quoteEvmXReserveTransfer`, `executeEvmXReserveTransfer`, and `getXReserveAttestation`
317
- - `executeXReservePrivateMint`
318
- - `buildXReserveBurnCall` and `executeXReserveBurn`
319
- - `buildAleoHyperlaneTransferRemoteCall` and `executeAleoHyperlaneTransferRemote`
320
- - Aleo address, xReserve hook, nonce, payload, and message-hash utilities
321
- - Ethereum and Solana Hyperlane recipient serialization for Aleo-origin transfers
322
- - `DEFAULT_BRIDGE_REGISTRY` and `validateBridgeRegistry`
323
- - Protocol-neutral asset, route, plan, fee, step, status, and receipt types
324
- - `createBridgeAgentTools` from `/agent`
325
- - `createBridgeMcpServer` from `/mcp`
326
-
327
- The agent and MCP surfaces expose discovery and planning only. They do not expose
328
- fund-moving wallet actions.
329
-
330
- ## Next implementation phases
331
-
332
- 1. Add protocol delivery tracking for relayer-driven xReserve and Hyperlane mints.
333
- 2. Replace and review the Aleo-origin USAD placeholders, then add destination confirmation for withdrawals.
334
- 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.