Skip to content

Instantly share code, notes, and snippets.

@AdamISZ
Last active July 14, 2026 14:18
Show Gist options
  • Select an option

  • Save AdamISZ/ebae2d5f547f56c6f8164db19f718795 to your computer and use it in GitHub Desktop.

Select an option

Save AdamISZ/ebae2d5f547f56c6f8164db19f718795 to your computer and use it in GitHub Desktop.
Outline of how babilonia-style bets could break amount correlation in coinswap

Probabilistic Maker Fees for Coinswap

A small cryptographic add-on that makes the maker's fee — and therefore the taker's output amount — unpredictable, so the published fee stops being the key a chain analyst uses to find a swap and pair its two legs. It's a delta to an ordinary maker–taker coinswap, reusing the adaptor-signature machinery you already have.


TL;DR

In a normal coinswap the maker's fee is public and deterministic: an analyst reads it straight off the maker's advertised offer, subtracts it, and re-links the two legs by amount. This add-on turns the fee into the outcome of a fair coin that neither party can bias or predict, so the taker's payout becomes one of two values decided only at settlement. Note too that the spread between those two values — the size of the effective bet — isn't published either: only the realized payout lands on-chain, so the counterfactual amount stays off. And the spread is the number that matters, because it's the spread, not the fee, that sets how far an analyst's amount test has to stretch (a small jitter stays hidden; a large one is bounded by how much the maker had to over-fund the multisig, which is on-chain — see below).

The protocol delta is small — two pre-signed payout transactions instead of one — and nothing else about the swap changes. In its free form it only jitters the fee by ±(the fee itself); pushed further, with the maker staking capital, it turns into a real amount-decorrelating bet.


The problem it addresses

Coinswap's residual leak is amount correlation. The two funding outputs are (near) equal, so an analyst pairs the legs by value. Two things you might hope would help, don't:

  • The maker fee doesn't hide anything. With default maker params the fee is ~0.02% of the swap, and every input to the fee formula (base + amount·pct + amount·locktime·pct) is in the maker's public offer. So the analyst computes the exact fee and subtracts it. "Equal up to a published, deterministic fee" is, for pairing, just equal.
  • Amount splitting is sum-preserving. Shattering the amount into random chunks (as the implementation already does) fuzzes the partition, but the chunks still sum to the swap value, so subset-sum recovers the total and re-pairs the legs.

So the exact, publicly-known fee is the analyst's pairing key: a precise linear relationship, maker_out = taker_out − fee, that both locates swaps and matches a given leg to its partner. For a small, known fee, almost no unrelated pair of outputs passes the test — which is exactly why it's such a good key.


The idea in one line

Make the fee f the outcome of a fair coin. The taker's output is then a known amount minus an unknown one — so the published fee stops being a key an analyst can use to find the swap or match its two legs.


The delta to a basic maker–taker swap

Look only at the leg where the maker's 2-of-2 pays the taker.

Basic swap:

  • One pre-signed payout transaction.
  • Taker sweeps it and receives X − fee, where fee is fixed and public.

With this add-on:

  • Two pre-signed payout transactions exist, paying the taker X − f_lo and X − f_hi.
  • Each is locked so it can only be completed once a particular secret is revealed. The taker will learn exactly one of the two secrets — and which one is a coin flip neither party can rig.
  • The taker completes and broadcasts that one. The other is never completable, so it never touches the chain.

What does not change:

  • Funding transactions, amounts, the 2-of-2, the atomic secret that links the two legs, and the timelock refund for a vanished counterparty — all identical.
  • No new on-chain script branch. A "someone wins the whole pot" bet would need a timeout leg to let the funder reclaim a stranded output when the other side can't spend. Here both coin outcomes pay the taker, so nothing is ever stranded and no such branch exists. (The ordinary swap abort-refund is unchanged and orthogonal.)

On-chain, an observer sees exactly one ordinary payout of X − f — indistinguishable from a normal swap payout, except f is now one of two values and appears in no public offer.


The coin flip, as a black box

For the rest of the protocol you can treat the coin as a component with three guarantees:

  1. Exactly one of the two payouts becomes spendable.
  2. Neither party can bias which one.
  3. Neither learns the outcome until the swap's normal reveal step.

That's all the surrounding protocol depends on. If that's enough for you, skip the next section.

Under the hood (optional)

You already use adaptor signatures: a signature "locked" under a public point S = s·G that completes precisely when someone learns the scalar s. This uses the same idea, twice, with one extra trick.

  • The maker commits to two secrets, s₁ and s₂ (public points S₁, S₂).
  • The taker builds two locks from them in a secretly shuffled order, each with a blinding value only the taker knows, and hands the maker a short proof that the two locks really are the maker's two secrets in some orderwithout revealing which order. PayoutTx_lo is adaptor-locked to the first lock, PayoutTx_hi to the second.
  • At settlement the maker releases one of the two scalars (coupled to the same atomic secret that releases the maker's own payout, so it falls out of the swap completing normally). Because of the taker's shuffle-and-blind, the maker cannot tell which payout amount that scalar unlocks — so the maker's release is a blind pick.
  • The taker, who knows the shuffle, can complete exactly the one payout whose lock matches the released scalar — and only that one; the other needs the scalar the maker didn't release. So the taker can't grind for the bigger amount either.

Neither side chose the fee. The shuffle-plus-proof is a small "sigma protocol" — the same family as the discrete-log-equality proofs used elsewhere in Bitcoin privacy work — and you can lift it as a black box. (This is the core of the babilonia bet design, from which this fee construction is derived.)


What it costs (the honest ceiling)

The taker can never receive more than the maker funded, so f = X − receipt ≥ 0: the fee can't go negative for free. The fair, zero-extra-capital regime is therefore:

f_lo = 0,   f_hi = 2·f_mean,   E[f] = f_mean   →   f ∈ [0, 2·f_mean]  =  mean ± mean

With a 10k-sat mean fee that's 10k ± 10k — small, but genuinely free: the maker funds exactly the swap amount and never subsidizes.

To get a wider spread ±δ, the maker must over-fund their leg by ~δ, and that over-funding is genuinely at risk — in the "taker wins the swing" outcome it transfers to the taker. So a large jitter turns both sides into gamblers staking ~δ (anti-correlated), with the mean fee as nothing more than the maker's edge. Concretely, 10k ± 1M requires the maker to post ~1M sats extra and risk it to earn 10k in expectation — no longer a fee, but a ±1M bet with a 10k skew.

you want maker funds at risk (each side) what it really is
fee jitter, ±f_mean X nothing extra free; tiny spread
spread ±δ (δ ≫ fee) X + δ ~δ a bet; the fee is the edge

There is no construction that gives a large spread for free — the zero-floor forces staking, and staking is variance.


What you get (the advantage)

  • The analyst loses the amount-based pairing key. This is the main win, so state it actively: with the fee known, "find an output of v and, disconnected, an output of v − fee" is a tight test that both finds swaps and pairs a leg to its partner. Make the fee μ ± δ with δ hidden and that test becomes "differ by μ plus-or-minus an amount I can't bound" — a band of unknown width, which discriminates nothing. Publishing μ no longer helps: the centre was never the problem, the tolerance was, and the bet is what blew the tolerance open. And there's a population effect — as long as large bets are possible, the analyst has to widen the band for every candidate, so even swaps that used a tiny bet benefit from the ones that might not have.
  • No fee watermarking. A maker who picks a "random" fee could encode a tag in the taker's output amount (f = g(taker id)). A fair coin can't be steered, so there's nothing to tag with. This is the specific reason to use a coin rather than a maker-rolled random number.
  • Composes with amount splitting. Splitting randomizes how the amount is partitioned; this randomizes the total. Together, the analyst's subset-sum goes from "hit exactly X − f" to "hit a band" — a strictly harder match.
  • At the staked end, genuine amount decorrelation. Push the spread up and the two legs actually differ in value, not just in partition — i.e. a probabilistic swap. (Same territory as Gerhart–Taylor–Thyagarajan's probabilistic atomic swaps; the coin here is a do-it-with-adaptor-sigs alternative to their OPRF.)

A note on transaction shape. The payout has to cover whichever of the two amounts fires, so the multisig holds at least the larger, and the maker's take — plus any excess it was over-funded with — comes back as a second, change-like output. That makes the spend-from-multisig a 2-output (payment + change) transaction. Worth knowing against the baseline: the current coinswap contract tx is 1-in-1-out — a single output, no change (create_senders_contract_tx, and the taker even validates the 1-in, 1-out shape). Whether the extra output is a plus depends on your mental model: if you want a swap to pass as an ordinary payment, a lone no-change multisig-spend is itself a mild tell and payment-plus-change blends into normal traffic better; if you're counting bytes and outputs, it's a small cost. Either way it falls out of the construction rather than being something you bolt on.


When it's worth it

  • Weak analyst: splitting already does most of the work; this is marginal.
  • Strong analyst (nets out fees, breaks splitting by subset-sum): the probabilistic fee removes the amount-based pairing key they lean on — the exact-difference test that finds and matches legs becomes an unknown-width band.
  • Free version: an always-on "don't hand out the exact fee" hygiene measure — cheap to leave on.
  • Staked version: opt-in gambling you'd only run if you (or a fee-subsidized maker) actually want amount decorrelation badly enough to pay variance for it — or amortize it by streaming many small ones.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment