Skip to main content

Components

  • Built on Distributed Lab foundations: The bridge architecture is adapted from Distributed Lab’s design (https://x.com/distributedlab).
  • Gateway: Orchestrates both directions, derives deposit/exit addresses via FROST DKG, and constructs Spark/BTC transactions.
  • Verifiers: Hold key shares, validate deposits, and co-sign mint/burn using threshold Schnorr signatures.
  • BTC indexer: Monitors a deposit txid until it reaches N_confirm_BTC = 1 confirmation.
  • Spark balance checker: Confirms that a Spark deposit address holds the expected wRunes.
  • Spark entity: Executes Spark-side transactions once signed.
  • User wallet (BTC/Spark): Sends deposits and receives payouts.

Conventions

  • wRunes: BTKN (LRC20-like) wrapped runes on Spark.
  • Signers: M = 3 verifiers; threshold t-of-M enforced by FROST.
  • Confirmations: N_confirm_BTC = 1 for BTC deposits before minting.
  • Amounts: Always strings (u64) to avoid precision loss.

Address derivation & signing

  • Gateway runs FROST DKG to derive a multisig pubkey for each (user_pubkey, rune_id) pair.
  • Each deposit address is tweaked with a random nonce, producing a unique address per request; prevents address reuse and binding attacks.
  • An issuer multisig is dedicated to minting/burning each wRune; verifiers co-sign via FROST.
  • Key shares never combine; optional periodic DKG refresh rotates shares without exposing the master key.

Flow: Runes -> Spark (mint wRunes)

  1. Issue deposit address: Gateway derives/tweaks (user_pubkey, rune_id) multisig and returns deposit_btc_address.
  2. User deposits runes: Broadcasts to deposit_btc_address, then submits { txid, vout, bridgeAddress, btcAddress } via bridgeRunes.
  3. Wait for confirmations: Verifiers subscribe the txid with the BTC indexer; once 1 conf, they validate the rune-bearing output.
  4. Mint wRunes: Gateway uses the issuer multisig + verifier shares to sign a Spark mint; Spark entity executes it to the Spark address associated with the user.
  5. Track status: Progression typically goes address_issued -> waiting_for_confirmations -> ready_for_mint -> minted (or failed).

Flow: Spark -> Runes (burn wRunes, pay BTC)

  1. Issue Spark deposit address: Gateway derives/tweaks the same multisig, returning a unique exit_spark_address.
  2. User deposits wRunes: Sends the specified amount to exit_spark_address, then notifies the gateway.
  3. Verify Spark balance: Verifiers call the Spark balance checker to confirm funding.
  4. Burn & build payout: Gateway signs a burn with the issuer multisig; simultaneously selects rune UTXOs to build the BTC payout to the user. Any change is sent to a fresh deposit address derived/tweaked from the same key.
  5. Track status: Progression typically goes address_issued -> waiting_for_confirmations -> ready_for_mint -> spent (or failed).

Security model

ThreatMitigation
Signer corruption < tFROST threshold: no subset below t can move funds.
BTC re-orgWait for 1 confirmation before minting.
Verifier livenessAny t-of-M online can sign; FROST hides which signers participated.
Front-running / address reusePer-deposit nonce produces unique addresses; replays are invalid.
Accounting driftVault invariant sum(deposits) - sum(withdrawals) enforced on every operation.
Key leakageShares never reconstructed; DKG refresh can rotate shares.

Operational notes

  • Use getActivity to poll by userPublicKey; minted indicates Spark mint completion, spent indicates BTC payout for Spark exits.
  • Preserve the provided bridgeAddress/exit_spark_address per request; each is single-use and bound to the requested amount/rune/user.