@obelyzk/sdk 0.5.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 +481 -0
- package/bin/bitsage-demo.ts +12 -0
- package/dist/chunk-3D3DPNVV.mjs +700 -0
- package/dist/chunk-CGPFHXUF.mjs +701 -0
- package/dist/chunk-F3E66KH7.mjs +683 -0
- package/dist/chunk-KRCKY3II.mjs +5857 -0
- package/dist/chunk-LXJT3QK6.mjs +5950 -0
- package/dist/chunk-O2PF7VJA.mjs +700 -0
- package/dist/index.d.mts +868 -0
- package/dist/index.d.ts +868 -0
- package/dist/index.js +7445 -0
- package/dist/index.mjs +853 -0
- package/dist/obelysk/index.d.mts +567 -0
- package/dist/obelysk/index.d.ts +567 -0
- package/dist/obelysk/index.js +1760 -0
- package/dist/obelysk/index.mjs +1449 -0
- package/dist/privacy/index.d.mts +408 -0
- package/dist/privacy/index.d.ts +408 -0
- package/dist/privacy/index.js +735 -0
- package/dist/privacy/index.mjs +44 -0
- package/dist/react/index.d.mts +1367 -0
- package/dist/react/index.d.ts +1367 -0
- package/dist/react/index.js +8442 -0
- package/dist/react/index.mjs +2600 -0
- package/dist/tee-BKXj7gQs.d.mts +4749 -0
- package/dist/tee-BKXj7gQs.d.ts +4749 -0
- package/dist/tee-BslKx4iU.d.mts +4749 -0
- package/dist/tee-BslKx4iU.d.ts +4749 -0
- package/dist/tee-CiR0hpfm.d.mts +4794 -0
- package/dist/tee-CiR0hpfm.d.ts +4794 -0
- package/package.json +101 -0
|
@@ -0,0 +1,2600 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BatchClient,
|
|
3
|
+
BitSageClient,
|
|
4
|
+
ContractClient,
|
|
5
|
+
DashboardClient,
|
|
6
|
+
GovernanceClient,
|
|
7
|
+
HttpClient,
|
|
8
|
+
MiningClient,
|
|
9
|
+
PaymentsClient,
|
|
10
|
+
PrivacyClient,
|
|
11
|
+
PrivacyKeyManager,
|
|
12
|
+
StakingClient,
|
|
13
|
+
StwoClient,
|
|
14
|
+
TeeClient,
|
|
15
|
+
WebSocketClient,
|
|
16
|
+
WorkersClient,
|
|
17
|
+
getContractsForNetwork
|
|
18
|
+
} from "../chunk-LXJT3QK6.mjs";
|
|
19
|
+
import "../chunk-O2PF7VJA.mjs";
|
|
20
|
+
|
|
21
|
+
// src/react/providers/BitSageProvider.tsx
|
|
22
|
+
import {
|
|
23
|
+
createContext,
|
|
24
|
+
useContext,
|
|
25
|
+
useMemo,
|
|
26
|
+
useCallback,
|
|
27
|
+
useState
|
|
28
|
+
} from "react";
|
|
29
|
+
import { jsx } from "react/jsx-runtime";
|
|
30
|
+
var BitSageContext = createContext(void 0);
|
|
31
|
+
function getDefaultApiUrl(network) {
|
|
32
|
+
switch (network) {
|
|
33
|
+
case "mainnet":
|
|
34
|
+
return "https://api.bitsage.network";
|
|
35
|
+
case "sepolia":
|
|
36
|
+
return "https://api.sepolia.bitsage.network";
|
|
37
|
+
case "local":
|
|
38
|
+
return "http://localhost:8080";
|
|
39
|
+
default:
|
|
40
|
+
return "https://api.sepolia.bitsage.network";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function getDefaultRpcUrl(network) {
|
|
44
|
+
switch (network) {
|
|
45
|
+
case "mainnet":
|
|
46
|
+
return "https://starknet-mainnet.public.blastapi.io";
|
|
47
|
+
case "sepolia":
|
|
48
|
+
return "https://starknet-sepolia.public.blastapi.io";
|
|
49
|
+
case "local":
|
|
50
|
+
return "http://localhost:5050";
|
|
51
|
+
default:
|
|
52
|
+
return "https://starknet-sepolia.public.blastapi.io";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function BitSageProvider({
|
|
56
|
+
children,
|
|
57
|
+
network = "sepolia",
|
|
58
|
+
apiUrl,
|
|
59
|
+
rpcUrl,
|
|
60
|
+
wallet: initialWallet,
|
|
61
|
+
httpConfig,
|
|
62
|
+
contractConfig
|
|
63
|
+
}) {
|
|
64
|
+
const [walletState, setWalletState] = useState(
|
|
65
|
+
initialWallet || null
|
|
66
|
+
);
|
|
67
|
+
const resolvedApiUrl = apiUrl || getDefaultApiUrl(network);
|
|
68
|
+
const resolvedRpcUrl = rpcUrl || getDefaultRpcUrl(network);
|
|
69
|
+
const contracts = useMemo(() => getContractsForNetwork(network), [network]);
|
|
70
|
+
const http = useMemo(
|
|
71
|
+
() => new HttpClient({
|
|
72
|
+
baseUrl: resolvedApiUrl,
|
|
73
|
+
...httpConfig
|
|
74
|
+
}),
|
|
75
|
+
[resolvedApiUrl, httpConfig]
|
|
76
|
+
);
|
|
77
|
+
const contract = useMemo(() => {
|
|
78
|
+
const config = {
|
|
79
|
+
rpcUrl: resolvedRpcUrl,
|
|
80
|
+
network,
|
|
81
|
+
...contractConfig
|
|
82
|
+
};
|
|
83
|
+
if (walletState) {
|
|
84
|
+
config.accountAddress = walletState.address;
|
|
85
|
+
if (walletState.privateKey) {
|
|
86
|
+
config.privateKey = walletState.privateKey;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return new ContractClient(config);
|
|
90
|
+
}, [resolvedRpcUrl, network, walletState, contractConfig]);
|
|
91
|
+
const client = useMemo(() => {
|
|
92
|
+
const config = {
|
|
93
|
+
apiUrl: resolvedApiUrl,
|
|
94
|
+
starknetRpcUrl: resolvedRpcUrl,
|
|
95
|
+
timeout: 3e4,
|
|
96
|
+
network
|
|
97
|
+
};
|
|
98
|
+
return new BitSageClient(config);
|
|
99
|
+
}, [resolvedApiUrl, resolvedRpcUrl, network]);
|
|
100
|
+
const mining = useMemo(
|
|
101
|
+
() => new MiningClient(http, contract, {
|
|
102
|
+
contractAddress: contracts.miningRewards
|
|
103
|
+
}),
|
|
104
|
+
[http, contract, contracts]
|
|
105
|
+
);
|
|
106
|
+
const dashboard = useMemo(
|
|
107
|
+
() => new DashboardClient(http),
|
|
108
|
+
[http]
|
|
109
|
+
);
|
|
110
|
+
const batch = useMemo(
|
|
111
|
+
() => new BatchClient(http),
|
|
112
|
+
[http]
|
|
113
|
+
);
|
|
114
|
+
const payments = useMemo(
|
|
115
|
+
() => new PaymentsClient(http, contract, {
|
|
116
|
+
paymentRouterAddress: contracts.paymentRouter,
|
|
117
|
+
proofGatedPaymentAddress: contracts.proofGatedPayment
|
|
118
|
+
}),
|
|
119
|
+
[http, contract, contracts]
|
|
120
|
+
);
|
|
121
|
+
const staking = useMemo(
|
|
122
|
+
() => new StakingClient(http, contract, {
|
|
123
|
+
proverStakingAddress: contracts.proverStaking,
|
|
124
|
+
cdcPoolAddress: contracts.cdcPool
|
|
125
|
+
}),
|
|
126
|
+
[http, contract, contracts]
|
|
127
|
+
);
|
|
128
|
+
const workers = useMemo(
|
|
129
|
+
() => new WorkersClient(http, contract, {
|
|
130
|
+
cdcPoolAddress: contracts.cdcPool,
|
|
131
|
+
reputationManagerAddress: contracts.reputationManager
|
|
132
|
+
}),
|
|
133
|
+
[http, contract, contracts]
|
|
134
|
+
);
|
|
135
|
+
const governance = useMemo(
|
|
136
|
+
() => new GovernanceClient(http, contract, {
|
|
137
|
+
sageTokenAddress: contracts.sageToken,
|
|
138
|
+
governanceAddress: contracts.sageToken
|
|
139
|
+
// Governance is part of token contract
|
|
140
|
+
}),
|
|
141
|
+
[http, contract, contracts]
|
|
142
|
+
);
|
|
143
|
+
const stwo = useMemo(
|
|
144
|
+
() => new StwoClient(http, contract, {
|
|
145
|
+
stwoVerifierAddress: contracts.stwoVerifier,
|
|
146
|
+
optimisticTeeAddress: contracts.optimisticTee
|
|
147
|
+
}),
|
|
148
|
+
[http, contract, contracts]
|
|
149
|
+
);
|
|
150
|
+
const tee = useMemo(
|
|
151
|
+
() => new TeeClient(http, contract, {
|
|
152
|
+
optimisticTeeAddress: contracts.optimisticTee
|
|
153
|
+
}),
|
|
154
|
+
[http, contract, contracts]
|
|
155
|
+
);
|
|
156
|
+
const connect = useCallback((wallet) => {
|
|
157
|
+
setWalletState(wallet);
|
|
158
|
+
}, []);
|
|
159
|
+
const disconnect = useCallback(() => {
|
|
160
|
+
setWalletState(null);
|
|
161
|
+
}, []);
|
|
162
|
+
const value = useMemo(
|
|
163
|
+
() => ({
|
|
164
|
+
client,
|
|
165
|
+
http,
|
|
166
|
+
contract,
|
|
167
|
+
contracts,
|
|
168
|
+
network,
|
|
169
|
+
isConnected: walletState !== null,
|
|
170
|
+
address: walletState?.address || null,
|
|
171
|
+
connect,
|
|
172
|
+
disconnect,
|
|
173
|
+
mining,
|
|
174
|
+
dashboard,
|
|
175
|
+
batch,
|
|
176
|
+
payments,
|
|
177
|
+
staking,
|
|
178
|
+
workers,
|
|
179
|
+
governance,
|
|
180
|
+
stwo,
|
|
181
|
+
tee
|
|
182
|
+
}),
|
|
183
|
+
[
|
|
184
|
+
client,
|
|
185
|
+
http,
|
|
186
|
+
contract,
|
|
187
|
+
contracts,
|
|
188
|
+
network,
|
|
189
|
+
walletState,
|
|
190
|
+
connect,
|
|
191
|
+
disconnect,
|
|
192
|
+
mining,
|
|
193
|
+
dashboard,
|
|
194
|
+
batch,
|
|
195
|
+
payments,
|
|
196
|
+
staking,
|
|
197
|
+
workers,
|
|
198
|
+
governance,
|
|
199
|
+
stwo,
|
|
200
|
+
tee
|
|
201
|
+
]
|
|
202
|
+
);
|
|
203
|
+
return /* @__PURE__ */ jsx(BitSageContext.Provider, { value, children });
|
|
204
|
+
}
|
|
205
|
+
function useBitSage() {
|
|
206
|
+
const context = useContext(BitSageContext);
|
|
207
|
+
if (context === void 0) {
|
|
208
|
+
throw new Error("useBitSage must be used within a BitSageProvider");
|
|
209
|
+
}
|
|
210
|
+
return context;
|
|
211
|
+
}
|
|
212
|
+
function useBitSageClient() {
|
|
213
|
+
const { client } = useBitSage();
|
|
214
|
+
return client;
|
|
215
|
+
}
|
|
216
|
+
function useWallet() {
|
|
217
|
+
const { isConnected, address, connect, disconnect } = useBitSage();
|
|
218
|
+
return { isConnected, address, connect, disconnect };
|
|
219
|
+
}
|
|
220
|
+
function useNetwork() {
|
|
221
|
+
const { network } = useBitSage();
|
|
222
|
+
return network;
|
|
223
|
+
}
|
|
224
|
+
function useContracts() {
|
|
225
|
+
const { contracts } = useBitSage();
|
|
226
|
+
return contracts;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/react/providers/PrivacyProvider.tsx
|
|
230
|
+
import {
|
|
231
|
+
createContext as createContext2,
|
|
232
|
+
useContext as useContext2,
|
|
233
|
+
useMemo as useMemo2,
|
|
234
|
+
useState as useState2,
|
|
235
|
+
useCallback as useCallback2,
|
|
236
|
+
useEffect as useEffect2
|
|
237
|
+
} from "react";
|
|
238
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
239
|
+
var PrivacyContext = createContext2(void 0);
|
|
240
|
+
function PrivacyProvider({
|
|
241
|
+
children,
|
|
242
|
+
autoLoad = false,
|
|
243
|
+
storagePrefix = "bitsage_privacy"
|
|
244
|
+
}) {
|
|
245
|
+
const { http, contract, contracts, address } = useBitSage();
|
|
246
|
+
const [keyPair, setKeyPair] = useState2(null);
|
|
247
|
+
const [isInitializing, setIsInitializing] = useState2(false);
|
|
248
|
+
const [error, setError] = useState2(null);
|
|
249
|
+
const [account, setAccount] = useState2(null);
|
|
250
|
+
const [balances, setBalances] = useState2(
|
|
251
|
+
/* @__PURE__ */ new Map()
|
|
252
|
+
);
|
|
253
|
+
const privacy = useMemo2(() => {
|
|
254
|
+
if (!keyPair) return null;
|
|
255
|
+
return new PrivacyClient(http, contract, {
|
|
256
|
+
privacyRouterAddress: contracts.privacyRouter,
|
|
257
|
+
mixingPoolAddress: contracts.privacyRouter
|
|
258
|
+
});
|
|
259
|
+
}, [http, contract, contracts, keyPair]);
|
|
260
|
+
const generateKeys = useCallback2(async () => {
|
|
261
|
+
setIsInitializing(true);
|
|
262
|
+
setError(null);
|
|
263
|
+
try {
|
|
264
|
+
const newKeyPair = await PrivacyKeyManager.generate();
|
|
265
|
+
setKeyPair(newKeyPair);
|
|
266
|
+
} catch (err) {
|
|
267
|
+
setError(err instanceof Error ? err.message : "Failed to generate keys");
|
|
268
|
+
throw err;
|
|
269
|
+
} finally {
|
|
270
|
+
setIsInitializing(false);
|
|
271
|
+
}
|
|
272
|
+
}, []);
|
|
273
|
+
const deriveKeysFromWallet = useCallback2(async (signature) => {
|
|
274
|
+
setIsInitializing(true);
|
|
275
|
+
setError(null);
|
|
276
|
+
try {
|
|
277
|
+
const derivedKeyPair = await PrivacyKeyManager.deriveFromSignature(signature);
|
|
278
|
+
setKeyPair(derivedKeyPair);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
setError(err instanceof Error ? err.message : "Failed to derive keys");
|
|
281
|
+
throw err;
|
|
282
|
+
} finally {
|
|
283
|
+
setIsInitializing(false);
|
|
284
|
+
}
|
|
285
|
+
}, []);
|
|
286
|
+
const loadKeys = useCallback2(
|
|
287
|
+
async (password) => {
|
|
288
|
+
setIsInitializing(true);
|
|
289
|
+
setError(null);
|
|
290
|
+
try {
|
|
291
|
+
const storageKey = address ? `${storagePrefix}_${address}` : storagePrefix;
|
|
292
|
+
const loadedKeyPair = await PrivacyKeyManager.retrieve(
|
|
293
|
+
password,
|
|
294
|
+
{ prefix: storageKey }
|
|
295
|
+
);
|
|
296
|
+
if (loadedKeyPair) {
|
|
297
|
+
setKeyPair(loadedKeyPair);
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
return false;
|
|
301
|
+
} catch (err) {
|
|
302
|
+
setError(err instanceof Error ? err.message : "Failed to load keys");
|
|
303
|
+
return false;
|
|
304
|
+
} finally {
|
|
305
|
+
setIsInitializing(false);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
[address, storagePrefix]
|
|
309
|
+
);
|
|
310
|
+
const storeKeys = useCallback2(
|
|
311
|
+
async (password) => {
|
|
312
|
+
if (!keyPair) {
|
|
313
|
+
throw new Error("No keys to store");
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const storageKey = address ? `${storagePrefix}_${address}` : storagePrefix;
|
|
317
|
+
await PrivacyKeyManager.store(keyPair, password, { prefix: storageKey });
|
|
318
|
+
} catch (err) {
|
|
319
|
+
setError(err instanceof Error ? err.message : "Failed to store keys");
|
|
320
|
+
throw err;
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
[keyPair, address, storagePrefix]
|
|
324
|
+
);
|
|
325
|
+
const clearKeys = useCallback2(() => {
|
|
326
|
+
setKeyPair(null);
|
|
327
|
+
setAccount(null);
|
|
328
|
+
setBalances(/* @__PURE__ */ new Map());
|
|
329
|
+
setError(null);
|
|
330
|
+
}, []);
|
|
331
|
+
const getDecryptedBalance = useCallback2(
|
|
332
|
+
async (asset = "SAGE") => {
|
|
333
|
+
if (!privacy || !keyPair || !address) return null;
|
|
334
|
+
try {
|
|
335
|
+
const multiBalances = await privacy.getPrivateBalances(address);
|
|
336
|
+
const encryptedBalance = multiBalances[asset];
|
|
337
|
+
if (!encryptedBalance) return null;
|
|
338
|
+
const decrypted = await privacy.revealBalance(
|
|
339
|
+
encryptedBalance,
|
|
340
|
+
keyPair.privateKey
|
|
341
|
+
);
|
|
342
|
+
return decrypted;
|
|
343
|
+
} catch (err) {
|
|
344
|
+
console.error("Failed to decrypt balance:", err);
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
[privacy, keyPair, address]
|
|
349
|
+
);
|
|
350
|
+
const refreshBalances = useCallback2(async () => {
|
|
351
|
+
if (!privacy || !address) return;
|
|
352
|
+
try {
|
|
353
|
+
const multiBalances = await privacy.getPrivateBalances(address);
|
|
354
|
+
const newBalances = /* @__PURE__ */ new Map();
|
|
355
|
+
const assets = ["SAGE", "USDC", "STRK", "WBTC", "ETH"];
|
|
356
|
+
for (const asset of assets) {
|
|
357
|
+
const balance = multiBalances[asset];
|
|
358
|
+
if (balance) {
|
|
359
|
+
newBalances.set(asset, balance);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
setBalances(newBalances);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
console.error("Failed to refresh balances:", err);
|
|
365
|
+
}
|
|
366
|
+
}, [privacy, address]);
|
|
367
|
+
useEffect2(() => {
|
|
368
|
+
if (!privacy || !address) {
|
|
369
|
+
setAccount(null);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
let cancelled = false;
|
|
373
|
+
async function loadAccount() {
|
|
374
|
+
try {
|
|
375
|
+
const accountInfo = await privacy.getAccount(address);
|
|
376
|
+
if (!cancelled) {
|
|
377
|
+
setAccount(accountInfo);
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.error("Failed to load private account:", err);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
loadAccount();
|
|
384
|
+
return () => {
|
|
385
|
+
cancelled = true;
|
|
386
|
+
};
|
|
387
|
+
}, [privacy, address]);
|
|
388
|
+
useEffect2(() => {
|
|
389
|
+
if (!autoLoad || !address) return;
|
|
390
|
+
const storageKey = `${storagePrefix}_${address}`;
|
|
391
|
+
const hasStoredKeys = typeof localStorage !== "undefined" && localStorage.getItem(storageKey) !== null;
|
|
392
|
+
if (hasStoredKeys) {
|
|
393
|
+
console.info("Privacy keys found in storage, call loadKeys() to decrypt");
|
|
394
|
+
}
|
|
395
|
+
}, [autoLoad, address, storagePrefix]);
|
|
396
|
+
const value = useMemo2(
|
|
397
|
+
() => ({
|
|
398
|
+
privacy,
|
|
399
|
+
hasKeys: keyPair !== null,
|
|
400
|
+
publicKey: keyPair?.publicKey || null,
|
|
401
|
+
isInitializing,
|
|
402
|
+
error,
|
|
403
|
+
generateKeys,
|
|
404
|
+
deriveKeysFromWallet,
|
|
405
|
+
loadKeys,
|
|
406
|
+
storeKeys,
|
|
407
|
+
clearKeys,
|
|
408
|
+
getDecryptedBalance,
|
|
409
|
+
refreshBalances,
|
|
410
|
+
account,
|
|
411
|
+
balances
|
|
412
|
+
}),
|
|
413
|
+
[
|
|
414
|
+
privacy,
|
|
415
|
+
keyPair,
|
|
416
|
+
isInitializing,
|
|
417
|
+
error,
|
|
418
|
+
generateKeys,
|
|
419
|
+
deriveKeysFromWallet,
|
|
420
|
+
loadKeys,
|
|
421
|
+
storeKeys,
|
|
422
|
+
clearKeys,
|
|
423
|
+
getDecryptedBalance,
|
|
424
|
+
refreshBalances,
|
|
425
|
+
account,
|
|
426
|
+
balances
|
|
427
|
+
]
|
|
428
|
+
);
|
|
429
|
+
return /* @__PURE__ */ jsx2(PrivacyContext.Provider, { value, children });
|
|
430
|
+
}
|
|
431
|
+
function usePrivacy() {
|
|
432
|
+
const context = useContext2(PrivacyContext);
|
|
433
|
+
if (context === void 0) {
|
|
434
|
+
throw new Error("usePrivacy must be used within a PrivacyProvider");
|
|
435
|
+
}
|
|
436
|
+
return context;
|
|
437
|
+
}
|
|
438
|
+
function usePrivacyKeys() {
|
|
439
|
+
const {
|
|
440
|
+
hasKeys,
|
|
441
|
+
publicKey,
|
|
442
|
+
generateKeys,
|
|
443
|
+
deriveKeysFromWallet,
|
|
444
|
+
clearKeys
|
|
445
|
+
} = usePrivacy();
|
|
446
|
+
return {
|
|
447
|
+
hasKeys,
|
|
448
|
+
publicKey,
|
|
449
|
+
generateKeys,
|
|
450
|
+
deriveKeysFromWallet,
|
|
451
|
+
clearKeys
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function usePrivacyClient() {
|
|
455
|
+
const { privacy } = usePrivacy();
|
|
456
|
+
return privacy;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/react/providers/WebSocketProvider.tsx
|
|
460
|
+
import {
|
|
461
|
+
createContext as createContext3,
|
|
462
|
+
useContext as useContext3,
|
|
463
|
+
useMemo as useMemo3,
|
|
464
|
+
useState as useState3,
|
|
465
|
+
useCallback as useCallback3,
|
|
466
|
+
useEffect as useEffect3,
|
|
467
|
+
useRef
|
|
468
|
+
} from "react";
|
|
469
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
470
|
+
var WebSocketContext = createContext3(void 0);
|
|
471
|
+
function getDefaultWsUrl(network) {
|
|
472
|
+
switch (network) {
|
|
473
|
+
case "mainnet":
|
|
474
|
+
return "wss://ws.bitsage.network";
|
|
475
|
+
case "sepolia":
|
|
476
|
+
return "wss://ws.sepolia.bitsage.network";
|
|
477
|
+
case "local":
|
|
478
|
+
return "ws://localhost:8081";
|
|
479
|
+
default:
|
|
480
|
+
return "wss://ws.sepolia.bitsage.network";
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function WebSocketProvider({
|
|
484
|
+
children,
|
|
485
|
+
url,
|
|
486
|
+
autoConnect = true,
|
|
487
|
+
config
|
|
488
|
+
}) {
|
|
489
|
+
const { network } = useBitSage();
|
|
490
|
+
const [connectionState, setConnectionState] = useState3("disconnected");
|
|
491
|
+
const [error, setError] = useState3(null);
|
|
492
|
+
const [reconnectAttempts, setReconnectAttempts] = useState3(0);
|
|
493
|
+
const [latestNetworkStats, setLatestNetworkStats] = useState3(null);
|
|
494
|
+
const wsRef = useRef(null);
|
|
495
|
+
const unsubscribersRef = useRef(/* @__PURE__ */ new Map());
|
|
496
|
+
const wsUrl = url || getDefaultWsUrl(network);
|
|
497
|
+
useEffect3(() => {
|
|
498
|
+
const ws = new WebSocketClient({
|
|
499
|
+
url: wsUrl,
|
|
500
|
+
reconnect: true,
|
|
501
|
+
reconnectIntervalMs: 1e3,
|
|
502
|
+
maxReconnectAttempts: 10,
|
|
503
|
+
heartbeatIntervalMs: 3e4,
|
|
504
|
+
...config
|
|
505
|
+
});
|
|
506
|
+
wsRef.current = ws;
|
|
507
|
+
ws.onReconnect(() => {
|
|
508
|
+
setReconnectAttempts((prev) => prev + 1);
|
|
509
|
+
});
|
|
510
|
+
ws.onError((err) => {
|
|
511
|
+
setError(err.message);
|
|
512
|
+
setConnectionState("error");
|
|
513
|
+
});
|
|
514
|
+
return () => {
|
|
515
|
+
unsubscribersRef.current.forEach((unsubscribe) => unsubscribe());
|
|
516
|
+
unsubscribersRef.current.clear();
|
|
517
|
+
ws.disconnect();
|
|
518
|
+
wsRef.current = null;
|
|
519
|
+
};
|
|
520
|
+
}, [wsUrl, config]);
|
|
521
|
+
const connect = useCallback3(async () => {
|
|
522
|
+
const ws = wsRef.current;
|
|
523
|
+
if (!ws) return;
|
|
524
|
+
setConnectionState("connecting");
|
|
525
|
+
setError(null);
|
|
526
|
+
try {
|
|
527
|
+
await ws.connect();
|
|
528
|
+
setConnectionState("connected");
|
|
529
|
+
setReconnectAttempts(0);
|
|
530
|
+
} catch (err) {
|
|
531
|
+
setError(err instanceof Error ? err.message : "Connection failed");
|
|
532
|
+
setConnectionState("error");
|
|
533
|
+
throw err;
|
|
534
|
+
}
|
|
535
|
+
}, []);
|
|
536
|
+
const disconnect = useCallback3(() => {
|
|
537
|
+
const ws = wsRef.current;
|
|
538
|
+
if (!ws) return;
|
|
539
|
+
ws.disconnect();
|
|
540
|
+
setConnectionState("disconnected");
|
|
541
|
+
setError(null);
|
|
542
|
+
}, []);
|
|
543
|
+
const reconnect = useCallback3(async () => {
|
|
544
|
+
disconnect();
|
|
545
|
+
await connect();
|
|
546
|
+
}, [connect, disconnect]);
|
|
547
|
+
const subscribeToJob = useCallback3(
|
|
548
|
+
(jobId, callback) => {
|
|
549
|
+
const ws = wsRef.current;
|
|
550
|
+
if (!ws) {
|
|
551
|
+
console.warn("WebSocket not initialized");
|
|
552
|
+
return () => {
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const unsubscribe = ws.subscribeToJobUpdates(jobId, callback);
|
|
556
|
+
const key = `job_${jobId}`;
|
|
557
|
+
unsubscribersRef.current.set(key, unsubscribe);
|
|
558
|
+
return () => {
|
|
559
|
+
unsubscribe();
|
|
560
|
+
unsubscribersRef.current.delete(key);
|
|
561
|
+
};
|
|
562
|
+
},
|
|
563
|
+
[]
|
|
564
|
+
);
|
|
565
|
+
const subscribeToWorker = useCallback3(
|
|
566
|
+
(workerId, callback) => {
|
|
567
|
+
const ws = wsRef.current;
|
|
568
|
+
if (!ws) {
|
|
569
|
+
console.warn("WebSocket not initialized");
|
|
570
|
+
return () => {
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
const unsubscribe = ws.subscribeToWorkerUpdates(workerId, callback);
|
|
574
|
+
const key = `worker_${workerId}`;
|
|
575
|
+
unsubscribersRef.current.set(key, unsubscribe);
|
|
576
|
+
return () => {
|
|
577
|
+
unsubscribe();
|
|
578
|
+
unsubscribersRef.current.delete(key);
|
|
579
|
+
};
|
|
580
|
+
},
|
|
581
|
+
[]
|
|
582
|
+
);
|
|
583
|
+
const subscribeToNetworkStats = useCallback3(
|
|
584
|
+
(callback) => {
|
|
585
|
+
const ws = wsRef.current;
|
|
586
|
+
if (!ws) {
|
|
587
|
+
console.warn("WebSocket not initialized");
|
|
588
|
+
return () => {
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
const wrappedCallback = (event) => {
|
|
592
|
+
setLatestNetworkStats(event);
|
|
593
|
+
callback(event);
|
|
594
|
+
};
|
|
595
|
+
const unsubscribe = ws.subscribeToNetworkStats(wrappedCallback);
|
|
596
|
+
const key = "network_stats";
|
|
597
|
+
unsubscribersRef.current.set(key, unsubscribe);
|
|
598
|
+
return () => {
|
|
599
|
+
unsubscribe();
|
|
600
|
+
unsubscribersRef.current.delete(key);
|
|
601
|
+
};
|
|
602
|
+
},
|
|
603
|
+
[]
|
|
604
|
+
);
|
|
605
|
+
const subscribeToProofVerified = useCallback3(
|
|
606
|
+
(proofHash, callback) => {
|
|
607
|
+
const ws = wsRef.current;
|
|
608
|
+
if (!ws) {
|
|
609
|
+
console.warn("WebSocket not initialized");
|
|
610
|
+
return () => {
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
const unsubscribe = ws.subscribeToProofVerified(proofHash, callback);
|
|
614
|
+
const key = `proof_${proofHash}`;
|
|
615
|
+
unsubscribersRef.current.set(key, unsubscribe);
|
|
616
|
+
return () => {
|
|
617
|
+
unsubscribe();
|
|
618
|
+
unsubscribersRef.current.delete(key);
|
|
619
|
+
};
|
|
620
|
+
},
|
|
621
|
+
[]
|
|
622
|
+
);
|
|
623
|
+
useEffect3(() => {
|
|
624
|
+
if (autoConnect && wsRef.current) {
|
|
625
|
+
connect().catch((err) => {
|
|
626
|
+
console.error("Auto-connect failed:", err);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}, [autoConnect, connect]);
|
|
630
|
+
const value = useMemo3(
|
|
631
|
+
() => ({
|
|
632
|
+
ws: wsRef.current,
|
|
633
|
+
connectionState,
|
|
634
|
+
isConnected: connectionState === "connected",
|
|
635
|
+
error,
|
|
636
|
+
reconnectAttempts,
|
|
637
|
+
connect,
|
|
638
|
+
disconnect,
|
|
639
|
+
reconnect,
|
|
640
|
+
subscribeToJob,
|
|
641
|
+
subscribeToWorker,
|
|
642
|
+
subscribeToNetworkStats,
|
|
643
|
+
subscribeToProofVerified,
|
|
644
|
+
latestNetworkStats
|
|
645
|
+
}),
|
|
646
|
+
[
|
|
647
|
+
connectionState,
|
|
648
|
+
error,
|
|
649
|
+
reconnectAttempts,
|
|
650
|
+
connect,
|
|
651
|
+
disconnect,
|
|
652
|
+
reconnect,
|
|
653
|
+
subscribeToJob,
|
|
654
|
+
subscribeToWorker,
|
|
655
|
+
subscribeToNetworkStats,
|
|
656
|
+
subscribeToProofVerified,
|
|
657
|
+
latestNetworkStats
|
|
658
|
+
]
|
|
659
|
+
);
|
|
660
|
+
return /* @__PURE__ */ jsx3(WebSocketContext.Provider, { value, children });
|
|
661
|
+
}
|
|
662
|
+
function useWebSocket() {
|
|
663
|
+
const context = useContext3(WebSocketContext);
|
|
664
|
+
if (context === void 0) {
|
|
665
|
+
throw new Error("useWebSocket must be used within a WebSocketProvider");
|
|
666
|
+
}
|
|
667
|
+
return context;
|
|
668
|
+
}
|
|
669
|
+
function useWebSocketConnection() {
|
|
670
|
+
const {
|
|
671
|
+
connectionState,
|
|
672
|
+
isConnected,
|
|
673
|
+
error,
|
|
674
|
+
connect,
|
|
675
|
+
disconnect,
|
|
676
|
+
reconnect
|
|
677
|
+
} = useWebSocket();
|
|
678
|
+
return {
|
|
679
|
+
connectionState,
|
|
680
|
+
isConnected,
|
|
681
|
+
error,
|
|
682
|
+
connect,
|
|
683
|
+
disconnect,
|
|
684
|
+
reconnect
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/react/hooks/useJobs.ts
|
|
689
|
+
import { useState as useState4, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
690
|
+
function useSubmitJob() {
|
|
691
|
+
const { client } = useBitSage();
|
|
692
|
+
const [state, setState] = useState4({
|
|
693
|
+
data: null,
|
|
694
|
+
isLoading: false,
|
|
695
|
+
error: null
|
|
696
|
+
});
|
|
697
|
+
const submit = useCallback4(
|
|
698
|
+
async (request) => {
|
|
699
|
+
setState({ data: null, isLoading: true, error: null });
|
|
700
|
+
try {
|
|
701
|
+
const response = await client.submitJob(request);
|
|
702
|
+
setState({ data: response, isLoading: false, error: null });
|
|
703
|
+
return response;
|
|
704
|
+
} catch (err) {
|
|
705
|
+
const error = err instanceof Error ? err : new Error("Failed to submit job");
|
|
706
|
+
setState({ data: null, isLoading: false, error });
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
[client]
|
|
711
|
+
);
|
|
712
|
+
const reset = useCallback4(() => {
|
|
713
|
+
setState({ data: null, isLoading: false, error: null });
|
|
714
|
+
}, []);
|
|
715
|
+
return { ...state, submit, reset };
|
|
716
|
+
}
|
|
717
|
+
function useJobStatus(jobId, options) {
|
|
718
|
+
const { client } = useBitSage();
|
|
719
|
+
const [state, setState] = useState4({
|
|
720
|
+
data: null,
|
|
721
|
+
isLoading: false,
|
|
722
|
+
error: null,
|
|
723
|
+
refetch: async () => {
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
const { pollingInterval = 0, enabled = true } = options || {};
|
|
727
|
+
const intervalRef = useRef2(null);
|
|
728
|
+
const fetch = useCallback4(async () => {
|
|
729
|
+
if (!jobId) return;
|
|
730
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
731
|
+
try {
|
|
732
|
+
const response = await client.getJobStatus(jobId);
|
|
733
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
734
|
+
} catch (err) {
|
|
735
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch job status");
|
|
736
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
737
|
+
}
|
|
738
|
+
}, [client, jobId]);
|
|
739
|
+
useEffect4(() => {
|
|
740
|
+
if (enabled && jobId) {
|
|
741
|
+
fetch();
|
|
742
|
+
}
|
|
743
|
+
}, [enabled, jobId, fetch]);
|
|
744
|
+
useEffect4(() => {
|
|
745
|
+
if (pollingInterval > 0 && enabled && jobId) {
|
|
746
|
+
intervalRef.current = setInterval(fetch, pollingInterval);
|
|
747
|
+
}
|
|
748
|
+
return () => {
|
|
749
|
+
if (intervalRef.current) {
|
|
750
|
+
clearInterval(intervalRef.current);
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}, [pollingInterval, enabled, jobId, fetch]);
|
|
754
|
+
return { ...state, refetch: fetch };
|
|
755
|
+
}
|
|
756
|
+
function useJobs(params) {
|
|
757
|
+
const { client } = useBitSage();
|
|
758
|
+
const [state, setState] = useState4({
|
|
759
|
+
data: null,
|
|
760
|
+
isLoading: false,
|
|
761
|
+
error: null,
|
|
762
|
+
refetch: async () => {
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
const fetch = useCallback4(async () => {
|
|
766
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
767
|
+
try {
|
|
768
|
+
const response = await client.listJobs(params);
|
|
769
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
770
|
+
} catch (err) {
|
|
771
|
+
const error = err instanceof Error ? err : new Error("Failed to list jobs");
|
|
772
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
773
|
+
}
|
|
774
|
+
}, [client, params]);
|
|
775
|
+
useEffect4(() => {
|
|
776
|
+
fetch();
|
|
777
|
+
}, [fetch]);
|
|
778
|
+
return { ...state, refetch: fetch };
|
|
779
|
+
}
|
|
780
|
+
function useWaitForJob(jobId, options) {
|
|
781
|
+
const { client } = useBitSage();
|
|
782
|
+
const [result, setResult] = useState4(null);
|
|
783
|
+
const [status, setStatus] = useState4(null);
|
|
784
|
+
const [isWaiting, setIsWaiting] = useState4(false);
|
|
785
|
+
const [error, setError] = useState4(null);
|
|
786
|
+
const { pollInterval = 2e3, timeout = 3e5, autoStart = false } = options || {};
|
|
787
|
+
const abortRef = useRef2(null);
|
|
788
|
+
const start = useCallback4(() => {
|
|
789
|
+
if (!jobId || isWaiting) return;
|
|
790
|
+
setIsWaiting(true);
|
|
791
|
+
setError(null);
|
|
792
|
+
setResult(null);
|
|
793
|
+
abortRef.current = new AbortController();
|
|
794
|
+
const poll = async () => {
|
|
795
|
+
const startTime = Date.now();
|
|
796
|
+
while (!abortRef.current?.signal.aborted) {
|
|
797
|
+
try {
|
|
798
|
+
const jobStatus = await client.getJobStatus(jobId);
|
|
799
|
+
setStatus(jobStatus.status);
|
|
800
|
+
if (jobStatus.status === "completed") {
|
|
801
|
+
const jobResult = await client.getJobResult(jobId);
|
|
802
|
+
setResult(jobResult);
|
|
803
|
+
setIsWaiting(false);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (jobStatus.status === "failed") {
|
|
807
|
+
throw new Error("Job failed");
|
|
808
|
+
}
|
|
809
|
+
if (jobStatus.status === "cancelled") {
|
|
810
|
+
throw new Error("Job was cancelled");
|
|
811
|
+
}
|
|
812
|
+
if (Date.now() - startTime > timeout) {
|
|
813
|
+
throw new Error("Timeout waiting for job completion");
|
|
814
|
+
}
|
|
815
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
816
|
+
} catch (err) {
|
|
817
|
+
if (!abortRef.current?.signal.aborted) {
|
|
818
|
+
setError(err instanceof Error ? err : new Error("Wait failed"));
|
|
819
|
+
setIsWaiting(false);
|
|
820
|
+
}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
poll();
|
|
826
|
+
}, [client, jobId, isWaiting, pollInterval, timeout]);
|
|
827
|
+
const cancel = useCallback4(() => {
|
|
828
|
+
if (abortRef.current) {
|
|
829
|
+
abortRef.current.abort();
|
|
830
|
+
abortRef.current = null;
|
|
831
|
+
}
|
|
832
|
+
setIsWaiting(false);
|
|
833
|
+
}, []);
|
|
834
|
+
useEffect4(() => {
|
|
835
|
+
return () => {
|
|
836
|
+
if (abortRef.current) {
|
|
837
|
+
abortRef.current.abort();
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
}, []);
|
|
841
|
+
useEffect4(() => {
|
|
842
|
+
if (autoStart && jobId) {
|
|
843
|
+
start();
|
|
844
|
+
}
|
|
845
|
+
}, [autoStart, jobId, start]);
|
|
846
|
+
return { result, status, isWaiting, error, start, cancel };
|
|
847
|
+
}
|
|
848
|
+
function useCancelJob() {
|
|
849
|
+
const { client } = useBitSage();
|
|
850
|
+
const [state, setState] = useState4({
|
|
851
|
+
data: null,
|
|
852
|
+
isLoading: false,
|
|
853
|
+
error: null
|
|
854
|
+
});
|
|
855
|
+
const cancel = useCallback4(
|
|
856
|
+
async (jobId) => {
|
|
857
|
+
setState({ data: null, isLoading: true, error: null });
|
|
858
|
+
try {
|
|
859
|
+
await client.cancelJob(jobId);
|
|
860
|
+
setState({ data: void 0, isLoading: false, error: null });
|
|
861
|
+
} catch (err) {
|
|
862
|
+
const error = err instanceof Error ? err : new Error("Failed to cancel job");
|
|
863
|
+
setState({ data: null, isLoading: false, error });
|
|
864
|
+
throw error;
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
[client]
|
|
868
|
+
);
|
|
869
|
+
return { ...state, cancel };
|
|
870
|
+
}
|
|
871
|
+
function useJobResult(jobId) {
|
|
872
|
+
const { client } = useBitSage();
|
|
873
|
+
const [state, setState] = useState4({
|
|
874
|
+
data: null,
|
|
875
|
+
isLoading: false,
|
|
876
|
+
error: null,
|
|
877
|
+
refetch: async () => {
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
const fetch = useCallback4(async () => {
|
|
881
|
+
if (!jobId) return;
|
|
882
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
883
|
+
try {
|
|
884
|
+
const response = await client.getJobResult(jobId);
|
|
885
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
886
|
+
} catch (err) {
|
|
887
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch job result");
|
|
888
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
889
|
+
}
|
|
890
|
+
}, [client, jobId]);
|
|
891
|
+
useEffect4(() => {
|
|
892
|
+
if (jobId) {
|
|
893
|
+
fetch();
|
|
894
|
+
}
|
|
895
|
+
}, [jobId, fetch]);
|
|
896
|
+
return { ...state, refetch: fetch };
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/react/hooks/useMining.ts
|
|
900
|
+
import { useState as useState5, useCallback as useCallback5, useEffect as useEffect5 } from "react";
|
|
901
|
+
function useMiningRewardEstimate(gpuTier) {
|
|
902
|
+
const { mining } = useBitSage();
|
|
903
|
+
const [state, setState] = useState5({
|
|
904
|
+
data: null,
|
|
905
|
+
isLoading: false,
|
|
906
|
+
error: null,
|
|
907
|
+
refetch: async () => {
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
const fetch = useCallback5(async () => {
|
|
911
|
+
if (!gpuTier) return;
|
|
912
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
913
|
+
try {
|
|
914
|
+
const response = await mining.getMiningRewardEstimate(gpuTier);
|
|
915
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
916
|
+
} catch (err) {
|
|
917
|
+
const error = err instanceof Error ? err : new Error("Failed to estimate rewards");
|
|
918
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
919
|
+
}
|
|
920
|
+
}, [mining, gpuTier]);
|
|
921
|
+
useEffect5(() => {
|
|
922
|
+
if (gpuTier) {
|
|
923
|
+
fetch();
|
|
924
|
+
}
|
|
925
|
+
}, [gpuTier, fetch]);
|
|
926
|
+
return { ...state, refetch: fetch };
|
|
927
|
+
}
|
|
928
|
+
function useWorkerMiningStats(workerId) {
|
|
929
|
+
const { mining } = useBitSage();
|
|
930
|
+
const [state, setState] = useState5({
|
|
931
|
+
data: null,
|
|
932
|
+
isLoading: false,
|
|
933
|
+
error: null,
|
|
934
|
+
refetch: async () => {
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
const fetch = useCallback5(async () => {
|
|
938
|
+
if (!workerId) return;
|
|
939
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
940
|
+
try {
|
|
941
|
+
const response = await mining.getWorkerMiningStats(workerId);
|
|
942
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
943
|
+
} catch (err) {
|
|
944
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch worker stats");
|
|
945
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
946
|
+
}
|
|
947
|
+
}, [mining, workerId]);
|
|
948
|
+
useEffect5(() => {
|
|
949
|
+
if (workerId) {
|
|
950
|
+
fetch();
|
|
951
|
+
}
|
|
952
|
+
}, [workerId, fetch]);
|
|
953
|
+
return { ...state, refetch: fetch };
|
|
954
|
+
}
|
|
955
|
+
function useMiningPoolStatus() {
|
|
956
|
+
const { mining } = useBitSage();
|
|
957
|
+
const [state, setState] = useState5({
|
|
958
|
+
data: null,
|
|
959
|
+
isLoading: false,
|
|
960
|
+
error: null,
|
|
961
|
+
refetch: async () => {
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
const fetch = useCallback5(async () => {
|
|
965
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
966
|
+
try {
|
|
967
|
+
const response = await mining.getMiningPoolStatus();
|
|
968
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
969
|
+
} catch (err) {
|
|
970
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch pool status");
|
|
971
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
972
|
+
}
|
|
973
|
+
}, [mining]);
|
|
974
|
+
useEffect5(() => {
|
|
975
|
+
fetch();
|
|
976
|
+
}, [fetch]);
|
|
977
|
+
return { ...state, refetch: fetch };
|
|
978
|
+
}
|
|
979
|
+
function useDailyCap(stakeTier) {
|
|
980
|
+
const { mining } = useBitSage();
|
|
981
|
+
const [state, setState] = useState5({
|
|
982
|
+
data: null,
|
|
983
|
+
isLoading: false,
|
|
984
|
+
error: null,
|
|
985
|
+
refetch: async () => {
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
const fetch = useCallback5(async () => {
|
|
989
|
+
if (!stakeTier) return;
|
|
990
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
991
|
+
try {
|
|
992
|
+
const response = await mining.getDailyCap(stakeTier);
|
|
993
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
994
|
+
} catch (err) {
|
|
995
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch daily cap");
|
|
996
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
997
|
+
}
|
|
998
|
+
}, [mining, stakeTier]);
|
|
999
|
+
useEffect5(() => {
|
|
1000
|
+
if (stakeTier) {
|
|
1001
|
+
fetch();
|
|
1002
|
+
}
|
|
1003
|
+
}, [stakeTier, fetch]);
|
|
1004
|
+
return { ...state, refetch: fetch };
|
|
1005
|
+
}
|
|
1006
|
+
function useCurrentBaseReward() {
|
|
1007
|
+
const { mining } = useBitSage();
|
|
1008
|
+
const [state, setState] = useState5({
|
|
1009
|
+
data: null,
|
|
1010
|
+
isLoading: false,
|
|
1011
|
+
error: null,
|
|
1012
|
+
refetch: async () => {
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
const fetch = useCallback5(async () => {
|
|
1016
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1017
|
+
try {
|
|
1018
|
+
const response = await mining.getCurrentBaseReward();
|
|
1019
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch base reward");
|
|
1022
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1023
|
+
}
|
|
1024
|
+
}, [mining]);
|
|
1025
|
+
useEffect5(() => {
|
|
1026
|
+
fetch();
|
|
1027
|
+
}, [fetch]);
|
|
1028
|
+
return { ...state, refetch: fetch };
|
|
1029
|
+
}
|
|
1030
|
+
function useGpuMultiplier(gpuTier) {
|
|
1031
|
+
const { mining } = useBitSage();
|
|
1032
|
+
const [state, setState] = useState5({
|
|
1033
|
+
data: null,
|
|
1034
|
+
isLoading: false,
|
|
1035
|
+
error: null,
|
|
1036
|
+
refetch: async () => {
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
const fetch = useCallback5(async () => {
|
|
1040
|
+
if (!gpuTier) return;
|
|
1041
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1042
|
+
try {
|
|
1043
|
+
const response = await mining.getGPUMultiplier(gpuTier);
|
|
1044
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1045
|
+
} catch (err) {
|
|
1046
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch GPU multiplier");
|
|
1047
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1048
|
+
}
|
|
1049
|
+
}, [mining, gpuTier]);
|
|
1050
|
+
useEffect5(() => {
|
|
1051
|
+
if (gpuTier) {
|
|
1052
|
+
fetch();
|
|
1053
|
+
}
|
|
1054
|
+
}, [gpuTier, fetch]);
|
|
1055
|
+
return { ...state, refetch: fetch };
|
|
1056
|
+
}
|
|
1057
|
+
function useRemainingDailyCap(workerAddress) {
|
|
1058
|
+
const { mining } = useBitSage();
|
|
1059
|
+
const [state, setState] = useState5({
|
|
1060
|
+
data: null,
|
|
1061
|
+
isLoading: false,
|
|
1062
|
+
error: null,
|
|
1063
|
+
refetch: async () => {
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
const fetch = useCallback5(async () => {
|
|
1067
|
+
if (!workerAddress) return;
|
|
1068
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1069
|
+
try {
|
|
1070
|
+
const response = await mining.getRemainingDailyCap(workerAddress);
|
|
1071
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1072
|
+
} catch (err) {
|
|
1073
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch remaining cap");
|
|
1074
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1075
|
+
}
|
|
1076
|
+
}, [mining, workerAddress]);
|
|
1077
|
+
useEffect5(() => {
|
|
1078
|
+
if (workerAddress) {
|
|
1079
|
+
fetch();
|
|
1080
|
+
}
|
|
1081
|
+
}, [workerAddress, fetch]);
|
|
1082
|
+
return { ...state, refetch: fetch };
|
|
1083
|
+
}
|
|
1084
|
+
function useMiningOverview(workerId) {
|
|
1085
|
+
const pool = useMiningPoolStatus();
|
|
1086
|
+
const worker = useWorkerMiningStats(workerId);
|
|
1087
|
+
const base = useCurrentBaseReward();
|
|
1088
|
+
const isLoading = pool.isLoading || worker.isLoading || base.isLoading;
|
|
1089
|
+
const error = pool.error || worker.error || base.error;
|
|
1090
|
+
const refetch = useCallback5(async () => {
|
|
1091
|
+
await Promise.all([pool.refetch(), worker.refetch(), base.refetch()]);
|
|
1092
|
+
}, [pool.refetch, worker.refetch, base.refetch]);
|
|
1093
|
+
return {
|
|
1094
|
+
poolStatus: pool.data,
|
|
1095
|
+
workerStats: worker.data,
|
|
1096
|
+
baseReward: base.data,
|
|
1097
|
+
isLoading,
|
|
1098
|
+
error,
|
|
1099
|
+
refetch
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// src/react/hooks/useStaking.ts
|
|
1104
|
+
import { useState as useState6, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
1105
|
+
function useStakeInfo(address) {
|
|
1106
|
+
const { staking } = useBitSage();
|
|
1107
|
+
const [state, setState] = useState6({
|
|
1108
|
+
data: null,
|
|
1109
|
+
isLoading: false,
|
|
1110
|
+
error: null,
|
|
1111
|
+
refetch: async () => {
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
const fetch = useCallback6(async () => {
|
|
1115
|
+
if (!address) return;
|
|
1116
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1117
|
+
try {
|
|
1118
|
+
const response = await staking.getStakeInfo(address);
|
|
1119
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch stake info");
|
|
1122
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1123
|
+
}
|
|
1124
|
+
}, [staking, address]);
|
|
1125
|
+
useEffect6(() => {
|
|
1126
|
+
if (address) {
|
|
1127
|
+
fetch();
|
|
1128
|
+
}
|
|
1129
|
+
}, [address, fetch]);
|
|
1130
|
+
return { ...state, refetch: fetch };
|
|
1131
|
+
}
|
|
1132
|
+
function useStake() {
|
|
1133
|
+
const { staking } = useBitSage();
|
|
1134
|
+
const [state, setState] = useState6({
|
|
1135
|
+
data: null,
|
|
1136
|
+
isLoading: false,
|
|
1137
|
+
error: null
|
|
1138
|
+
});
|
|
1139
|
+
const stake = useCallback6(
|
|
1140
|
+
async (amount, gpuTier) => {
|
|
1141
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1142
|
+
try {
|
|
1143
|
+
const response = await staking.stake(amount, gpuTier);
|
|
1144
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1145
|
+
return response;
|
|
1146
|
+
} catch (err) {
|
|
1147
|
+
const error = err instanceof Error ? err : new Error("Failed to stake");
|
|
1148
|
+
setState({ data: null, isLoading: false, error });
|
|
1149
|
+
throw error;
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
[staking]
|
|
1153
|
+
);
|
|
1154
|
+
const stakeWithTee = useCallback6(
|
|
1155
|
+
async (amount, gpuTier, hasTee) => {
|
|
1156
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1157
|
+
try {
|
|
1158
|
+
const response = await staking.stakeWithTEE(amount, gpuTier, hasTee);
|
|
1159
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1160
|
+
return response;
|
|
1161
|
+
} catch (err) {
|
|
1162
|
+
const error = err instanceof Error ? err : new Error("Failed to stake with TEE");
|
|
1163
|
+
setState({ data: null, isLoading: false, error });
|
|
1164
|
+
throw error;
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
[staking]
|
|
1168
|
+
);
|
|
1169
|
+
return { ...state, stake, stakeWithTee };
|
|
1170
|
+
}
|
|
1171
|
+
function useUnstake() {
|
|
1172
|
+
const { staking } = useBitSage();
|
|
1173
|
+
const [state, setState] = useState6({
|
|
1174
|
+
data: null,
|
|
1175
|
+
isLoading: false,
|
|
1176
|
+
error: null
|
|
1177
|
+
});
|
|
1178
|
+
const unstake = useCallback6(
|
|
1179
|
+
async (amount) => {
|
|
1180
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1181
|
+
try {
|
|
1182
|
+
const response = await staking.requestUnstake(amount);
|
|
1183
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1184
|
+
return response;
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
const error = err instanceof Error ? err : new Error("Failed to unstake");
|
|
1187
|
+
setState({ data: null, isLoading: false, error });
|
|
1188
|
+
throw error;
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
[staking]
|
|
1192
|
+
);
|
|
1193
|
+
return { ...state, unstake };
|
|
1194
|
+
}
|
|
1195
|
+
function useClaimRewards() {
|
|
1196
|
+
const { staking } = useBitSage();
|
|
1197
|
+
const [state, setState] = useState6({
|
|
1198
|
+
data: null,
|
|
1199
|
+
isLoading: false,
|
|
1200
|
+
error: null
|
|
1201
|
+
});
|
|
1202
|
+
const claim = useCallback6(async () => {
|
|
1203
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1204
|
+
try {
|
|
1205
|
+
const response = await staking.claimRewards();
|
|
1206
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1207
|
+
return response;
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
const error = err instanceof Error ? err : new Error("Failed to claim rewards");
|
|
1210
|
+
setState({ data: null, isLoading: false, error });
|
|
1211
|
+
throw error;
|
|
1212
|
+
}
|
|
1213
|
+
}, [staking]);
|
|
1214
|
+
return { ...state, claim };
|
|
1215
|
+
}
|
|
1216
|
+
function useStakingConfig() {
|
|
1217
|
+
const { staking } = useBitSage();
|
|
1218
|
+
const [state, setState] = useState6({
|
|
1219
|
+
data: null,
|
|
1220
|
+
isLoading: false,
|
|
1221
|
+
error: null,
|
|
1222
|
+
refetch: async () => {
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
const fetch = useCallback6(async () => {
|
|
1226
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1227
|
+
try {
|
|
1228
|
+
const response = await staking.getStakingConfig();
|
|
1229
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch config");
|
|
1232
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1233
|
+
}
|
|
1234
|
+
}, [staking]);
|
|
1235
|
+
useEffect6(() => {
|
|
1236
|
+
fetch();
|
|
1237
|
+
}, [fetch]);
|
|
1238
|
+
return { ...state, refetch: fetch };
|
|
1239
|
+
}
|
|
1240
|
+
function useWorkerTier(workerAddress) {
|
|
1241
|
+
const { staking } = useBitSage();
|
|
1242
|
+
const [state, setState] = useState6({
|
|
1243
|
+
data: null,
|
|
1244
|
+
isLoading: false,
|
|
1245
|
+
error: null,
|
|
1246
|
+
refetch: async () => {
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
const fetch = useCallback6(async () => {
|
|
1250
|
+
if (!workerAddress) return;
|
|
1251
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1252
|
+
try {
|
|
1253
|
+
const response = await staking.getWorkerTier(workerAddress);
|
|
1254
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch worker tier");
|
|
1257
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1258
|
+
}
|
|
1259
|
+
}, [staking, workerAddress]);
|
|
1260
|
+
useEffect6(() => {
|
|
1261
|
+
if (workerAddress) {
|
|
1262
|
+
fetch();
|
|
1263
|
+
}
|
|
1264
|
+
}, [workerAddress, fetch]);
|
|
1265
|
+
return { ...state, refetch: fetch };
|
|
1266
|
+
}
|
|
1267
|
+
function useWorkerTierBenefits(tier) {
|
|
1268
|
+
const { staking } = useBitSage();
|
|
1269
|
+
const [state, setState] = useState6({
|
|
1270
|
+
data: null,
|
|
1271
|
+
isLoading: false,
|
|
1272
|
+
error: null,
|
|
1273
|
+
refetch: async () => {
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
const fetch = useCallback6(async () => {
|
|
1277
|
+
if (!tier) return;
|
|
1278
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1279
|
+
try {
|
|
1280
|
+
const response = await staking.getWorkerTierBenefits(tier);
|
|
1281
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1282
|
+
} catch (err) {
|
|
1283
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch tier benefits");
|
|
1284
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1285
|
+
}
|
|
1286
|
+
}, [staking, tier]);
|
|
1287
|
+
useEffect6(() => {
|
|
1288
|
+
if (tier) {
|
|
1289
|
+
fetch();
|
|
1290
|
+
}
|
|
1291
|
+
}, [tier, fetch]);
|
|
1292
|
+
return { ...state, refetch: fetch };
|
|
1293
|
+
}
|
|
1294
|
+
function useMinStake(gpuTier, hasTee = false, reputation = 50) {
|
|
1295
|
+
const { staking } = useBitSage();
|
|
1296
|
+
const [state, setState] = useState6({
|
|
1297
|
+
data: null,
|
|
1298
|
+
isLoading: false,
|
|
1299
|
+
error: null,
|
|
1300
|
+
refetch: async () => {
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
const fetch = useCallback6(async () => {
|
|
1304
|
+
if (!gpuTier) return;
|
|
1305
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1306
|
+
try {
|
|
1307
|
+
const response = await staking.getMinStake(gpuTier, hasTee, reputation);
|
|
1308
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1309
|
+
} catch (err) {
|
|
1310
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch min stake");
|
|
1311
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1312
|
+
}
|
|
1313
|
+
}, [staking, gpuTier, hasTee, reputation]);
|
|
1314
|
+
useEffect6(() => {
|
|
1315
|
+
if (gpuTier) {
|
|
1316
|
+
fetch();
|
|
1317
|
+
}
|
|
1318
|
+
}, [gpuTier, fetch]);
|
|
1319
|
+
return { ...state, refetch: fetch };
|
|
1320
|
+
}
|
|
1321
|
+
function usePendingUnstakes(address) {
|
|
1322
|
+
const { staking } = useBitSage();
|
|
1323
|
+
const [state, setState] = useState6({
|
|
1324
|
+
data: null,
|
|
1325
|
+
isLoading: false,
|
|
1326
|
+
error: null,
|
|
1327
|
+
refetch: async () => {
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
const fetch = useCallback6(async () => {
|
|
1331
|
+
if (!address) return;
|
|
1332
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1333
|
+
try {
|
|
1334
|
+
const response = await staking.getUnstakeRequests(address);
|
|
1335
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1336
|
+
} catch (err) {
|
|
1337
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch pending unstakes");
|
|
1338
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1339
|
+
}
|
|
1340
|
+
}, [staking, address]);
|
|
1341
|
+
useEffect6(() => {
|
|
1342
|
+
if (address) {
|
|
1343
|
+
fetch();
|
|
1344
|
+
}
|
|
1345
|
+
}, [address, fetch]);
|
|
1346
|
+
return { ...state, refetch: fetch };
|
|
1347
|
+
}
|
|
1348
|
+
function useDelegateStake() {
|
|
1349
|
+
const { staking } = useBitSage();
|
|
1350
|
+
const [state, setState] = useState6({
|
|
1351
|
+
data: null,
|
|
1352
|
+
isLoading: false,
|
|
1353
|
+
error: null
|
|
1354
|
+
});
|
|
1355
|
+
const delegate = useCallback6(
|
|
1356
|
+
async (worker, amount) => {
|
|
1357
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1358
|
+
try {
|
|
1359
|
+
const response = await staking.delegateStake(worker, amount);
|
|
1360
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1361
|
+
return response;
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
const error = err instanceof Error ? err : new Error("Failed to delegate");
|
|
1364
|
+
setState({ data: null, isLoading: false, error });
|
|
1365
|
+
throw error;
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
[staking]
|
|
1369
|
+
);
|
|
1370
|
+
return { ...state, delegate };
|
|
1371
|
+
}
|
|
1372
|
+
function useTotalStaked() {
|
|
1373
|
+
const { staking } = useBitSage();
|
|
1374
|
+
const [state, setState] = useState6({
|
|
1375
|
+
data: null,
|
|
1376
|
+
isLoading: false,
|
|
1377
|
+
error: null,
|
|
1378
|
+
refetch: async () => {
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
const fetch = useCallback6(async () => {
|
|
1382
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1383
|
+
try {
|
|
1384
|
+
const response = await staking.getTotalStaked();
|
|
1385
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1386
|
+
} catch (err) {
|
|
1387
|
+
const error = err instanceof Error ? err : new Error("Failed to fetch total staked");
|
|
1388
|
+
setState((prev) => ({ ...prev, isLoading: false, error }));
|
|
1389
|
+
}
|
|
1390
|
+
}, [staking]);
|
|
1391
|
+
useEffect6(() => {
|
|
1392
|
+
fetch();
|
|
1393
|
+
}, [fetch]);
|
|
1394
|
+
return { ...state, refetch: fetch };
|
|
1395
|
+
}
|
|
1396
|
+
function useMyStaking() {
|
|
1397
|
+
const { address } = useBitSage();
|
|
1398
|
+
const stake = useStakeInfo(address || void 0);
|
|
1399
|
+
const tier = useWorkerTier(address || void 0);
|
|
1400
|
+
const pending = usePendingUnstakes(address || void 0);
|
|
1401
|
+
const isLoading = stake.isLoading || tier.isLoading || pending.isLoading;
|
|
1402
|
+
const error = stake.error || tier.error || pending.error;
|
|
1403
|
+
const refetch = useCallback6(async () => {
|
|
1404
|
+
await Promise.all([stake.refetch(), tier.refetch(), pending.refetch()]);
|
|
1405
|
+
}, [stake.refetch, tier.refetch, pending.refetch]);
|
|
1406
|
+
return {
|
|
1407
|
+
stakeInfo: stake.data,
|
|
1408
|
+
tier: tier.data,
|
|
1409
|
+
pendingUnstakes: pending.data,
|
|
1410
|
+
isLoading,
|
|
1411
|
+
error,
|
|
1412
|
+
refetch
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// src/react/hooks/useWorkers.ts
|
|
1417
|
+
import { useState as useState7, useCallback as useCallback7, useEffect as useEffect7 } from "react";
|
|
1418
|
+
function useWorkers(options) {
|
|
1419
|
+
const { workers } = useBitSage();
|
|
1420
|
+
const [state, setState] = useState7({
|
|
1421
|
+
data: null,
|
|
1422
|
+
isLoading: false,
|
|
1423
|
+
error: null,
|
|
1424
|
+
refetch: async () => {
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
const fetch = useCallback7(async () => {
|
|
1428
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1429
|
+
try {
|
|
1430
|
+
const response = await workers.listWorkers();
|
|
1431
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
setState((prev) => ({
|
|
1434
|
+
...prev,
|
|
1435
|
+
isLoading: false,
|
|
1436
|
+
error: err instanceof Error ? err : new Error("Failed to fetch workers")
|
|
1437
|
+
}));
|
|
1438
|
+
}
|
|
1439
|
+
}, [workers]);
|
|
1440
|
+
useEffect7(() => {
|
|
1441
|
+
fetch();
|
|
1442
|
+
if (options?.pollingInterval) {
|
|
1443
|
+
const interval = setInterval(fetch, options.pollingInterval);
|
|
1444
|
+
return () => clearInterval(interval);
|
|
1445
|
+
}
|
|
1446
|
+
}, [fetch, options?.pollingInterval]);
|
|
1447
|
+
return { ...state, refetch: fetch };
|
|
1448
|
+
}
|
|
1449
|
+
function useWorker(workerId) {
|
|
1450
|
+
const { workers } = useBitSage();
|
|
1451
|
+
const [state, setState] = useState7({
|
|
1452
|
+
data: null,
|
|
1453
|
+
isLoading: false,
|
|
1454
|
+
error: null,
|
|
1455
|
+
refetch: async () => {
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
const fetch = useCallback7(async () => {
|
|
1459
|
+
if (!workerId) return;
|
|
1460
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1461
|
+
try {
|
|
1462
|
+
const response = await workers.getWorker(workerId);
|
|
1463
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1464
|
+
} catch (err) {
|
|
1465
|
+
setState((prev) => ({
|
|
1466
|
+
...prev,
|
|
1467
|
+
isLoading: false,
|
|
1468
|
+
error: err instanceof Error ? err : new Error("Failed to fetch worker")
|
|
1469
|
+
}));
|
|
1470
|
+
}
|
|
1471
|
+
}, [workers, workerId]);
|
|
1472
|
+
useEffect7(() => {
|
|
1473
|
+
if (workerId) fetch();
|
|
1474
|
+
}, [workerId, fetch]);
|
|
1475
|
+
return { ...state, refetch: fetch };
|
|
1476
|
+
}
|
|
1477
|
+
function useWorkerProfile(workerId) {
|
|
1478
|
+
const { workers } = useBitSage();
|
|
1479
|
+
const [state, setState] = useState7({
|
|
1480
|
+
data: null,
|
|
1481
|
+
isLoading: false,
|
|
1482
|
+
error: null,
|
|
1483
|
+
refetch: async () => {
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
const fetch = useCallback7(async () => {
|
|
1487
|
+
if (!workerId) return;
|
|
1488
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1489
|
+
try {
|
|
1490
|
+
const response = await workers.getWorkerProfile(workerId);
|
|
1491
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1492
|
+
} catch (err) {
|
|
1493
|
+
setState((prev) => ({
|
|
1494
|
+
...prev,
|
|
1495
|
+
isLoading: false,
|
|
1496
|
+
error: err instanceof Error ? err : new Error("Failed to fetch profile")
|
|
1497
|
+
}));
|
|
1498
|
+
}
|
|
1499
|
+
}, [workers, workerId]);
|
|
1500
|
+
useEffect7(() => {
|
|
1501
|
+
if (workerId) fetch();
|
|
1502
|
+
}, [workerId, fetch]);
|
|
1503
|
+
return { ...state, refetch: fetch };
|
|
1504
|
+
}
|
|
1505
|
+
function useLeaderboard(metric = "reputation", limit = 100) {
|
|
1506
|
+
const { workers } = useBitSage();
|
|
1507
|
+
const [state, setState] = useState7({
|
|
1508
|
+
data: null,
|
|
1509
|
+
isLoading: false,
|
|
1510
|
+
error: null,
|
|
1511
|
+
refetch: async () => {
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
const fetch = useCallback7(async () => {
|
|
1515
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1516
|
+
try {
|
|
1517
|
+
const response = await workers.getLeaderboard(metric, limit);
|
|
1518
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1519
|
+
} catch (err) {
|
|
1520
|
+
setState((prev) => ({
|
|
1521
|
+
...prev,
|
|
1522
|
+
isLoading: false,
|
|
1523
|
+
error: err instanceof Error ? err : new Error("Failed to fetch leaderboard")
|
|
1524
|
+
}));
|
|
1525
|
+
}
|
|
1526
|
+
}, [workers, metric, limit]);
|
|
1527
|
+
useEffect7(() => {
|
|
1528
|
+
fetch();
|
|
1529
|
+
}, [fetch]);
|
|
1530
|
+
return { ...state, refetch: fetch };
|
|
1531
|
+
}
|
|
1532
|
+
function useRegisterWorker() {
|
|
1533
|
+
const { workers } = useBitSage();
|
|
1534
|
+
const [state, setState] = useState7({
|
|
1535
|
+
data: null,
|
|
1536
|
+
isLoading: false,
|
|
1537
|
+
error: null
|
|
1538
|
+
});
|
|
1539
|
+
const register = useCallback7(
|
|
1540
|
+
async (params) => {
|
|
1541
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1542
|
+
try {
|
|
1543
|
+
const response = await workers.registerWorker(params);
|
|
1544
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1545
|
+
return response;
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
const error = err instanceof Error ? err : new Error("Failed to register");
|
|
1548
|
+
setState({ data: null, isLoading: false, error });
|
|
1549
|
+
throw error;
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1552
|
+
[workers]
|
|
1553
|
+
);
|
|
1554
|
+
return { ...state, register };
|
|
1555
|
+
}
|
|
1556
|
+
function useHeartbeat() {
|
|
1557
|
+
const { workers } = useBitSage();
|
|
1558
|
+
const [state, setState] = useState7({
|
|
1559
|
+
data: null,
|
|
1560
|
+
isLoading: false,
|
|
1561
|
+
error: null
|
|
1562
|
+
});
|
|
1563
|
+
const submitHeartbeat = useCallback7(
|
|
1564
|
+
async (data) => {
|
|
1565
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1566
|
+
try {
|
|
1567
|
+
const response = await workers.submitHeartbeat(data);
|
|
1568
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1569
|
+
return response;
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
const error = err instanceof Error ? err : new Error("Failed to submit heartbeat");
|
|
1572
|
+
setState({ data: null, isLoading: false, error });
|
|
1573
|
+
throw error;
|
|
1574
|
+
}
|
|
1575
|
+
},
|
|
1576
|
+
[workers]
|
|
1577
|
+
);
|
|
1578
|
+
return { ...state, submitHeartbeat };
|
|
1579
|
+
}
|
|
1580
|
+
function useWorkersByCapability(flags, minReputation = 0) {
|
|
1581
|
+
const { workers } = useBitSage();
|
|
1582
|
+
const [state, setState] = useState7({
|
|
1583
|
+
data: null,
|
|
1584
|
+
isLoading: false,
|
|
1585
|
+
error: null,
|
|
1586
|
+
refetch: async () => {
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
const fetch = useCallback7(async () => {
|
|
1590
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1591
|
+
try {
|
|
1592
|
+
const response = await workers.getWorkersByCapability(flags, minReputation);
|
|
1593
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1594
|
+
} catch (err) {
|
|
1595
|
+
setState((prev) => ({
|
|
1596
|
+
...prev,
|
|
1597
|
+
isLoading: false,
|
|
1598
|
+
error: err instanceof Error ? err : new Error("Failed to fetch workers")
|
|
1599
|
+
}));
|
|
1600
|
+
}
|
|
1601
|
+
}, [workers, flags, minReputation]);
|
|
1602
|
+
useEffect7(() => {
|
|
1603
|
+
fetch();
|
|
1604
|
+
}, [fetch]);
|
|
1605
|
+
return { ...state, refetch: fetch };
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// src/react/hooks/usePrivacyHooks.ts
|
|
1609
|
+
import { useState as useState8, useCallback as useCallback8, useEffect as useEffect8 } from "react";
|
|
1610
|
+
function usePrivateAccount() {
|
|
1611
|
+
const { account, privacy } = usePrivacy();
|
|
1612
|
+
const { address } = useBitSage();
|
|
1613
|
+
const [state, setState] = useState8({
|
|
1614
|
+
data: account,
|
|
1615
|
+
isLoading: false,
|
|
1616
|
+
error: null,
|
|
1617
|
+
refetch: async () => {
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
const fetch = useCallback8(async () => {
|
|
1621
|
+
if (!privacy || !address) return;
|
|
1622
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1623
|
+
try {
|
|
1624
|
+
const response = await privacy.getAccount(address);
|
|
1625
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1626
|
+
} catch (err) {
|
|
1627
|
+
setState((prev) => ({
|
|
1628
|
+
...prev,
|
|
1629
|
+
isLoading: false,
|
|
1630
|
+
error: err instanceof Error ? err : new Error("Failed to fetch account")
|
|
1631
|
+
}));
|
|
1632
|
+
}
|
|
1633
|
+
}, [privacy, address]);
|
|
1634
|
+
useEffect8(() => {
|
|
1635
|
+
setState((prev) => ({ ...prev, data: account }));
|
|
1636
|
+
}, [account]);
|
|
1637
|
+
return { ...state, refetch: fetch };
|
|
1638
|
+
}
|
|
1639
|
+
function usePrivateBalance(asset = "SAGE") {
|
|
1640
|
+
const { getDecryptedBalance, balances, hasKeys } = usePrivacy();
|
|
1641
|
+
const [state, setState] = useState8({
|
|
1642
|
+
data: null,
|
|
1643
|
+
isLoading: false,
|
|
1644
|
+
error: null,
|
|
1645
|
+
refetch: async () => {
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
const fetch = useCallback8(async () => {
|
|
1649
|
+
if (!hasKeys) {
|
|
1650
|
+
setState((prev) => ({
|
|
1651
|
+
...prev,
|
|
1652
|
+
error: new Error("Privacy keys not loaded")
|
|
1653
|
+
}));
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1657
|
+
try {
|
|
1658
|
+
const balance = await getDecryptedBalance(asset);
|
|
1659
|
+
setState((prev) => ({ ...prev, data: balance, isLoading: false }));
|
|
1660
|
+
} catch (err) {
|
|
1661
|
+
setState((prev) => ({
|
|
1662
|
+
...prev,
|
|
1663
|
+
isLoading: false,
|
|
1664
|
+
error: err instanceof Error ? err : new Error("Failed to decrypt balance")
|
|
1665
|
+
}));
|
|
1666
|
+
}
|
|
1667
|
+
}, [getDecryptedBalance, hasKeys, asset]);
|
|
1668
|
+
useEffect8(() => {
|
|
1669
|
+
if (hasKeys) fetch();
|
|
1670
|
+
}, [hasKeys, fetch]);
|
|
1671
|
+
return { ...state, refetch: fetch, encrypted: balances.get(asset) || null };
|
|
1672
|
+
}
|
|
1673
|
+
function usePrivateTransfer() {
|
|
1674
|
+
const { privacy, hasKeys } = usePrivacy();
|
|
1675
|
+
const [state, setState] = useState8({
|
|
1676
|
+
data: null,
|
|
1677
|
+
isLoading: false,
|
|
1678
|
+
error: null
|
|
1679
|
+
});
|
|
1680
|
+
const transfer = useCallback8(
|
|
1681
|
+
async (params) => {
|
|
1682
|
+
if (!privacy || !hasKeys) {
|
|
1683
|
+
throw new Error("Privacy not initialized");
|
|
1684
|
+
}
|
|
1685
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1686
|
+
try {
|
|
1687
|
+
const response = await privacy.privateTransfer(params);
|
|
1688
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1689
|
+
return response;
|
|
1690
|
+
} catch (err) {
|
|
1691
|
+
const error = err instanceof Error ? err : new Error("Transfer failed");
|
|
1692
|
+
setState({ data: null, isLoading: false, error });
|
|
1693
|
+
throw error;
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
[privacy, hasKeys]
|
|
1697
|
+
);
|
|
1698
|
+
return { ...state, transfer };
|
|
1699
|
+
}
|
|
1700
|
+
function useRegisterPrivateAccount() {
|
|
1701
|
+
const { privacy, publicKey, hasKeys } = usePrivacy();
|
|
1702
|
+
const [state, setState] = useState8({
|
|
1703
|
+
data: null,
|
|
1704
|
+
isLoading: false,
|
|
1705
|
+
error: null
|
|
1706
|
+
});
|
|
1707
|
+
const register = useCallback8(async () => {
|
|
1708
|
+
if (!privacy || !hasKeys || !publicKey) {
|
|
1709
|
+
throw new Error("Privacy keys not loaded");
|
|
1710
|
+
}
|
|
1711
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1712
|
+
try {
|
|
1713
|
+
const response = await privacy.registerAccount(publicKey);
|
|
1714
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1715
|
+
return response;
|
|
1716
|
+
} catch (err) {
|
|
1717
|
+
const error = err instanceof Error ? err : new Error("Registration failed");
|
|
1718
|
+
setState({ data: null, isLoading: false, error });
|
|
1719
|
+
throw error;
|
|
1720
|
+
}
|
|
1721
|
+
}, [privacy, publicKey, hasKeys]);
|
|
1722
|
+
return { ...state, register };
|
|
1723
|
+
}
|
|
1724
|
+
function useStealthAddress() {
|
|
1725
|
+
const { privacy, hasKeys } = usePrivacy();
|
|
1726
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
1727
|
+
const [error, setError] = useState8(null);
|
|
1728
|
+
const generate = useCallback8(
|
|
1729
|
+
async (recipientPublicKey) => {
|
|
1730
|
+
if (!privacy || !hasKeys) {
|
|
1731
|
+
throw new Error("Privacy not initialized");
|
|
1732
|
+
}
|
|
1733
|
+
setIsLoading(true);
|
|
1734
|
+
setError(null);
|
|
1735
|
+
try {
|
|
1736
|
+
const address = await privacy.generateStealthAddress(recipientPublicKey);
|
|
1737
|
+
setIsLoading(false);
|
|
1738
|
+
return address;
|
|
1739
|
+
} catch (err) {
|
|
1740
|
+
const e = err instanceof Error ? err : new Error("Generation failed");
|
|
1741
|
+
setError(e);
|
|
1742
|
+
setIsLoading(false);
|
|
1743
|
+
throw e;
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
[privacy, hasKeys]
|
|
1747
|
+
);
|
|
1748
|
+
return { generate, isLoading, error };
|
|
1749
|
+
}
|
|
1750
|
+
function useRefreshPrivateBalances() {
|
|
1751
|
+
const { refreshBalances } = usePrivacy();
|
|
1752
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
1753
|
+
const [error, setError] = useState8(null);
|
|
1754
|
+
const refresh = useCallback8(async () => {
|
|
1755
|
+
setIsLoading(true);
|
|
1756
|
+
setError(null);
|
|
1757
|
+
try {
|
|
1758
|
+
await refreshBalances();
|
|
1759
|
+
setIsLoading(false);
|
|
1760
|
+
} catch (err) {
|
|
1761
|
+
const e = err instanceof Error ? err : new Error("Refresh failed");
|
|
1762
|
+
setError(e);
|
|
1763
|
+
setIsLoading(false);
|
|
1764
|
+
}
|
|
1765
|
+
}, [refreshBalances]);
|
|
1766
|
+
return { refresh, isLoading, error };
|
|
1767
|
+
}
|
|
1768
|
+
function useAllPrivateBalances() {
|
|
1769
|
+
const { balances, getDecryptedBalance, hasKeys, refreshBalances } = usePrivacy();
|
|
1770
|
+
const [decryptedBalances, setDecryptedBalances] = useState8(/* @__PURE__ */ new Map());
|
|
1771
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
1772
|
+
const [error, setError] = useState8(null);
|
|
1773
|
+
const loadBalances = useCallback8(async () => {
|
|
1774
|
+
if (!hasKeys) return;
|
|
1775
|
+
setIsLoading(true);
|
|
1776
|
+
setError(null);
|
|
1777
|
+
const newBalances = /* @__PURE__ */ new Map();
|
|
1778
|
+
const assets = ["SAGE", "USDC", "STRK", "WBTC", "ETH"];
|
|
1779
|
+
for (const asset of assets) {
|
|
1780
|
+
const encrypted = balances.get(asset);
|
|
1781
|
+
if (encrypted) {
|
|
1782
|
+
try {
|
|
1783
|
+
const decrypted = await getDecryptedBalance(asset);
|
|
1784
|
+
newBalances.set(asset, { encrypted, decrypted });
|
|
1785
|
+
} catch {
|
|
1786
|
+
newBalances.set(asset, { encrypted, decrypted: null });
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
setDecryptedBalances(newBalances);
|
|
1791
|
+
setIsLoading(false);
|
|
1792
|
+
}, [balances, getDecryptedBalance, hasKeys]);
|
|
1793
|
+
useEffect8(() => {
|
|
1794
|
+
loadBalances();
|
|
1795
|
+
}, [loadBalances]);
|
|
1796
|
+
const refresh = useCallback8(async () => {
|
|
1797
|
+
await refreshBalances();
|
|
1798
|
+
await loadBalances();
|
|
1799
|
+
}, [refreshBalances, loadBalances]);
|
|
1800
|
+
return { balances: decryptedBalances, isLoading, error, refresh };
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
// src/react/hooks/useGovernance.ts
|
|
1804
|
+
import { useState as useState9, useCallback as useCallback9, useEffect as useEffect9 } from "react";
|
|
1805
|
+
function useProposal(proposalId) {
|
|
1806
|
+
const { governance } = useBitSage();
|
|
1807
|
+
const [state, setState] = useState9({
|
|
1808
|
+
data: null,
|
|
1809
|
+
isLoading: false,
|
|
1810
|
+
error: null,
|
|
1811
|
+
refetch: async () => {
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
const fetch = useCallback9(async () => {
|
|
1815
|
+
if (proposalId === void 0) return;
|
|
1816
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1817
|
+
try {
|
|
1818
|
+
const response = await governance.getProposal(proposalId);
|
|
1819
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
setState((prev) => ({
|
|
1822
|
+
...prev,
|
|
1823
|
+
isLoading: false,
|
|
1824
|
+
error: err instanceof Error ? err : new Error("Failed to fetch proposal")
|
|
1825
|
+
}));
|
|
1826
|
+
}
|
|
1827
|
+
}, [governance, proposalId]);
|
|
1828
|
+
useEffect9(() => {
|
|
1829
|
+
if (proposalId !== void 0) fetch();
|
|
1830
|
+
}, [proposalId, fetch]);
|
|
1831
|
+
return { ...state, refetch: fetch };
|
|
1832
|
+
}
|
|
1833
|
+
function useProposals() {
|
|
1834
|
+
const { governance } = useBitSage();
|
|
1835
|
+
const [state, setState] = useState9({
|
|
1836
|
+
data: null,
|
|
1837
|
+
isLoading: false,
|
|
1838
|
+
error: null,
|
|
1839
|
+
refetch: async () => {
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
const fetch = useCallback9(async () => {
|
|
1843
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1844
|
+
try {
|
|
1845
|
+
const response = await governance.listProposals({ status: "Active" });
|
|
1846
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
setState((prev) => ({
|
|
1849
|
+
...prev,
|
|
1850
|
+
isLoading: false,
|
|
1851
|
+
error: err instanceof Error ? err : new Error("Failed to fetch proposals")
|
|
1852
|
+
}));
|
|
1853
|
+
}
|
|
1854
|
+
}, [governance]);
|
|
1855
|
+
useEffect9(() => {
|
|
1856
|
+
fetch();
|
|
1857
|
+
}, [fetch]);
|
|
1858
|
+
return { ...state, refetch: fetch };
|
|
1859
|
+
}
|
|
1860
|
+
function useCreateProposal() {
|
|
1861
|
+
const { governance } = useBitSage();
|
|
1862
|
+
const [state, setState] = useState9({
|
|
1863
|
+
data: null,
|
|
1864
|
+
isLoading: false,
|
|
1865
|
+
error: null
|
|
1866
|
+
});
|
|
1867
|
+
const create = useCallback9(
|
|
1868
|
+
async (params) => {
|
|
1869
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1870
|
+
try {
|
|
1871
|
+
const response = await governance.createProposal(params);
|
|
1872
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1873
|
+
return response;
|
|
1874
|
+
} catch (err) {
|
|
1875
|
+
const error = err instanceof Error ? err : new Error("Failed to create proposal");
|
|
1876
|
+
setState({ data: null, isLoading: false, error });
|
|
1877
|
+
throw error;
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
[governance]
|
|
1881
|
+
);
|
|
1882
|
+
return { ...state, create };
|
|
1883
|
+
}
|
|
1884
|
+
function useVote() {
|
|
1885
|
+
const { governance } = useBitSage();
|
|
1886
|
+
const [state, setState] = useState9({
|
|
1887
|
+
data: null,
|
|
1888
|
+
isLoading: false,
|
|
1889
|
+
error: null
|
|
1890
|
+
});
|
|
1891
|
+
const vote = useCallback9(
|
|
1892
|
+
async (proposalId, direction) => {
|
|
1893
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1894
|
+
try {
|
|
1895
|
+
const response = await governance.vote(proposalId, direction);
|
|
1896
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1897
|
+
return response;
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
const error = err instanceof Error ? err : new Error("Failed to vote");
|
|
1900
|
+
setState({ data: null, isLoading: false, error });
|
|
1901
|
+
throw error;
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
[governance]
|
|
1905
|
+
);
|
|
1906
|
+
return { ...state, vote };
|
|
1907
|
+
}
|
|
1908
|
+
function useVotingPower(address) {
|
|
1909
|
+
const { governance } = useBitSage();
|
|
1910
|
+
const [state, setState] = useState9({
|
|
1911
|
+
data: null,
|
|
1912
|
+
isLoading: false,
|
|
1913
|
+
error: null,
|
|
1914
|
+
refetch: async () => {
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
const fetch = useCallback9(async () => {
|
|
1918
|
+
if (!address) return;
|
|
1919
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1920
|
+
try {
|
|
1921
|
+
const response = await governance.getVotingPower(address);
|
|
1922
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1923
|
+
} catch (err) {
|
|
1924
|
+
setState((prev) => ({
|
|
1925
|
+
...prev,
|
|
1926
|
+
isLoading: false,
|
|
1927
|
+
error: err instanceof Error ? err : new Error("Failed to fetch voting power")
|
|
1928
|
+
}));
|
|
1929
|
+
}
|
|
1930
|
+
}, [governance, address]);
|
|
1931
|
+
useEffect9(() => {
|
|
1932
|
+
if (address) fetch();
|
|
1933
|
+
}, [address, fetch]);
|
|
1934
|
+
return { ...state, refetch: fetch };
|
|
1935
|
+
}
|
|
1936
|
+
function useGovernanceRights(address) {
|
|
1937
|
+
const { governance } = useBitSage();
|
|
1938
|
+
const [state, setState] = useState9({
|
|
1939
|
+
data: null,
|
|
1940
|
+
isLoading: false,
|
|
1941
|
+
error: null,
|
|
1942
|
+
refetch: async () => {
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
const fetch = useCallback9(async () => {
|
|
1946
|
+
if (!address) return;
|
|
1947
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1948
|
+
try {
|
|
1949
|
+
const response = await governance.getGovernanceRights(address);
|
|
1950
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
1951
|
+
} catch (err) {
|
|
1952
|
+
setState((prev) => ({
|
|
1953
|
+
...prev,
|
|
1954
|
+
isLoading: false,
|
|
1955
|
+
error: err instanceof Error ? err : new Error("Failed to fetch governance rights")
|
|
1956
|
+
}));
|
|
1957
|
+
}
|
|
1958
|
+
}, [governance, address]);
|
|
1959
|
+
useEffect9(() => {
|
|
1960
|
+
if (address) fetch();
|
|
1961
|
+
}, [address, fetch]);
|
|
1962
|
+
return { ...state, refetch: fetch };
|
|
1963
|
+
}
|
|
1964
|
+
function useDelegate() {
|
|
1965
|
+
const { governance } = useBitSage();
|
|
1966
|
+
const [state, setState] = useState9({
|
|
1967
|
+
data: null,
|
|
1968
|
+
isLoading: false,
|
|
1969
|
+
error: null
|
|
1970
|
+
});
|
|
1971
|
+
const delegate = useCallback9(
|
|
1972
|
+
async (delegatee) => {
|
|
1973
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1974
|
+
try {
|
|
1975
|
+
const response = await governance.delegate(delegatee);
|
|
1976
|
+
setState({ data: response, isLoading: false, error: null });
|
|
1977
|
+
return response;
|
|
1978
|
+
} catch (err) {
|
|
1979
|
+
const error = err instanceof Error ? err : new Error("Failed to delegate");
|
|
1980
|
+
setState({ data: null, isLoading: false, error });
|
|
1981
|
+
throw error;
|
|
1982
|
+
}
|
|
1983
|
+
},
|
|
1984
|
+
[governance]
|
|
1985
|
+
);
|
|
1986
|
+
return { ...state, delegate };
|
|
1987
|
+
}
|
|
1988
|
+
function useGovernanceStats() {
|
|
1989
|
+
const { governance } = useBitSage();
|
|
1990
|
+
const [state, setState] = useState9({
|
|
1991
|
+
data: null,
|
|
1992
|
+
isLoading: false,
|
|
1993
|
+
error: null,
|
|
1994
|
+
refetch: async () => {
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
const fetch = useCallback9(async () => {
|
|
1998
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
1999
|
+
try {
|
|
2000
|
+
const response = await governance.getGovernanceStats();
|
|
2001
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2002
|
+
} catch (err) {
|
|
2003
|
+
setState((prev) => ({
|
|
2004
|
+
...prev,
|
|
2005
|
+
isLoading: false,
|
|
2006
|
+
error: err instanceof Error ? err : new Error("Failed to fetch stats")
|
|
2007
|
+
}));
|
|
2008
|
+
}
|
|
2009
|
+
}, [governance]);
|
|
2010
|
+
useEffect9(() => {
|
|
2011
|
+
fetch();
|
|
2012
|
+
}, [fetch]);
|
|
2013
|
+
return { ...state, refetch: fetch };
|
|
2014
|
+
}
|
|
2015
|
+
function usePoolBalances() {
|
|
2016
|
+
const { governance } = useBitSage();
|
|
2017
|
+
const [state, setState] = useState9({
|
|
2018
|
+
data: null,
|
|
2019
|
+
isLoading: false,
|
|
2020
|
+
error: null,
|
|
2021
|
+
refetch: async () => {
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
const fetch = useCallback9(async () => {
|
|
2025
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2026
|
+
try {
|
|
2027
|
+
const response = await governance.getPoolBalances();
|
|
2028
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2029
|
+
} catch (err) {
|
|
2030
|
+
setState((prev) => ({
|
|
2031
|
+
...prev,
|
|
2032
|
+
isLoading: false,
|
|
2033
|
+
error: err instanceof Error ? err : new Error("Failed to fetch pool balances")
|
|
2034
|
+
}));
|
|
2035
|
+
}
|
|
2036
|
+
}, [governance]);
|
|
2037
|
+
useEffect9(() => {
|
|
2038
|
+
fetch();
|
|
2039
|
+
}, [fetch]);
|
|
2040
|
+
return { ...state, refetch: fetch };
|
|
2041
|
+
}
|
|
2042
|
+
function useTotalBurned() {
|
|
2043
|
+
const { governance } = useBitSage();
|
|
2044
|
+
const [state, setState] = useState9({
|
|
2045
|
+
data: null,
|
|
2046
|
+
isLoading: false,
|
|
2047
|
+
error: null,
|
|
2048
|
+
refetch: async () => {
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
const fetch = useCallback9(async () => {
|
|
2052
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2053
|
+
try {
|
|
2054
|
+
const response = await governance.getTotalBurned();
|
|
2055
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2056
|
+
} catch (err) {
|
|
2057
|
+
setState((prev) => ({
|
|
2058
|
+
...prev,
|
|
2059
|
+
isLoading: false,
|
|
2060
|
+
error: err instanceof Error ? err : new Error("Failed to fetch total burned")
|
|
2061
|
+
}));
|
|
2062
|
+
}
|
|
2063
|
+
}, [governance]);
|
|
2064
|
+
useEffect9(() => {
|
|
2065
|
+
fetch();
|
|
2066
|
+
}, [fetch]);
|
|
2067
|
+
return { ...state, refetch: fetch };
|
|
2068
|
+
}
|
|
2069
|
+
function useVestingStatus() {
|
|
2070
|
+
const { governance } = useBitSage();
|
|
2071
|
+
const [state, setState] = useState9({
|
|
2072
|
+
data: null,
|
|
2073
|
+
isLoading: false,
|
|
2074
|
+
error: null,
|
|
2075
|
+
refetch: async () => {
|
|
2076
|
+
}
|
|
2077
|
+
});
|
|
2078
|
+
const fetch = useCallback9(async () => {
|
|
2079
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2080
|
+
try {
|
|
2081
|
+
const response = await governance.getVestingStatus();
|
|
2082
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2083
|
+
} catch (err) {
|
|
2084
|
+
setState((prev) => ({
|
|
2085
|
+
...prev,
|
|
2086
|
+
isLoading: false,
|
|
2087
|
+
error: err instanceof Error ? err : new Error("Failed to fetch vesting status")
|
|
2088
|
+
}));
|
|
2089
|
+
}
|
|
2090
|
+
}, [governance]);
|
|
2091
|
+
useEffect9(() => {
|
|
2092
|
+
fetch();
|
|
2093
|
+
}, [fetch]);
|
|
2094
|
+
return { ...state, refetch: fetch };
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// src/react/hooks/useDashboard.ts
|
|
2098
|
+
import { useState as useState10, useCallback as useCallback10, useEffect as useEffect10 } from "react";
|
|
2099
|
+
function useValidatorStatus(options) {
|
|
2100
|
+
const { dashboard } = useBitSage();
|
|
2101
|
+
const [state, setState] = useState10({
|
|
2102
|
+
data: null,
|
|
2103
|
+
isLoading: false,
|
|
2104
|
+
error: null,
|
|
2105
|
+
refetch: async () => {
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
const fetch = useCallback10(async () => {
|
|
2109
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2110
|
+
try {
|
|
2111
|
+
const response = await dashboard.getValidatorStatus();
|
|
2112
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2113
|
+
} catch (err) {
|
|
2114
|
+
setState((prev) => ({
|
|
2115
|
+
...prev,
|
|
2116
|
+
isLoading: false,
|
|
2117
|
+
error: err instanceof Error ? err : new Error("Failed to fetch validator status")
|
|
2118
|
+
}));
|
|
2119
|
+
}
|
|
2120
|
+
}, [dashboard]);
|
|
2121
|
+
useEffect10(() => {
|
|
2122
|
+
fetch();
|
|
2123
|
+
if (options?.pollingInterval) {
|
|
2124
|
+
const interval = setInterval(fetch, options.pollingInterval);
|
|
2125
|
+
return () => clearInterval(interval);
|
|
2126
|
+
}
|
|
2127
|
+
}, [fetch, options?.pollingInterval]);
|
|
2128
|
+
return { ...state, refetch: fetch };
|
|
2129
|
+
}
|
|
2130
|
+
function useGpuMetrics(options) {
|
|
2131
|
+
const { dashboard } = useBitSage();
|
|
2132
|
+
const [state, setState] = useState10({
|
|
2133
|
+
data: null,
|
|
2134
|
+
isLoading: false,
|
|
2135
|
+
error: null,
|
|
2136
|
+
refetch: async () => {
|
|
2137
|
+
}
|
|
2138
|
+
});
|
|
2139
|
+
const fetch = useCallback10(async () => {
|
|
2140
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2141
|
+
try {
|
|
2142
|
+
const response = await dashboard.getGPUMetrics();
|
|
2143
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2144
|
+
} catch (err) {
|
|
2145
|
+
setState((prev) => ({
|
|
2146
|
+
...prev,
|
|
2147
|
+
isLoading: false,
|
|
2148
|
+
error: err instanceof Error ? err : new Error("Failed to fetch GPU metrics")
|
|
2149
|
+
}));
|
|
2150
|
+
}
|
|
2151
|
+
}, [dashboard]);
|
|
2152
|
+
useEffect10(() => {
|
|
2153
|
+
fetch();
|
|
2154
|
+
if (options?.pollingInterval) {
|
|
2155
|
+
const interval = setInterval(fetch, options.pollingInterval);
|
|
2156
|
+
return () => clearInterval(interval);
|
|
2157
|
+
}
|
|
2158
|
+
}, [fetch, options?.pollingInterval]);
|
|
2159
|
+
return { ...state, refetch: fetch };
|
|
2160
|
+
}
|
|
2161
|
+
function useRewardsInfo() {
|
|
2162
|
+
const { dashboard } = useBitSage();
|
|
2163
|
+
const [state, setState] = useState10({
|
|
2164
|
+
data: null,
|
|
2165
|
+
isLoading: false,
|
|
2166
|
+
error: null,
|
|
2167
|
+
refetch: async () => {
|
|
2168
|
+
}
|
|
2169
|
+
});
|
|
2170
|
+
const fetch = useCallback10(async () => {
|
|
2171
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2172
|
+
try {
|
|
2173
|
+
const response = await dashboard.getRewardsInfo();
|
|
2174
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2175
|
+
} catch (err) {
|
|
2176
|
+
setState((prev) => ({
|
|
2177
|
+
...prev,
|
|
2178
|
+
isLoading: false,
|
|
2179
|
+
error: err instanceof Error ? err : new Error("Failed to fetch rewards info")
|
|
2180
|
+
}));
|
|
2181
|
+
}
|
|
2182
|
+
}, [dashboard]);
|
|
2183
|
+
useEffect10(() => {
|
|
2184
|
+
fetch();
|
|
2185
|
+
}, [fetch]);
|
|
2186
|
+
return { ...state, refetch: fetch };
|
|
2187
|
+
}
|
|
2188
|
+
function useRewardsHistory(period = "week") {
|
|
2189
|
+
const { dashboard } = useBitSage();
|
|
2190
|
+
const [state, setState] = useState10({
|
|
2191
|
+
data: null,
|
|
2192
|
+
isLoading: false,
|
|
2193
|
+
error: null,
|
|
2194
|
+
refetch: async () => {
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
const fetch = useCallback10(async () => {
|
|
2198
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2199
|
+
try {
|
|
2200
|
+
const response = await dashboard.getRewardsHistory(period);
|
|
2201
|
+
setState((prev) => ({ ...prev, data: response.entries, isLoading: false }));
|
|
2202
|
+
} catch (err) {
|
|
2203
|
+
setState((prev) => ({
|
|
2204
|
+
...prev,
|
|
2205
|
+
isLoading: false,
|
|
2206
|
+
error: err instanceof Error ? err : new Error("Failed to fetch rewards history")
|
|
2207
|
+
}));
|
|
2208
|
+
}
|
|
2209
|
+
}, [dashboard, period]);
|
|
2210
|
+
useEffect10(() => {
|
|
2211
|
+
fetch();
|
|
2212
|
+
}, [fetch]);
|
|
2213
|
+
return { ...state, refetch: fetch };
|
|
2214
|
+
}
|
|
2215
|
+
function useJobAnalytics() {
|
|
2216
|
+
const { dashboard } = useBitSage();
|
|
2217
|
+
const [state, setState] = useState10({
|
|
2218
|
+
data: null,
|
|
2219
|
+
isLoading: false,
|
|
2220
|
+
error: null,
|
|
2221
|
+
refetch: async () => {
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
const fetch = useCallback10(async () => {
|
|
2225
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2226
|
+
try {
|
|
2227
|
+
const response = await dashboard.getJobAnalytics();
|
|
2228
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
setState((prev) => ({
|
|
2231
|
+
...prev,
|
|
2232
|
+
isLoading: false,
|
|
2233
|
+
error: err instanceof Error ? err : new Error("Failed to fetch job analytics")
|
|
2234
|
+
}));
|
|
2235
|
+
}
|
|
2236
|
+
}, [dashboard]);
|
|
2237
|
+
useEffect10(() => {
|
|
2238
|
+
fetch();
|
|
2239
|
+
}, [fetch]);
|
|
2240
|
+
return { ...state, refetch: fetch };
|
|
2241
|
+
}
|
|
2242
|
+
function useRecentJobs(limit = 10) {
|
|
2243
|
+
const { dashboard } = useBitSage();
|
|
2244
|
+
const [state, setState] = useState10({
|
|
2245
|
+
data: null,
|
|
2246
|
+
isLoading: false,
|
|
2247
|
+
error: null,
|
|
2248
|
+
refetch: async () => {
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
const fetch = useCallback10(async () => {
|
|
2252
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2253
|
+
try {
|
|
2254
|
+
const response = await dashboard.getRecentJobs(limit);
|
|
2255
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2256
|
+
} catch (err) {
|
|
2257
|
+
setState((prev) => ({
|
|
2258
|
+
...prev,
|
|
2259
|
+
isLoading: false,
|
|
2260
|
+
error: err instanceof Error ? err : new Error("Failed to fetch recent jobs")
|
|
2261
|
+
}));
|
|
2262
|
+
}
|
|
2263
|
+
}, [dashboard, limit]);
|
|
2264
|
+
useEffect10(() => {
|
|
2265
|
+
fetch();
|
|
2266
|
+
}, [fetch]);
|
|
2267
|
+
return { ...state, refetch: fetch };
|
|
2268
|
+
}
|
|
2269
|
+
function useNetworkStats(options) {
|
|
2270
|
+
const { dashboard } = useBitSage();
|
|
2271
|
+
const [state, setState] = useState10({
|
|
2272
|
+
data: null,
|
|
2273
|
+
isLoading: false,
|
|
2274
|
+
error: null,
|
|
2275
|
+
refetch: async () => {
|
|
2276
|
+
}
|
|
2277
|
+
});
|
|
2278
|
+
const fetch = useCallback10(async () => {
|
|
2279
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2280
|
+
try {
|
|
2281
|
+
const response = await dashboard.getNetworkStats();
|
|
2282
|
+
setState((prev) => ({ ...prev, data: response, isLoading: false }));
|
|
2283
|
+
} catch (err) {
|
|
2284
|
+
setState((prev) => ({
|
|
2285
|
+
...prev,
|
|
2286
|
+
isLoading: false,
|
|
2287
|
+
error: err instanceof Error ? err : new Error("Failed to fetch network stats")
|
|
2288
|
+
}));
|
|
2289
|
+
}
|
|
2290
|
+
}, [dashboard]);
|
|
2291
|
+
useEffect10(() => {
|
|
2292
|
+
fetch();
|
|
2293
|
+
if (options?.pollingInterval) {
|
|
2294
|
+
const interval = setInterval(fetch, options.pollingInterval);
|
|
2295
|
+
return () => clearInterval(interval);
|
|
2296
|
+
}
|
|
2297
|
+
}, [fetch, options?.pollingInterval]);
|
|
2298
|
+
return { ...state, refetch: fetch };
|
|
2299
|
+
}
|
|
2300
|
+
function useNetworkWorkers() {
|
|
2301
|
+
const { dashboard } = useBitSage();
|
|
2302
|
+
const [state, setState] = useState10({
|
|
2303
|
+
data: null,
|
|
2304
|
+
isLoading: false,
|
|
2305
|
+
error: null,
|
|
2306
|
+
refetch: async () => {
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
const fetch = useCallback10(async () => {
|
|
2310
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2311
|
+
try {
|
|
2312
|
+
const response = await dashboard.getNetworkWorkers();
|
|
2313
|
+
setState((prev) => ({ ...prev, data: response.workers, isLoading: false }));
|
|
2314
|
+
} catch (err) {
|
|
2315
|
+
setState((prev) => ({
|
|
2316
|
+
...prev,
|
|
2317
|
+
isLoading: false,
|
|
2318
|
+
error: err instanceof Error ? err : new Error("Failed to fetch network workers")
|
|
2319
|
+
}));
|
|
2320
|
+
}
|
|
2321
|
+
}, [dashboard]);
|
|
2322
|
+
useEffect10(() => {
|
|
2323
|
+
fetch();
|
|
2324
|
+
}, [fetch]);
|
|
2325
|
+
return { ...state, refetch: fetch };
|
|
2326
|
+
}
|
|
2327
|
+
function useValidatorOverview(options) {
|
|
2328
|
+
const status = useValidatorStatus(options);
|
|
2329
|
+
const gpu = useGpuMetrics(options);
|
|
2330
|
+
const rewards = useRewardsInfo();
|
|
2331
|
+
const isLoading = status.isLoading || gpu.isLoading || rewards.isLoading;
|
|
2332
|
+
const error = status.error || gpu.error || rewards.error;
|
|
2333
|
+
const refetch = useCallback10(async () => {
|
|
2334
|
+
await Promise.all([status.refetch(), gpu.refetch(), rewards.refetch()]);
|
|
2335
|
+
}, [status.refetch, gpu.refetch, rewards.refetch]);
|
|
2336
|
+
return {
|
|
2337
|
+
status: status.data,
|
|
2338
|
+
gpuMetrics: gpu.data,
|
|
2339
|
+
rewards: rewards.data,
|
|
2340
|
+
isLoading,
|
|
2341
|
+
error,
|
|
2342
|
+
refetch
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// src/react/hooks/useWebSocketHooks.ts
|
|
2347
|
+
import { useState as useState11, useEffect as useEffect11, useRef as useRef3 } from "react";
|
|
2348
|
+
function useJobUpdates(jobId) {
|
|
2349
|
+
const { subscribeToJob, isConnected } = useWebSocket();
|
|
2350
|
+
const [event, setEvent] = useState11(null);
|
|
2351
|
+
const [history, setHistory] = useState11([]);
|
|
2352
|
+
const [isSubscribed, setIsSubscribed] = useState11(false);
|
|
2353
|
+
const unsubscribeRef = useRef3(null);
|
|
2354
|
+
useEffect11(() => {
|
|
2355
|
+
if (!jobId || !isConnected) {
|
|
2356
|
+
setIsSubscribed(false);
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
const handleEvent = (e) => {
|
|
2360
|
+
setEvent(e);
|
|
2361
|
+
setHistory((prev) => [...prev, e]);
|
|
2362
|
+
};
|
|
2363
|
+
unsubscribeRef.current = subscribeToJob(jobId, handleEvent);
|
|
2364
|
+
setIsSubscribed(true);
|
|
2365
|
+
return () => {
|
|
2366
|
+
if (unsubscribeRef.current) {
|
|
2367
|
+
unsubscribeRef.current();
|
|
2368
|
+
unsubscribeRef.current = null;
|
|
2369
|
+
}
|
|
2370
|
+
setIsSubscribed(false);
|
|
2371
|
+
};
|
|
2372
|
+
}, [jobId, isConnected, subscribeToJob]);
|
|
2373
|
+
return { event, isSubscribed, history };
|
|
2374
|
+
}
|
|
2375
|
+
function useWorkerUpdates(workerId) {
|
|
2376
|
+
const { subscribeToWorker, isConnected } = useWebSocket();
|
|
2377
|
+
const [event, setEvent] = useState11(null);
|
|
2378
|
+
const [isSubscribed, setIsSubscribed] = useState11(false);
|
|
2379
|
+
const unsubscribeRef = useRef3(null);
|
|
2380
|
+
useEffect11(() => {
|
|
2381
|
+
if (!workerId || !isConnected) {
|
|
2382
|
+
setIsSubscribed(false);
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
unsubscribeRef.current = subscribeToWorker(workerId, setEvent);
|
|
2386
|
+
setIsSubscribed(true);
|
|
2387
|
+
return () => {
|
|
2388
|
+
if (unsubscribeRef.current) {
|
|
2389
|
+
unsubscribeRef.current();
|
|
2390
|
+
unsubscribeRef.current = null;
|
|
2391
|
+
}
|
|
2392
|
+
setIsSubscribed(false);
|
|
2393
|
+
};
|
|
2394
|
+
}, [workerId, isConnected, subscribeToWorker]);
|
|
2395
|
+
return { event, isSubscribed };
|
|
2396
|
+
}
|
|
2397
|
+
function useNetworkStatsStream() {
|
|
2398
|
+
const { subscribeToNetworkStats, latestNetworkStats, isConnected } = useWebSocket();
|
|
2399
|
+
const [stats, setStats] = useState11(latestNetworkStats);
|
|
2400
|
+
const [isSubscribed, setIsSubscribed] = useState11(false);
|
|
2401
|
+
const unsubscribeRef = useRef3(null);
|
|
2402
|
+
useEffect11(() => {
|
|
2403
|
+
if (!isConnected) {
|
|
2404
|
+
setIsSubscribed(false);
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
unsubscribeRef.current = subscribeToNetworkStats(setStats);
|
|
2408
|
+
setIsSubscribed(true);
|
|
2409
|
+
return () => {
|
|
2410
|
+
if (unsubscribeRef.current) {
|
|
2411
|
+
unsubscribeRef.current();
|
|
2412
|
+
unsubscribeRef.current = null;
|
|
2413
|
+
}
|
|
2414
|
+
setIsSubscribed(false);
|
|
2415
|
+
};
|
|
2416
|
+
}, [isConnected, subscribeToNetworkStats]);
|
|
2417
|
+
useEffect11(() => {
|
|
2418
|
+
if (latestNetworkStats) {
|
|
2419
|
+
setStats(latestNetworkStats);
|
|
2420
|
+
}
|
|
2421
|
+
}, [latestNetworkStats]);
|
|
2422
|
+
return { stats, isSubscribed };
|
|
2423
|
+
}
|
|
2424
|
+
function useProofVerified(proofHash) {
|
|
2425
|
+
const { subscribeToProofVerified, isConnected } = useWebSocket();
|
|
2426
|
+
const [event, setEvent] = useState11(null);
|
|
2427
|
+
const [isSubscribed, setIsSubscribed] = useState11(false);
|
|
2428
|
+
const unsubscribeRef = useRef3(null);
|
|
2429
|
+
useEffect11(() => {
|
|
2430
|
+
if (!proofHash || !isConnected) {
|
|
2431
|
+
setIsSubscribed(false);
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
unsubscribeRef.current = subscribeToProofVerified(proofHash, setEvent);
|
|
2435
|
+
setIsSubscribed(true);
|
|
2436
|
+
return () => {
|
|
2437
|
+
if (unsubscribeRef.current) {
|
|
2438
|
+
unsubscribeRef.current();
|
|
2439
|
+
unsubscribeRef.current = null;
|
|
2440
|
+
}
|
|
2441
|
+
setIsSubscribed(false);
|
|
2442
|
+
};
|
|
2443
|
+
}, [proofHash, isConnected, subscribeToProofVerified]);
|
|
2444
|
+
return {
|
|
2445
|
+
event,
|
|
2446
|
+
isVerified: event?.data?.verified ?? false,
|
|
2447
|
+
isSubscribed
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2450
|
+
function useMultipleJobUpdates(jobIds) {
|
|
2451
|
+
const { subscribeToJob, isConnected } = useWebSocket();
|
|
2452
|
+
const [events, setEvents] = useState11({});
|
|
2453
|
+
const unsubscribersRef = useRef3(/* @__PURE__ */ new Map());
|
|
2454
|
+
useEffect11(() => {
|
|
2455
|
+
if (!isConnected) return;
|
|
2456
|
+
for (const jobId of jobIds) {
|
|
2457
|
+
if (!unsubscribersRef.current.has(jobId)) {
|
|
2458
|
+
const unsubscribe = subscribeToJob(jobId, (event) => {
|
|
2459
|
+
setEvents((prev) => ({ ...prev, [jobId]: event }));
|
|
2460
|
+
});
|
|
2461
|
+
unsubscribersRef.current.set(jobId, unsubscribe);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
for (const [jobId, unsubscribe] of unsubscribersRef.current.entries()) {
|
|
2465
|
+
if (!jobIds.includes(jobId)) {
|
|
2466
|
+
unsubscribe();
|
|
2467
|
+
unsubscribersRef.current.delete(jobId);
|
|
2468
|
+
setEvents((prev) => {
|
|
2469
|
+
const next = { ...prev };
|
|
2470
|
+
delete next[jobId];
|
|
2471
|
+
return next;
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
return () => {
|
|
2476
|
+
for (const unsubscribe of unsubscribersRef.current.values()) {
|
|
2477
|
+
unsubscribe();
|
|
2478
|
+
}
|
|
2479
|
+
unsubscribersRef.current.clear();
|
|
2480
|
+
};
|
|
2481
|
+
}, [jobIds, isConnected, subscribeToJob]);
|
|
2482
|
+
const completedCount = Object.values(events).filter(
|
|
2483
|
+
(e) => e?.data?.status === "completed"
|
|
2484
|
+
).length;
|
|
2485
|
+
const failedCount = Object.values(events).filter(
|
|
2486
|
+
(e) => e?.data?.status === "failed"
|
|
2487
|
+
).length;
|
|
2488
|
+
return { events, completedCount, failedCount };
|
|
2489
|
+
}
|
|
2490
|
+
function useSmoothedNetworkStats(metric, windowSize = 10) {
|
|
2491
|
+
const { stats } = useNetworkStatsStream();
|
|
2492
|
+
const [history, setHistory] = useState11([]);
|
|
2493
|
+
useEffect11(() => {
|
|
2494
|
+
if (stats && typeof stats[metric] === "number") {
|
|
2495
|
+
setHistory((prev) => {
|
|
2496
|
+
const next = [...prev, stats[metric]];
|
|
2497
|
+
return next.slice(-windowSize);
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
}, [stats, metric, windowSize]);
|
|
2501
|
+
const currentValue = history[history.length - 1] || 0;
|
|
2502
|
+
const average = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : 0;
|
|
2503
|
+
let trend = 0;
|
|
2504
|
+
if (history.length >= 4) {
|
|
2505
|
+
const mid = Math.floor(history.length / 2);
|
|
2506
|
+
const firstHalf = history.slice(0, mid);
|
|
2507
|
+
const secondHalf = history.slice(mid);
|
|
2508
|
+
const firstAvg = firstHalf.reduce((a, b) => a + b, 0) / firstHalf.length;
|
|
2509
|
+
const secondAvg = secondHalf.reduce((a, b) => a + b, 0) / secondHalf.length;
|
|
2510
|
+
trend = secondAvg - firstAvg;
|
|
2511
|
+
}
|
|
2512
|
+
return { currentValue, average, trend };
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
// src/react/hooks/useConfidentialSwap.ts
|
|
2516
|
+
import { useState as useState12, useCallback as useCallback12, useEffect as useEffect12, useMemo as useMemo4 } from "react";
|
|
2517
|
+
export {
|
|
2518
|
+
BitSageContext,
|
|
2519
|
+
BitSageProvider,
|
|
2520
|
+
PrivacyContext,
|
|
2521
|
+
PrivacyProvider,
|
|
2522
|
+
WebSocketContext,
|
|
2523
|
+
WebSocketProvider,
|
|
2524
|
+
useAllPrivateBalances,
|
|
2525
|
+
useBitSage,
|
|
2526
|
+
useBitSageClient,
|
|
2527
|
+
useCancelJob,
|
|
2528
|
+
useClaimRewards,
|
|
2529
|
+
useContracts,
|
|
2530
|
+
useCreateProposal,
|
|
2531
|
+
useCurrentBaseReward,
|
|
2532
|
+
useDailyCap,
|
|
2533
|
+
useDelegate,
|
|
2534
|
+
useDelegateStake,
|
|
2535
|
+
useGovernanceRights,
|
|
2536
|
+
useGovernanceStats,
|
|
2537
|
+
useGpuMetrics,
|
|
2538
|
+
useGpuMultiplier,
|
|
2539
|
+
useHeartbeat,
|
|
2540
|
+
useJobAnalytics,
|
|
2541
|
+
useJobResult,
|
|
2542
|
+
useJobStatus,
|
|
2543
|
+
useJobUpdates,
|
|
2544
|
+
useJobs,
|
|
2545
|
+
useLeaderboard,
|
|
2546
|
+
useMinStake,
|
|
2547
|
+
useMiningOverview,
|
|
2548
|
+
useMiningPoolStatus,
|
|
2549
|
+
useMiningRewardEstimate,
|
|
2550
|
+
useMultipleJobUpdates,
|
|
2551
|
+
useMyStaking,
|
|
2552
|
+
useNetwork,
|
|
2553
|
+
useNetworkStats,
|
|
2554
|
+
useNetworkStatsStream,
|
|
2555
|
+
useNetworkWorkers,
|
|
2556
|
+
usePendingUnstakes,
|
|
2557
|
+
usePoolBalances,
|
|
2558
|
+
usePrivacy,
|
|
2559
|
+
usePrivacyClient,
|
|
2560
|
+
usePrivacyKeys,
|
|
2561
|
+
usePrivateAccount,
|
|
2562
|
+
usePrivateBalance,
|
|
2563
|
+
usePrivateTransfer,
|
|
2564
|
+
useProofVerified,
|
|
2565
|
+
useProposal,
|
|
2566
|
+
useProposals,
|
|
2567
|
+
useRecentJobs,
|
|
2568
|
+
useRefreshPrivateBalances,
|
|
2569
|
+
useRegisterPrivateAccount,
|
|
2570
|
+
useRegisterWorker,
|
|
2571
|
+
useRemainingDailyCap,
|
|
2572
|
+
useRewardsHistory,
|
|
2573
|
+
useRewardsInfo,
|
|
2574
|
+
useSmoothedNetworkStats,
|
|
2575
|
+
useStake,
|
|
2576
|
+
useStakeInfo,
|
|
2577
|
+
useStakingConfig,
|
|
2578
|
+
useStealthAddress,
|
|
2579
|
+
useSubmitJob,
|
|
2580
|
+
useTotalBurned,
|
|
2581
|
+
useTotalStaked,
|
|
2582
|
+
useUnstake,
|
|
2583
|
+
useValidatorOverview,
|
|
2584
|
+
useValidatorStatus,
|
|
2585
|
+
useVestingStatus,
|
|
2586
|
+
useVote,
|
|
2587
|
+
useVotingPower,
|
|
2588
|
+
useWaitForJob,
|
|
2589
|
+
useWallet,
|
|
2590
|
+
useWebSocket,
|
|
2591
|
+
useWebSocketConnection,
|
|
2592
|
+
useWorker,
|
|
2593
|
+
useWorkerMiningStats,
|
|
2594
|
+
useWorkerProfile,
|
|
2595
|
+
useWorkerTier,
|
|
2596
|
+
useWorkerTierBenefits,
|
|
2597
|
+
useWorkerUpdates,
|
|
2598
|
+
useWorkers,
|
|
2599
|
+
useWorkersByCapability
|
|
2600
|
+
};
|