Skip to content

Commit a75ecb2

Browse files
authored
chore(ONRAMP-843): FundCard accept sessionToken param (#2501)
1 parent aeed32a commit a75ecb2

23 files changed

+328
-277
lines changed

packages/onchainkit/src/buy/components/Buy.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,18 @@ describe('Buy', () => {
155155
render(<Buy className="test-class" toToken={degenToken} />);
156156
expect(screen.queryByTestId('mock-BuyDropdown')).not.toBeInTheDocument();
157157
});
158+
159+
it('should pass sessionToken to BuyProvider', () => {
160+
const sessionToken = 'test-session-token';
161+
render(
162+
<Buy
163+
className="test-class"
164+
toToken={degenToken}
165+
sessionToken={sessionToken}
166+
/>,
167+
);
168+
169+
// Verify BuyProvider is rendered (which receives the sessionToken prop)
170+
expect(screen.getByTestId('mock-BuyProvider')).toBeInTheDocument();
171+
});
158172
});

packages/onchainkit/src/buy/components/Buy.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function Buy({
4949
onSuccess,
5050
toToken,
5151
fromToken,
52+
sessionToken,
5253
}: BuyProps) {
5354
return (
5455
<BuyProvider
@@ -61,6 +62,7 @@ export function Buy({
6162
onSuccess={onSuccess}
6263
toToken={toToken}
6364
fromToken={fromToken}
65+
sessionToken={sessionToken}
6466
>
6567
<BuyContent className={className} />
6668
</BuyProvider>

packages/onchainkit/src/buy/components/BuyDropdown.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const mockContextValue = {
5858
from: { token: { symbol: 'DAI' } },
5959
startPopupMonitor: mockStartPopupMonitor,
6060
setIsDropdownOpen: vi.fn(),
61+
sessionToken: undefined,
6162
};
6263

6364
describe('BuyDropdown', () => {
@@ -156,4 +157,31 @@ describe('BuyDropdown', () => {
156157

157158
expect(mockStartPopupMonitor).not.toHaveBeenCalled();
158159
});
160+
161+
it('passes sessionToken to getBuyFundingUrl when available', () => {
162+
const sessionToken = 'test-session-token';
163+
(useBuyContext as Mock).mockReturnValue({
164+
...mockContextValue,
165+
sessionToken,
166+
fromETH: { token: ethToken, amount: '10' },
167+
fromUSDC: { token: usdcToken, amount: '10' },
168+
from: { token: { symbol: 'DAI' }, amount: '10' },
169+
});
170+
(openPopup as Mock).mockReturnValue('popup');
171+
(getBuyFundingUrl as Mock).mockReturnValue('valid-funding-url');
172+
173+
render(<BuyDropdown />);
174+
175+
const onrampButton = screen.getByTestId('ock-coinbasePayOnrampItem');
176+
177+
act(() => {
178+
fireEvent.click(onrampButton);
179+
});
180+
181+
expect(getBuyFundingUrl).toHaveBeenCalledWith({
182+
to: mockContextValue.to,
183+
paymentMethodId: 'CRYPTO_ACCOUNT',
184+
sessionToken,
185+
});
186+
});
159187
});

packages/onchainkit/src/buy/components/BuyDropdown.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { useAnalytics } from '@/core/analytics/hooks/useAnalytics';
33
import { BuyEvent, type BuyOptionType } from '@/core/analytics/types';
44
import { type PaymentMethod } from '@/fund/types';
55
import { openPopup } from '@/internal/utils/openPopup';
6-
import { useOnchainKit } from '@/useOnchainKit';
76
import { useCallback, useEffect, useMemo } from 'react';
8-
import { base } from 'viem/chains';
9-
import { useAccount } from 'wagmi';
107
import { getFundingPopupSize } from '../../fund/utils/getFundingPopupSize';
118
import { getRoundedAmount } from '../../internal/utils/getRoundedAmount';
129
import { cn, text } from '../../styles/theme';
@@ -19,10 +16,15 @@ import { BuyTokenItem } from './BuyTokenItem';
1916

2017
// eslint-disable-next-line complexity
2118
export function BuyDropdown() {
22-
const { projectId } = useOnchainKit();
23-
const { to, fromETH, fromUSDC, from, startPopupMonitor, setIsDropdownOpen } =
24-
useBuyContext();
25-
const { address } = useAccount();
19+
const {
20+
to,
21+
fromETH,
22+
fromUSDC,
23+
from,
24+
startPopupMonitor,
25+
setIsDropdownOpen,
26+
sessionToken,
27+
} = useBuyContext();
2628
const { sendAnalytics } = useAnalytics();
2729

2830
const handleOnrampClick = useCallback(
@@ -34,10 +36,8 @@ export function BuyDropdown() {
3436

3537
const fundingUrl = getBuyFundingUrl({
3638
to,
37-
projectId,
3839
paymentMethodId,
39-
address,
40-
chain: base,
40+
sessionToken,
4141
});
4242

4343
if (!fundingUrl) {
@@ -59,7 +59,7 @@ export function BuyDropdown() {
5959
}
6060
};
6161
},
62-
[address, to, projectId, startPopupMonitor, sendAnalytics],
62+
[sendAnalytics, to, sessionToken, startPopupMonitor],
6363
);
6464

6565
const formattedAmountUSD = useMemo(() => {

packages/onchainkit/src/buy/components/BuyProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function BuyProvider({
5757
onSuccess,
5858
toToken,
5959
fromToken,
60+
sessionToken,
6061
}: BuyProviderProps) {
6162
const { config: { paymaster } = { paymaster: undefined }, projectId } =
6263
useOnchainKit();
@@ -470,6 +471,7 @@ export function BuyProvider({
470471
toToken,
471472
fromToken,
472473
startPopupMonitor,
474+
sessionToken,
473475
}}
474476
>
475477
{children}

packages/onchainkit/src/buy/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export type BuyProps = {
3131
fromToken?: Token;
3232
/** The token to swap to */
3333
toToken: Token;
34+
/** Onramp session token */
35+
sessionToken?: string;
3436
};
3537

3638
export type BuyContextType = {
@@ -56,6 +58,7 @@ export type BuyContextType = {
5658
isDropdownOpen: boolean;
5759
setIsDropdownOpen: (open: boolean) => void;
5860
startPopupMonitor: (popupWindow: Window) => void;
61+
sessionToken?: string;
5962
};
6063

6164
export type BuyProviderProps = {
@@ -79,6 +82,7 @@ export type BuyProviderProps = {
7982
onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
8083
fromToken?: Token;
8184
toToken: Token;
85+
sessionToken?: string;
8286
};
8387

8488
export type BuyTokens = {
Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,96 @@
11
import { getOnrampBuyUrl } from '@/fund/utils/getOnrampBuyUrl';
22
import { SwapUnit } from '@/swap/types';
33
import { Token } from '@/token';
4-
import type { Chain } from 'viem';
5-
import { base } from 'viem/chains';
6-
import { describe, it, expect, vi } from 'vitest';
4+
import { describe, it, expect, vi, beforeEach } from 'vitest';
75
import { getBuyFundingUrl } from './getBuyFundingUrl';
86

97
vi.mock('@/fund/utils/getOnrampBuyUrl', () => ({
108
getOnrampBuyUrl: vi.fn(),
119
}));
1210

1311
describe('getBuyFundingUrl', () => {
14-
const baseParams = {
15-
projectId: 'abc123',
16-
address: '0x123',
17-
paymentMethodId: 'pm_001',
18-
chain: base as Chain,
19-
};
12+
beforeEach(() => {
13+
vi.clearAllMocks();
14+
});
2015

21-
it('returns undefined if projectId is null', () => {
16+
it('returns undefined if sessionToken is not provided', () => {
2217
const result = getBuyFundingUrl({
23-
...baseParams,
24-
projectId: null,
2518
to: {
2619
amount: '1',
2720
token: { symbol: 'ETH' } as Token,
2821
} as SwapUnit,
22+
paymentMethodId: 'pm_001',
2923
});
3024

3125
expect(result).toBeUndefined();
26+
expect(getOnrampBuyUrl).not.toHaveBeenCalled();
3227
});
3328

34-
it('returns undefined if address is undefined', () => {
29+
it('returns undefined if fundAmount is missing', () => {
3530
const result = getBuyFundingUrl({
36-
...baseParams,
37-
address: undefined,
38-
to: {
39-
amount: '1',
40-
token: { symbol: 'ETH' },
41-
} as SwapUnit,
31+
to: undefined,
32+
sessionToken: 'test-session-token',
33+
paymentMethodId: 'pm_001',
4234
});
4335

4436
expect(result).toBeUndefined();
37+
expect(getOnrampBuyUrl).toHaveBeenCalled();
4538
});
4639

47-
it('returns undefined if assetSymbol is missing', () => {
40+
it('returns undefined if amount is missing from to', () => {
4841
const result = getBuyFundingUrl({
49-
...baseParams,
5042
to: {
51-
amount: '1',
52-
token: undefined,
43+
token: { symbol: 'ETH' } as Token,
5344
} as SwapUnit,
45+
sessionToken: 'test-session-token',
46+
paymentMethodId: 'pm_001',
5447
});
5548

5649
expect(result).toBeUndefined();
50+
expect(getOnrampBuyUrl).toHaveBeenCalled();
5751
});
5852

59-
it('calls getOnrampBuyUrl with correct parameters', () => {
53+
it('calls getOnrampBuyUrl with correct parameters when sessionToken is provided', () => {
6054
const mockUrl = 'https://onramp.com/buy';
6155
(getOnrampBuyUrl as ReturnType<typeof vi.fn>).mockReturnValue(mockUrl);
6256

6357
const result = getBuyFundingUrl({
64-
...baseParams,
6558
to: {
6659
amount: '1.5',
6760
token: { symbol: 'ETH' },
6861
} as SwapUnit,
62+
sessionToken: 'test-session-token',
63+
paymentMethodId: 'pm_001',
6964
});
7065

7166
expect(getOnrampBuyUrl).toHaveBeenCalledWith({
72-
projectId: 'abc123',
73-
assets: ['ETH'],
67+
sessionToken: 'test-session-token',
7468
presetCryptoAmount: 1.5,
7569
defaultPaymentMethod: 'pm_001',
76-
addresses: {
77-
'0x123': ['base'],
78-
},
7970
originComponentName: 'FundCard',
8071
});
8172

8273
expect(result).toBe(mockUrl);
8374
});
75+
76+
it('uses correct originComponentName', () => {
77+
const mockUrl = 'https://onramp.com/buy';
78+
(getOnrampBuyUrl as ReturnType<typeof vi.fn>).mockReturnValue(mockUrl);
79+
80+
getBuyFundingUrl({
81+
to: {
82+
amount: '2.0',
83+
token: { symbol: 'USDC' },
84+
} as SwapUnit,
85+
sessionToken: 'another-session-token',
86+
paymentMethodId: 'CARD',
87+
});
88+
89+
expect(getOnrampBuyUrl).toHaveBeenCalledWith({
90+
sessionToken: 'another-session-token',
91+
presetCryptoAmount: 2.0,
92+
defaultPaymentMethod: 'CARD',
93+
originComponentName: 'FundCard',
94+
});
95+
});
8496
});
Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
11
import { getOnrampBuyUrl } from '@/fund/utils/getOnrampBuyUrl';
22
import { SwapUnit } from '@/swap/types';
3-
import { Chain } from 'viem';
43

54
type GetBuyFundingUrlParams = {
65
to?: SwapUnit;
7-
projectId?: string | null;
8-
address?: string;
6+
sessionToken?: string;
97
paymentMethodId?: string;
10-
chain: Chain;
118
};
129

1310
export function getBuyFundingUrl({
1411
to,
15-
projectId,
12+
sessionToken,
1613
paymentMethodId,
17-
address,
18-
chain,
1914
}: GetBuyFundingUrlParams) {
20-
const assetSymbol = to?.token?.symbol;
2115
const fundAmount = to?.amount;
2216

23-
if (!projectId || address === undefined || !assetSymbol) {
17+
if (!sessionToken) {
2418
return undefined;
2519
}
2620

2721
return getOnrampBuyUrl({
28-
projectId,
29-
assets: [assetSymbol],
22+
sessionToken,
3023
presetCryptoAmount: Number(fundAmount),
3124
defaultPaymentMethod: paymentMethodId,
32-
addresses: { [address]: [chain.name.toLowerCase()] },
3325
originComponentName: 'FundCard',
3426
});
3527
}

packages/onchainkit/src/fund/components/FundButton.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,22 @@ describe('FundButton', () => {
238238
).not.toBeInTheDocument();
239239
});
240240

241+
it('passes sessionToken to useGetFundingUrl hook', () => {
242+
const sessionToken = 'test-session-token';
243+
const fundingUrl = 'https://default.funding.url';
244+
const { height, width } = { height: 200, width: 100 };
245+
(useGetFundingUrl as Mock).mockReturnValue(fundingUrl);
246+
(getFundingPopupSize as Mock).mockReturnValue({ height, width });
247+
248+
render(<FundButton sessionToken={sessionToken} />);
249+
250+
expect(useGetFundingUrl).toHaveBeenCalledWith({
251+
fiatCurrency: 'USD',
252+
originComponentName: 'FundButton',
253+
sessionToken: sessionToken,
254+
});
255+
});
256+
241257
describe('analytics', () => {
242258
it('sends FundInitiated analytics when fund button is clicked', () => {
243259
const fundingUrl = 'https://props.funding.url';

packages/onchainkit/src/fund/components/FundButton.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ export function FundButton({
3030
onPopupClose,
3131
onClick,
3232
render,
33+
sessionToken,
3334
}: FundButtonProps) {
3435
// If the fundingUrl prop is undefined, fallback to our recommended funding URL based on the wallet type
3536
const fallbackFundingUrl = useGetFundingUrl({
3637
fiatCurrency,
3738
originComponentName: 'FundButton',
39+
sessionToken,
3840
});
3941
const { address } = useAccount();
4042
const fundingUrlToRender = fundingUrl ?? fallbackFundingUrl;

0 commit comments

Comments
 (0)