diff --git a/CHANGELOG.md b/CHANGELOG.md index 597e7c137fb..eff25fb8bb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - added: Logbox disable option to env.json - added: Reverse-resolve recipient addresses to ENS / Unstoppable Domains / ZNS names in the send flow, address modal, and transaction history. - added: Warning confirmation when sending a zero amount on EVM chains, since the transaction still spends gas. +- 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. - added: Exchange deep links (`edge://exchange/buy|sell|swap`) that open the flow with the asset pre-selected - added: Promo attribution for buy, sell and swap flows opened from deep links and promo cards 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/docs/ramps-deeplink-provider-priority.md b/src/docs/ramps-deeplink-provider-priority.md index 0317ac94321..4a403297cef 100644 --- a/src/docs/ramps-deeplink-provider-priority.md +++ b/src/docs/ramps-deeplink-provider-priority.md @@ -373,7 +373,7 @@ The ordering and the badge are each simple rules, but their product across the i Both blocks below are cards inside the `promoCards2` array of the [info server](#info-server) rollup document in the `info_data` CouchDB database. `promoCards2` is a healing array, so a card that fails the cleaner is dropped silently and the rest still load. `background` is required on every card, including the silent one. -[Ramp plugin](#ramp-plugin) ids usable in `preferProviders`: `banxa`, `bitsofgold`, `infinite`, `libertyx`, `moonpay`, `paybis`, `revolut`, `simplex`. +[Ramp plugin](#ramp-plugin) ids usable in `preferProviders`: `banxa`, `bitsofgold`, `dfx`, `infinite`, `libertyx`, `moonpay`, `paybis`, `revolut`, `simplex`. ### 6.1 Visible card with a deep link call-to-action 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..fa4e1d75628 --- /dev/null +++ b/src/plugins/ramps/dfx/dfxRampPlugin.ts @@ -0,0 +1,89 @@ +import { guiPlugins } from '../../../constants/plugins/GuiPlugins' +import type { RampPluginFactory, SettlementRange } from '../rampPluginTypes' +import { + createExternalRampPlugin, + type DirectionSupportData +} 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' } +} + +// EEA countries plus Switzerland where DFX accepts bank transfers. Bulgaria +// is left out: DFX's country list (api.dfx.swiss/v1/country) disallows IBAN, +// bank and KYC there. +const DFX_COUNTRIES = [ + 'AT', + 'BE', + 'CH', + 'CY', + 'CZ', + 'DE', + 'DK', + 'EE', + 'ES', + 'FI', + 'FR', + 'GR', + 'HR', + 'HU', + 'IE', + 'IS', + 'IT', + 'LI', + 'LT', + 'LU', + 'LV', + 'MT', + 'NL', + 'NO', + '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 support: Omit = { + paymentTypes: ['sepa'], + countries: DFX_COUNTRIES, + fiatCurrencyCodes: DFX_FIAT_CURRENCY_CODES, + cryptoAssets: DFX_CRYPTO_ASSETS, + settlementRange: SETTLEMENT_ONE_TO_THREE_DAYS, + deepQuery: { + wallet: initOptions.wallet, + lang: 'en' + } + } + + return createExternalRampPlugin( + 'dfx', + { + guiPlugin: guiPlugins.dfx, + partnerIcon: initOptions.partnerIcon, + buy: { ...support, deepPath: '/buy' }, + sell: { ...support, deepPath: '/sell' } + }, + 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