@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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/index.d.mts +404 -0
- package/dist/index.d.ts +404 -0
- package/dist/index.js +389 -0
- package/dist/index.mjs +358 -0
- package/package.json +64 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/use-private-swap.ts +268 -0
- package/src/hooks/use-sip.ts +188 -0
- package/src/hooks/use-stealth-address.ts +184 -0
- package/src/hooks/use-viewing-key.ts +190 -0
- package/src/index.ts +10 -0
- package/src/providers/sip-provider.tsx +50 -0
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
|
+
});
|