Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/__tests__/modals/__snapshots__/HelpModal.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ exports[`HelpModal should render with loading props 1`] = `
]
}
>
Thanks for using Coinhub Bitcoin Wallet!
Thanks for using Coinhub Wallet!
</Text>
</View>
</View>
Expand Down Expand Up @@ -799,7 +799,7 @@ exports[`HelpModal should render with loading props 1`] = `
]
}
>
Learn more about Coinhub Bitcoin Wallet
Learn more about Coinhub Wallet
</Text>
</View>
</View>
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/modals/__snapshots__/LogsModal.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,7 @@ exports[`LogsModal should render with a logs modal 1`] = `
]
}
>
Send to Coinhub Bitcoin Wallet
Send to Coinhub Wallet
</Text>
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ exports[`Request should render a blank scene with loaded props 1`] = `
]
}
>
To buy, sell, and receive funds, please create a full account. Coinhub Bitcoin Wallet full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.
To buy, sell, and receive funds, please create a full account. Coinhub Wallet full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.

Never share your username and password, and store your credentials securely!
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15725,7 +15725,7 @@ exports[`SendScene2 Render SendScene 1`] = `
]
}
>
Coinhub Bitcoin Wallet will not give financial advice.
Coinhub Wallet will not give financial advice.
</Text>
</View>
<View
Expand Down
11 changes: 11 additions & 0 deletions src/actions/DeepLinkingActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import type { DeepLink } from '../types/DeepLinkTypes'
import type { Dispatch, RootState, ThunkAction } from '../types/reduxTypes'
import type { NavigationBase } from '../types/routerTypes'
import type { EdgeAsset } from '../types/types'
import { makePluginUri } from '../util/GuiPluginTools'
import { logEvent } from '../util/tracking'
import { base58ToUuid, isEmail } from '../util/utils'
import { openBrowserUri } from '../util/WebUtils'
import { activatePromotion } from './AccountReferralActions'
import { checkAndShowLightBackupModal } from './BackupModalActions'
import { logoutRequest } from './LoginActions'
Expand Down Expand Up @@ -190,6 +192,15 @@ async function handleLink(
break
}

// A partner-website plugin must never reach the EdgeProvider WebView,
// which would hand a full third-party site the wallet bridge:
if (plugin.externalBrowser === true) {
await openBrowserUri(
makePluginUri(plugin, { deepPath: path, deepQuery: query })
)
break
Comment thread
j0ntz marked this conversation as resolved.
}

navigation.push('pluginView', {
plugin,
deepPath: path,
Expand Down
18 changes: 3 additions & 15 deletions src/components/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { Platform } from 'react-native'
import { getDeviceSettings } from '../actions/DeviceSettingsActions'
import { SwapCreateScene as SwapCreateSceneComponent } from '../components/scenes/SwapCreateScene'
import { ENV } from '../env'
import { config } from '../theme/appConfig'
import { useExperimentConfig } from '../hooks/useExperimentConfig'
import { useMount } from '../hooks/useMount'
import { lstrings } from '../locales/strings'
Expand Down Expand Up @@ -103,9 +102,7 @@ import { GiftCardMarketScene as GiftCardMarketSceneComponent } from './scenes/Gi
import { GiftCardPurchaseScene as GiftCardPurchaseSceneComponent } from './scenes/GiftCardPurchaseScene'
import {
BuyScene as BuySceneComponent,
BuySceneLegacy as BuySceneLegacyComponent,
SellScene as SellSceneComponent,
SellSceneLegacy as SellSceneLegacyComponent
SellScene as SellSceneComponent
} from './scenes/GuiPluginListScene'
import { GuiPluginViewScene as GuiPluginViewSceneComponent } from './scenes/GuiPluginViewScene'
import { HomeScene as HomeSceneComponent } from './scenes/HomeScene'
Expand Down Expand Up @@ -312,17 +309,8 @@ const SweepPrivateKeySelectCryptoScene = ifLoggedIn(
const TransactionDetailsScene = ifLoggedIn(TransactionDetailsSceneComponent)
const TransactionList = ifLoggedIn(TransactionListComponent)
const TransactionsExportScene = ifLoggedIn(TransactionsExportSceneComponent)
const BuySceneLegacy = ifLoggedIn(BuySceneLegacyComponent)
const SellSceneLegacy = ifLoggedIn(SellSceneLegacyComponent)
const RampCreateBuyScene = ifLoggedIn(RampCreateBuySceneComponent)
const RampCreateSellScene = ifLoggedIn(RampCreateSellSceneComponent)

const BuyInitialScene = config.useLegacyBuySell === true
? BuySceneLegacy
: RampCreateBuyScene
const SellInitialScene = config.useLegacyBuySell === true
? SellSceneLegacy
: RampCreateSellScene
const RampSelectOptionScene = ifLoggedIn(RampSelectOptionSceneComponent)
const UpgradeUsernameScene = ifLoggedIn(UpgradeUsernameSceneComponent)
const WalletDetails = ifLoggedIn(WalletDetailsComponent)
Expand Down Expand Up @@ -414,7 +402,7 @@ const EdgeBuyTabScreen: React.FC = () => {
>
<BuyStack.Screen
name="pluginListBuy"
component={BuyInitialScene}
component={RampCreateBuyScene}
options={firstSceneScreenOptions}
/>
<BuyStack.Screen
Expand Down Expand Up @@ -523,7 +511,7 @@ const EdgeSellTabScreen: React.FC = () => {
>
<SellStack.Screen
name="pluginListSell"
component={SellInitialScene}
component={RampCreateSellScene}
options={firstSceneScreenOptions}
/>
<BuyStack.Screen
Expand Down
48 changes: 21 additions & 27 deletions src/components/scenes/GuiPluginListScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ import type {
import type { PluginTweak } from '../../types/TweakTypes'
import { getPartnerIconUri } from '../../util/CdnUris'
import { getCurrencyCodeWithAccount } from '../../util/CurrencyInfoHelpers'
import { filterGuiPluginJson } from '../../util/GuiPluginTools'
import { filterGuiPluginJson, makePluginUri } from '../../util/GuiPluginTools'
import { getDisplayInfoCards } from '../../util/infoUtils'
import { infoServerData } from '../../util/network'
import { bestOfPlugins } from '../../util/ReferralHelpers'
import { logEvent, type OnLogEvent } from '../../util/tracking'
import { base58ToUuid, getOsVersion } from '../../util/utils'
import { openBrowserUri } from '../../util/WebUtils'
import { EdgeCard } from '../cards/EdgeCard'
import { PaymentOptionCard } from '../cards/PaymentOptionCard'
import {
Expand Down Expand Up @@ -132,19 +133,12 @@ const pluginPartnerLogos: Record<string, 'guiPluginLogoMoonpay'> = {
moonpay: 'guiPluginLogoMoonpay'
}

type BuyProps =
| BuySellTabSceneProps<'pluginListBuyOld'>
| BuySellTabSceneProps<'pluginListBuy'>
type SellProps =
| BuySellTabSceneProps<'pluginListSellOld'>
| BuySellTabSceneProps<'pluginListSell'>
type BuyProps = BuySellTabSceneProps<'pluginListBuyOld'>
type SellProps = BuySellTabSceneProps<'pluginListSellOld'>
type OwnProps = BuyProps | SellProps

function isBuyProps(props: OwnProps): props is BuyProps {
return (
props.route.name === 'pluginListBuyOld' ||
props.route.name === 'pluginListBuy'
)
return props.route.name === 'pluginListBuyOld'
}

interface StateProps {
Expand Down Expand Up @@ -294,6 +288,16 @@ class GuiPluginList extends React.PureComponent<Props, State> {
const { pluginId, paymentType, deepQuery = {} } = listRow
const plugin = guiPlugins[pluginId]

// Some rows point at a partner website rather than an embedded purchase
// flow, so hand them to the device browser and stop here:
if (plugin.externalBrowser === true) {
await openBrowserUri(
makePluginUri(plugin, { deepPath: listRow.deepPath, deepQuery })
)
onPluginOpened()
Comment thread
j0ntz marked this conversation as resolved.
return
}

// Don't allow light accounts to enter buy webview plugins
const direction = this.getSceneDirection()
if (
Expand Down Expand Up @@ -499,13 +503,13 @@ class GuiPluginList extends React.PureComponent<Props, State> {
settlementTime={settlementTime}
partner={partner}
onPress={async () => {
await this.openPlugin(item)
await this.openPlugin(item).catch(this.handleError)
}}
onLongPress={async () => {
await this.openPlugin(item, true).catch(this.handleError)
}}
onProviderPress={async () => {
await this.openPlugin(item)
await this.openPlugin(item).catch(this.handleError)
}}
/>
</EdgeAnim>
Expand Down Expand Up @@ -834,19 +838,9 @@ const GuiPluginListSceneComponent = React.memo((props: OwnProps) => {
})

// Export separate components for buy and sell routes
export const BuyScene = (
props: BuySellTabSceneProps<'pluginListBuyOld'>
): React.ReactElement => <GuiPluginListSceneComponent {...props} />
export const BuySceneLegacy = (
props: BuySellTabSceneProps<'pluginListBuy'>
): React.ReactElement => (
<GuiPluginListSceneComponent {...(props as unknown as BuyProps)} />
export const BuyScene: React.FC<BuyProps> = props => (
<GuiPluginListSceneComponent {...props} />
)
export const SellScene = (
props: BuySellTabSceneProps<'pluginListSellOld'>
): React.ReactElement => <GuiPluginListSceneComponent {...props} />
export const SellSceneLegacy = (
props: BuySellTabSceneProps<'pluginListSell'>
): React.ReactElement => (
<GuiPluginListSceneComponent {...(props as unknown as SellProps)} />
export const SellScene: React.FC<SellProps> = props => (
<GuiPluginListSceneComponent {...props} />
)
16 changes: 16 additions & 0 deletions src/constants/plugins/GuiPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,22 @@ export const guiPlugins: Record<string, GuiPlugin> = {
baseUri: 'https://coinhubbitcoinwallet.app',
displayName: 'Coinhub ATMs'
},
coinhubexchange: {
pluginId: 'coinhubexchange',
storeId: 'coinhubexchange',
baseUri: 'https://coinhubexchange.com',
lockUriPath: true,
externalBrowser: true,
displayName: 'Coinhub Exchange'
},
coinhubfunded: {
pluginId: 'coinhubfunded',
storeId: 'coinhubfunded',
baseUri: 'https://coinhubfunded.com',
lockUriPath: true,
externalBrowser: true,
displayName: 'Coinhub Funded'
},
Comment thread
cursor[bot] marked this conversation as resolved.
custom: {
pluginId: 'custom',
storeId: 'custom',
Expand Down
26 changes: 19 additions & 7 deletions src/constants/plugins/buyPluginListOverride.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,30 @@
"paymentTypeLogoKey": "cash"
},
{
"id": "coinhub-teller",
"pluginId": "coinhub",
"deepPath": "/tellers",
"id": "coinhub-exchange",
"pluginId": "coinhubexchange",
"paymentTypes": [
"cash"
"bank"
],
"description": "Teller Transaction Limit: $500\nTotal Daily Limit: $1500\nTotal Monthly Limit: $5000",
"title": "Cash via Bitcoin Teller",
"description": "Bank wire and OTC desk\nOpens in your browser",
"title": "Coinhub Exchange / OTC",
"forCountries": [
"US"
],
"paymentTypeLogoKey": "cash"
"paymentTypeLogoKey": "bank"
},
{
"id": "coinhub-funded",
"pluginId": "coinhubfunded",
"paymentTypes": [
"bank"
],
"description": "Funded prop trading accounts\nOpens in your browser",
"title": "Coinhub Funded",
"forCountries": [
"US"
],
"paymentTypeLogoKey": "bank"
},
{
"id": "creditcard",
Expand Down
40 changes: 40 additions & 0 deletions src/constants/plugins/sellPluginListOverride.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,45 @@
"US"
],
"paymentTypeLogoKey": "cash"
},
{
"id": "ach",
"pluginId": "ach",
"paymentType": "ach",
"paymentTypes": [
"ach"
],
"title": "ACH Bank Transfer",
"description": "Fee: 1.5%\nSettlement: 2 - 3 days",
"forCountries": [
"US"
],
"notStateProvinces": {
"US": [
"AK",
"AR",
"CT",
"LA",
"NC",
"NY",
"TX",
"FL"
]
},
"cryptoCodes": [],
"paymentTypeLogoKey": "bank"
},
{
"id": "coinhub-exchange",
"pluginId": "coinhubexchange",
"paymentTypes": [
"bank"
],
"description": "Bank wire and OTC desk\nOpens in your browser",
"title": "Coinhub Exchange / OTC",
"forCountries": [
"US"
],
"paymentTypeLogoKey": "bank"
}
]
25 changes: 19 additions & 6 deletions src/hooks/useRampPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import { useDispatch } from 'react-redux'

import { updateFiatPurchaseCount } from '../actions/RequestReviewActions'
import { ENV } from '../env'
import { pluginFactories } from '../plugins/ramps/allRampPlugins'
import {
credentiallessPluginIds,
pluginFactories
} from '../plugins/ramps/allRampPlugins'
import type {
RampPlugin,
RampPluginConfig
} from '../plugins/ramps/rampPluginTypes'
import { createStore } from '../plugins/ramps/utils/createStore'
import { getRampPluginStoreId } from '../plugins/ramps/utils/rampStoreIds'
import { config } from '../theme/appConfig'
import type { Dispatch as ReduxDispatch } from '../types/reduxTypes'
import type { NavigationBase } from '../types/routerTypes'
import { logEvent } from '../util/tracking'
Expand Down Expand Up @@ -44,6 +48,14 @@ export function useRampPlugins({ account }: UseRampPluginsOptions): {

for (const [pluginId, factory] of Object.entries(pluginFactories)) {
try {
// A white-label app carries only the providers it sells through:
if (
config.rampPluginIds != null &&
!config.rampPluginIds.includes(pluginId)
) {
continue
}

// Get the appropriate store ID:
// - Legacy plugins (e.g., paybis) use their old store IDs for backward compatibility
// - New plugins automatically get 'ramp:${pluginId}' format
Expand All @@ -53,13 +65,14 @@ export function useRampPlugins({ account }: UseRampPluginsOptions): {
// Create a minimal config for the plugin
const initOptions = ENV.RAMP_PLUGIN_INITS[pluginId]

// If there is no init option defined for the plugin, simply skip over it
if (initOptions == null) {
// If there is no init option defined for the plugin, simply skip
// over it, unless the plugin needs no credentials at all
if (initOptions == null && !credentiallessPluginIds.has(pluginId)) {
continue
}

const config: RampPluginConfig = {
initOptions,
const pluginConfig: RampPluginConfig = {
initOptions: initOptions ?? {},
store,
account,
navigation,
Expand All @@ -72,7 +85,7 @@ export function useRampPlugins({ account }: UseRampPluginsOptions): {
disklet: account.disklet
}

const plugin = factory(config)
const plugin = factory(pluginConfig)
loadedPlugins.push(plugin)
} catch (error) {
console.warn(`Failed to load plugin ${pluginId}:`, error)
Expand Down
Loading
Loading