Skip to content

Recognize exchange-only promotions in the swap settings - #6221

Open
peachbits wants to merge 1 commit into
developfrom
matthew/swap-promo-source-gate
Open

peachbits wants to merge 1 commit into
developfrom
matthew/swap-promo-source-gate

Conversation

@peachbits

@peachbits peachbits commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

The swap settings have a promotion-controlled mode: when a promotion is choosing the preferred exchange they say "the current promotion always prefers: …", replace the tappable list with the promoted provider, and offer a delete row that removes the promotion. The gate in bestOfPlugins that decides whether a promotion is the source tested fromPromo.preferredFiatPluginId != null, so a promotion that only sets an exchange preference never triggered it (ReferralHelpers.ts:102-106). The settings kept showing the normal list, a tap on another provider was saved but silently overridden for the promotion's whole window, and the only way out was the promotion settings screen. This is reproducible on 4.51 with a promotion of { pluginId: "nexchange", preferredSwap: true, durationDays: 14 }: the list highlights n.exchange, tapping Exolix appears to do nothing, and Exolix appears only after the promotion is removed elsewhere.

Test the swap preference instead of the fiat one. The second half of the condition stays: with no exchange preference anywhere, undefined === undefined, and a buy/sell-only promotion would otherwise claim the source.

Not changed here: the account-referral clause two lines up has the same undefined-equality shape, so an account with no referral preference reports account as its source rather than settings. Its only effect is a harmless ignoreAccountSwap write when the user picks a provider, so it's left alone.

bestOfPlugins had no tests. Added five covering: a promotion outranking the settings preference; an exchange-only promotion reporting itself as the source; a promotion with both preferences still doing so; a buy/sell-only promotion not claiming the source; and an expired promotion being ignored. Full suite and tsc pass.

Related: #6220 makes the preferred provider's quote the selected one; this makes it visible and removable when a promotion chose it.


Note

Low Risk
Small logic fix in referral merge helper with targeted tests; behavior change is limited to swap settings display and promotion removal when only swap is promoted.

Overview
Fixes swap settings when a promotion only sets a preferred exchange (not buy/sell). Those promotions were treated like normal user choice: the provider list stayed tappable, saves looked successful but the promotion kept overriding until it was removed elsewhere.

In bestOfPlugins, attribution of swapSource to a promotion now checks that the promotion actually set preferredSwapPluginId, instead of requiring a fiat preference. That unlocks the existing promotion-controlled UI (message, single promoted provider, remove-promotion row).

Adds unit tests for bestOfPlugins covering promotion vs settings ranking, exchange-only and dual preferences, buy/sell-only promos not claiming swap source, and expired promotions.

Reviewed by Cursor Bugbot for commit d661a7f. Bugbot is set up for automated code reviews on this repo. Configure here.


@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

The swap settings switch to a promotion-controlled mode when a
promotion is choosing the preferred exchange: they say so, replace the
tappable list with the promoted provider, and offer to remove the
promotion. The gate that decides this asked whether the promotion set a
preferred *fiat* plugin, so a promotion that only set an exchange
preference never triggered it. The settings kept showing the normal
list, a tap on another provider was saved but silently overridden for
the promotion's whole window, and the only way out was the promotion
settings screen.

Test the swap preference instead. The second half of the condition is
still needed: with no exchange preference anywhere, undefined equals
undefined, and a buy/sell-only promotion would otherwise claim the
source. bestOfPlugins had no tests, so add the cases that pin this.
@peachbits
peachbits force-pushed the matthew/swap-promo-source-gate branch from 0b99612 to d661a7f Compare September 24, 2026 20:02

@j0ntz j0ntz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix looks right, and the tests cover the cases that matter.

Optional, on the account clause at src/util/ReferralHelpers.ts:93 that the description leaves out: the ignoreAccountSwap write isn't entirely harmless. When neither the account referral nor the settings prefer an exchange, swapSource reads account, so picking an exchange in Swap Settings saves ignoreAccountSwap: true permanently. If the installer's referral config later adds a swap preference, it gets silently ignored. Adding the same fromAccount.preferredSwapPluginId != null check would close that, here or in a follow-up.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants