Zwap integration - #323
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b40d728f87
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| throw StateError('eth-relayer claim_buy failed: ${job['error']}'); | ||
| } | ||
| final tx = job['tx_hash_hex'] as String?; | ||
| if (tx != null && tx.isNotEmpty) return tx; | ||
| } catch (_) { |
There was a problem hiding this comment.
Propagate failed relayer jobs before recording a claim
When the eth-relayer reports state == 'failed', this throws but the immediately following catch swallows it as a transient polling error. After 40 polls relayZ2eClaim returns relayer:$swapId; _maybeClaimZ2e then stores that as claimTxid and future polls skip the claim, so a permanently failed z2e/z2erc20 claim is treated as submitted and is never retried or surfaced. Re-throw the failed state instead of catching it.
Useful? React with 👍 / 👎.
| final benign = lower.contains('already') || | ||
| lower.contains('duplicate') || | ||
| lower.contains('spent') || | ||
| lower.contains('missing inputs'); |
There was a problem hiding this comment.
Do not treat missing inputs as broadcast success
When /v1/btc/tx or /v1/zec/broadcast rejects with missing inputs, the transaction was not accepted (for example, the referenced lock UTXO is not known/spendable yet). Returning success here makes callers set claimTxid or swept and stop retrying even though no claim/sweep is on-chain. Keep duplicate/already-spent benign handling separate from missing-input rejections.
Useful? React with 👍 / 👎.
| if (dest.isEmpty) { | ||
| throw StateError('zwap z2b: a BTC receive address is required'); | ||
| } | ||
| final destSpk = await zwap.zwapBtcAddressToSpkHex(address: dest); |
There was a problem hiding this comment.
Reject unsupported legacy BTC recipients before review
For ZEC→BTC, the destination validator still accepts legacy 1.../3... Bitcoin addresses, but this start path sends the recipient to zwapBtcAddressToSpkHex, whose Rust implementation only decodes SegWit/bech32 addresses. A user entering a valid legacy BTC address can pass review and then have the swap fail at start; either support base58 scriptPubKey conversion here or gate z2b to SegWit addresses in the UI.
Useful? React with 👍 / 👎.
Non-custodial in-app atomic swaps (BTC/ETH/ERC20 <-> ZEC) via zwap
Adds an in-wallet atomic-swap backend so users can swap between ZEC and BTC/ETH/USDC without custodians or wrapped assets. Swaps settle through a joint 2-of-2 Orchard note on Zcash plus a native lock on the external chain (BTC P2WSH HTLC, EVM HTLC contract). ZEC stays in user custody for the entire flow.
Off by default. The backend is selected at build time with
--dart-define=VIZOR_SWAP_BACKEND=zwap(defaultnear). Without the define, the existing NEAR Intents path is unchanged and the zwap code is tree-shaken out of the build.Supported directions
ETH and USDC support an in-app chain selector (Ethereum + Base), with per-chain RPC, chainId, and token address wiring.
Architecture
rust/src/zwap/: all swap cryptography (joint Orchard keys and notes, Pallas DLEq, adaptor signatures, BTC scripts, EVM slot derivation). Fund-critical crypto never runs in Dart.lib/src/features/swap/integrations/zwap/: Dart driver (orderbook client, adapter, crash-safe intent store). Plugs into the existing swap UI through the sameSwapProvidercontract NEAR uses; no screen changes.native/hashbind_prover/: on-device ProveKit hashbind prover, built byscripts/build-hashbind-prover.shand vendored via a guarded pod.Security
k_anever leaves the device (release builds fail closed on remote provers).Testing
flutter analyzeclean; 1406 Dart tests, 239 Rust tests, prover round-trip and tamper tests all pass.Mainnet ready
The swap engine is network-agnostic and mainnet ready: the full FSM, the crypto, and on-device proving carry over unchanged. Going live against a deployed mainnet backend is a build-time configuration step, endpoints are already defines, and the chain constants (token addresses, timelocks, fee floors) mirror the deployed backend config through a network-selected table. The rollout lane is documented in
docs/vizor-zwap-mainnet-support-plan.md.