diff --git a/.changeset/silent-cooks-laugh.md b/.changeset/silent-cooks-laugh.md
new file mode 100644
index 000000000..a8ac5b3d2
--- /dev/null
+++ b/.changeset/silent-cooks-laugh.md
@@ -0,0 +1,9 @@
+---
+"@ant-design/web3-bitcoin": major
+"@ant-design/web3-assets": patch
+"@ant-design/web3-common": patch
+"@ant-design/web3-icons": patch
+"@ant-design/web3": patch
+---
+
+feat: support for bitcoin
diff --git a/packages/assets/src/wallets/index.ts b/packages/assets/src/wallets/index.ts
index d28b613ba..1e4792819 100644
--- a/packages/assets/src/wallets/index.ts
+++ b/packages/assets/src/wallets/index.ts
@@ -6,5 +6,7 @@ export * from './safeheron';
export * from './okx-wallet';
export * from './phantom';
export * from './im-token';
+export * from './xverse';
+export * from './unisat-wallet';
export * from './backpack';
export * from './trust';
diff --git a/packages/assets/src/wallets/unisat-wallet.tsx b/packages/assets/src/wallets/unisat-wallet.tsx
new file mode 100644
index 000000000..50199dece
--- /dev/null
+++ b/packages/assets/src/wallets/unisat-wallet.tsx
@@ -0,0 +1,20 @@
+import type { WalletMetadata } from '@ant-design/web3-common';
+import { ChromeCircleColorful, UnisatColorful } from '@ant-design/web3-icons';
+
+export const metadata_Unisat: WalletMetadata = {
+ icon: ,
+ name: 'Unisat Wallet',
+ remark: 'Unisat Wallet',
+ app: {
+ link: 'https://unisat.io/download',
+ },
+ extensions: [
+ {
+ key: 'Chrome',
+ browserIcon: ,
+ browserName: 'Chrome',
+ link: 'https://chrome.google.com/webstore/detail/unisat/ppbibelpcjmhbdihakflkdcoccbgbkpo',
+ description: 'Access your wallet right from your favorite web browser.',
+ },
+ ],
+};
diff --git a/packages/assets/src/wallets/xverse.tsx b/packages/assets/src/wallets/xverse.tsx
new file mode 100644
index 000000000..f50e3f3dd
--- /dev/null
+++ b/packages/assets/src/wallets/xverse.tsx
@@ -0,0 +1,20 @@
+import type { WalletMetadata } from '@ant-design/web3-common';
+import { ChromeCircleColorful, XverseColorful } from '@ant-design/web3-icons';
+
+export const metadata_Xverse: WalletMetadata = {
+ icon: ,
+ name: 'Xverse',
+ remark: 'Xverse Wallet',
+ app: {
+ link: 'https://www.xverse.app/',
+ },
+ extensions: [
+ {
+ key: 'Chrome',
+ browserIcon: ,
+ browserName: 'Chrome',
+ link: 'https://chrome.google.com/webstore/detail/xverse-wallet/idnnbdplmphpflfnlkomgpfbpcgelopg',
+ description: 'Access your wallet right from your favorite web browser.',
+ },
+ ],
+};
diff --git a/packages/bitcoin/.fatherrc.ts b/packages/bitcoin/.fatherrc.ts
new file mode 100644
index 000000000..3305dd5a7
--- /dev/null
+++ b/packages/bitcoin/.fatherrc.ts
@@ -0,0 +1,5 @@
+import { defineConfig } from 'father';
+
+export default defineConfig({
+ extends: '../../.fatherrc.base.ts',
+});
diff --git a/packages/bitcoin/README.md b/packages/bitcoin/README.md
new file mode 100644
index 000000000..a17f0a554
--- /dev/null
+++ b/packages/bitcoin/README.md
@@ -0,0 +1,36 @@
+# @ant-design/web3-bitcoin
+
+This package provides a Bitcoin adapter for [@ant-design/web3](https://www.npmjs.com/package/@ant-design/web3).
+
+## Installation
+
+```bash
+npm install @ant-design/web3 @ant-design/web3-bitcoin
+```
+
+## Usage
+
+```tsx
+import { ConnectButton, Connector } from '@ant-design/web3';
+import { BitcoinWeb3ConfigProvider, UnisatWallet, XverseWallet } from '@ant-design/web3-bitcoin';
+
+const App: React.FC = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default App;
+```
+
+For more examples, refer to [Bitcoin - Ant Design Web3](https://web3.ant.design/components/bitcoin).
+
+## Documentation
+
+- For more information, visit [Ant Design Web3](https://web3.ant.design).
+- For an introduction to UniSat wallet, visit [UniSat Wallet](https://docs.unisat.io/dev/unisat-developer-service/unisat-wallet).
+- For an introduction to Xverse wallet, visit [Sats Connect](https://docs.xverse.app/sats-connect).
diff --git a/packages/bitcoin/package.json b/packages/bitcoin/package.json
new file mode 100644
index 000000000..f26fe78e8
--- /dev/null
+++ b/packages/bitcoin/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@ant-design/web3-bitcoin",
+ "version": "0.0.1",
+ "main": "dist/lib/index.js",
+ "module": "dist/esm/index.js",
+ "typings": "dist/esm/index.d.ts",
+ "exports": {
+ "import": "./dist/esm/index.js",
+ "require": "./dist/lib/index.js",
+ "types": "./dist/esm/index.d.ts"
+ },
+ "files": [
+ "dist",
+ "CHANGELOG.md",
+ "README.md"
+ ],
+ "keywords": [
+ "ant",
+ "component",
+ "components",
+ "design",
+ "framework",
+ "frontend",
+ "react",
+ "react-component",
+ "ui",
+ "web3",
+ "bitcoin"
+ ],
+ "homepage": "https://web3.ant.design",
+ "bugs": {
+ "url": "https://github.com/ant-design/ant-design-web3/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ant-design/ant-design-web3"
+ },
+ "scripts": {
+ "dev": "father dev",
+ "build": "father build"
+ },
+ "dependencies": {
+ "@ant-design/web3-common": "workspace:*",
+ "@ant-design/web3-icons": "workspace:*",
+ "@mempool/mempool.js": "^2.3.0",
+ "sats-connect": "^2.1.0"
+ },
+ "devDependencies": {
+ "father": "^4.3.8",
+ "typescript": "^5.4.2"
+ },
+ "publishConfig": {
+ "registry": "https://registry.npmjs.org",
+ "access": "public"
+ },
+ "browserslist": [
+ "last 2 versions",
+ "Firefox ESR",
+ "> 1%",
+ "ie >= 11"
+ ]
+}
diff --git a/packages/bitcoin/src/adapter/index.ts b/packages/bitcoin/src/adapter/index.ts
new file mode 100644
index 000000000..e8a2e3d24
--- /dev/null
+++ b/packages/bitcoin/src/adapter/index.ts
@@ -0,0 +1,2 @@
+export * from './useBitcoinWallet';
+export * from './wallets';
diff --git a/packages/bitcoin/src/adapter/useBitcoinWallet.ts b/packages/bitcoin/src/adapter/useBitcoinWallet.ts
new file mode 100644
index 000000000..dd4870320
--- /dev/null
+++ b/packages/bitcoin/src/adapter/useBitcoinWallet.ts
@@ -0,0 +1,23 @@
+import React, { createContext, useContext } from 'react';
+import type { Account, Balance } from '@ant-design/web3-common';
+
+export interface BitcoinWallet {
+ name: string;
+ provider: any;
+ account?: Account;
+ getBalance: () => Promise;
+ connect: () => Promise;
+ signMessage: (message: string) => Promise;
+ sendTransfer: (
+ to: string,
+ sats: number,
+ options?: { feeRate: number },
+ ) => Promise;
+}
+
+export const BitcoinAdapterContext = createContext({} as BitcoinWallet);
+
+export const useBitcoinWallet = () => {
+ const adapter = useContext(BitcoinAdapterContext);
+ return adapter;
+};
diff --git a/packages/bitcoin/src/adapter/wallets/index.ts b/packages/bitcoin/src/adapter/wallets/index.ts
new file mode 100644
index 000000000..915d445a9
--- /dev/null
+++ b/packages/bitcoin/src/adapter/wallets/index.ts
@@ -0,0 +1,2 @@
+export * from './unisat';
+export * from './xverse';
diff --git a/packages/bitcoin/src/adapter/wallets/unisat.ts b/packages/bitcoin/src/adapter/wallets/unisat.ts
new file mode 100644
index 000000000..595b76e6d
--- /dev/null
+++ b/packages/bitcoin/src/adapter/wallets/unisat.ts
@@ -0,0 +1,55 @@
+import type { Account, Balance } from '@ant-design/web3-common';
+
+import { getBalanceObject } from '../../helpers';
+import type { BitcoinWallet } from '../useBitcoinWallet';
+
+export class UnisatBitcoinWallet implements BitcoinWallet {
+ name: string;
+ provider: Window['unisat'];
+ account?: Account;
+
+ constructor(name: string) {
+ this.name = name;
+ this.provider = window.unisat;
+ this.account = undefined;
+ }
+
+ connect = async (): Promise => {
+ if (!this.provider) return;
+ try {
+ const accounts = await this.provider.requestAccounts();
+ this.account = { address: accounts[0] };
+ } catch (e) {
+ throw e;
+ }
+ return;
+ };
+
+ getBalance = async (): Promise => {
+ if (!this.provider) return;
+ const { confirmed } = await this.provider.getBalance();
+ const balance = getBalanceObject(confirmed);
+ return balance;
+ };
+
+ signMessage = async (msg: string): Promise => {
+ if (!this.provider) return;
+ const signature = await this.provider.signMessage(msg);
+ return signature;
+ };
+
+ sendTransfer = async (
+ to: string,
+ sats: number,
+ options?: { feeRate: number },
+ ): Promise => {
+ if (!this.provider) return;
+ let txid = '';
+ try {
+ txid = await this.provider.sendBitcoin(to, sats, options);
+ } catch (e) {
+ throw e;
+ }
+ return txid;
+ };
+}
diff --git a/packages/bitcoin/src/adapter/wallets/xverse.ts b/packages/bitcoin/src/adapter/wallets/xverse.ts
new file mode 100644
index 000000000..d82576312
--- /dev/null
+++ b/packages/bitcoin/src/adapter/wallets/xverse.ts
@@ -0,0 +1,71 @@
+import type { Account, Balance } from '@ant-design/web3-common';
+import { AddressPurpose, getProviderById, request, type BitcoinProvider } from 'sats-connect';
+
+import { getBalanceByMempool } from '../../helpers';
+import type { BitcoinWallet } from '../useBitcoinWallet';
+
+export class XverseBitcoinWallet implements BitcoinWallet {
+ name: string;
+ provider: BitcoinProvider | null;
+ account?: Account;
+ payment?: string;
+
+ constructor(name: string, id = 'XverseProviders.BitcoinProvider') {
+ this.name = name;
+ this.provider = getProviderById(id);
+ }
+
+ connect = async (): Promise => {
+ if (!this.provider) return;
+ const response = await request('getAccounts', {
+ purposes: [AddressPurpose.Ordinals, AddressPurpose.Payment],
+ });
+ if (response.status === 'error') {
+ throw new Error(response.error.message);
+ }
+ const [ordinals, payment] = response.result;
+ this.account = { address: ordinals.address };
+ this.payment = payment.address;
+ };
+
+ getBalance = async (): Promise => {
+ if (!this.payment) return;
+ const balance = await getBalanceByMempool(this.payment);
+ return balance;
+ };
+
+ signMessage = async (msg: string): Promise => {
+ if (!this.account?.address || !this.provider) return;
+ const response = await request('signMessage', {
+ address: this.account.address,
+ message: msg,
+ });
+ if (response.status === 'success') {
+ return response.result.signature;
+ } else {
+ throw new Error(response.error.message);
+ }
+ };
+
+ sendTransfer = async (to: string, sats: number): Promise => {
+ let txid = '';
+ try {
+ const response = await request('sendTransfer', {
+ recipients: [
+ {
+ address: to,
+ amount: sats,
+ },
+ ],
+ });
+ if (response.status === 'success') {
+ txid = response.result.txid;
+ } else {
+ throw new Error(response.error.message);
+ }
+ } catch (e) {
+ throw e;
+ }
+ return txid;
+ };
+}
diff --git a/packages/bitcoin/src/global.d.ts b/packages/bitcoin/src/global.d.ts
new file mode 100644
index 000000000..01f4f36e6
--- /dev/null
+++ b/packages/bitcoin/src/global.d.ts
@@ -0,0 +1,4 @@
+declare interface Window {
+ // TODO: unisat interface
+ unisat?: any;
+}
diff --git a/packages/bitcoin/src/helpers.tsx b/packages/bitcoin/src/helpers.tsx
new file mode 100644
index 000000000..6ccf0c678
--- /dev/null
+++ b/packages/bitcoin/src/helpers.tsx
@@ -0,0 +1,29 @@
+import type { Balance } from '@ant-design/web3-common';
+import { BitcoinCircleColorful } from '@ant-design/web3-icons';
+import mempoolJS from '@mempool/mempool.js';
+
+const {
+ bitcoin: { addresses },
+} = mempoolJS({
+ hostname: 'mempool.space',
+});
+
+export const getBalanceObject = (sats: number): Balance => {
+ return {
+ value: BigInt(sats),
+ decimals: 8,
+ symbol: 'BTC',
+ icon: ,
+ };
+};
+
+/**
+ * 对于不提供获取余额接口的钱包,如 Xverse,需调用 mempool API 自行获取
+ * https://github.com/secretkeylabs/sats-connect/issues/12#issuecomment-2038963924
+ */
+export const getBalanceByMempool = async (address: string): Promise => {
+ const addr = await addresses.getAddress({ address });
+ const { funded_txo_sum, spent_txo_count } = addr.chain_stats;
+ // mempool not included
+ return getBalanceObject(funded_txo_sum - spent_txo_count);
+};
diff --git a/packages/bitcoin/src/index.ts b/packages/bitcoin/src/index.ts
new file mode 100644
index 000000000..8cd0413dc
--- /dev/null
+++ b/packages/bitcoin/src/index.ts
@@ -0,0 +1,3 @@
+export * from './provider';
+export * from './wallets';
+export { useBitcoinWallet } from './adapter/useBitcoinWallet';
diff --git a/packages/bitcoin/src/provider/__tests__/basic.test.tsx b/packages/bitcoin/src/provider/__tests__/basic.test.tsx
new file mode 100644
index 000000000..137f5e0b7
--- /dev/null
+++ b/packages/bitcoin/src/provider/__tests__/basic.test.tsx
@@ -0,0 +1,96 @@
+import { ConnectButton, Connector, useConnection } from '@ant-design/web3';
+import { fireEvent } from '@testing-library/react';
+import { Button } from 'antd';
+import { describe, expect, it, vi } from 'vitest';
+
+import { XverseWallet } from '../../wallets';
+import { BitcoinWeb3ConfigProvider } from '../index';
+import { xrender } from './utils';
+
+vi.mock('sats-connect', async () => {
+ const originModules = await vi.importActual('sats-connect');
+ return {
+ ...originModules,
+ request: (method: string) => {
+ if (method === 'getAccounts') {
+ return Promise.resolve({
+ status: 'success',
+ result: [
+ {
+ address: '123',
+ },
+ {
+ address: '456',
+ },
+ ],
+ });
+ }
+ return;
+ },
+ getProviderById: () => ({}),
+ };
+});
+
+vi.mock('@mempool/mempool.js', async () => {
+ return {
+ default: () => ({
+ bitcoin: {
+ addresses: {
+ getAddress: () =>
+ Promise.resolve({
+ chain_stats: {
+ funded_txo_sum: 1000,
+ spent_txo_count: 100,
+ },
+ }),
+ },
+ },
+ }),
+ };
+});
+
+describe('BitcoinWeb3ConfigProvider', () => {
+ it('mount correctly', () => {
+ const App = () => (
+
+ test
+
+ );
+
+ const { selector } = xrender(App);
+ expect(selector('.content')?.textContent).toBe('test');
+ });
+
+ it('connect and disconnect', async () => {
+ const Disconnect = () => {
+ const { disconnect } = useConnection();
+ return (
+
+ );
+ };
+
+ const App = () => {
+ return (
+
+
+
+
+
+
+ );
+ };
+
+ const { selector } = xrender(App);
+ const modalBtn = selector('.connect')!;
+ fireEvent.click(modalBtn);
+ // select wallet
+ const connectBtn = selector('.ant-list-item')!;
+ expect(connectBtn.textContent).not.toBeNull();
+ fireEvent.click(connectBtn);
+ // disconnect
+ const disconnectBtn = selector('.disconnect')!;
+ fireEvent.click(disconnectBtn);
+ });
+});
diff --git a/packages/bitcoin/src/provider/__tests__/utils.tsx b/packages/bitcoin/src/provider/__tests__/utils.tsx
new file mode 100644
index 000000000..3955fa77f
--- /dev/null
+++ b/packages/bitcoin/src/provider/__tests__/utils.tsx
@@ -0,0 +1,19 @@
+import type { FC } from 'react';
+import { render } from '@testing-library/react';
+
+type RenderResult = ReturnType;
+type RenderWithUtils = RenderResult & {
+ selector: (selector: string) => T | null;
+ selectors: (selector: string) => NodeListOf;
+};
+type XRender = (Comp: FC, options?: Parameters[1]) => RenderWithUtils;
+
+export const xrender: XRender = (Comp, options) => {
+ const { baseElement, ...others } = render(, options);
+ return {
+ baseElement,
+ ...others,
+ selector: (selector) => baseElement.querySelector(selector),
+ selectors: (selector) => baseElement.querySelectorAll(selector),
+ };
+};
diff --git a/packages/bitcoin/src/provider/config-provider.tsx b/packages/bitcoin/src/provider/config-provider.tsx
new file mode 100644
index 000000000..422a3708c
--- /dev/null
+++ b/packages/bitcoin/src/provider/config-provider.tsx
@@ -0,0 +1,50 @@
+import { useEffect, useState, type FC, type PropsWithChildren } from 'react';
+import {
+ Web3ConfigProvider,
+ type Balance,
+ type Locale,
+ type Wallet,
+} from '@ant-design/web3-common';
+
+import { useBitcoinWallet } from '../adapter';
+
+export interface BitcoinConfigProviderProps {
+ locale?: Locale;
+ wallets: Wallet[];
+ selectWallet: (wallet?: Wallet | null) => void;
+ balance: boolean;
+}
+
+export const BitcoinConfigProvider: FC> = ({
+ children,
+ locale,
+ wallets,
+ selectWallet,
+ balance: showBalance,
+}) => {
+ const { getBalance, account } = useBitcoinWallet();
+ const [balance, setBalance] = useState();
+
+ useEffect(() => {
+ if (!showBalance) return;
+ getBalance?.().then((b) => setBalance(b));
+ }, [showBalance, getBalance]);
+
+ return (
+ {
+ selectWallet(wallet);
+ }}
+ disconnect={async () => {
+ selectWallet(null);
+ }}
+ >
+ {children}
+
+ );
+};
diff --git a/packages/bitcoin/src/provider/index.tsx b/packages/bitcoin/src/provider/index.tsx
new file mode 100644
index 000000000..0b4cab832
--- /dev/null
+++ b/packages/bitcoin/src/provider/index.tsx
@@ -0,0 +1,54 @@
+import { useEffect, useState, type FC, type PropsWithChildren } from 'react';
+import type { Locale, Wallet } from '@ant-design/web3-common';
+
+import { type BitcoinWallet } from '../adapter';
+import { BitcoinAdapterContext } from '../adapter/useBitcoinWallet';
+import { WalletFactory, WalletWithAdapter } from '../wallets/types';
+import { BitcoinConfigProvider } from './config-provider';
+
+export interface BitcoinWeb3ConfigProviderProps {
+ // 钱包显式传入
+ wallets?: WalletFactory[];
+ locale?: Locale;
+ balance?: boolean;
+}
+
+export const BitcoinWeb3ConfigProvider: FC> = ({
+ children,
+ wallets: initWallets = [],
+ balance = false,
+ locale,
+}) => {
+ const [adapter, setAdapter] = useState({} as BitcoinWallet);
+ const [wallets, setWallets] = useState([]);
+
+ useEffect(() => {
+ if (initWallets.length === 0) return;
+ setWallets(initWallets.map((w) => w.create()));
+ }, [initWallets]);
+
+ const selectWallet = async (wallet?: Wallet | null) => {
+ if (!wallet) {
+ // disconnect
+ if (!!adapter) setAdapter({} as BitcoinWallet);
+ return;
+ }
+ const provider = wallets.find((w) => w.name === wallet.name)?.adapter;
+ await provider?.connect();
+ // @ts-ignore provider is not undefined
+ setAdapter(provider);
+ };
+
+ return (
+
+
+ {children}
+
+
+ );
+};
diff --git a/packages/bitcoin/src/wallets/factory.ts b/packages/bitcoin/src/wallets/factory.ts
new file mode 100644
index 000000000..951baca7a
--- /dev/null
+++ b/packages/bitcoin/src/wallets/factory.ts
@@ -0,0 +1,15 @@
+import { WalletFactoryBuilder } from './types';
+
+export const WalletFactory: WalletFactoryBuilder = (adapter, metadata) => {
+ return {
+ adapter,
+ create: () => {
+ return {
+ ...metadata,
+ adapter: adapter,
+ hasWalletReady: () => Promise.resolve(!!adapter.provider),
+ hasExtensionInstalled: () => Promise.resolve(!!adapter.provider),
+ };
+ },
+ };
+};
diff --git a/packages/bitcoin/src/wallets/index.ts b/packages/bitcoin/src/wallets/index.ts
new file mode 100644
index 000000000..12370c288
--- /dev/null
+++ b/packages/bitcoin/src/wallets/index.ts
@@ -0,0 +1,12 @@
+/**
+ * wallets 是将 adapter 与钱包静态信息一起封装
+ */
+import { metadata_Unisat, metadata_Xverse } from '@ant-design/web3-assets';
+
+import { UnisatBitcoinWallet, XverseBitcoinWallet } from '../adapter';
+import { WalletFactory } from './factory';
+
+export const UnisatWallet = () =>
+ WalletFactory(new UnisatBitcoinWallet(metadata_Unisat.name), metadata_Unisat);
+export const XverseWallet = () =>
+ WalletFactory(new XverseBitcoinWallet(metadata_Xverse.name), metadata_Xverse);
diff --git a/packages/bitcoin/src/wallets/types.ts b/packages/bitcoin/src/wallets/types.ts
new file mode 100644
index 000000000..fd71c7eae
--- /dev/null
+++ b/packages/bitcoin/src/wallets/types.ts
@@ -0,0 +1,17 @@
+import type { Wallet, WalletMetadata } from '@ant-design/web3-common';
+
+import type { BitcoinWallet } from '../adapter';
+
+export interface WalletWithAdapter extends Wallet {
+ adapter: BitcoinWallet;
+}
+
+export interface WalletFactory {
+ adapter: BitcoinWallet;
+ create: () => WalletWithAdapter;
+}
+
+export type WalletFactoryBuilder = (
+ adapter: BitcoinWallet,
+ metadata: WalletMetadata,
+) => WalletFactory;
diff --git a/packages/bitcoin/tsconfig.json b/packages/bitcoin/tsconfig.json
new file mode 100644
index 000000000..928e5b0ef
--- /dev/null
+++ b/packages/bitcoin/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": ["src", "global.d.ts"]
+}
diff --git a/packages/web3/package.json b/packages/web3/package.json
index 39e98e752..4906a16eb 100644
--- a/packages/web3/package.json
+++ b/packages/web3/package.json
@@ -53,6 +53,7 @@
},
"devDependencies": {
"@ant-design/web3-solana": "workspace:*",
+ "@ant-design/web3-bitcoin": "workspace:*",
"@ant-design/web3-wagmi": "workspace:*",
"@ant-design/web3-ethers": "workspace:*",
"@types/react": "^18.2.78",
diff --git a/packages/web3/src/bitcoin/demos/basic.tsx b/packages/web3/src/bitcoin/demos/basic.tsx
new file mode 100644
index 000000000..548d97695
--- /dev/null
+++ b/packages/web3/src/bitcoin/demos/basic.tsx
@@ -0,0 +1,14 @@
+import { ConnectButton, Connector } from '@ant-design/web3';
+import { BitcoinWeb3ConfigProvider, UnisatWallet, XverseWallet } from '@ant-design/web3-bitcoin';
+
+const App: React.FC = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/packages/web3/src/bitcoin/demos/get-inscriptions.tsx b/packages/web3/src/bitcoin/demos/get-inscriptions.tsx
new file mode 100644
index 000000000..4cd57c472
--- /dev/null
+++ b/packages/web3/src/bitcoin/demos/get-inscriptions.tsx
@@ -0,0 +1,52 @@
+import { useState } from 'react';
+import { ConnectButton, Connector } from '@ant-design/web3';
+import {
+ BitcoinWeb3ConfigProvider,
+ UnisatWallet,
+ useBitcoinWallet,
+} from '@ant-design/web3-bitcoin';
+import { Button, message, Space } from 'antd';
+
+const GetInscriptions: React.FC = () => {
+ const { account, name, provider } = useBitcoinWallet();
+ const [img, setImg] = useState();
+
+ return account ? (
+
+
+ {img ? : null}
+
+ ) : null;
+};
+
+const App: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/packages/web3/src/bitcoin/demos/send-transfer.tsx b/packages/web3/src/bitcoin/demos/send-transfer.tsx
new file mode 100644
index 000000000..c51398a4d
--- /dev/null
+++ b/packages/web3/src/bitcoin/demos/send-transfer.tsx
@@ -0,0 +1,45 @@
+import { ConnectButton, Connector } from '@ant-design/web3';
+import {
+ BitcoinWeb3ConfigProvider,
+ UnisatWallet,
+ useBitcoinWallet,
+ XverseWallet,
+} from '@ant-design/web3-bitcoin';
+import { Button, Space } from 'antd';
+
+const SendBitcoin: React.FC = () => {
+ const { sendTransfer, account } = useBitcoinWallet();
+
+ return account ? (
+
+ ) : null;
+};
+
+const App: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/packages/web3/src/bitcoin/demos/sign-message.tsx b/packages/web3/src/bitcoin/demos/sign-message.tsx
new file mode 100644
index 000000000..192d89105
--- /dev/null
+++ b/packages/web3/src/bitcoin/demos/sign-message.tsx
@@ -0,0 +1,42 @@
+import { ConnectButton, Connector } from '@ant-design/web3';
+import {
+ BitcoinWeb3ConfigProvider,
+ UnisatWallet,
+ useBitcoinWallet,
+ XverseWallet,
+} from '@ant-design/web3-bitcoin';
+import { Button, Space } from 'antd';
+
+const SignMessage: React.FC = () => {
+ const { signMessage, account } = useBitcoinWallet();
+
+ return account ? (
+
+ ) : null;
+};
+
+const App: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/packages/web3/src/bitcoin/index.md b/packages/web3/src/bitcoin/index.md
new file mode 100644
index 000000000..94e99042d
--- /dev/null
+++ b/packages/web3/src/bitcoin/index.md
@@ -0,0 +1,59 @@
+---
+nav: Components
+group:
+ title: Adapter
+ order: 1
+tag:
+ title: New
+ color: success
+---
+
+# Bitcoin
+
+Ant Design Web3 officially provides `@ant-design/web3-bitcoin` to adapt to Bitcoin. It provides the ability to connect to Bitcoin for the components of `@ant-design/web3`.
+
+Currently, connections to Xverse and Unisat wallets are supported. In the future, [StandardWallet protocol](https://github.com/ExodusMovement/bitcoin-wallet-standard) and more commonly used wallets will be supported.
+
+`useBitcoinWallet` exposes common methods such as signing and transaction handling, which can be called directly. Alternatively, wallet APIs can be accessed through the `provider` for customized handling of wallet logic.
+
+Support for other methods like PSBT will be added in the future. Feel free to submit a GitHub issue or a PR to support this. Currently, related logic can be implemented through the provider.
+
+## WalletConnect
+
+
+
+## signMessage
+
+
+
+## sendTransfer
+
+
+
+## get Ordinals Inscriptions using unisat wallet
+
+
+
+## API
+
+### BitcoinWeb3ConfigProvider
+
+| Property | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| balance | Whether to display balance | `boolean` | `false` | - |
+| wallets | Wallets | `WalletFactory[]` | - | - |
+| locale | Multilingual settings | [Locale](https://github.com/ant-design/ant-design-web3/blob/main/packages/common/src/locale/en_US.ts) | - | - |
+
+### useBitcoinWallet
+
+#### result
+
+| Property | Description | Type |
+| --- | --- | --- |
+| name | The connected wallet's name | `string` |
+| provider | The connected wallet's provider | `any` |
+| account | Represents the web3 account address of the current user | `string` |
+| connect | Connect the wallet | `() => Promise` |
+| getBalance | Get the balance of the wallet | `() => Promise` |
+| signMessage | Sign message | `() => Promise` |
+| sendTransfer | Transfer bticoin | `() => Promise` |
diff --git a/packages/web3/src/bitcoin/index.zh-CN.md b/packages/web3/src/bitcoin/index.zh-CN.md
new file mode 100644
index 000000000..d19701b68
--- /dev/null
+++ b/packages/web3/src/bitcoin/index.zh-CN.md
@@ -0,0 +1,60 @@
+---
+nav: 组件
+subtitle: 比特币
+group:
+ title: 适配器
+ order: 1
+tag:
+ title: 新
+ color: success
+---
+
+# Bitcoin
+
+Ant Design Web3 官方提供了 `@ant-design/web3-bitcoin` 来适配比特币,它为 `@ant-design/web3` 的组件提供了连接比特币链的能力。通过它,你不需要自己处理组件的连接状态,链数据请求等逻辑。它会通过 [Web3ConfigProvider](../web3-config-provider/index.zh-CN.md) 为组件提供相关全局状态和接口。
+
+目前支持连接 Xverse / Unisat 钱包。未来会支持 [StandardWallet](https://github.com/ExodusMovement/bitcoin-wallet-standard) 协议及更多常用钱包。
+
+`useBitcoinWallet` 暴露出了诸如签名、交易等通用的方法, 可以直接调用;也可以通过 `provider` 直接调用钱包的 API,定制化处理各个钱包逻辑。
+
+未来会支持其他如 PSBT 等方法,欢迎给我们提交 GitHub issue 或 PR 支持。当前可以通过 provider 实现相关逻辑。
+
+## 连接钱包
+
+
+
+## 使用 signMessage 签名
+
+
+
+## 使用 sendBitcoin 发送交易
+
+
+
+## 使用 unisat 查看 Ordinals 铭文
+
+
+
+## API
+
+### BitcoinWeb3ConfigProvider
+
+| 属性 | 描述 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| balance | 是否显示余额 | `boolean` | `false` | - |
+| wallets | 可用的钱包 | `WalletFactory[]` | - | - |
+| locale | 多语言设置 | [Locale](https://github.com/ant-design/ant-design-web3/blob/main/packages/common/src/locale/zh_CN.ts) | - | - |
+
+### useBitcoinWallet
+
+#### result
+
+| 参数 | 描述 | 类型 |
+| ------------ | -------------------------------- | ------------------------------------- |
+| name | 当前连接的钱包名称 | `string` |
+| provider | 获取当前连接钱包的 provider 对象 | `any` |
+| account | 账户地址 | `string` |
+| connect | 连接钱包 | `() => Promise` |
+| getBalance | 获取钱包余额 | `() => Promise` |
+| signMessage | 签名 | `() => Promise` |
+| sendTransfer | 发送交易 | `() => Promise` |
diff --git a/packages/web3/src/ethereum/index.md b/packages/web3/src/ethereum/index.md
index 4a512e9f9..a248b9f27 100644
--- a/packages/web3/src/ethereum/index.md
+++ b/packages/web3/src/ethereum/index.md
@@ -2,7 +2,7 @@
nav: Components
group:
title: Adapter
- order: 1
+ order: 2
tag:
title: Update
color: success
diff --git a/packages/web3/src/ethereum/index.zh-CN.md b/packages/web3/src/ethereum/index.zh-CN.md
index 57eff6c3a..40c125362 100644
--- a/packages/web3/src/ethereum/index.zh-CN.md
+++ b/packages/web3/src/ethereum/index.zh-CN.md
@@ -3,7 +3,7 @@ nav: 组件
subtitle: 以太坊
group:
title: 适配器
- order: 1
+ order: 2
tag:
title: 更新
color: success
diff --git a/packages/web3/src/solana/index.md b/packages/web3/src/solana/index.md
index de83c3ab2..9b9b79a20 100644
--- a/packages/web3/src/solana/index.md
+++ b/packages/web3/src/solana/index.md
@@ -2,7 +2,7 @@
nav: Components
group:
title: Adapter
- order: 2
+ order: 3
---
# Solana
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13aec1c46..588949340 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -262,6 +262,28 @@ importers:
specifier: ^5.4.4
version: 5.4.4
+ packages/bitcoin:
+ dependencies:
+ '@ant-design/web3-common':
+ specifier: workspace:*
+ version: link:../common
+ '@ant-design/web3-icons':
+ specifier: workspace:*
+ version: link:../icons
+ '@mempool/mempool.js':
+ specifier: ^2.3.0
+ version: 2.3.0
+ sats-connect:
+ specifier: ^2.1.0
+ version: 2.1.0
+ devDependencies:
+ father:
+ specifier: ^4.3.8
+ version: 4.3.8(@types/node@20.10.5)(webpack@5.91.0)
+ typescript:
+ specifier: ^5.4.2
+ version: 5.4.4
+
packages/common:
dependencies:
lodash-es:
@@ -498,6 +520,9 @@ importers:
specifier: '>=17.0.0'
version: 18.2.0(react@18.2.0)
devDependencies:
+ '@ant-design/web3-bitcoin':
+ specifier: workspace:*
+ version: link:../bitcoin
'@ant-design/web3-ethers':
specifier: workspace:*
version: link:../ethers
@@ -5980,6 +6005,17 @@ packages:
read-yaml-file: 1.1.0
dev: true
+ /@mempool/mempool.js@2.3.0:
+ resolution: {integrity: sha512-FrN9WjZCEyyLodrTPQxmlWDh8B/UGK0jlKfVNzJxqzQ1IMPo/Hpdws8xwYEcsks5JqsaxbjLwaC3GAtJ6Brd0A==}
+ dependencies:
+ axios: 0.24.0
+ ws: 8.3.0
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - utf-8-validate
+ dev: false
+
/@metamask/eth-json-rpc-provider@1.0.1:
resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==}
engines: {node: '>=14.0.0'}
@@ -6647,6 +6683,10 @@ packages:
resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
engines: {node: '>= 16'}
+ /@noble/secp256k1@1.7.1:
+ resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==}
+ dev: false
+
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -13504,6 +13544,14 @@ packages:
- supports-color
dev: true
+ /axios@0.24.0:
+ resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
+ dependencies:
+ follow-redirects: 1.15.5(debug@4.3.4)
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
/axios@0.27.2(debug@4.3.4):
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
dependencies:
@@ -19667,6 +19715,14 @@ packages:
/jsonschema@1.2.2:
resolution: {integrity: sha512-iX5OFQ6yx9NgbHCwse51ohhKgLuLL7Z5cNOeZOPIlDUtAMrxlruHLzVZxbltdHE5mEDXN+75oFOwq6Gn0MZwsA==}
+ /jsontokens@4.0.1:
+ resolution: {integrity: sha512-+MO415LEN6M+3FGsRz4wU20g7N2JA+2j9d9+pGaNJHviG4L8N0qzavGyENw6fJqsq9CcrHOIL6iWX5yeTZ86+Q==}
+ dependencies:
+ '@noble/hashes': 1.3.3
+ '@noble/secp256k1': 1.7.1
+ base64-js: 1.5.1
+ dev: false
+
/jsqr@1.4.0:
resolution: {integrity: sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==}
@@ -24985,6 +25041,14 @@ packages:
source-map-js: 1.0.2
dev: true
+ /sats-connect@2.1.0:
+ resolution: {integrity: sha512-HCPG3L0GModbC2g4uk+ezpSUr8kfCw1y5kv6LC9N8ZNNSRFTTK/CX9eSStV7bBGk5po+euaLsRy4YlLihRcnnw==}
+ dependencies:
+ jsontokens: 4.0.1
+ process: 0.11.10
+ util: 0.12.5
+ dev: false
+
/sax@1.2.4:
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
dev: true
@@ -28061,6 +28125,19 @@ packages:
bufferutil: 4.0.8
utf-8-validate: 5.0.10
+ /ws@8.3.0:
+ resolution: {integrity: sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ dev: false
+
/ws@8.5.0:
resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==}
engines: {node: '>=10.0.0'}