Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions bridge-sdk/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
> GENERATED from SDK docstrings by `codegen/gen_context.py` — do not
> edit by hand; edit the docstrings and regenerate.

Typed Python client that moves assets between Aleo, Ethereum, Arc, Base, Arbitrum and Solana
over reviewed Hyperlane, Circle xReserve and native-USDC CCTP deployments
Typed Python client that moves assets between Aleo, Ethereum, BSC, Arc, Base, Arbitrum and Solana
over reviewed Hyperlane, Verulink, Circle xReserve and native-USDC CCTP deployments
(`pip install aleo-bridge-sdk`, imports as `aleo_bridge`).
MCP alternative: `python -m aleo_bridge.mcp` exposes the same lifecycle as
tools; `aleo_bridge.agent.bridge_tools()` gives Claude-shape tool schemas.
Registry version `2026-09-30.hyperlane-bat-usdg-zec.1`.
Registry version `2026-11-04.verulink-bsc.1`.
CCTP supports Arc ↔ Ethereum/Base/Arbitrum. Pass `cctp={"speed": "fast",
"forwarding": True, "max_fee": "0.1"}` to quote; execute the returned plan to retain its ceiling.
Use `Bridge(..., evm={"arc": Ethereum(...), "base": Ethereum(...)})` for route-selected connections.
Arc native gas uses 18 decimals; ERC-20 USDC uses 6. They spend the same balance.
CCTP completion requires the exact message and mint receipt; a consumed nonce alone stays pending.
For stalled forwarding, `complete(progress, manual_mint=True)` explicitly authorizes a destination mint.
Recovery keeps the approved fee ceiling; never rerun execute after a broadcast.
Verulink moves public ALEO between Aleo and BSC; configure `evm={"bsc": Ethereum(...)}`.
Both directions require `complete(progress)` with destination gas, without a secret nonce.
Fee rates, pause state and peers are read live; unknown gas costs are None, not zero.
An approval-only BSC checkpoint requires source-hash reconciliation, never another burn.
Holding is not delivery; its checkpoint remains available for review and recovery.

Runnable examples ship in the package: start with
`python -m aleo_bridge.examples.quote_transfer --help`.
Expand Down Expand Up @@ -68,19 +73,24 @@ Price a transfer and get the plan that ``execute`` takes. Nothing is signed.

Name the route the way veil's ``quote`` does: ``source_chain`` + ``source_asset``
+ ``destination_chain`` (``"ethereum"``, ``"usdc"``, ``"aleo"``), adding
``destination_asset`` / ``bridge_protocol`` (``"xreserve"`` | ``"hyperlane"``)
``destination_asset`` / ``bridge_protocol`` (``"xreserve"``, ``"hyperlane"``,
``"cctp"``, or ``"verulink"``)
only when more than one route fits — or ``route=`` with a ``Route`` from
``routes()`` or its id. Give exactly one of ``amount`` (human units, str)
or ``amount_atomic`` (int). ``recipient`` is the destination-chain
address. ``mint_mode`` (xReserve into Aleo only): ``"public"`` balance,
``"record"`` minted by the relayer, or ``"private"`` — you finish it
yourself with ``complete`` and must keep ``secret_nonce``. Returns a
kind-specific ``Quote`` (``quote.kind`` in evm-hyperlane /
solana-hyperlane / aleo-hyperlane / evm-xreserve / aleo-xreserve) with
solana-hyperlane / aleo-hyperlane / evm-xreserve / aleo-xreserve /
evm-cctp / aleo-verulink / evm-verulink) with
``fees`` and ``amount_out`` in human units and ``quote.plan``. Show the
user fees + amount before ``execute``. Refuses with ``RoutePausedError`` /
``PauseCheckError`` when a pause switch on either end reads paused or
cannot be read (see ``pause_status``).
Verulink supports public ALEO
between Aleo and BSC; both directions require a destination claim.
Unpriced network gas has ``Fee.amount=None``, never zero.

### `execute(self, plan, *, on_checkpoint=None, proving: 'str' = 'delegate', mode: 'str | None' = None, record: 'str | None' = None, compliance_proofs: 'ComplianceProofs' = 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)`

Expand Down Expand Up @@ -138,16 +148,18 @@ end reads paused or cannot be read.

### `complete(self, progress, *, secret_nonce: 'str | None' = None, on_checkpoint=None, proving: 'str' = 'delegate', manual_mint: 'bool' = False)`

Claim a private USDCx or native-USDC CCTP destination mint.
Claim a private USDCx, native-USDC CCTP, or public Verulink destination.

Requires the same ``secret_nonce`` given to ``execute``; the SDK never
Private xReserve requires the same ``secret_nonce`` given to ``execute``; the SDK never
stored it. Submits exactly one ``private_mint`` and returns
``DESTINATION_CONFIRMING`` progress to ``wait`` on. CCTP needs no secret
nonce, but requires a destination signer and gas. Set ``manual_mint=True``
to explicitly authorize fallback for stalled forwarding; an already
submitted destination transaction is observed rather than repeated.
Refuses with ``RoutePausedError`` / ``PauseCheckError`` when a pause switch
on the destination side reads paused or cannot be read.
Verulink requires a destination signer and native gas, with no secret
nonce. A holding result retains its checkpoint for review and recovery.

### `pending(self) -> 'list'`

Expand All @@ -170,7 +182,8 @@ Read every operator pause switch governing ``route`` (a ``Route``, its id, or a
Both ends are read unless ``sides`` narrows it: the ``pause`` mapping and enrolled router of the
Aleo warp program (plus the ARC-20 token's ``pause__``), the USDCx bridge and stablecoin pauses,
``paused()`` on xReserve and the CCTP transmitter, any ``PausableHook`` / ``PausableIsm`` in the
Ethereum Mailbox's trees, and the Solana token PDA's enrolled router. Nothing is cached and the
Ethereum Mailbox's trees, the Solana token PDA's enrolled router, and Verulink's Aleo bridge/service
settings and BSC bridge/service pauses. Nothing is cached and the
result never raises for a pause: ``status.paused`` / ``status.unknown`` say so and
``status.reason`` names the switch. ``quote``, ``execute``, ``resume`` and ``complete`` run this same
check and refuse with ``RoutePausedError`` or ``PauseCheckError``. A far-side EVM chain with no
Expand Down Expand Up @@ -460,6 +473,8 @@ tolerated, so re-stating the plan's own amount is harmless). Without a plan, ``r
| `cctp:arc/usdc->base/usdc` | cctp | mainnet | active |
| `cctp:arbitrum/usdc->arc/usdc` | cctp | mainnet | active |
| `cctp:arc/usdc->arbitrum/usdc` | cctp | mainnet | active |
| `verulink:aleo/aleo->bsc/aleo` | verulink | mainnet | active |
| `verulink:bsc/aleo->aleo/aleo` | verulink | mainnet | active |

`metadata-required` routes are listed but refused by `quote`/`execute`
until their deployments are reviewed upstream.
Expand Down
43 changes: 43 additions & 0 deletions bridge-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Bring assets from Ethereum or Solana to Aleo for payments and applications,
or withdraw them back to their source chain. Hyperlane carries ETH, WBTC,
USDT, SOL, BAT, USDG, and ZEC; Circle xReserve connects Ethereum USDC with Aleo USDCx.
Verulink connects public native ALEO with wrapped ALEO on BSC.

Review the cost before sending, monitor whether the recipient received the
funds, and recover an interrupted transfer without making another deposit.
Expand Down Expand Up @@ -57,6 +58,8 @@ Choose the source chain and asset first, then find the corresponding destination
| Aleo | USDG | Ethereum | USDG | Hyperlane |
| Aleo | BAT, USDG, or ZEC | Solana | BAT, USDG, or ZEC | Hyperlane |
| Aleo | USDCx | Ethereum | USDC | Circle xReserve |
| Aleo | ALEO | BSC | Wrapped ALEO | Verulink |
| BSC | Wrapped ALEO | Aleo | ALEO | Verulink |
| Ethereum | ETH | Aleo | ETH | Hyperlane |
| Ethereum | WBTC | Aleo | WBTC | Hyperlane |
| Ethereum | USDT | Aleo | USDT | Hyperlane |
Expand All @@ -73,6 +76,46 @@ BAT has 18 decimals on Ethereum and Aleo but 8 on Solana, so amounts on a Solana
route must fit 8 decimals. On Aleo these three are ARC-22 tokens with their own
shield programs, so `shield` and `unshield` work for them like USDCx.

## Public ALEO on BSC

Verulink moves public native ALEO to BSC and returns wrapped ALEO to a public
Aleo balance. Both directions need a destination claim and gas on both chains:
ALEO on Aleo and BNB on BSC. Add the BSC connection to the client:

```python
bridge = Bridge(aleo, evm={"bsc": Ethereum(bsc_rpc_url, private_key=evm_private_key)},
checkpoints=FileCheckpointStore("./bridge-checkpoints"))
quote = bridge.quote(source_chain="aleo", source_asset="aleo", destination_chain="bsc",
amount="20", recipient=bsc_recipient)
print(quote.amount_out, quote.fees)
```

This uses the `Bridge`, `Ethereum`, and `FileCheckpointStore` imports and the
Aleo client shown in Setup. To return wrapped ALEO, swap the source and
destination chains and use an Aleo recipient. Private and record mint modes
are unavailable for Verulink.

Quotes read current fees, limits, peer contracts and pause state. The protocol
fee is charged in the source ALEO asset. Network gas is unpriced
(`Fee.amount=None`); it is an additional cost. Execute `quote.plan` to retain
the quoted minimum output, then use `wait` and `complete(progress)` when a
claim is ready. Verulink completion needs no secret nonce.

Save checkpoints before continuing after an interruption. Aleo recovery
rebroadcasts the saved prepared transaction. An approval-only BSC checkpoint
requires the wallet's source transaction hash before continuing; repeating
the burn could send funds twice. After a claim is submitted, recover or wait
using its transaction ID.

Completion requires the exact recipient delivery. A consumed packet alone
stays pending, and a screening hold is reported as failed with
`receipt.protocol_state["held"] == True`. Its checkpoint remains stored
because the recipient has not received the funds.

The [round-trip example](examples/verulink_roundtrip.py) previews both legs and
can resume a saved journey. Its return leg spends the verified amount received.
Checkpoints from the preceding registry remain valid for unchanged routes.

## Setup

To bridge assets to and from Aleo, create a `Bridge` client with the accounts
Expand Down
9 changes: 7 additions & 2 deletions bridge-sdk/codegen/gen_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ def render() -> str:
"> GENERATED from SDK docstrings by `codegen/gen_context.py` — do not",
"> edit by hand; edit the docstrings and regenerate.",
"",
"Typed Python client that moves assets between Aleo, Ethereum, Arc, Base, Arbitrum and Solana",
"over reviewed Hyperlane, Circle xReserve and native-USDC CCTP deployments",
"Typed Python client that moves assets between Aleo, Ethereum, BSC, Arc, Base, Arbitrum and Solana",
"over reviewed Hyperlane, Verulink, Circle xReserve and native-USDC CCTP deployments",
"(`pip install aleo-bridge-sdk`, imports as `aleo_bridge`).",
"MCP alternative: `python -m aleo_bridge.mcp` exposes the same lifecycle as",
"tools; `aleo_bridge.agent.bridge_tools()` gives Claude-shape tool schemas.",
Expand All @@ -198,6 +198,11 @@ def render() -> str:
"CCTP completion requires the exact message and mint receipt; a consumed nonce alone stays pending.",
"For stalled forwarding, `complete(progress, manual_mint=True)` explicitly authorizes a destination mint.",
"Recovery keeps the approved fee ceiling; never rerun execute after a broadcast.",
"Verulink moves public ALEO between Aleo and BSC; configure `evm={\"bsc\": Ethereum(...)}`.",
"Both directions require `complete(progress)` with destination gas, without a secret nonce.",
"Fee rates, pause state and peers are read live; unknown gas costs are None, not zero.",
"An approval-only BSC checkpoint requires source-hash reconciliation, never another burn.",
"Holding is not delivery; its checkpoint remains available for review and recovery.",
"",
"Runnable examples ship in the package: start with",
"`python -m aleo_bridge.examples.quote_transfer --help`.",
Expand Down
40 changes: 40 additions & 0 deletions bridge-sdk/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,46 @@ real funds. Review a quote before submitting a new transfer.
| [recover_from_journal.py](recover_from_journal.py) | Find a saved transfer, check progress, or submit its remaining step. |
| [recover_without_files.py](recover_without_files.py) | Restore monitoring of a confirmed Ethereum-to-Aleo WBTC dispatch from explorer details. |
| [shield_assets.py](shield_assets.py) | Convert an existing public Aleo balance to an encrypted private record. |
| [verulink_roundtrip.py](verulink_roundtrip.py) | Preview or recover public ALEO → BSC → Aleo with a checkpoint for each leg. |

## ALEO through BSC

Preview the Verulink round trip using public addresses:

```sh
python -m aleo_bridge.examples.verulink_roundtrip \
--aleo-address "$ALEO_ADDRESS" --bsc-address "$BSC_ADDRESS" --amount 20
```

The preview loads no keys and writes no journey file. Both directions use
public balances. Execution requires `ALEO_PRIVATE_KEY`, `EVM_PRIVATE_KEY`,
`--execute`, and the operator's own
`BRIDGE_LIVE_MAINNET_EXECUTE=I_ACKNOWLEDGE_THIS_SUBMITS_MAINNET_TRANSACTIONS`
acknowledgement. The signers must match the two addresses. Keep ALEO for fees
and at least 0.001 BNB before starting; that BNB reserve is a budget, not a
gas estimate. Override `ALEO_RPC_URL` and `BSC_RPC_URL` for other mainnet providers.

Execution saves to `~/.aleo-bridge/verulink-roundtrip.json`, or `--state PATH`.
Run the same arguments to recover that journey. The second leg spends the
verified packet amount received on BSC. A lock prevents concurrent runners;
after a process crash, confirm that its owner has stopped before removing
the adjacent `.lock` file.

A wallet boundary is marked before submission. If its response is lost
without a transaction ID, the example stops for reconciliation; it never
starts another source transfer or claim on a retry. Retain the state file,
find the transaction hash in the wallet, and update that leg's checkpoint.
An approval-only BSC checkpoint cannot authorize another burn. Screening
holds keep their checkpoint and are not reported as delivered.

From the checkout, public deployment checks need no accounts:

```sh
BRIDGE_VERULINK_READ_ONLY=1 python -m pytest -m live tests/live/test_verulink_readonly.py
```

Funded live coverage uses the existing opt-in gates with the
`aleo-verulink` and `evm-verulink` case names. No funded test runs by default.

## Review a transfer

Expand Down
Loading
Loading