@naculus/connect-appkit-react 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +144 -0
- package/dist/client.d.ts +144 -0
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2270 -0
- package/dist/index.d.ts +2270 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +30 -12
- package/api-extractor.json +0 -21
- package/src/chain-abstraction/index.ts +0 -8
- package/src/chain-abstraction/useCompareCosts.ts +0 -137
- package/src/chain-abstraction/useExecuteRoute.ts +0 -129
- package/src/chain-abstraction/useRouteQuote.ts +0 -168
- package/src/client.test.ts +0 -242
- package/src/client.ts +0 -265
- package/src/hooks/useAccount.test.ts +0 -67
- package/src/hooks/useAccount.ts +0 -37
- package/src/hooks/useBalance.test.ts +0 -459
- package/src/hooks/useBalance.ts +0 -170
- package/src/hooks/useChain.test.ts +0 -160
- package/src/hooks/useChain.ts +0 -53
- package/src/hooks/useConnect.test.ts +0 -112
- package/src/hooks/useConnect.ts +0 -29
- package/src/hooks/useDisconnect.test.ts +0 -75
- package/src/hooks/useDisconnect.ts +0 -32
- package/src/hooks/useERC20Allowance.ts +0 -93
- package/src/hooks/useERC20Approve.ts +0 -220
- package/src/hooks/useERC20Transfer.ts +0 -134
- package/src/hooks/useERC20TransferSimulation.ts +0 -147
- package/src/hooks/useEmbeddedWallet.test.ts +0 -266
- package/src/hooks/useEmbeddedWallet.ts +0 -181
- package/src/hooks/useLastTx.test.ts +0 -117
- package/src/hooks/useLastTx.ts +0 -73
- package/src/hooks/useLookupAddress.ts +0 -131
- package/src/hooks/useNotification.ts +0 -308
- package/src/hooks/useResolveName.ts +0 -128
- package/src/hooks/useSIWxLogin.test.ts +0 -307
- package/src/hooks/useSIWxLogin.ts +0 -163
- package/src/hooks/useSIWxSession.test.ts +0 -481
- package/src/hooks/useSIWxSession.ts +0 -290
- package/src/hooks/useSendCalls.ts +0 -103
- package/src/hooks/useSendTransaction.test.ts +0 -175
- package/src/hooks/useSendTransaction.ts +0 -80
- package/src/hooks/useSendUserOperation.ts +0 -313
- package/src/hooks/useSession.ts +0 -96
- package/src/hooks/useSessionKeys.ts +0 -356
- package/src/hooks/useSignInWithEthereum.test.ts +0 -224
- package/src/hooks/useSignInWithEthereum.ts +0 -46
- package/src/hooks/useSignInWithX.test.ts +0 -210
- package/src/hooks/useSignInWithX.ts +0 -141
- package/src/hooks/useSignMessage.test.ts +0 -151
- package/src/hooks/useSignMessage.ts +0 -65
- package/src/hooks/useSimulateTransfer.ts +0 -165
- package/src/hooks/useSiwxAuthSession.test.ts +0 -456
- package/src/hooks/useSiwxAuthSession.ts +0 -163
- package/src/hooks/useSmartAccount.ts +0 -194
- package/src/hooks/useSwitchChain.ts +0 -68
- package/src/hooks/useTokenBalance.test.ts +0 -337
- package/src/hooks/useTokenBalance.ts +0 -210
- package/src/hooks/useTransactionSimulation.ts +0 -401
- package/src/hooks/useTxHistory.test.ts +0 -108
- package/src/hooks/useTxHistory.ts +0 -83
- package/src/hooks/useTxMonitor.test.ts +0 -127
- package/src/hooks/useTxMonitor.ts +0 -178
- package/src/hooks/useUserOpStatus.ts +0 -245
- package/src/hooks/useValidateDestination.test.ts +0 -96
- package/src/hooks/useValidateDestination.ts +0 -72
- package/src/hooks/useViemClient.test.ts +0 -155
- package/src/hooks/useViemClient.ts +0 -65
- package/src/hooks/useWallet.test.ts +0 -133
- package/src/hooks/useWallet.ts +0 -39
- package/src/hooks/useWeb3ErrorHandler.ts +0 -102
- package/src/index.ts +0 -186
- package/src/provider/Web3ConnectProvider.test.tsx +0 -178
- package/src/provider/Web3ConnectProvider.tsx +0 -530
- package/src/token-list/TokenSelector.tsx +0 -351
- package/src/token-list/defaults.ts +0 -39
- package/src/token-list/index.ts +0 -14
- package/src/token-list/useTokenList.ts +0 -119
- package/src/token-list/useTokenSearch.ts +0 -99
- package/src/types/index.ts +0 -99
- package/src/utils/chains.ts +0 -115
- package/src/utils/errorMessages.ts +0 -142
- package/src/utils/i18n.ts +0 -90
- package/src/wc-generated/components.ts +0 -377
- package/test/setup.ts +0 -17
- package/test/test-utils.tsx +0 -78
- package/tsup.config.ts +0 -18
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/// <reference types="vitest" />
|
|
2
|
-
/// @vitest-environment jsdom
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
-
import { renderHook } from "@testing-library/react";
|
|
6
|
-
|
|
7
|
-
const mockUseWeb3 = vi.fn();
|
|
8
|
-
vi.mock("../provider/Web3ConnectProvider", () => ({
|
|
9
|
-
useWeb3: () => mockUseWeb3(),
|
|
10
|
-
Web3ConnectProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
vi.mock("../utils/chains", () => {
|
|
14
|
-
const chains = [
|
|
15
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
16
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
17
|
-
];
|
|
18
|
-
return {
|
|
19
|
-
getDefaultChains: () => chains,
|
|
20
|
-
getChainById: (ch: typeof chains, chainId: string) =>
|
|
21
|
-
ch.find((c: typeof chains[0]) => {
|
|
22
|
-
const ns = chainId.startsWith("eip155:")
|
|
23
|
-
? "eip155" : null;
|
|
24
|
-
if (!ns) return false;
|
|
25
|
-
const num = chainId.includes(":") ? parseInt(chainId.split(":")[1], 10) : parseInt(chainId, 10);
|
|
26
|
-
return c.namespace === ns && c.id === num;
|
|
27
|
-
}),
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
import { useChain } from "./useChain";
|
|
32
|
-
|
|
33
|
-
describe("useChain", () => {
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
vi.clearAllMocks();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should return null chainId and null chainInfo when not connected", () => {
|
|
39
|
-
mockUseWeb3.mockReturnValue({
|
|
40
|
-
chainId: null,
|
|
41
|
-
session: null,
|
|
42
|
-
switchChain: vi.fn(),
|
|
43
|
-
chains: [
|
|
44
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
45
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
46
|
-
],
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const { result } = renderHook(() => useChain());
|
|
50
|
-
|
|
51
|
-
expect(result.current.chainId).toBeNull();
|
|
52
|
-
expect(result.current.currentChain).toBeNull();
|
|
53
|
-
expect(result.current.chainInfo).toBeNull();
|
|
54
|
-
// availableChains now returns all configured chains regardless of session
|
|
55
|
-
expect(result.current.availableChains).toHaveLength(2);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("should identify EVM chain", () => {
|
|
59
|
-
mockUseWeb3.mockReturnValue({
|
|
60
|
-
chainId: "eip155:1",
|
|
61
|
-
session: {
|
|
62
|
-
topic: "test",
|
|
63
|
-
namespaces: {
|
|
64
|
-
eip155: {
|
|
65
|
-
chains: ["eip155:1"],
|
|
66
|
-
accounts: ["eip155:1:0x1234"],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
switchChain: vi.fn(),
|
|
71
|
-
chains: [
|
|
72
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
73
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
74
|
-
],
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const { result } = renderHook(() => useChain());
|
|
78
|
-
|
|
79
|
-
expect(result.current.isEvm).toBe(true);
|
|
80
|
-
expect(result.current.currentChain?.name).toBe("Ethereum");
|
|
81
|
-
expect(result.current.chainInfo?.name).toBe("Ethereum");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should return chainInfo as null when no session", () => {
|
|
85
|
-
mockUseWeb3.mockReturnValue({
|
|
86
|
-
chainId: "eip155:137",
|
|
87
|
-
session: null,
|
|
88
|
-
switchChain: vi.fn(),
|
|
89
|
-
chains: [
|
|
90
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
91
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
92
|
-
],
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const { result } = renderHook(() => useChain());
|
|
96
|
-
|
|
97
|
-
expect(result.current.chainInfo).toBeNull();
|
|
98
|
-
expect(result.current.currentChain?.name).toBe("Polygon");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should return all configured chains as available", () => {
|
|
102
|
-
mockUseWeb3.mockReturnValue({
|
|
103
|
-
chainId: "eip155:1",
|
|
104
|
-
session: null,
|
|
105
|
-
switchChain: vi.fn(),
|
|
106
|
-
chains: [
|
|
107
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
108
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
109
|
-
],
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const { result } = renderHook(() => useChain());
|
|
113
|
-
|
|
114
|
-
// availableChains now returns all configured chains regardless of session
|
|
115
|
-
expect(result.current.availableChains.length).toBeGreaterThanOrEqual(2);
|
|
116
|
-
expect(result.current.availableChains.map((c: { name: string }) => c.name)).toEqual(
|
|
117
|
-
expect.arrayContaining(["Ethereum", "Polygon"])
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("should pass through switchChain function", () => {
|
|
122
|
-
const switchChain = vi.fn();
|
|
123
|
-
mockUseWeb3.mockReturnValue({
|
|
124
|
-
chainId: null,
|
|
125
|
-
session: null,
|
|
126
|
-
switchChain,
|
|
127
|
-
chains: [],
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const { result } = renderHook(() => useChain());
|
|
131
|
-
|
|
132
|
-
result.current.switchChain("eip155:137");
|
|
133
|
-
expect(switchChain).toHaveBeenCalledWith("eip155:137");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("should return chainInfo with selected=true", () => {
|
|
137
|
-
mockUseWeb3.mockReturnValue({
|
|
138
|
-
chainId: "eip155:137",
|
|
139
|
-
session: {
|
|
140
|
-
topic: "test",
|
|
141
|
-
namespaces: {
|
|
142
|
-
eip155: {
|
|
143
|
-
chains: ["eip155:137"],
|
|
144
|
-
accounts: ["eip155:137:0x5678"],
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
switchChain: vi.fn(),
|
|
149
|
-
chains: [
|
|
150
|
-
{ id: 1, namespace: "eip155", name: "Ethereum", rpcUrl: "https://eth.llamarpc.com", token: "ETH" },
|
|
151
|
-
{ id: 137, namespace: "eip155", name: "Polygon", rpcUrl: "https://polygon-rpc.com", token: "MATIC" },
|
|
152
|
-
],
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const { result } = renderHook(() => useChain());
|
|
156
|
-
|
|
157
|
-
expect(result.current.chainInfo?.selected).toBe(true);
|
|
158
|
-
expect(result.current.chainInfo?.namespace).toBe("eip155");
|
|
159
|
-
});
|
|
160
|
-
});
|
package/src/hooks/useChain.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { useWeb3 } from "../provider/Web3ConnectProvider";
|
|
3
|
-
import { getChainById } from "../utils/chains";
|
|
4
|
-
import type { WalletChain, ChainInfo } from "../types";
|
|
5
|
-
|
|
6
|
-
export function useChain() {
|
|
7
|
-
const { chainId, session, switchChain, chains } = useWeb3();
|
|
8
|
-
|
|
9
|
-
const currentChain = useMemo(() => {
|
|
10
|
-
if (!chainId) return null;
|
|
11
|
-
return getChainById(chains, chainId) ?? null;
|
|
12
|
-
}, [chainId, chains]);
|
|
13
|
-
|
|
14
|
-
const chainInfo = useMemo<ChainInfo | null>(() => {
|
|
15
|
-
if (!chainId || !session) return null;
|
|
16
|
-
|
|
17
|
-
const namespace = chainId.startsWith("eip155:")
|
|
18
|
-
? "eip155"
|
|
19
|
-
: null;
|
|
20
|
-
|
|
21
|
-
if (!namespace) return null;
|
|
22
|
-
|
|
23
|
-
const chainNum = chainId.includes(":")
|
|
24
|
-
? parseInt(chainId.split(":")[1], 10)
|
|
25
|
-
: parseInt(chainId, 10);
|
|
26
|
-
|
|
27
|
-
const chain = chains.find((c) => c.namespace === namespace && c.id === chainNum);
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
namespace,
|
|
31
|
-
chainId,
|
|
32
|
-
name: chain?.name ?? `Chain ${chainNum}`,
|
|
33
|
-
selected: true
|
|
34
|
-
};
|
|
35
|
-
}, [chainId, session, chains]);
|
|
36
|
-
|
|
37
|
-
const availableChains = useMemo<WalletChain[]>(() => {
|
|
38
|
-
// Return all default EVM chains so users can see and switch to any supported chain
|
|
39
|
-
// The session namespace only contains the current/approved chain, but we want to
|
|
40
|
-
// make all configured chains available for switching.
|
|
41
|
-
return chains;
|
|
42
|
-
}, [chains]);
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
chainId,
|
|
46
|
-
currentChain,
|
|
47
|
-
chainInfo,
|
|
48
|
-
availableChains,
|
|
49
|
-
chains: availableChains,
|
|
50
|
-
isEvm: chainId?.startsWith("eip155:"),
|
|
51
|
-
switchChain
|
|
52
|
-
};
|
|
53
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/// <reference types="vitest" />
|
|
2
|
-
/// @vitest-environment jsdom
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
-
import { renderHook, act } from "@testing-library/react";
|
|
6
|
-
|
|
7
|
-
const mockUseWeb3 = vi.fn();
|
|
8
|
-
vi.mock("../provider/Web3ConnectProvider", () => ({
|
|
9
|
-
useWeb3: () => mockUseWeb3(),
|
|
10
|
-
Web3ConnectProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import { useConnect } from "./useConnect";
|
|
14
|
-
|
|
15
|
-
function mockWeb3(overrides: Record<string, unknown> = {}) {
|
|
16
|
-
return {
|
|
17
|
-
connect: vi.fn(),
|
|
18
|
-
status: "disconnected" as const,
|
|
19
|
-
...overrides,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("useConnect", () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should have initial idle state", () => {
|
|
29
|
-
mockUseWeb3.mockReturnValue(mockWeb3());
|
|
30
|
-
const { result } = renderHook(() => useConnect());
|
|
31
|
-
|
|
32
|
-
expect(result.current.isConnecting).toBe(false);
|
|
33
|
-
expect(result.current.error).toBeNull();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("should call connect and succeed", async () => {
|
|
37
|
-
const connect = vi.fn().mockResolvedValue(undefined);
|
|
38
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ connect }));
|
|
39
|
-
|
|
40
|
-
const { result } = renderHook(() => useConnect());
|
|
41
|
-
|
|
42
|
-
await act(async () => {
|
|
43
|
-
await result.current.connect();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(connect).toHaveBeenCalledTimes(1);
|
|
47
|
-
expect(result.current.isConnecting).toBe(false);
|
|
48
|
-
expect(result.current.error).toBeNull();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should set connecting state during connect", async () => {
|
|
52
|
-
let resolvePromise: () => void;
|
|
53
|
-
const connectPromise = new Promise<void>((resolve) => {
|
|
54
|
-
resolvePromise = resolve;
|
|
55
|
-
});
|
|
56
|
-
const connect = vi.fn().mockReturnValue(connectPromise);
|
|
57
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ connect }));
|
|
58
|
-
|
|
59
|
-
const { result } = renderHook(() => useConnect());
|
|
60
|
-
|
|
61
|
-
let connectCallPromise: Promise<void>;
|
|
62
|
-
act(() => {
|
|
63
|
-
connectCallPromise = result.current.connect();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(result.current.isConnecting).toBe(true);
|
|
67
|
-
|
|
68
|
-
await act(async () => {
|
|
69
|
-
resolvePromise!();
|
|
70
|
-
await connectCallPromise!;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
expect(result.current.isConnecting).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should capture error on failed connect", async () => {
|
|
77
|
-
const testError = new Error("Connection rejected");
|
|
78
|
-
const connect = vi.fn().mockRejectedValue(testError);
|
|
79
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ connect }));
|
|
80
|
-
|
|
81
|
-
const { result } = renderHook(() => useConnect());
|
|
82
|
-
|
|
83
|
-
await act(async () => {
|
|
84
|
-
await result.current.connect();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(result.current.error).not.toBeNull();
|
|
88
|
-
expect(result.current.error?.message).toBe("Connection rejected");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("should capture wallet error on failed connect", async () => {
|
|
92
|
-
const { WalletError } = await import("@naculus/connect-core");
|
|
93
|
-
const walletError = new WalletError("user_rejected", "User rejected");
|
|
94
|
-
const connect = vi.fn().mockRejectedValue(walletError);
|
|
95
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ connect }));
|
|
96
|
-
|
|
97
|
-
const { result } = renderHook(() => useConnect());
|
|
98
|
-
|
|
99
|
-
await act(async () => {
|
|
100
|
-
await result.current.connect();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(result.current.error).toBeInstanceOf(WalletError);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("should reflect connecting status from Web3Provider", () => {
|
|
107
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ status: "connecting" }));
|
|
108
|
-
const { result } = renderHook(() => useConnect());
|
|
109
|
-
|
|
110
|
-
expect(result.current.isConnecting).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
});
|
package/src/hooks/useConnect.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState } from "react";
|
|
2
|
-
import { useWeb3 } from "../provider/Web3ConnectProvider";
|
|
3
|
-
import { WalletError } from "@naculus/connect-core";
|
|
4
|
-
|
|
5
|
-
export function useConnect() {
|
|
6
|
-
const { connect, status } = useWeb3();
|
|
7
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
8
|
-
const [error, setError] = useState<Error | null>(null);
|
|
9
|
-
|
|
10
|
-
const handleConnect = useCallback(async () => {
|
|
11
|
-
setIsLoading(true);
|
|
12
|
-
setError(null);
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await connect();
|
|
16
|
-
} catch (err) {
|
|
17
|
-
const errorMessage = err instanceof Error ? err.message : "Connection failed";
|
|
18
|
-
setError(err instanceof WalletError ? err : new Error(errorMessage));
|
|
19
|
-
} finally {
|
|
20
|
-
setIsLoading(false);
|
|
21
|
-
}
|
|
22
|
-
}, [connect]);
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
connect: handleConnect,
|
|
26
|
-
isConnecting: isLoading || status === "connecting",
|
|
27
|
-
error
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/// <reference types="vitest" />
|
|
2
|
-
/// @vitest-environment jsdom
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
-
import { renderHook, act } from "@testing-library/react";
|
|
6
|
-
|
|
7
|
-
const mockUseWeb3 = vi.fn();
|
|
8
|
-
vi.mock("../provider/Web3ConnectProvider", () => ({
|
|
9
|
-
useWeb3: () => mockUseWeb3(),
|
|
10
|
-
Web3ConnectProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import { useDisconnect } from "./useDisconnect";
|
|
14
|
-
|
|
15
|
-
function mockWeb3(overrides: Record<string, unknown> = {}) {
|
|
16
|
-
return {
|
|
17
|
-
disconnect: vi.fn(),
|
|
18
|
-
status: "connected" as const,
|
|
19
|
-
...overrides,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("useDisconnect", () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should have initial idle state", () => {
|
|
29
|
-
mockUseWeb3.mockReturnValue(mockWeb3());
|
|
30
|
-
const { result } = renderHook(() => useDisconnect());
|
|
31
|
-
|
|
32
|
-
expect(result.current.isDisconnecting).toBe(false);
|
|
33
|
-
expect(result.current.error).toBeNull();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("should call disconnect and succeed when connected", async () => {
|
|
37
|
-
const disconnect = vi.fn().mockResolvedValue(undefined);
|
|
38
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ disconnect }));
|
|
39
|
-
|
|
40
|
-
const { result } = renderHook(() => useDisconnect());
|
|
41
|
-
|
|
42
|
-
await act(async () => {
|
|
43
|
-
await result.current.disconnect();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(disconnect).toHaveBeenCalledTimes(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should not call disconnect if already disconnected", async () => {
|
|
50
|
-
const disconnect = vi.fn();
|
|
51
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ disconnect, status: "disconnected" }));
|
|
52
|
-
|
|
53
|
-
const { result } = renderHook(() => useDisconnect());
|
|
54
|
-
|
|
55
|
-
await act(async () => {
|
|
56
|
-
await result.current.disconnect();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
expect(disconnect).not.toHaveBeenCalled();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should capture error on failed disconnect", async () => {
|
|
63
|
-
const disconnect = vi.fn().mockRejectedValue(new Error("Disconnect failed"));
|
|
64
|
-
mockUseWeb3.mockReturnValue(mockWeb3({ disconnect }));
|
|
65
|
-
|
|
66
|
-
const { result } = renderHook(() => useDisconnect());
|
|
67
|
-
|
|
68
|
-
await act(async () => {
|
|
69
|
-
await result.current.disconnect();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(result.current.error).not.toBeNull();
|
|
73
|
-
expect(result.current.error?.message).toBe("Disconnect failed");
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from "react";
|
|
2
|
-
import { useWeb3 } from "../provider/Web3ConnectProvider";
|
|
3
|
-
|
|
4
|
-
export function useDisconnect() {
|
|
5
|
-
const { disconnect, status } = useWeb3();
|
|
6
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
7
|
-
const [error, setError] = useState<Error | null>(null);
|
|
8
|
-
const mountedRef = useRef(true);
|
|
9
|
-
|
|
10
|
-
const handleDisconnect = useCallback(async () => {
|
|
11
|
-
if (status === "disconnected") return;
|
|
12
|
-
|
|
13
|
-
setIsLoading(true);
|
|
14
|
-
setError(null);
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
await disconnect();
|
|
18
|
-
} catch (err) {
|
|
19
|
-
if (!mountedRef.current) return;
|
|
20
|
-
setError(err instanceof Error ? err : new Error("Disconnect failed"));
|
|
21
|
-
} finally {
|
|
22
|
-
if (mountedRef.current) setIsLoading(false);
|
|
23
|
-
}
|
|
24
|
-
}, [disconnect, status]);
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
disconnect: handleDisconnect,
|
|
28
|
-
isDisconnecting: isLoading,
|
|
29
|
-
error,
|
|
30
|
-
cleanup: () => { mountedRef.current = false; },
|
|
31
|
-
};
|
|
32
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
2
|
-
import { useViemClient } from "./useViemClient";
|
|
3
|
-
import { ERC20_MIN_ABI } from "@naculus/connect-core";
|
|
4
|
-
import type { TokenConfig } from "@naculus/connect-core";
|
|
5
|
-
import type { Address } from "viem";
|
|
6
|
-
|
|
7
|
-
// ── Types ─────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
export interface UseERC20AllowanceOptions {
|
|
10
|
-
token: TokenConfig;
|
|
11
|
-
owner: Address;
|
|
12
|
-
spender: Address;
|
|
13
|
-
refreshInterval?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface UseERC20AllowanceReturn {
|
|
17
|
-
allowance: string | null;
|
|
18
|
-
allowanceRaw: bigint | null;
|
|
19
|
-
isFetching: boolean;
|
|
20
|
-
error: Error | null;
|
|
21
|
-
refetch: () => Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ── Hook ──────────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
export function useERC20Allowance(
|
|
27
|
-
options: UseERC20AllowanceOptions,
|
|
28
|
-
): UseERC20AllowanceReturn {
|
|
29
|
-
const { publicClient } = useViemClient();
|
|
30
|
-
const [allowanceRaw, setAllowanceRaw] = useState<bigint | null>(null);
|
|
31
|
-
const [isFetching, setIsFetching] = useState(false);
|
|
32
|
-
const [error, setError] = useState<Error | null>(null);
|
|
33
|
-
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
34
|
-
|
|
35
|
-
const fetchAllowance = useCallback(async () => {
|
|
36
|
-
if (!publicClient) return;
|
|
37
|
-
|
|
38
|
-
setIsFetching(true);
|
|
39
|
-
setError(null);
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const raw = await publicClient.readContract({
|
|
43
|
-
address: options.token.address,
|
|
44
|
-
abi: ERC20_MIN_ABI,
|
|
45
|
-
functionName: "allowance",
|
|
46
|
-
args: [options.owner, options.spender],
|
|
47
|
-
});
|
|
48
|
-
setAllowanceRaw(raw as bigint);
|
|
49
|
-
} catch (err) {
|
|
50
|
-
setError(err instanceof Error ? err : new Error("Failed to fetch allowance"));
|
|
51
|
-
setAllowanceRaw(null);
|
|
52
|
-
} finally {
|
|
53
|
-
setIsFetching(false);
|
|
54
|
-
}
|
|
55
|
-
}, [publicClient, options.token.address, options.owner, options.spender]);
|
|
56
|
-
|
|
57
|
-
// Initial fetch
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
fetchAllowance().catch((e) => console.warn("useERC20Allowance: initial fetch failed:", e));
|
|
60
|
-
}, [fetchAllowance]);
|
|
61
|
-
|
|
62
|
-
// Auto-refresh interval
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
if (options.refreshInterval && options.refreshInterval > 0) {
|
|
65
|
-
intervalRef.current = setInterval(fetchAllowance, options.refreshInterval);
|
|
66
|
-
}
|
|
67
|
-
return () => {
|
|
68
|
-
if (intervalRef.current) {
|
|
69
|
-
clearInterval(intervalRef.current);
|
|
70
|
-
intervalRef.current = null;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}, [options.refreshInterval, fetchAllowance]);
|
|
74
|
-
|
|
75
|
-
const allowance = useMemo(() => {
|
|
76
|
-
if (allowanceRaw === null) return null;
|
|
77
|
-
const decimals = options.token.decimals ?? 18;
|
|
78
|
-
const str = allowanceRaw.toString();
|
|
79
|
-
const padded = str.padStart(decimals + 1, "0");
|
|
80
|
-
const dotPos = padded.length - decimals;
|
|
81
|
-
let intPart = padded.slice(0, dotPos).replace(/^0+/, "") || "0";
|
|
82
|
-
let fracPart = padded.slice(dotPos).replace(/0+$/, "");
|
|
83
|
-
return fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
84
|
-
}, [allowanceRaw, options.token.decimals]);
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
allowance,
|
|
88
|
-
allowanceRaw,
|
|
89
|
-
isFetching,
|
|
90
|
-
error,
|
|
91
|
-
refetch: fetchAllowance,
|
|
92
|
-
};
|
|
93
|
-
}
|