@tangle-network/blueprint-ui 0.5.4 → 0.5.6
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/package.json +1 -1
- package/src/components.ts +4 -0
- package/src/index.ts +4 -2
- package/dist/chunk-37ADATBT.js +0 -55
- package/dist/chunk-37ADATBT.js.map +0 -1
- package/dist/chunk-F2QBCGUW.js +0 -1582
- package/dist/chunk-F2QBCGUW.js.map +0 -1
- package/dist/chunk-TM5ROMDV.js +0 -57
- package/dist/chunk-TM5ROMDV.js.map +0 -1
- package/dist/components.d.ts +0 -195
- package/dist/components.js +0 -1168
- package/dist/components.js.map +0 -1
- package/dist/detectParentOrigin-BYruoIdc.d.ts +0 -26
- package/dist/iframe/index.d.ts +0 -146
- package/dist/iframe/index.js +0 -607
- package/dist/iframe/index.js.map +0 -1
- package/dist/iframe/testing-index.d.ts +0 -82
- package/dist/iframe/testing-index.js +0 -560
- package/dist/iframe/testing-index.js.map +0 -1
- package/dist/index.d.ts +0 -8620
- package/dist/index.js +0 -870
- package/dist/index.js.map +0 -1
- package/dist/parentBridgeProtocol-BSgLXg9g.d.ts +0 -204
- package/dist/preset.d.ts +0 -60
- package/dist/preset.js +0 -7
- package/dist/preset.js.map +0 -1
- package/dist/styles.css +0 -568
- package/dist/tangleIframeClient-C7NFG_Dw.d.ts +0 -133
- package/dist/useRegistrationCommand-BvqgHBiJ.d.ts +0 -151
- package/dist/wallet/index.d.ts +0 -134
- package/dist/wallet/index.js +0 -472
- package/dist/wallet/index.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,870 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BlueprintHostHero,
|
|
3
|
-
BlueprintHostPanel,
|
|
4
|
-
DEFAULT_THEME,
|
|
5
|
-
addTx,
|
|
6
|
-
allTangleChains,
|
|
7
|
-
buildCanonicalBlueprintSlug,
|
|
8
|
-
canPublisherClaimSlug,
|
|
9
|
-
clearTxs,
|
|
10
|
-
cn,
|
|
11
|
-
configureNetworks,
|
|
12
|
-
createTangleLocalChain,
|
|
13
|
-
deriveBlueprintRequestedSlug,
|
|
14
|
-
deserializeWithBigInt,
|
|
15
|
-
encodeJobArgs,
|
|
16
|
-
formatCost,
|
|
17
|
-
getAddresses,
|
|
18
|
-
getBlueprintExperienceTierLabel,
|
|
19
|
-
getBlueprintPath,
|
|
20
|
-
getBlueprintPublisherVerificationLabel,
|
|
21
|
-
getBlueprintServicePath,
|
|
22
|
-
getBlueprintSlugPolicyLabel,
|
|
23
|
-
getBlueprintSurfaceLabel,
|
|
24
|
-
getEnvVar,
|
|
25
|
-
getExternalAppTrustLabel,
|
|
26
|
-
getInfra,
|
|
27
|
-
getNetworks,
|
|
28
|
-
getPublicClient,
|
|
29
|
-
infraStore,
|
|
30
|
-
isTrustedExternalAppHost,
|
|
31
|
-
isVerifiedBlueprintPublisher,
|
|
32
|
-
kTheme,
|
|
33
|
-
mainnet,
|
|
34
|
-
pendingCount,
|
|
35
|
-
persistedAtom,
|
|
36
|
-
publicClient,
|
|
37
|
-
publicClientStore,
|
|
38
|
-
resolveBlueprintAppView,
|
|
39
|
-
resolveOperatorRpc,
|
|
40
|
-
resolveRpcUrl,
|
|
41
|
-
rpcUrl,
|
|
42
|
-
sanitizeBlueprintSlugPart,
|
|
43
|
-
sanitizeSelectedChainId,
|
|
44
|
-
selectedChainIdStore,
|
|
45
|
-
serializeWithBigInt,
|
|
46
|
-
solvePoW,
|
|
47
|
-
tangleJobsAbi,
|
|
48
|
-
tangleLocal,
|
|
49
|
-
tangleMainnet,
|
|
50
|
-
tangleOperatorsAbi,
|
|
51
|
-
tangleServicesAbi,
|
|
52
|
-
tangleTestnet,
|
|
53
|
-
themeIsDark,
|
|
54
|
-
themeStore,
|
|
55
|
-
toBlueprintAppEntry,
|
|
56
|
-
toggleTheme,
|
|
57
|
-
txListStore,
|
|
58
|
-
updateInfra,
|
|
59
|
-
updateTx,
|
|
60
|
-
useJobForm,
|
|
61
|
-
useJobPrice,
|
|
62
|
-
useJobPrices,
|
|
63
|
-
useQuotes,
|
|
64
|
-
useRegistrationCommand,
|
|
65
|
-
useSubmitJob,
|
|
66
|
-
useThemeValue
|
|
67
|
-
} from "./chunk-F2QBCGUW.js";
|
|
68
|
-
import {
|
|
69
|
-
bpThemeTokens
|
|
70
|
-
} from "./chunk-37ADATBT.js";
|
|
71
|
-
|
|
72
|
-
// src/utils/web3.ts
|
|
73
|
-
import { http } from "wagmi";
|
|
74
|
-
function getTangleWalletChains(localChain = tangleLocal) {
|
|
75
|
-
return [localChain, tangleTestnet, tangleMainnet, mainnet];
|
|
76
|
-
}
|
|
77
|
-
var tangleWalletChains = getTangleWalletChains();
|
|
78
|
-
function createTangleTransports(localChain = tangleLocal) {
|
|
79
|
-
const localRpcUrl = localChain.rpcUrls.default.http[0] ?? rpcUrl;
|
|
80
|
-
return {
|
|
81
|
-
[localChain.id]: http(localRpcUrl),
|
|
82
|
-
[tangleTestnet.id]: http("https://testnet-rpc.tangle.tools"),
|
|
83
|
-
[tangleMainnet.id]: http("https://rpc.tangle.tools"),
|
|
84
|
-
[mainnet.id]: http()
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
var defaultConnectKitOptions = {
|
|
88
|
-
hideBalance: false,
|
|
89
|
-
hideTooltips: false,
|
|
90
|
-
hideQuestionMarkCTA: true,
|
|
91
|
-
overlayBlur: 4
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// src/stores/session.ts
|
|
95
|
-
var sessionMapStore = persistedAtom({
|
|
96
|
-
key: "bp_sessions",
|
|
97
|
-
initial: {}
|
|
98
|
-
});
|
|
99
|
-
function getSession(sandboxId) {
|
|
100
|
-
const map = sessionMapStore.get();
|
|
101
|
-
const entry = map[sandboxId];
|
|
102
|
-
if (!entry) return null;
|
|
103
|
-
if (Date.now() / 1e3 > entry.expiresAt - 60) {
|
|
104
|
-
removeSession(sandboxId);
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return entry;
|
|
108
|
-
}
|
|
109
|
-
function setSession(entry) {
|
|
110
|
-
const map = { ...sessionMapStore.get() };
|
|
111
|
-
map[entry.sandboxId] = entry;
|
|
112
|
-
sessionMapStore.set(map);
|
|
113
|
-
}
|
|
114
|
-
function removeSession(sandboxId) {
|
|
115
|
-
const map = { ...sessionMapStore.get() };
|
|
116
|
-
delete map[sandboxId];
|
|
117
|
-
sessionMapStore.set(map);
|
|
118
|
-
}
|
|
119
|
-
function gcSessions() {
|
|
120
|
-
const now = Date.now() / 1e3;
|
|
121
|
-
const map = sessionMapStore.get();
|
|
122
|
-
const cleaned = {};
|
|
123
|
-
let changed = false;
|
|
124
|
-
for (const [key, entry] of Object.entries(map)) {
|
|
125
|
-
if (entry.expiresAt > now) {
|
|
126
|
-
cleaned[key] = entry;
|
|
127
|
-
} else {
|
|
128
|
-
changed = true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (changed) {
|
|
132
|
-
sessionMapStore.set(cleaned);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// src/blueprints/registry.ts
|
|
137
|
-
var blueprintRegistry = /* @__PURE__ */ new Map();
|
|
138
|
-
function registerBlueprint(bp) {
|
|
139
|
-
blueprintRegistry.set(bp.id, bp);
|
|
140
|
-
}
|
|
141
|
-
function getBlueprint(id) {
|
|
142
|
-
return blueprintRegistry.get(id);
|
|
143
|
-
}
|
|
144
|
-
function getAllBlueprints() {
|
|
145
|
-
return Array.from(blueprintRegistry.values());
|
|
146
|
-
}
|
|
147
|
-
function getBlueprintJobs(blueprintId, category) {
|
|
148
|
-
const bp = blueprintRegistry.get(blueprintId);
|
|
149
|
-
if (!bp) return [];
|
|
150
|
-
return category ? bp.jobs.filter((j) => j.category === category) : bp.jobs;
|
|
151
|
-
}
|
|
152
|
-
function getJobById(blueprintId, jobId) {
|
|
153
|
-
const bp = blueprintRegistry.get(blueprintId);
|
|
154
|
-
return bp?.jobs.find((j) => j.id === jobId);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/hooks/useOperators.ts
|
|
158
|
-
import { useState, useEffect } from "react";
|
|
159
|
-
function readPreferenceValue(result, field) {
|
|
160
|
-
if (Array.isArray(result)) {
|
|
161
|
-
return String(result[field === "ecdsaPublicKey" ? 0 : 1] ?? "");
|
|
162
|
-
}
|
|
163
|
-
if (result && typeof result === "object") {
|
|
164
|
-
return String(result[field] ?? "");
|
|
165
|
-
}
|
|
166
|
-
return "";
|
|
167
|
-
}
|
|
168
|
-
async function verifyCandidatesWithMulticall(client, servicesAddress, blueprintId, candidates) {
|
|
169
|
-
const [registrationResults, preferencesResults] = await Promise.all([
|
|
170
|
-
client.multicall({
|
|
171
|
-
contracts: candidates.map((op) => ({
|
|
172
|
-
address: servicesAddress,
|
|
173
|
-
abi: tangleOperatorsAbi,
|
|
174
|
-
functionName: "isOperatorRegistered",
|
|
175
|
-
args: [blueprintId, op.address]
|
|
176
|
-
}))
|
|
177
|
-
}),
|
|
178
|
-
client.multicall({
|
|
179
|
-
contracts: candidates.map((op) => ({
|
|
180
|
-
address: servicesAddress,
|
|
181
|
-
abi: tangleOperatorsAbi,
|
|
182
|
-
functionName: "getOperatorPreferences",
|
|
183
|
-
args: [blueprintId, op.address]
|
|
184
|
-
}))
|
|
185
|
-
})
|
|
186
|
-
]);
|
|
187
|
-
const hadRegistrationFailure = registrationResults.some((result) => result?.status === "failure");
|
|
188
|
-
const hadPreferenceFailure = preferencesResults.some((result) => result?.status === "failure");
|
|
189
|
-
const active = [];
|
|
190
|
-
candidates.forEach((op, i) => {
|
|
191
|
-
if (registrationResults[i]?.result !== true) return;
|
|
192
|
-
const prefs = preferencesResults[i];
|
|
193
|
-
if (prefs?.status === "success" && prefs.result != null) {
|
|
194
|
-
active.push({
|
|
195
|
-
...op,
|
|
196
|
-
ecdsaPublicKey: readPreferenceValue(prefs.result, "ecdsaPublicKey") || op.ecdsaPublicKey,
|
|
197
|
-
rpcAddress: readPreferenceValue(prefs.result, "rpcAddress") || op.rpcAddress
|
|
198
|
-
});
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
active.push(op);
|
|
202
|
-
});
|
|
203
|
-
if (active.length === 0 && (hadRegistrationFailure || hadPreferenceFailure)) {
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
return active;
|
|
207
|
-
}
|
|
208
|
-
async function verifyCandidatesDirectly(client, servicesAddress, blueprintId, candidates) {
|
|
209
|
-
const results = await Promise.allSettled(
|
|
210
|
-
candidates.map(async (op) => {
|
|
211
|
-
const registration = await client.readContract({
|
|
212
|
-
address: servicesAddress,
|
|
213
|
-
abi: tangleOperatorsAbi,
|
|
214
|
-
functionName: "isOperatorRegistered",
|
|
215
|
-
args: [blueprintId, op.address]
|
|
216
|
-
});
|
|
217
|
-
if (registration !== true) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
try {
|
|
221
|
-
const preferences = await client.readContract({
|
|
222
|
-
address: servicesAddress,
|
|
223
|
-
abi: tangleOperatorsAbi,
|
|
224
|
-
functionName: "getOperatorPreferences",
|
|
225
|
-
args: [blueprintId, op.address]
|
|
226
|
-
});
|
|
227
|
-
return {
|
|
228
|
-
...op,
|
|
229
|
-
ecdsaPublicKey: readPreferenceValue(preferences, "ecdsaPublicKey") || op.ecdsaPublicKey,
|
|
230
|
-
rpcAddress: readPreferenceValue(preferences, "rpcAddress") || op.rpcAddress
|
|
231
|
-
};
|
|
232
|
-
} catch {
|
|
233
|
-
return op;
|
|
234
|
-
}
|
|
235
|
-
})
|
|
236
|
-
);
|
|
237
|
-
const active = results.filter((result) => result.status === "fulfilled").map((result) => result.value).filter((op) => op != null);
|
|
238
|
-
if (active.length > 0) {
|
|
239
|
-
return active;
|
|
240
|
-
}
|
|
241
|
-
const failure = results.find((result) => result.status === "rejected");
|
|
242
|
-
if (failure) {
|
|
243
|
-
throw failure.reason instanceof Error ? failure.reason : new Error(String(failure.reason));
|
|
244
|
-
}
|
|
245
|
-
return [];
|
|
246
|
-
}
|
|
247
|
-
async function discoverOperatorsWithClient(client, servicesAddress, blueprintId) {
|
|
248
|
-
const count = await client.readContract({
|
|
249
|
-
address: servicesAddress,
|
|
250
|
-
abi: tangleOperatorsAbi,
|
|
251
|
-
functionName: "blueprintOperatorCount",
|
|
252
|
-
args: [blueprintId]
|
|
253
|
-
});
|
|
254
|
-
const operatorCount = count;
|
|
255
|
-
if (operatorCount === 0n) {
|
|
256
|
-
return { operators: [], operatorCount };
|
|
257
|
-
}
|
|
258
|
-
const registeredLogs = await client.getLogs({
|
|
259
|
-
address: servicesAddress,
|
|
260
|
-
event: {
|
|
261
|
-
type: "event",
|
|
262
|
-
name: "OperatorRegistered",
|
|
263
|
-
inputs: [
|
|
264
|
-
{ name: "blueprintId", type: "uint64", indexed: true },
|
|
265
|
-
{ name: "operator", type: "address", indexed: true },
|
|
266
|
-
{ name: "ecdsaPublicKey", type: "bytes", indexed: false },
|
|
267
|
-
{ name: "rpcAddress", type: "string", indexed: false }
|
|
268
|
-
]
|
|
269
|
-
},
|
|
270
|
-
args: { blueprintId },
|
|
271
|
-
fromBlock: 0n,
|
|
272
|
-
toBlock: "latest"
|
|
273
|
-
});
|
|
274
|
-
const byAddress = /* @__PURE__ */ new Map();
|
|
275
|
-
for (const log of registeredLogs) {
|
|
276
|
-
const args = log.args ?? {};
|
|
277
|
-
const addr = args.operator;
|
|
278
|
-
if (!addr) continue;
|
|
279
|
-
byAddress.set(addr, {
|
|
280
|
-
address: addr,
|
|
281
|
-
ecdsaPublicKey: String(args.ecdsaPublicKey ?? "0x"),
|
|
282
|
-
rpcAddress: String(args.rpcAddress ?? "")
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
const candidates = Array.from(byAddress.values());
|
|
286
|
-
if (candidates.length === 0) {
|
|
287
|
-
return { operators: [], operatorCount };
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
const active2 = await verifyCandidatesWithMulticall(client, servicesAddress, blueprintId, candidates);
|
|
291
|
-
if (active2 != null) {
|
|
292
|
-
return { operators: active2, operatorCount };
|
|
293
|
-
}
|
|
294
|
-
} catch {
|
|
295
|
-
}
|
|
296
|
-
const active = await verifyCandidatesDirectly(client, servicesAddress, blueprintId, candidates);
|
|
297
|
-
return { operators: active, operatorCount };
|
|
298
|
-
}
|
|
299
|
-
function useOperators(blueprintId) {
|
|
300
|
-
const [operators, setOperators] = useState([]);
|
|
301
|
-
const [operatorCount, setOperatorCount] = useState(0n);
|
|
302
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
303
|
-
const [error, setError] = useState(null);
|
|
304
|
-
useEffect(() => {
|
|
305
|
-
let cancelled = false;
|
|
306
|
-
const addrs = getAddresses();
|
|
307
|
-
async function discover() {
|
|
308
|
-
setIsLoading(true);
|
|
309
|
-
setError(null);
|
|
310
|
-
try {
|
|
311
|
-
const result = await discoverOperatorsWithClient(publicClient, addrs.services, blueprintId);
|
|
312
|
-
if (cancelled) return;
|
|
313
|
-
setOperatorCount(result.operatorCount);
|
|
314
|
-
setOperators(result.operators);
|
|
315
|
-
} catch (err) {
|
|
316
|
-
if (!cancelled) {
|
|
317
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
318
|
-
}
|
|
319
|
-
} finally {
|
|
320
|
-
if (!cancelled) setIsLoading(false);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
discover();
|
|
324
|
-
return () => {
|
|
325
|
-
cancelled = true;
|
|
326
|
-
};
|
|
327
|
-
}, [blueprintId]);
|
|
328
|
-
return { operators, isLoading, error, operatorCount };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// src/hooks/useServiceValidation.ts
|
|
332
|
-
import { useState as useState2, useCallback } from "react";
|
|
333
|
-
function useServiceValidation() {
|
|
334
|
-
const [isValidating, setIsValidating] = useState2(false);
|
|
335
|
-
const [serviceInfo, setServiceInfo] = useState2(null);
|
|
336
|
-
const [error, setError] = useState2(null);
|
|
337
|
-
const validate = useCallback(async (serviceId, userAddress) => {
|
|
338
|
-
setIsValidating(true);
|
|
339
|
-
setError(null);
|
|
340
|
-
setServiceInfo(null);
|
|
341
|
-
const addrs = getAddresses();
|
|
342
|
-
try {
|
|
343
|
-
const [isActiveResult, serviceDataResult, operatorsResult, permittedResult] = await Promise.all([
|
|
344
|
-
publicClient.readContract({
|
|
345
|
-
address: addrs.services,
|
|
346
|
-
abi: tangleServicesAbi,
|
|
347
|
-
functionName: "isServiceActive",
|
|
348
|
-
args: [serviceId]
|
|
349
|
-
}).catch(() => false),
|
|
350
|
-
publicClient.readContract({
|
|
351
|
-
address: addrs.services,
|
|
352
|
-
abi: tangleServicesAbi,
|
|
353
|
-
functionName: "getService",
|
|
354
|
-
args: [serviceId]
|
|
355
|
-
}).catch(() => null),
|
|
356
|
-
publicClient.readContract({
|
|
357
|
-
address: addrs.services,
|
|
358
|
-
abi: tangleServicesAbi,
|
|
359
|
-
functionName: "getServiceOperators",
|
|
360
|
-
args: [serviceId]
|
|
361
|
-
}).catch(() => []),
|
|
362
|
-
userAddress ? publicClient.readContract({
|
|
363
|
-
address: addrs.services,
|
|
364
|
-
abi: tangleServicesAbi,
|
|
365
|
-
functionName: "isPermittedCaller",
|
|
366
|
-
args: [serviceId, userAddress]
|
|
367
|
-
}).catch(() => false) : Promise.resolve(true)
|
|
368
|
-
]);
|
|
369
|
-
const isActive = isActiveResult;
|
|
370
|
-
const serviceData = serviceDataResult;
|
|
371
|
-
const operators = operatorsResult ?? [];
|
|
372
|
-
const permitted = permittedResult;
|
|
373
|
-
if (!serviceData) {
|
|
374
|
-
setError("Service not found");
|
|
375
|
-
setIsValidating(false);
|
|
376
|
-
return null;
|
|
377
|
-
}
|
|
378
|
-
const info = {
|
|
379
|
-
active: isActive,
|
|
380
|
-
blueprintId: serviceData.blueprintId ?? serviceData[0] ?? 0n,
|
|
381
|
-
owner: serviceData.owner ?? serviceData[1] ?? "0x0",
|
|
382
|
-
operatorCount: operators.length,
|
|
383
|
-
operators: [...operators],
|
|
384
|
-
permitted,
|
|
385
|
-
ttl: serviceData.ttl ?? serviceData[3] ?? 0n,
|
|
386
|
-
createdAt: serviceData.createdAt ?? serviceData[2] ?? 0n
|
|
387
|
-
};
|
|
388
|
-
setServiceInfo(info);
|
|
389
|
-
if (!isActive) {
|
|
390
|
-
setError("Service is not active");
|
|
391
|
-
} else if (!permitted && userAddress) {
|
|
392
|
-
setError("You are not a permitted caller for this service");
|
|
393
|
-
}
|
|
394
|
-
setIsValidating(false);
|
|
395
|
-
return info;
|
|
396
|
-
} catch (err) {
|
|
397
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
398
|
-
setError(msg);
|
|
399
|
-
setIsValidating(false);
|
|
400
|
-
return null;
|
|
401
|
-
}
|
|
402
|
-
}, []);
|
|
403
|
-
const reset = useCallback(() => {
|
|
404
|
-
setServiceInfo(null);
|
|
405
|
-
setError(null);
|
|
406
|
-
}, []);
|
|
407
|
-
return { validate, reset, isValidating, serviceInfo, error };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// src/hooks/useAuthenticatedFetch.ts
|
|
411
|
-
import { useCallback as useCallback3 } from "react";
|
|
412
|
-
|
|
413
|
-
// src/hooks/useSessionAuth.ts
|
|
414
|
-
import { useCallback as useCallback2, useState as useState3 } from "react";
|
|
415
|
-
import { useSignMessage } from "wagmi";
|
|
416
|
-
function useSessionAuth({ sandboxId, apiUrl }) {
|
|
417
|
-
const baseUrl = apiUrl ?? getEnvVar("VITE_OPERATOR_API_URL") ?? "http://localhost:9090";
|
|
418
|
-
const { signMessageAsync } = useSignMessage();
|
|
419
|
-
const [isAuthenticating, setIsAuthenticating] = useState3(false);
|
|
420
|
-
const [error, setError] = useState3(null);
|
|
421
|
-
const session = getSession(sandboxId);
|
|
422
|
-
const authenticate = useCallback2(async () => {
|
|
423
|
-
setIsAuthenticating(true);
|
|
424
|
-
setError(null);
|
|
425
|
-
try {
|
|
426
|
-
const challengeRes = await fetch(`${baseUrl}/api/auth/challenge`, {
|
|
427
|
-
method: "POST"
|
|
428
|
-
});
|
|
429
|
-
if (!challengeRes.ok) {
|
|
430
|
-
throw new Error(`Challenge request failed: HTTP ${challengeRes.status}`);
|
|
431
|
-
}
|
|
432
|
-
const challenge = await challengeRes.json();
|
|
433
|
-
const signature = await signMessageAsync({ message: challenge.message });
|
|
434
|
-
const sessionRes = await fetch(`${baseUrl}/api/auth/session`, {
|
|
435
|
-
method: "POST",
|
|
436
|
-
headers: { "Content-Type": "application/json" },
|
|
437
|
-
body: JSON.stringify({
|
|
438
|
-
nonce: challenge.nonce,
|
|
439
|
-
signature
|
|
440
|
-
})
|
|
441
|
-
});
|
|
442
|
-
if (!sessionRes.ok) {
|
|
443
|
-
const body = await sessionRes.json().catch(() => ({}));
|
|
444
|
-
throw new Error(body.error ?? `Session exchange failed: HTTP ${sessionRes.status}`);
|
|
445
|
-
}
|
|
446
|
-
const sessionData = await sessionRes.json();
|
|
447
|
-
const entry = {
|
|
448
|
-
token: sessionData.token,
|
|
449
|
-
address: sessionData.address,
|
|
450
|
-
expiresAt: sessionData.expires_at,
|
|
451
|
-
sandboxId
|
|
452
|
-
};
|
|
453
|
-
setSession(entry);
|
|
454
|
-
return entry;
|
|
455
|
-
} catch (err) {
|
|
456
|
-
const msg = err instanceof Error ? err.message : "Authentication failed";
|
|
457
|
-
setError(msg);
|
|
458
|
-
return null;
|
|
459
|
-
} finally {
|
|
460
|
-
setIsAuthenticating(false);
|
|
461
|
-
}
|
|
462
|
-
}, [baseUrl, sandboxId, signMessageAsync]);
|
|
463
|
-
const logout = useCallback2(() => {
|
|
464
|
-
removeSession(sandboxId);
|
|
465
|
-
}, [sandboxId]);
|
|
466
|
-
return {
|
|
467
|
-
session,
|
|
468
|
-
isAuthenticated: session !== null,
|
|
469
|
-
isAuthenticating,
|
|
470
|
-
error,
|
|
471
|
-
authenticate,
|
|
472
|
-
logout
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/hooks/useAuthenticatedFetch.ts
|
|
477
|
-
function useAuthenticatedFetch({ sandboxId, apiUrl }) {
|
|
478
|
-
const { authenticate } = useSessionAuth({ sandboxId, apiUrl });
|
|
479
|
-
const authFetch = useCallback3(
|
|
480
|
-
async (url, init) => {
|
|
481
|
-
let session = getSession(sandboxId);
|
|
482
|
-
if (!session) {
|
|
483
|
-
session = await authenticate();
|
|
484
|
-
if (!session) {
|
|
485
|
-
throw new Error("Authentication required");
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
const headers = new Headers(init?.headers);
|
|
489
|
-
headers.set("Authorization", `Bearer ${session.token}`);
|
|
490
|
-
const res = await fetch(url, { ...init, headers });
|
|
491
|
-
if (res.status === 401) {
|
|
492
|
-
session = await authenticate();
|
|
493
|
-
if (!session) {
|
|
494
|
-
throw new Error("Re-authentication failed");
|
|
495
|
-
}
|
|
496
|
-
const retryHeaders = new Headers(init?.headers);
|
|
497
|
-
retryHeaders.set("Authorization", `Bearer ${session.token}`);
|
|
498
|
-
return fetch(url, { ...init, headers: retryHeaders });
|
|
499
|
-
}
|
|
500
|
-
return res;
|
|
501
|
-
},
|
|
502
|
-
[sandboxId, authenticate]
|
|
503
|
-
);
|
|
504
|
-
return { authFetch };
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// src/hooks/useProvisionProgress.ts
|
|
508
|
-
import { useCallback as useCallback4, useEffect as useEffect2, useRef, useState as useState4 } from "react";
|
|
509
|
-
var PHASE_LABELS = {
|
|
510
|
-
queued: "Queued",
|
|
511
|
-
image_pull: "Pulling image",
|
|
512
|
-
container_create: "Creating container",
|
|
513
|
-
container_start: "Starting container",
|
|
514
|
-
health_check: "Health check",
|
|
515
|
-
ready: "Ready",
|
|
516
|
-
failed: "Failed"
|
|
517
|
-
};
|
|
518
|
-
function getPhaseLabel(phase) {
|
|
519
|
-
return PHASE_LABELS[phase] ?? phase;
|
|
520
|
-
}
|
|
521
|
-
function isTerminalPhase(phase) {
|
|
522
|
-
return phase === "ready" || phase === "failed";
|
|
523
|
-
}
|
|
524
|
-
var POLL_INTERVAL = 2e3;
|
|
525
|
-
function useProvisionProgress({
|
|
526
|
-
callId,
|
|
527
|
-
apiUrl,
|
|
528
|
-
enabled = true
|
|
529
|
-
}) {
|
|
530
|
-
const [status, setStatus] = useState4(null);
|
|
531
|
-
const [error, setError] = useState4(null);
|
|
532
|
-
const [isPolling, setIsPolling] = useState4(false);
|
|
533
|
-
const intervalRef = useRef(null);
|
|
534
|
-
const baseUrl = apiUrl ?? getEnvVar("VITE_OPERATOR_API_URL") ?? "http://localhost:9090";
|
|
535
|
-
const fetchProgress = useCallback4(async () => {
|
|
536
|
-
if (callId == null) return;
|
|
537
|
-
try {
|
|
538
|
-
const res = await fetch(`${baseUrl}/api/provisions/${callId}`);
|
|
539
|
-
if (res.status === 404) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (!res.ok) {
|
|
543
|
-
throw new Error(`HTTP ${res.status}`);
|
|
544
|
-
}
|
|
545
|
-
const data = await res.json();
|
|
546
|
-
setStatus(data);
|
|
547
|
-
setError(null);
|
|
548
|
-
if (isTerminalPhase(data.phase) && intervalRef.current) {
|
|
549
|
-
clearInterval(intervalRef.current);
|
|
550
|
-
intervalRef.current = null;
|
|
551
|
-
setIsPolling(false);
|
|
552
|
-
}
|
|
553
|
-
} catch (err) {
|
|
554
|
-
setError(err instanceof Error ? err.message : "Failed to fetch provision status");
|
|
555
|
-
}
|
|
556
|
-
}, [callId, baseUrl]);
|
|
557
|
-
useEffect2(() => {
|
|
558
|
-
if (!enabled || callId == null) return;
|
|
559
|
-
setIsPolling(true);
|
|
560
|
-
fetchProgress();
|
|
561
|
-
intervalRef.current = setInterval(fetchProgress, POLL_INTERVAL);
|
|
562
|
-
return () => {
|
|
563
|
-
if (intervalRef.current) {
|
|
564
|
-
clearInterval(intervalRef.current);
|
|
565
|
-
intervalRef.current = null;
|
|
566
|
-
}
|
|
567
|
-
setIsPolling(false);
|
|
568
|
-
};
|
|
569
|
-
}, [enabled, callId, fetchProgress]);
|
|
570
|
-
return {
|
|
571
|
-
status,
|
|
572
|
-
error,
|
|
573
|
-
isPolling,
|
|
574
|
-
phase: status?.phase ?? null,
|
|
575
|
-
progressPct: status?.progress_pct ?? 0,
|
|
576
|
-
sandboxId: status?.sandbox_id ?? null,
|
|
577
|
-
sidecarUrl: status?.sidecar_url ?? null,
|
|
578
|
-
message: status?.message ?? null,
|
|
579
|
-
isReady: status?.phase === "ready",
|
|
580
|
-
isFailed: status?.phase === "failed"
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// src/hooks/useSidecarAuth.ts
|
|
585
|
-
import { useState as useState5, useCallback as useCallback5, useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
586
|
-
function storageKey(resourceId, apiUrl) {
|
|
587
|
-
return `sidecar_session_${resourceId}__${apiUrl}`;
|
|
588
|
-
}
|
|
589
|
-
function loadSession(resourceId, apiUrl) {
|
|
590
|
-
if (typeof window === "undefined") return null;
|
|
591
|
-
try {
|
|
592
|
-
const raw = localStorage.getItem(storageKey(resourceId, apiUrl));
|
|
593
|
-
if (!raw) return null;
|
|
594
|
-
const data = JSON.parse(raw);
|
|
595
|
-
if (data.expiresAt * 1e3 - Date.now() < 6e4) {
|
|
596
|
-
localStorage.removeItem(storageKey(resourceId, apiUrl));
|
|
597
|
-
return null;
|
|
598
|
-
}
|
|
599
|
-
return data;
|
|
600
|
-
} catch {
|
|
601
|
-
return null;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
function saveSession(resourceId, apiUrl, token, expiresAt) {
|
|
605
|
-
if (typeof window === "undefined") return;
|
|
606
|
-
try {
|
|
607
|
-
localStorage.setItem(storageKey(resourceId, apiUrl), JSON.stringify({ token, expiresAt }));
|
|
608
|
-
} catch {
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
function clearSession(resourceId, apiUrl) {
|
|
612
|
-
if (typeof window === "undefined") return;
|
|
613
|
-
localStorage.removeItem(storageKey(resourceId, apiUrl));
|
|
614
|
-
}
|
|
615
|
-
function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
|
|
616
|
-
const cached = loadSession(resourceId, apiUrl);
|
|
617
|
-
const [token, setToken] = useState5(cached?.token ?? null);
|
|
618
|
-
const [expiresAt, setExpiresAt] = useState5(cached?.expiresAt ?? 0);
|
|
619
|
-
const [isAuthenticating, setIsAuthenticating] = useState5(false);
|
|
620
|
-
const [error, setError] = useState5(null);
|
|
621
|
-
const refreshTimerRef = useRef2();
|
|
622
|
-
const clearCachedToken = useCallback5(() => {
|
|
623
|
-
setToken(null);
|
|
624
|
-
setExpiresAt(0);
|
|
625
|
-
clearSession(resourceId, apiUrl);
|
|
626
|
-
}, [resourceId, apiUrl]);
|
|
627
|
-
const authenticate = useCallback5(async () => {
|
|
628
|
-
if (!apiUrl) return null;
|
|
629
|
-
setIsAuthenticating(true);
|
|
630
|
-
setError(null);
|
|
631
|
-
try {
|
|
632
|
-
const challengeRes = await fetch(`${apiUrl}/api/auth/challenge`, {
|
|
633
|
-
method: "POST"
|
|
634
|
-
});
|
|
635
|
-
if (!challengeRes.ok) {
|
|
636
|
-
throw new Error(`Challenge failed: ${challengeRes.status}`);
|
|
637
|
-
}
|
|
638
|
-
const { nonce, message } = await challengeRes.json();
|
|
639
|
-
const signature = await signMessage(message);
|
|
640
|
-
const sessionRes = await fetch(`${apiUrl}/api/auth/session`, {
|
|
641
|
-
method: "POST",
|
|
642
|
-
headers: { "Content-Type": "application/json" },
|
|
643
|
-
body: JSON.stringify({ nonce, signature })
|
|
644
|
-
});
|
|
645
|
-
if (!sessionRes.ok) {
|
|
646
|
-
const text = await sessionRes.text();
|
|
647
|
-
throw new Error(text || `Session exchange failed: ${sessionRes.status}`);
|
|
648
|
-
}
|
|
649
|
-
const { token: newToken, expires_at } = await sessionRes.json();
|
|
650
|
-
setToken(newToken);
|
|
651
|
-
setExpiresAt(expires_at);
|
|
652
|
-
saveSession(resourceId, apiUrl, newToken, expires_at);
|
|
653
|
-
return newToken;
|
|
654
|
-
} catch (err) {
|
|
655
|
-
setError(err instanceof Error ? err.message : "Authentication failed");
|
|
656
|
-
clearCachedToken();
|
|
657
|
-
return null;
|
|
658
|
-
} finally {
|
|
659
|
-
setIsAuthenticating(false);
|
|
660
|
-
}
|
|
661
|
-
}, [resourceId, apiUrl, signMessage, clearCachedToken]);
|
|
662
|
-
useEffect3(() => {
|
|
663
|
-
if (refreshTimerRef.current) {
|
|
664
|
-
clearTimeout(refreshTimerRef.current);
|
|
665
|
-
}
|
|
666
|
-
if (!token || !expiresAt) return;
|
|
667
|
-
const msUntilRefresh = (expiresAt - 300) * 1e3 - Date.now();
|
|
668
|
-
if (msUntilRefresh <= 0) {
|
|
669
|
-
clearCachedToken();
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
refreshTimerRef.current = setTimeout(() => {
|
|
673
|
-
authenticate().catch(() => {
|
|
674
|
-
clearCachedToken();
|
|
675
|
-
});
|
|
676
|
-
}, msUntilRefresh);
|
|
677
|
-
return () => {
|
|
678
|
-
if (refreshTimerRef.current) {
|
|
679
|
-
clearTimeout(refreshTimerRef.current);
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
}, [token, expiresAt, authenticate, clearCachedToken]);
|
|
683
|
-
return {
|
|
684
|
-
token,
|
|
685
|
-
isAuthenticated: token !== null,
|
|
686
|
-
isAuthenticating,
|
|
687
|
-
authenticate,
|
|
688
|
-
clearCachedToken,
|
|
689
|
-
error
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// src/hooks/useWagmiSidecarAuth.ts
|
|
694
|
-
import { useSignMessage as useSignMessage2 } from "wagmi";
|
|
695
|
-
function useWagmiSidecarAuth(resourceId, apiUrl) {
|
|
696
|
-
const { signMessageAsync } = useSignMessage2();
|
|
697
|
-
return useSidecarAuth({
|
|
698
|
-
resourceId,
|
|
699
|
-
apiUrl,
|
|
700
|
-
signMessage: (msg) => signMessageAsync({ message: msg })
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// src/hooks/useWalletEthBalance.ts
|
|
705
|
-
import { useEffect as useEffect4, useState as useState6 } from "react";
|
|
706
|
-
var WEI_PER_ETH = 1000000000000000000n;
|
|
707
|
-
var DISPLAY_SCALE = 1000n;
|
|
708
|
-
function formatEthBalance(wei) {
|
|
709
|
-
const whole = wei / WEI_PER_ETH;
|
|
710
|
-
const fraction = wei % WEI_PER_ETH * DISPLAY_SCALE / WEI_PER_ETH;
|
|
711
|
-
return `${whole.toString()}.${fraction.toString().padStart(3, "0")}`;
|
|
712
|
-
}
|
|
713
|
-
function useWalletEthBalance({
|
|
714
|
-
address,
|
|
715
|
-
refreshKey,
|
|
716
|
-
readBalance,
|
|
717
|
-
pollMs = 15e3,
|
|
718
|
-
onError
|
|
719
|
-
}) {
|
|
720
|
-
const [balance, setBalance] = useState6(null);
|
|
721
|
-
const [hasError, setHasError] = useState6(false);
|
|
722
|
-
useEffect4(() => {
|
|
723
|
-
if (!address) {
|
|
724
|
-
setBalance(null);
|
|
725
|
-
setHasError(false);
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
let cancelled = false;
|
|
729
|
-
const fetchBalance = () => {
|
|
730
|
-
readBalance(address).then((wei) => {
|
|
731
|
-
if (cancelled) return;
|
|
732
|
-
setBalance(formatEthBalance(wei));
|
|
733
|
-
setHasError(false);
|
|
734
|
-
}).catch((error) => {
|
|
735
|
-
if (cancelled) return;
|
|
736
|
-
setHasError(true);
|
|
737
|
-
onError?.(error);
|
|
738
|
-
});
|
|
739
|
-
};
|
|
740
|
-
fetchBalance();
|
|
741
|
-
const interval = setInterval(fetchBalance, pollMs);
|
|
742
|
-
return () => {
|
|
743
|
-
cancelled = true;
|
|
744
|
-
clearInterval(interval);
|
|
745
|
-
};
|
|
746
|
-
}, [address, refreshKey, readBalance, pollMs, onError]);
|
|
747
|
-
return { balance, hasError };
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// src/hooks/useServiceRequest.ts
|
|
751
|
-
import { useWriteContract } from "wagmi";
|
|
752
|
-
import { useCallback as useCallback6 } from "react";
|
|
753
|
-
function useServiceRequest() {
|
|
754
|
-
const { writeContractAsync, isPending, error, data: txHash } = useWriteContract();
|
|
755
|
-
const requestService = useCallback6(
|
|
756
|
-
async (params) => {
|
|
757
|
-
const addresses = getAddresses();
|
|
758
|
-
return writeContractAsync({
|
|
759
|
-
address: addresses.services,
|
|
760
|
-
abi: tangleServicesAbi,
|
|
761
|
-
functionName: "requestService",
|
|
762
|
-
args: [
|
|
763
|
-
params.blueprintId,
|
|
764
|
-
params.operators,
|
|
765
|
-
params.config,
|
|
766
|
-
params.permittedCallers,
|
|
767
|
-
params.ttl,
|
|
768
|
-
params.paymentToken,
|
|
769
|
-
params.paymentAmount
|
|
770
|
-
]
|
|
771
|
-
});
|
|
772
|
-
},
|
|
773
|
-
[writeContractAsync]
|
|
774
|
-
);
|
|
775
|
-
return { requestService, isPending, error, txHash };
|
|
776
|
-
}
|
|
777
|
-
export {
|
|
778
|
-
BlueprintHostHero,
|
|
779
|
-
BlueprintHostPanel,
|
|
780
|
-
DEFAULT_THEME,
|
|
781
|
-
addTx,
|
|
782
|
-
allTangleChains,
|
|
783
|
-
bpThemeTokens,
|
|
784
|
-
buildCanonicalBlueprintSlug,
|
|
785
|
-
canPublisherClaimSlug,
|
|
786
|
-
clearTxs,
|
|
787
|
-
cn,
|
|
788
|
-
configureNetworks,
|
|
789
|
-
createTangleLocalChain,
|
|
790
|
-
createTangleTransports,
|
|
791
|
-
defaultConnectKitOptions,
|
|
792
|
-
deriveBlueprintRequestedSlug,
|
|
793
|
-
deserializeWithBigInt,
|
|
794
|
-
discoverOperatorsWithClient,
|
|
795
|
-
encodeJobArgs,
|
|
796
|
-
formatCost,
|
|
797
|
-
gcSessions,
|
|
798
|
-
getAddresses,
|
|
799
|
-
getAllBlueprints,
|
|
800
|
-
getBlueprint,
|
|
801
|
-
getBlueprintExperienceTierLabel,
|
|
802
|
-
getBlueprintJobs,
|
|
803
|
-
getBlueprintPath,
|
|
804
|
-
getBlueprintPublisherVerificationLabel,
|
|
805
|
-
getBlueprintServicePath,
|
|
806
|
-
getBlueprintSlugPolicyLabel,
|
|
807
|
-
getBlueprintSurfaceLabel,
|
|
808
|
-
getExternalAppTrustLabel,
|
|
809
|
-
getInfra,
|
|
810
|
-
getJobById,
|
|
811
|
-
getNetworks,
|
|
812
|
-
getPhaseLabel,
|
|
813
|
-
getPublicClient,
|
|
814
|
-
getSession,
|
|
815
|
-
getTangleWalletChains,
|
|
816
|
-
infraStore,
|
|
817
|
-
isTerminalPhase,
|
|
818
|
-
isTrustedExternalAppHost,
|
|
819
|
-
isVerifiedBlueprintPublisher,
|
|
820
|
-
kTheme,
|
|
821
|
-
mainnet,
|
|
822
|
-
pendingCount,
|
|
823
|
-
persistedAtom,
|
|
824
|
-
publicClient,
|
|
825
|
-
publicClientStore,
|
|
826
|
-
registerBlueprint,
|
|
827
|
-
removeSession,
|
|
828
|
-
resolveBlueprintAppView,
|
|
829
|
-
resolveOperatorRpc,
|
|
830
|
-
resolveRpcUrl,
|
|
831
|
-
rpcUrl,
|
|
832
|
-
sanitizeBlueprintSlugPart,
|
|
833
|
-
sanitizeSelectedChainId,
|
|
834
|
-
selectedChainIdStore,
|
|
835
|
-
serializeWithBigInt,
|
|
836
|
-
sessionMapStore,
|
|
837
|
-
setSession,
|
|
838
|
-
solvePoW,
|
|
839
|
-
tangleJobsAbi,
|
|
840
|
-
tangleLocal,
|
|
841
|
-
tangleMainnet,
|
|
842
|
-
tangleOperatorsAbi,
|
|
843
|
-
tangleServicesAbi,
|
|
844
|
-
tangleTestnet,
|
|
845
|
-
tangleWalletChains,
|
|
846
|
-
themeIsDark,
|
|
847
|
-
themeStore,
|
|
848
|
-
toBlueprintAppEntry,
|
|
849
|
-
toggleTheme,
|
|
850
|
-
txListStore,
|
|
851
|
-
updateInfra,
|
|
852
|
-
updateTx,
|
|
853
|
-
useAuthenticatedFetch,
|
|
854
|
-
useJobForm,
|
|
855
|
-
useJobPrice,
|
|
856
|
-
useJobPrices,
|
|
857
|
-
useOperators,
|
|
858
|
-
useProvisionProgress,
|
|
859
|
-
useQuotes,
|
|
860
|
-
useRegistrationCommand,
|
|
861
|
-
useServiceRequest,
|
|
862
|
-
useServiceValidation,
|
|
863
|
-
useSessionAuth,
|
|
864
|
-
useSidecarAuth,
|
|
865
|
-
useSubmitJob,
|
|
866
|
-
useThemeValue,
|
|
867
|
-
useWagmiSidecarAuth,
|
|
868
|
-
useWalletEthBalance
|
|
869
|
-
};
|
|
870
|
-
//# sourceMappingURL=index.js.map
|