Skip to content

Commit 036e20f

Browse files
authored
fix(bitcoin): adapt to Next.js (#881)
* fix(bitcoin): adapt to next.js * chore(bitcoin): unit test and clarify docs
1 parent 8c59ee1 commit 036e20f

File tree

11 files changed

+72
-77
lines changed

11 files changed

+72
-77
lines changed

.changeset/rude-apples-chew.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ant-design/web3-bitcoin": patch
3+
---
4+
5+
fix: adapt to next.js

docs/guide/quick-start.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ Of course, compared with `NFTImage`, we may use more components of the connectio
4545

4646
## Use in Next.js
4747

48-
In [Next.js](https://nextjs.org/), based on its build mechanism, you may encounter the following errors when introducing packages such as `@ant-design/web3`:
48+
- If you're using recommended App Router, add the `"use client"` directive at the top of the file to use Provider such as `WagmiWeb3ConfigProvider`, considering that `createContext` only works in Client Components.
49+
50+
- In [Next.js](https://nextjs.org/), based on its build mechanism, you may encounter the following errors when introducing packages such as `@ant-design/web3`:
4951

5052
1. `Error: require() of ES Module ... from ... not supported.`
5153
2. `SyntaxError: Cannot use import statement outside a module`

docs/guide/quick-start.zh-CN.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ export default () => {
4545

4646
## 在 Next.js 中使用
4747

48-
[Next.js](https://nextjs.org/) 中,基于它的构建机制,你可能在引入 `@ant-design/web3` 等包时会遇到类型下面的错误:
48+
- 若使用默认的 App Router,由于 `createContext` 仅可使用在客户端组件中,因此使用诸如 `WagmiWeb3ConfigProvider` 等 Provider 时,均应在组件代码顶部增加`"use client"`指令。
49+
50+
-[Next.js](https://nextjs.org/) 中,基于它的构建机制,你可能在引入 `@ant-design/web3` 等包时会遇到类型下面的错误:
4951

5052
1. `Error: require() of ES Module ... from ... not supported.`
5153
2. `SyntaxError: Cannot use import statement outside a module`

packages/bitcoin/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
"dependencies": {
4444
"@ant-design/web3-common": "workspace:*",
4545
"@ant-design/web3-icons": "workspace:*",
46-
"@mempool/mempool.js": "^2.3.0",
4746
"sats-connect": "^2.3.1"
4847
},
4948
"devDependencies": {

packages/bitcoin/src/error.ts

+9
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ export class NoAddressError extends Error {
1616
this.name = this.constructor.name;
1717
}
1818
}
19+
20+
export class NoBalanceError extends Error {
21+
name: string;
22+
23+
constructor(message = "Can't fetch the balance") {
24+
super(message);
25+
this.name = this.constructor.name;
26+
}
27+
}

packages/bitcoin/src/helpers.tsx

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import type { Balance } from '@ant-design/web3-common';
22
import { BitcoinCircleColorful } from '@ant-design/web3-icons';
3-
import mempoolJS from '@mempool/mempool.js';
43

5-
const {
6-
bitcoin: { addresses },
7-
} = mempoolJS({
8-
hostname: 'mempool.space',
9-
});
4+
import { NoBalanceError } from './error';
5+
6+
const MEMPOOL_URL = 'https://mempool.space/api';
107

118
export const getBalanceObject = (sats: number): Balance => {
129
return {
@@ -22,8 +19,13 @@ export const getBalanceObject = (sats: number): Balance => {
2219
* https://github.com/secretkeylabs/sats-connect/issues/12#issuecomment-2038963924
2320
*/
2421
export const getBalanceByMempool = async (address: string): Promise<Balance> => {
25-
const addr = await addresses.getAddress({ address });
26-
const { funded_txo_sum, spent_txo_count } = addr.chain_stats;
27-
// mempool not included
28-
return getBalanceObject(funded_txo_sum - spent_txo_count);
22+
const res = await fetch(`${MEMPOOL_URL}/address/${address}`);
23+
if (res.ok) {
24+
const data = await res.json();
25+
const { chain_stats } = data;
26+
const { funded_txo_sum, spent_txo_sum } = chain_stats;
27+
return getBalanceObject(funded_txo_sum - spent_txo_sum);
28+
} else {
29+
throw new NoBalanceError();
30+
}
2931
};

packages/bitcoin/src/provider/__tests__/basic.test.tsx

+33-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { ConnectButton, Connector, useConnection } from '@ant-design/web3';
22
import { fireEvent } from '@testing-library/react';
33
import { Button } from 'antd';
4-
import { describe, expect, it, vi } from 'vitest';
4+
import { beforeEach, describe, expect, it, vi } from 'vitest';
55

6+
import { getBalanceByMempool } from '../../helpers';
67
import { XverseWallet } from '../../wallets';
78
import { BitcoinWeb3ConfigProvider } from '../index';
89
import { xrender } from './utils';
910

11+
// mock fetch
12+
global.fetch = vi.fn();
13+
function createFetchResponse(data: any) {
14+
return Promise.resolve({ ok: true, json: () => new Promise((resolve) => resolve(data)) });
15+
}
16+
1017
vi.mock('sats-connect', async () => {
1118
const originModules = await vi.importActual('sats-connect');
1219
return {
@@ -31,25 +38,12 @@ vi.mock('sats-connect', async () => {
3138
};
3239
});
3340

34-
vi.mock('@mempool/mempool.js', async () => {
35-
return {
36-
default: () => ({
37-
bitcoin: {
38-
addresses: {
39-
getAddress: () =>
40-
Promise.resolve({
41-
chain_stats: {
42-
funded_txo_sum: 1000,
43-
spent_txo_count: 100,
44-
},
45-
}),
46-
},
47-
},
48-
}),
49-
};
50-
});
51-
5241
describe('BitcoinWeb3ConfigProvider', () => {
42+
beforeEach(() => {
43+
// @ts-ignore: vi.fn().mockReset
44+
global.fetch.mockReset();
45+
});
46+
5347
it('mount correctly', () => {
5448
const App = () => (
5549
<BitcoinWeb3ConfigProvider>
@@ -61,7 +55,27 @@ describe('BitcoinWeb3ConfigProvider', () => {
6155
expect(selector('.content')?.textContent).toBe('test');
6256
});
6357

58+
it("can't get balance", async () => {
59+
// @ts-ignore: vi.fn().mockResolvedValue
60+
fetch.mockResolvedValue(() => Promise.resolve({ ok: false }));
61+
try {
62+
await getBalanceByMempool('bc1p');
63+
} catch (e: any) {
64+
console.log(e.message);
65+
}
66+
});
67+
6468
it('connect and disconnect', async () => {
69+
const addressResponse = {
70+
chain_stats: {
71+
funded_txo_sum: 1000,
72+
spent_txo_sum: 100,
73+
},
74+
};
75+
76+
// @ts-ignore: vi.fn().mockResolvedValue
77+
fetch.mockResolvedValue(createFetchResponse(addressResponse));
78+
6579
const Disconnect = () => {
6680
const { disconnect } = useConnection();
6781
return (

packages/bitcoin/src/wallets/factory.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { WalletFactoryBuilder } from './types';
22

3-
export const WalletFactory: WalletFactoryBuilder = (adapter, metadata) => {
3+
export const WalletFactory: WalletFactoryBuilder = (adapterConstructor, metadata) => {
44
return {
5-
adapter,
65
create: () => {
6+
const adapter = new adapterConstructor(metadata.name);
77
return {
88
...metadata,
9-
adapter: adapter,
9+
adapter,
1010
hasWalletReady: () => Promise.resolve(!!adapter.provider),
1111
hasExtensionInstalled: () => Promise.resolve(!!adapter.provider),
1212
};

packages/bitcoin/src/wallets/index.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,5 @@ import { metadata_Unisat, metadata_Xverse } from '@ant-design/web3-assets';
66
import { UnisatBitcoinWallet, XverseBitcoinWallet } from '../adapter';
77
import { WalletFactory } from './factory';
88

9-
export const UnisatWallet = () =>
10-
WalletFactory(new UnisatBitcoinWallet(metadata_Unisat.name), metadata_Unisat);
11-
export const XverseWallet = () =>
12-
WalletFactory(new XverseBitcoinWallet(metadata_Xverse.name), metadata_Xverse);
9+
export const UnisatWallet = () => WalletFactory(UnisatBitcoinWallet, metadata_Unisat);
10+
export const XverseWallet = () => WalletFactory(XverseBitcoinWallet, metadata_Xverse);

packages/bitcoin/src/wallets/types.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import type { Wallet, WalletMetadata } from '@ant-design/web3-common';
22

3-
import type { BitcoinWallet } from '../adapter';
3+
import { BitcoinWallet, XverseBitcoinWallet } from '../adapter';
44

55
export interface WalletWithAdapter extends Wallet {
66
adapter: BitcoinWallet;
77
}
88

99
export interface WalletFactory {
10-
adapter: BitcoinWallet;
1110
create: () => WalletWithAdapter;
1211
}
1312

1413
export type WalletFactoryBuilder = (
15-
adapter: BitcoinWallet,
14+
adapter: typeof XverseBitcoinWallet,
1615
metadata: WalletMetadata,
1716
) => WalletFactory;

pnpm-lock.yaml

-35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)