diff --git a/.changeset/green-coins-add.md b/.changeset/green-coins-add.md new file mode 100644 index 0000000000..42dd3a651c --- /dev/null +++ b/.changeset/green-coins-add.md @@ -0,0 +1,5 @@ +--- +'@coinbase/onchainkit': patch +--- + +- **feat**: Add Phantom Wallet connection support in `WalletModal`. By @cpcramer #1770 diff --git a/site/docs/public/assets/wallet-modal.png b/site/docs/public/assets/wallet-modal.png index 3b4c7657cf..d249aff53c 100644 Binary files a/site/docs/public/assets/wallet-modal.png and b/site/docs/public/assets/wallet-modal.png differ diff --git a/src/internal/svg/phantomSvg.tsx b/src/internal/svg/phantomSvg.tsx new file mode 100644 index 0000000000..c097b9d9f2 --- /dev/null +++ b/src/internal/svg/phantomSvg.tsx @@ -0,0 +1,26 @@ +export const phantomSvg = ( + + Phantom Logo + + + + + + + + + + +); diff --git a/src/wallet/components/WalletModal.test.tsx b/src/wallet/components/WalletModal.test.tsx index ea3e2a78e3..f6408e1380 100644 --- a/src/wallet/components/WalletModal.test.tsx +++ b/src/wallet/components/WalletModal.test.tsx @@ -27,6 +27,9 @@ vi.mock('../../core-react/useOnchainKit', () => ({ vi.mock('wagmi/connectors', () => ({ coinbaseWallet: () => ({ preference: 'all' }), metaMask: ({ dappMetadata }: MetaMaskParameters) => ({ dappMetadata }), + injected: ({ target }: { target: string }) => ({ + target, + }), })); describe('WalletModal', () => { @@ -493,4 +496,62 @@ describe('WalletModal', () => { 'Some string error', ); }); + + it('connects with Phantom when clicking Phantom button', () => { + render(); + + fireEvent.click(screen.getByText('Phantom')); + + expect(mockConnect).toHaveBeenCalledWith({ + connector: { + target: 'phantom', + }, + }); + expect(mockOnClose).toHaveBeenCalled(); + }); + + it('handles Phantom connection errors', () => { + const mockError = new Error('Phantom connection failed'); + const mockOnError = vi.fn(); + (useConnect as Mock).mockReturnValue({ + connect: vi.fn(() => { + throw mockError; + }), + }); + + render( + , + ); + + fireEvent.click(screen.getByText('Phantom')); + + expect(mockOnError).toHaveBeenCalledWith(mockError); + expect(console.error).toHaveBeenCalledWith( + 'Phantom connection error:', + mockError, + ); + }); + + it('handles non-Error objects in Phantom connection errors', () => { + const mockOnError = vi.fn(); + (useConnect as Mock).mockReturnValue({ + connect: vi.fn(() => { + throw 'Some string error'; + }), + }); + + render( + , + ); + + fireEvent.click(screen.getByText('Phantom')); + + expect(mockOnError).toHaveBeenCalledWith( + new Error('Failed to connect wallet'), + ); + expect(console.error).toHaveBeenCalledWith( + 'Phantom connection error:', + 'Some string error', + ); + }); }); diff --git a/src/wallet/components/WalletModal.tsx b/src/wallet/components/WalletModal.tsx index 2127bb586e..d7fc1b6b19 100644 --- a/src/wallet/components/WalletModal.tsx +++ b/src/wallet/components/WalletModal.tsx @@ -1,11 +1,12 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useConnect } from 'wagmi'; -import { coinbaseWallet, metaMask } from 'wagmi/connectors'; +import { coinbaseWallet, injected, metaMask } from 'wagmi/connectors'; import { useOnchainKit } from '../../core-react/useOnchainKit'; import { closeSvg } from '../../internal/svg/closeSvg'; import { coinbaseWalletSvg } from '../../internal/svg/coinbaseWalletSvg'; import { defaultAvatarSVG } from '../../internal/svg/defaultAvatarSVG'; import { metamaskSvg } from '../../internal/svg/metamaskSvg'; +import { phantomSvg } from '../../internal/svg/phantomSvg'; import { background, border, @@ -125,6 +126,22 @@ export function WalletModal({ } }, [connect, onClose, onError, appName, appLogo]); + const handlePhantomConnection = useCallback(() => { + try { + const phantomConnector = injected({ + target: 'phantom', + }); + + connect({ connector: phantomConnector }); + onClose(); + } catch (error) { + console.error('Phantom connection error:', error); + onError?.( + error instanceof Error ? error : new Error('Failed to connect wallet'), + ); + } + }, [connect, onClose, onError]); + const handleLinkKeyDown = ( event: React.KeyboardEvent, url: string, @@ -278,6 +295,24 @@ export function WalletModal({ {metamaskSvg} + +