aleo-bridge-sdk 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. aleo_bridge_sdk-0.5.1/.gitignore +5 -0
  2. aleo_bridge_sdk-0.5.1/AGENTS.md +380 -0
  3. aleo_bridge_sdk-0.5.1/PKG-INFO +799 -0
  4. aleo_bridge_sdk-0.5.1/README.md +779 -0
  5. aleo_bridge_sdk-0.5.1/codegen/gen_context.py +248 -0
  6. aleo_bridge_sdk-0.5.1/examples/README.md +219 -0
  7. aleo_bridge_sdk-0.5.1/examples/__init__.py +1 -0
  8. aleo_bridge_sdk-0.5.1/examples/_arguments.py +61 -0
  9. aleo_bridge_sdk-0.5.1/examples/bridge_sol.py +93 -0
  10. aleo_bridge_sdk-0.5.1/examples/bridge_sol_to_solana.py +97 -0
  11. aleo_bridge_sdk-0.5.1/examples/bridge_usdc_private_balance.py +101 -0
  12. aleo_bridge_sdk-0.5.1/examples/bridge_usdc_private_recipient.py +126 -0
  13. aleo_bridge_sdk-0.5.1/examples/bridge_usdcx_to_ethereum.py +99 -0
  14. aleo_bridge_sdk-0.5.1/examples/bridge_wbtc.py +100 -0
  15. aleo_bridge_sdk-0.5.1/examples/bridge_wbtc_to_ethereum.py +97 -0
  16. aleo_bridge_sdk-0.5.1/examples/checkpoints/2026-09-25_001_solana-sol_to_aleo-sol_676.2.json +27 -0
  17. aleo_bridge_sdk-0.5.1/examples/quote_transfer.py +55 -0
  18. aleo_bridge_sdk-0.5.1/examples/recover_from_journal.py +117 -0
  19. aleo_bridge_sdk-0.5.1/examples/recover_without_files.py +81 -0
  20. aleo_bridge_sdk-0.5.1/examples/shield_assets.py +65 -0
  21. aleo_bridge_sdk-0.5.1/pyproject.toml +44 -0
  22. aleo_bridge_sdk-0.5.1/pyrightconfig.json +7 -0
  23. aleo_bridge_sdk-0.5.1/pytest.ini +7 -0
  24. aleo_bridge_sdk-0.5.1/python/aleo_bridge/AGENTS.md +380 -0
  25. aleo_bridge_sdk-0.5.1/python/aleo_bridge/__init__.py +79 -0
  26. aleo_bridge_sdk-0.5.1/python/aleo_bridge/__main__.py +36 -0
  27. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_base58.py +35 -0
  28. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_calls.py +422 -0
  29. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_evm_abi.py +60 -0
  30. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_keccak.py +57 -0
  31. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_plan.py +68 -0
  32. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_registry_data.py +386 -0
  33. aleo_bridge_sdk-0.5.1/python/aleo_bridge/_sealevel.py +388 -0
  34. aleo_bridge_sdk-0.5.1/python/aleo_bridge/agent.py +523 -0
  35. aleo_bridge_sdk-0.5.1/python/aleo_bridge/checkpoint.py +398 -0
  36. aleo_bridge_sdk-0.5.1/python/aleo_bridge/circle.py +57 -0
  37. aleo_bridge_sdk-0.5.1/python/aleo_bridge/client.py +484 -0
  38. aleo_bridge_sdk-0.5.1/python/aleo_bridge/encoding.py +289 -0
  39. aleo_bridge_sdk-0.5.1/python/aleo_bridge/errors.py +106 -0
  40. aleo_bridge_sdk-0.5.1/python/aleo_bridge/eth.py +1801 -0
  41. aleo_bridge_sdk-0.5.1/python/aleo_bridge/freezelist.py +193 -0
  42. aleo_bridge_sdk-0.5.1/python/aleo_bridge/hyperlane.py +189 -0
  43. aleo_bridge_sdk-0.5.1/python/aleo_bridge/lifecycle.py +1531 -0
  44. aleo_bridge_sdk-0.5.1/python/aleo_bridge/mcp.py +132 -0
  45. aleo_bridge_sdk-0.5.1/python/aleo_bridge/privacy.py +133 -0
  46. aleo_bridge_sdk-0.5.1/python/aleo_bridge/profile.py +107 -0
  47. aleo_bridge_sdk-0.5.1/python/aleo_bridge/registry.py +309 -0
  48. aleo_bridge_sdk-0.5.1/python/aleo_bridge/sol.py +981 -0
  49. aleo_bridge_sdk-0.5.1/python/aleo_bridge/types.py +277 -0
  50. aleo_bridge_sdk-0.5.1/python/aleo_bridge/units.py +60 -0
  51. aleo_bridge_sdk-0.5.1/python/aleo_bridge/xreserve.py +193 -0
  52. aleo_bridge_sdk-0.5.1/scripts/rehearse.py +266 -0
  53. aleo_bridge_sdk-0.5.1/tests/__init__.py +0 -0
  54. aleo_bridge_sdk-0.5.1/tests/conftest.py +263 -0
  55. aleo_bridge_sdk-0.5.1/tests/fakes/__init__.py +0 -0
  56. aleo_bridge_sdk-0.5.1/tests/fakes/fake_bridge.py +519 -0
  57. aleo_bridge_sdk-0.5.1/tests/fakes/fake_solana.py +151 -0
  58. aleo_bridge_sdk-0.5.1/tests/fakes/fake_web3.py +366 -0
  59. aleo_bridge_sdk-0.5.1/tests/fakes/sealevel_fixtures.py +52 -0
  60. aleo_bridge_sdk-0.5.1/tests/fixtures/sealevel-igp-account.json +5 -0
  61. aleo_bridge_sdk-0.5.1/tests/fixtures/sealevel-transfer-remote.json +122 -0
  62. aleo_bridge_sdk-0.5.1/tests/live/__init__.py +0 -0
  63. aleo_bridge_sdk-0.5.1/tests/live/cases.py +580 -0
  64. aleo_bridge_sdk-0.5.1/tests/live/config.py +223 -0
  65. aleo_bridge_sdk-0.5.1/tests/live/conftest.py +17 -0
  66. aleo_bridge_sdk-0.5.1/tests/live/helpers.py +436 -0
  67. aleo_bridge_sdk-0.5.1/tests/live/test_aleo_reads.py +138 -0
  68. aleo_bridge_sdk-0.5.1/tests/live/test_eth_reads.py +108 -0
  69. aleo_bridge_sdk-0.5.1/tests/live/test_eth_sepolia_leg1.py +98 -0
  70. aleo_bridge_sdk-0.5.1/tests/live/test_lifecycle_live.py +366 -0
  71. aleo_bridge_sdk-0.5.1/tests/live/test_sol_reads.py +82 -0
  72. aleo_bridge_sdk-0.5.1/tests/test_agent.py +500 -0
  73. aleo_bridge_sdk-0.5.1/tests/test_base58.py +29 -0
  74. aleo_bridge_sdk-0.5.1/tests/test_bridge_eth_wiring.py +105 -0
  75. aleo_bridge_sdk-0.5.1/tests/test_calls.py +133 -0
  76. aleo_bridge_sdk-0.5.1/tests/test_checkpoint.py +328 -0
  77. aleo_bridge_sdk-0.5.1/tests/test_checkpoint_store_home.py +16 -0
  78. aleo_bridge_sdk-0.5.1/tests/test_circle.py +92 -0
  79. aleo_bridge_sdk-0.5.1/tests/test_client.py +267 -0
  80. aleo_bridge_sdk-0.5.1/tests/test_client_lifecycle.py +216 -0
  81. aleo_bridge_sdk-0.5.1/tests/test_encoding.py +214 -0
  82. aleo_bridge_sdk-0.5.1/tests/test_eth_connection.py +305 -0
  83. aleo_bridge_sdk-0.5.1/tests/test_eth_hyperlane_execute.py +245 -0
  84. aleo_bridge_sdk-0.5.1/tests/test_eth_hyperlane_quote.py +155 -0
  85. aleo_bridge_sdk-0.5.1/tests/test_eth_recover.py +504 -0
  86. aleo_bridge_sdk-0.5.1/tests/test_eth_status.py +382 -0
  87. aleo_bridge_sdk-0.5.1/tests/test_eth_xreserve_execute.py +229 -0
  88. aleo_bridge_sdk-0.5.1/tests/test_eth_xreserve_quote.py +152 -0
  89. aleo_bridge_sdk-0.5.1/tests/test_evm_call.py +383 -0
  90. aleo_bridge_sdk-0.5.1/tests/test_examples.py +184 -0
  91. aleo_bridge_sdk-0.5.1/tests/test_execute.py +360 -0
  92. aleo_bridge_sdk-0.5.1/tests/test_freezelist.py +166 -0
  93. aleo_bridge_sdk-0.5.1/tests/test_gen_context.py +49 -0
  94. aleo_bridge_sdk-0.5.1/tests/test_get_status.py +351 -0
  95. aleo_bridge_sdk-0.5.1/tests/test_hyperlane.py +204 -0
  96. aleo_bridge_sdk-0.5.1/tests/test_import_without_web3.py +28 -0
  97. aleo_bridge_sdk-0.5.1/tests/test_keccak.py +22 -0
  98. aleo_bridge_sdk-0.5.1/tests/test_live_case_predicates.py +27 -0
  99. aleo_bridge_sdk-0.5.1/tests/test_live_drive_dropped.py +87 -0
  100. aleo_bridge_sdk-0.5.1/tests/test_live_helpers.py +1040 -0
  101. aleo_bridge_sdk-0.5.1/tests/test_mcp.py +84 -0
  102. aleo_bridge_sdk-0.5.1/tests/test_package.py +115 -0
  103. aleo_bridge_sdk-0.5.1/tests/test_prepare.py +219 -0
  104. aleo_bridge_sdk-0.5.1/tests/test_privacy.py +125 -0
  105. aleo_bridge_sdk-0.5.1/tests/test_profile.py +76 -0
  106. aleo_bridge_sdk-0.5.1/tests/test_quote.py +80 -0
  107. aleo_bridge_sdk-0.5.1/tests/test_recover.py +251 -0
  108. aleo_bridge_sdk-0.5.1/tests/test_registry.py +349 -0
  109. aleo_bridge_sdk-0.5.1/tests/test_resume_complete.py +412 -0
  110. aleo_bridge_sdk-0.5.1/tests/test_sealevel_accounts.py +105 -0
  111. aleo_bridge_sdk-0.5.1/tests/test_sealevel_igp.py +71 -0
  112. aleo_bridge_sdk-0.5.1/tests/test_sealevel_instruction.py +41 -0
  113. aleo_bridge_sdk-0.5.1/tests/test_sol_bridge.py +119 -0
  114. aleo_bridge_sdk-0.5.1/tests/test_sol_connection.py +325 -0
  115. aleo_bridge_sdk-0.5.1/tests/test_sol_quote.py +174 -0
  116. aleo_bridge_sdk-0.5.1/tests/test_sol_rpc.py +186 -0
  117. aleo_bridge_sdk-0.5.1/tests/test_sol_send.py +433 -0
  118. aleo_bridge_sdk-0.5.1/tests/test_sol_status.py +106 -0
  119. aleo_bridge_sdk-0.5.1/tests/test_types.py +125 -0
  120. aleo_bridge_sdk-0.5.1/tests/test_units.py +60 -0
  121. aleo_bridge_sdk-0.5.1/tests/test_wait.py +231 -0
  122. aleo_bridge_sdk-0.5.1/tests/test_xreserve.py +166 -0
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ *.egg-info/
@@ -0,0 +1,380 @@
1
+ # aleo-bridge — agent guide
2
+
3
+ > GENERATED from SDK docstrings by `codegen/gen_context.py` — do not
4
+ > edit by hand; edit the docstrings and regenerate.
5
+
6
+ Typed Python client that moves assets between Aleo, Ethereum and Solana
7
+ over the reviewed Hyperlane warp routes and Circle xReserve deployments
8
+ (`pip install aleo-bridge-sdk`, imports as `aleo_bridge`).
9
+ MCP alternative: `python -m aleo_bridge.mcp` exposes the same lifecycle as
10
+ tools; `aleo_bridge.agent.bridge_tools()` gives Claude-shape tool schemas.
11
+ Registry version `2026-08-31.solana-deposits.1`.
12
+
13
+ Runnable examples ship in the package: start with
14
+ `python -m aleo_bridge.examples.quote_transfer --help`.
15
+ Use `aleo_bridge.examples.<script_name>` for transfers, shielding, and recovery;
16
+ the examples directory includes a README with the available scripts.
17
+
18
+ ## Tier 1 — the lifecycle (quote → execute → wait, then resume / complete as asked)
19
+
20
+ ```python
21
+ from aleo_bridge import Bridge
22
+
23
+ bridge = Bridge.from_env() # BRIDGE_PRIVATE_KEY (+ EVM/Solana keys) from the environment
24
+ print(bridge.status()) # addresses, balances, pending transfers
25
+ quote = bridge.quote(source_chain="ethereum", source_asset="wbtc", destination_chain="aleo",
26
+ amount="0.001", recipient=bridge.aleo_address()) # or route=<id from bridge.routes()>
27
+ print(quote.fees, quote.amount_out) # show these to the user BEFORE executing
28
+ progress = bridge.execute(quote.plan) # source step; checkpoints saved to the bound store
29
+ progress = bridge.wait(progress) # stops at resume / complete / done / failed
30
+ if progress.next == "resume": progress = bridge.wait(bridge.resume(progress))
31
+ if progress.next == "complete": progress = bridge.wait(bridge.complete(progress, secret_nonce=nonce))
32
+ assert progress.next == "done", progress.error
33
+ ```
34
+
35
+ ### `from_env(**overrides: 'Any') -> "'Bridge'"`
36
+
37
+ Everything from the environment (spec §3.3); writes nothing to disk. Overrides: ethereum, solana, registry, checkpoints.
38
+
39
+ ### `from_profile(home: 'Any' = None, *, network: 'str | None' = None, endpoint: 'str | None' = None, ethereum: 'Any' = None, solana: 'Any' = None) -> "'Bridge'"`
40
+
41
+ The client for the local profile (spec §3.4), created on first use. *network*/*endpoint* apply only when
42
+ creating. Side-chain connections come from the arguments or the same env variables as ``from_env``.
43
+
44
+ ### `status(self) -> 'BridgeStatus'`
45
+
46
+ Read-only re-orientation: addresses and public balances of every registry asset per configured chain.
47
+
48
+ ``pending`` is every in-flight transfer of the bound checkpoint store, reconstructed offline by
49
+ :meth:`pending` — no chain is read for it, and a malformed record comes back as a ``Progress``
50
+ with ``next == "failed"`` instead of hiding the others. A record this client cannot interpret at
51
+ all (an unknown route, a registry version it did not write) and a file the store could not read
52
+ back are dict entries instead — ``{"next": "failed", "error", "error_type"}`` plus the
53
+ ``checkpoint_id`` or ``path`` that names them — so nothing is ever dropped silently. It is empty
54
+ when no store is bound. Finish any entry with ``recover`` → ``wait`` / ``resume`` / ``complete``,
55
+ never by starting a new transfer.
56
+
57
+ ### `quote(self, *, source_chain: 'str | None' = None, source_asset: 'str | None' = None, destination_chain: 'str | None' = None, destination_asset: 'str | None' = None, bridge_protocol: 'str | None' = None, route=None, amount=None, amount_atomic=None, recipient: 'str', sender: 'str | None' = None, mint_mode: 'str' = 'public', secret_nonce: 'str' = '0scalar')`
58
+
59
+ Price a transfer and get the plan that ``execute`` takes. Nothing is signed.
60
+
61
+ Name the route the way veil's ``quote`` does: ``source_chain`` + ``source_asset``
62
+ + ``destination_chain`` (``"ethereum"``, ``"usdc"``, ``"aleo"``), adding
63
+ ``destination_asset`` / ``bridge_protocol`` (``"xreserve"`` | ``"hyperlane"``)
64
+ only when more than one route fits — or ``route=`` with a ``Route`` from
65
+ ``routes()`` or its id. Give exactly one of ``amount`` (human units, str)
66
+ or ``amount_atomic`` (int). ``recipient`` is the destination-chain
67
+ address. ``mint_mode`` (xReserve into Aleo only): ``"public"`` balance,
68
+ ``"record"`` minted by the relayer, or ``"private"`` — you finish it
69
+ yourself with ``complete`` and must keep ``secret_nonce``. Returns a
70
+ kind-specific ``Quote`` (``quote.kind`` in evm-hyperlane /
71
+ solana-hyperlane / aleo-hyperlane / evm-xreserve / aleo-xreserve) with
72
+ ``fees`` and ``amount_out`` in human units and ``quote.plan``. Show the
73
+ user fees + amount before ``execute``.
74
+
75
+ ### `execute(self, plan, *, on_checkpoint=None, proving: 'str' = 'delegate', mode: 'str | None' = None, record: 'str | None' = None, merkle_proof: 'str | None' = None, gas_payment_microcredits: 'int | None' = None, secret_nonce: 'str | None' = None, poll_seconds: 'float' = 1.0, timeout_seconds: 'float' = 120.0)`
76
+
77
+ Commit funds on the source chain for ``quote.plan``; returns ``Progress``.
78
+
79
+ Runs approval(s) → deposit / dispatch / burn, emitting a ``Checkpoint`` to
80
+ ``on_checkpoint`` (and the bound store) at every boundary — including
81
+ AFTER proving and BEFORE broadcast for Aleo legs, so a crash there is
82
+ resumable without proving twice. With ``FileCheckpointStore``, a UTC date/counter
83
+ journal id is reserved before submission and stays fixed through recovery.
84
+ ``checkpoint.id`` is that local key; ``checkpoint.receipt_id`` remains the
85
+ changing on-chain receipt identity. ``proving`` is ``"delegate"`` (DPS) or
86
+ ``"local"``; ``mode`` is ``"caller"|"signer"`` (Aleo Hyperlane) or
87
+ ``"private"|"public"|"public-as-signer"`` (Aleo xReserve burn, default
88
+ private; ``record``/``merkle_proof`` optional — the SDK selects a record
89
+ and computes the exclusion proof). The Hyperlane hook payment is
90
+ re-quoted right before proving unless ``gas_payment_microcredits`` is
91
+ pinned. Irreversible once the source step is broadcast: afterwards use
92
+ ``wait`` / ``recover``, never ``execute`` again.
93
+
94
+ ### `wait(self, progress, *, until=None, poll_seconds: 'float' = 15.0, timeout_seconds: 'float' = 1200.0, on_update=None, on_error=None, max_consecutive_errors: 'int' = 5)`
95
+
96
+ Poll until the transfer finishes or needs you: stops at ``progress.next``
97
+ in resume / complete / done / failed, or at any status in ``until``.
98
+
99
+ A ``PollingTimeoutError`` is NOT a failure — the transfer is still in
100
+ flight; call ``wait`` again or ``recover`` later. ``on_update`` receives
101
+ each changed ``Progress``. A transient error (flaky RPC/HTTP transport)
102
+ is retried up to ``max_consecutive_errors`` times, calling ``on_error``
103
+ on each tolerated retry; a non-transient error propagates immediately.
104
+
105
+ ### `recover(self, checkpoint)`
106
+
107
+ Rebuild ``Progress`` from a saved checkpoint (``Checkpoint``, dict or JSON) — reads only.
108
+
109
+ Re-resolves the route from the live registry and reads chain state once;
110
+ ``progress.next`` then says what to do: ``wait``, ``resume``, ``complete``,
111
+ ``done`` or ``failed``.
112
+
113
+ ### `resume(self, progress, *, on_checkpoint=None, secret_nonce: 'str | None' = None, poll_seconds: 'float' = 1.0, timeout_seconds: 'float' = 120.0, proving: 'str' = 'delegate')`
114
+
115
+ Finish an interrupted source submission (``progress.next == "resume"``).
116
+
117
+ Rebroadcasts the identical proved Aleo transaction (a duplicate response is
118
+ success) or, on EVM, re-scans history and only then authorizes the single
119
+ missing deposit/dispatch. Never repeats a confirmed step.
120
+
121
+ ### `complete(self, progress, *, secret_nonce: 'str', on_checkpoint=None, proving: 'str' = 'delegate')`
122
+
123
+ Submit the private USDCx mint (``progress.next == "complete"``).
124
+
125
+ Requires the same ``secret_nonce`` given to ``execute``; the SDK never
126
+ stored it. Submits exactly one ``private_mint`` and returns
127
+ ``DESTINATION_CONFIRMING`` progress to ``wait`` on.
128
+
129
+ ### `pending(self) -> 'list'`
130
+
131
+ The in-flight transfers of this profile — every checkpoint in the bound store,
132
+ reconstructed offline (:func:`lifecycle.progress_from_checkpoint`): no network read, so one
133
+ unreachable chain can never hide the others. A malformed checkpoint yields a ``Progress``
134
+ with ``next == "failed"`` and ``error`` set instead of raising; call ``wait()``/``recover()``
135
+ on any entry to refresh it against live chain state.
136
+
137
+ Nothing is ever dropped silently. A record this client cannot interpret at all — a route
138
+ that no longer exists, a registry version this build did not write — and a file the store
139
+ could not even read back come back as ``{"next": "failed", "error", "error_type"}`` entries
140
+ (naming the ``checkpoint_id`` or the ``path``) alongside the healthy ``Progress`` objects,
141
+ so a stale or corrupt file can never make a transfer that is still on the wire invisible.
142
+
143
+ ## Serving a chatting user (the conversation pattern)
144
+
145
+ ### Keys and identity
146
+
147
+ 1. **NEVER ask the user to paste a private key into the conversation.** Keys
148
+ come from the environment only: `BRIDGE_PRIVATE_KEY` (Aleo),
149
+ `EVM_PRIVATE_KEY` + `ETHEREUM_RPC_URL`, `SOLANA_PRIVATE_KEY` (+ optional
150
+ `SOLANA_RPC_URL`), set in the user's own shell before the process starts.
151
+ `Bridge.from_profile()` creates an Aleo key on first use and never writes
152
+ EVM/Solana keys to disk.
153
+ 2. `status()` first in any session: which chains are configured, balances of
154
+ every bridge asset, and the pending transfers in the checkpoint store. A
155
+ pending transfer is finished with `recover` → `wait`/`resume`/`complete`,
156
+ never by starting a new one.
157
+
158
+ ### Quote first, always
159
+
160
+ 3. **Always `quote` before `execute`** and show the user the route, the fees
161
+ and `amount_out` in human units with symbols ("2 USDC → 2 USDCx; Hyperlane
162
+ hook payment 8.17 ALEO"), never raw atomic units. Minimums: xReserve
163
+ needs at least 2 USDC in and strictly more than the 2 USDCx withdrawal fee
164
+ out; Hyperlane moves one atomic unit but network fees and the relayer
165
+ payment cost more than that — say so. That 2 USDCx withdrawal fee is the
166
+ registry's quote ASSUMPTION, not a live read: on 2026-09-18 the testnet
167
+ route actually charged ≈1.0035 USDC (2.000001 USDCx burned delivered
168
+ 0.996501 USDC), so the fee is flagged `estimated` and `amount_out` on that
169
+ route is a LOWER BOUND — tell the user they may receive more, never less.
170
+ 4. Only `execute` after the user confirms. Through the agent tools every
171
+ write requires `confirm=true`; without it the tool returns the quote and
172
+ moves nothing. A live mainnet execution additionally needs the user's
173
+ own `BRIDGE_LIVE_MAINNET_EXECUTE` acknowledgement — never set it yourself;
174
+ without it, treat any mainnet run as a rehearsal.
175
+
176
+ ### The source step is irreversible
177
+
178
+ 5. Once the deposit / dispatch / burn is broadcast the funds are committed.
179
+ A timeout, an RPC error or a crash after that point is an UNKNOWN outcome,
180
+ not a failure: recover from the last checkpoint (`recover(checkpoint)` or
181
+ `pending()`) — never run `execute` again for the same transfer. This is
182
+ the funds-safety rule above all others: never resend after an ambiguous
183
+ broadcast.
184
+
185
+ ### What `progress.next` means for the user
186
+
187
+ | `progress.next` | Status | Tell the user | Do |
188
+ | --- | --- | --- | --- |
189
+ | `wait` | source confirming, attestation pending, delivery pending | "In flight; I'll keep checking." | `wait(progress)` (or re-check later from the checkpoint) |
190
+ | `resume` | `SOURCE_SUBMISSION_PENDING` | "An approval confirmed / a proof was built but the transfer itself was not submitted; I can submit it now." | confirm, then `resume(progress)` |
191
+ | `complete` | `DESTINATION_ACTION_REQUIRED` | "Circle attested your deposit; your private mint needs your signature (and the secret nonce)." | confirm, then `complete(progress, secret_nonce=...)` |
192
+ | `done` | `COMPLETED` | "Delivered." Report source and destination transaction ids. | nothing |
193
+ | `failed` | `FAILED` / `EXPIRED` | Relay `progress.error`; the source step did not commit funds or was rejected. | nothing — a new transfer needs a new quote |
194
+
195
+ `wait` raising `PollingTimeoutError` is NOT a failure — say the transfer is
196
+ still in flight and check again later.
197
+
198
+ ### Private mints and the secret nonce
199
+
200
+ 6. `mint_mode="private"` (USDC → USDCx) commits `(recipient, secret_nonce)` on
201
+ Ethereum. The same `secret_nonce` is required by `complete`; the SDK
202
+ **never stores** it and checkpoints exclude it (and every other secret).
203
+ Tell the user to keep it (the default `0scalar` needs no storage but adds
204
+ no entropy). Only the recipient's Aleo key can complete a private mint —
205
+ make sure the recipient IS the configured Aleo address before depositing.
206
+ 7. Aleo-origin Hyperlane transfers spend PUBLIC balances: `unshield` a private
207
+ record first. Hyperlane delivers into public balances; `shield` afterwards
208
+ if the user wants privacy. Private xReserve burns spend records directly.
209
+
210
+ ### The hosted record scanner needs the account's VIEW KEY
211
+
212
+ 8. Selecting a private USDCx record — `bridge.privacy.select_record`, and so
213
+ `bridge.unshield` and the default private xReserve burn — reads the
214
+ account's records through the hosted record scanner. The scanner answers
215
+ nothing for an account that has not been registered with it, and
216
+ **registering shares that account's VIEW KEY** with the scanning service,
217
+ which can then decrypt every record the account owns, forever. That is a
218
+ privacy decision belonging to the user, so the SDK NEVER registers on its
219
+ own: tell the user what registering shares, and only if they agree run
220
+
221
+ ```python
222
+ bridge.aleo.records.register(bridge.aleo.default_account)
223
+ ```
224
+
225
+ Without it record selection raises a configuration error saying exactly
226
+ this. A public burn (`mode="public"`) or passing `record=` yourself needs
227
+ no scanner and no view key.
228
+
229
+ ### While acting
230
+
231
+ 9. Writes are slow (proving + confirmation ≈ a minute or two on Aleo; Circle
232
+ attestation and Hyperlane relay take minutes). Never re-submit because a
233
+ call seems slow — `status()` / `recover` first.
234
+ 10. Confirm, act, report ids. Errors name their own fix — read the exception
235
+ message and do what it says.
236
+
237
+
238
+ ## Tier 2 — the protocol modules (building your own flows)
239
+
240
+ Every Aleo write returns an `AleoCall`: nothing touches the network until
241
+ `.simulate()` (free), `.prove()` / `.delegate_prepared()` (proved, not
242
+ broadcast — checkpoint it), `.submit_prepared()`, `.transact()` (local
243
+ proving + broadcast) or `.delegate()` (DPS + broadcast). EVM and Solana
244
+ writes return `EvmCall` / `SolCall` with `.build()` (unsigned) and `.send()`.
245
+ The lifecycle verbs above compose these; use them directly only when you
246
+ need a single leg. Confirm-gated writes and the never-resend rule above
247
+ apply here too — these are the same broadcasts, just one leg at a time.
248
+
249
+ ### `hyperlane.transfer_remote(self, asset: 'Any', recipient: 'str', *, amount: 'Any' = None, amount_atomic: 'int | None' = None, as_signer: 'bool' = False, gas_payment_microcredits: 'int | None' = None) -> 'AleoCall[DispatchReceipt]'`
250
+
251
+ Withdraw an Aleo warp asset to Ethereum/Solana. Quotes the IGP payment now unless pinned; the
252
+ lifecycle layer (plan 4) re-quotes at the last responsible moment by calling this again.
253
+
254
+ ### `hyperlane.quote_gas_payment(self, asset: 'Any') -> 'GasQuote'`
255
+
256
+ Live relayer payment for the route (the exact u64 the hook asserts); quote right before proving.
257
+
258
+ ### `xreserve.burn(self, recipient: 'str', *, amount: 'Any' = None, amount_atomic: 'int | None' = None, mode: 'str' = 'private', record: 'str | None' = None, merkle_proof: 'str | None' = None) -> 'AleoCall[BurnReceipt]'`
259
+
260
+ Burn USDCx for USDC on Ethereum. ``private`` (default) spends a Token record via the wrapper and needs a
261
+ freeze-list exclusion proof — both are resolved from chain state when not supplied. Minimum: more than
262
+ the 2 USDCx withdrawal fee. The Aleo burn-attestation service forwards accepted burns to Circle.
263
+
264
+ ### `xreserve.private_mint(self, attestation: 'Attestation', recipient: 'str', *, secret_nonce: 'str' = '0scalar', route: 'Route | None' = None) -> 'AleoCall[MintReceipt]'`
265
+
266
+ Finish a private-mode deposit: the only user-signed Aleo step of the inbound flow (``wrapper.private_mint``).
267
+
268
+ ### `xreserve.get_attestation(self, message_hash: "'str | bytes'", *, route: 'Route | None' = None) -> 'Attestation | None'`
269
+
270
+ One Circle request for *message_hash*; ``None`` while pending (404).
271
+
272
+ ### `shield(self, asset: 'Any', *, amount: 'Any' = None, amount_atomic: 'int | None' = None, recipient: 'str | None' = None) -> 'AleoCall[PrivacyReceipt]'`
273
+
274
+
275
+
276
+ ### `unshield(self, asset: 'Any', *, amount: 'Any' = None, amount_atomic: 'int | None' = None, record: 'str | None' = None, merkle_proof: 'str | None' = None, recipient: 'str | None' = None) -> 'AleoCall[PrivacyReceipt]'`
277
+
278
+
279
+
280
+ ### `freezelist.exclusion_proof(self, address: 'str', program: 'str') -> 'str'`
281
+
282
+ ``[MerkleProof; 2]`` proving *address* is not frozen on *program*; veil's empty pair when the list is empty.
283
+
284
+ ### `eth.transfer_remote(self, asset: 'Any' = None, recipient: 'str | None' = None, *, amount: 'Any' = None, amount_atomic: 'int | None' = None, plan: 'Plan | None' = None) -> 'EvmCall[DispatchReceipt]'`
285
+
286
+ Send ETH, WBTC or USDT to Aleo through its Hyperlane Warp Route.
287
+
288
+ Re-quotes ``quoteTransferRemote`` at send time. Collateral routes approve exactly the
289
+ quoted token amount only when the allowance is short (USDT: a non-zero allowance is
290
+ reset to 0 first). Native ETH sends amount + fee as ``msg.value``; collateral routes
291
+ send the fee only. Each hash is checkpointed before polling; a timeout returns a
292
+ pending ``DispatchReceipt``. The message id comes from the Mailbox ``DispatchId`` log.
293
+
294
+ ``plan=`` executes a plan prepared earlier (typically ``quote.plan``): the route is
295
+ re-resolved by id against the live registry, the sender must be the connected account, and
296
+ the plan must equal what this call would have prepared itself. Mutually exclusive with ``asset=``.
297
+
298
+ ### `eth.deposit_usdc(self, recipient: 'str | None' = None, *, amount: 'Any' = None, amount_atomic: 'int | None' = None, mint_mode: 'str | None' = None, secret_nonce: 'str' = '0scalar', plan: 'Plan | None' = None) -> 'EvmCall[DepositReceipt]'`
299
+
300
+ Deposit USDC into Circle xReserve for USDCx on Aleo (minimum 2 USDC; irreversible once confirmed).
301
+
302
+ ``mint_mode``: ``public`` (public USDCx balance), ``record`` (protocol-minted private
303
+ record), or ``private`` (deposit addressed to the shielded wrapper program; you must later
304
+ run ``bridge.xreserve.private_mint`` / plan 4's ``complete`` with the same ``secret_nonce``,
305
+ which the SDK never stores). Approves exactly the amount only when the allowance is
306
+ short, then ``depositToRemote`` with no ``msg.value``. The confirmed ``DepositReceipt``
307
+ carries Circle's message hash (receipt id) and the deposit nonce. ``mint_mode`` defaults to
308
+ ``plan.mint_mode`` when a plan is given, else ``"public"``.
309
+
310
+ ``plan=`` executes a plan prepared earlier (typically ``quote.plan``): the route is
311
+ re-resolved by id against the live registry, the sender must be the connected account, and
312
+ the plan must equal what this call would have prepared itself. ``secret_nonce`` is never
313
+ part of a plan, so a private deposit must still pass the same one it was quoted with.
314
+
315
+ ### `eth.quote_transfer_remote(self, asset: 'Any' = None, recipient: 'str | None' = None, *, amount: 'Any' = None, amount_atomic: 'int | None' = None, route: 'Route | None' = None, sender: 'str | None' = None, plan: 'Plan | None' = None) -> 'EvmHyperlaneQuote'`
316
+
317
+ Quote an Ethereum → Aleo Hyperlane transfer without signing.
318
+
319
+ Native routes (ETH): ``msg.value`` carries the asset and the relayer fee, so
320
+ ``native_fee_atomic = native_value_atomic - amount``. Collateral routes (WBTC, USDT):
321
+ ``msg.value`` is fee only and ``approval_required`` reflects the router's ERC-20
322
+ allowance for ``sender`` (or the connection's account); it is ``None`` when no account is known.
323
+
324
+ ``plan=`` re-quotes a plan prepared earlier: it supplies the route, sender, recipient and
325
+ amount, and is validated against the live registry. It is mutually exclusive with
326
+ ``asset=``/``route=``/``sender=``.
327
+
328
+ ### `sol.transfer_remote(self, recipient: 'str | None' = None, *, amount: 'str | None' = None, amount_atomic: 'int | None' = None, plan: 'Plan | None' = None) -> 'SolCall[DispatchReceipt]'`
329
+
330
+ Send native SOL to an Aleo address over the Hyperlane warp route (spec §6).
331
+
332
+ Returns a :class:`SolCall`: ``build()`` previews the partially signed transaction,
333
+ ``send()`` moves funds (amount + IGP payment + network fee + rent leave the wallet).
334
+
335
+ ``plan`` (from ``Bridge.execute``) supplies recipient and amount and must have been prepared for
336
+ the connected wallet; its registry version and route id are re-checked against the live registry
337
+ when the call runs. An ``amount``/``amount_atomic`` that disagrees with the plan is a
338
+ ``ValueError``. Without a plan, ``recipient`` is required.
339
+
340
+ ### `sol.quote_transfer_remote(self, recipient: 'str | None' = None, *, amount: 'str | None' = None, amount_atomic: 'int | None' = None, sender: 'str | None' = None, plan: 'Plan | None' = None) -> 'SolanaHyperlaneQuote'`
341
+
342
+ Lamports required for a SOL → Aleo transfer: amount + IGP payment + network fee + rent (spec §5 kind
343
+ ``solana-hyperlane``). Reads Solana; never signs. ``sender`` defaults to the connected wallet and is required
344
+ for the fee estimate.
345
+
346
+ ``plan`` (from ``Bridge.quote``) supplies recipient, amount and sender, and must match the live registry
347
+ version and route; like ``EthModule`` it is mutually exclusive with ``sender=``, and an ``amount``/
348
+ ``amount_atomic`` that disagrees with the plan is a ``ValueError`` (an identical one is tolerated, so
349
+ re-stating the plan's own amount is harmless). Without a plan, ``recipient`` is required.
350
+
351
+ ### Routes in the pinned registry
352
+
353
+ | Route id | Protocol | Environment | Availability |
354
+ | --- | --- | --- | --- |
355
+ | `xreserve:ethereum/usdc->aleo/usdcx` | xreserve | mainnet | active |
356
+ | `xreserve:aleo/usdcx->ethereum/usdc` | xreserve | mainnet | active |
357
+ | `xreserve:sepolia/usdc->aleo-testnet/usdcx` | xreserve | testnet | active |
358
+ | `xreserve:aleo-testnet/usdcx->sepolia/usdc` | xreserve | testnet | active |
359
+ | `hyperlane:ethereum/eth->aleo/eth` | hyperlane | mainnet | active |
360
+ | `hyperlane:aleo/eth->ethereum/eth` | hyperlane | mainnet | active |
361
+ | `hyperlane:ethereum/wbtc->aleo/wbtc` | hyperlane | mainnet | active |
362
+ | `hyperlane:aleo/wbtc->ethereum/wbtc` | hyperlane | mainnet | active |
363
+ | `hyperlane:ethereum/usdt->aleo/usdt` | hyperlane | mainnet | active |
364
+ | `hyperlane:aleo/usdt->ethereum/usdt` | hyperlane | mainnet | active |
365
+ | `hyperlane:solana/sol->aleo/sol` | hyperlane | mainnet | active |
366
+ | `hyperlane:aleo/sol->solana/sol` | hyperlane | mainnet | active |
367
+ | `hyperlane:aleo/aleo->ethereum/aleo` | hyperlane | mainnet | metadata-required |
368
+ | `hyperlane:ethereum/aleo->aleo/aleo` | hyperlane | mainnet | metadata-required |
369
+ | `hyperlane:aleo/aleo->solana/aleo` | hyperlane | mainnet | metadata-required |
370
+ | `hyperlane:solana/aleo->aleo/aleo` | hyperlane | mainnet | metadata-required |
371
+ | `hyperlane:aleo/aleo->base/aleo` | hyperlane | mainnet | metadata-required |
372
+ | `hyperlane:base/aleo->aleo/aleo` | hyperlane | mainnet | metadata-required |
373
+ | `hyperlane:aleo/aleo->hyperevm/aleo` | hyperlane | mainnet | metadata-required |
374
+ | `hyperlane:hyperevm/aleo->aleo/aleo` | hyperlane | mainnet | metadata-required |
375
+ | `hyperlane:ethereum/usad->aleo/usad` | hyperlane | mainnet | metadata-required |
376
+ | `hyperlane:aleo/usad->ethereum/usad` | hyperlane | mainnet | metadata-required |
377
+
378
+ `metadata-required` routes are listed but refused by `quote`/`execute`
379
+ until their deployments are reviewed upstream.
380
+