Skip to content

Zwap integration - #323

Open
yash25198 wants to merge 8 commits into
chainapsis:mainfrom
atheonxyz:zwap-integration
Open

Zwap integration#323
yash25198 wants to merge 8 commits into
chainapsis:mainfrom
atheonxyz:zwap-integration

Conversation

@yash25198

Copy link
Copy Markdown

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 (default near). Without the define, the existing NEAR Intents path is unchanged and the zwap code is tree-shaken out of the build.

Supported directions

Direction Give Receive Mechanism
b2z BTC ZEC user deposits BTC, reveals secret, sweeps joint note
z2b ZEC BTC wallet funds joint note, claims solver's BTC HTLC
e2z ETH / USDC ZEC CREATE2 proxy deposit, adaptor-signature claim
z2e ZEC ETH / USDC wallet funds joint note, relayed EVM claim

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 same SwapProvider contract NEAR uses; no screen changes.
  • native/hashbind_prover/: on-device ProveKit hashbind prover, built by scripts/build-hashbind-prover.sh and vendored via a guarded pod.

Security

  • Seed and orderbook token are in-memory only; the hashbind scalar k_a never leaves the device (release builds fail closed on remote provers).
  • Proving key verified against a pinned sha256; one-time deposit addresses per order; z2e slot-divergence guard before any reveal.

Testing

  • flutter analyze clean; 1406 Dart tests, 239 Rust tests, prover round-trip and tamper tests all pass.
  • Every direction driven from the UI through live settlement on the dockerized regtest stack.

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +898 to +902
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 (_) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant