@unicitylabs/sphere-sdk 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -15
- package/dist/core/index.cjs +829 -236
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +332 -210
- package/dist/core/index.d.ts +332 -210
- package/dist/core/index.js +829 -236
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +1224 -339
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +1221 -342
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +471 -37
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +470 -36
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +1238 -345
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +142 -57
- package/dist/impl/nodejs/index.d.ts +142 -57
- package/dist/impl/nodejs/index.js +1245 -348
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +871 -235
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1395 -503
- package/dist/index.d.ts +1395 -503
- package/dist/index.js +862 -235
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
|
@@ -1,6 +1,131 @@
|
|
|
1
1
|
// impl/nodejs/storage/FileStorageProvider.ts
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
// constants.ts
|
|
6
|
+
var STORAGE_KEYS_GLOBAL = {
|
|
7
|
+
/** Encrypted BIP39 mnemonic */
|
|
8
|
+
MNEMONIC: "mnemonic",
|
|
9
|
+
/** Encrypted master private key */
|
|
10
|
+
MASTER_KEY: "master_key",
|
|
11
|
+
/** BIP32 chain code */
|
|
12
|
+
CHAIN_CODE: "chain_code",
|
|
13
|
+
/** HD derivation path (full path like m/44'/0'/0'/0/0) */
|
|
14
|
+
DERIVATION_PATH: "derivation_path",
|
|
15
|
+
/** Base derivation path (like m/44'/0'/0' without chain/index) */
|
|
16
|
+
BASE_PATH: "base_path",
|
|
17
|
+
/** Derivation mode: bip32, wif_hmac, legacy_hmac */
|
|
18
|
+
DERIVATION_MODE: "derivation_mode",
|
|
19
|
+
/** Wallet source: mnemonic, file, unknown */
|
|
20
|
+
WALLET_SOURCE: "wallet_source",
|
|
21
|
+
/** Wallet existence flag */
|
|
22
|
+
WALLET_EXISTS: "wallet_exists",
|
|
23
|
+
/** Current active address index */
|
|
24
|
+
CURRENT_ADDRESS_INDEX: "current_address_index",
|
|
25
|
+
/** Index of address nametags (JSON: { "0": "alice", "1": "bob" }) - for discovery */
|
|
26
|
+
ADDRESS_NAMETAGS: "address_nametags"
|
|
27
|
+
};
|
|
28
|
+
var STORAGE_KEYS_ADDRESS = {
|
|
29
|
+
/** Pending transfers for this address */
|
|
30
|
+
PENDING_TRANSFERS: "pending_transfers",
|
|
31
|
+
/** Transfer outbox for this address */
|
|
32
|
+
OUTBOX: "outbox",
|
|
33
|
+
/** Conversations for this address */
|
|
34
|
+
CONVERSATIONS: "conversations",
|
|
35
|
+
/** Messages for this address */
|
|
36
|
+
MESSAGES: "messages",
|
|
37
|
+
/** Transaction history for this address */
|
|
38
|
+
TRANSACTION_HISTORY: "transaction_history"
|
|
39
|
+
};
|
|
40
|
+
var STORAGE_KEYS = {
|
|
41
|
+
...STORAGE_KEYS_GLOBAL,
|
|
42
|
+
...STORAGE_KEYS_ADDRESS
|
|
43
|
+
};
|
|
44
|
+
function getAddressId(directAddress) {
|
|
45
|
+
let hash = directAddress;
|
|
46
|
+
if (hash.startsWith("DIRECT://")) {
|
|
47
|
+
hash = hash.slice(9);
|
|
48
|
+
} else if (hash.startsWith("DIRECT:")) {
|
|
49
|
+
hash = hash.slice(7);
|
|
50
|
+
}
|
|
51
|
+
const first = hash.slice(0, 6).toLowerCase();
|
|
52
|
+
const last = hash.slice(-6).toLowerCase();
|
|
53
|
+
return `DIRECT_${first}_${last}`;
|
|
54
|
+
}
|
|
55
|
+
var DEFAULT_NOSTR_RELAYS = [
|
|
56
|
+
"wss://relay.unicity.network",
|
|
57
|
+
"wss://relay.damus.io",
|
|
58
|
+
"wss://nos.lol",
|
|
59
|
+
"wss://relay.nostr.band"
|
|
60
|
+
];
|
|
61
|
+
var NOSTR_EVENT_KINDS = {
|
|
62
|
+
/** NIP-04 encrypted direct message */
|
|
63
|
+
DIRECT_MESSAGE: 4,
|
|
64
|
+
/** Token transfer (Unicity custom - 31113) */
|
|
65
|
+
TOKEN_TRANSFER: 31113,
|
|
66
|
+
/** Payment request (Unicity custom - 31115) */
|
|
67
|
+
PAYMENT_REQUEST: 31115,
|
|
68
|
+
/** Payment request response (Unicity custom - 31116) */
|
|
69
|
+
PAYMENT_REQUEST_RESPONSE: 31116,
|
|
70
|
+
/** Nametag binding (NIP-78 app-specific data) */
|
|
71
|
+
NAMETAG_BINDING: 30078,
|
|
72
|
+
/** Public broadcast */
|
|
73
|
+
BROADCAST: 1
|
|
74
|
+
};
|
|
75
|
+
var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
|
|
76
|
+
var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
77
|
+
var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
|
|
78
|
+
var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
|
|
79
|
+
var DEFAULT_AGGREGATOR_API_KEY = "sk_06365a9c44654841a366068bcfc68986";
|
|
80
|
+
var DEFAULT_IPFS_GATEWAYS = [
|
|
81
|
+
"https://ipfs.unicity.network",
|
|
82
|
+
"https://dweb.link",
|
|
83
|
+
"https://ipfs.io"
|
|
84
|
+
];
|
|
85
|
+
var DEFAULT_BASE_PATH = "m/44'/0'/0'";
|
|
86
|
+
var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
|
|
87
|
+
var DEFAULT_ELECTRUM_URL = "wss://fulcrum.alpha.unicity.network:50004";
|
|
88
|
+
var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
|
|
89
|
+
var TEST_NOSTR_RELAYS = [
|
|
90
|
+
"wss://nostr-relay.testnet.unicity.network"
|
|
91
|
+
];
|
|
92
|
+
var NETWORKS = {
|
|
93
|
+
mainnet: {
|
|
94
|
+
name: "Mainnet",
|
|
95
|
+
aggregatorUrl: DEFAULT_AGGREGATOR_URL,
|
|
96
|
+
nostrRelays: DEFAULT_NOSTR_RELAYS,
|
|
97
|
+
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
98
|
+
electrumUrl: DEFAULT_ELECTRUM_URL
|
|
99
|
+
},
|
|
100
|
+
testnet: {
|
|
101
|
+
name: "Testnet",
|
|
102
|
+
aggregatorUrl: TEST_AGGREGATOR_URL,
|
|
103
|
+
nostrRelays: TEST_NOSTR_RELAYS,
|
|
104
|
+
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
105
|
+
electrumUrl: TEST_ELECTRUM_URL
|
|
106
|
+
},
|
|
107
|
+
dev: {
|
|
108
|
+
name: "Development",
|
|
109
|
+
aggregatorUrl: DEV_AGGREGATOR_URL,
|
|
110
|
+
nostrRelays: TEST_NOSTR_RELAYS,
|
|
111
|
+
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
112
|
+
electrumUrl: TEST_ELECTRUM_URL
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var TIMEOUTS = {
|
|
116
|
+
/** WebSocket connection timeout */
|
|
117
|
+
WEBSOCKET_CONNECT: 1e4,
|
|
118
|
+
/** Nostr relay reconnect delay */
|
|
119
|
+
NOSTR_RECONNECT_DELAY: 3e3,
|
|
120
|
+
/** Max reconnect attempts */
|
|
121
|
+
MAX_RECONNECT_ATTEMPTS: 5,
|
|
122
|
+
/** Proof polling interval */
|
|
123
|
+
PROOF_POLL_INTERVAL: 1e3,
|
|
124
|
+
/** Sync interval */
|
|
125
|
+
SYNC_INTERVAL: 6e4
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// impl/nodejs/storage/FileStorageProvider.ts
|
|
4
129
|
var FileStorageProvider = class {
|
|
5
130
|
id = "file-storage";
|
|
6
131
|
name = "File Storage";
|
|
@@ -53,18 +178,22 @@ var FileStorageProvider = class {
|
|
|
53
178
|
return this.status;
|
|
54
179
|
}
|
|
55
180
|
async get(key) {
|
|
56
|
-
|
|
181
|
+
const fullKey = this.getFullKey(key);
|
|
182
|
+
return this.data[fullKey] ?? null;
|
|
57
183
|
}
|
|
58
184
|
async set(key, value) {
|
|
59
|
-
this.
|
|
185
|
+
const fullKey = this.getFullKey(key);
|
|
186
|
+
this.data[fullKey] = value;
|
|
60
187
|
await this.save();
|
|
61
188
|
}
|
|
62
189
|
async remove(key) {
|
|
63
|
-
|
|
190
|
+
const fullKey = this.getFullKey(key);
|
|
191
|
+
delete this.data[fullKey];
|
|
64
192
|
await this.save();
|
|
65
193
|
}
|
|
66
194
|
async has(key) {
|
|
67
|
-
|
|
195
|
+
const fullKey = this.getFullKey(key);
|
|
196
|
+
return fullKey in this.data;
|
|
68
197
|
}
|
|
69
198
|
async keys(prefix) {
|
|
70
199
|
const allKeys = Object.keys(this.data);
|
|
@@ -84,6 +213,19 @@ var FileStorageProvider = class {
|
|
|
84
213
|
}
|
|
85
214
|
await this.save();
|
|
86
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Get full storage key with address prefix for per-address keys
|
|
218
|
+
*/
|
|
219
|
+
getFullKey(key) {
|
|
220
|
+
const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(
|
|
221
|
+
key
|
|
222
|
+
);
|
|
223
|
+
if (isPerAddressKey && this._identity?.directAddress) {
|
|
224
|
+
const addressId = getAddressId(this._identity.directAddress);
|
|
225
|
+
return `${addressId}_${key}`;
|
|
226
|
+
}
|
|
227
|
+
return key;
|
|
228
|
+
}
|
|
87
229
|
async save() {
|
|
88
230
|
if (!fs.existsSync(this.dataDir)) {
|
|
89
231
|
fs.mkdirSync(this.dataDir, { recursive: true });
|
|
@@ -102,15 +244,26 @@ var FileTokenStorageProvider = class {
|
|
|
102
244
|
id = "file-token-storage";
|
|
103
245
|
name = "File Token Storage";
|
|
104
246
|
type = "local";
|
|
105
|
-
|
|
247
|
+
baseTokensDir;
|
|
106
248
|
status = "disconnected";
|
|
107
249
|
identity = null;
|
|
108
250
|
constructor(config) {
|
|
109
|
-
this.
|
|
251
|
+
this.baseTokensDir = typeof config === "string" ? config : config.tokensDir;
|
|
110
252
|
}
|
|
111
253
|
setIdentity(identity) {
|
|
112
254
|
this.identity = identity;
|
|
113
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Get tokens directory for current address
|
|
258
|
+
* Format: {baseTokensDir}/{addressId}/
|
|
259
|
+
*/
|
|
260
|
+
get tokensDir() {
|
|
261
|
+
if (this.identity?.directAddress) {
|
|
262
|
+
const addressId = getAddressId(this.identity.directAddress);
|
|
263
|
+
return path2.join(this.baseTokensDir, addressId);
|
|
264
|
+
}
|
|
265
|
+
return this.baseTokensDir;
|
|
266
|
+
}
|
|
114
267
|
async initialize() {
|
|
115
268
|
if (!fs2.existsSync(this.tokensDir)) {
|
|
116
269
|
fs2.mkdirSync(this.tokensDir, { recursive: true });
|
|
@@ -137,7 +290,7 @@ var FileTokenStorageProvider = class {
|
|
|
137
290
|
const data = {
|
|
138
291
|
_meta: {
|
|
139
292
|
version: 1,
|
|
140
|
-
address: this.identity?.
|
|
293
|
+
address: this.identity?.l1Address ?? "",
|
|
141
294
|
formatVersion: "2.0",
|
|
142
295
|
updatedAt: Date.now()
|
|
143
296
|
}
|
|
@@ -251,21 +404,566 @@ function createFileTokenStorageProvider(config) {
|
|
|
251
404
|
import WebSocket from "ws";
|
|
252
405
|
|
|
253
406
|
// transport/NostrTransportProvider.ts
|
|
254
|
-
import { Buffer } from "buffer";
|
|
407
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
408
|
+
|
|
409
|
+
// node_modules/@noble/hashes/utils.js
|
|
410
|
+
function isBytes(a) {
|
|
411
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
412
|
+
}
|
|
413
|
+
function anumber(n, title = "") {
|
|
414
|
+
if (!Number.isSafeInteger(n) || n < 0) {
|
|
415
|
+
const prefix = title && `"${title}" `;
|
|
416
|
+
throw new Error(`${prefix}expected integer >= 0, got ${n}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function abytes(value, length, title = "") {
|
|
420
|
+
const bytes = isBytes(value);
|
|
421
|
+
const len = value?.length;
|
|
422
|
+
const needsLen = length !== void 0;
|
|
423
|
+
if (!bytes || needsLen && len !== length) {
|
|
424
|
+
const prefix = title && `"${title}" `;
|
|
425
|
+
const ofLen = needsLen ? ` of length ${length}` : "";
|
|
426
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
427
|
+
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|
428
|
+
}
|
|
429
|
+
return value;
|
|
430
|
+
}
|
|
431
|
+
function ahash(h) {
|
|
432
|
+
if (typeof h !== "function" || typeof h.create !== "function")
|
|
433
|
+
throw new Error("Hash must wrapped by utils.createHasher");
|
|
434
|
+
anumber(h.outputLen);
|
|
435
|
+
anumber(h.blockLen);
|
|
436
|
+
}
|
|
437
|
+
function aexists(instance, checkFinished = true) {
|
|
438
|
+
if (instance.destroyed)
|
|
439
|
+
throw new Error("Hash instance has been destroyed");
|
|
440
|
+
if (checkFinished && instance.finished)
|
|
441
|
+
throw new Error("Hash#digest() has already been called");
|
|
442
|
+
}
|
|
443
|
+
function aoutput(out, instance) {
|
|
444
|
+
abytes(out, void 0, "digestInto() output");
|
|
445
|
+
const min = instance.outputLen;
|
|
446
|
+
if (out.length < min) {
|
|
447
|
+
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function clean(...arrays) {
|
|
451
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
452
|
+
arrays[i].fill(0);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function createView(arr) {
|
|
456
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
457
|
+
}
|
|
458
|
+
function rotr(word, shift) {
|
|
459
|
+
return word << 32 - shift | word >>> shift;
|
|
460
|
+
}
|
|
461
|
+
function createHasher(hashCons, info = {}) {
|
|
462
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
463
|
+
const tmp = hashCons(void 0);
|
|
464
|
+
hashC.outputLen = tmp.outputLen;
|
|
465
|
+
hashC.blockLen = tmp.blockLen;
|
|
466
|
+
hashC.create = (opts) => hashCons(opts);
|
|
467
|
+
Object.assign(hashC, info);
|
|
468
|
+
return Object.freeze(hashC);
|
|
469
|
+
}
|
|
470
|
+
var oidNist = (suffix) => ({
|
|
471
|
+
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// node_modules/@noble/hashes/hmac.js
|
|
475
|
+
var _HMAC = class {
|
|
476
|
+
oHash;
|
|
477
|
+
iHash;
|
|
478
|
+
blockLen;
|
|
479
|
+
outputLen;
|
|
480
|
+
finished = false;
|
|
481
|
+
destroyed = false;
|
|
482
|
+
constructor(hash, key) {
|
|
483
|
+
ahash(hash);
|
|
484
|
+
abytes(key, void 0, "key");
|
|
485
|
+
this.iHash = hash.create();
|
|
486
|
+
if (typeof this.iHash.update !== "function")
|
|
487
|
+
throw new Error("Expected instance of class which extends utils.Hash");
|
|
488
|
+
this.blockLen = this.iHash.blockLen;
|
|
489
|
+
this.outputLen = this.iHash.outputLen;
|
|
490
|
+
const blockLen = this.blockLen;
|
|
491
|
+
const pad = new Uint8Array(blockLen);
|
|
492
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
493
|
+
for (let i = 0; i < pad.length; i++)
|
|
494
|
+
pad[i] ^= 54;
|
|
495
|
+
this.iHash.update(pad);
|
|
496
|
+
this.oHash = hash.create();
|
|
497
|
+
for (let i = 0; i < pad.length; i++)
|
|
498
|
+
pad[i] ^= 54 ^ 92;
|
|
499
|
+
this.oHash.update(pad);
|
|
500
|
+
clean(pad);
|
|
501
|
+
}
|
|
502
|
+
update(buf) {
|
|
503
|
+
aexists(this);
|
|
504
|
+
this.iHash.update(buf);
|
|
505
|
+
return this;
|
|
506
|
+
}
|
|
507
|
+
digestInto(out) {
|
|
508
|
+
aexists(this);
|
|
509
|
+
abytes(out, this.outputLen, "output");
|
|
510
|
+
this.finished = true;
|
|
511
|
+
this.iHash.digestInto(out);
|
|
512
|
+
this.oHash.update(out);
|
|
513
|
+
this.oHash.digestInto(out);
|
|
514
|
+
this.destroy();
|
|
515
|
+
}
|
|
516
|
+
digest() {
|
|
517
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
518
|
+
this.digestInto(out);
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
_cloneInto(to) {
|
|
522
|
+
to ||= Object.create(Object.getPrototypeOf(this), {});
|
|
523
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
524
|
+
to = to;
|
|
525
|
+
to.finished = finished;
|
|
526
|
+
to.destroyed = destroyed;
|
|
527
|
+
to.blockLen = blockLen;
|
|
528
|
+
to.outputLen = outputLen;
|
|
529
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
530
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
531
|
+
return to;
|
|
532
|
+
}
|
|
533
|
+
clone() {
|
|
534
|
+
return this._cloneInto();
|
|
535
|
+
}
|
|
536
|
+
destroy() {
|
|
537
|
+
this.destroyed = true;
|
|
538
|
+
this.oHash.destroy();
|
|
539
|
+
this.iHash.destroy();
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
var hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest();
|
|
543
|
+
hmac.create = (hash, key) => new _HMAC(hash, key);
|
|
544
|
+
|
|
545
|
+
// node_modules/@noble/hashes/hkdf.js
|
|
546
|
+
function extract(hash, ikm, salt) {
|
|
547
|
+
ahash(hash);
|
|
548
|
+
if (salt === void 0)
|
|
549
|
+
salt = new Uint8Array(hash.outputLen);
|
|
550
|
+
return hmac(hash, salt, ikm);
|
|
551
|
+
}
|
|
552
|
+
var HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);
|
|
553
|
+
var EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
|
|
554
|
+
function expand(hash, prk, info, length = 32) {
|
|
555
|
+
ahash(hash);
|
|
556
|
+
anumber(length, "length");
|
|
557
|
+
const olen = hash.outputLen;
|
|
558
|
+
if (length > 255 * olen)
|
|
559
|
+
throw new Error("Length must be <= 255*HashLen");
|
|
560
|
+
const blocks = Math.ceil(length / olen);
|
|
561
|
+
if (info === void 0)
|
|
562
|
+
info = EMPTY_BUFFER;
|
|
563
|
+
else
|
|
564
|
+
abytes(info, void 0, "info");
|
|
565
|
+
const okm = new Uint8Array(blocks * olen);
|
|
566
|
+
const HMAC = hmac.create(hash, prk);
|
|
567
|
+
const HMACTmp = HMAC._cloneInto();
|
|
568
|
+
const T = new Uint8Array(HMAC.outputLen);
|
|
569
|
+
for (let counter = 0; counter < blocks; counter++) {
|
|
570
|
+
HKDF_COUNTER[0] = counter + 1;
|
|
571
|
+
HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T).update(info).update(HKDF_COUNTER).digestInto(T);
|
|
572
|
+
okm.set(T, olen * counter);
|
|
573
|
+
HMAC._cloneInto(HMACTmp);
|
|
574
|
+
}
|
|
575
|
+
HMAC.destroy();
|
|
576
|
+
HMACTmp.destroy();
|
|
577
|
+
clean(T, HKDF_COUNTER);
|
|
578
|
+
return okm.slice(0, length);
|
|
579
|
+
}
|
|
580
|
+
var hkdf = (hash, ikm, salt, info, length) => expand(hash, extract(hash, ikm, salt), info, length);
|
|
581
|
+
|
|
582
|
+
// node_modules/@noble/hashes/_md.js
|
|
583
|
+
function Chi(a, b, c) {
|
|
584
|
+
return a & b ^ ~a & c;
|
|
585
|
+
}
|
|
586
|
+
function Maj(a, b, c) {
|
|
587
|
+
return a & b ^ a & c ^ b & c;
|
|
588
|
+
}
|
|
589
|
+
var HashMD = class {
|
|
590
|
+
blockLen;
|
|
591
|
+
outputLen;
|
|
592
|
+
padOffset;
|
|
593
|
+
isLE;
|
|
594
|
+
// For partial updates less than block size
|
|
595
|
+
buffer;
|
|
596
|
+
view;
|
|
597
|
+
finished = false;
|
|
598
|
+
length = 0;
|
|
599
|
+
pos = 0;
|
|
600
|
+
destroyed = false;
|
|
601
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
602
|
+
this.blockLen = blockLen;
|
|
603
|
+
this.outputLen = outputLen;
|
|
604
|
+
this.padOffset = padOffset;
|
|
605
|
+
this.isLE = isLE;
|
|
606
|
+
this.buffer = new Uint8Array(blockLen);
|
|
607
|
+
this.view = createView(this.buffer);
|
|
608
|
+
}
|
|
609
|
+
update(data) {
|
|
610
|
+
aexists(this);
|
|
611
|
+
abytes(data);
|
|
612
|
+
const { view, buffer, blockLen } = this;
|
|
613
|
+
const len = data.length;
|
|
614
|
+
for (let pos = 0; pos < len; ) {
|
|
615
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
616
|
+
if (take === blockLen) {
|
|
617
|
+
const dataView = createView(data);
|
|
618
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
619
|
+
this.process(dataView, pos);
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
623
|
+
this.pos += take;
|
|
624
|
+
pos += take;
|
|
625
|
+
if (this.pos === blockLen) {
|
|
626
|
+
this.process(view, 0);
|
|
627
|
+
this.pos = 0;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
this.length += data.length;
|
|
631
|
+
this.roundClean();
|
|
632
|
+
return this;
|
|
633
|
+
}
|
|
634
|
+
digestInto(out) {
|
|
635
|
+
aexists(this);
|
|
636
|
+
aoutput(out, this);
|
|
637
|
+
this.finished = true;
|
|
638
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
639
|
+
let { pos } = this;
|
|
640
|
+
buffer[pos++] = 128;
|
|
641
|
+
clean(this.buffer.subarray(pos));
|
|
642
|
+
if (this.padOffset > blockLen - pos) {
|
|
643
|
+
this.process(view, 0);
|
|
644
|
+
pos = 0;
|
|
645
|
+
}
|
|
646
|
+
for (let i = pos; i < blockLen; i++)
|
|
647
|
+
buffer[i] = 0;
|
|
648
|
+
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
649
|
+
this.process(view, 0);
|
|
650
|
+
const oview = createView(out);
|
|
651
|
+
const len = this.outputLen;
|
|
652
|
+
if (len % 4)
|
|
653
|
+
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|
654
|
+
const outLen = len / 4;
|
|
655
|
+
const state = this.get();
|
|
656
|
+
if (outLen > state.length)
|
|
657
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
658
|
+
for (let i = 0; i < outLen; i++)
|
|
659
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
660
|
+
}
|
|
661
|
+
digest() {
|
|
662
|
+
const { buffer, outputLen } = this;
|
|
663
|
+
this.digestInto(buffer);
|
|
664
|
+
const res = buffer.slice(0, outputLen);
|
|
665
|
+
this.destroy();
|
|
666
|
+
return res;
|
|
667
|
+
}
|
|
668
|
+
_cloneInto(to) {
|
|
669
|
+
to ||= new this.constructor();
|
|
670
|
+
to.set(...this.get());
|
|
671
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
672
|
+
to.destroyed = destroyed;
|
|
673
|
+
to.finished = finished;
|
|
674
|
+
to.length = length;
|
|
675
|
+
to.pos = pos;
|
|
676
|
+
if (length % blockLen)
|
|
677
|
+
to.buffer.set(buffer);
|
|
678
|
+
return to;
|
|
679
|
+
}
|
|
680
|
+
clone() {
|
|
681
|
+
return this._cloneInto();
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
685
|
+
1779033703,
|
|
686
|
+
3144134277,
|
|
687
|
+
1013904242,
|
|
688
|
+
2773480762,
|
|
689
|
+
1359893119,
|
|
690
|
+
2600822924,
|
|
691
|
+
528734635,
|
|
692
|
+
1541459225
|
|
693
|
+
]);
|
|
694
|
+
|
|
695
|
+
// node_modules/@noble/hashes/sha2.js
|
|
696
|
+
var SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
697
|
+
1116352408,
|
|
698
|
+
1899447441,
|
|
699
|
+
3049323471,
|
|
700
|
+
3921009573,
|
|
701
|
+
961987163,
|
|
702
|
+
1508970993,
|
|
703
|
+
2453635748,
|
|
704
|
+
2870763221,
|
|
705
|
+
3624381080,
|
|
706
|
+
310598401,
|
|
707
|
+
607225278,
|
|
708
|
+
1426881987,
|
|
709
|
+
1925078388,
|
|
710
|
+
2162078206,
|
|
711
|
+
2614888103,
|
|
712
|
+
3248222580,
|
|
713
|
+
3835390401,
|
|
714
|
+
4022224774,
|
|
715
|
+
264347078,
|
|
716
|
+
604807628,
|
|
717
|
+
770255983,
|
|
718
|
+
1249150122,
|
|
719
|
+
1555081692,
|
|
720
|
+
1996064986,
|
|
721
|
+
2554220882,
|
|
722
|
+
2821834349,
|
|
723
|
+
2952996808,
|
|
724
|
+
3210313671,
|
|
725
|
+
3336571891,
|
|
726
|
+
3584528711,
|
|
727
|
+
113926993,
|
|
728
|
+
338241895,
|
|
729
|
+
666307205,
|
|
730
|
+
773529912,
|
|
731
|
+
1294757372,
|
|
732
|
+
1396182291,
|
|
733
|
+
1695183700,
|
|
734
|
+
1986661051,
|
|
735
|
+
2177026350,
|
|
736
|
+
2456956037,
|
|
737
|
+
2730485921,
|
|
738
|
+
2820302411,
|
|
739
|
+
3259730800,
|
|
740
|
+
3345764771,
|
|
741
|
+
3516065817,
|
|
742
|
+
3600352804,
|
|
743
|
+
4094571909,
|
|
744
|
+
275423344,
|
|
745
|
+
430227734,
|
|
746
|
+
506948616,
|
|
747
|
+
659060556,
|
|
748
|
+
883997877,
|
|
749
|
+
958139571,
|
|
750
|
+
1322822218,
|
|
751
|
+
1537002063,
|
|
752
|
+
1747873779,
|
|
753
|
+
1955562222,
|
|
754
|
+
2024104815,
|
|
755
|
+
2227730452,
|
|
756
|
+
2361852424,
|
|
757
|
+
2428436474,
|
|
758
|
+
2756734187,
|
|
759
|
+
3204031479,
|
|
760
|
+
3329325298
|
|
761
|
+
]);
|
|
762
|
+
var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
763
|
+
var SHA2_32B = class extends HashMD {
|
|
764
|
+
constructor(outputLen) {
|
|
765
|
+
super(64, outputLen, 8, false);
|
|
766
|
+
}
|
|
767
|
+
get() {
|
|
768
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
769
|
+
return [A, B, C, D, E, F, G, H];
|
|
770
|
+
}
|
|
771
|
+
// prettier-ignore
|
|
772
|
+
set(A, B, C, D, E, F, G, H) {
|
|
773
|
+
this.A = A | 0;
|
|
774
|
+
this.B = B | 0;
|
|
775
|
+
this.C = C | 0;
|
|
776
|
+
this.D = D | 0;
|
|
777
|
+
this.E = E | 0;
|
|
778
|
+
this.F = F | 0;
|
|
779
|
+
this.G = G | 0;
|
|
780
|
+
this.H = H | 0;
|
|
781
|
+
}
|
|
782
|
+
process(view, offset) {
|
|
783
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
784
|
+
SHA256_W[i] = view.getUint32(offset, false);
|
|
785
|
+
for (let i = 16; i < 64; i++) {
|
|
786
|
+
const W15 = SHA256_W[i - 15];
|
|
787
|
+
const W2 = SHA256_W[i - 2];
|
|
788
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
789
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
790
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
791
|
+
}
|
|
792
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
793
|
+
for (let i = 0; i < 64; i++) {
|
|
794
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
795
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
796
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
797
|
+
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
798
|
+
H = G;
|
|
799
|
+
G = F;
|
|
800
|
+
F = E;
|
|
801
|
+
E = D + T1 | 0;
|
|
802
|
+
D = C;
|
|
803
|
+
C = B;
|
|
804
|
+
B = A;
|
|
805
|
+
A = T1 + T2 | 0;
|
|
806
|
+
}
|
|
807
|
+
A = A + this.A | 0;
|
|
808
|
+
B = B + this.B | 0;
|
|
809
|
+
C = C + this.C | 0;
|
|
810
|
+
D = D + this.D | 0;
|
|
811
|
+
E = E + this.E | 0;
|
|
812
|
+
F = F + this.F | 0;
|
|
813
|
+
G = G + this.G | 0;
|
|
814
|
+
H = H + this.H | 0;
|
|
815
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
816
|
+
}
|
|
817
|
+
roundClean() {
|
|
818
|
+
clean(SHA256_W);
|
|
819
|
+
}
|
|
820
|
+
destroy() {
|
|
821
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
822
|
+
clean(this.buffer);
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
var _SHA256 = class extends SHA2_32B {
|
|
826
|
+
// We cannot use array here since array allows indexing by variable
|
|
827
|
+
// which means optimizer/compiler cannot use registers.
|
|
828
|
+
A = SHA256_IV[0] | 0;
|
|
829
|
+
B = SHA256_IV[1] | 0;
|
|
830
|
+
C = SHA256_IV[2] | 0;
|
|
831
|
+
D = SHA256_IV[3] | 0;
|
|
832
|
+
E = SHA256_IV[4] | 0;
|
|
833
|
+
F = SHA256_IV[5] | 0;
|
|
834
|
+
G = SHA256_IV[6] | 0;
|
|
835
|
+
H = SHA256_IV[7] | 0;
|
|
836
|
+
constructor() {
|
|
837
|
+
super(32);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
var sha256 = /* @__PURE__ */ createHasher(
|
|
841
|
+
() => new _SHA256(),
|
|
842
|
+
/* @__PURE__ */ oidNist(1)
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
// transport/NostrTransportProvider.ts
|
|
255
846
|
import {
|
|
256
847
|
NostrKeyManager,
|
|
257
848
|
NIP04,
|
|
849
|
+
NIP17,
|
|
258
850
|
Event as NostrEventClass,
|
|
259
|
-
|
|
851
|
+
EventKinds,
|
|
852
|
+
hashNametag,
|
|
853
|
+
NostrClient,
|
|
854
|
+
Filter
|
|
260
855
|
} from "@unicitylabs/nostr-js-sdk";
|
|
261
856
|
|
|
857
|
+
// core/crypto.ts
|
|
858
|
+
import * as bip39 from "bip39";
|
|
859
|
+
import CryptoJS from "crypto-js";
|
|
860
|
+
import elliptic from "elliptic";
|
|
861
|
+
|
|
862
|
+
// core/bech32.ts
|
|
863
|
+
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
864
|
+
var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
865
|
+
function convertBits(data, fromBits, toBits, pad) {
|
|
866
|
+
let acc = 0;
|
|
867
|
+
let bits = 0;
|
|
868
|
+
const ret = [];
|
|
869
|
+
const maxv = (1 << toBits) - 1;
|
|
870
|
+
for (let i = 0; i < data.length; i++) {
|
|
871
|
+
const value = data[i];
|
|
872
|
+
if (value < 0 || value >> fromBits !== 0) return null;
|
|
873
|
+
acc = acc << fromBits | value;
|
|
874
|
+
bits += fromBits;
|
|
875
|
+
while (bits >= toBits) {
|
|
876
|
+
bits -= toBits;
|
|
877
|
+
ret.push(acc >> bits & maxv);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (pad) {
|
|
881
|
+
if (bits > 0) {
|
|
882
|
+
ret.push(acc << toBits - bits & maxv);
|
|
883
|
+
}
|
|
884
|
+
} else if (bits >= fromBits || acc << toBits - bits & maxv) {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
return ret;
|
|
888
|
+
}
|
|
889
|
+
function hrpExpand(hrp) {
|
|
890
|
+
const ret = [];
|
|
891
|
+
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);
|
|
892
|
+
ret.push(0);
|
|
893
|
+
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);
|
|
894
|
+
return ret;
|
|
895
|
+
}
|
|
896
|
+
function bech32Polymod(values) {
|
|
897
|
+
let chk = 1;
|
|
898
|
+
for (let p = 0; p < values.length; p++) {
|
|
899
|
+
const top = chk >> 25;
|
|
900
|
+
chk = (chk & 33554431) << 5 ^ values[p];
|
|
901
|
+
for (let i = 0; i < 5; i++) {
|
|
902
|
+
if (top >> i & 1) chk ^= GENERATOR[i];
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
return chk;
|
|
906
|
+
}
|
|
907
|
+
function bech32Checksum(hrp, data) {
|
|
908
|
+
const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
|
909
|
+
const mod = bech32Polymod(values) ^ 1;
|
|
910
|
+
const ret = [];
|
|
911
|
+
for (let p = 0; p < 6; p++) {
|
|
912
|
+
ret.push(mod >> 5 * (5 - p) & 31);
|
|
913
|
+
}
|
|
914
|
+
return ret;
|
|
915
|
+
}
|
|
916
|
+
function encodeBech32(hrp, version, program) {
|
|
917
|
+
if (version < 0 || version > 16) {
|
|
918
|
+
throw new Error("Invalid witness version");
|
|
919
|
+
}
|
|
920
|
+
const converted = convertBits(Array.from(program), 8, 5, true);
|
|
921
|
+
if (!converted) {
|
|
922
|
+
throw new Error("Failed to convert bits");
|
|
923
|
+
}
|
|
924
|
+
const data = [version].concat(converted);
|
|
925
|
+
const checksum = bech32Checksum(hrp, data);
|
|
926
|
+
const combined = data.concat(checksum);
|
|
927
|
+
let out = hrp + "1";
|
|
928
|
+
for (let i = 0; i < combined.length; i++) {
|
|
929
|
+
out += CHARSET[combined[i]];
|
|
930
|
+
}
|
|
931
|
+
return out;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// core/crypto.ts
|
|
935
|
+
var ec = new elliptic.ec("secp256k1");
|
|
936
|
+
var CURVE_ORDER = BigInt(
|
|
937
|
+
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
938
|
+
);
|
|
939
|
+
function getPublicKey(privateKey, compressed = true) {
|
|
940
|
+
const keyPair = ec.keyFromPrivate(privateKey, "hex");
|
|
941
|
+
return keyPair.getPublic(compressed, "hex");
|
|
942
|
+
}
|
|
943
|
+
function sha2562(data, inputEncoding = "hex") {
|
|
944
|
+
const parsed = inputEncoding === "hex" ? CryptoJS.enc.Hex.parse(data) : CryptoJS.enc.Utf8.parse(data);
|
|
945
|
+
return CryptoJS.SHA256(parsed).toString();
|
|
946
|
+
}
|
|
947
|
+
function ripemd160(data, inputEncoding = "hex") {
|
|
948
|
+
const parsed = inputEncoding === "hex" ? CryptoJS.enc.Hex.parse(data) : CryptoJS.enc.Utf8.parse(data);
|
|
949
|
+
return CryptoJS.RIPEMD160(parsed).toString();
|
|
950
|
+
}
|
|
951
|
+
function hash160(data) {
|
|
952
|
+
const sha = sha2562(data, "hex");
|
|
953
|
+
return ripemd160(sha, "hex");
|
|
954
|
+
}
|
|
955
|
+
function hash160ToBytes(hash160Hex) {
|
|
956
|
+
const matches = hash160Hex.match(/../g);
|
|
957
|
+
if (!matches) return new Uint8Array(0);
|
|
958
|
+
return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
|
|
959
|
+
}
|
|
960
|
+
function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
|
|
961
|
+
const pubKeyHash = hash160(publicKey);
|
|
962
|
+
const programBytes = hash160ToBytes(pubKeyHash);
|
|
963
|
+
return encodeBech32(prefix, witnessVersion, programBytes);
|
|
964
|
+
}
|
|
965
|
+
|
|
262
966
|
// transport/websocket.ts
|
|
263
|
-
var WebSocketReadyState = {
|
|
264
|
-
CONNECTING: 0,
|
|
265
|
-
OPEN: 1,
|
|
266
|
-
CLOSING: 2,
|
|
267
|
-
CLOSED: 3
|
|
268
|
-
};
|
|
269
967
|
function defaultUUIDGenerator() {
|
|
270
968
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
271
969
|
return crypto.randomUUID();
|
|
@@ -277,124 +975,61 @@ function defaultUUIDGenerator() {
|
|
|
277
975
|
});
|
|
278
976
|
}
|
|
279
977
|
|
|
280
|
-
// constants.ts
|
|
281
|
-
var STORAGE_PREFIX = "sphere_";
|
|
282
|
-
var STORAGE_KEYS = {
|
|
283
|
-
/** Encrypted BIP39 mnemonic */
|
|
284
|
-
MNEMONIC: `${STORAGE_PREFIX}mnemonic`,
|
|
285
|
-
/** Encrypted master private key */
|
|
286
|
-
MASTER_KEY: `${STORAGE_PREFIX}master_key`,
|
|
287
|
-
/** BIP32 chain code */
|
|
288
|
-
CHAIN_CODE: `${STORAGE_PREFIX}chain_code`,
|
|
289
|
-
/** HD derivation path (full path like m/44'/0'/0'/0/0) */
|
|
290
|
-
DERIVATION_PATH: `${STORAGE_PREFIX}derivation_path`,
|
|
291
|
-
/** Base derivation path (like m/44'/0'/0' without chain/index) */
|
|
292
|
-
BASE_PATH: `${STORAGE_PREFIX}base_path`,
|
|
293
|
-
/** Derivation mode: bip32, wif_hmac, legacy_hmac */
|
|
294
|
-
DERIVATION_MODE: `${STORAGE_PREFIX}derivation_mode`,
|
|
295
|
-
/** Wallet source: mnemonic, file, unknown */
|
|
296
|
-
WALLET_SOURCE: `${STORAGE_PREFIX}wallet_source`,
|
|
297
|
-
/** Wallet existence flag */
|
|
298
|
-
WALLET_EXISTS: `${STORAGE_PREFIX}wallet_exists`,
|
|
299
|
-
/** Registered nametag (legacy - single address) */
|
|
300
|
-
NAMETAG: `${STORAGE_PREFIX}nametag`,
|
|
301
|
-
/** Current active address index */
|
|
302
|
-
CURRENT_ADDRESS_INDEX: `${STORAGE_PREFIX}current_address_index`,
|
|
303
|
-
/** Address nametags map (JSON: { "0": "alice", "1": "bob" }) */
|
|
304
|
-
ADDRESS_NAMETAGS: `${STORAGE_PREFIX}address_nametags`,
|
|
305
|
-
/** Token data */
|
|
306
|
-
TOKENS: `${STORAGE_PREFIX}tokens`,
|
|
307
|
-
/** Pending transfers */
|
|
308
|
-
PENDING_TRANSFERS: `${STORAGE_PREFIX}pending_transfers`,
|
|
309
|
-
/** Transfer outbox */
|
|
310
|
-
OUTBOX: `${STORAGE_PREFIX}outbox`,
|
|
311
|
-
/** Conversations */
|
|
312
|
-
CONVERSATIONS: `${STORAGE_PREFIX}conversations`,
|
|
313
|
-
/** Messages */
|
|
314
|
-
MESSAGES: `${STORAGE_PREFIX}messages`,
|
|
315
|
-
/** Transaction history */
|
|
316
|
-
TRANSACTION_HISTORY: `${STORAGE_PREFIX}transaction_history`,
|
|
317
|
-
/** Archived tokens (spent token history) */
|
|
318
|
-
ARCHIVED_TOKENS: `${STORAGE_PREFIX}archived_tokens`,
|
|
319
|
-
/** Tombstones (records of deleted/spent tokens) */
|
|
320
|
-
TOMBSTONES: `${STORAGE_PREFIX}tombstones`,
|
|
321
|
-
/** Forked tokens (alternative histories) */
|
|
322
|
-
FORKED_TOKENS: `${STORAGE_PREFIX}forked_tokens`
|
|
323
|
-
};
|
|
324
|
-
var DEFAULT_NOSTR_RELAYS = [
|
|
325
|
-
"wss://relay.unicity.network",
|
|
326
|
-
"wss://relay.damus.io",
|
|
327
|
-
"wss://nos.lol",
|
|
328
|
-
"wss://relay.nostr.band"
|
|
329
|
-
];
|
|
330
|
-
var NOSTR_EVENT_KINDS = {
|
|
331
|
-
/** NIP-04 encrypted direct message */
|
|
332
|
-
DIRECT_MESSAGE: 4,
|
|
333
|
-
/** Token transfer (Unicity custom - 31113) */
|
|
334
|
-
TOKEN_TRANSFER: 31113,
|
|
335
|
-
/** Payment request (Unicity custom - 31115) */
|
|
336
|
-
PAYMENT_REQUEST: 31115,
|
|
337
|
-
/** Payment request response (Unicity custom - 31116) */
|
|
338
|
-
PAYMENT_REQUEST_RESPONSE: 31116,
|
|
339
|
-
/** Nametag binding (NIP-78 app-specific data) */
|
|
340
|
-
NAMETAG_BINDING: 30078,
|
|
341
|
-
/** Public broadcast */
|
|
342
|
-
BROADCAST: 1
|
|
343
|
-
};
|
|
344
|
-
var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
|
|
345
|
-
var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
346
|
-
var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
|
|
347
|
-
var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
|
|
348
|
-
var DEFAULT_IPFS_GATEWAYS = [
|
|
349
|
-
"https://ipfs.unicity.network",
|
|
350
|
-
"https://dweb.link",
|
|
351
|
-
"https://ipfs.io"
|
|
352
|
-
];
|
|
353
|
-
var DEFAULT_BASE_PATH = "m/44'/0'/0'";
|
|
354
|
-
var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
|
|
355
|
-
var DEFAULT_ELECTRUM_URL = "wss://fulcrum.alpha.unicity.network:50004";
|
|
356
|
-
var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
|
|
357
|
-
var TEST_NOSTR_RELAYS = [
|
|
358
|
-
"wss://nostr-relay.testnet.unicity.network"
|
|
359
|
-
];
|
|
360
|
-
var NETWORKS = {
|
|
361
|
-
mainnet: {
|
|
362
|
-
name: "Mainnet",
|
|
363
|
-
aggregatorUrl: DEFAULT_AGGREGATOR_URL,
|
|
364
|
-
nostrRelays: DEFAULT_NOSTR_RELAYS,
|
|
365
|
-
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
366
|
-
electrumUrl: DEFAULT_ELECTRUM_URL
|
|
367
|
-
},
|
|
368
|
-
testnet: {
|
|
369
|
-
name: "Testnet",
|
|
370
|
-
aggregatorUrl: TEST_AGGREGATOR_URL,
|
|
371
|
-
nostrRelays: TEST_NOSTR_RELAYS,
|
|
372
|
-
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
373
|
-
electrumUrl: TEST_ELECTRUM_URL
|
|
374
|
-
},
|
|
375
|
-
dev: {
|
|
376
|
-
name: "Development",
|
|
377
|
-
aggregatorUrl: DEV_AGGREGATOR_URL,
|
|
378
|
-
nostrRelays: TEST_NOSTR_RELAYS,
|
|
379
|
-
ipfsGateways: DEFAULT_IPFS_GATEWAYS,
|
|
380
|
-
electrumUrl: TEST_ELECTRUM_URL
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
var TIMEOUTS = {
|
|
384
|
-
/** WebSocket connection timeout */
|
|
385
|
-
WEBSOCKET_CONNECT: 1e4,
|
|
386
|
-
/** Nostr relay reconnect delay */
|
|
387
|
-
NOSTR_RECONNECT_DELAY: 3e3,
|
|
388
|
-
/** Max reconnect attempts */
|
|
389
|
-
MAX_RECONNECT_ATTEMPTS: 5,
|
|
390
|
-
/** Proof polling interval */
|
|
391
|
-
PROOF_POLL_INTERVAL: 1e3,
|
|
392
|
-
/** Sync interval */
|
|
393
|
-
SYNC_INTERVAL: 6e4
|
|
394
|
-
};
|
|
395
|
-
|
|
396
978
|
// transport/NostrTransportProvider.ts
|
|
397
979
|
var EVENT_KINDS = NOSTR_EVENT_KINDS;
|
|
980
|
+
function deriveNametagEncryptionKey(privateKeyHex) {
|
|
981
|
+
const privateKeyBytes = Buffer2.from(privateKeyHex, "hex");
|
|
982
|
+
const saltInput = new TextEncoder().encode("sphere-nametag-salt");
|
|
983
|
+
const salt = sha256(saltInput);
|
|
984
|
+
const info = new TextEncoder().encode("nametag-encryption");
|
|
985
|
+
return hkdf(sha256, privateKeyBytes, salt, info, 32);
|
|
986
|
+
}
|
|
987
|
+
async function encryptNametag(nametag, privateKeyHex) {
|
|
988
|
+
const key = deriveNametagEncryptionKey(privateKeyHex);
|
|
989
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
990
|
+
const encoder = new TextEncoder();
|
|
991
|
+
const data = encoder.encode(nametag);
|
|
992
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
993
|
+
"raw",
|
|
994
|
+
new Uint8Array(key).buffer,
|
|
995
|
+
{ name: "AES-GCM" },
|
|
996
|
+
false,
|
|
997
|
+
["encrypt"]
|
|
998
|
+
);
|
|
999
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
1000
|
+
{ name: "AES-GCM", iv: new Uint8Array(iv).buffer },
|
|
1001
|
+
cryptoKey,
|
|
1002
|
+
new Uint8Array(data).buffer
|
|
1003
|
+
);
|
|
1004
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
1005
|
+
combined.set(iv, 0);
|
|
1006
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
1007
|
+
return Buffer2.from(combined).toString("base64");
|
|
1008
|
+
}
|
|
1009
|
+
async function decryptNametag(encryptedBase64, privateKeyHex) {
|
|
1010
|
+
try {
|
|
1011
|
+
const key = deriveNametagEncryptionKey(privateKeyHex);
|
|
1012
|
+
const combined = Buffer2.from(encryptedBase64, "base64");
|
|
1013
|
+
const iv = combined.slice(0, 12);
|
|
1014
|
+
const ciphertext = combined.slice(12);
|
|
1015
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
1016
|
+
"raw",
|
|
1017
|
+
new Uint8Array(key).buffer,
|
|
1018
|
+
{ name: "AES-GCM" },
|
|
1019
|
+
false,
|
|
1020
|
+
["decrypt"]
|
|
1021
|
+
);
|
|
1022
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
1023
|
+
{ name: "AES-GCM", iv: new Uint8Array(iv).buffer },
|
|
1024
|
+
cryptoKey,
|
|
1025
|
+
new Uint8Array(ciphertext).buffer
|
|
1026
|
+
);
|
|
1027
|
+
const decoder = new TextDecoder();
|
|
1028
|
+
return decoder.decode(decrypted);
|
|
1029
|
+
} catch {
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
398
1033
|
var NostrTransportProvider = class {
|
|
399
1034
|
id = "nostr";
|
|
400
1035
|
name = "Nostr Transport";
|
|
@@ -404,9 +1039,10 @@ var NostrTransportProvider = class {
|
|
|
404
1039
|
identity = null;
|
|
405
1040
|
keyManager = null;
|
|
406
1041
|
status = "disconnected";
|
|
407
|
-
//
|
|
408
|
-
|
|
409
|
-
|
|
1042
|
+
// NostrClient from nostr-js-sdk handles all WebSocket management,
|
|
1043
|
+
// keepalive pings, reconnection, and NIP-42 authentication
|
|
1044
|
+
nostrClient = null;
|
|
1045
|
+
mainSubscriptionId = null;
|
|
410
1046
|
// Event handlers
|
|
411
1047
|
messageHandlers = /* @__PURE__ */ new Set();
|
|
412
1048
|
transferHandlers = /* @__PURE__ */ new Set();
|
|
@@ -414,9 +1050,6 @@ var NostrTransportProvider = class {
|
|
|
414
1050
|
paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
|
|
415
1051
|
broadcastHandlers = /* @__PURE__ */ new Map();
|
|
416
1052
|
eventCallbacks = /* @__PURE__ */ new Set();
|
|
417
|
-
// Subscriptions
|
|
418
|
-
subscriptions = /* @__PURE__ */ new Map();
|
|
419
|
-
// subId -> relays
|
|
420
1053
|
constructor(config) {
|
|
421
1054
|
this.config = {
|
|
422
1055
|
relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],
|
|
@@ -436,16 +1069,43 @@ var NostrTransportProvider = class {
|
|
|
436
1069
|
if (this.status === "connected") return;
|
|
437
1070
|
this.status = "connecting";
|
|
438
1071
|
try {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
1072
|
+
if (!this.keyManager) {
|
|
1073
|
+
const tempKey = Buffer2.alloc(32);
|
|
1074
|
+
crypto.getRandomValues(tempKey);
|
|
1075
|
+
this.keyManager = NostrKeyManager.fromPrivateKey(tempKey);
|
|
1076
|
+
}
|
|
1077
|
+
this.nostrClient = new NostrClient(this.keyManager, {
|
|
1078
|
+
autoReconnect: this.config.autoReconnect,
|
|
1079
|
+
reconnectIntervalMs: this.config.reconnectDelay,
|
|
1080
|
+
maxReconnectIntervalMs: this.config.reconnectDelay * 16,
|
|
1081
|
+
// exponential backoff cap
|
|
1082
|
+
pingIntervalMs: 15e3
|
|
1083
|
+
// 15 second keepalive pings (more aggressive to prevent drops)
|
|
1084
|
+
});
|
|
1085
|
+
this.nostrClient.addConnectionListener({
|
|
1086
|
+
onConnect: (url) => {
|
|
1087
|
+
this.log("NostrClient connected to relay:", url);
|
|
1088
|
+
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
1089
|
+
},
|
|
1090
|
+
onDisconnect: (url, reason) => {
|
|
1091
|
+
this.log("NostrClient disconnected from relay:", url, "reason:", reason);
|
|
1092
|
+
},
|
|
1093
|
+
onReconnecting: (url, attempt) => {
|
|
1094
|
+
this.log("NostrClient reconnecting to relay:", url, "attempt:", attempt);
|
|
1095
|
+
this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
|
|
1096
|
+
},
|
|
1097
|
+
onReconnected: (url) => {
|
|
1098
|
+
this.log("NostrClient reconnected to relay:", url);
|
|
1099
|
+
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
await this.nostrClient.connect(...this.config.relays);
|
|
1103
|
+
if (!this.nostrClient.isConnected()) {
|
|
444
1104
|
throw new Error("Failed to connect to any relay");
|
|
445
1105
|
}
|
|
446
1106
|
this.status = "connected";
|
|
447
1107
|
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
448
|
-
this.log("Connected to", this.
|
|
1108
|
+
this.log("Connected to", this.nostrClient.getConnectedRelays().size, "relays");
|
|
449
1109
|
if (this.identity) {
|
|
450
1110
|
this.subscribeToEvents();
|
|
451
1111
|
}
|
|
@@ -455,17 +1115,19 @@ var NostrTransportProvider = class {
|
|
|
455
1115
|
}
|
|
456
1116
|
}
|
|
457
1117
|
async disconnect() {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
this.
|
|
1118
|
+
if (this.nostrClient) {
|
|
1119
|
+
this.nostrClient.disconnect();
|
|
1120
|
+
this.nostrClient = null;
|
|
461
1121
|
}
|
|
462
|
-
this.
|
|
1122
|
+
this.mainSubscriptionId = null;
|
|
1123
|
+
this.walletSubscriptionId = null;
|
|
1124
|
+
this.chatSubscriptionId = null;
|
|
463
1125
|
this.status = "disconnected";
|
|
464
1126
|
this.emitEvent({ type: "transport:disconnected", timestamp: Date.now() });
|
|
465
1127
|
this.log("Disconnected from all relays");
|
|
466
1128
|
}
|
|
467
1129
|
isConnected() {
|
|
468
|
-
return this.status === "connected" && this.
|
|
1130
|
+
return this.status === "connected" && this.nostrClient?.isConnected() === true;
|
|
469
1131
|
}
|
|
470
1132
|
getStatus() {
|
|
471
1133
|
return this.status;
|
|
@@ -483,7 +1145,8 @@ var NostrTransportProvider = class {
|
|
|
483
1145
|
* Get list of currently connected relay URLs
|
|
484
1146
|
*/
|
|
485
1147
|
getConnectedRelays() {
|
|
486
|
-
|
|
1148
|
+
if (!this.nostrClient) return [];
|
|
1149
|
+
return Array.from(this.nostrClient.getConnectedRelays());
|
|
487
1150
|
}
|
|
488
1151
|
/**
|
|
489
1152
|
* Add a new relay dynamically
|
|
@@ -495,9 +1158,9 @@ var NostrTransportProvider = class {
|
|
|
495
1158
|
return false;
|
|
496
1159
|
}
|
|
497
1160
|
this.config.relays.push(relayUrl);
|
|
498
|
-
if (this.status === "connected") {
|
|
1161
|
+
if (this.status === "connected" && this.nostrClient) {
|
|
499
1162
|
try {
|
|
500
|
-
await this.
|
|
1163
|
+
await this.nostrClient.connect(relayUrl);
|
|
501
1164
|
this.log("Added and connected to relay:", relayUrl);
|
|
502
1165
|
this.emitEvent({
|
|
503
1166
|
type: "transport:relay_added",
|
|
@@ -525,6 +1188,8 @@ var NostrTransportProvider = class {
|
|
|
525
1188
|
/**
|
|
526
1189
|
* Remove a relay dynamically
|
|
527
1190
|
* Will disconnect from the relay if connected
|
|
1191
|
+
* NOTE: NostrClient doesn't support removing individual relays at runtime.
|
|
1192
|
+
* We remove from config so it won't be used on next connect().
|
|
528
1193
|
*/
|
|
529
1194
|
async removeRelay(relayUrl) {
|
|
530
1195
|
const index = this.config.relays.indexOf(relayUrl);
|
|
@@ -533,19 +1198,13 @@ var NostrTransportProvider = class {
|
|
|
533
1198
|
return false;
|
|
534
1199
|
}
|
|
535
1200
|
this.config.relays.splice(index, 1);
|
|
536
|
-
|
|
537
|
-
if (ws) {
|
|
538
|
-
ws.close();
|
|
539
|
-
this.connections.delete(relayUrl);
|
|
540
|
-
this.reconnectAttempts.delete(relayUrl);
|
|
541
|
-
this.log("Removed and disconnected from relay:", relayUrl);
|
|
542
|
-
}
|
|
1201
|
+
this.log("Removed relay from config:", relayUrl);
|
|
543
1202
|
this.emitEvent({
|
|
544
1203
|
type: "transport:relay_removed",
|
|
545
1204
|
timestamp: Date.now(),
|
|
546
1205
|
data: { relay: relayUrl }
|
|
547
1206
|
});
|
|
548
|
-
if (this.
|
|
1207
|
+
if (this.nostrClient && !this.nostrClient.isConnected() && this.status === "connected") {
|
|
549
1208
|
this.status = "error";
|
|
550
1209
|
this.emitEvent({
|
|
551
1210
|
type: "transport:error",
|
|
@@ -565,19 +1224,49 @@ var NostrTransportProvider = class {
|
|
|
565
1224
|
* Check if a relay is currently connected
|
|
566
1225
|
*/
|
|
567
1226
|
isRelayConnected(relayUrl) {
|
|
568
|
-
|
|
569
|
-
return
|
|
1227
|
+
if (!this.nostrClient) return false;
|
|
1228
|
+
return this.nostrClient.getConnectedRelays().has(relayUrl);
|
|
570
1229
|
}
|
|
571
1230
|
// ===========================================================================
|
|
572
1231
|
// TransportProvider Implementation
|
|
573
1232
|
// ===========================================================================
|
|
574
1233
|
setIdentity(identity) {
|
|
575
1234
|
this.identity = identity;
|
|
576
|
-
const secretKey =
|
|
1235
|
+
const secretKey = Buffer2.from(identity.privateKey, "hex");
|
|
577
1236
|
this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);
|
|
578
1237
|
const nostrPubkey = this.keyManager.getPublicKeyHex();
|
|
579
1238
|
this.log("Identity set, Nostr pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
580
|
-
if (this.
|
|
1239
|
+
if (this.nostrClient && this.status === "connected") {
|
|
1240
|
+
this.log("Identity changed while connected - recreating NostrClient");
|
|
1241
|
+
const oldClient = this.nostrClient;
|
|
1242
|
+
this.nostrClient = new NostrClient(this.keyManager, {
|
|
1243
|
+
autoReconnect: this.config.autoReconnect,
|
|
1244
|
+
reconnectIntervalMs: this.config.reconnectDelay,
|
|
1245
|
+
maxReconnectIntervalMs: this.config.reconnectDelay * 16,
|
|
1246
|
+
pingIntervalMs: 15e3
|
|
1247
|
+
// 15 second keepalive pings
|
|
1248
|
+
});
|
|
1249
|
+
this.nostrClient.addConnectionListener({
|
|
1250
|
+
onConnect: (url) => {
|
|
1251
|
+
this.log("NostrClient connected to relay:", url);
|
|
1252
|
+
},
|
|
1253
|
+
onDisconnect: (url, reason) => {
|
|
1254
|
+
this.log("NostrClient disconnected from relay:", url, "reason:", reason);
|
|
1255
|
+
},
|
|
1256
|
+
onReconnecting: (url, attempt) => {
|
|
1257
|
+
this.log("NostrClient reconnecting to relay:", url, "attempt:", attempt);
|
|
1258
|
+
},
|
|
1259
|
+
onReconnected: (url) => {
|
|
1260
|
+
this.log("NostrClient reconnected to relay:", url);
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
this.nostrClient.connect(...this.config.relays).then(() => {
|
|
1264
|
+
this.subscribeToEvents();
|
|
1265
|
+
oldClient.disconnect();
|
|
1266
|
+
}).catch((err) => {
|
|
1267
|
+
this.log("Failed to reconnect with new identity:", err);
|
|
1268
|
+
});
|
|
1269
|
+
} else if (this.isConnected()) {
|
|
581
1270
|
this.subscribeToEvents();
|
|
582
1271
|
}
|
|
583
1272
|
}
|
|
@@ -593,18 +1282,17 @@ var NostrTransportProvider = class {
|
|
|
593
1282
|
}
|
|
594
1283
|
async sendMessage(recipientPubkey, content) {
|
|
595
1284
|
this.ensureReady();
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
);
|
|
601
|
-
await this.publishEvent(event);
|
|
1285
|
+
const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith("02") || recipientPubkey.startsWith("03")) ? recipientPubkey.slice(2) : recipientPubkey;
|
|
1286
|
+
const senderNametag = this.identity?.nametag;
|
|
1287
|
+
const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
|
|
1288
|
+
const giftWrap = NIP17.createGiftWrap(this.keyManager, nostrRecipient, wrappedContent);
|
|
1289
|
+
await this.publishEvent(giftWrap);
|
|
602
1290
|
this.emitEvent({
|
|
603
1291
|
type: "message:sent",
|
|
604
1292
|
timestamp: Date.now(),
|
|
605
1293
|
data: { recipient: recipientPubkey }
|
|
606
1294
|
});
|
|
607
|
-
return
|
|
1295
|
+
return giftWrap.id;
|
|
608
1296
|
}
|
|
609
1297
|
onMessage(handler) {
|
|
610
1298
|
this.messageHandlers.add(handler);
|
|
@@ -721,6 +1409,118 @@ var NostrTransportProvider = class {
|
|
|
721
1409
|
if (pubkeyTag?.[1]) return pubkeyTag[1];
|
|
722
1410
|
return null;
|
|
723
1411
|
}
|
|
1412
|
+
async resolveNametagInfo(nametag) {
|
|
1413
|
+
this.ensureReady();
|
|
1414
|
+
const hashedNametag = hashNametag(nametag);
|
|
1415
|
+
let events = await this.queryEvents({
|
|
1416
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1417
|
+
"#t": [hashedNametag],
|
|
1418
|
+
limit: 1
|
|
1419
|
+
});
|
|
1420
|
+
if (events.length === 0) {
|
|
1421
|
+
events = await this.queryEvents({
|
|
1422
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1423
|
+
"#d": [hashedNametag],
|
|
1424
|
+
limit: 1
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
if (events.length === 0) return null;
|
|
1428
|
+
const bindingEvent = events[0];
|
|
1429
|
+
try {
|
|
1430
|
+
const content = JSON.parse(bindingEvent.content);
|
|
1431
|
+
if (content.public_key && content.l1_address) {
|
|
1432
|
+
const l3Address = `PROXY:${hashedNametag}`;
|
|
1433
|
+
return {
|
|
1434
|
+
nametag,
|
|
1435
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1436
|
+
chainPubkey: content.public_key,
|
|
1437
|
+
l1Address: content.l1_address,
|
|
1438
|
+
directAddress: content.direct_address || "",
|
|
1439
|
+
proxyAddress: l3Address,
|
|
1440
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
this.log("Legacy nametag event without extended fields:", nametag);
|
|
1444
|
+
const pubkeyTag = bindingEvent.tags.find((t) => t[0] === "pubkey");
|
|
1445
|
+
const l1Tag = bindingEvent.tags.find((t) => t[0] === "l1");
|
|
1446
|
+
if (pubkeyTag?.[1] && l1Tag?.[1]) {
|
|
1447
|
+
const l3Address = `PROXY:${hashedNametag}`;
|
|
1448
|
+
return {
|
|
1449
|
+
nametag,
|
|
1450
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1451
|
+
chainPubkey: pubkeyTag[1],
|
|
1452
|
+
l1Address: l1Tag[1],
|
|
1453
|
+
directAddress: "",
|
|
1454
|
+
proxyAddress: l3Address,
|
|
1455
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
return {
|
|
1459
|
+
nametag,
|
|
1460
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1461
|
+
chainPubkey: "",
|
|
1462
|
+
// Cannot derive from 32-byte Nostr pubkey
|
|
1463
|
+
l1Address: "",
|
|
1464
|
+
// Cannot derive without 33-byte pubkey
|
|
1465
|
+
directAddress: "",
|
|
1466
|
+
proxyAddress: `PROXY:${hashedNametag}`,
|
|
1467
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1468
|
+
};
|
|
1469
|
+
} catch {
|
|
1470
|
+
return {
|
|
1471
|
+
nametag,
|
|
1472
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1473
|
+
chainPubkey: "",
|
|
1474
|
+
l1Address: "",
|
|
1475
|
+
directAddress: "",
|
|
1476
|
+
proxyAddress: `PROXY:${hashedNametag}`,
|
|
1477
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Recover nametag for the current identity by searching for encrypted nametag events
|
|
1483
|
+
* Used after wallet import to recover associated nametag
|
|
1484
|
+
* @returns Decrypted nametag or null if none found
|
|
1485
|
+
*/
|
|
1486
|
+
async recoverNametag() {
|
|
1487
|
+
this.ensureReady();
|
|
1488
|
+
if (!this.identity || !this.keyManager) {
|
|
1489
|
+
throw new Error("Identity not set");
|
|
1490
|
+
}
|
|
1491
|
+
const nostrPubkey = this.getNostrPubkey();
|
|
1492
|
+
this.log("Searching for nametag events for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1493
|
+
const events = await this.queryEvents({
|
|
1494
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1495
|
+
authors: [nostrPubkey],
|
|
1496
|
+
limit: 10
|
|
1497
|
+
// Get recent events in case of updates
|
|
1498
|
+
});
|
|
1499
|
+
if (events.length === 0) {
|
|
1500
|
+
this.log("No nametag events found for this pubkey");
|
|
1501
|
+
return null;
|
|
1502
|
+
}
|
|
1503
|
+
events.sort((a, b) => b.created_at - a.created_at);
|
|
1504
|
+
for (const event of events) {
|
|
1505
|
+
try {
|
|
1506
|
+
const content = JSON.parse(event.content);
|
|
1507
|
+
if (content.encrypted_nametag) {
|
|
1508
|
+
const decrypted = await decryptNametag(
|
|
1509
|
+
content.encrypted_nametag,
|
|
1510
|
+
this.identity.privateKey
|
|
1511
|
+
);
|
|
1512
|
+
if (decrypted) {
|
|
1513
|
+
this.log("Recovered nametag:", decrypted);
|
|
1514
|
+
return decrypted;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
} catch {
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
this.log("Could not decrypt nametag from any event");
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
724
1524
|
async publishNametag(nametag, address) {
|
|
725
1525
|
this.ensureReady();
|
|
726
1526
|
const hashedNametag = hashNametag(nametag);
|
|
@@ -731,8 +1531,11 @@ var NostrTransportProvider = class {
|
|
|
731
1531
|
await this.publishEvent(event);
|
|
732
1532
|
this.log("Published nametag binding:", nametag);
|
|
733
1533
|
}
|
|
734
|
-
async registerNametag(nametag, _publicKey) {
|
|
1534
|
+
async registerNametag(nametag, _publicKey, directAddress = "") {
|
|
735
1535
|
this.ensureReady();
|
|
1536
|
+
if (!this.identity) {
|
|
1537
|
+
throw new Error("Identity not set");
|
|
1538
|
+
}
|
|
736
1539
|
const nostrPubkey = this.getNostrPubkey();
|
|
737
1540
|
const existing = await this.resolveNametag(nametag);
|
|
738
1541
|
this.log("registerNametag:", nametag, "existing:", existing, "myPubkey:", nostrPubkey);
|
|
@@ -740,27 +1543,42 @@ var NostrTransportProvider = class {
|
|
|
740
1543
|
this.log("Nametag already taken:", nametag, "- owner:", existing);
|
|
741
1544
|
return false;
|
|
742
1545
|
}
|
|
1546
|
+
const privateKeyHex = this.identity.privateKey;
|
|
1547
|
+
const compressedPubkey = getPublicKey(privateKeyHex, true);
|
|
1548
|
+
const l1Address = publicKeyToAddress(compressedPubkey, "alpha");
|
|
1549
|
+
const encryptedNametag = await encryptNametag(nametag, privateKeyHex);
|
|
743
1550
|
const hashedNametag = hashNametag(nametag);
|
|
744
1551
|
const content = JSON.stringify({
|
|
745
1552
|
nametag_hash: hashedNametag,
|
|
746
1553
|
address: nostrPubkey,
|
|
747
|
-
verified: Date.now()
|
|
1554
|
+
verified: Date.now(),
|
|
1555
|
+
// Extended fields for nametag recovery and address lookup
|
|
1556
|
+
encrypted_nametag: encryptedNametag,
|
|
1557
|
+
public_key: compressedPubkey,
|
|
1558
|
+
l1_address: l1Address,
|
|
1559
|
+
direct_address: directAddress
|
|
748
1560
|
});
|
|
749
1561
|
const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, [
|
|
750
1562
|
["d", hashedNametag],
|
|
751
1563
|
["nametag", hashedNametag],
|
|
752
1564
|
["t", hashedNametag],
|
|
753
|
-
["address", nostrPubkey]
|
|
1565
|
+
["address", nostrPubkey],
|
|
1566
|
+
// Extended tags for indexing
|
|
1567
|
+
["pubkey", compressedPubkey],
|
|
1568
|
+
["l1", l1Address]
|
|
754
1569
|
]);
|
|
755
1570
|
await this.publishEvent(event);
|
|
756
|
-
this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1571
|
+
this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...", "l1:", l1Address.slice(0, 12) + "...");
|
|
757
1572
|
return true;
|
|
758
1573
|
}
|
|
1574
|
+
// Track broadcast subscriptions
|
|
1575
|
+
broadcastSubscriptions = /* @__PURE__ */ new Map();
|
|
1576
|
+
// key -> subId
|
|
759
1577
|
subscribeToBroadcast(tags, handler) {
|
|
760
1578
|
const key = tags.sort().join(":");
|
|
761
1579
|
if (!this.broadcastHandlers.has(key)) {
|
|
762
1580
|
this.broadcastHandlers.set(key, /* @__PURE__ */ new Set());
|
|
763
|
-
if (this.isConnected()) {
|
|
1581
|
+
if (this.isConnected() && this.nostrClient) {
|
|
764
1582
|
this.subscribeToTags(tags);
|
|
765
1583
|
}
|
|
766
1584
|
}
|
|
@@ -769,6 +1587,11 @@ var NostrTransportProvider = class {
|
|
|
769
1587
|
this.broadcastHandlers.get(key)?.delete(handler);
|
|
770
1588
|
if (this.broadcastHandlers.get(key)?.size === 0) {
|
|
771
1589
|
this.broadcastHandlers.delete(key);
|
|
1590
|
+
const subId = this.broadcastSubscriptions.get(key);
|
|
1591
|
+
if (subId && this.nostrClient) {
|
|
1592
|
+
this.nostrClient.unsubscribe(subId);
|
|
1593
|
+
this.broadcastSubscriptions.delete(key);
|
|
1594
|
+
}
|
|
772
1595
|
}
|
|
773
1596
|
};
|
|
774
1597
|
}
|
|
@@ -787,81 +1610,19 @@ var NostrTransportProvider = class {
|
|
|
787
1610
|
return () => this.eventCallbacks.delete(callback);
|
|
788
1611
|
}
|
|
789
1612
|
// ===========================================================================
|
|
790
|
-
// Private: Connection Management
|
|
791
|
-
// ===========================================================================
|
|
792
|
-
async connectToRelay(url) {
|
|
793
|
-
return new Promise((resolve, reject) => {
|
|
794
|
-
const ws = this.config.createWebSocket(url);
|
|
795
|
-
const timeout = setTimeout(() => {
|
|
796
|
-
ws.close();
|
|
797
|
-
reject(new Error(`Connection timeout: ${url}`));
|
|
798
|
-
}, this.config.timeout);
|
|
799
|
-
ws.onopen = () => {
|
|
800
|
-
clearTimeout(timeout);
|
|
801
|
-
this.connections.set(url, ws);
|
|
802
|
-
this.reconnectAttempts.set(url, 0);
|
|
803
|
-
this.log("Connected to relay:", url);
|
|
804
|
-
resolve();
|
|
805
|
-
};
|
|
806
|
-
ws.onerror = (error) => {
|
|
807
|
-
clearTimeout(timeout);
|
|
808
|
-
this.log("Relay error:", url, error);
|
|
809
|
-
reject(error);
|
|
810
|
-
};
|
|
811
|
-
ws.onclose = () => {
|
|
812
|
-
this.connections.delete(url);
|
|
813
|
-
if (this.config.autoReconnect && this.status === "connected") {
|
|
814
|
-
this.scheduleReconnect(url);
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
ws.onmessage = (event) => {
|
|
818
|
-
this.handleRelayMessage(url, event.data);
|
|
819
|
-
};
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
scheduleReconnect(url) {
|
|
823
|
-
const attempts = this.reconnectAttempts.get(url) ?? 0;
|
|
824
|
-
if (attempts >= this.config.maxReconnectAttempts) {
|
|
825
|
-
this.log("Max reconnect attempts reached for:", url);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
this.reconnectAttempts.set(url, attempts + 1);
|
|
829
|
-
const delay = this.config.reconnectDelay * Math.pow(2, attempts);
|
|
830
|
-
this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
|
|
831
|
-
setTimeout(() => {
|
|
832
|
-
this.connectToRelay(url).catch(() => {
|
|
833
|
-
});
|
|
834
|
-
}, delay);
|
|
835
|
-
}
|
|
836
|
-
// ===========================================================================
|
|
837
1613
|
// Private: Message Handling
|
|
838
1614
|
// ===========================================================================
|
|
839
|
-
handleRelayMessage(relay, data) {
|
|
840
|
-
try {
|
|
841
|
-
const message = JSON.parse(data);
|
|
842
|
-
const [type, ...args] = message;
|
|
843
|
-
switch (type) {
|
|
844
|
-
case "EVENT":
|
|
845
|
-
this.handleEvent(args[1]);
|
|
846
|
-
break;
|
|
847
|
-
case "EOSE":
|
|
848
|
-
break;
|
|
849
|
-
case "OK":
|
|
850
|
-
break;
|
|
851
|
-
case "NOTICE":
|
|
852
|
-
this.log("Relay notice:", relay, args[0]);
|
|
853
|
-
break;
|
|
854
|
-
}
|
|
855
|
-
} catch (error) {
|
|
856
|
-
this.log("Failed to parse relay message:", error);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
1615
|
async handleEvent(event) {
|
|
1616
|
+
this.log("Processing event kind:", event.kind, "id:", event.id?.slice(0, 12));
|
|
860
1617
|
try {
|
|
861
1618
|
switch (event.kind) {
|
|
862
1619
|
case EVENT_KINDS.DIRECT_MESSAGE:
|
|
863
1620
|
await this.handleDirectMessage(event);
|
|
864
1621
|
break;
|
|
1622
|
+
case EventKinds.GIFT_WRAP:
|
|
1623
|
+
this.log("Handling gift wrap (NIP-17 DM)");
|
|
1624
|
+
await this.handleGiftWrap(event);
|
|
1625
|
+
break;
|
|
865
1626
|
case EVENT_KINDS.TOKEN_TRANSFER:
|
|
866
1627
|
await this.handleTokenTransfer(event);
|
|
867
1628
|
break;
|
|
@@ -880,23 +1641,54 @@ var NostrTransportProvider = class {
|
|
|
880
1641
|
}
|
|
881
1642
|
}
|
|
882
1643
|
async handleDirectMessage(event) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1644
|
+
this.log("Ignoring NIP-04 kind 4 event (DMs use NIP-17):", event.id?.slice(0, 12));
|
|
1645
|
+
}
|
|
1646
|
+
async handleGiftWrap(event) {
|
|
1647
|
+
if (!this.identity || !this.keyManager) {
|
|
1648
|
+
this.log("handleGiftWrap: no identity/keyManager");
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
try {
|
|
1652
|
+
const pm = NIP17.unwrap(event, this.keyManager);
|
|
1653
|
+
this.log("Gift wrap unwrapped, sender:", pm.senderPubkey?.slice(0, 16), "kind:", pm.kind);
|
|
1654
|
+
if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {
|
|
1655
|
+
this.log("Skipping own message");
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
if (pm.kind !== EventKinds.CHAT_MESSAGE) {
|
|
1659
|
+
this.log("Skipping non-chat message, kind:", pm.kind);
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
let content = pm.content;
|
|
1663
|
+
let senderNametag;
|
|
895
1664
|
try {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1665
|
+
const parsed = JSON.parse(content);
|
|
1666
|
+
if (typeof parsed === "object" && parsed.text !== void 0) {
|
|
1667
|
+
content = parsed.text;
|
|
1668
|
+
senderNametag = parsed.senderNametag || void 0;
|
|
1669
|
+
}
|
|
1670
|
+
} catch {
|
|
899
1671
|
}
|
|
1672
|
+
this.log("DM received from:", senderNametag || pm.senderPubkey?.slice(0, 16), "content:", content?.slice(0, 50));
|
|
1673
|
+
const message = {
|
|
1674
|
+
id: pm.eventId,
|
|
1675
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
1676
|
+
senderNametag,
|
|
1677
|
+
content,
|
|
1678
|
+
timestamp: pm.timestamp * 1e3,
|
|
1679
|
+
encrypted: true
|
|
1680
|
+
};
|
|
1681
|
+
this.emitEvent({ type: "message:received", timestamp: Date.now() });
|
|
1682
|
+
this.log("Dispatching to", this.messageHandlers.size, "handlers");
|
|
1683
|
+
for (const handler of this.messageHandlers) {
|
|
1684
|
+
try {
|
|
1685
|
+
handler(message);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
this.log("Message handler error:", error);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
} catch (err) {
|
|
1691
|
+
this.log("Gift wrap decrypt failed (expected if not for us):", err?.message?.slice(0, 50));
|
|
900
1692
|
}
|
|
901
1693
|
}
|
|
902
1694
|
async handleTokenTransfer(event) {
|
|
@@ -905,7 +1697,7 @@ var NostrTransportProvider = class {
|
|
|
905
1697
|
const payload = JSON.parse(content);
|
|
906
1698
|
const transfer = {
|
|
907
1699
|
id: event.id,
|
|
908
|
-
|
|
1700
|
+
senderTransportPubkey: event.pubkey,
|
|
909
1701
|
payload,
|
|
910
1702
|
timestamp: event.created_at * 1e3
|
|
911
1703
|
};
|
|
@@ -925,7 +1717,7 @@ var NostrTransportProvider = class {
|
|
|
925
1717
|
const requestData = JSON.parse(content);
|
|
926
1718
|
const request = {
|
|
927
1719
|
id: event.id,
|
|
928
|
-
|
|
1720
|
+
senderTransportPubkey: event.pubkey,
|
|
929
1721
|
request: {
|
|
930
1722
|
requestId: requestData.requestId,
|
|
931
1723
|
amount: requestData.amount,
|
|
@@ -955,7 +1747,7 @@ var NostrTransportProvider = class {
|
|
|
955
1747
|
const responseData = JSON.parse(content);
|
|
956
1748
|
const response = {
|
|
957
1749
|
id: event.id,
|
|
958
|
-
|
|
1750
|
+
responderTransportPubkey: event.pubkey,
|
|
959
1751
|
response: {
|
|
960
1752
|
requestId: responseData.requestId,
|
|
961
1753
|
responseType: responseData.responseType,
|
|
@@ -980,7 +1772,7 @@ var NostrTransportProvider = class {
|
|
|
980
1772
|
const tags = event.tags.filter((t) => t[0] === "t").map((t) => t[1]);
|
|
981
1773
|
const broadcast = {
|
|
982
1774
|
id: event.id,
|
|
983
|
-
|
|
1775
|
+
authorTransportPubkey: event.pubkey,
|
|
984
1776
|
content: event.content,
|
|
985
1777
|
tags,
|
|
986
1778
|
timestamp: event.created_at * 1e3
|
|
@@ -1035,109 +1827,149 @@ var NostrTransportProvider = class {
|
|
|
1035
1827
|
return this.createEvent(kind, encrypted, tags);
|
|
1036
1828
|
}
|
|
1037
1829
|
async publishEvent(event) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
ws.send(message);
|
|
1046
|
-
resolve();
|
|
1047
|
-
});
|
|
1048
|
-
});
|
|
1049
|
-
await Promise.any(publishPromises);
|
|
1830
|
+
if (!this.nostrClient) {
|
|
1831
|
+
throw new Error("NostrClient not initialized");
|
|
1832
|
+
}
|
|
1833
|
+
const sdkEvent = NostrEventClass.fromJSON(event);
|
|
1834
|
+
await this.nostrClient.publishEvent(sdkEvent);
|
|
1050
1835
|
}
|
|
1051
|
-
async queryEvents(
|
|
1052
|
-
if (this.
|
|
1836
|
+
async queryEvents(filterObj) {
|
|
1837
|
+
if (!this.nostrClient || !this.nostrClient.isConnected()) {
|
|
1053
1838
|
throw new Error("No connected relays");
|
|
1054
1839
|
}
|
|
1055
|
-
const queryPromises = Array.from(this.connections.values()).map(
|
|
1056
|
-
(ws) => this.queryEventsFromRelay(ws, filter)
|
|
1057
|
-
);
|
|
1058
|
-
const results = await Promise.allSettled(queryPromises);
|
|
1059
|
-
for (const result of results) {
|
|
1060
|
-
if (result.status === "fulfilled" && result.value.length > 0) {
|
|
1061
|
-
return result.value;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
return [];
|
|
1065
|
-
}
|
|
1066
|
-
async queryEventsFromRelay(ws, filter) {
|
|
1067
|
-
const subId = this.config.generateUUID().slice(0, 8);
|
|
1068
1840
|
const events = [];
|
|
1841
|
+
const filter = new Filter(filterObj);
|
|
1069
1842
|
return new Promise((resolve) => {
|
|
1070
1843
|
const timeout = setTimeout(() => {
|
|
1071
|
-
|
|
1844
|
+
if (subId) {
|
|
1845
|
+
this.nostrClient?.unsubscribe(subId);
|
|
1846
|
+
}
|
|
1072
1847
|
resolve(events);
|
|
1073
1848
|
}, 5e3);
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1849
|
+
const subId = this.nostrClient.subscribe(filter, {
|
|
1850
|
+
onEvent: (event) => {
|
|
1851
|
+
events.push({
|
|
1852
|
+
id: event.id,
|
|
1853
|
+
kind: event.kind,
|
|
1854
|
+
content: event.content,
|
|
1855
|
+
tags: event.tags,
|
|
1856
|
+
pubkey: event.pubkey,
|
|
1857
|
+
created_at: event.created_at,
|
|
1858
|
+
sig: event.sig
|
|
1859
|
+
});
|
|
1860
|
+
},
|
|
1861
|
+
onEndOfStoredEvents: () => {
|
|
1085
1862
|
clearTimeout(timeout);
|
|
1086
|
-
|
|
1087
|
-
this.unsubscribeFromRelay(ws, subId);
|
|
1863
|
+
this.nostrClient?.unsubscribe(subId);
|
|
1088
1864
|
resolve(events);
|
|
1089
1865
|
}
|
|
1090
|
-
};
|
|
1091
|
-
ws.send(JSON.stringify(["REQ", subId, filter]));
|
|
1866
|
+
});
|
|
1092
1867
|
});
|
|
1093
1868
|
}
|
|
1094
|
-
unsubscribeFromRelay(ws, subId) {
|
|
1095
|
-
if (ws.readyState === WebSocketReadyState.OPEN) {
|
|
1096
|
-
ws.send(JSON.stringify(["CLOSE", subId]));
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
1869
|
// ===========================================================================
|
|
1100
1870
|
// Private: Subscriptions
|
|
1101
1871
|
// ===========================================================================
|
|
1872
|
+
// Track subscription IDs for cleanup
|
|
1873
|
+
walletSubscriptionId = null;
|
|
1874
|
+
chatSubscriptionId = null;
|
|
1102
1875
|
subscribeToEvents() {
|
|
1103
|
-
|
|
1104
|
-
|
|
1876
|
+
this.log("subscribeToEvents called, identity:", !!this.identity, "keyManager:", !!this.keyManager, "nostrClient:", !!this.nostrClient);
|
|
1877
|
+
if (!this.identity || !this.keyManager || !this.nostrClient) {
|
|
1878
|
+
this.log("subscribeToEvents: skipped - no identity, keyManager, or nostrClient");
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (this.walletSubscriptionId) {
|
|
1882
|
+
this.nostrClient.unsubscribe(this.walletSubscriptionId);
|
|
1883
|
+
this.walletSubscriptionId = null;
|
|
1884
|
+
}
|
|
1885
|
+
if (this.chatSubscriptionId) {
|
|
1886
|
+
this.nostrClient.unsubscribe(this.chatSubscriptionId);
|
|
1887
|
+
this.chatSubscriptionId = null;
|
|
1888
|
+
}
|
|
1889
|
+
if (this.mainSubscriptionId) {
|
|
1890
|
+
this.nostrClient.unsubscribe(this.mainSubscriptionId);
|
|
1891
|
+
this.mainSubscriptionId = null;
|
|
1892
|
+
}
|
|
1105
1893
|
const nostrPubkey = this.keyManager.getPublicKeyHex();
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1894
|
+
this.log("Subscribing with Nostr pubkey:", nostrPubkey);
|
|
1895
|
+
const walletFilter = new Filter();
|
|
1896
|
+
walletFilter.kinds = [
|
|
1897
|
+
EVENT_KINDS.DIRECT_MESSAGE,
|
|
1898
|
+
EVENT_KINDS.TOKEN_TRANSFER,
|
|
1899
|
+
EVENT_KINDS.PAYMENT_REQUEST,
|
|
1900
|
+
EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
|
|
1901
|
+
];
|
|
1902
|
+
walletFilter["#p"] = [nostrPubkey];
|
|
1903
|
+
walletFilter.since = Math.floor(Date.now() / 1e3) - 86400;
|
|
1904
|
+
this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {
|
|
1905
|
+
onEvent: (event) => {
|
|
1906
|
+
this.log("Received wallet event kind:", event.kind, "id:", event.id?.slice(0, 12));
|
|
1907
|
+
this.handleEvent({
|
|
1908
|
+
id: event.id,
|
|
1909
|
+
kind: event.kind,
|
|
1910
|
+
content: event.content,
|
|
1911
|
+
tags: event.tags,
|
|
1912
|
+
pubkey: event.pubkey,
|
|
1913
|
+
created_at: event.created_at,
|
|
1914
|
+
sig: event.sig
|
|
1915
|
+
});
|
|
1916
|
+
},
|
|
1917
|
+
onEndOfStoredEvents: () => {
|
|
1918
|
+
this.log("Wallet subscription ready (EOSE)");
|
|
1919
|
+
},
|
|
1920
|
+
onError: (_subId, error) => {
|
|
1921
|
+
this.log("Wallet subscription error:", error);
|
|
1121
1922
|
}
|
|
1122
|
-
}
|
|
1123
|
-
this.
|
|
1124
|
-
|
|
1923
|
+
});
|
|
1924
|
+
this.log("Wallet subscription created, subId:", this.walletSubscriptionId);
|
|
1925
|
+
const chatFilter = new Filter();
|
|
1926
|
+
chatFilter.kinds = [EventKinds.GIFT_WRAP];
|
|
1927
|
+
chatFilter["#p"] = [nostrPubkey];
|
|
1928
|
+
this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {
|
|
1929
|
+
onEvent: (event) => {
|
|
1930
|
+
this.log("Received chat event kind:", event.kind, "id:", event.id?.slice(0, 12));
|
|
1931
|
+
this.handleEvent({
|
|
1932
|
+
id: event.id,
|
|
1933
|
+
kind: event.kind,
|
|
1934
|
+
content: event.content,
|
|
1935
|
+
tags: event.tags,
|
|
1936
|
+
pubkey: event.pubkey,
|
|
1937
|
+
created_at: event.created_at,
|
|
1938
|
+
sig: event.sig
|
|
1939
|
+
});
|
|
1940
|
+
},
|
|
1941
|
+
onEndOfStoredEvents: () => {
|
|
1942
|
+
this.log("Chat subscription ready (EOSE)");
|
|
1943
|
+
},
|
|
1944
|
+
onError: (_subId, error) => {
|
|
1945
|
+
this.log("Chat subscription error:", error);
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
this.log("Chat subscription created, subId:", this.chatSubscriptionId);
|
|
1125
1949
|
}
|
|
1126
1950
|
subscribeToTags(tags) {
|
|
1127
|
-
|
|
1128
|
-
const
|
|
1951
|
+
if (!this.nostrClient) return;
|
|
1952
|
+
const key = tags.sort().join(":");
|
|
1953
|
+
const filter = new Filter({
|
|
1129
1954
|
kinds: [EVENT_KINDS.BROADCAST],
|
|
1130
1955
|
"#t": tags,
|
|
1131
1956
|
since: Math.floor(Date.now() / 1e3) - 3600
|
|
1132
1957
|
// Last hour
|
|
1133
|
-
};
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1958
|
+
});
|
|
1959
|
+
const subId = this.nostrClient.subscribe(filter, {
|
|
1960
|
+
onEvent: (event) => {
|
|
1961
|
+
this.handleBroadcast({
|
|
1962
|
+
id: event.id,
|
|
1963
|
+
kind: event.kind,
|
|
1964
|
+
content: event.content,
|
|
1965
|
+
tags: event.tags,
|
|
1966
|
+
pubkey: event.pubkey,
|
|
1967
|
+
created_at: event.created_at,
|
|
1968
|
+
sig: event.sig
|
|
1969
|
+
});
|
|
1138
1970
|
}
|
|
1139
|
-
}
|
|
1140
|
-
this.
|
|
1971
|
+
});
|
|
1972
|
+
this.broadcastSubscriptions.set(key, subId);
|
|
1141
1973
|
}
|
|
1142
1974
|
// ===========================================================================
|
|
1143
1975
|
// Private: Encryption
|
|
@@ -1598,13 +2430,72 @@ var UnicityAggregatorProvider = class {
|
|
|
1598
2430
|
};
|
|
1599
2431
|
var UnicityOracleProvider = UnicityAggregatorProvider;
|
|
1600
2432
|
|
|
2433
|
+
// assets/trustbase.ts
|
|
2434
|
+
var TRUSTBASE_TESTNET = {
|
|
2435
|
+
version: 1,
|
|
2436
|
+
networkId: 3,
|
|
2437
|
+
epoch: 1,
|
|
2438
|
+
epochStartRound: 1,
|
|
2439
|
+
rootNodes: [
|
|
2440
|
+
{
|
|
2441
|
+
nodeId: "16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU",
|
|
2442
|
+
sigKey: "0x039afb2acb65f5fbc272d8907f763d0a5d189aadc9b97afdcc5897ea4dd112e68b",
|
|
2443
|
+
stake: 1
|
|
2444
|
+
}
|
|
2445
|
+
],
|
|
2446
|
+
quorumThreshold: 1,
|
|
2447
|
+
stateHash: "",
|
|
2448
|
+
changeRecordHash: "",
|
|
2449
|
+
previousEntryHash: "",
|
|
2450
|
+
signatures: {
|
|
2451
|
+
"16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU": "0xf157c9fdd8a378e3ca70d354ccc4475ab2cd8de360127bc46b0aeab4b453a80f07fd9136a5843b60a8babaff23e20acc8879861f7651440a5e2829f7541b31f100"
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
var TRUSTBASE_MAINNET = null;
|
|
2455
|
+
var TRUSTBASE_DEV = TRUSTBASE_TESTNET;
|
|
2456
|
+
|
|
2457
|
+
// impl/shared/trustbase-loader.ts
|
|
2458
|
+
function getEmbeddedTrustBase(network) {
|
|
2459
|
+
switch (network) {
|
|
2460
|
+
case "mainnet":
|
|
2461
|
+
return TRUSTBASE_MAINNET;
|
|
2462
|
+
case "testnet":
|
|
2463
|
+
return TRUSTBASE_TESTNET;
|
|
2464
|
+
case "dev":
|
|
2465
|
+
return TRUSTBASE_DEV;
|
|
2466
|
+
default:
|
|
2467
|
+
return TRUSTBASE_TESTNET;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
var BaseTrustBaseLoader = class {
|
|
2471
|
+
network;
|
|
2472
|
+
constructor(network = "testnet") {
|
|
2473
|
+
this.network = network;
|
|
2474
|
+
}
|
|
2475
|
+
async load() {
|
|
2476
|
+
const external = await this.loadFromExternal();
|
|
2477
|
+
if (external) {
|
|
2478
|
+
return external;
|
|
2479
|
+
}
|
|
2480
|
+
return getEmbeddedTrustBase(this.network);
|
|
2481
|
+
}
|
|
2482
|
+
};
|
|
2483
|
+
|
|
1601
2484
|
// impl/nodejs/oracle/index.ts
|
|
1602
|
-
var NodeTrustBaseLoader = class {
|
|
2485
|
+
var NodeTrustBaseLoader = class extends BaseTrustBaseLoader {
|
|
1603
2486
|
filePath;
|
|
1604
|
-
constructor(
|
|
1605
|
-
|
|
2487
|
+
constructor(filePathOrNetwork) {
|
|
2488
|
+
if (!filePathOrNetwork) {
|
|
2489
|
+
super("testnet");
|
|
2490
|
+
} else if (filePathOrNetwork.includes("/") || filePathOrNetwork.includes(".")) {
|
|
2491
|
+
super("testnet");
|
|
2492
|
+
this.filePath = filePathOrNetwork;
|
|
2493
|
+
} else {
|
|
2494
|
+
super(filePathOrNetwork);
|
|
2495
|
+
}
|
|
1606
2496
|
}
|
|
1607
|
-
async
|
|
2497
|
+
async loadFromExternal() {
|
|
2498
|
+
if (!this.filePath) return null;
|
|
1608
2499
|
try {
|
|
1609
2500
|
if (fs3.existsSync(this.filePath)) {
|
|
1610
2501
|
const content = fs3.readFileSync(this.filePath, "utf-8");
|
|
@@ -1615,14 +2506,14 @@ var NodeTrustBaseLoader = class {
|
|
|
1615
2506
|
return null;
|
|
1616
2507
|
}
|
|
1617
2508
|
};
|
|
1618
|
-
function createNodeTrustBaseLoader(
|
|
1619
|
-
return new NodeTrustBaseLoader(
|
|
2509
|
+
function createNodeTrustBaseLoader(filePathOrNetwork) {
|
|
2510
|
+
return new NodeTrustBaseLoader(filePathOrNetwork);
|
|
1620
2511
|
}
|
|
1621
2512
|
function createUnicityAggregatorProvider(config) {
|
|
1622
|
-
const { trustBasePath, ...restConfig } = config;
|
|
2513
|
+
const { trustBasePath, network, ...restConfig } = config;
|
|
1623
2514
|
return new UnicityAggregatorProvider({
|
|
1624
2515
|
...restConfig,
|
|
1625
|
-
trustBaseLoader: createNodeTrustBaseLoader(trustBasePath)
|
|
2516
|
+
trustBaseLoader: createNodeTrustBaseLoader(trustBasePath ?? network ?? "testnet")
|
|
1626
2517
|
});
|
|
1627
2518
|
}
|
|
1628
2519
|
var createUnicityOracleProvider = createUnicityAggregatorProvider;
|
|
@@ -1656,7 +2547,7 @@ function resolveOracleConfig(network, config) {
|
|
|
1656
2547
|
const networkConfig = getNetworkConfig(network);
|
|
1657
2548
|
return {
|
|
1658
2549
|
url: config?.url ?? networkConfig.aggregatorUrl,
|
|
1659
|
-
apiKey: config?.apiKey,
|
|
2550
|
+
apiKey: config?.apiKey ?? DEFAULT_AGGREGATOR_API_KEY,
|
|
1660
2551
|
timeout: config?.timeout,
|
|
1661
2552
|
skipVerification: config?.skipVerification,
|
|
1662
2553
|
debug: config?.debug,
|
|
@@ -1701,7 +2592,8 @@ function createNodeProviders(config) {
|
|
|
1701
2592
|
timeout: oracleConfig.timeout,
|
|
1702
2593
|
trustBasePath: oracleConfig.trustBasePath,
|
|
1703
2594
|
skipVerification: oracleConfig.skipVerification,
|
|
1704
|
-
debug: oracleConfig.debug
|
|
2595
|
+
debug: oracleConfig.debug,
|
|
2596
|
+
network
|
|
1705
2597
|
}),
|
|
1706
2598
|
l1: l1Config
|
|
1707
2599
|
};
|
|
@@ -1722,4 +2614,9 @@ export {
|
|
|
1722
2614
|
createUnicityAggregatorProvider,
|
|
1723
2615
|
createUnicityOracleProvider
|
|
1724
2616
|
};
|
|
2617
|
+
/*! Bundled license information:
|
|
2618
|
+
|
|
2619
|
+
@noble/hashes/utils.js:
|
|
2620
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
2621
|
+
*/
|
|
1725
2622
|
//# sourceMappingURL=index.js.map
|