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.
- aleo_bridge_sdk-0.5.1/.gitignore +5 -0
- aleo_bridge_sdk-0.5.1/AGENTS.md +380 -0
- aleo_bridge_sdk-0.5.1/PKG-INFO +799 -0
- aleo_bridge_sdk-0.5.1/README.md +779 -0
- aleo_bridge_sdk-0.5.1/codegen/gen_context.py +248 -0
- aleo_bridge_sdk-0.5.1/examples/README.md +219 -0
- aleo_bridge_sdk-0.5.1/examples/__init__.py +1 -0
- aleo_bridge_sdk-0.5.1/examples/_arguments.py +61 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_sol.py +93 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_sol_to_solana.py +97 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_usdc_private_balance.py +101 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_usdc_private_recipient.py +126 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_usdcx_to_ethereum.py +99 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_wbtc.py +100 -0
- aleo_bridge_sdk-0.5.1/examples/bridge_wbtc_to_ethereum.py +97 -0
- aleo_bridge_sdk-0.5.1/examples/checkpoints/2026-09-25_001_solana-sol_to_aleo-sol_676.2.json +27 -0
- aleo_bridge_sdk-0.5.1/examples/quote_transfer.py +55 -0
- aleo_bridge_sdk-0.5.1/examples/recover_from_journal.py +117 -0
- aleo_bridge_sdk-0.5.1/examples/recover_without_files.py +81 -0
- aleo_bridge_sdk-0.5.1/examples/shield_assets.py +65 -0
- aleo_bridge_sdk-0.5.1/pyproject.toml +44 -0
- aleo_bridge_sdk-0.5.1/pyrightconfig.json +7 -0
- aleo_bridge_sdk-0.5.1/pytest.ini +7 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/AGENTS.md +380 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/__init__.py +79 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/__main__.py +36 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_base58.py +35 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_calls.py +422 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_evm_abi.py +60 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_keccak.py +57 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_plan.py +68 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_registry_data.py +386 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/_sealevel.py +388 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/agent.py +523 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/checkpoint.py +398 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/circle.py +57 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/client.py +484 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/encoding.py +289 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/errors.py +106 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/eth.py +1801 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/freezelist.py +193 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/hyperlane.py +189 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/lifecycle.py +1531 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/mcp.py +132 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/privacy.py +133 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/profile.py +107 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/registry.py +309 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/sol.py +981 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/types.py +277 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/units.py +60 -0
- aleo_bridge_sdk-0.5.1/python/aleo_bridge/xreserve.py +193 -0
- aleo_bridge_sdk-0.5.1/scripts/rehearse.py +266 -0
- aleo_bridge_sdk-0.5.1/tests/__init__.py +0 -0
- aleo_bridge_sdk-0.5.1/tests/conftest.py +263 -0
- aleo_bridge_sdk-0.5.1/tests/fakes/__init__.py +0 -0
- aleo_bridge_sdk-0.5.1/tests/fakes/fake_bridge.py +519 -0
- aleo_bridge_sdk-0.5.1/tests/fakes/fake_solana.py +151 -0
- aleo_bridge_sdk-0.5.1/tests/fakes/fake_web3.py +366 -0
- aleo_bridge_sdk-0.5.1/tests/fakes/sealevel_fixtures.py +52 -0
- aleo_bridge_sdk-0.5.1/tests/fixtures/sealevel-igp-account.json +5 -0
- aleo_bridge_sdk-0.5.1/tests/fixtures/sealevel-transfer-remote.json +122 -0
- aleo_bridge_sdk-0.5.1/tests/live/__init__.py +0 -0
- aleo_bridge_sdk-0.5.1/tests/live/cases.py +580 -0
- aleo_bridge_sdk-0.5.1/tests/live/config.py +223 -0
- aleo_bridge_sdk-0.5.1/tests/live/conftest.py +17 -0
- aleo_bridge_sdk-0.5.1/tests/live/helpers.py +436 -0
- aleo_bridge_sdk-0.5.1/tests/live/test_aleo_reads.py +138 -0
- aleo_bridge_sdk-0.5.1/tests/live/test_eth_reads.py +108 -0
- aleo_bridge_sdk-0.5.1/tests/live/test_eth_sepolia_leg1.py +98 -0
- aleo_bridge_sdk-0.5.1/tests/live/test_lifecycle_live.py +366 -0
- aleo_bridge_sdk-0.5.1/tests/live/test_sol_reads.py +82 -0
- aleo_bridge_sdk-0.5.1/tests/test_agent.py +500 -0
- aleo_bridge_sdk-0.5.1/tests/test_base58.py +29 -0
- aleo_bridge_sdk-0.5.1/tests/test_bridge_eth_wiring.py +105 -0
- aleo_bridge_sdk-0.5.1/tests/test_calls.py +133 -0
- aleo_bridge_sdk-0.5.1/tests/test_checkpoint.py +328 -0
- aleo_bridge_sdk-0.5.1/tests/test_checkpoint_store_home.py +16 -0
- aleo_bridge_sdk-0.5.1/tests/test_circle.py +92 -0
- aleo_bridge_sdk-0.5.1/tests/test_client.py +267 -0
- aleo_bridge_sdk-0.5.1/tests/test_client_lifecycle.py +216 -0
- aleo_bridge_sdk-0.5.1/tests/test_encoding.py +214 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_connection.py +305 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_hyperlane_execute.py +245 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_hyperlane_quote.py +155 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_recover.py +504 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_status.py +382 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_xreserve_execute.py +229 -0
- aleo_bridge_sdk-0.5.1/tests/test_eth_xreserve_quote.py +152 -0
- aleo_bridge_sdk-0.5.1/tests/test_evm_call.py +383 -0
- aleo_bridge_sdk-0.5.1/tests/test_examples.py +184 -0
- aleo_bridge_sdk-0.5.1/tests/test_execute.py +360 -0
- aleo_bridge_sdk-0.5.1/tests/test_freezelist.py +166 -0
- aleo_bridge_sdk-0.5.1/tests/test_gen_context.py +49 -0
- aleo_bridge_sdk-0.5.1/tests/test_get_status.py +351 -0
- aleo_bridge_sdk-0.5.1/tests/test_hyperlane.py +204 -0
- aleo_bridge_sdk-0.5.1/tests/test_import_without_web3.py +28 -0
- aleo_bridge_sdk-0.5.1/tests/test_keccak.py +22 -0
- aleo_bridge_sdk-0.5.1/tests/test_live_case_predicates.py +27 -0
- aleo_bridge_sdk-0.5.1/tests/test_live_drive_dropped.py +87 -0
- aleo_bridge_sdk-0.5.1/tests/test_live_helpers.py +1040 -0
- aleo_bridge_sdk-0.5.1/tests/test_mcp.py +84 -0
- aleo_bridge_sdk-0.5.1/tests/test_package.py +115 -0
- aleo_bridge_sdk-0.5.1/tests/test_prepare.py +219 -0
- aleo_bridge_sdk-0.5.1/tests/test_privacy.py +125 -0
- aleo_bridge_sdk-0.5.1/tests/test_profile.py +76 -0
- aleo_bridge_sdk-0.5.1/tests/test_quote.py +80 -0
- aleo_bridge_sdk-0.5.1/tests/test_recover.py +251 -0
- aleo_bridge_sdk-0.5.1/tests/test_registry.py +349 -0
- aleo_bridge_sdk-0.5.1/tests/test_resume_complete.py +412 -0
- aleo_bridge_sdk-0.5.1/tests/test_sealevel_accounts.py +105 -0
- aleo_bridge_sdk-0.5.1/tests/test_sealevel_igp.py +71 -0
- aleo_bridge_sdk-0.5.1/tests/test_sealevel_instruction.py +41 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_bridge.py +119 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_connection.py +325 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_quote.py +174 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_rpc.py +186 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_send.py +433 -0
- aleo_bridge_sdk-0.5.1/tests/test_sol_status.py +106 -0
- aleo_bridge_sdk-0.5.1/tests/test_types.py +125 -0
- aleo_bridge_sdk-0.5.1/tests/test_units.py +60 -0
- aleo_bridge_sdk-0.5.1/tests/test_wait.py +231 -0
- aleo_bridge_sdk-0.5.1/tests/test_xreserve.py +166 -0
|
@@ -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
|
+
|