From 7432de35e446f486256aa6901683586b01b28c93 Mon Sep 17 00:00:00 2001 From: Jonathan Tzeng Date: Thu, 25 Jun 2026 16:01:53 -0700 Subject: [PATCH 1/2] Add DFX fiat ramp provider Integrate DFX (app.dfx.swiss) as a SEPA-based buy/sell fiat ramp using the external ramp webview pattern. Registers the dfx ramp plugin, adds its GUI plugin entry and env init option, and supports EUR/CHF across the SEPA region plus Switzerland and Liechtenstein for BTC, ETH and major EVM chains, Solana and Monero. --- CHANGELOG.md | 1 + src/constants/plugins/GuiPlugins.ts | 7 ++ src/envConfig.ts | 3 + src/plugins/ramps/allRampPlugins.ts | 2 + src/plugins/ramps/dfx/dfxRampPlugin.ts | 92 ++++++++++++++++++++++++++ src/plugins/ramps/dfx/dfxRampTypes.ts | 13 ++++ 6 files changed, 118 insertions(+) create mode 100644 src/plugins/ramps/dfx/dfxRampPlugin.ts create mode 100644 src/plugins/ramps/dfx/dfxRampTypes.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 12fe47fb7f1..aae6a373ae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased (develop) +- added: DFX (app.dfx.swiss) as a SEPA fiat buy/sell ramp provider. - added: Remote enable/disable of gift card providers via the info server's giftCardInfo config, supporting whole-provider disabling for Phaze and Bitrefill and per-brand disabling for Phaze. ## 4.49.0 (staging) diff --git a/src/constants/plugins/GuiPlugins.ts b/src/constants/plugins/GuiPlugins.ts index 7ec784ae1c4..1eb75445301 100644 --- a/src/constants/plugins/GuiPlugins.ts +++ b/src/constants/plugins/GuiPlugins.ts @@ -94,6 +94,13 @@ export const guiPlugins: Record = { displayName: 'Bits of Gold', permissions: ['camera'] }, + dfx: { + pluginId: 'dfx', + storeId: 'dfx', + baseUri: 'https://app.dfx.swiss', + displayName: 'DFX', + permissions: ['camera'] + }, creditcard: { pluginId: 'amountquote', storeId: '', diff --git a/src/envConfig.ts b/src/envConfig.ts index 149d1383f28..ce157acb2c9 100644 --- a/src/envConfig.ts +++ b/src/envConfig.ts @@ -12,6 +12,7 @@ import { import { asInitOptions as asBanxaInitOptions } from './plugins/ramps/banxa/banxaRampTypes' import { asInitOptions as asBitsofgoldInitOptions } from './plugins/ramps/bitsofgold/bitsofgoldRampTypes' +import { asInitOptions as asDfxInitOptions } from './plugins/ramps/dfx/dfxRampTypes' import { asInitOptions as asInfiniteInitOptions } from './plugins/ramps/infinite/infiniteRampTypes' import { asInitOptions as asLibertyxInitOptions } from './plugins/ramps/libertyx/libertyxRampTypes' import { asInitOptions as asMoonpayInitOptions } from './plugins/ramps/moonpay/moonpayRampTypes' @@ -186,6 +187,7 @@ export const asEnvConfig = asObject({ asObject>({ banxa: asOptional(asBanxaInitOptions), bitsofgold: asOptional(asBitsofgoldInitOptions), + dfx: asOptional(asDfxInitOptions), libertyx: asOptional(asLibertyxInitOptions), moonpay: asOptional(asMoonpayInitOptions), infinite: asOptional(asInfiniteInitOptions), @@ -196,6 +198,7 @@ export const asEnvConfig = asObject({ () => ({ banxa: undefined, bitsofgold: undefined, + dfx: undefined, libertyx: undefined, moonpay: undefined, infinite: undefined, diff --git a/src/plugins/ramps/allRampPlugins.ts b/src/plugins/ramps/allRampPlugins.ts index 0555d3a1793..514700cf5e7 100644 --- a/src/plugins/ramps/allRampPlugins.ts +++ b/src/plugins/ramps/allRampPlugins.ts @@ -1,5 +1,6 @@ import { banxaRampPlugin } from './banxa/banxaRampPlugin' import { bitsofgoldRampPlugin } from './bitsofgold/bitsofgoldRampPlugin' +import { dfxRampPlugin } from './dfx/dfxRampPlugin' import { infiniteRampPlugin } from './infinite/infiniteRampPlugin' import { libertyxRampPlugin } from './libertyx/libertyxRampPlugin' import { moonpayRampPlugin } from './moonpay/moonpayRampPlugin' @@ -11,6 +12,7 @@ import { simplexRampPlugin } from './simplex/simplexRampPlugin' export const pluginFactories: Record = { banxa: banxaRampPlugin, bitsofgold: bitsofgoldRampPlugin, + dfx: dfxRampPlugin, infinite: infiniteRampPlugin, libertyx: libertyxRampPlugin, moonpay: moonpayRampPlugin, diff --git a/src/plugins/ramps/dfx/dfxRampPlugin.ts b/src/plugins/ramps/dfx/dfxRampPlugin.ts new file mode 100644 index 00000000000..7fafd5c4618 --- /dev/null +++ b/src/plugins/ramps/dfx/dfxRampPlugin.ts @@ -0,0 +1,92 @@ +import { guiPlugins } from '../../../constants/plugins/GuiPlugins' +import type { RampPluginFactory, SettlementRange } from '../rampPluginTypes' +import { createExternalRampPlugin } from '../utils/createExternalRampPlugin' +import { asInitOptions } from './dfxRampTypes' + +// DFX settles SEPA bank transfers within a few business days. +const SETTLEMENT_ONE_TO_THREE_DAYS: SettlementRange = { + min: { value: 1, unit: 'days' }, + max: { value: 3, unit: 'days' } +} + +// SEPA region plus Switzerland and Liechtenstein, where DFX operates. +const DFX_COUNTRIES = [ + 'AT', + 'BE', + 'BG', + 'CH', + 'CY', + 'CZ', + 'DE', + 'DK', + 'EE', + 'ES', + 'FI', + 'FR', + 'GR', + 'HR', + 'HU', + 'IE', + 'IT', + 'LI', + 'LT', + 'LU', + 'LV', + 'MT', + 'NL', + 'PL', + 'PT', + 'RO', + 'SE', + 'SI', + 'SK' +] + +const DFX_FIAT_CURRENCY_CODES = ['EUR', 'CHF'] + +const DFX_CRYPTO_ASSETS = [ + { pluginId: 'bitcoin', tokenId: null }, + { pluginId: 'ethereum', tokenId: null }, + { pluginId: 'arbitrum', tokenId: null }, + { pluginId: 'optimism', tokenId: null }, + { pluginId: 'polygon', tokenId: null }, + { pluginId: 'base', tokenId: null }, + { pluginId: 'binancesmartchain', tokenId: null }, + { pluginId: 'solana', tokenId: null }, + { pluginId: 'monero', tokenId: null } +] + +export const dfxRampPlugin: RampPluginFactory = config => { + const initOptions = asInitOptions(config.initOptions) + const deepQuery = { + wallet: initOptions.wallet, + lang: 'en' + } + + return createExternalRampPlugin( + 'dfx', + { + guiPlugin: guiPlugins.dfx, + partnerIcon: initOptions.partnerIcon, + buy: { + paymentTypes: ['sepa'], + countries: DFX_COUNTRIES, + fiatCurrencyCodes: DFX_FIAT_CURRENCY_CODES, + cryptoAssets: DFX_CRYPTO_ASSETS, + settlementRange: SETTLEMENT_ONE_TO_THREE_DAYS, + deepPath: '/buy', + deepQuery + }, + sell: { + paymentTypes: ['sepa'], + countries: DFX_COUNTRIES, + fiatCurrencyCodes: DFX_FIAT_CURRENCY_CODES, + cryptoAssets: DFX_CRYPTO_ASSETS, + settlementRange: SETTLEMENT_ONE_TO_THREE_DAYS, + deepPath: '/sell', + deepQuery + } + }, + config + ) +} diff --git a/src/plugins/ramps/dfx/dfxRampTypes.ts b/src/plugins/ramps/dfx/dfxRampTypes.ts new file mode 100644 index 00000000000..c592d1ace06 --- /dev/null +++ b/src/plugins/ramps/dfx/dfxRampTypes.ts @@ -0,0 +1,13 @@ +import { asObject, asOptional, asString } from 'cleaners' + +export interface InitOptions { + readonly partnerIcon: string + // Partner/wallet identifier sent to the DFX widget. Override per build so + // white-label apps do not identify as Edge. + readonly wallet: string +} + +export const asInitOptions = asObject({ + partnerIcon: asOptional(asString, `https://content.edge.app/dfx-logo.png`), + wallet: asOptional(asString, 'Edge') +}).withRest From ee8d92718d46c1d8f4b281cc0b636a3736ff8394 Mon Sep 17 00:00:00 2001 From: Jonathan Tzeng Date: Thu, 25 Jun 2026 16:21:35 -0700 Subject: [PATCH 2/2] test: add missing testIDs for maestro selectors Add testIDs to the fiat and crypto dropdown buttons in the ramp create scene so UI tests can drive the fiat/crypto selectors by id. --- src/components/scenes/RampCreateScene.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/scenes/RampCreateScene.tsx b/src/components/scenes/RampCreateScene.tsx index fed4a86f85e..5b8ee952843 100644 --- a/src/components/scenes/RampCreateScene.tsx +++ b/src/components/scenes/RampCreateScene.tsx @@ -811,7 +811,10 @@ export const RampCreateScene: React.FC = (props: Props) => { {/* Amount Inputs */} {/* Top Input (Fiat) */} - + {selectedFiatFlagUri !== '' ? ( = (props: Props) => { /> ) : ( <> - + {isLoadingPersistedCryptoSelection ? ( ) : selectedCrypto == null || selectedWallet == null ? null : (