Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add siwe #1214

Merged
merged 20 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/friendly-pandas-burn.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: wagmi add siwe config
1 change: 1 addition & 0 deletions packages/common/src/locale/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const localeValues: RequiredLocale = {
copied: 'Copied!',
LCJove marked this conversation as resolved.
Show resolved Hide resolved
walletAddress: 'Wallet address',
moreWallets: 'More Wallets',
sign: 'Sign',
},
ConnectModal: {
title: 'Connect Wallet',
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/locale/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const localeValues: RequiredLocale = {
copied: '复制成功!',
walletAddress: '钱包地址',
moreWallets: '更多钱包',
sign: '签名',
},
ConnectModal: {
title: '连接钱包',
Expand Down
23 changes: 22 additions & 1 deletion packages/common/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
export const enum ConnectStatus {
Connected = 'connected',
Disconnected = 'disconnected',
Signed = 'signed',
}

export interface Account {
address: string;
name?: string;
avatar?: string;
addresses?: readonly [`0x${string}`, ...`0x${string}`[]];
addresses?: [`0x${string}`, ...`0x${string}`[]] | readonly `0x${string}`[];
status?: ConnectStatus;
}

export enum ChainIds {
Expand Down Expand Up @@ -119,6 +126,9 @@ export interface UniversalWeb3ProviderInterface {

// For Bitcoin, tokenId is undefined.
getNFTMetadata?: (params: { address: string; tokenId?: bigint }) => Promise<NFTMetadata>;

// For Sign
sign?: SignConfig;
}

export interface Wallet extends WalletMetadata {
Expand Down Expand Up @@ -247,6 +257,7 @@ export interface RequiredLocale {
copied: string;
walletAddress: string;
moreWallets: string;
sign: string;
};
ConnectModal: {
title: string;
Expand Down Expand Up @@ -328,3 +339,13 @@ export type Token = {
contract?: string;
}[];
};

export interface SignConfig {
// required
signIn: (address: string) => Promise<void>;
signOut?: () => Promise<void>;

// signOutOnDisconnect?: boolean; // defaults true
// signOutOnAccountChange?: boolean; // defaults true
// signOutOnNetworkChange?: boolean; // defaults true
}
1 change: 0 additions & 1 deletion packages/common/src/web3-config-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
11 changes: 11 additions & 0 deletions packages/wagmi/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
WalletMetadata,
} from '@ant-design/web3-common';
import type { Chain as WagmiChain } from 'viem';
import type { CreateSiweMessageParameters } from 'viem/siwe';
import type { Connector, CreateConnectorFn } from 'wagmi';

export interface WalletUseInWagmiAdapter extends Wallet {
Expand All @@ -31,3 +32,13 @@ export interface WalletFactory {
export type EIP6963Config = boolean | UniversalEIP6963Config;

export type ChainAssetWithWagmiChain = Chain & { wagmiChain?: WagmiChain };

export interface SIWEConfig {
getNonce: (address: string, chainId?: number) => Promise<string>;
createMessage: (args: CreateSiweMessageParameters) => string;
verifyMessage: (message: string, signature: string) => Promise<boolean>;

// WIP: optional
LCJove marked this conversation as resolved.
Show resolved Hide resolved
// getSession?: () => Promise<SIWESession | null>;
// signOut?: () => Promise<boolean>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from 'react';
import { Connector, Web3ConfigProvider, type ConnectorTriggerProps } from '@ant-design/web3';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { Button } from 'antd';
import { describe, expect, it, vi } from 'vitest';
import type * as Wagmi from 'wagmi';
import { mainnet } from 'wagmi/chains';

vi.mock('wagmi', async (importOriginal) => {
const actual = await importOriginal<typeof Wagmi>();
return {
...actual,
useConfig: () => {
return {};
},
// https://wagmi.sh/react/hooks/useAccount
useAccount: () => {
return {
chain: mainnet,
isDisconnected: false,
};
},
useConnect: () => {
return {
connectors: [],
connectAsync: async () => {
return {
accounts: ['0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B'],
};
},
};
},
useDisconnect: () => {
return {
disconnectAsync: () => {},
};
},
useSwitchChain: () => {
return {
switchChain: () => {},
};
},
useBalance: () => {
return {};
},
useEnsName: () => ({}),
useEnsAvatar: () => ({}),
useSignMessage: () => ({ signMessageAsync: async () => 'signMessage' }),
};
});

describe('sign after connect', () => {
it('sign after connect', async () => {
const signIn = vi.fn(async () => {});

const CustomButton: React.FC<React.PropsWithChildren<ConnectorTriggerProps>> = (props) => {
const { account, onConnectClick, onDisconnectClick, children } = props;
return (
<Button
onClick={() => {
if (account) {
onDisconnectClick?.();
} else {
debugger;
onConnectClick?.({
name: 'Custom',
remark: 'Custom',
hasExtensionInstalled: async () => true,
});
}
}}
>
{(account?.name || account?.address) ?? children}
</Button>
);
};

const App = () => {
return (
<Web3ConfigProvider
connect={async () => ({
address: '0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B',
})}
sign={{ signIn }}
account={undefined}
chain={{
id: 2,
name: 'custom',
}}
>
<Connector>
<CustomButton>Custom</CustomButton>
</Connector>
</Web3ConfigProvider>
);
};
const { baseElement } = render(<App />);
const btn = baseElement.querySelector('.ant-btn')!;

expect(btn.textContent).toBe('Custom');

fireEvent.click(btn);

await waitFor(() => {
expect(signIn).toBeCalledWith('0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B');
});
});
});
Loading