Skip to content

Commit

Permalink
feat: support useWalletConnectOfficialModal for wagmi provider and ad…
Browse files Browse the repository at this point in the history
…d customQrCodePanel for wallet (#1151)

* fix: support useWalletConnectOfficialModal

* test: add useWalletConnectOfficialModal test case

* chore: add changelog

* fix: tslint error

* docs: update en doc

* fix: lint issue

* fix: for test bad case

* chore: fix test cov

---------

Co-authored-by: tingzhao.ytz <[email protected]>
  • Loading branch information
yutingzhao1991 and tingzhao.ytz authored Sep 14, 2024
1 parent 613b265 commit c32f8e2
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 40 deletions.
7 changes: 7 additions & 0 deletions .changeset/gorgeous-shoes-wash.md
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface Wallet extends WalletMetadata {
hasWalletReady?: () => Promise<boolean>;
hasExtensionInstalled?: () => Promise<boolean>;
getQrCode?: () => Promise<{ uri: string }>;
customQrCodePanel?: boolean;
}

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/wagmi/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ export interface WalletUseInWagmiAdapter extends Wallet {

export type EthereumWallet = (metadata?: Partial<WalletMetadata>) => 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;
}

Expand Down
54 changes: 51 additions & 3 deletions packages/wagmi/src/wagmi-provider/__tests__/basic.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {
Polygon,
TokenPocket,
WagmiWeb3ConfigProvider,
WalletConnect,
WalletFactory,
type WalletConnectOptions,
} from '@ant-design/web3-wagmi';
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', () => {
Expand Down Expand Up @@ -213,14 +215,23 @@ describe('WagmiWeb3ConfigProvider', () => {
const { availableWallets } = useProvider();
return (
<div className="wallets-qrcode">
{availableWallets?.map((item) => (item.getQrCode ? 'qrcode' : 'noqrcode')).join(',')}
{availableWallets
?.map((item) => `${typeof item.getQrCode}-${item.customQrCodePanel}`)
.join(',')}
</div>
);
};

const App = () => (
<WagmiWeb3ConfigProvider
wallets={[MetaMask(), TokenPocket(), OkxWallet()]}
wallets={[
MetaMask(),
TokenPocket(),
OkxWallet(),
WalletConnect({
useWalletConnectOfficialModal: true,
}),
]}
chains={[Polygon, Goerli, Mainnet]}
walletConnect={{
projectId: 'test',
Expand All @@ -235,7 +246,44 @@ describe('WagmiWeb3ConfigProvider', () => {
</WagmiWeb3ConfigProvider>
);
const { baseElement } = render(<App />);
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 (
<div className="wallets-qrcode">
{availableWallets
?.map((item) => `${typeof item.getQrCode}-${item.customQrCodePanel}`)
.join(',')}
</div>
);
};

const App = () => (
<WagmiWeb3ConfigProvider
wallets={[MetaMask(), TokenPocket(), OkxWallet(), WalletConnect()]}
chains={[Polygon, Goerli, Mainnet]}
walletConnect={{
projectId: 'test',
useWalletConnectOfficialModal: true,
}}
transports={{
[Goerli.id]: http(),
[Mainnet.id]: http(),
[Polygon.id]: http(),
}}
>
<CustomConnector />
</WagmiWeb3ConfigProvider>
);
const { baseElement } = render(<App />);
expect(baseElement.querySelector('.wallets-qrcode')?.textContent).toBe(
'function-true,function-true,function-true,function-true',
);
});

it('refresh walletConnect', () => {
Expand Down
12 changes: 10 additions & 2 deletions packages/wagmi/src/wagmi-provider/config-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface AntDesignWeb3ConfigProviderProps {
balance?: boolean;
eip6963?: EIP6963Config;
wagimConfig: WagmiConfig;
useWalletConnectOfficialModal?: boolean;
}

export const AntDesignWeb3ConfigProvider: React.FC<AntDesignWeb3ConfigProviderProps> = (props) => {
Expand All @@ -45,6 +46,7 @@ export const AntDesignWeb3ConfigProvider: React.FC<AntDesignWeb3ConfigProviderPr
locale,
eip6963,
wagimConfig,
useWalletConnectOfficialModal,
} = props;
const { address, isDisconnected, chain } = useAccount();
const config = useConfig();
Expand Down Expand Up @@ -93,7 +95,11 @@ export const AntDesignWeb3ConfigProvider: React.FC<AntDesignWeb3ConfigProviderPr
)
) {
// not config wallet and find the wallet in connectors, auto add it
autoAddEIP6963Wallets.push(EIP6963Wallet().create([connector]));
autoAddEIP6963Wallets.push(
EIP6963Wallet().create([connector], {
useWalletConnectOfficialModal,
}),
);
}
// Do not need check eip6963 wallet
return;
Expand Down Expand Up @@ -127,7 +133,9 @@ export const AntDesignWeb3ConfigProvider: React.FC<AntDesignWeb3ConfigProviderPr
);
return null;
}
return factory.create(connectors);
return factory.create(connectors, {
useWalletConnectOfficialModal,
});
})
.filter((item) => item !== null) as Wallet[];

Expand Down
3 changes: 3 additions & 0 deletions packages/wagmi/src/wagmi-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ export function WagmiWeb3ConfigProvider({
balance={balance}
eip6963={eip6963}
wagimConfig={wagmiConfig}
useWalletConnectOfficialModal={
typeof walletConnect === 'object' && walletConnect?.useWalletConnectOfficialModal
}
>
{children}
</AntDesignWeb3ConfigProvider>
Expand Down
3 changes: 2 additions & 1 deletion packages/wagmi/src/wallets/__tests__/mobile-wallet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/wagmi/src/wallets/__tests__/wallet-connect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
12 changes: 8 additions & 4 deletions packages/wagmi/src/wallets/universal-wallet.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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()) {
Expand All @@ -69,6 +72,7 @@ export class UniversalWallet implements WalletFactory {
const installed = await hasExtensionInstalled();
return !!(installed || walletConnector);
},
customQrCodePanel: options?.useWalletConnectOfficialModal,
getQrCode: walletConnector ? getQrCode : undefined,
};
};
Expand Down
5 changes: 3 additions & 2 deletions packages/wagmi/src/wallets/wallet-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -41,7 +41,8 @@ export const WalletConnect: EthereumWalletConnect = (metadata) => {
hasWalletReady: async () => {
return true;
},
getQrCode: useWalletConnectOfficialModal ? undefined : getQrCode,
getQrCode: getQrCode,
customQrCodePanel: useWalletConnectOfficialModal || options.useWalletConnectOfficialModal,
...rest,
};
},
Expand Down
31 changes: 31 additions & 0 deletions packages/web3/src/connect-modal/__tests__/qrcode.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,35 @@ describe('ConnectModal with qrcode', () => {
'wallet Wallect Connect app is undefined, please check your config.',
);
});

it('customQrCodePanel', async () => {
const App = () => (
<ConnectModal
open
title="ConnectModal"
footer="Powered by AntChain"
walletList={[
{
icon: 'https://xsgames.co/randomusers/avatar.php?g=pixel&key=0',
name: 'Wallect Connect',
remark: '备注',
app: {
link: 'https://test.com/xxx',
},
group: 'Popular',
getQrCode: () =>
Promise.resolve({
uri: 'wc:f7a2ae968db3720de3efa7f088a3a05a746c011bb972a4dae0a61abe66632e3d@2?relay-protocol=irn&symKey=85bf278d8a4240154c449939eb047863585619c9c7acaa78657606d66c5630b3',
}),
customQrCodePanel: true,
},
]}
guide={guide}
/>
);
const { baseElement } = render(<App />);

fireEvent.click(baseElement.querySelector('.ant-web3-connect-modal-wallet-item')!);
expect(baseElement.querySelector('.ant-web3-connect-modal-qr-code-link-loading')).toBeNull();
});
});
2 changes: 1 addition & 1 deletion packages/web3/src/connect-modal/components/ModalPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const ModalPanel: React.FC<ModalPanelProps> = (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);
Expand Down
78 changes: 76 additions & 2 deletions packages/web3/src/connector/__tests__/basic.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,7 +15,13 @@ describe('Connector', () => {
const onCancelCallTest = vi.fn();
const App = () => {
return (
<Connector modalProps={{ title: 'modal title', open: true, onCancel: onCancelCallTest }}>
<Connector
modalProps={{
title: 'modal title',
open: true,
onCancel: onCancelCallTest,
}}
>
<Button>children</Button>
</Connector>
);
Expand Down Expand Up @@ -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<React.PropsWithChildren<ConnectorTriggerProps>> = (props) => {
const { account, onConnectClick, onDisconnectClick, children } = props;
return (
<Button
onClick={() => {
if (account) {
onDisconnectClick?.();
} else {
onConnectClick?.();
}
}}
>
{account?.address ?? children}
</Button>
);
};

const App = () => {
const [account, setAccount] = React.useState<Account | undefined>();
return (
<Connector
account={account}
modalProps={{
afterOpenChange: afterOpenChangeTest,
}}
availableWallets={[
{
...metadata_WalletConnect,
hasWalletReady: async () => {
return true;
},
getQrCode: async () => {
return {
uri: 'mock qr code',
};
},
customQrCodePanel: true,
},
]}
onConnect={onConnectCallTest}
connect={() => {
return new Promise(() => {});
}}
disconnect={async () => {
setAccount(undefined);
}}
>
<CustomButton>children</CustomButton>
</Connector>
);
};
const { baseElement } = render(<App />);
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();
Expand Down
Loading

0 comments on commit c32f8e2

Please sign in to comment.