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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased (develop)

- changed: Update Infinite ONRAMP to the new headless transfer API: ACH push payment to a per-customer virtual bank account, with the deposit beneficiary surfaced on the bank routing scene.
- fixed: Always reset the Infinite confirmation slider once `onConfirm` settles so a successful transfer with missing deposit instructions no longer leaves the slider spinning.
- fixed: Pop the Infinite ramp stack to the top after the user dismisses the bank routing details scene, instead of stepping back through the confirmation scene.
- fixed: Allow the Infinite bank routing instructions and warning text to wrap and stop them from shrinking under large system fonts.

## 4.49.0 (staging)

- added: Honor `af` affiliate parameter on `deep.edge.app` deep links, activating the promotion alongside any inner payload (e.g. private-key import).
Expand Down
23 changes: 18 additions & 5 deletions src/components/scenes/RampBankRoutingDetailsScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import { SceneContainer } from '../layout/SceneContainer'
import { EdgeRow } from '../rows/EdgeRow'
import { showToast } from '../services/AirshipInstance'
import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext'
import { EdgeText, Paragraph } from '../themed/EdgeText'
import { EdgeText } from '../themed/EdgeText'

export interface BankInfo {
name: string
accountNumber: string
routingNumber: string
beneficiaryName?: string
}

export interface RampBankRoutingDetailsParams {
Expand Down Expand Up @@ -57,9 +58,9 @@ export const RampBankRoutingDetailsScene: React.FC<Props> = props => {
size={theme.rem(2.5)}
color={theme.primaryText}
/>
<Paragraph style={styles.instructionText}>
<EdgeText numberOfLines={0} style={styles.instructionText}>
{lstrings.ramp_bank_routing_instructions}
</Paragraph>
</EdgeText>
</View>

<EdgeCard>
Expand All @@ -82,6 +83,13 @@ export const RampBankRoutingDetailsScene: React.FC<Props> = props => {

<SectionHeader leftTitle={lstrings.ramp_bank_details_section_title} />
<EdgeCard sections>
{bank.beneficiaryName != null && bank.beneficiaryName !== '' ? (
<EdgeRow
title={lstrings.ramp_bank_beneficiary_name_label}
body={bank.beneficiaryName}
rightButtonType="copy"
/>
) : null}
<EdgeRow
title={lstrings.ramp_bank_name_label}
body={bank.name}
Expand All @@ -100,7 +108,11 @@ export const RampBankRoutingDetailsScene: React.FC<Props> = props => {
</EdgeCard>

<View style={styles.warningTextContainer}>
<EdgeText style={styles.warningText}>
<EdgeText
style={styles.warningText}
disableFontScaling
numberOfLines={0}
>
{lstrings.ramp_bank_routing_warning}
</EdgeText>
</View>
Expand All @@ -127,7 +139,8 @@ const getStyles = cacheStyles((theme: Theme) => ({
color: theme.iconTappable
},
instructionText: {
flexShrink: 1
flexShrink: 1,
marginLeft: theme.rem(0.5)
},
cardContent: {
padding: theme.rem(0.5)
Expand Down
4 changes: 2 additions & 2 deletions src/components/scenes/RampConfirmationScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ export const RampConfirmationScene: React.FC<Props> = props => {
setIsConfirming(true)
try {
await onConfirm()
} catch (err) {
} catch (err: unknown) {
setError(err)
reset() // Reset the slider on error
} finally {
reset()
setIsConfirming(false)
}
})
Expand Down
1 change: 1 addition & 0 deletions src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2545,6 +2545,7 @@ const strings = {
'Please send the exact amount shown below to the following bank account.',
ramp_send_amount_label: 'Amount to Send',
ramp_bank_details_section_title: 'Bank Details',
ramp_bank_beneficiary_name_label: 'Beneficiary Name',
ramp_bank_name_label: 'Bank Name',
ramp_account_number_label: 'Account Number',
ramp_routing_number_label: 'Routing Number',
Expand Down
1 change: 1 addition & 0 deletions src/locales/strings/enUS.json
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,7 @@
"ramp_bank_routing_instructions": "Please send the exact amount shown below to the following bank account.",
"ramp_send_amount_label": "Amount to Send",
"ramp_bank_details_section_title": "Bank Details",
"ramp_bank_beneficiary_name_label": "Beneficiary Name",
"ramp_bank_name_label": "Bank Name",
"ramp_account_number_label": "Account Number",
"ramp_routing_number_label": "Routing Number",
Expand Down
61 changes: 53 additions & 8 deletions src/plugins/ramps/infinite/infiniteApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const USE_DUMMY_DATA: Record<keyof InfiniteApi, boolean> = {
createQuote: false,
createTransfer: false,
getTransferStatus: false,
getDepositAddress: false,
createCustomer: false,
verifyOtp: false,
getKycStatus: false,
Expand Down Expand Up @@ -335,28 +336,35 @@ export const makeInfiniteApi = (config: InfiniteApiConfig): InfiniteApi => {
return asInfiniteTransferResponse(data)
}

// Dummy response - New format
const dummyResponse: InfiniteTransferResponse = {
id: `transfer_${params.type.toLowerCase()}_${Date.now()}`,
sourceDepositInstructions:
params.type === 'ONRAMP'
? {
// Dummy response - New format. ONRAMP returns null id + depositAddressId.
const dummyResponse: InfiniteTransferResponse =
params.type === 'ONRAMP'
? {
id: null,
depositAddressId: `vba_${Date.now().toString(16)}`,
sourceDepositInstructions: {
amount: params.amount,
bankAccountNumber: '8312008517',
bankRoutingNumber: '021000021',
bankName: 'JPMorgan Chase Bank',
bankBeneficiaryName: 'Edge Wallet, Inc.',
toAddress: null
}
: {
}
: {
id: `tfr_${Date.now().toString(16)}`,
depositAddressId: undefined,
sourceDepositInstructions: {
amount: params.amount,
bankAccountNumber: null,
bankRoutingNumber: null,
bankName: null,
bankBeneficiaryName: null,
toAddress: `0xdeadbeef2${params.source.currency}${
params.source.network
}${Date.now().toString(16)}`
}
}
}

return dummyResponse
},
Expand All @@ -382,12 +390,49 @@ export const makeInfiniteApi = (config: InfiniteApiConfig): InfiniteApi => {
// Dummy response - simulate a completed transfer
const dummyResponse: InfiniteTransferResponse = {
id: transferId,
depositAddressId: undefined,
sourceDepositInstructions: undefined
}

return dummyResponse
},

getDepositAddress: async (depositAddressId: string) => {
// Reload deposit instructions for an existing ONRAMP virtual bank
// account. Auth required; only onboarded wallets get a 200 here.
if (authState.token == null || isTokenExpired()) {
throw new Error('Authentication required')
}

if (!USE_DUMMY_DATA.getDepositAddress) {
const response = await fetchInfinite(
`/v1/headless/transfers/deposit-address/${depositAddressId}`,
{
headers: makeHeaders({ includeAuth: true })
}
)

const data = await response.text()
return asInfiniteTransferResponse(data)
}

// Dummy response - same shape as ONRAMP create
const dummyResponse: InfiniteTransferResponse = {
id: null,
depositAddressId,
sourceDepositInstructions: {
amount: 0,
bankAccountNumber: '8312008517',
bankRoutingNumber: '021000021',
bankName: 'JPMorgan Chase Bank',
bankBeneficiaryName: 'Edge Wallet, Inc.',
toAddress: null
}
}

return dummyResponse
},

// Customer methods
createCustomer: async (
params: InfiniteCustomerRequest
Expand Down
78 changes: 58 additions & 20 deletions src/plugins/ramps/infinite/infiniteApiTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,24 @@ export const asInfiniteQuoteResponse = asJSON(
)

// Transfer response - New format for headless API
// ONRAMP create returns id: null and a depositAddressId (vba_…). OFFRAMP and
// follow-up GETs against /transfers/:transferId return id as a string.
export const asInfiniteTransferResponse = asJSON(
asObject({
id: asString,
id: asEither(asString, asNull),
depositAddressId: asOptional(asString),
sourceDepositInstructions: asOptional(
asObject({
amount: asNumber,
bankAccountNumber: asOptional(asString, null),
bankRoutingNumber: asOptional(asString, null),
bankName: asOptional(asString, null),
bankBeneficiaryName: asOptional(asString, null),
toAddress: asOptional(asString, null)
// UNUSED fields:
// network: asString,
// currency: asString,
// depositMessage: asOptional(asString, null),
// bankBeneficiaryName: asOptional(asString, null),
// fromAddress: asOptional(asString, null)
})
)
Expand All @@ -111,11 +114,57 @@ export const asInfiniteTransferResponse = asJSON(
// accountId: asOptional(asString, null),
// toAddress: asOptional(asString, null)
// }),
// fees: asObject({
// infiniteFee: asNumber,
// partnerFee: asNumber,
// total: asNumber,
// currency: asString
// }),
// createdAt: asString,
// updatedAt: asString
})
)

// Transfer request - discriminated by direction. ONRAMP no longer accepts an
// account id; Infinite provisions a virtual bank account and returns deposit
// instructions in the create response.
export interface InfiniteOnrampTransferRequest {
type: 'ONRAMP'
amount: number
source: {
currency: string
network: string
}
destination: {
currency: string
network: string
toAddress: string
}
clientReferenceId?: string
developerFee?: string
}

export interface InfiniteOfframpTransferRequest {
type: 'OFFRAMP'
amount: number
source: {
currency: string
network: string
fromAddress: string
}
destination: {
currency: string
network: string
accountId: string
}
clientReferenceId?: string
developerFee?: string
}

export type InfiniteTransferRequest =
| InfiniteOnrampTransferRequest
| InfiniteOfframpTransferRequest

// Customer types
export const asInfiniteCustomerType = asValue('individual', 'business')

Expand Down Expand Up @@ -443,27 +492,16 @@ export interface InfiniteApi {
}) => Promise<InfiniteQuoteResponse>

// Transfer methods
createTransfer: (params: {
type: InfiniteQuoteFlow
amount: number
source: {
currency: string
network: string
accountId?: string
fromAddress?: string
}
destination: {
currency: string
network: string
accountId?: string
toAddress?: string
}
clientReferenceId?: string
developerFee?: string
}) => Promise<InfiniteTransferResponse>
createTransfer: (
params: InfiniteTransferRequest
) => Promise<InfiniteTransferResponse>

getTransferStatus: (transferId: string) => Promise<InfiniteTransferResponse>

getDepositAddress: (
depositAddressId: string
) => Promise<InfiniteTransferResponse>

// Customer methods
createCustomer: (
params: InfiniteCustomerRequest
Expand Down
Loading
Loading