Skip to content
4 changes: 2 additions & 2 deletions src/components/scenes/RampSelectOptionScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const RampSelectOptionScene: React.FC<Props> = (props: Props) => {
selectedCrypto:
rampQuoteRequest.wallet != null
? {
pluginId: rampQuoteRequest.pluginId,
pluginId: rampQuoteRequest.wallet.currencyInfo.pluginId,
tokenId: rampQuoteRequest.tokenId
}
: undefined,
Expand Down Expand Up @@ -96,7 +96,7 @@ export const RampSelectOptionScene: React.FC<Props> = (props: Props) => {
const handleQuotePress = useHandler(
async (quote: RampQuote): Promise<void> => {
await quote.approveQuote({
coreWallet: rampQuoteRequest.wallet!
coreWallet: rampQuoteRequest.wallet
})
}
)
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/gui/fiatPluginTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const asFiatPaymentType = asValue(
'directtobank',
'fasterpayments',
'googlepay',
// TODO: Remove `iach` from the variants when old fiat plugin code has been gutted.
/** @deprecated because Instant ACH isn't different from regular ACH. Use `ach` instead. */
'iach',
'ideal',
'interac',
Expand Down
30 changes: 29 additions & 1 deletion src/plugins/ramps/banxa/banxaRampPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ import type {
RampQuoteRequest,
RampSupportResult
} from '../rampPluginTypes'
import {
validateRampCheckSupportRequest,
validateRampQuoteRequest
} from '../utils/constraintUtils'
import { getSettlementRange } from '../utils/getSettlementRange'
import { openExternalWebView } from '../utils/webViewUtils'
import { asInitOptions } from './banxaRampTypes'
Expand Down Expand Up @@ -819,6 +823,22 @@ export const banxaRampPlugin: RampPluginFactory = (
const config = await fetchProviderConfig()
const { allowedCountryCodes, allowedCurrencyCodes } = config

// Get supported payment types for this direction
const supportedPaymentTypes = Object.keys(
allowedPaymentTypes[request.direction]
).filter(
pt =>
allowedPaymentTypes[request.direction][pt as FiatPaymentType] === true
) as FiatPaymentType[]

// Global constraints pre-check
const constraintOk = validateRampCheckSupportRequest(
pluginId,
request,
supportedPaymentTypes
)
if (!constraintOk) return { supported: false }

// Check region support
if (!isRegionSupported(request.regionCode, allowedCountryCodes)) {
return { supported: false }
Expand Down Expand Up @@ -864,11 +884,11 @@ export const banxaRampPlugin: RampPluginFactory = (
direction,
regionCode,
amountType,
pluginId: currencyPluginId,
fiatCurrencyCode,
displayCurrencyCode,
tokenId
} = request
const currencyPluginId = request.wallet.currencyInfo.pluginId

const isMaxAmount =
typeof request.exchangeAmount === 'object' && request.exchangeAmount.max
Expand Down Expand Up @@ -936,6 +956,14 @@ export const banxaRampPlugin: RampPluginFactory = (
const quotes: RampQuote[] = []

for (const paymentType of supportedPaymentTypes) {
// Constraints per request
const constraintOk = validateRampQuoteRequest(
pluginId,
request,
paymentType
)
if (!constraintOk) continue

try {
// Find payment method for this type
let paymentObj: BanxaPaymentIdLimit | undefined
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/ramps/bity/bityRampPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,10 +679,10 @@ export const bityRampPlugin = (pluginConfig: RampPluginConfig): RampPlugin => {
direction,
fiatCurrencyCode,
regionCode,
pluginId: currencyPluginId,
tokenId,
displayCurrencyCode
} = request
const currencyPluginId = request.wallet.currencyInfo.pluginId
const isBuy = direction === 'buy'

const isMaxAmount =
Expand Down
46 changes: 30 additions & 16 deletions src/plugins/ramps/moonpay/moonpayRampPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ import type {
RampQuoteRequest,
RampSupportResult
} from '../rampPluginTypes'
import {
validateRampCheckSupportRequest,
validateRampQuoteRequest
} from '../utils/constraintUtils'
import { getSettlementRange } from '../utils/getSettlementRange'
import { openExternalWebView } from '../utils/webViewUtils'
import {
Expand Down Expand Up @@ -455,11 +459,6 @@ export const moonpayRampPlugin: RampPluginFactory = (
const config = await fetchProviderConfig()
const { allowedCountryCodes, allowedCurrencyCodes } = config

// Check region support
if (!isRegionSupported(regionCode, direction, allowedCountryCodes)) {
return { supported: false }
}

// Get supported payment methods
const supportedMethods = getSupportedPaymentMethods(
direction,
Expand All @@ -469,6 +468,21 @@ export const moonpayRampPlugin: RampPluginFactory = (
return { supported: false }
}

// Get supported payment methods
const paymentTypes = supportedMethods.map(method => method.paymentType)
// Global constraints pre-check
const constraintOk = validateRampCheckSupportRequest(
pluginId,
request,
paymentTypes
)
if (!constraintOk) return { supported: false }

// Check region support
if (!isRegionSupported(regionCode, direction, allowedCountryCodes)) {
return { supported: false }
}

// Check support across all payment methods
for (const { assetMap } of supportedMethods) {
// Check crypto support
Expand Down Expand Up @@ -534,7 +548,7 @@ export const moonpayRampPlugin: RampPluginFactory = (
}

// Build list of payment methods that support both fiat and crypto
let methodCandidates: Array<{
const methodCandidates: Array<{
paymentType: FiatPaymentType
paymentMethod: MoonpayPaymentMethod
assetMap: AssetMap
Expand All @@ -544,7 +558,7 @@ export const moonpayRampPlugin: RampPluginFactory = (

for (const method of supportedPaymentMethods) {
const cryptoSupported = isCryptoSupported(
request.pluginId,
request.wallet.currencyInfo.pluginId,
request.tokenId,
method.assetMap,
regionCode
Expand All @@ -554,6 +568,13 @@ export const moonpayRampPlugin: RampPluginFactory = (
const fiatSupported = isFiatSupported(fiatCurrencyCode, method.assetMap)
if (fiatSupported == null) continue

const constraintOk = validateRampQuoteRequest(
pluginId,
request,
method.paymentType
)
if (!constraintOk) continue

methodCandidates.push({
paymentType: method.paymentType,
paymentMethod: method.paymentMethod,
Expand All @@ -563,13 +584,6 @@ export const moonpayRampPlugin: RampPluginFactory = (
})
}

// Venmo payment method is only supported in the USA
if (regionCode.countryCode !== 'US') {
methodCandidates = methodCandidates.filter(
method => method.paymentType !== 'venmo'
)
}

// If no payment method supports both crypto and fiat, throw error
if (methodCandidates.length === 0) {
throw new FiatProviderError({
Expand All @@ -581,8 +595,8 @@ export const moonpayRampPlugin: RampPluginFactory = (
const displayFiatCurrencyCode = removeIsoPrefix(fiatCurrencyCode)
const fiatCode = removeIsoPrefix(fiatCurrencyCode).toLowerCase()
const walletAddress = (
await request.wallet?.getAddresses({ tokenId: null })
)?.[0]?.publicAddress
await request.wallet.getAddresses({ tokenId: null })
)[0].publicAddress
const walletAddressParam =
walletAddress == null ? '' : `&walletAddress=${walletAddress}`

Expand Down
45 changes: 34 additions & 11 deletions src/plugins/ramps/paybis/paybisRampPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ import type {
RampQuoteRequest,
RampSupportResult
} from '../rampPluginTypes'
import {
validateRampCheckSupportRequest,
validateRampQuoteRequest
} from '../utils/constraintUtils'
import { getSettlementRange } from '../utils/getSettlementRange'
import { asInitOptions } from './paybisRampTypes'

Expand All @@ -95,7 +99,7 @@ type AllowedPaymentTypes = Record<

const allowedPaymentTypes: AllowedPaymentTypes = {
buy: {
iach: true,
ach: true,
applepay: true,
credit: true,
googlepay: true,
Expand All @@ -105,7 +109,7 @@ const allowedPaymentTypes: AllowedPaymentTypes = {
spei: true
},
sell: {
iach: true,
ach: true,
colombiabank: true,
credit: true,
mexicobank: true,
Expand Down Expand Up @@ -334,7 +338,7 @@ const EDGE_TO_PAYBIS_CURRENCY_MAP: StringMap = Object.entries(
}, {})

const PAYMENT_METHOD_MAP: Record<PaymentMethodId, FiatPaymentType> = {
'method-id-trustly': 'iach',
'method-id-trustly': 'ach',
'method-id-credit-card': 'credit',
'method-id-credit-card-out': 'credit',
'method-id_bridgerpay_revolutpay': 'revolut',
Expand All @@ -351,7 +355,7 @@ const PAYMENT_METHOD_MAP: Record<PaymentMethodId, FiatPaymentType> = {
const REVERSE_PAYMENT_METHOD_MAP: Partial<
Record<FiatPaymentType, PaymentMethodId>
> = {
iach: 'method-id-trustly',
ach: 'method-id-trustly',
applepay: 'method-id-credit-card',
credit: 'method-id-credit-card',
googlepay: 'method-id-credit-card',
Expand Down Expand Up @@ -685,6 +689,20 @@ export const paybisRampPlugin: RampPluginFactory = (
): Promise<RampSupportResult> => {
const { direction, regionCode, fiatAsset, cryptoAsset } = request

const allPaymentTypes = Object.keys(
allowedPaymentTypes[direction]
).filter(
key => allowedPaymentTypes[direction][key as FiatPaymentType] === true
) as FiatPaymentType[]

// Global constraints pre-check
const constraintOk = validateRampCheckSupportRequest(
pluginId,
request,
allPaymentTypes
)
if (!constraintOk) return { supported: false }

// Ensure assets are initialized for the direction
await ensureAssetsInitialized(direction)

Expand Down Expand Up @@ -721,13 +739,13 @@ export const paybisRampPlugin: RampPluginFactory = (
const {
amountType,
regionCode,
pluginId: currencyPluginId,
promoCode: maybePromoCode,
fiatCurrencyCode,
displayCurrencyCode,
direction,
tokenId
} = request
const currencyPluginId = request.wallet.currencyInfo.pluginId

const isMaxAmount =
typeof request.exchangeAmount === 'object' && request.exchangeAmount.max
Expand All @@ -743,15 +761,12 @@ export const paybisRampPlugin: RampPluginFactory = (
await ensureAssetsInitialized(direction)

// Get all supported payment types for the direction
let allPaymentTypes = Object.keys(allowedPaymentTypes[direction]).filter(
const allPaymentTypes = Object.keys(
allowedPaymentTypes[direction]
).filter(
key => allowedPaymentTypes[direction][key as FiatPaymentType] === true
) as FiatPaymentType[]

// Filter out credit for sell in US
if (direction === 'sell' && regionCode.countryCode === 'US') {
allPaymentTypes = allPaymentTypes.filter(pt => pt !== 'credit')
}

if (allPaymentTypes.length === 0) {
// Return empty array if no payment types supported
return []
Expand Down Expand Up @@ -798,6 +813,14 @@ export const paybisRampPlugin: RampPluginFactory = (

// Get quote for each supported payment type
for (const paymentType of allPaymentTypes) {
// Constraints per request
const constraintOk = validateRampQuoteRequest(
pluginId,
request,
paymentType
)
if (!constraintOk) continue

try {
const paymentMethod =
direction === 'buy'
Expand Down
78 changes: 78 additions & 0 deletions src/plugins/ramps/rampConstraints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type {
FiatPaymentType,
FiatPluginRegionCode
} from '../gui/fiatPluginTypes'
import type { CryptoAsset } from './rampPluginTypes'

export interface RampConstraintParams {
rampPluginId: string
cryptoAsset: CryptoAsset
fiatCurrencyCode: string
direction: 'buy' | 'sell'
regionCode: FiatPluginRegionCode
paymentType: FiatPaymentType
}

export function validateRampConstraintParams(
params: RampConstraintParams
): boolean {
for (const constraint of constraintGenerator(params)) {
if (!constraint) {
return false
}
}
return true
}

export function* constraintGenerator(
params: RampConstraintParams
): Generator<boolean, undefined> {
// Restrict ACH to only US
if (params.paymentType === 'ach') {
yield params.regionCode.countryCode === 'US'
}

// Venmo payment method is only supported in the USA
if (params.paymentType === 'venmo') {
yield params.regionCode.countryCode === 'US'
}

// Filter out credit for sell in US for Paybis
if (params.rampPluginId === 'paybis') {
yield params.direction !== 'sell' &&
params.regionCode.countryCode !== 'US' &&
params.paymentType !== 'credit'
}
Copy link

Choose a reason for hiding this comment

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

Bug: Paybis US Sell Constraint Inverts Logic

The Paybis constraint for sell operations in the US fails to filter out 'credit' payment types. The logic is inverted, allowing US regions instead of blocking them, and it applies to all payment types, contradicting the comment and original behavior.

Fix in Cursor Fix in Web


// Constrain Revolut to the supported regions
if (params.paymentType === 'revolut') {
const forCountries = `
AU, BR, AT, BE, BG, 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, JP, NZ,
SG, CH, GB, IO, IM, JE, GG, GF, GP, YT,
MQ, RE, MF `
.replace(/\s/g, '')
.split(',')

yield forCountries.includes(params.regionCode.countryCode)
}

if (params.paymentType === 'paypal') {
const forCountries = `
AE, AR, AT, AU, BE, BR, CA, CH, CL, CO,
CZ, DE, DK, EG, ES, FI, FR, GH, GR, HK,
HU, IE, IN, IT, JP, KE, KR, MX, MY,
NG, NL, NO, NZ, PE, PH, PL, PT, RU, SA,
SE, SG, TH, TR, TZ, US, VN, ZA`
.replace(/\s/g, '')
.split(',')

yield forCountries.includes(params.regionCode.countryCode)
}

// Paybis is not supported in the UK (Great Britain)
if (params.rampPluginId === 'paybis') {
yield params.regionCode.countryCode !== 'GB'
}
}
Loading
Loading