Skip to content

hardfork(v4): Spam deterrence#675

Open
binarybaron wants to merge 129 commits intohardfork/v4from
protocol/partial-refund
Open

hardfork(v4): Spam deterrence#675
binarybaron wants to merge 129 commits intohardfork/v4from
protocol/partial-refund

Conversation

@binarybaron
Copy link

@binarybaron binarybaron commented Nov 4, 2025

This PR introduces a change in the atomic swap protocol to discourage spam. It does so by attaching a fee to refunds. This fee will be burnt.

Current situation

The exchange rate of each swap is set at the start and can't be changed.
A swap either completes within 12 hours (this requires cooperation by both parties), or be cancelled.
A swap might still be cancelled after both parties lock their respective currency.

In that case the taker ("Bob") can issue a refund within a 24 hours window. He will get his Bitcoin back and the maker ("Alice") will get her Monero back.

The problem

This can be abused by malicious takers in two ways:

  1. A maker with significantly higher reserves than other makes might use part of it to block other maker's reserves.
    This is possible by initiating a swap and waiting for the maker to lock the Monero.
    Then not continue the swap and wait for a refund.
    This will only cost the malicious actor transaction fees for 3 Bitcoin transactions, while preventing the "small maker" from competing in the market for trading volume.
  2. A malicous actor might start a swap and decide only to complete it when the exchange rates change in their favor enough.
    If that doesn't happen they can refund for the low cost of 3 Bitcoin transactions.
    This allows the malicious actor to treat the swap like a (bascially) free Monero call option.

The solution

The root problem causing both these issues is that refunds are free (for the taker).
By attaching a cost to refunds (only refunding a part of the Bitcoin) this is no longer the case.
Thus both tactics are no longer profitable.

Makers will be able to set the extend of the refund fee. Takers will know the refund fee prior to starting a swap and decide whether to trade or not.

The refund fee does not go to the maker. That would create skewed incentives. The refund fee will be "burnt". The only way to retrieve it is for the maker to sign a transaction giving it back to the taker.

However, the maker will be able to voluntarily give the taker a full refund. This is what we call "amnesty" for now. This is intended such that makers can help out clueless takers who accidentally refunded.
This path is, however, purely voluntary and takers can't 100% rely on it.

Implementation

  • Add TxPartialRefund
  • Add TxRefundAmnesty
  • Change swap setup to transmit amnesty_amount, tx_partial_refund_fee, tx_refund_amnesty_fee, tx_partial_refund_encsig and optionally tx_full_refund_encsig and tx_refund_amnesty_sig
  • Add those same fields to the necessary states
    • Alice
    • Bob
  • Add new states to the Bob's state machine to handle the new scenarios
  • Implement Alice watching for TxFullRefund and TxPartialRefund
  • Desing + implement system for Alice to configure full refunds and amnesty
    • add config entry
    • update orchestrator wizard and add swap-controller command
  • Figure out how to make it all backwards compatible with old swaps stored in the database

Important: This implementation plan does not reflect the current design of the feature, see the discussion below for details.

image

@binarybaron binarybaron marked this pull request as draft November 4, 2025 19:12
@binarybaron
Copy link
Author

binarybaron commented Nov 4, 2025

This issue has been assigned a bounty (💰)

Current bounty:

Exceptions for this specific bounties

Due to the nature of this issue (cryptographic/protocol complexity rather than implementation complexity) the bounty will also be used to fund audits.

Bounty donation address

If you want to incentivize work on this issue, you can help increase the bounty by donating to the address below.

87UHJU9GA3qHn4sX4ZZndEERmqEXx94kuS3QFZ1XGVNg28YcWX2H9AqBh5G6Uc4cfmKRbTDQ3HgsoVKW1RuPHtzLSUEHrmN

Fine print

We use bounties to incentivize development and reward contributors. All issues available for a bounty have the Bounty 💰 label.

To receive the bounty of this issue, you agree to these conditions:

  • Bounties will be set and awarded at discretion of @binarybaron
  • An issue is considered resolved when the patch(es) proposed by the contributor is/are merged in the appropriate repository according to terms of the issue.
  • The first person who resolves an issue in its entirety will receive the entire amount of the bounty.
  • If the issue is resolved collaboratively by more than one person, the reward will be distributed among the contributors
  • Donating to the bounty does not guarantee that this issue will be completed and refunds are generally not issued. If they are then they are granted at the discretion of @binarybaron

@pokefan77
Copy link

Can you elaborate when partial refund will be done instead of full refund and punish?

It seems like Bob needs Alice to get 10% of his money back in case of partial refund.
Why 10% of Bobs money is at risk in case Alice doesnt coorporate, which happened before with some makers!

@Einliterflasche
Copy link

Yes. This update will essentially move some trust requirement from the maker (that the taker is not a spammer) to the taker (that the maker will refund).

However, this can be addressed as follows:

  • a decentralized reputation system. Takers who didn't get the refund can publish a proof, thereby discouraging other takers from swapping with this maker
  • takers might choose to only start swaps for which the maker has already provided the full refund address. Thus not requiring the maker to fully refund
  • the maker does not gain anything from withholding the refund, thereby he is not incentivized to do so

Everyone would be better off if this wasn't necessary, but it is. That's the sad truth.

@pokefan77
Copy link

I am not sure if i and others understand your words "takers might choose to only start swaps for which the maker has already provided the full refund address. Thus not requiring the maker to fully refund" can you rephrase it please? sorry

"the maker does not gain anything from withholding the refund, thereby he is not incentivized to do so"
your phrasing is totally misleading, confirming your narrative. My question was why Bobs 10% are at risk, and neutrally said the truth is: "the maker does not gain anything from releasing the refund, thereby he is not incentivized to do so"
Its the same situation as btc punished. the truth is that there is even ongoing discussion on matrix because somebody lost his big amount of money because the maker did not help him, because he has 0 incentive to do so.

Can you elaborate when partial refund will be done instead of full refund and punish?

@jstark2a
Copy link

jstark2a commented Nov 7, 2025

I am not sure if i and others understand your words "takers might choose to only start swaps for which the maker has already provided the full refund address. Thus not requiring the maker to fully refund" can you rephrase it please? sorry

I am also trying to understand but here is how I interpreted it: Makers will have the choice to offer full refunds or partial refunds. The taker will know the maker's refund policy before they begin a swap. The full refund policy is straightforward: If the taker cancels, they get the full refund. For the partial refund policy, if the taker cancels, they are not guaranteed to receive 100% of their money back. The purpose of this is to disincentivize malicious takers from abandoning swaps because they know there is no penalty for doing so. If you as a taker do not like the idea of a partial refund, you as a free market participant, can refuse to do business with that maker. Because partial refunds mean less risk to the maker, this will allow them to offer lower rates.

somebody lost his big amount of money because the maker did not help him, because he has 0 incentive to do so.

He lost his money due to negligence (or at least failure to understand the refund process). As the docs state "With most makers, you can still redeem the Monero even after being punished. This is, however, purely voluntary and we advise against relying on this."

@pokefan77
Copy link

That would mean either makers are ok with full refund = they automatically give it, or they are not ok with full refund, meaning they will also not help giving it. Its just 10% scam. Its not even remotely justified as fees of all makers are currently around 2%, why should a taker loose 10%?

@jstark2a
Copy link

jstark2a commented Nov 7, 2025

That would mean either makers are ok with full refund = they automatically give it, or they are not ok with full refund, meaning they will also not help giving it. Its just 10% scam. Its not even remotely justified as fees of all makers are currently around 2%, why should a taker loose 10%?

It's not a scam if you know what you are agreeing to. The taker also doesn't lose anything if they simply complete the swap like they agreed to. Makers are not obligated to hand out free 12 hour XMR call options.

Adding a partial refund support will actually help honest takers. If there is not penalty, a maker can lock up the liquidity of other makers that offer lower rates than them. Then they can jack up their rate because their competition is gone. This is effectively a Denial of Service attack against makers.

@pokefan77
Copy link

If thats the worry the penality fee should at maximum be the fee that they ask from the user for a completed trade, not a flat 10%

@Franz-Hermann-GT
Copy link

If thats the worry the penality fee should at maximum be the fee that they ask from the user for a completed trade, not a flat 10%

The XMR price movement also needs to be taken into account here — it can easily move 5–8% during those 12 hours.
In most cases, takers cancel the swap when the XMR price drops during that period. As mentioned earlier, this effectively works like a free call option.

@binarybaron
Copy link
Author

It is not a flat 10%. The 10% is purely for demonstrational purposes. The percentage can be dynamically configured by the maker.

@binarybaron
Copy link
Author

This issue has been assigned a bounty (💰)

Current bounty:

Exceptions for this specific bounties

Due to the nature of this issue (cryptographic/protocol complexity rather than implementation complexity) the bounty will also be used to fund audits.

Bounty donation address

If you want to incentivize work on this issue, you can help increase the bounty by donating to the address below.


87UHJU9GA3qHn4sX4ZZndEERmqEXx94kuS3QFZ1XGVNg28YcWX2H9AqBh5G6Uc4cfmKRbTDQ3HgsoVKW1RuPHtzLSUEHrmN

Fine print

We use bounties to incentivize development and reward contributors. All issues available for a bounty have the Bounty 💰 label.

To receive the bounty of this issue, you agree to these conditions:

  • Bounties will be set and awarded at discretion of @binarybaron

  • An issue is considered resolved when the patch(es) proposed by the contributor is/are merged in the appropriate repository according to terms of the issue.

  • The first person who resolves an issue in its entirety will receive the entire amount of the bounty.

  • If the issue is resolved collaboratively by more than one person, the reward will be distributed among the contributors

  • Donating to the bounty does not guarantee that this issue will be completed and refunds are generally not issued. If they are then they are granted at the discretion of @binarybaron

I wanted to specify this in a bit more in detail as they seems to be some confusion around the bounty on this issue.

In general donations to specific issues will NEVER go directly to neither me nor @Einliterflasche. Issue specific bounties are for funding work of outside contributors.

Donations to this issue will not necessarily result in me implementing this in a faster manner.

Donations will be used to potentially fund an outside contributor to give this a review to assess the security of this on the protocol level. As the funds collected here are unlikely to be enough to fund a full audit (something like TrailOfBits would do) this is going to be "light review" most likely with no attribution to the reviewer.

We are in talks with someone who is well trusted in the Monero community and is more than enough qualified.

If we don't find a reviewer, the funds will be used for other bounties.

@pokefan77
Copy link

Shouldnt a protocol change be fully audited?

@binarybaron
Copy link
Author

binarybaron commented Nov 8, 2025

Shouldnt a protocol change be fully audited?

No, not at all protocol changes necessarily require an audit. We are not introducing any new novel cryptography here.

I understand the concern here though. This will obviously be well tested.

@binarybaron binarybaron changed the title protocol: Partial refund protocol: Spam deterrence Nov 8, 2025
@Einliterflasche Einliterflasche force-pushed the protocol/partial-refund branch 2 times, most recently from 266e3a1 to c5e0871 Compare December 1, 2025 15:32
@Einliterflasche Einliterflasche force-pushed the protocol/partial-refund branch from c5e0871 to f46f4ee Compare December 1, 2025 16:01
@Franz-Hermann-GT
Copy link

One more thought: I actually don’t quite understand why the “suspend” button exists at all.
If a taker initiates a swap and accepts the terms at the start, why is this option needed in the first place?

@pokefan77
Copy link

To make further swaps while for example the current one is bugged.

@binarybaron
Copy link
Author

The suspend button is equivalent to shutting to the app. Removing it would not make a difference in terms of the protocol.

@Franz-Hermann-GT
Copy link

The suspend button is equivalent to shutting to the app. Removing it would not make a difference in terms of the protocol.

I think there is a practical difference though.
Closing the app is an implicit interruption — the user is essentially abandoning the process.
But exposing a dedicated “suspend” button in the UI explicitly offers the user a clean, intentional way to halt an ongoing swap.
Given that the taker has already initiated the swap and accepted the terms, this doesn’t really fit the normal workflow in my view. It signals that pausing mid-protocol is an expected action, while economically and behaviorally a swap should be a commit-or-don’t-start operation.

@Einliterflasche
Copy link

Einliterflasche commented Feb 25, 2026

If you want to discuss this further, please create a separate issue for it

@Franz-Hermann-GT
Copy link

There was some earlier discussion about what the optimal “happy window” should be.
To add a bit of objective data, I’m attaching a statistic from the last 120 days only — i.e. the period during which network connectivity has been noticeably more stable.
This distribution refers to the elapsed time between the swap start (“bitcoin lock transaction in mempool”) and completion (“bitcoin redeem transaction published”).
The result is fairly clear: time ranges above ~2 hours have almost no practical relevance in real-world usage.

      Bucket |  Count |        % |    Cum %
----------------------------------------------
    0–20 min |     52 |    9.89% |    9.89%
   20–30 min |    153 |   29.09% |   38.97%
   30–45 min |    171 |   32.51% |   71.48%
   45–60 min |     73 |   13.88% |   85.36%
   1–2 hours |     57 |   10.84% |   96.20%
   2–3 hours |      8 |    1.52% |   97.72%
   3–4 hours |      0 |    0.00% |   97.72%
   4–5 hours |      3 |    0.57% |   98.29%
   5–6 hours |      1 |    0.19% |   98.48%
   6–7 hours |      1 |    0.19% |   98.67%
   7–8 hours |      2 |    0.38% |   99.05%
   8–9 hours |      2 |    0.38% |   99.43%
  9–10 hours |      1 |    0.19% |   99.62%
 10–11 hours |      2 |    0.38% |  100.00%
 11–12 hours |      0 |    0.00% |  100.00%

@pokefan77
Copy link

pokefan77 commented Mar 2, 2026

will #660 and #880 be included in the hardfork v4? otherwise they will be pushed away indefinitely...

@Franz-Hermann-GT
Copy link

will #660 and #880 be included in the hardfork v4? otherwise they will be pushed away indefinitely...

I strongly suggest keeping v4 strictly focused on spam deterrence.
The network has effectively been operating under degraded economic conditions for months, and restoring safe operation should be the primary goal.
#660 while an important improvement, is a breaking change on its own and would significantly increase the review and testing surface.
#880 appears independent from the hardfork and could be implemented separately.

@Franz-Hermann-GT
Copy link

UI / UX observation for v4.0 regarding maker selection

In the “Available makers” list, makers running older versions (v3.x) are still selectable in v4.0.
However, attempting to start a swap with them eventually results in a failure, without any clear explanation that the issue is version incompatibility.
At the moment, this can be confusing for users because the failure appears as a generic swap error rather than a version mismatch.
Screenshot from 2026-03-03 13-24-28
Screenshot from 2026-03-03 10-33-41
Screenshot from 2026-03-03 12-19-58

@Franz-Hermann-GT
Copy link

Incentive Design Concern: Zero Deposit Weakens Spam Deterrence

Currently, makers with “Zero anti-spam deposit” are highlighted in green, while makers requiring a deposit are shown in yellow.

From a UX perspective this strongly suggests:

  • green = good / preferred
  • yellow = caution / less attractive

However, the anti-spam deposit is not a penalty. It is a protocol-level safeguard introduced to restore normal market behavior and prevent free-option abuse.

By visually marking zero-deposit makers as “green”, the UI unintentionally:

  • Frames zero deposit as a positive competitive feature.
  • Makes anti-spam–enabled makers appear less attractive.
  • Reintroduces the same structural advantage for well-capitalized makers that spam deterrence aims to neutralize.

There is also a broader incentive issue.

If 0% deposit remains allowed and competitively viable:

  • Takers will rationally route flow toward zero-deposit makers (lower friction + positive visual signal).
  • Makers are pressured to reduce deposit to remain competitive.
  • Deposit levels trend toward zero.
  • The anti-spam mechanism becomes economically non-binding in practice.

This does not merely create a visual inconsistency — it risks undermining the economic integrity of the spam deterrence design itself.
If v4 aims to rebalance incentives and eliminate the free-option dynamic, deposit settings must not create a competitive race toward zero.

Possible improvements:

  • Consider a protocol-level minimum deposit (e.g. 2%) to avoid competitive distortion entirely.
  • Use neutral styling for deposit values (no green/yellow distinction).
  • Add a brief, easy-to-understand explanation so users don’t mistake the deposit for an extra fee.

Spam deterrence is meant to restore fair and robust market structure, something that takers ultimately benefit from as well. Allowing and visually rewarding 0% deposit risks weakening that objective at a systemic level.

@Franz-Hermann-GT
Copy link

Suspend dialog should clearly communicate deposit risk

The current suspend dialog explains the mechanical effects (requests paused, timelocks continue, refund runs in background), but it does not clearly communicate the economic implications introduced by spam deterrence.

With v4, the security deposit is not symbolic: it is part of the anti-spam incentive structure.

Suspending a swap may be interpreted as non-cooperative or suspicious behavior. In such cases, the maker may retain part of the security deposit according to the anti-spam design.

At the moment, the dialog presents “Suspend” almost like a neutral pause action.
In reality, it can alter the economic balance of the swap.
Takers have a clear interest in receiving precise information about the consequences of their actions. Transparent communication reduces misunderstandings and protects both sides from unintended outcomes.

Suggestion:

  • Clearly state that the security deposit may be at risk if behavior is considered abusive.
  • Consider requiring an explicit confirmation acknowledging this risk.

If spam deterrence is meant to enforce cooperative behavior, the UI should transparently communicate the consequences of actions that may trigger economic penalties.
Screenshot from 2026-03-02 15-56-53

@pokefan77
Copy link

Consider a protocol-level minimum deposit (e.g. 2%) to avoid competitive distortion entirely.

XMR is very much part of a free market narrative! If a maker doesnt require a deposit then he doesnt require a deposit. If you want to put 50% deposit so be it, but dont force others to require a minimum of 2%.
Some users / takers might not like that idea of a deposit at all. Then they are excluded from the app just because your opinion on a new feature takes over.

Use neutral styling for deposit values (no green/yellow distinction). green = good / preferred yellow = caution / less attractive

Of course 0% deposit is better, so green indicator is just normal UI standarts behing uphold. That a client needs another 5% on top of the trade does warrant a yellow warning indication. I think the devs made the right choice here.
You correctly state that green is prefered and yellow is less attractive. Thats just how it is.

The anti-spam mechanism becomes economically non-binding in practice.
If v4 aims to rebalance incentives and eliminate the free-option dynamic, deposit settings must not create a competitive race toward zero.
If you offer XMR for 0.5% at 2% deposit versus somebody who offers XMR for 3% at 0% deposit in my opinion the user / taker will take the cheaper offer, not necessarily the offer with the lower deposit (what for? its more expensive)

Your arguments seem to be fueled by personal bias.

@Franz-Hermann-GT
Copy link

Franz-Hermann-GT commented Mar 3, 2026

You’re right in principle — in a perfect world, spam deterrence wouldn’t even be necessary.

But it was introduced because repeated abuses showed that the previous equilibrium was not sustainable.

If 0% deposit remains competitively dominant, we risk recreating the very conditions that made this change necessary.

There may be more elegant solutions, but due to current Monero limitations, they are not yet feasible. For now, some compromise is unavoidable.

This is not about restricting freedom. It’s about preventing bad-faith or opportunistic use of the system that contradicts its original cooperative design.
An open-source, community-driven project like this should aim for balanced incentives — not a system where competitive outcomes are driven by loopholes.
Some participants would never consider blocking others’ liquidity or exploiting timing dynamics, regardless of capital. Some clearly would. Unfortunately, the protocol must be designed with the latter in mind.

@Einliterflasche
Copy link

Einliterflasche commented Mar 3, 2026

I want to point out that the "deposit" is not actually a "deposit" in the classical sense. It is a part of the Bitcoin amount that is split out in the case of a refund. On the happy path there is no "deposit" at all.

I know this naming is not entirely intuitive. We're open to suggestions on how to explain this mechanic to users better.

Regarding protocol enforced minimums

This is not technically possible. There is no way to enforce this.

There is no way around the UI distinction: a lower deposit is better for the user.
For the exact UI we are open to suggestions, but we will not trick users into thinking that there is absolutely zero risk involved when deposit > 0.

That said, I would not be too fast in assuming that a capitally strong competitor would allow zero deposit swaps. They would still be at risk to the free-option problem.

We have already laid the ground work for concurrent swaps. Once we do that there is no need for a suspend button, which will at least make such attacks less easy.

We will have to add more explanations to the app and update the documentation. Users who are not familiar with the matter must be able to make an informed choice.

Concrete suggestions towards that end are appreciated.

@Franz-Hermann-GT
Copy link

Thanks for the clarification.

If the mechanism is essentially a portion of the BTC amount that is only split out in the refund case, then the term deposit can indeed be misleading, especially since it does not exist on the happy path.

A more neutral term could be “Refund reserve” or “Refund allocation” which better reflect the mechanism.

Example:
Refund reserve: 3%
or
Refund allocation: 3%

Tooltip:
A small portion of the BTC amount that may be forfeited if the swap ends in a refund.

Of course, the documentation should include a more detailed explanation of the reasons and possible risks.
Concurrent swaps sound like a very good improvement.

@jstark2a
Copy link

jstark2a commented Mar 3, 2026

If a maker offers 0% deposits they are essentially offering free options. Unless the have a very high markup % that prices in the volatility of xmr/btc, they are underpricing their risk. Smart money will drain dumb money.

@jstark2a
Copy link

jstark2a commented Mar 3, 2026

I think the term "cancellation fee" communicates the point the best to takers

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

Labels

Alice Related to the maker state machine Bob Related to the taker state machine enhancement New feature or request protocol

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants