@sip-protocol/react 0.1.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/dist/index.js ADDED
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ SIPProvider: () => SIPProvider,
34
+ usePrivateSwap: () => usePrivateSwap,
35
+ useSIP: () => useSIP,
36
+ useStealthAddress: () => useStealthAddress,
37
+ useViewingKey: () => useViewingKey
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+
41
+ // src/providers/sip-provider.tsx
42
+ var import_react = __toESM(require("react"));
43
+ var import_sdk = require("@sip-protocol/sdk");
44
+ var SIPContext = (0, import_react.createContext)(void 0);
45
+ function SIPProvider({ config, children }) {
46
+ const client = (0, import_react.useMemo)(() => new import_sdk.SIP(config), [config]);
47
+ const value = (0, import_react.useMemo)(() => ({ client, config }), [client, config]);
48
+ return /* @__PURE__ */ import_react.default.createElement(SIPContext.Provider, { value }, children);
49
+ }
50
+ function useSIPContext() {
51
+ const context = (0, import_react.useContext)(SIPContext);
52
+ if (!context) {
53
+ throw new Error("useSIPContext must be used within SIPProvider");
54
+ }
55
+ return context;
56
+ }
57
+
58
+ // src/hooks/use-sip.ts
59
+ var import_react2 = require("react");
60
+ var import_sdk2 = require("@sip-protocol/sdk");
61
+ function useSIP() {
62
+ const [standaloneClient, setStandaloneClient] = (0, import_react2.useState)(null);
63
+ const [standaloneReady, setStandaloneReady] = (0, import_react2.useState)(false);
64
+ const [standaloneError, setStandaloneError] = (0, import_react2.useState)(null);
65
+ let providerContext;
66
+ try {
67
+ providerContext = useSIPContext();
68
+ } catch {
69
+ providerContext = null;
70
+ }
71
+ const standaloneInitialize = (0, import_react2.useCallback)(async (config) => {
72
+ if (standaloneClient && standaloneReady) {
73
+ console.warn("SIP client already initialized. Call will be ignored.");
74
+ return;
75
+ }
76
+ try {
77
+ setStandaloneError(null);
78
+ setStandaloneReady(false);
79
+ const newClient = new import_sdk2.SIP(config);
80
+ setStandaloneClient(newClient);
81
+ setStandaloneReady(true);
82
+ } catch (err) {
83
+ const error = err instanceof Error ? err : new Error(String(err));
84
+ setStandaloneError(error);
85
+ setStandaloneReady(false);
86
+ throw error;
87
+ }
88
+ }, [standaloneClient, standaloneReady]);
89
+ if (providerContext) {
90
+ return {
91
+ client: providerContext.client,
92
+ isReady: true,
93
+ // Provider always provides ready client
94
+ error: null,
95
+ // Provider throws on error, doesn't expose it
96
+ initialize: async () => {
97
+ console.warn("initialize() called but SIPProvider is already providing a client. This call will be ignored.");
98
+ }
99
+ };
100
+ }
101
+ return {
102
+ client: standaloneClient,
103
+ isReady: standaloneReady,
104
+ error: standaloneError,
105
+ initialize: standaloneInitialize
106
+ };
107
+ }
108
+
109
+ // src/hooks/use-stealth-address.ts
110
+ var import_react3 = require("react");
111
+ var import_sdk3 = require("@sip-protocol/sdk");
112
+ function useStealthAddress(chain) {
113
+ const [metaAddress, setMetaAddress] = (0, import_react3.useState)(null);
114
+ const [stealthAddress, setStealthAddress] = (0, import_react3.useState)(null);
115
+ const [isGenerating, setIsGenerating] = (0, import_react3.useState)(false);
116
+ (0, import_react3.useEffect)(() => {
117
+ let cancelled = false;
118
+ setIsGenerating(true);
119
+ const timer = setTimeout(() => {
120
+ if (cancelled) return;
121
+ try {
122
+ const isEd25519 = (0, import_sdk3.isEd25519Chain)(chain);
123
+ const metaAddressData = isEd25519 ? (0, import_sdk3.generateEd25519StealthMetaAddress)(chain) : (0, import_sdk3.generateStealthMetaAddress)(chain);
124
+ const encoded = (0, import_sdk3.encodeStealthMetaAddress)(metaAddressData.metaAddress);
125
+ if (cancelled) return;
126
+ setMetaAddress(encoded);
127
+ const stealthData = isEd25519 ? (0, import_sdk3.generateEd25519StealthAddress)(metaAddressData.metaAddress) : (0, import_sdk3.generateStealthAddress)(metaAddressData.metaAddress);
128
+ if (cancelled) return;
129
+ setStealthAddress(stealthData.stealthAddress.address);
130
+ } catch (error) {
131
+ console.error("Failed to generate stealth addresses:", error);
132
+ if (cancelled) return;
133
+ setMetaAddress(null);
134
+ setStealthAddress(null);
135
+ } finally {
136
+ if (!cancelled) {
137
+ setIsGenerating(false);
138
+ }
139
+ }
140
+ }, 0);
141
+ return () => {
142
+ cancelled = true;
143
+ clearTimeout(timer);
144
+ };
145
+ }, [chain]);
146
+ const regenerate = (0, import_react3.useCallback)(() => {
147
+ if (!metaAddress) {
148
+ return;
149
+ }
150
+ setIsGenerating(true);
151
+ setTimeout(() => {
152
+ try {
153
+ const parts = metaAddress.split(":");
154
+ if (parts.length < 4) {
155
+ throw new Error("Invalid meta-address format");
156
+ }
157
+ const [, chainId, spendingKey, viewingKey] = parts;
158
+ const metaAddressObj = {
159
+ chain: chainId,
160
+ spendingKey: spendingKey.startsWith("0x") ? spendingKey : `0x${spendingKey}`,
161
+ viewingKey: viewingKey.startsWith("0x") ? viewingKey : `0x${viewingKey}`
162
+ };
163
+ const isEd25519 = (0, import_sdk3.isEd25519Chain)(chain);
164
+ const stealthData = isEd25519 ? (0, import_sdk3.generateEd25519StealthAddress)(metaAddressObj) : (0, import_sdk3.generateStealthAddress)(metaAddressObj);
165
+ setStealthAddress(stealthData.stealthAddress.address);
166
+ } catch (error) {
167
+ console.error("Failed to regenerate stealth address:", error);
168
+ } finally {
169
+ setIsGenerating(false);
170
+ }
171
+ }, 0);
172
+ }, [metaAddress, chain]);
173
+ const copyToClipboard = (0, import_react3.useCallback)(async () => {
174
+ if (!stealthAddress) {
175
+ return;
176
+ }
177
+ try {
178
+ await navigator.clipboard.writeText(stealthAddress);
179
+ } catch (error) {
180
+ console.error("Failed to copy to clipboard:", error);
181
+ const textArea = document.createElement("textarea");
182
+ textArea.value = stealthAddress;
183
+ textArea.style.position = "fixed";
184
+ textArea.style.left = "-999999px";
185
+ document.body.appendChild(textArea);
186
+ textArea.select();
187
+ try {
188
+ document.execCommand("copy");
189
+ } catch (err) {
190
+ console.error("Fallback copy failed:", err);
191
+ } finally {
192
+ document.body.removeChild(textArea);
193
+ }
194
+ }
195
+ }, [stealthAddress]);
196
+ return {
197
+ metaAddress,
198
+ stealthAddress,
199
+ isGenerating,
200
+ regenerate,
201
+ copyToClipboard
202
+ };
203
+ }
204
+
205
+ // src/hooks/use-private-swap.ts
206
+ var import_react4 = require("react");
207
+ function usePrivateSwap() {
208
+ const { client: sip } = useSIP();
209
+ const [quote, setQuote] = (0, import_react4.useState)(null);
210
+ const [status, setStatus] = (0, import_react4.useState)("idle");
211
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
212
+ const [error, setError] = (0, import_react4.useState)(null);
213
+ const fetchQuote = (0, import_react4.useCallback)(async (params) => {
214
+ if (!sip) {
215
+ throw new Error("SIP client not initialized. Wrap your app with SIPProvider or call initialize().");
216
+ }
217
+ try {
218
+ setStatus("fetching_quote");
219
+ setIsLoading(true);
220
+ setError(null);
221
+ const intentParams = {
222
+ input: {
223
+ asset: {
224
+ chain: params.inputChain,
225
+ symbol: params.inputToken,
226
+ address: null,
227
+ decimals: 9
228
+ // Default, should be configurable
229
+ },
230
+ amount: BigInt(params.inputAmount)
231
+ },
232
+ output: {
233
+ asset: {
234
+ chain: params.outputChain,
235
+ symbol: params.outputToken,
236
+ address: null,
237
+ decimals: 18
238
+ // Default, should be configurable
239
+ },
240
+ minAmount: 0n,
241
+ maxSlippage: params.maxSlippage ?? 0.01
242
+ },
243
+ privacy: params.privacyLevel ?? "shielded"
244
+ };
245
+ const quotes = await sip.getQuotes(intentParams);
246
+ if (quotes.length === 0) {
247
+ throw new Error("No quotes available");
248
+ }
249
+ setQuote(quotes[0]);
250
+ setStatus("idle");
251
+ } catch (err) {
252
+ setError(err instanceof Error ? err : new Error("Failed to fetch quote"));
253
+ setStatus("failed");
254
+ throw err;
255
+ } finally {
256
+ setIsLoading(false);
257
+ }
258
+ }, [sip]);
259
+ const swap = (0, import_react4.useCallback)(async (params) => {
260
+ if (!sip) {
261
+ throw new Error("SIP client not initialized. Wrap your app with SIPProvider or call initialize().");
262
+ }
263
+ try {
264
+ setStatus("pending");
265
+ setIsLoading(true);
266
+ setError(null);
267
+ const intentParams = {
268
+ input: {
269
+ asset: {
270
+ chain: params.input.chain,
271
+ symbol: params.input.token,
272
+ address: null,
273
+ decimals: 9
274
+ // Default, should be configurable
275
+ },
276
+ amount: params.input.amount
277
+ },
278
+ output: {
279
+ asset: {
280
+ chain: params.output.chain,
281
+ symbol: params.output.token,
282
+ address: null,
283
+ decimals: 18
284
+ // Default, should be configurable
285
+ },
286
+ minAmount: params.output.minAmount,
287
+ maxSlippage: params.maxSlippage ?? 0.01
288
+ },
289
+ privacy: params.privacyLevel
290
+ };
291
+ const intent = await sip.createIntent(intentParams);
292
+ let swapQuote = quote;
293
+ if (!swapQuote) {
294
+ const quotes = await sip.getQuotes(intentParams);
295
+ if (quotes.length === 0) {
296
+ throw new Error("No quotes available");
297
+ }
298
+ swapQuote = quotes[0];
299
+ }
300
+ setStatus("confirming");
301
+ const result = await sip.execute(intent, swapQuote);
302
+ setStatus("completed");
303
+ return {
304
+ txHash: result.txHash,
305
+ status: result.status,
306
+ outputAmount: result.outputAmount,
307
+ intentId: result.intentId
308
+ };
309
+ } catch (err) {
310
+ setError(err instanceof Error ? err : new Error("Swap failed"));
311
+ setStatus("failed");
312
+ throw err;
313
+ } finally {
314
+ setIsLoading(false);
315
+ }
316
+ }, [sip, quote]);
317
+ const reset = (0, import_react4.useCallback)(() => {
318
+ setQuote(null);
319
+ setStatus("idle");
320
+ setIsLoading(false);
321
+ setError(null);
322
+ }, []);
323
+ return {
324
+ quote,
325
+ fetchQuote,
326
+ swap,
327
+ status,
328
+ isLoading,
329
+ error,
330
+ reset
331
+ };
332
+ }
333
+
334
+ // src/hooks/use-viewing-key.ts
335
+ var import_react5 = require("react");
336
+ var import_sdk4 = require("@sip-protocol/sdk");
337
+ function useViewingKey() {
338
+ const [viewingKey, setViewingKey] = (0, import_react5.useState)(null);
339
+ const [sharedWith, setSharedWith] = (0, import_react5.useState)([]);
340
+ const generate = (0, import_react5.useCallback)((path) => {
341
+ const key = (0, import_sdk4.generateViewingKey)(path);
342
+ setViewingKey(key);
343
+ setSharedWith([]);
344
+ return key;
345
+ }, []);
346
+ const decrypt = (0, import_react5.useCallback)(
347
+ async (encrypted) => {
348
+ if (!viewingKey) {
349
+ throw new Error("No viewing key available. Call generate() first.");
350
+ }
351
+ return (0, import_sdk4.decryptWithViewing)(encrypted, viewingKey);
352
+ },
353
+ [viewingKey]
354
+ );
355
+ const share = (0, import_react5.useCallback)(
356
+ async (auditorId) => {
357
+ if (!viewingKey) {
358
+ throw new Error("No viewing key available. Call generate() first.");
359
+ }
360
+ const shareEntry = {
361
+ auditorId,
362
+ viewingKeyHash: viewingKey.hash,
363
+ sharedAt: Date.now()
364
+ };
365
+ setSharedWith((prev) => [...prev, shareEntry]);
366
+ },
367
+ [viewingKey]
368
+ );
369
+ return {
370
+ /** Current viewing key (null if not generated) */
371
+ viewingKey,
372
+ /** List of auditors who have been given access */
373
+ sharedWith,
374
+ /** Generate a new viewing key */
375
+ generate,
376
+ /** Decrypt encrypted transaction data */
377
+ decrypt,
378
+ /** Share viewing key with an auditor */
379
+ share
380
+ };
381
+ }
382
+ // Annotate the CommonJS export names for ESM import in node:
383
+ 0 && (module.exports = {
384
+ SIPProvider,
385
+ usePrivateSwap,
386
+ useSIP,
387
+ useStealthAddress,
388
+ useViewingKey
389
+ });