diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index bfc1a74c4..47274b798 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1,3 +1,5 @@ +import { type CreateSiweMessageParameters } from 'viem/siwe'; + export interface Account { address: string; name?: string; @@ -119,6 +121,7 @@ export interface UniversalWeb3ProviderInterface { // For Bitcoin, tokenId is undefined. getNFTMetadata?: (params: { address: string; tokenId?: bigint }) => Promise; + swie?: SWIEConfig; } export interface Wallet extends WalletMetadata { @@ -327,3 +330,24 @@ export type Token = { contract?: string; }[]; }; + +export interface SignConfig { + // required + signIn: (options: { address: string; chainId: number }) => Promise; + signOut: () => Promise; + + // WIP: optional + // signOutOnDisconnect?: boolean; // defaults true + // signOutOnAccountChange?: boolean; // defaults true + // signOutOnNetworkChange?: boolean; // defaults true +} + +export interface SWIEConfig { + getNonce: (address: string, chainId?: number) => Promise; + createMessage: (args: CreateSiweMessageParameters) => string; + verifyMessage: (message: string, signature: string) => Promise; + + // WIP: optional + // getSession?: () => Promise; + // signOut?: () => Promise; +} diff --git a/packages/common/src/web3-config-provider/index.tsx b/packages/common/src/web3-config-provider/index.tsx index a30839bb3..e69f2c79e 100644 --- a/packages/common/src/web3-config-provider/index.tsx +++ b/packages/common/src/web3-config-provider/index.tsx @@ -8,7 +8,6 @@ const ProviderChildren: React.FC< ConfigConsumerProps & { children?: React.ReactNode; parentContext?: ConfigConsumerProps } > = (props) => { const { children, parentContext, ...rest } = props; - const config = { ...parentContext }; Object.keys(rest).forEach((key) => { diff --git a/packages/wagmi/src/wagmi-provider/config-provider.tsx b/packages/wagmi/src/wagmi-provider/config-provider.tsx index a6f4ba532..364d7cfdd 100644 --- a/packages/wagmi/src/wagmi-provider/config-provider.tsx +++ b/packages/wagmi/src/wagmi-provider/config-provider.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { + SWIEConfig, Web3ConfigProvider, type Account, type Chain, @@ -34,6 +35,7 @@ export interface AntDesignWeb3ConfigProviderProps { eip6963?: EIP6963Config; wagimConfig: WagmiConfig; useWalletConnectOfficialModal?: boolean; + swie?: SWIEConfig; } export const AntDesignWeb3ConfigProvider: React.FC = (props) => { @@ -47,6 +49,7 @@ export const AntDesignWeb3ConfigProvider: React.FC; -}; + swie?: SWIEConfig; +} export function WagmiWeb3ConfigProvider({ children, @@ -54,6 +55,7 @@ export function WagmiWeb3ConfigProvider({ eip6963, walletConnect, transports, + swie, ...restProps }: React.PropsWithChildren): React.ReactElement { // When user custom config, add Mainnet by default @@ -63,7 +65,6 @@ export function WagmiWeb3ConfigProvider({ : chains?.length ? chains : [Mainnet]; - const generateConfigFlag = () => { return `${JSON.stringify(walletConnect)}-${chains.map((item) => item.id).join(',')}-${wallets.map((item) => item.name).join(',')}`; }; @@ -138,6 +139,7 @@ export function WagmiWeb3ConfigProvider({ balance={balance} eip6963={eip6963} wagimConfig={wagmiConfig} + swie={swie} useWalletConnectOfficialModal={ typeof walletConnect === 'object' && walletConnect?.useWalletConnectOfficialModal } diff --git a/packages/web3/src/ethereum/demos/siwe/index.tsx b/packages/web3/src/ethereum/demos/siwe/index.tsx index cff0276a8..94bcea851 100644 --- a/packages/web3/src/ethereum/demos/siwe/index.tsx +++ b/packages/web3/src/ethereum/demos/siwe/index.tsx @@ -1,4 +1,3 @@ -import { ConnectButton, Connector } from '@ant-design/web3'; import { Mainnet, MetaMask, @@ -8,8 +7,10 @@ import { WalletConnect, } from '@ant-design/web3-wagmi'; import { QueryClient } from '@tanstack/react-query'; +import { createSiweMessage } from 'viem/siwe'; import { http } from 'wagmi'; +import { getNonce, verifyMessage } from './mock-api'; import SignBtn from './sign-btn'; const queryClient = new QueryClient(); @@ -17,6 +18,11 @@ const queryClient = new QueryClient(); const App: React.FC = () => { return ( createSiweMessage({ ...props, statement: 'Ant Design Web3' }), + verifyMessage: verifyMessage, + }} eip6963={{ autoAddInjectedWallets: true, }} diff --git a/packages/web3/src/ethereum/demos/siwe/sign-btn.tsx b/packages/web3/src/ethereum/demos/siwe/sign-btn.tsx index 60db1157e..e010bb8cd 100644 --- a/packages/web3/src/ethereum/demos/siwe/sign-btn.tsx +++ b/packages/web3/src/ethereum/demos/siwe/sign-btn.tsx @@ -1,60 +1,11 @@ -import { useCallback, useState } from 'react'; -import { Account, ConnectButton, Connector, useAccount } from '@ant-design/web3'; -import { Mainnet } from '@ant-design/web3-wagmi'; -import { Button, message, Space } from 'antd'; -import { createSiweMessage } from 'viem/siwe'; -import { useSignMessage } from 'wagmi'; +import { ConnectButton, Connector, useAccount } from '@ant-design/web3'; +import { Button, Space } from 'antd'; -import { getNonce, verifyMessage } from './mock-api'; +import { useSwie } from './useSwie'; export default function App() { const { account } = useAccount(); - - const [signed, setSigned] = useState(false); - const [signLoading, setSignLoading] = useState(false); - const { signMessageAsync } = useSignMessage(); - - const signIn = useCallback(async (a?: Account) => { - const address = a?.address as `0x${string}`; - - if (!address) { - message.error('Please connect wallet first.'); - return; - } - - // get nonce - const nonce = await getNonce(address); - if (!nonce) { - message.error('Failed to get nonce.'); - return; - } - - let msg: string; - let signature: `0x${string}`; - - try { - msg = createSiweMessage({ - domain: window.location.hostname, - address, - statement: 'Sign in with Ethereum', - uri: window.location.origin, - version: '1', - chainId: Mainnet.id, - nonce, - }); - setSignLoading(true); - console.log('signing message'); - signature = await signMessageAsync({ message: msg }); - console.log('get signature', signature); - await verifyMessage(msg!, signature!); - message.success('Sign in successfully.'); - setSigned(true); - setSignLoading(false); - } catch (error: any) { - message.error(error.message); - setSignLoading(false); - } - }, []); + const { signLoading, signed, signIn, setSigned } = useSwie(); return ( diff --git a/packages/web3/src/ethereum/demos/siwe/useSwie.tsx b/packages/web3/src/ethereum/demos/siwe/useSwie.tsx new file mode 100644 index 000000000..1b68983ad --- /dev/null +++ b/packages/web3/src/ethereum/demos/siwe/useSwie.tsx @@ -0,0 +1,67 @@ +import { useCallback, useState } from 'react'; +import { Account, useProvider } from '@ant-design/web3'; +import { Mainnet } from '@ant-design/web3-wagmi'; +import { message } from 'antd'; +import { useSignMessage } from 'wagmi'; + +const DefaultSignInfo = {}; + +export const useSwie = () => { + const { swie } = useProvider(); + const [signLoading, setSignLoading] = useState(false); + const { signMessageAsync } = useSignMessage(); + const [signed, setSigned] = useState(false); + + const signIn = useCallback(async (a?: Account) => { + if (!swie) return; + + const { getNonce, createMessage, verifyMessage } = swie; + + const address = a?.address as `0x${string}`; + + if (!address) { + message.error('Please connect wallet first.'); + return; + } + + // get nonce + const nonce = await getNonce?.(address); + if (!nonce) { + message.error('Failed to get nonce.'); + return; + } + + let msg: string; + let signature: `0x${string}`; + + try { + msg = createMessage({ + domain: window.location.hostname, + address, + uri: window.location.origin, + nonce, + // Default config + version: '1', + chainId: Mainnet.id, + }); + setSignLoading(true); + console.log('signing message'); + signature = await signMessageAsync({ message: msg }); + console.log('get signature', signature); + await verifyMessage(msg!, signature!); + message.success('Sign in successfully.'); + setSigned(true); + setSignLoading(false); + } catch (error: any) { + message.error(error.message); + setSignLoading(false); + } + }, []); + + return { + signLoading, + signed, + signIn, + setSigned, + }; +};