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.
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.
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.
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.
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, wherefeeis fixed and public.
With this add-on:
- Two pre-signed payout transactions exist, paying the taker
X − f_loandX − 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.
For the rest of the protocol you can treat the coin as a component with three guarantees:
- Exactly one of the two payouts becomes spendable.
- Neither party can bias which one.
- 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.
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₁ands₂(public pointsS₁, 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 order — without revealing which
order.
PayoutTx_lois adaptor-locked to the first lock,PayoutTx_hito 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.)
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.
- 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
vand, disconnected, an output ofv − 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.
- 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.