-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Overview
Add chain swap support (ARK ↔ BTC) to the rust-sdk ark-client, mirroring the feature already implemented in boltz-swap (TypeScript). Chain swaps allow direct on-chain BTC ↔ Ark VTXO swaps via Boltz without routing through Lightning.
This is the Rust equivalent of arkToBtc() / btcToArk() in boltz-swap/src/arkade-swaps.ts.
Background
The rust-sdk currently supports two Boltz swap types in ark-client/src/boltz.rs:
- Submarine swap (ARK → Lightning):
pay_ln_invoice(),refund_expired_vhtlc(),refund_vhtlc() - Reverse submarine swap (Lightning → ARK):
get_ln_invoice(),claim_vhtlc(),wait_for_vhtlc()
Chain swaps are a third type:
- ARK → BTC: User locks a VHTLC on Ark; Boltz claims it and locks BTC; user claims BTC via MuSig2 HTLC
- BTC → ARK: User sends on-chain BTC to a Boltz HTLC address; Boltz locks a VHTLC on Ark; user claims the VHTLC
Both flows use the Boltz v2 chain swap API (POST /v2/swap/chain). The TypeScript implementation in boltz-swap is the reference.
Affected Components
| File | Change Needed |
|---|---|
ark-client/src/boltz.rs |
Add chain swap structs, API types, and methods |
ark-client/src/swap_storage/mod.rs |
Add ChainSwapData storage trait methods |
ark-client/src/swap_storage/memory.rs |
Implement in-memory storage for ChainSwapData |
ark-client/src/swap_storage/sqlite.rs |
Implement SQLite persistence for ChainSwapData |
ark-client/Cargo.toml |
Likely no new deps; may need boltz-client or existing crypto crates |
New Types to Add
/// Data for a pending chain swap (ARK ↔ BTC).
pub struct ChainSwapData {
pub id: String,
pub status: SwapStatus,
pub direction: ChainSwapDirection,
/// Ephemeral keypair used for MuSig2 claim (BTC side)
pub ephemeral_key: secp256k1::SecretKey,
/// Preimage (set after claim, for BTC→ARK)
pub preimage: Option<[u8; 32]>,
pub preimage_hash: ripemd160::Hash,
pub lockup_details: ChainLockupDetails,
pub claim_details: ChainClaimDetails,
pub fee_sats_per_vbyte: u64,
pub created_at: u64,
}
pub enum ChainSwapDirection {
ArkToBtc { btc_address: bitcoin::Address },
BtcToArk,
}
/// Result of a successful ARK → BTC chain swap initiation.
pub struct ArkToBtcResult {
pub swap_id: String,
/// Amount to lock on Ark (send to ark_address)
pub amount: Amount,
/// Ark VHTLC address to fund
pub ark_address: ArkAddress,
pub pending_swap: ChainSwapData,
}
/// Result of a successful BTC → ARK chain swap initiation.
pub struct BtcToArkResult {
pub swap_id: String,
/// Amount of BTC to send
pub amount: Amount,
/// On-chain BTC address to fund
pub btc_address: bitcoin::Address,
pub pending_swap: ChainSwapData,
}New Methods to Add to Client<B, W, S, K>
ARK → BTC
/// Create a chain swap from ARK to BTC.
/// Equivalent to `arkade-swaps.ts: arkToBtc()`.
pub async fn ark_to_btc(
&self,
btc_address: bitcoin::Address,
amount: SwapAmount,
fee_rate: u64,
) -> Result<ArkToBtcResult, Error>;
/// After funding the VHTLC, wait for Boltz to lock BTC and claim it.
/// Equivalent to `arkade-swaps.ts: waitAndClaimBtc()`.
pub async fn wait_and_claim_btc(
&self,
swap_id: &str,
) -> Result<Txid, Error>;
/// Claim BTC using MuSig2 once Boltz has locked funds.
/// Equivalent to `arkade-swaps.ts: claimBtc()`.
pub async fn claim_btc(&self, swap: &ChainSwapData) -> Result<Txid, Error>;
/// If ARK→BTC swap fails, refund the VHTLC back to self.
/// Equivalent to `arkade-swaps.ts: refundArk()`.
pub async fn refund_ark_chain_swap(&self, swap_id: &str) -> Result<Txid, Error>;BTC → ARK
/// Create a chain swap from BTC to ARK.
/// Equivalent to `arkade-swaps.ts: btcToArk()`.
pub async fn btc_to_ark(
&self,
amount: SwapAmount,
fee_rate: u64,
) -> Result<BtcToArkResult, Error>;
/// After funding the BTC HTLC, wait for Boltz to lock a VHTLC and claim it.
/// Equivalent to `arkade-swaps.ts: waitAndClaimArk()`.
pub async fn wait_and_claim_ark(
&self,
swap_id: &str,
) -> Result<ClaimVhtlcResult, Error>;
/// Claim the VHTLC on Ark once Boltz has locked it.
/// Equivalent to `arkade-swaps.ts: claimArk()`.
pub async fn claim_ark_chain_swap(&self, swap: &ChainSwapData) -> Result<ClaimVhtlcResult, Error>;Boltz API Types to Add
// POST /v2/swap/chain
struct CreateChainSwapRequest {
from: Asset,
to: Asset,
preimage_hash: sha256::Hash,
claim_public_key: Option<PublicKey>, // BTC claim key (ARK→BTC: Boltz's; BTC→ARK: ours, MuSig2)
refund_public_key: Option<PublicKey>, // refund key
to_address: Option<String>, // destination BTC address (ARK→BTC)
fee_rate: Option<u64>,
server_lock_amount: Option<u64>,
user_lock_amount: Option<u64>,
}
struct CreateChainSwapResponse {
id: String,
preimage_hash: sha256::Hash,
claim_details: ChainSideDetails, // the side we claim
lockup_details: ChainSideDetails, // the side we fund
}
struct ChainSideDetails {
swap_tree: Option<SwapTree>,
lockup_address: String,
server_public_key: Option<String>,
timeout_block_height: u32,
amount: u64,
}
// POST /v2/swap/chain/{id}/claim (ARK→BTC: post MuSig2 partial sig to get server's)
struct PostChainClaimDetailsRequest {
preimage: Option<String>,
to_sign: ToSignDetails,
}Implementation Notes
ARK → BTC flow
- Call
POST /v2/swap/chain(from: Ark, to: Btc) → getlockupDetails.lockupAddress(VHTLC on Ark) andclaimDetails(BTC HTLC) - Fund the VHTLC: reuse existing
pay_vhtlc/offchain-tx logic from submarine swap - Monitor swap status: wait for
transaction.server.mempool/transaction.server.confirmed - Construct BTC claim tx, sign with MuSig2 (ephemeral key + Boltz server key), post to
/v2/swap/chain/{id}/claim - If expired: refund via
refund_ark_chain_swap()— uses same VHTLC refund path asrefund_vhtlc()
BTC → ARK flow
- Call
POST /v2/swap/chain(from: Btc, to: Ark) → getlockupDetails.lockupAddress(BTC on-chain HTLC) andclaimDetails(VHTLC on Ark) - User sends BTC to
lockupDetails.lockupAddressexternally - Monitor swap status: wait for
transaction.server.mempool/transaction.server.confirmed - Claim the VHTLC: reuse existing
claim_vhtlc()logic from reverse swap
MuSig2 for BTC claim
The BTC claim transaction uses a MuSig2 aggregate key (ephemeral key + Boltz server key). This is the same pattern as in boltz-swap/src/arkade-swaps.ts: claimBtc(). The existing codebase has taproot/MuSig2 primitives in ark-core.
Fee fetching
The existing get_fees() method should be extended to include ChainFeesResponse (already has SubmarineSwapFees and ReverseSwapFees).
Related
- TypeScript reference implementation:
boltz-swap/src/arkade-swaps.ts—arkToBtc(),btcToArk(),claimArk(),claimBtc(),refundArk() - Recovery path: boltz-swap#98 — recovery for pending VHTLC txs after crash; same pattern will apply here
- Chain swap in service worker: boltz-swap#81 — add chain swap methods to service worker (TypeScript)
- Existing swap storage:
ark-client/src/swap_storage/— needsChainSwapDatavariants added to trait and both impls - PR Add support for (reverse) submarine swaps via Boltz #121: feat: add support for (reverse) submarine swaps via Boltz — original Boltz integration; follow same structure