@unicitylabs/sphere-sdk 0.1.2 → 0.1.4

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.
@@ -0,0 +1,1112 @@
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
+ // impl/browser/ipfs.ts
31
+ var ipfs_exports = {};
32
+ __export(ipfs_exports, {
33
+ IpfsStorageProvider: () => IpfsStorageProvider,
34
+ createIpfsStorageProvider: () => createIpfsStorageProvider
35
+ });
36
+ module.exports = __toCommonJS(ipfs_exports);
37
+
38
+ // constants.ts
39
+ var STORAGE_PREFIX = "sphere_";
40
+ var STORAGE_KEYS = {
41
+ /** Encrypted BIP39 mnemonic */
42
+ MNEMONIC: `${STORAGE_PREFIX}mnemonic`,
43
+ /** Encrypted master private key */
44
+ MASTER_KEY: `${STORAGE_PREFIX}master_key`,
45
+ /** BIP32 chain code */
46
+ CHAIN_CODE: `${STORAGE_PREFIX}chain_code`,
47
+ /** HD derivation path (full path like m/44'/0'/0'/0/0) */
48
+ DERIVATION_PATH: `${STORAGE_PREFIX}derivation_path`,
49
+ /** Base derivation path (like m/44'/0'/0' without chain/index) */
50
+ BASE_PATH: `${STORAGE_PREFIX}base_path`,
51
+ /** Derivation mode: bip32, wif_hmac, legacy_hmac */
52
+ DERIVATION_MODE: `${STORAGE_PREFIX}derivation_mode`,
53
+ /** Wallet source: mnemonic, file, unknown */
54
+ WALLET_SOURCE: `${STORAGE_PREFIX}wallet_source`,
55
+ /** Wallet existence flag */
56
+ WALLET_EXISTS: `${STORAGE_PREFIX}wallet_exists`,
57
+ /** Registered nametag (legacy - single address) */
58
+ NAMETAG: `${STORAGE_PREFIX}nametag`,
59
+ /** Current active address index */
60
+ CURRENT_ADDRESS_INDEX: `${STORAGE_PREFIX}current_address_index`,
61
+ /** Address nametags map (JSON: { "0": "alice", "1": "bob" }) */
62
+ ADDRESS_NAMETAGS: `${STORAGE_PREFIX}address_nametags`,
63
+ /** Token data */
64
+ TOKENS: `${STORAGE_PREFIX}tokens`,
65
+ /** Pending transfers */
66
+ PENDING_TRANSFERS: `${STORAGE_PREFIX}pending_transfers`,
67
+ /** Transfer outbox */
68
+ OUTBOX: `${STORAGE_PREFIX}outbox`,
69
+ /** Conversations */
70
+ CONVERSATIONS: `${STORAGE_PREFIX}conversations`,
71
+ /** Messages */
72
+ MESSAGES: `${STORAGE_PREFIX}messages`,
73
+ /** Transaction history */
74
+ TRANSACTION_HISTORY: `${STORAGE_PREFIX}transaction_history`,
75
+ /** Archived tokens (spent token history) */
76
+ ARCHIVED_TOKENS: `${STORAGE_PREFIX}archived_tokens`,
77
+ /** Tombstones (records of deleted/spent tokens) */
78
+ TOMBSTONES: `${STORAGE_PREFIX}tombstones`,
79
+ /** Forked tokens (alternative histories) */
80
+ FORKED_TOKENS: `${STORAGE_PREFIX}forked_tokens`
81
+ };
82
+ var DEFAULT_IPFS_GATEWAYS = [
83
+ "https://ipfs.unicity.network",
84
+ "https://dweb.link",
85
+ "https://ipfs.io"
86
+ ];
87
+ var DEFAULT_IPFS_BOOTSTRAP_PEERS = [
88
+ "/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh",
89
+ "/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6",
90
+ "/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1",
91
+ "/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv"
92
+ ];
93
+ var DEFAULT_BASE_PATH = "m/44'/0'/0'";
94
+ var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
95
+
96
+ // node_modules/@noble/hashes/utils.js
97
+ function isBytes(a) {
98
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
99
+ }
100
+ function anumber(n, title = "") {
101
+ if (!Number.isSafeInteger(n) || n < 0) {
102
+ const prefix = title && `"${title}" `;
103
+ throw new Error(`${prefix}expected integer >= 0, got ${n}`);
104
+ }
105
+ }
106
+ function abytes(value, length, title = "") {
107
+ const bytes = isBytes(value);
108
+ const len = value?.length;
109
+ const needsLen = length !== void 0;
110
+ if (!bytes || needsLen && len !== length) {
111
+ const prefix = title && `"${title}" `;
112
+ const ofLen = needsLen ? ` of length ${length}` : "";
113
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
114
+ throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
115
+ }
116
+ return value;
117
+ }
118
+ function ahash(h) {
119
+ if (typeof h !== "function" || typeof h.create !== "function")
120
+ throw new Error("Hash must wrapped by utils.createHasher");
121
+ anumber(h.outputLen);
122
+ anumber(h.blockLen);
123
+ }
124
+ function aexists(instance, checkFinished = true) {
125
+ if (instance.destroyed)
126
+ throw new Error("Hash instance has been destroyed");
127
+ if (checkFinished && instance.finished)
128
+ throw new Error("Hash#digest() has already been called");
129
+ }
130
+ function aoutput(out, instance) {
131
+ abytes(out, void 0, "digestInto() output");
132
+ const min = instance.outputLen;
133
+ if (out.length < min) {
134
+ throw new Error('"digestInto() output" expected to be of length >=' + min);
135
+ }
136
+ }
137
+ function clean(...arrays) {
138
+ for (let i = 0; i < arrays.length; i++) {
139
+ arrays[i].fill(0);
140
+ }
141
+ }
142
+ function createView(arr) {
143
+ return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
144
+ }
145
+ function rotr(word, shift) {
146
+ return word << 32 - shift | word >>> shift;
147
+ }
148
+ function createHasher(hashCons, info = {}) {
149
+ const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
150
+ const tmp = hashCons(void 0);
151
+ hashC.outputLen = tmp.outputLen;
152
+ hashC.blockLen = tmp.blockLen;
153
+ hashC.create = (opts) => hashCons(opts);
154
+ Object.assign(hashC, info);
155
+ return Object.freeze(hashC);
156
+ }
157
+ var oidNist = (suffix) => ({
158
+ oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
159
+ });
160
+
161
+ // node_modules/@noble/hashes/hmac.js
162
+ var _HMAC = class {
163
+ oHash;
164
+ iHash;
165
+ blockLen;
166
+ outputLen;
167
+ finished = false;
168
+ destroyed = false;
169
+ constructor(hash, key) {
170
+ ahash(hash);
171
+ abytes(key, void 0, "key");
172
+ this.iHash = hash.create();
173
+ if (typeof this.iHash.update !== "function")
174
+ throw new Error("Expected instance of class which extends utils.Hash");
175
+ this.blockLen = this.iHash.blockLen;
176
+ this.outputLen = this.iHash.outputLen;
177
+ const blockLen = this.blockLen;
178
+ const pad = new Uint8Array(blockLen);
179
+ pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
180
+ for (let i = 0; i < pad.length; i++)
181
+ pad[i] ^= 54;
182
+ this.iHash.update(pad);
183
+ this.oHash = hash.create();
184
+ for (let i = 0; i < pad.length; i++)
185
+ pad[i] ^= 54 ^ 92;
186
+ this.oHash.update(pad);
187
+ clean(pad);
188
+ }
189
+ update(buf) {
190
+ aexists(this);
191
+ this.iHash.update(buf);
192
+ return this;
193
+ }
194
+ digestInto(out) {
195
+ aexists(this);
196
+ abytes(out, this.outputLen, "output");
197
+ this.finished = true;
198
+ this.iHash.digestInto(out);
199
+ this.oHash.update(out);
200
+ this.oHash.digestInto(out);
201
+ this.destroy();
202
+ }
203
+ digest() {
204
+ const out = new Uint8Array(this.oHash.outputLen);
205
+ this.digestInto(out);
206
+ return out;
207
+ }
208
+ _cloneInto(to) {
209
+ to ||= Object.create(Object.getPrototypeOf(this), {});
210
+ const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
211
+ to = to;
212
+ to.finished = finished;
213
+ to.destroyed = destroyed;
214
+ to.blockLen = blockLen;
215
+ to.outputLen = outputLen;
216
+ to.oHash = oHash._cloneInto(to.oHash);
217
+ to.iHash = iHash._cloneInto(to.iHash);
218
+ return to;
219
+ }
220
+ clone() {
221
+ return this._cloneInto();
222
+ }
223
+ destroy() {
224
+ this.destroyed = true;
225
+ this.oHash.destroy();
226
+ this.iHash.destroy();
227
+ }
228
+ };
229
+ var hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest();
230
+ hmac.create = (hash, key) => new _HMAC(hash, key);
231
+
232
+ // node_modules/@noble/hashes/hkdf.js
233
+ function extract(hash, ikm, salt) {
234
+ ahash(hash);
235
+ if (salt === void 0)
236
+ salt = new Uint8Array(hash.outputLen);
237
+ return hmac(hash, salt, ikm);
238
+ }
239
+ var HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);
240
+ var EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
241
+ function expand(hash, prk, info, length = 32) {
242
+ ahash(hash);
243
+ anumber(length, "length");
244
+ const olen = hash.outputLen;
245
+ if (length > 255 * olen)
246
+ throw new Error("Length must be <= 255*HashLen");
247
+ const blocks = Math.ceil(length / olen);
248
+ if (info === void 0)
249
+ info = EMPTY_BUFFER;
250
+ else
251
+ abytes(info, void 0, "info");
252
+ const okm = new Uint8Array(blocks * olen);
253
+ const HMAC = hmac.create(hash, prk);
254
+ const HMACTmp = HMAC._cloneInto();
255
+ const T = new Uint8Array(HMAC.outputLen);
256
+ for (let counter = 0; counter < blocks; counter++) {
257
+ HKDF_COUNTER[0] = counter + 1;
258
+ HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T).update(info).update(HKDF_COUNTER).digestInto(T);
259
+ okm.set(T, olen * counter);
260
+ HMAC._cloneInto(HMACTmp);
261
+ }
262
+ HMAC.destroy();
263
+ HMACTmp.destroy();
264
+ clean(T, HKDF_COUNTER);
265
+ return okm.slice(0, length);
266
+ }
267
+ var hkdf = (hash, ikm, salt, info, length) => expand(hash, extract(hash, ikm, salt), info, length);
268
+
269
+ // node_modules/@noble/hashes/_md.js
270
+ function Chi(a, b, c) {
271
+ return a & b ^ ~a & c;
272
+ }
273
+ function Maj(a, b, c) {
274
+ return a & b ^ a & c ^ b & c;
275
+ }
276
+ var HashMD = class {
277
+ blockLen;
278
+ outputLen;
279
+ padOffset;
280
+ isLE;
281
+ // For partial updates less than block size
282
+ buffer;
283
+ view;
284
+ finished = false;
285
+ length = 0;
286
+ pos = 0;
287
+ destroyed = false;
288
+ constructor(blockLen, outputLen, padOffset, isLE) {
289
+ this.blockLen = blockLen;
290
+ this.outputLen = outputLen;
291
+ this.padOffset = padOffset;
292
+ this.isLE = isLE;
293
+ this.buffer = new Uint8Array(blockLen);
294
+ this.view = createView(this.buffer);
295
+ }
296
+ update(data) {
297
+ aexists(this);
298
+ abytes(data);
299
+ const { view, buffer, blockLen } = this;
300
+ const len = data.length;
301
+ for (let pos = 0; pos < len; ) {
302
+ const take = Math.min(blockLen - this.pos, len - pos);
303
+ if (take === blockLen) {
304
+ const dataView = createView(data);
305
+ for (; blockLen <= len - pos; pos += blockLen)
306
+ this.process(dataView, pos);
307
+ continue;
308
+ }
309
+ buffer.set(data.subarray(pos, pos + take), this.pos);
310
+ this.pos += take;
311
+ pos += take;
312
+ if (this.pos === blockLen) {
313
+ this.process(view, 0);
314
+ this.pos = 0;
315
+ }
316
+ }
317
+ this.length += data.length;
318
+ this.roundClean();
319
+ return this;
320
+ }
321
+ digestInto(out) {
322
+ aexists(this);
323
+ aoutput(out, this);
324
+ this.finished = true;
325
+ const { buffer, view, blockLen, isLE } = this;
326
+ let { pos } = this;
327
+ buffer[pos++] = 128;
328
+ clean(this.buffer.subarray(pos));
329
+ if (this.padOffset > blockLen - pos) {
330
+ this.process(view, 0);
331
+ pos = 0;
332
+ }
333
+ for (let i = pos; i < blockLen; i++)
334
+ buffer[i] = 0;
335
+ view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
336
+ this.process(view, 0);
337
+ const oview = createView(out);
338
+ const len = this.outputLen;
339
+ if (len % 4)
340
+ throw new Error("_sha2: outputLen must be aligned to 32bit");
341
+ const outLen = len / 4;
342
+ const state = this.get();
343
+ if (outLen > state.length)
344
+ throw new Error("_sha2: outputLen bigger than state");
345
+ for (let i = 0; i < outLen; i++)
346
+ oview.setUint32(4 * i, state[i], isLE);
347
+ }
348
+ digest() {
349
+ const { buffer, outputLen } = this;
350
+ this.digestInto(buffer);
351
+ const res = buffer.slice(0, outputLen);
352
+ this.destroy();
353
+ return res;
354
+ }
355
+ _cloneInto(to) {
356
+ to ||= new this.constructor();
357
+ to.set(...this.get());
358
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
359
+ to.destroyed = destroyed;
360
+ to.finished = finished;
361
+ to.length = length;
362
+ to.pos = pos;
363
+ if (length % blockLen)
364
+ to.buffer.set(buffer);
365
+ return to;
366
+ }
367
+ clone() {
368
+ return this._cloneInto();
369
+ }
370
+ };
371
+ var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
372
+ 1779033703,
373
+ 3144134277,
374
+ 1013904242,
375
+ 2773480762,
376
+ 1359893119,
377
+ 2600822924,
378
+ 528734635,
379
+ 1541459225
380
+ ]);
381
+
382
+ // node_modules/@noble/hashes/sha2.js
383
+ var SHA256_K = /* @__PURE__ */ Uint32Array.from([
384
+ 1116352408,
385
+ 1899447441,
386
+ 3049323471,
387
+ 3921009573,
388
+ 961987163,
389
+ 1508970993,
390
+ 2453635748,
391
+ 2870763221,
392
+ 3624381080,
393
+ 310598401,
394
+ 607225278,
395
+ 1426881987,
396
+ 1925078388,
397
+ 2162078206,
398
+ 2614888103,
399
+ 3248222580,
400
+ 3835390401,
401
+ 4022224774,
402
+ 264347078,
403
+ 604807628,
404
+ 770255983,
405
+ 1249150122,
406
+ 1555081692,
407
+ 1996064986,
408
+ 2554220882,
409
+ 2821834349,
410
+ 2952996808,
411
+ 3210313671,
412
+ 3336571891,
413
+ 3584528711,
414
+ 113926993,
415
+ 338241895,
416
+ 666307205,
417
+ 773529912,
418
+ 1294757372,
419
+ 1396182291,
420
+ 1695183700,
421
+ 1986661051,
422
+ 2177026350,
423
+ 2456956037,
424
+ 2730485921,
425
+ 2820302411,
426
+ 3259730800,
427
+ 3345764771,
428
+ 3516065817,
429
+ 3600352804,
430
+ 4094571909,
431
+ 275423344,
432
+ 430227734,
433
+ 506948616,
434
+ 659060556,
435
+ 883997877,
436
+ 958139571,
437
+ 1322822218,
438
+ 1537002063,
439
+ 1747873779,
440
+ 1955562222,
441
+ 2024104815,
442
+ 2227730452,
443
+ 2361852424,
444
+ 2428436474,
445
+ 2756734187,
446
+ 3204031479,
447
+ 3329325298
448
+ ]);
449
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
450
+ var SHA2_32B = class extends HashMD {
451
+ constructor(outputLen) {
452
+ super(64, outputLen, 8, false);
453
+ }
454
+ get() {
455
+ const { A, B, C, D, E, F, G, H } = this;
456
+ return [A, B, C, D, E, F, G, H];
457
+ }
458
+ // prettier-ignore
459
+ set(A, B, C, D, E, F, G, H) {
460
+ this.A = A | 0;
461
+ this.B = B | 0;
462
+ this.C = C | 0;
463
+ this.D = D | 0;
464
+ this.E = E | 0;
465
+ this.F = F | 0;
466
+ this.G = G | 0;
467
+ this.H = H | 0;
468
+ }
469
+ process(view, offset) {
470
+ for (let i = 0; i < 16; i++, offset += 4)
471
+ SHA256_W[i] = view.getUint32(offset, false);
472
+ for (let i = 16; i < 64; i++) {
473
+ const W15 = SHA256_W[i - 15];
474
+ const W2 = SHA256_W[i - 2];
475
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
476
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
477
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
478
+ }
479
+ let { A, B, C, D, E, F, G, H } = this;
480
+ for (let i = 0; i < 64; i++) {
481
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
482
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
483
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
484
+ const T2 = sigma0 + Maj(A, B, C) | 0;
485
+ H = G;
486
+ G = F;
487
+ F = E;
488
+ E = D + T1 | 0;
489
+ D = C;
490
+ C = B;
491
+ B = A;
492
+ A = T1 + T2 | 0;
493
+ }
494
+ A = A + this.A | 0;
495
+ B = B + this.B | 0;
496
+ C = C + this.C | 0;
497
+ D = D + this.D | 0;
498
+ E = E + this.E | 0;
499
+ F = F + this.F | 0;
500
+ G = G + this.G | 0;
501
+ H = H + this.H | 0;
502
+ this.set(A, B, C, D, E, F, G, H);
503
+ }
504
+ roundClean() {
505
+ clean(SHA256_W);
506
+ }
507
+ destroy() {
508
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
509
+ clean(this.buffer);
510
+ }
511
+ };
512
+ var _SHA256 = class extends SHA2_32B {
513
+ // We cannot use array here since array allows indexing by variable
514
+ // which means optimizer/compiler cannot use registers.
515
+ A = SHA256_IV[0] | 0;
516
+ B = SHA256_IV[1] | 0;
517
+ C = SHA256_IV[2] | 0;
518
+ D = SHA256_IV[3] | 0;
519
+ E = SHA256_IV[4] | 0;
520
+ F = SHA256_IV[5] | 0;
521
+ G = SHA256_IV[6] | 0;
522
+ H = SHA256_IV[7] | 0;
523
+ constructor() {
524
+ super(32);
525
+ }
526
+ };
527
+ var sha256 = /* @__PURE__ */ createHasher(
528
+ () => new _SHA256(),
529
+ /* @__PURE__ */ oidNist(1)
530
+ );
531
+
532
+ // impl/browser/storage/IpfsStorageProvider.ts
533
+ var heliaModule = null;
534
+ var heliaJsonModule = null;
535
+ var libp2pBootstrapModule = null;
536
+ var libp2pCryptoModule = null;
537
+ var libp2pPeerIdModule = null;
538
+ var multiformatsCidModule = null;
539
+ async function loadHeliaModules() {
540
+ if (!heliaModule) {
541
+ [
542
+ heliaModule,
543
+ heliaJsonModule,
544
+ libp2pBootstrapModule,
545
+ libp2pCryptoModule,
546
+ libp2pPeerIdModule,
547
+ multiformatsCidModule
548
+ ] = await Promise.all([
549
+ import("helia"),
550
+ import("@helia/json"),
551
+ import("@libp2p/bootstrap"),
552
+ import("@libp2p/crypto/keys"),
553
+ import("@libp2p/peer-id"),
554
+ import("multiformats/cid")
555
+ ]);
556
+ }
557
+ return {
558
+ createHelia: heliaModule.createHelia,
559
+ json: heliaJsonModule.json,
560
+ bootstrap: libp2pBootstrapModule.bootstrap,
561
+ generateKeyPairFromSeed: libp2pCryptoModule.generateKeyPairFromSeed,
562
+ peerIdFromPrivateKey: libp2pPeerIdModule.peerIdFromPrivateKey,
563
+ CID: multiformatsCidModule.CID
564
+ };
565
+ }
566
+ var HKDF_INFO = new TextEncoder().encode("ipfs-storage-key");
567
+ var IpfsStorageProvider = class {
568
+ id = "ipfs";
569
+ name = "IPFS Storage";
570
+ type = "p2p";
571
+ description = "Decentralized storage via IPFS/IPNS";
572
+ config;
573
+ identity = null;
574
+ status = "disconnected";
575
+ ipnsName = null;
576
+ lastCid = null;
577
+ eventCallbacks = /* @__PURE__ */ new Set();
578
+ // Helia instance for browser-based IPFS
579
+ helia = null;
580
+ heliaJson = null;
581
+ ipnsKeyPair = null;
582
+ /** Get the last published CID */
583
+ getLastCid() {
584
+ return this.lastCid;
585
+ }
586
+ // Local cache for faster loads
587
+ localCache = null;
588
+ cacheTimestamp = 0;
589
+ constructor(config) {
590
+ this.config = {
591
+ gateways: config?.gateways ?? [...DEFAULT_IPFS_GATEWAYS],
592
+ bootstrapPeers: config?.bootstrapPeers ?? [...DEFAULT_IPFS_BOOTSTRAP_PEERS],
593
+ enableIpns: config?.enableIpns ?? true,
594
+ ipnsTimeout: config?.ipnsTimeout ?? 3e4,
595
+ fetchTimeout: config?.fetchTimeout ?? 15e3,
596
+ debug: config?.debug ?? false
597
+ };
598
+ }
599
+ // ===========================================================================
600
+ // BaseProvider Implementation
601
+ // ===========================================================================
602
+ async connect() {
603
+ if (this.status === "connected") return;
604
+ this.status = "connecting";
605
+ try {
606
+ await this.testGatewayConnectivity();
607
+ await this.initializeHelia();
608
+ this.status = "connected";
609
+ this.log("Connected to IPFS gateways and Helia initialized");
610
+ } catch (error) {
611
+ this.status = "error";
612
+ throw new Error(`IPFS connection failed: ${error}`);
613
+ }
614
+ }
615
+ /**
616
+ * Initialize Helia browser IPFS node
617
+ */
618
+ async initializeHelia() {
619
+ if (this.helia) return;
620
+ try {
621
+ this.log("Initializing Helia with bootstrap peers...");
622
+ const { createHelia, json, bootstrap } = await loadHeliaModules();
623
+ this.helia = await createHelia({
624
+ libp2p: {
625
+ peerDiscovery: [
626
+ bootstrap({ list: this.config.bootstrapPeers })
627
+ ],
628
+ connectionManager: {
629
+ maxConnections: 10
630
+ }
631
+ }
632
+ });
633
+ this.heliaJson = json(this.helia);
634
+ const peerId = this.helia.libp2p.peerId.toString();
635
+ this.log("Helia initialized, browser peer ID:", peerId.slice(0, 20) + "...");
636
+ setTimeout(() => {
637
+ const connections = this.helia?.libp2p.getConnections() || [];
638
+ this.log(`Active Helia connections: ${connections.length}`);
639
+ }, 3e3);
640
+ } catch (error) {
641
+ this.log("Helia initialization failed (will use HTTP only):", error);
642
+ }
643
+ }
644
+ async disconnect() {
645
+ if (this.helia) {
646
+ try {
647
+ await this.helia.stop();
648
+ } catch (error) {
649
+ this.log("Error stopping Helia:", error);
650
+ }
651
+ this.helia = null;
652
+ this.heliaJson = null;
653
+ }
654
+ this.status = "disconnected";
655
+ this.localCache = null;
656
+ this.ipnsKeyPair = null;
657
+ this.log("Disconnected from IPFS");
658
+ }
659
+ isConnected() {
660
+ return this.status === "connected";
661
+ }
662
+ getStatus() {
663
+ return this.status;
664
+ }
665
+ // ===========================================================================
666
+ // TokenStorageProvider Implementation
667
+ // ===========================================================================
668
+ async setIdentity(identity) {
669
+ this.identity = identity;
670
+ try {
671
+ const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadHeliaModules();
672
+ const walletSecret = this.hexToBytes(identity.privateKey);
673
+ const derivedKey = hkdf(sha256, walletSecret, void 0, HKDF_INFO, 32);
674
+ this.ipnsKeyPair = await generateKeyPairFromSeed("Ed25519", derivedKey);
675
+ const peerId = peerIdFromPrivateKey(this.ipnsKeyPair);
676
+ this.ipnsName = peerId.toString();
677
+ this.log("Identity set, IPNS name:", this.ipnsName);
678
+ } catch {
679
+ this.ipnsName = identity.ipnsName ?? this.deriveIpnsNameSimple(identity.privateKey);
680
+ this.log("Identity set with fallback IPNS name:", this.ipnsName);
681
+ }
682
+ }
683
+ async initialize() {
684
+ if (!this.identity) {
685
+ throw new Error("Identity must be set before initialization");
686
+ }
687
+ try {
688
+ await this.connect();
689
+ return true;
690
+ } catch {
691
+ return false;
692
+ }
693
+ }
694
+ async shutdown() {
695
+ await this.disconnect();
696
+ }
697
+ async save(data) {
698
+ this.ensureReady();
699
+ this.emitEvent({ type: "storage:saving", timestamp: Date.now() });
700
+ try {
701
+ const dataToSave = {
702
+ ...data,
703
+ _meta: {
704
+ ...data._meta,
705
+ updatedAt: Date.now(),
706
+ ipnsName: this.ipnsName ?? void 0
707
+ }
708
+ };
709
+ const cid = await this.publishToGateways(dataToSave);
710
+ if (this.config.enableIpns && this.ipnsName) {
711
+ await this.publishIpns(cid);
712
+ }
713
+ this.localCache = dataToSave;
714
+ this.cacheTimestamp = Date.now();
715
+ this.lastCid = cid;
716
+ this.emitEvent({ type: "storage:saved", timestamp: Date.now(), data: { cid } });
717
+ return {
718
+ success: true,
719
+ cid,
720
+ timestamp: Date.now()
721
+ };
722
+ } catch (error) {
723
+ const errorMsg = error instanceof Error ? error.message : String(error);
724
+ this.emitEvent({ type: "storage:error", timestamp: Date.now(), error: errorMsg });
725
+ return {
726
+ success: false,
727
+ error: errorMsg,
728
+ timestamp: Date.now()
729
+ };
730
+ }
731
+ }
732
+ async load(identifier) {
733
+ this.ensureReady();
734
+ this.emitEvent({ type: "storage:loading", timestamp: Date.now() });
735
+ try {
736
+ const cacheAge = Date.now() - this.cacheTimestamp;
737
+ if (this.localCache && cacheAge < 6e4) {
738
+ this.log("Returning cached data");
739
+ return {
740
+ success: true,
741
+ data: this.localCache,
742
+ source: "cache",
743
+ timestamp: Date.now()
744
+ };
745
+ }
746
+ let cid = identifier ?? null;
747
+ if (!cid && this.config.enableIpns && this.ipnsName) {
748
+ cid = await this.resolveIpns(this.ipnsName);
749
+ }
750
+ if (!cid) {
751
+ return {
752
+ success: true,
753
+ data: void 0,
754
+ source: "remote",
755
+ timestamp: Date.now()
756
+ };
757
+ }
758
+ const data = await this.fetchFromGateways(cid);
759
+ this.localCache = data;
760
+ this.cacheTimestamp = Date.now();
761
+ this.lastCid = cid;
762
+ this.emitEvent({ type: "storage:loaded", timestamp: Date.now() });
763
+ return {
764
+ success: true,
765
+ data,
766
+ source: "remote",
767
+ timestamp: Date.now()
768
+ };
769
+ } catch (error) {
770
+ const errorMsg = error instanceof Error ? error.message : String(error);
771
+ this.emitEvent({ type: "storage:error", timestamp: Date.now(), error: errorMsg });
772
+ if (this.localCache) {
773
+ return {
774
+ success: true,
775
+ data: this.localCache,
776
+ source: "cache",
777
+ timestamp: Date.now()
778
+ };
779
+ }
780
+ return {
781
+ success: false,
782
+ error: errorMsg,
783
+ source: "remote",
784
+ timestamp: Date.now()
785
+ };
786
+ }
787
+ }
788
+ async sync(localData) {
789
+ this.ensureReady();
790
+ this.emitEvent({ type: "sync:started", timestamp: Date.now() });
791
+ try {
792
+ const remoteResult = await this.load();
793
+ const remoteData = remoteResult.data;
794
+ if (!remoteData) {
795
+ await this.save(localData);
796
+ this.emitEvent({ type: "sync:completed", timestamp: Date.now() });
797
+ return {
798
+ success: true,
799
+ merged: localData,
800
+ added: 0,
801
+ removed: 0,
802
+ conflicts: 0
803
+ };
804
+ }
805
+ const mergeResult = this.mergeData(localData, remoteData);
806
+ await this.save(mergeResult.merged);
807
+ this.emitEvent({ type: "sync:completed", timestamp: Date.now() });
808
+ return {
809
+ success: true,
810
+ merged: mergeResult.merged,
811
+ added: mergeResult.added,
812
+ removed: mergeResult.removed,
813
+ conflicts: mergeResult.conflicts
814
+ };
815
+ } catch (error) {
816
+ const errorMsg = error instanceof Error ? error.message : String(error);
817
+ this.emitEvent({ type: "sync:error", timestamp: Date.now(), error: errorMsg });
818
+ return {
819
+ success: false,
820
+ added: 0,
821
+ removed: 0,
822
+ conflicts: 0,
823
+ error: errorMsg
824
+ };
825
+ }
826
+ }
827
+ async exists() {
828
+ if (!this.ipnsName) return false;
829
+ try {
830
+ const cid = await this.resolveIpns(this.ipnsName);
831
+ return cid !== null;
832
+ } catch {
833
+ return false;
834
+ }
835
+ }
836
+ async clear() {
837
+ const emptyData = {
838
+ _meta: {
839
+ version: 0,
840
+ address: this.identity?.l1Address ?? "",
841
+ formatVersion: "2.0",
842
+ updatedAt: Date.now()
843
+ },
844
+ _tombstones: []
845
+ };
846
+ const result = await this.save(emptyData);
847
+ return result.success;
848
+ }
849
+ onEvent(callback) {
850
+ this.eventCallbacks.add(callback);
851
+ return () => this.eventCallbacks.delete(callback);
852
+ }
853
+ // ===========================================================================
854
+ // Private: IPFS Operations
855
+ // ===========================================================================
856
+ async testGatewayConnectivity() {
857
+ const gateway = this.config.gateways[0];
858
+ const controller = new AbortController();
859
+ const timeout = setTimeout(() => controller.abort(), 5e3);
860
+ try {
861
+ const response = await fetch(`${gateway}/api/v0/version`, {
862
+ method: "POST",
863
+ signal: controller.signal
864
+ });
865
+ if (!response.ok) throw new Error("Gateway not responding");
866
+ } finally {
867
+ clearTimeout(timeout);
868
+ }
869
+ }
870
+ async publishToGateways(data) {
871
+ const content = JSON.stringify(data);
872
+ const blob = new Blob([content], { type: "application/json" });
873
+ const promises = [];
874
+ for (const gateway of this.config.gateways) {
875
+ promises.push(this.publishToGateway(gateway, blob));
876
+ }
877
+ if (this.heliaJson) {
878
+ promises.push(this.publishToHelia(data));
879
+ }
880
+ try {
881
+ const cid = await Promise.any(promises);
882
+ this.log("Published to IPFS, CID:", cid);
883
+ return cid;
884
+ } catch {
885
+ throw new Error("All publish attempts failed");
886
+ }
887
+ }
888
+ /**
889
+ * Publish data via Helia (browser DHT)
890
+ */
891
+ async publishToHelia(data) {
892
+ if (!this.heliaJson) {
893
+ throw new Error("Helia not initialized");
894
+ }
895
+ const cid = await this.heliaJson.add(data);
896
+ this.log("Published via Helia, CID:", cid.toString());
897
+ return cid.toString();
898
+ }
899
+ async publishToGateway(gateway, blob) {
900
+ const formData = new FormData();
901
+ formData.append("file", blob);
902
+ const controller = new AbortController();
903
+ const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);
904
+ try {
905
+ const response = await fetch(`${gateway}/api/v0/add?pin=true`, {
906
+ method: "POST",
907
+ body: formData,
908
+ signal: controller.signal
909
+ });
910
+ if (!response.ok) {
911
+ throw new Error(`Gateway ${gateway} returned ${response.status}`);
912
+ }
913
+ const result = await response.json();
914
+ return result.Hash;
915
+ } finally {
916
+ clearTimeout(timeout);
917
+ }
918
+ }
919
+ async publishIpns(cid) {
920
+ if (!this.identity) return;
921
+ const promises = this.config.gateways.map(
922
+ (gateway) => this.publishIpnsToGateway(gateway, cid).catch(() => null)
923
+ );
924
+ await Promise.allSettled(promises);
925
+ this.log("Published IPNS:", this.ipnsName, "->", cid);
926
+ }
927
+ async publishIpnsToGateway(gateway, cid) {
928
+ const controller = new AbortController();
929
+ const timeout = setTimeout(() => controller.abort(), this.config.ipnsTimeout);
930
+ try {
931
+ const response = await fetch(
932
+ `${gateway}/api/v0/name/publish?arg=${cid}&key=${this.ipnsName}`,
933
+ {
934
+ method: "POST",
935
+ signal: controller.signal
936
+ }
937
+ );
938
+ if (!response.ok) {
939
+ throw new Error(`IPNS publish failed: ${response.status}`);
940
+ }
941
+ } finally {
942
+ clearTimeout(timeout);
943
+ }
944
+ }
945
+ async resolveIpns(name) {
946
+ for (const gateway of this.config.gateways) {
947
+ try {
948
+ return await this.resolveIpnsFromGateway(gateway, name);
949
+ } catch {
950
+ continue;
951
+ }
952
+ }
953
+ return null;
954
+ }
955
+ async resolveIpnsFromGateway(gateway, name) {
956
+ const controller = new AbortController();
957
+ const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);
958
+ try {
959
+ const response = await fetch(`${gateway}/api/v0/name/resolve?arg=${name}`, {
960
+ method: "POST",
961
+ signal: controller.signal
962
+ });
963
+ if (!response.ok) {
964
+ throw new Error(`IPNS resolve failed: ${response.status}`);
965
+ }
966
+ const result = await response.json();
967
+ return result.Path.replace("/ipfs/", "");
968
+ } finally {
969
+ clearTimeout(timeout);
970
+ }
971
+ }
972
+ async fetchFromGateways(cid) {
973
+ const promises = [];
974
+ for (const gateway of this.config.gateways) {
975
+ promises.push(this.fetchFromGateway(gateway, cid));
976
+ }
977
+ if (this.heliaJson) {
978
+ promises.push(this.fetchFromHelia(cid));
979
+ }
980
+ return Promise.any(promises);
981
+ }
982
+ /**
983
+ * Fetch content via Helia (browser DHT)
984
+ */
985
+ async fetchFromHelia(cidString) {
986
+ if (!this.heliaJson) {
987
+ throw new Error("Helia not initialized");
988
+ }
989
+ const { CID } = await loadHeliaModules();
990
+ const cid = CID.parse(cidString);
991
+ const data = await this.heliaJson.get(cid);
992
+ this.log("Fetched via Helia, CID:", cidString);
993
+ return data;
994
+ }
995
+ async fetchFromGateway(gateway, cid) {
996
+ const controller = new AbortController();
997
+ const timeout = setTimeout(() => controller.abort(), this.config.fetchTimeout);
998
+ try {
999
+ const response = await fetch(`${gateway}/ipfs/${cid}`, {
1000
+ signal: controller.signal
1001
+ });
1002
+ if (!response.ok) {
1003
+ throw new Error(`Fetch failed: ${response.status}`);
1004
+ }
1005
+ return response.json();
1006
+ } finally {
1007
+ clearTimeout(timeout);
1008
+ }
1009
+ }
1010
+ // ===========================================================================
1011
+ // Private: Merge Logic
1012
+ // ===========================================================================
1013
+ mergeData(local, remote) {
1014
+ const localVersion = local._meta?.version ?? 0;
1015
+ const remoteVersion = remote._meta?.version ?? 0;
1016
+ const baseMeta = remoteVersion > localVersion ? remote._meta : local._meta;
1017
+ const tombstones = /* @__PURE__ */ new Map();
1018
+ for (const t of local._tombstones ?? []) {
1019
+ tombstones.set(t.tokenId, t);
1020
+ }
1021
+ for (const t of remote._tombstones ?? []) {
1022
+ const existing = tombstones.get(t.tokenId);
1023
+ if (!existing || t.timestamp > existing.timestamp) {
1024
+ tombstones.set(t.tokenId, t);
1025
+ }
1026
+ }
1027
+ const merged = {
1028
+ _meta: {
1029
+ ...baseMeta,
1030
+ version: Math.max(localVersion, remoteVersion) + 1,
1031
+ updatedAt: Date.now()
1032
+ },
1033
+ _tombstones: Array.from(tombstones.values())
1034
+ };
1035
+ let added = 0;
1036
+ let conflicts = 0;
1037
+ const processedKeys = /* @__PURE__ */ new Set();
1038
+ for (const key of Object.keys(local)) {
1039
+ if (!key.startsWith("_") || key === "_meta" || key === "_tombstones") continue;
1040
+ processedKeys.add(key);
1041
+ const tokenId = key.slice(1);
1042
+ if (tombstones.has(tokenId)) continue;
1043
+ const localToken = local[key];
1044
+ const remoteToken = remote[key];
1045
+ if (remoteToken) {
1046
+ conflicts++;
1047
+ merged[key] = localToken;
1048
+ } else {
1049
+ merged[key] = localToken;
1050
+ }
1051
+ }
1052
+ for (const key of Object.keys(remote)) {
1053
+ if (!key.startsWith("_") || key === "_meta" || key === "_tombstones") continue;
1054
+ if (processedKeys.has(key)) continue;
1055
+ const tokenId = key.slice(1);
1056
+ if (tombstones.has(tokenId)) continue;
1057
+ merged[key] = remote[key];
1058
+ added++;
1059
+ }
1060
+ return { merged, added, removed: 0, conflicts };
1061
+ }
1062
+ // ===========================================================================
1063
+ // Private: Helpers
1064
+ // ===========================================================================
1065
+ ensureReady() {
1066
+ if (this.status !== "connected") {
1067
+ throw new Error("IpfsStorageProvider not connected");
1068
+ }
1069
+ if (!this.identity) {
1070
+ throw new Error("Identity not set");
1071
+ }
1072
+ }
1073
+ /**
1074
+ * Simple IPNS name derivation (fallback when libp2p is unavailable)
1075
+ */
1076
+ deriveIpnsNameSimple(privateKey) {
1077
+ return `12D3KooW${privateKey.slice(0, 40)}`;
1078
+ }
1079
+ /**
1080
+ * Convert hex string to Uint8Array
1081
+ */
1082
+ hexToBytes(hex) {
1083
+ const bytes = new Uint8Array(hex.length / 2);
1084
+ for (let i = 0; i < bytes.length; i++) {
1085
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
1086
+ }
1087
+ return bytes;
1088
+ }
1089
+ emitEvent(event) {
1090
+ for (const callback of this.eventCallbacks) {
1091
+ try {
1092
+ callback(event);
1093
+ } catch (error) {
1094
+ console.error("[IpfsStorageProvider] Event callback error:", error);
1095
+ }
1096
+ }
1097
+ }
1098
+ log(...args) {
1099
+ if (this.config.debug) {
1100
+ console.log("[IpfsStorageProvider]", ...args);
1101
+ }
1102
+ }
1103
+ };
1104
+ function createIpfsStorageProvider(config) {
1105
+ return new IpfsStorageProvider(config);
1106
+ }
1107
+ /*! Bundled license information:
1108
+
1109
+ @noble/hashes/utils.js:
1110
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
1111
+ */
1112
+ //# sourceMappingURL=ipfs.cjs.map