Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { useRampsController } from '../../../hooks/useRampsController';
import { useRampsQuotes } from '../../../hooks/useRampsQuotes';
import useRampAccountAddress from '../../../hooks/useRampAccountAddress';
import { getOrdersProviders } from '../../../../../../reducers/fiatOrders';
import { selectRampsOrders } from '../../../../../../selectors/rampsController';
import { selectRampsOrdersForSelectedAccountGroup } from '../../../../../../selectors/rampsController';
import { completedOrdersFromRampsOrders } from '../../../utils/determinePreferredProvider';
import { useStyles } from '../../../../../hooks/useStyles';
import styleSheet from './ProviderSelectionModal.styles';
Expand Down Expand Up @@ -59,7 +59,9 @@ function ProviderSelectionModal() {
} = useRampsController();

const legacyOrdersProviders = useSelector(getOrdersProviders);
const controllerOrders = useSelector(selectRampsOrders);
const controllerOrders = useSelector(
selectRampsOrdersForSelectedAccountGroup,
);

const ordersProviders = useMemo(() => {
const v2ProviderIds = completedOrdersFromRampsOrders(controllerOrders).map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ jest.mock('../../../../../util/navigation/navUtils', () => ({
(..._args: unknown[]) =>
(params: unknown) => ['MockRoute', params],
useParams: () => ({
quote: { quoteId: 'test-quote-id', fiatAmount: 100 },
quote: { quoteId: 'test-quote-id', fiatAmount: 127.37 },
kycUrl: 'https://kyc.example.com',
workFlowRunId: 'wf-123',
amount: 25,
}),
}));

Expand Down Expand Up @@ -71,7 +72,7 @@ describe('V2AdditionalVerification', () => {

expect(mockNavigateToKycWebview).toHaveBeenCalledWith({
kycUrl: 'https://kyc.example.com',
amount: 100,
amount: 25,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ interface V2AdditionalVerificationParams {
quote: TransakBuyQuote;
kycUrl: string;
workFlowRunId: string;
/** From BuildQuote route; keeps stack amount in sync when opening KYC webview. */
amount?: number;
}

const V2AdditionalVerification = () => {
const navigation = useNavigation();
const { kycUrl, quote } = useParams<V2AdditionalVerificationParams>();
const { kycUrl, amount: userEnteredAmount } =
useParams<V2AdditionalVerificationParams>();

const { styles, theme } = useStyles(styleSheet, {});

Expand All @@ -46,8 +49,8 @@ const V2AdditionalVerification = () => {
}, [navigation, theme]);

const handleContinuePress = useCallback(() => {
navigateToKycWebview({ kycUrl, amount: quote?.fiatAmount });
}, [navigateToKycWebview, kycUrl, quote?.fiatAmount]);
navigateToKycWebview({ kycUrl, amount: userEnteredAmount });
}, [navigateToKycWebview, kycUrl, userEnteredAmount]);

return (
<ScreenLayout>
Expand Down
6 changes: 4 additions & 2 deletions app/components/UI/Ramp/hooks/useRampsButtonClickData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getRampRoutingDecision,
UnifiedRampRoutingType,
} from '../../../../reducers/fiatOrders';
import { selectRampsOrders } from '../../../../selectors/rampsController';
import { selectRampsOrdersForSelectedAccountGroup } from '../../../../selectors/rampsController';
import { getProviderToken } from '../Deposit/utils/ProviderTokenVault';
import {
completedOrdersFromFiatOrders,
Expand All @@ -21,7 +21,9 @@ export interface RampsButtonClickData {

export function useRampsButtonClickData(): RampsButtonClickData {
const orders = useSelector(getOrders);
const controllerOrders = useSelector(selectRampsOrders);
const controllerOrders = useSelector(
selectRampsOrdersForSelectedAccountGroup,
);
const rampRoutingDecision = useSelector(getRampRoutingDecision);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);

Expand Down
70 changes: 68 additions & 2 deletions app/components/UI/Ramp/hooks/useRampsOrders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@ import { renderHook, act } from '@testing-library/react-native';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import React from 'react';
import { AccountGroupType } from '@metamask/account-api';
import { RampsOrderStatus, type RampsOrder } from '@metamask/ramps-controller';
import { createMockInternalAccount } from '../../../../util/test/accountsControllerTestUtils';
import { useRampsOrders } from './useRampsOrders';

const RAMP_HOOKS_TEST_WALLET_ID = 'keyring:use-ramps-orders-test' as const;
const RAMP_HOOKS_TEST_GROUP_ID =
`${RAMP_HOOKS_TEST_WALLET_ID}/ethereum` as const;
const RAMP_HOOKS_TEST_ACCOUNT_ID = 'account-rh-1';
/** Must be a valid EVM address (20 bytes) so `areAddressesEqual` treats it as EVM. */
const RAMP_HOOKS_TEST_ADDRESS = '0x2990079bcdee240329a520d2444386fc119da21a';

const rampHooksTestInternalAccount = {
...createMockInternalAccount(RAMP_HOOKS_TEST_ADDRESS, 'Test'),
id: RAMP_HOOKS_TEST_ACCOUNT_ID,
};

const mockAddOrder = jest.fn();
const mockAddPrecreatedOrder = jest.fn();
const mockRemoveOrder = jest.fn();
Expand Down Expand Up @@ -35,7 +49,7 @@ const createMockOrder = (overrides: Partial<RampsOrder> = {}): RampsOrder => ({
createdAt: Date.now(),
totalFeesFiat: 5,
txHash: '0xabc',
walletAddress: '0x123',
walletAddress: RAMP_HOOKS_TEST_ADDRESS,
status: RampsOrderStatus.Completed,
network: { name: 'Ethereum', chainId: 'eip155:1' },
canBeUpdated: false,
Expand All @@ -54,6 +68,45 @@ const createMockStore = (orders: RampsOrder[] = []) =>
RampsController: {
orders,
},
AccountTreeController: {
accountTree: {
wallets: {
[RAMP_HOOKS_TEST_WALLET_ID]: {
id: RAMP_HOOKS_TEST_WALLET_ID,
metadata: { name: 'Test wallet' },
groups: {
[RAMP_HOOKS_TEST_GROUP_ID]: {
id: RAMP_HOOKS_TEST_GROUP_ID,
type: AccountGroupType.SingleAccount,
accounts: [RAMP_HOOKS_TEST_ACCOUNT_ID],
metadata: { name: 'Test Group' },
},
},
},
},
selectedAccountGroup: RAMP_HOOKS_TEST_GROUP_ID,
},
},
RemoteFeatureFlagController: {
remoteFeatureFlags: {
enableMultichainAccounts: {
enabled: true,
featureVersion: '1',
minimumVersion: '1.0.0',
},
},
},
AccountsController: {
internalAccounts: {
accounts: {
[RAMP_HOOKS_TEST_ACCOUNT_ID]: rampHooksTestInternalAccount,
},
selectedAccount: RAMP_HOOKS_TEST_ACCOUNT_ID,
},
},
KeyringController: {
keyrings: [],
},
},
}),
},
Expand All @@ -78,7 +131,7 @@ describe('useRampsOrders', () => {
expect(result.current.orders).toEqual([]);
});

it('returns orders from the store', () => {
it('returns orders from the store when walletAddress matches the selected account group', () => {
const order = createMockOrder();
const store = createMockStore([order]);
const { result } = renderHook(() => useRampsOrders(), {
Expand All @@ -88,6 +141,19 @@ describe('useRampsOrders', () => {
expect(result.current.orders).toEqual([order]);
});

it('excludes orders whose walletAddress is not in the selected account group', () => {
const foreignOrder = createMockOrder({
providerOrderId: 'foreign-order',
walletAddress: '0x0000000000000000000000000000000000000001',
});
const store = createMockStore([foreignOrder]);
const { result } = renderHook(() => useRampsOrders(), {
wrapper: wrapper(store),
});

expect(result.current.orders).toEqual([]);
});

it('finds an order by providerOrderId', () => {
const order1 = createMockOrder({ providerOrderId: 'order-1' });
const order2 = createMockOrder({ providerOrderId: 'order-2' });
Expand Down
4 changes: 2 additions & 2 deletions app/components/UI/Ramp/hooks/useRampsOrders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useSelector } from 'react-redux';
import type { RampsOrder } from '@metamask/ramps-controller';
import { extractOrderCode } from '../utils/extractOrderCode';
import Engine from '../../../../core/Engine';
import { selectRampsOrders } from '../../../../selectors/rampsController';
import { selectRampsOrdersForSelectedAccountGroup } from '../../../../selectors/rampsController';

export interface AddPrecreatedOrderParams {
orderId: string;
Expand Down Expand Up @@ -31,7 +31,7 @@ export interface UseRampsOrdersResult {
}

export function useRampsOrders(): UseRampsOrdersResult {
const orders = useSelector(selectRampsOrders);
const orders = useSelector(selectRampsOrdersForSelectedAccountGroup);

const getOrderById = useCallback(
(providerOrderId: string) => {
Expand Down
6 changes: 4 additions & 2 deletions app/components/UI/Ramp/hooks/useRampsProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo } from 'react';
import { useSelector } from 'react-redux';
import {
selectProviders,
selectRampsOrders,
selectRampsOrdersForSelectedAccountGroup,
} from '../../../../selectors/rampsController';
import { type Provider } from '@metamask/ramps-controller';
import Engine from '../../../../core/Engine';
Expand Down Expand Up @@ -55,7 +55,9 @@ export function useRampsProviders(): UseRampsProvidersResult {
} = useSelector(selectProviders);

const legacyOrders = useSelector(getOrders);
const controllerOrders = useSelector(selectRampsOrders);
const controllerOrders = useSelector(
selectRampsOrdersForSelectedAccountGroup,
);

const completedOrders = useMemo(
() => [
Expand Down
59 changes: 53 additions & 6 deletions app/components/UI/Ramp/hooks/useTransakRouting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe('useTransakRouting', () => {
'test-ott',
mockQuote,
MOCK_WALLET_ADDRESS,
expect.any(Object),
{ theme: 'light' },
);
expect(mockReset).toHaveBeenCalledWith(
expect.objectContaining({
Expand Down Expand Up @@ -482,10 +482,7 @@ describe('useTransakRouting', () => {
const { result } = renderHook(() => useTransakRouting());

await act(async () => {
await result.current.routeAfterAuthentication(
mockQuote as never,
mockQuote.fiatAmount,
);
await result.current.routeAfterAuthentication(mockQuote as never, 25);
});

expect(mockReset).toHaveBeenCalledWith(
Expand All @@ -494,14 +491,64 @@ describe('useTransakRouting', () => {
routes: [
expect.objectContaining({
name: 'RampAmountInput',
params: { amount: mockQuote.fiatAmount },
params: { amount: 25 },
}),
expect.objectContaining({
name: 'RampAdditionalVerification',
params: expect.objectContaining({
quote: mockQuote,
kycUrl: 'https://kyc.example.com',
workFlowRunId: 'wf-123',
amount: 25,
}),
}),
],
}),
);
});

it('handles ADDITIONAL_FORMS_REQUIRED with IDPROOF when user amount is omitted', async () => {
mockGetUserDetails.mockResolvedValue({
firstName: 'John',
address: {},
});
mockGetKycRequirement.mockResolvedValue({
status: 'ADDITIONAL_FORMS_REQUIRED',
kycType: 'STANDARD',
});
mockGetAdditionalRequirements.mockResolvedValue({
formsRequired: [
{
type: 'IDPROOF',
metadata: {
kycUrl: 'https://kyc.example.com',
workFlowRunId: 'wf-123',
},
},
],
});

const { result } = renderHook(() => useTransakRouting());

await act(async () => {
await result.current.routeAfterAuthentication(mockQuote as never);
});

expect(mockReset).toHaveBeenCalledWith(
expect.objectContaining({
index: 1,
routes: [
expect.objectContaining({
name: 'RampAmountInput',
params: { amount: undefined },
}),
expect.objectContaining({
name: 'RampAdditionalVerification',
params: expect.objectContaining({
quote: mockQuote,
kycUrl: 'https://kyc.example.com',
workFlowRunId: 'wf-123',
amount: undefined,
}),
}),
],
Expand Down
4 changes: 3 additions & 1 deletion app/components/UI/Ramp/hooks/useTransakRouting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ interface RampStackParamList {
quote: TransakBuyQuote;
kycUrl: string;
workFlowRunId: string;
/** User-entered fiat from BuildQuote; used when resetting stack so amount screen keeps the typed value. */
amount?: number;
};
RampKycProcessing: { quote: TransakBuyQuote };
RampEnterEmail: undefined;
Expand Down Expand Up @@ -258,7 +260,7 @@ export const useTransakRouting = (_config?: UseTransakRoutingConfig) => {
},
{
name: Routes.RAMP.ADDITIONAL_VERIFICATION,
params: { quote, kycUrl, workFlowRunId },
params: { quote, kycUrl, workFlowRunId, amount },
},
],
});
Expand Down
Loading
Loading