diff --git a/.changeset/gorgeous-shoes-wash.md b/.changeset/gorgeous-shoes-wash.md new file mode 100644 index 000000000..0fb509e53 --- /dev/null +++ b/.changeset/gorgeous-shoes-wash.md @@ -0,0 +1,7 @@ +--- +'@ant-design/web3-common': minor +'@ant-design/web3-wagmi': minor +'@ant-design/web3': minor +--- + +feat: support useWalletConnectOfficialModal for wagmi provider and add customQrCodePanel for wallet diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index a96ee5d6f..bfc1a74c4 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -126,6 +126,7 @@ export interface Wallet extends WalletMetadata { hasWalletReady?: () => Promise; hasExtensionInstalled?: () => Promise; getQrCode?: () => Promise<{ uri: string }>; + customQrCodePanel?: boolean; } /** diff --git a/packages/wagmi/src/interface.ts b/packages/wagmi/src/interface.ts index 5f6797104..818c148f8 100644 --- a/packages/wagmi/src/interface.ts +++ b/packages/wagmi/src/interface.ts @@ -14,10 +14,17 @@ export interface WalletUseInWagmiAdapter extends Wallet { export type EthereumWallet = (metadata?: Partial) => WalletFactory; +export interface CreateWalletOptions { + useWalletConnectOfficialModal?: boolean; +} + export interface WalletFactory { name?: string; connectors: Connector['name'][]; - create: (connector?: readonly Connector[]) => WalletUseInWagmiAdapter; + create: ( + connector?: readonly Connector[], + options?: CreateWalletOptions, + ) => WalletUseInWagmiAdapter; createWagmiConnector?: () => CreateConnectorFn; } diff --git a/packages/wagmi/src/wagmi-provider/__tests__/basic.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/basic.test.tsx index 1c12cabba..277d1ea70 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/basic.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/basic.test.tsx @@ -8,6 +8,7 @@ import { Polygon, TokenPocket, WagmiWeb3ConfigProvider, + WalletConnect, WalletFactory, type WalletConnectOptions, } from '@ant-design/web3-wagmi'; @@ -15,6 +16,7 @@ import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { http } from 'wagmi'; import { base } from 'wagmi/chains'; +import { walletConnect } from 'wagmi/connectors'; describe('WagmiWeb3ConfigProvider', () => { it('mount correctly', () => { @@ -213,14 +215,23 @@ describe('WagmiWeb3ConfigProvider', () => { const { availableWallets } = useProvider(); return (
- {availableWallets?.map((item) => (item.getQrCode ? 'qrcode' : 'noqrcode')).join(',')} + {availableWallets + ?.map((item) => `${typeof item.getQrCode}-${item.customQrCodePanel}`) + .join(',')}
); }; const App = () => ( { ); const { baseElement } = render(); - expect(baseElement.querySelector('.wallets-qrcode')?.textContent).toBe('qrcode,qrcode,qrcode'); + expect(baseElement.querySelector('.wallets-qrcode')?.textContent).toBe( + 'function-undefined,function-undefined,function-undefined,function-true', + ); + }); + + it('wallet connect with customQrCodePanel', () => { + const CustomConnector: React.FC = () => { + const { availableWallets } = useProvider(); + return ( +
+ {availableWallets + ?.map((item) => `${typeof item.getQrCode}-${item.customQrCodePanel}`) + .join(',')} +
+ ); + }; + + const App = () => ( + + + + ); + const { baseElement } = render(); + expect(baseElement.querySelector('.wallets-qrcode')?.textContent).toBe( + 'function-true,function-true,function-true,function-true', + ); }); it('refresh walletConnect', () => { diff --git a/packages/wagmi/src/wagmi-provider/config-provider.tsx b/packages/wagmi/src/wagmi-provider/config-provider.tsx index 177d4ec33..a6f4ba532 100644 --- a/packages/wagmi/src/wagmi-provider/config-provider.tsx +++ b/packages/wagmi/src/wagmi-provider/config-provider.tsx @@ -33,6 +33,7 @@ export interface AntDesignWeb3ConfigProviderProps { balance?: boolean; eip6963?: EIP6963Config; wagimConfig: WagmiConfig; + useWalletConnectOfficialModal?: boolean; } export const AntDesignWeb3ConfigProvider: React.FC = (props) => { @@ -45,6 +46,7 @@ export const AntDesignWeb3ConfigProvider: React.FC item !== null) as Wallet[]; diff --git a/packages/wagmi/src/wagmi-provider/index.tsx b/packages/wagmi/src/wagmi-provider/index.tsx index 0e34e099c..155acd676 100644 --- a/packages/wagmi/src/wagmi-provider/index.tsx +++ b/packages/wagmi/src/wagmi-provider/index.tsx @@ -138,6 +138,9 @@ export function WagmiWeb3ConfigProvider({ balance={balance} eip6963={eip6963} wagimConfig={wagmiConfig} + useWalletConnectOfficialModal={ + typeof walletConnect === 'object' && walletConnect?.useWalletConnectOfficialModal + } > {children} diff --git a/packages/wagmi/src/wallets/__tests__/mobile-wallet.test.tsx b/packages/wagmi/src/wallets/__tests__/mobile-wallet.test.tsx index df81cd4d8..b3b2d85ad 100644 --- a/packages/wagmi/src/wallets/__tests__/mobile-wallet.test.tsx +++ b/packages/wagmi/src/wallets/__tests__/mobile-wallet.test.tsx @@ -26,7 +26,8 @@ describe('MobileWallet', () => { const wallet = MobileWallet({ useWalletConnectOfficialModal: true, }).create(); - expect(wallet.getQrCode).toBe(undefined); + expect(wallet.getQrCode).toBeTruthy(); + expect(wallet.customQrCodePanel).toBeTruthy(); }); it('get qr code', async () => { diff --git a/packages/wagmi/src/wallets/__tests__/wallet-connect.test.tsx b/packages/wagmi/src/wallets/__tests__/wallet-connect.test.tsx index 5c2f8f43c..b2edd137f 100644 --- a/packages/wagmi/src/wallets/__tests__/wallet-connect.test.tsx +++ b/packages/wagmi/src/wallets/__tests__/wallet-connect.test.tsx @@ -56,7 +56,8 @@ describe('WalletConnect', async () => { const wallet = WalletConnect({ useWalletConnectOfficialModal: true, }).create(); - expect(wallet.getQrCode).toBe(undefined); + expect(wallet.getQrCode).toBeTruthy(); + expect(wallet.customQrCodePanel).toBeTruthy(); }); it('get qr code', async () => { diff --git a/packages/wagmi/src/wallets/universal-wallet.tsx b/packages/wagmi/src/wallets/universal-wallet.tsx index 1e3da24b8..c7e43ee13 100644 --- a/packages/wagmi/src/wallets/universal-wallet.tsx +++ b/packages/wagmi/src/wallets/universal-wallet.tsx @@ -1,7 +1,7 @@ import type { WalletMetadata } from '@ant-design/web3-common'; import type { Connector, CreateConnectorFn } from 'wagmi'; -import type { WalletFactory, WalletUseInWagmiAdapter } from '../interface'; +import type { CreateWalletOptions, WalletFactory, WalletUseInWagmiAdapter } from '../interface'; export class UniversalWallet implements WalletFactory { public name?: string; @@ -23,7 +23,10 @@ export class UniversalWallet implements WalletFactory { this.connectors.push('WalletConnect'); } } - create = (connectors?: readonly Connector[]): WalletUseInWagmiAdapter => { + create = ( + connectors?: readonly Connector[], + options?: CreateWalletOptions, + ): WalletUseInWagmiAdapter => { const walletConnector = connectors?.find((item) => item.name === 'WalletConnect'); const injectedConnector = connectors?.find((item) => item.name === this.wallet.name); @@ -53,8 +56,8 @@ export class UniversalWallet implements WalletFactory { return { ...this.wallet, - getWagmiConnector: async (options) => { - if (options?.connectType === 'qrCode') { + getWagmiConnector: async (connectOptions) => { + if (connectOptions?.connectType === 'qrCode') { return walletConnector; } if (await hasExtensionInstalled()) { @@ -69,6 +72,7 @@ export class UniversalWallet implements WalletFactory { const installed = await hasExtensionInstalled(); return !!(installed || walletConnector); }, + customQrCodePanel: options?.useWalletConnectOfficialModal, getQrCode: walletConnector ? getQrCode : undefined, }; }; diff --git a/packages/wagmi/src/wallets/wallet-connect.ts b/packages/wagmi/src/wallets/wallet-connect.ts index 7a6756ec0..969a3e2f2 100644 --- a/packages/wagmi/src/wallets/wallet-connect.ts +++ b/packages/wagmi/src/wallets/wallet-connect.ts @@ -14,7 +14,7 @@ export const WalletConnect: EthereumWalletConnect = (metadata) => { const { useWalletConnectOfficialModal = false, ...rest } = metadata || {}; return { connectors: ['WalletConnect'], - create: (connectors?: readonly Connector[]): WalletUseInWagmiAdapter => { + create: (connectors?: readonly Connector[], options = {}): WalletUseInWagmiAdapter => { let qrCodeUri: string | undefined = undefined; const getQrCode = async () => { const provider = await connectors?.[0]?.getProvider(); @@ -41,7 +41,8 @@ export const WalletConnect: EthereumWalletConnect = (metadata) => { hasWalletReady: async () => { return true; }, - getQrCode: useWalletConnectOfficialModal ? undefined : getQrCode, + getQrCode: getQrCode, + customQrCodePanel: useWalletConnectOfficialModal || options.useWalletConnectOfficialModal, ...rest, }; }, diff --git a/packages/web3/src/connect-modal/__tests__/qrcode.test.tsx b/packages/web3/src/connect-modal/__tests__/qrcode.test.tsx index 8125a55af..111201d35 100644 --- a/packages/web3/src/connect-modal/__tests__/qrcode.test.tsx +++ b/packages/web3/src/connect-modal/__tests__/qrcode.test.tsx @@ -74,4 +74,35 @@ describe('ConnectModal with qrcode', () => { 'wallet Wallect Connect app is undefined, please check your config.', ); }); + + it('customQrCodePanel', async () => { + const App = () => ( + + Promise.resolve({ + uri: 'wc:f7a2ae968db3720de3efa7f088a3a05a746c011bb972a4dae0a61abe66632e3d@2?relay-protocol=irn&symKey=85bf278d8a4240154c449939eb047863585619c9c7acaa78657606d66c5630b3', + }), + customQrCodePanel: true, + }, + ]} + guide={guide} + /> + ); + const { baseElement } = render(); + + fireEvent.click(baseElement.querySelector('.ant-web3-connect-modal-wallet-item')!); + expect(baseElement.querySelector('.ant-web3-connect-modal-qr-code-link-loading')).toBeNull(); + }); }); diff --git a/packages/web3/src/connect-modal/components/ModalPanel.tsx b/packages/web3/src/connect-modal/components/ModalPanel.tsx index 0927d24d6..e24bdafe4 100644 --- a/packages/web3/src/connect-modal/components/ModalPanel.tsx +++ b/packages/web3/src/connect-modal/components/ModalPanel.tsx @@ -64,7 +64,7 @@ const ModalPanel: React.FC = (props) => { async (wallet?: Wallet, connectOptions?: ConnectOptions) => { setSelectedWallet(wallet); if (wallet && connectOptions) { - if (connectOptions.connectType === 'qrCode') { + if (connectOptions.connectType === 'qrCode' && !wallet.customQrCodePanel) { updatePanelRoute('qrCode', true); } else if (connectOptions.connectType === 'extension') { updatePanelRoute('link', true); diff --git a/packages/web3/src/connector/__tests__/basic.test.tsx b/packages/web3/src/connector/__tests__/basic.test.tsx index fe89f4271..ecf181112 100644 --- a/packages/web3/src/connector/__tests__/basic.test.tsx +++ b/packages/web3/src/connector/__tests__/basic.test.tsx @@ -5,7 +5,7 @@ import { type Account, type ConnectorTriggerProps, } from '@ant-design/web3'; -import { metadata_MetaMask } from '@ant-design/web3-assets'; +import { metadata_MetaMask, metadata_WalletConnect } from '@ant-design/web3-assets'; import { fireEvent, render } from '@testing-library/react'; import { Button } from 'antd'; import { describe, expect, it, vi } from 'vitest'; @@ -15,7 +15,13 @@ describe('Connector', () => { const onCancelCallTest = vi.fn(); const App = () => { return ( - + ); @@ -183,6 +189,74 @@ describe('Connector', () => { expect(baseElement.querySelector('.ant-btn')?.textContent).toBe('children'); }); + it('customQrCodePanel', async () => { + const onConnectCallTest = vi.fn(); + const afterOpenChangeTest = vi.fn(); + const CustomButton: React.FC> = (props) => { + const { account, onConnectClick, onDisconnectClick, children } = props; + return ( + + ); + }; + + const App = () => { + const [account, setAccount] = React.useState(); + return ( + { + return true; + }, + getQrCode: async () => { + return { + uri: 'mock qr code', + }; + }, + customQrCodePanel: true, + }, + ]} + onConnect={onConnectCallTest} + connect={() => { + return new Promise(() => {}); + }} + disconnect={async () => { + setAccount(undefined); + }} + > + children + + ); + }; + const { baseElement } = render(); + expect(baseElement.querySelector('.ant-btn')?.textContent).toBe('children'); + fireEvent.click(baseElement.querySelector('.ant-btn')!); + + await vi.waitFor(() => { + expect(baseElement.querySelector('.ant-web3-connect-modal-wallet-item')).toBeTruthy(); + }); + fireEvent.click(baseElement.querySelector('.ant-web3-connect-modal-wallet-item')!); + + await vi.waitFor(() => { + expect(onConnectCallTest).toBeCalled(); + }); + }); + it('connect without return account info', async () => { const onConnectCallTest = vi.fn(); const onDisconnected = vi.fn(); diff --git a/packages/web3/src/connector/connector.tsx b/packages/web3/src/connector/connector.tsx index 323b68d6b..904b05f54 100644 --- a/packages/web3/src/connector/connector.tsx +++ b/packages/web3/src/connector/connector.tsx @@ -38,6 +38,9 @@ export const Connector: React.FC = (props) => { onConnect?.(); try { setConnecting(true); + if (wallet?.customQrCodePanel && options?.connectType === 'qrCode') { + setOpen(false); + } const connectedAccount = await connect?.(wallet, options); onConnected?.(connectedAccount ? connectedAccount : undefined); setOpen(false); diff --git a/packages/web3/src/ethereum/demos/web3modal.tsx b/packages/web3/src/ethereum/demos/web3modal.tsx index 3aeaa4c8e..76c5b1997 100644 --- a/packages/web3/src/ethereum/demos/web3modal.tsx +++ b/packages/web3/src/ethereum/demos/web3modal.tsx @@ -1,31 +1,14 @@ import { ConnectButton, Connector } from '@ant-design/web3'; -import { WagmiWeb3ConfigProvider, WalletConnect } from '@ant-design/web3-wagmi'; -import { createConfig, http } from 'wagmi'; -import { mainnet } from 'wagmi/chains'; -import { walletConnect } from 'wagmi/connectors'; - -const config = createConfig({ - chains: [mainnet], - transports: { - [mainnet.id]: http(), - }, - connectors: [ - walletConnect({ - showQrModal: true, - projectId: YOUR_WALLET_CONNECT_PROJECT_ID, - }), - ], -}); +import { MetaMask, WagmiWeb3ConfigProvider, WalletConnect } from '@ant-design/web3-wagmi'; const App: React.FC = () => { return ( diff --git a/packages/web3/src/types/index.md b/packages/web3/src/types/index.md index 83b666ef4..a71b3302e 100644 --- a/packages/web3/src/types/index.md +++ b/packages/web3/src/types/index.md @@ -53,6 +53,7 @@ This is an enum type that contains the IDs of some commonly used chains. Its val | hasWalletReady | Whether the wallet is ready | `() => boolean` | - | - | | hasExtensionInstalled | Whether the browser extension is installed | `() => boolean` | - | - | | getQrCode | Get the QR code of the wallet | `() => { uri: string }` | - | - | +| customQrCodePanel | Custom QR code display panel | `boolean` | `false` | `1.17.0` | | universalProtocol | Universal protocol config | `{ link: string }` | - | - | ## ExtensionItem diff --git a/packages/web3/src/types/index.zh-CN.md b/packages/web3/src/types/index.zh-CN.md index ed7ef48fa..877a8ae81 100644 --- a/packages/web3/src/types/index.zh-CN.md +++ b/packages/web3/src/types/index.zh-CN.md @@ -54,6 +54,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_mutawc/afts/img/A*n4F2RK3AVTsAAA | hasWalletReady | 钱包是否已经准备好 | `() => boolean` | - | - | | hasExtensionInstalled | 是否安装浏览器扩展程序 | `() => boolean` | - | - | | getQrCode | 获取钱包的二维码 | `() => { uri: string }` | - | - | +| customQrCodePanel | 自定义二维码展示的面板 | `boolean` | `false` | `1.17.0` | | universalProtocol | 通用协议配置 | `{ link: string }` | - | - | ### ExtensionItem