Skip to content

Commit

Permalink
feat: improved funding flow in Checkout (#1692)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xAlec authored Dec 9, 2024
1 parent 163efaf commit da7491a
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/cold-clocks-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@coinbase/onchainkit': patch
---

feat: improved funding flow in Checkout by @0xAlec #1692
12 changes: 0 additions & 12 deletions src/checkout/components/CheckoutButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@ describe('CheckoutButton', () => {
expect(screen.getByRole('button').textContent).toBe('View payment details');
});

it('should render "Get USDC" when there is insufficient balance error', () => {
useCheckoutContextMock.mockReturnValue({
lifecycleStatus: {
statusName: CHECKOUT_LIFECYCLESTATUS.ERROR,
statusData: { error: 'User has insufficient balance' },
},
onSubmit: mockOnSubmit,
});
render(<CheckoutButton />);
expect(screen.getByRole('button').textContent).toBe('Get USDC');
});

it('should call onSubmit when clicked', () => {
render(<CheckoutButton />);
fireEvent.click(screen.getByRole('button'));
Expand Down
8 changes: 1 addition & 7 deletions src/checkout/components/CheckoutButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,8 @@ export function CheckoutButton({
if (lifecycleStatus?.statusName === CHECKOUT_LIFECYCLESTATUS.SUCCESS) {
return 'View payment details';
}
if (
lifecycleStatus?.statusName === CHECKOUT_LIFECYCLESTATUS.ERROR &&
lifecycleStatus?.statusData.error === 'User has insufficient balance'
) {
return 'Get USDC';
}
return text;
}, [lifecycleStatus?.statusName, lifecycleStatus?.statusData, text]);
}, [lifecycleStatus?.statusName, text]);
const shouldRenderIcon = buttonText === text && iconSvg;

return (
Expand Down
34 changes: 9 additions & 25 deletions src/checkout/components/CheckoutProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useAccount, useConnect, useSwitchChain } from 'wagmi';
import { useWaitForTransactionReceipt } from 'wagmi';
import { useCallsStatus } from 'wagmi/experimental';
import { useWriteContracts } from 'wagmi/experimental';
import { openPopup } from '../../internal/utils/openPopup';
import { useOnchainKit } from '../../useOnchainKit';
import { useIsWalletACoinbaseSmartWallet } from '../../wallet/hooks/useIsWalletACoinbaseSmartWallet';
import { GENERIC_ERROR_MESSAGE } from '../constants';
Expand Down Expand Up @@ -42,6 +43,10 @@ vi.mock('../../useOnchainKit', () => ({
useOnchainKit: vi.fn(),
}));

vi.mock('../../internal/utils/openPopup', () => ({
openPopup: vi.fn(),
}));

const windowOpenMock = vi.fn();

const TestComponent = () => {
Expand Down Expand Up @@ -197,23 +202,6 @@ describe('CheckoutProvider', () => {
});
});

it('should handle insufficient balance', async () => {
(useCommerceContracts as Mock).mockReturnValue(() =>
Promise.resolve({ insufficientBalance: true, priceInUSDC: '10' }),
);
render(
<CheckoutProvider>
<TestComponent />
</CheckoutProvider>,
);
fireEvent.click(screen.getByText('Submit'));
await waitFor(() => {
expect(screen.getByTestId('error-message').textContent).toBe(
'You need at least 10 USDC to continue with payment',
);
});
});

it('should handle successful transaction', async () => {
(useWaitForTransactionReceipt as Mock).mockReturnValue({
data: { status: 'success' },
Expand Down Expand Up @@ -431,16 +419,12 @@ describe('CheckoutProvider', () => {
);
fireEvent.click(screen.getByText('Submit'));
await waitFor(() => {
expect(screen.getByTestId('error-message').textContent).toBe(
'You need at least 10 USDC to continue with payment',
expect(openPopup as Mock).toHaveBeenCalledWith(
expect.objectContaining({
url: 'https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=10',
}),
);
});
fireEvent.click(screen.getByText('Submit'));
expect(windowOpenMock).toHaveBeenCalledWith(
'https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=10',
'_blank',
'noopener,noreferrer',
);
});

it('should handle errors when fetching contracts', async () => {
Expand Down
52 changes: 13 additions & 39 deletions src/checkout/components/CheckoutProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { coinbaseWallet } from 'wagmi/connectors';
import { useWriteContracts } from 'wagmi/experimental';
import { useCallsStatus } from 'wagmi/experimental';
import { useValue } from '../../internal/hooks/useValue';
import { getWindowDimensions } from '../../internal/utils/getWindowDimensions';
import { openPopup } from '../../internal/utils/openPopup';
import { isUserRejectedRequestError } from '../../transaction/utils/isUserRejectedRequestError';
import { useOnchainKit } from '../../useOnchainKit';
import { useIsWalletACoinbaseSmartWallet } from '../../wallet/hooks/useIsWalletACoinbaseSmartWallet';
Expand All @@ -23,12 +25,7 @@ import {
NO_CONTRACTS_ERROR,
USER_REJECTED_ERROR,
} from '../constants';
import {
CHECKOUT_INSUFFICIENT_BALANCE_ERROR,
CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE,
CHECKOUT_LIFECYCLESTATUS,
CheckoutErrorCode,
} from '../constants';
import { CHECKOUT_LIFECYCLESTATUS, CheckoutErrorCode } from '../constants';
import { useCommerceContracts } from '../hooks/useCommerceContracts';
import { useLifecycleStatus } from '../hooks/useLifecycleStatus';
import type { CheckoutContextType, CheckoutProviderReact } from '../types';
Expand Down Expand Up @@ -212,26 +209,6 @@ export function CheckoutProvider({
);
return;
}
// Open funding flow
// TODO: Deprecate this once we have USDC Magic Spend
if (
lifecycleStatus.statusName === CHECKOUT_LIFECYCLESTATUS.ERROR &&
lifecycleStatus.statusData?.code ===
CheckoutErrorCode.INSUFFICIENT_BALANCE
) {
window.open(
`https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=${priceInUSDCRef.current}`,
'_blank',
'noopener,noreferrer',
);
// Reset status
setErrorMessage('');
updateLifecycleStatus({
statusName: CHECKOUT_LIFECYCLESTATUS.INIT,
statusData: {},
});
return;
}
if (errorMessage === USER_REJECTED_ERROR) {
// Reset status if previous request was a rejection
setErrorMessage('');
Expand Down Expand Up @@ -285,19 +262,17 @@ export function CheckoutProvider({

// Check for sufficient balance
if (insufficientBalanceRef.current && priceInUSDCRef.current) {
setErrorMessage(
CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE(priceInUSDCRef.current),
);
updateLifecycleStatus({
statusName: CHECKOUT_LIFECYCLESTATUS.ERROR,
statusData: {
code: CheckoutErrorCode.INSUFFICIENT_BALANCE,
error: CHECKOUT_INSUFFICIENT_BALANCE_ERROR,
message: CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE(
priceInUSDCRef.current,
),
},
const { height, width } = getWindowDimensions('md');
openPopup({
url: `https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=${priceInUSDCRef.current}`,
target: '_blank',
height,
width,
});
// Reset state
insufficientBalanceRef.current = false;
priceInUSDCRef.current = undefined;
fetchedDataUseEffect.current = false;
return;
}

Expand Down Expand Up @@ -362,7 +337,6 @@ export function CheckoutProvider({
isConnected,
isSmartWallet,
isSponsored,
lifecycleStatus.statusData,
lifecycleStatus.statusName,
paymaster,
switchChainAsync,
Expand Down
5 changes: 0 additions & 5 deletions src/checkout/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ export const CHECKOUT_TOO_MANY_REQUESTS_ERROR_MESSAGE =

export const CHECKOUT_INSUFFICIENT_BALANCE_ERROR =
'User has insufficient balance';
export const CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE = (
priceInUSD: string,
) => {
return `You need at least ${priceInUSD} USDC to continue with payment`;
};
export const CHECKOUT_INVALID_CHARGE_ERROR_MESSAGE =
'CHECKOUT_INVALID_CHARGE_ERROR';
export const CHECKOUT_INVALID_PARAMETER_ERROR_MESSAGE =
Expand Down

0 comments on commit da7491a

Please sign in to comment.