Skip to content

Commit

Permalink
feat(bitcoin): bitcoin support (#778)
Browse files Browse the repository at this point in the history
* feat(bitcoin): bitcoin init and support xverse

* feat(bitcoin): bitcoin init and support xverse

* feat(bitcoin): add types

* feat(bitcoin): support leather and sendBitcoin

* feat(bitcoin): fix ci error

* feat(bitcoin): fix ci error

* feat(bitcoin): add icon

* feat(bitcoin): fix test

* feat(bitcoin): fix test

* feat(bitcoin): add changelog

* chore: freeze pnpm version 8

* feat(bitcoin): fix changelog

* feat(bitcoin): add English API

* feat(bitcoin): add readme
  • Loading branch information
AmAzing129 authored Apr 19, 2024
1 parent 5ce1923 commit ab3eac4
Show file tree
Hide file tree
Showing 34 changed files with 963 additions and 3 deletions.
9 changes: 9 additions & 0 deletions .changeset/silent-cooks-laugh.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions packages/assets/src/wallets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
20 changes: 20 additions & 0 deletions packages/assets/src/wallets/unisat-wallet.tsx
Original file line number Diff line number Diff line change
@@ -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: <UnisatColorful />,
name: 'Unisat Wallet',
remark: 'Unisat Wallet',
app: {
link: 'https://unisat.io/download',
},
extensions: [
{
key: 'Chrome',
browserIcon: <ChromeCircleColorful />,
browserName: 'Chrome',
link: 'https://chrome.google.com/webstore/detail/unisat/ppbibelpcjmhbdihakflkdcoccbgbkpo',
description: 'Access your wallet right from your favorite web browser.',
},
],
};
20 changes: 20 additions & 0 deletions packages/assets/src/wallets/xverse.tsx
Original file line number Diff line number Diff line change
@@ -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: <XverseColorful />,
name: 'Xverse',
remark: 'Xverse Wallet',
app: {
link: 'https://www.xverse.app/',
},
extensions: [
{
key: 'Chrome',
browserIcon: <ChromeCircleColorful />,
browserName: 'Chrome',
link: 'https://chrome.google.com/webstore/detail/xverse-wallet/idnnbdplmphpflfnlkomgpfbpcgelopg',
description: 'Access your wallet right from your favorite web browser.',
},
],
};
5 changes: 5 additions & 0 deletions packages/bitcoin/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineConfig } from 'father';

export default defineConfig({
extends: '../../.fatherrc.base.ts',
});
36 changes: 36 additions & 0 deletions packages/bitcoin/README.md
Original file line number Diff line number Diff line change
@@ -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 (
<BitcoinWeb3ConfigProvider wallets={[XverseWallet(), UnisatWallet()]}>
<Connector>
<ConnectButton />
</Connector>
</BitcoinWeb3ConfigProvider>
);
};

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).
62 changes: 62 additions & 0 deletions packages/bitcoin/package.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
2 changes: 2 additions & 0 deletions packages/bitcoin/src/adapter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useBitcoinWallet';
export * from './wallets';
23 changes: 23 additions & 0 deletions packages/bitcoin/src/adapter/useBitcoinWallet.ts
Original file line number Diff line number Diff line change
@@ -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<Balance | undefined>;
connect: () => Promise<void>;
signMessage: (message: string) => Promise<string | undefined>;
sendTransfer: (
to: string,
sats: number,
options?: { feeRate: number },
) => Promise<string | undefined>;
}

export const BitcoinAdapterContext = createContext<BitcoinWallet>({} as BitcoinWallet);

export const useBitcoinWallet = () => {
const adapter = useContext(BitcoinAdapterContext);
return adapter;
};
2 changes: 2 additions & 0 deletions packages/bitcoin/src/adapter/wallets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './unisat';
export * from './xverse';
55 changes: 55 additions & 0 deletions packages/bitcoin/src/adapter/wallets/unisat.ts
Original file line number Diff line number Diff line change
@@ -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<void> => {
if (!this.provider) return;
try {
const accounts = await this.provider.requestAccounts();
this.account = { address: accounts[0] };
} catch (e) {
throw e;
}
return;
};

getBalance = async (): Promise<Balance | undefined> => {
if (!this.provider) return;
const { confirmed } = await this.provider.getBalance();
const balance = getBalanceObject(confirmed);
return balance;
};

signMessage = async (msg: string): Promise<string | undefined> => {
if (!this.provider) return;
const signature = await this.provider.signMessage(msg);
return signature;
};

sendTransfer = async (
to: string,
sats: number,
options?: { feeRate: number },
): Promise<string | undefined> => {
if (!this.provider) return;
let txid = '';
try {
txid = await this.provider.sendBitcoin(to, sats, options);
} catch (e) {
throw e;
}
return txid;
};
}
71 changes: 71 additions & 0 deletions packages/bitcoin/src/adapter/wallets/xverse.ts
Original file line number Diff line number Diff line change
@@ -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<void> => {
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<Balance | undefined> => {
if (!this.payment) return;
const balance = await getBalanceByMempool(this.payment);
return balance;
};

signMessage = async (msg: string): Promise<string | undefined> => {
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<string> => {
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;
};
}
4 changes: 4 additions & 0 deletions packages/bitcoin/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare interface Window {
// TODO: unisat interface
unisat?: any;
}
29 changes: 29 additions & 0 deletions packages/bitcoin/src/helpers.tsx
Original file line number Diff line number Diff line change
@@ -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: <BitcoinCircleColorful />,
};
};

/**
* 对于不提供获取余额接口的钱包,如 Xverse,需调用 mempool API 自行获取
* https://github.com/secretkeylabs/sats-connect/issues/12#issuecomment-2038963924
*/
export const getBalanceByMempool = async (address: string): Promise<Balance> => {
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);
};
3 changes: 3 additions & 0 deletions packages/bitcoin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './provider';
export * from './wallets';
export { useBitcoinWallet } from './adapter/useBitcoinWallet';
Loading

0 comments on commit ab3eac4

Please sign in to comment.