@btc-vision/walletconnect 1.2.4 → 1.3.1
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/browser/WalletProvider.d.ts +6 -6
- package/browser/index.js +1 -1
- package/build/WalletProvider.d.ts +6 -6
- package/build/WalletProvider.js +104 -83
- package/package.json +7 -7
- package/src/WalletProvider.tsx +122 -105
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
2
|
import { Address } from '@btc-vision/transaction';
|
|
3
3
|
import { AbstractRpcProvider } from 'opnet';
|
|
4
|
-
import
|
|
5
|
-
import { Signers, SupportedWallets, Wallets } from './WalletConnection
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
import { Signers, SupportedWallets, Wallets } from './WalletConnection';
|
|
6
6
|
export interface Account {
|
|
7
7
|
isConnected: boolean;
|
|
8
8
|
signer: Signers | null;
|
|
@@ -12,14 +12,14 @@ export interface Account {
|
|
|
12
12
|
provider: AbstractRpcProvider;
|
|
13
13
|
}
|
|
14
14
|
interface WalletContextType {
|
|
15
|
-
connect: (
|
|
15
|
+
connect: (wallet: SupportedWallets, signal?: AbortSignal) => Promise<void>;
|
|
16
16
|
disconnect: () => void;
|
|
17
17
|
walletType: SupportedWallets | null;
|
|
18
18
|
walletWindowInstance: Wallets | null;
|
|
19
19
|
account: Account | null;
|
|
20
20
|
}
|
|
21
|
-
export declare const WalletProvider:
|
|
22
|
-
children:
|
|
23
|
-
}
|
|
21
|
+
export declare const WalletProvider: ({ children }: {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
24
24
|
export declare const useWallet: () => WalletContextType;
|
|
25
25
|
export {};
|
package/build/WalletProvider.js
CHANGED
|
@@ -1,31 +1,66 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
|
|
3
|
-
import WalletConnection, { SupportedWallets } from './WalletConnection
|
|
3
|
+
import WalletConnection, { SupportedWallets } from './WalletConnection';
|
|
4
4
|
const WalletContext = createContext(undefined);
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const MAX_RETRIES = 10;
|
|
6
|
+
const RETRY_DELAY_MS = 2000;
|
|
7
|
+
function useDocumentComplete(fn) {
|
|
8
|
+
const fired = useRef(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (fired.current)
|
|
11
|
+
return;
|
|
12
|
+
const run = () => {
|
|
13
|
+
if (fired.current)
|
|
14
|
+
return;
|
|
15
|
+
fired.current = true;
|
|
16
|
+
fn();
|
|
17
|
+
};
|
|
18
|
+
if (document.readyState === 'complete') {
|
|
19
|
+
run();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const handler = () => {
|
|
23
|
+
if (document.readyState === 'complete') {
|
|
24
|
+
document.removeEventListener('readystatechange', handler);
|
|
25
|
+
run();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
document.addEventListener('readystatechange', handler);
|
|
29
|
+
return () => document.removeEventListener('readystatechange', handler);
|
|
30
|
+
}, [fn]);
|
|
31
|
+
}
|
|
7
32
|
export const WalletProvider = ({ children }) => {
|
|
8
33
|
const [walletConnection] = useState(() => new WalletConnection());
|
|
9
34
|
const [walletType, setWalletType] = useState(null);
|
|
10
35
|
const [walletWindowInstance, setWalletWindowInstance] = useState(null);
|
|
11
36
|
const [account, setAccount] = useState(null);
|
|
12
|
-
const
|
|
37
|
+
const listeners = useRef({});
|
|
13
38
|
const disconnect = useCallback(() => {
|
|
39
|
+
const inst = walletWindowInstance;
|
|
40
|
+
if (inst) {
|
|
41
|
+
if (listeners.current.disconnect) {
|
|
42
|
+
inst.removeListener?.('disconnect', listeners.current.disconnect);
|
|
43
|
+
listeners.current.disconnect = undefined;
|
|
44
|
+
}
|
|
45
|
+
if (listeners.current.accountsChanged) {
|
|
46
|
+
inst.removeListener?.('accountsChanged', listeners.current.accountsChanged);
|
|
47
|
+
listeners.current.accountsChanged = undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
14
50
|
walletConnection.disconnect();
|
|
15
51
|
setWalletType(null);
|
|
16
52
|
setWalletWindowInstance(null);
|
|
17
53
|
localStorage.removeItem('walletType');
|
|
18
54
|
setAccount(null);
|
|
19
|
-
|
|
20
|
-
}, [walletConnection]);
|
|
55
|
+
}, [walletConnection, walletWindowInstance]);
|
|
21
56
|
const connect = useCallback(async (type, signal) => {
|
|
22
|
-
let success = false;
|
|
23
57
|
let attempt = 0;
|
|
24
|
-
|
|
25
|
-
if (signal?.aborted)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
const throwIfAborted = () => {
|
|
59
|
+
if (signal?.aborted)
|
|
60
|
+
throw new DOMException('Aborted', 'AbortError');
|
|
61
|
+
};
|
|
62
|
+
while (attempt < MAX_RETRIES) {
|
|
63
|
+
throwIfAborted();
|
|
29
64
|
try {
|
|
30
65
|
await walletConnection.connect(type);
|
|
31
66
|
if ((walletConnection.walletType !== SupportedWallets.OP_WALLET &&
|
|
@@ -33,20 +68,22 @@ export const WalletProvider = ({ children }) => {
|
|
|
33
68
|
!walletConnection.walletWindowInstance) {
|
|
34
69
|
throw new Error('Wallet not fully loaded yet');
|
|
35
70
|
}
|
|
36
|
-
success = true;
|
|
37
71
|
break;
|
|
38
72
|
}
|
|
39
|
-
catch (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
73
|
+
catch (err) {
|
|
74
|
+
attempt += 1;
|
|
75
|
+
if (attempt >= MAX_RETRIES) {
|
|
76
|
+
console.warn(`Failed to connect after ${MAX_RETRIES} attempts.`, err);
|
|
77
|
+
disconnect();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.warn(`Connection attempt ${attempt} failed:`, err.message);
|
|
44
81
|
try {
|
|
45
|
-
await new Promise((
|
|
46
|
-
const
|
|
82
|
+
await new Promise((res, rej) => {
|
|
83
|
+
const t = setTimeout(res, RETRY_DELAY_MS);
|
|
47
84
|
signal?.addEventListener('abort', () => {
|
|
48
|
-
clearTimeout(
|
|
49
|
-
|
|
85
|
+
clearTimeout(t);
|
|
86
|
+
rej(new DOMException('Aborted', 'AbortError'));
|
|
50
87
|
});
|
|
51
88
|
});
|
|
52
89
|
}
|
|
@@ -56,25 +93,18 @@ export const WalletProvider = ({ children }) => {
|
|
|
56
93
|
}
|
|
57
94
|
}
|
|
58
95
|
}
|
|
59
|
-
|
|
60
|
-
console.warn(`Failed to connect after ${maxRetries} attempts.`);
|
|
61
|
-
disconnect();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (signal?.aborted) {
|
|
65
|
-
console.debug('Connection aborted after a success, cleaning up.');
|
|
66
|
-
disconnect();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
96
|
+
throwIfAborted();
|
|
69
97
|
setWalletType(type);
|
|
70
98
|
setWalletWindowInstance(walletConnection.walletWindowInstance);
|
|
71
99
|
localStorage.setItem('walletType', type);
|
|
72
100
|
try {
|
|
73
|
-
const signer =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
const [signer, address, addressTyped, network, provider] = await Promise.all([
|
|
102
|
+
walletConnection.signer,
|
|
103
|
+
walletConnection.getAddress(),
|
|
104
|
+
walletConnection.getAddressTyped(),
|
|
105
|
+
walletConnection.getNetwork(),
|
|
106
|
+
walletConnection.getProvider(),
|
|
107
|
+
]);
|
|
78
108
|
setAccount({
|
|
79
109
|
isConnected: true,
|
|
80
110
|
signer,
|
|
@@ -83,75 +113,66 @@ export const WalletProvider = ({ children }) => {
|
|
|
83
113
|
network,
|
|
84
114
|
provider,
|
|
85
115
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const instance = walletConnection.walletWindowInstance;
|
|
91
|
-
instance.on('disconnect', () => {
|
|
92
|
-
disconnect();
|
|
93
|
-
});
|
|
94
|
-
instance.on('accountsChanged', async () => {
|
|
116
|
+
const instance = walletConnection.walletWindowInstance;
|
|
117
|
+
if (instance) {
|
|
118
|
+
const onDisconnect = () => disconnect();
|
|
119
|
+
const onAccountsChanged = async () => {
|
|
95
120
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
const [updatedAddr, updatedAddrTyped, updatedNet, updatedProv] = await Promise.all([
|
|
122
|
+
walletConnection.getAddress(),
|
|
123
|
+
walletConnection.getAddressTyped(),
|
|
124
|
+
walletConnection.getNetwork(),
|
|
125
|
+
walletConnection.getProvider(),
|
|
126
|
+
]);
|
|
100
127
|
setAccount((prev) => prev
|
|
101
128
|
? {
|
|
102
129
|
...prev,
|
|
103
|
-
address:
|
|
104
|
-
addressTyped:
|
|
105
|
-
network:
|
|
106
|
-
provider:
|
|
130
|
+
address: updatedAddr,
|
|
131
|
+
addressTyped: updatedAddrTyped,
|
|
132
|
+
network: updatedNet,
|
|
133
|
+
provider: updatedProv,
|
|
107
134
|
}
|
|
108
135
|
: prev);
|
|
109
136
|
}
|
|
110
|
-
catch
|
|
137
|
+
catch {
|
|
111
138
|
disconnect();
|
|
112
|
-
throw err;
|
|
113
139
|
}
|
|
114
|
-
}
|
|
115
|
-
|
|
140
|
+
};
|
|
141
|
+
listeners.current.disconnect = onDisconnect;
|
|
142
|
+
listeners.current.accountsChanged = onAccountsChanged;
|
|
143
|
+
instance.on('disconnect', onDisconnect);
|
|
144
|
+
instance.on('accountsChanged', onAccountsChanged);
|
|
116
145
|
}
|
|
117
146
|
}
|
|
118
|
-
catch (
|
|
119
|
-
console.warn('Unable to finalize wallet connection:',
|
|
147
|
+
catch (err) {
|
|
148
|
+
console.warn('Unable to finalize wallet connection:', err);
|
|
120
149
|
disconnect();
|
|
121
150
|
}
|
|
122
|
-
}, [
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
151
|
+
}, [walletConnection, disconnect]);
|
|
152
|
+
useDocumentComplete(() => {
|
|
153
|
+
const stored = localStorage.getItem('walletType');
|
|
154
|
+
if (!stored)
|
|
126
155
|
return;
|
|
127
156
|
const controller = new AbortController();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
connect(stored, controller.signal).catch((err) => {
|
|
158
|
+
if (err.name !== 'AbortError') {
|
|
159
|
+
console.warn('Failed to reconnect to wallet:', err);
|
|
131
160
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
})();
|
|
138
|
-
return () => {
|
|
139
|
-
controller.abort();
|
|
140
|
-
};
|
|
141
|
-
}, [connect]);
|
|
142
|
-
const value = {
|
|
161
|
+
});
|
|
162
|
+
return () => controller.abort();
|
|
163
|
+
});
|
|
164
|
+
const ctx = {
|
|
143
165
|
connect,
|
|
144
166
|
disconnect,
|
|
145
167
|
walletType,
|
|
146
168
|
walletWindowInstance,
|
|
147
169
|
account,
|
|
148
170
|
};
|
|
149
|
-
return _jsx(WalletContext.Provider, { value:
|
|
171
|
+
return _jsx(WalletContext.Provider, { value: ctx, children: children });
|
|
150
172
|
};
|
|
151
173
|
export const useWallet = () => {
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
174
|
+
const ctx = useContext(WalletContext);
|
|
175
|
+
if (!ctx)
|
|
154
176
|
throw new Error('useWallet must be used within a WalletProvider');
|
|
155
|
-
|
|
156
|
-
return context;
|
|
177
|
+
return ctx;
|
|
157
178
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/walletconnect",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.1",
|
|
5
5
|
"author": "impredmet",
|
|
6
6
|
"description": "The OP_NET Wallet Connect library helps your dApp connect to any compatible wallet.",
|
|
7
7
|
"engines": {
|
|
@@ -67,10 +67,10 @@
|
|
|
67
67
|
"react-dom": "^19"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@babel/preset-env": "^7.27.
|
|
70
|
+
"@babel/preset-env": "^7.27.2",
|
|
71
71
|
"@babel/preset-react": "^7.27.1",
|
|
72
72
|
"@babel/preset-typescript": "^7.27.1",
|
|
73
|
-
"@types/react": "^19.1.
|
|
73
|
+
"@types/react": "^19.1.5",
|
|
74
74
|
"gulp": "^5.0.0",
|
|
75
75
|
"gulp-cached": "^1.1.1",
|
|
76
76
|
"gulp-typescript": "^6.0.0-alpha.1",
|
|
@@ -80,17 +80,17 @@
|
|
|
80
80
|
"https-browserify": "^1.0.0",
|
|
81
81
|
"os-browserify": "^0.3.0",
|
|
82
82
|
"stream-browserify": "^3.0.0",
|
|
83
|
-
"typescript-eslint": "^8.32.
|
|
83
|
+
"typescript-eslint": "^8.32.1",
|
|
84
84
|
"webpack-cli": "^6.0.1"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
87
|
"@btc-vision/bitcoin": "^6.3.6",
|
|
88
88
|
"@btc-vision/transaction": "^1.4.0",
|
|
89
|
-
"@eslint/js": "^9.
|
|
89
|
+
"@eslint/js": "^9.27.0",
|
|
90
90
|
"gulp-clean": "^0.4.0",
|
|
91
91
|
"gulp-eslint-new": "^2.4.0",
|
|
92
92
|
"gulp-logger-new": "^1.0.1",
|
|
93
|
-
"opnet": "^1.
|
|
94
|
-
"webpack": "^5.99.
|
|
93
|
+
"opnet": "^1.5.1",
|
|
94
|
+
"webpack": "^5.99.9"
|
|
95
95
|
}
|
|
96
96
|
}
|
package/src/WalletProvider.tsx
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
2
|
import { Address } from '@btc-vision/transaction';
|
|
3
3
|
import { AbstractRpcProvider } from 'opnet';
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
5
|
createContext,
|
|
6
|
+
ReactNode,
|
|
6
7
|
useCallback,
|
|
7
8
|
useContext,
|
|
8
9
|
useEffect,
|
|
9
10
|
useRef,
|
|
10
11
|
useState,
|
|
11
12
|
} from 'react';
|
|
12
|
-
import WalletConnection, { Signers, SupportedWallets, Wallets } from './WalletConnection
|
|
13
|
+
import WalletConnection, { Signers, SupportedWallets, Wallets } from './WalletConnection';
|
|
13
14
|
|
|
14
15
|
export interface Account {
|
|
15
16
|
isConnected: boolean;
|
|
@@ -21,7 +22,7 @@ export interface Account {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
interface WalletContextType {
|
|
24
|
-
connect: (
|
|
25
|
+
connect: (wallet: SupportedWallets, signal?: AbortSignal) => Promise<void>;
|
|
25
26
|
disconnect: () => void;
|
|
26
27
|
walletType: SupportedWallets | null;
|
|
27
28
|
walletWindowInstance: Wallets | null;
|
|
@@ -30,38 +31,79 @@ interface WalletContextType {
|
|
|
30
31
|
|
|
31
32
|
const WalletContext = createContext<WalletContextType | undefined>(undefined);
|
|
32
33
|
|
|
33
|
-
const
|
|
34
|
-
const
|
|
34
|
+
const MAX_RETRIES = 10;
|
|
35
|
+
const RETRY_DELAY_MS = 2_000;
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
function useDocumentComplete(fn: () => void) {
|
|
38
|
+
const fired = useRef(false);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (fired.current) return;
|
|
42
|
+
const run = () => {
|
|
43
|
+
if (fired.current) return;
|
|
44
|
+
fired.current = true;
|
|
45
|
+
fn();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (document.readyState === 'complete') {
|
|
49
|
+
run();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const handler = () => {
|
|
54
|
+
if (document.readyState === 'complete') {
|
|
55
|
+
document.removeEventListener('readystatechange', handler);
|
|
56
|
+
run();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
document.addEventListener('readystatechange', handler);
|
|
60
|
+
return () => document.removeEventListener('readystatechange', handler);
|
|
61
|
+
}, [fn]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const WalletProvider = ({ children }: { children: ReactNode }) => {
|
|
37
65
|
const [walletConnection] = useState(() => new WalletConnection());
|
|
38
66
|
const [walletType, setWalletType] = useState<SupportedWallets | null>(null);
|
|
39
67
|
const [walletWindowInstance, setWalletWindowInstance] = useState<Wallets | null>(null);
|
|
40
68
|
const [account, setAccount] = useState<Account | null>(null);
|
|
41
69
|
|
|
42
|
-
|
|
70
|
+
/** keeps the latest listeners so they can be removed in `disconnect` */
|
|
71
|
+
const listeners = useRef<{
|
|
72
|
+
disconnect?: () => void;
|
|
73
|
+
accountsChanged?: () => void;
|
|
74
|
+
}>({});
|
|
43
75
|
|
|
44
76
|
const disconnect = useCallback(() => {
|
|
77
|
+
// detach previously attached listeners, if any
|
|
78
|
+
const inst = walletWindowInstance;
|
|
79
|
+
if (inst) {
|
|
80
|
+
if (listeners.current.disconnect) {
|
|
81
|
+
inst.removeListener?.('disconnect', listeners.current.disconnect);
|
|
82
|
+
listeners.current.disconnect = undefined;
|
|
83
|
+
}
|
|
84
|
+
if (listeners.current.accountsChanged) {
|
|
85
|
+
inst.removeListener?.('accountsChanged', listeners.current.accountsChanged);
|
|
86
|
+
listeners.current.accountsChanged = undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
45
90
|
walletConnection.disconnect();
|
|
46
91
|
setWalletType(null);
|
|
47
92
|
setWalletWindowInstance(null);
|
|
48
93
|
localStorage.removeItem('walletType');
|
|
49
94
|
setAccount(null);
|
|
50
|
-
|
|
51
|
-
}, [walletConnection]);
|
|
95
|
+
}, [walletConnection, walletWindowInstance]);
|
|
52
96
|
|
|
53
97
|
const connect = useCallback(
|
|
54
98
|
async (type: SupportedWallets, signal?: AbortSignal) => {
|
|
55
|
-
let success = false;
|
|
56
99
|
let attempt = 0;
|
|
57
100
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
console.debug('Connection aborted.');
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
101
|
+
const throwIfAborted = () => {
|
|
102
|
+
if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');
|
|
103
|
+
};
|
|
64
104
|
|
|
105
|
+
while (attempt < MAX_RETRIES) {
|
|
106
|
+
throwIfAborted();
|
|
65
107
|
try {
|
|
66
108
|
await walletConnection.connect(type);
|
|
67
109
|
|
|
@@ -72,60 +114,45 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
|
|
72
114
|
) {
|
|
73
115
|
throw new Error('Wallet not fully loaded yet');
|
|
74
116
|
}
|
|
117
|
+
break; // success
|
|
118
|
+
} catch (err) {
|
|
119
|
+
attempt += 1;
|
|
120
|
+
if (attempt >= MAX_RETRIES) {
|
|
121
|
+
console.warn(`Failed to connect after ${MAX_RETRIES} attempts.`, err);
|
|
122
|
+
disconnect();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.warn(`Connection attempt ${attempt} failed:`, (err as Error).message);
|
|
75
126
|
|
|
76
|
-
success = true;
|
|
77
|
-
break;
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.warn(
|
|
80
|
-
`Connection attempt ${attempt + 1} failed:`,
|
|
81
|
-
(error as Error).message,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
attempt++;
|
|
86
|
-
if (attempt < maxRetries) {
|
|
87
|
-
// Wait before the next retry, unless aborted
|
|
88
127
|
try {
|
|
89
|
-
await new Promise<void>((
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
// If the signal is aborted during the timeout, reject so we jump out
|
|
128
|
+
await new Promise<void>((res, rej) => {
|
|
129
|
+
const t = setTimeout(res, RETRY_DELAY_MS);
|
|
93
130
|
signal?.addEventListener('abort', () => {
|
|
94
|
-
clearTimeout(
|
|
95
|
-
|
|
131
|
+
clearTimeout(t);
|
|
132
|
+
rej(new DOMException('Aborted', 'AbortError'));
|
|
96
133
|
});
|
|
97
134
|
});
|
|
98
135
|
} catch {
|
|
99
|
-
// The abort event was triggered during the delay
|
|
100
136
|
console.debug('Connection aborted during retry delay.');
|
|
101
137
|
return;
|
|
102
138
|
}
|
|
103
139
|
}
|
|
104
140
|
}
|
|
105
141
|
|
|
106
|
-
|
|
107
|
-
console.warn(`Failed to connect after ${maxRetries} attempts.`);
|
|
108
|
-
disconnect();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// If the signal was aborted right after a successful connect, bail
|
|
113
|
-
if (signal?.aborted) {
|
|
114
|
-
console.debug('Connection aborted after a success, cleaning up.');
|
|
115
|
-
disconnect();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
142
|
+
throwIfAborted();
|
|
118
143
|
|
|
119
144
|
setWalletType(type);
|
|
120
145
|
setWalletWindowInstance(walletConnection.walletWindowInstance);
|
|
121
146
|
localStorage.setItem('walletType', type);
|
|
122
147
|
|
|
123
148
|
try {
|
|
124
|
-
const signer =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
149
|
+
const [signer, address, addressTyped, network, provider] = await Promise.all([
|
|
150
|
+
walletConnection.signer,
|
|
151
|
+
walletConnection.getAddress(),
|
|
152
|
+
walletConnection.getAddressTyped(),
|
|
153
|
+
walletConnection.getNetwork(),
|
|
154
|
+
walletConnection.getProvider(),
|
|
155
|
+
]);
|
|
129
156
|
|
|
130
157
|
setAccount({
|
|
131
158
|
isConnected: true,
|
|
@@ -136,74 +163,66 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
|
|
136
163
|
provider,
|
|
137
164
|
});
|
|
138
165
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
) {
|
|
145
|
-
const instance = walletConnection.walletWindowInstance;
|
|
146
|
-
instance.on('disconnect', () => {
|
|
147
|
-
disconnect();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
instance.on('accountsChanged', async () => {
|
|
166
|
+
/* attach listeners exactly once per successful connect */
|
|
167
|
+
const instance = walletConnection.walletWindowInstance;
|
|
168
|
+
if (instance) {
|
|
169
|
+
const onDisconnect = () => disconnect();
|
|
170
|
+
const onAccountsChanged = async () => {
|
|
151
171
|
try {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
172
|
+
const [updatedAddr, updatedAddrTyped, updatedNet, updatedProv] =
|
|
173
|
+
await Promise.all([
|
|
174
|
+
walletConnection.getAddress(),
|
|
175
|
+
walletConnection.getAddressTyped(),
|
|
176
|
+
walletConnection.getNetwork(),
|
|
177
|
+
walletConnection.getProvider(),
|
|
178
|
+
]);
|
|
156
179
|
|
|
157
180
|
setAccount((prev) =>
|
|
158
181
|
prev
|
|
159
182
|
? {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
183
|
+
...prev,
|
|
184
|
+
address: updatedAddr,
|
|
185
|
+
addressTyped: updatedAddrTyped,
|
|
186
|
+
network: updatedNet,
|
|
187
|
+
provider: updatedProv,
|
|
188
|
+
}
|
|
166
189
|
: prev,
|
|
167
190
|
);
|
|
168
|
-
} catch
|
|
191
|
+
} catch {
|
|
169
192
|
disconnect();
|
|
170
|
-
throw err;
|
|
171
193
|
}
|
|
172
|
-
}
|
|
194
|
+
};
|
|
173
195
|
|
|
174
|
-
|
|
196
|
+
/* store for later removal */
|
|
197
|
+
listeners.current.disconnect = onDisconnect;
|
|
198
|
+
listeners.current.accountsChanged = onAccountsChanged;
|
|
199
|
+
|
|
200
|
+
instance.on('disconnect', onDisconnect);
|
|
201
|
+
instance.on('accountsChanged', onAccountsChanged);
|
|
175
202
|
}
|
|
176
|
-
} catch (
|
|
177
|
-
console.warn('Unable to finalize wallet connection:',
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.warn('Unable to finalize wallet connection:', err);
|
|
178
205
|
disconnect();
|
|
179
206
|
}
|
|
180
207
|
},
|
|
181
|
-
[
|
|
208
|
+
[walletConnection, disconnect],
|
|
182
209
|
);
|
|
183
210
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (!storedWalletType) return;
|
|
211
|
+
useDocumentComplete(() => {
|
|
212
|
+
const stored = localStorage.getItem('walletType') as SupportedWallets | null;
|
|
213
|
+
if (!stored) return;
|
|
188
214
|
|
|
189
215
|
const controller = new AbortController();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
} catch (error) {
|
|
194
|
-
// AbortError is normal if the component unmounted or re-rendered quickly
|
|
195
|
-
if ((error as DOMException).name !== 'AbortError') {
|
|
196
|
-
console.warn('Failed to reconnect to wallet:', error);
|
|
197
|
-
}
|
|
216
|
+
connect(stored, controller.signal).catch((err: unknown) => {
|
|
217
|
+
if ((err as DOMException).name !== 'AbortError') {
|
|
218
|
+
console.warn('Failed to reconnect to wallet:', err);
|
|
198
219
|
}
|
|
199
|
-
})
|
|
220
|
+
});
|
|
200
221
|
|
|
201
|
-
return () =>
|
|
202
|
-
|
|
203
|
-
};
|
|
204
|
-
}, [connect]);
|
|
222
|
+
return () => controller.abort();
|
|
223
|
+
});
|
|
205
224
|
|
|
206
|
-
const
|
|
225
|
+
const ctx: WalletContextType = {
|
|
207
226
|
connect,
|
|
208
227
|
disconnect,
|
|
209
228
|
walletType,
|
|
@@ -211,13 +230,11 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
|
|
211
230
|
account,
|
|
212
231
|
};
|
|
213
232
|
|
|
214
|
-
return <WalletContext.Provider value={
|
|
233
|
+
return <WalletContext.Provider value={ctx}>{children}</WalletContext.Provider>;
|
|
215
234
|
};
|
|
216
235
|
|
|
217
236
|
export const useWallet = (): WalletContextType => {
|
|
218
|
-
const
|
|
219
|
-
if (!
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
return context;
|
|
237
|
+
const ctx = useContext(WalletContext);
|
|
238
|
+
if (!ctx) throw new Error('useWallet must be used within a WalletProvider');
|
|
239
|
+
return ctx;
|
|
223
240
|
};
|