epistery 1.3.0 → 1.3.1

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/client/wallet.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Wallet - Base class for client wallets
3
- *
3
+ *
4
4
  * Handles wallet creation, persistence, and signing for Epistery
5
5
  */
6
6
 
@@ -17,17 +17,17 @@ export class Wallet {
17
17
  return {
18
18
  address: this.address,
19
19
  publicKey: this.publicKey,
20
- source: this.source
20
+ source: this.source,
21
21
  };
22
22
  }
23
23
 
24
24
  // Factory method to create appropriate wallet type from saved data
25
25
  static async fromJSON(data, ethers) {
26
- if (data.source === 'web3') {
26
+ if (data.source === "web3") {
27
27
  return await Web3Wallet.fromJSON(data, ethers);
28
- } else if (data.source === 'local') {
28
+ } else if (data.source === "local") {
29
29
  return await BrowserWallet.fromJSON(data, ethers);
30
- } else if (data.source === 'rivet') {
30
+ } else if (data.source === "rivet") {
31
31
  return await RivetWallet.fromJSON(data, ethers);
32
32
  }
33
33
  throw new Error(`Unknown wallet source: ${data.source}`);
@@ -35,11 +35,11 @@ export class Wallet {
35
35
 
36
36
  // Abstract methods - must be implemented by subclasses
37
37
  async sign(message) {
38
- throw new Error('sign() must be implemented by subclass');
38
+ throw new Error("sign() must be implemented by subclass");
39
39
  }
40
40
 
41
41
  static async create(ethers) {
42
- throw new Error('create() must be implemented by subclass');
42
+ throw new Error("create() must be implemented by subclass");
43
43
  }
44
44
  }
45
45
 
@@ -47,7 +47,7 @@ export class Wallet {
47
47
  export class Web3Wallet extends Wallet {
48
48
  constructor() {
49
49
  super();
50
- this.source = 'web3';
50
+ this.source = "web3";
51
51
  this.signer = null;
52
52
  this.provider = null;
53
53
  }
@@ -64,7 +64,7 @@ export class Web3Wallet extends Wallet {
64
64
  const wallet = new Web3Wallet();
65
65
  wallet.address = data.address;
66
66
  wallet.publicKey = data.publicKey;
67
-
67
+
68
68
  // Attempt to reconnect to Web3 provider
69
69
  await wallet.reconnectWeb3(ethers);
70
70
  return wallet;
@@ -72,7 +72,7 @@ export class Web3Wallet extends Wallet {
72
72
 
73
73
  static async create(ethers) {
74
74
  const wallet = new Web3Wallet();
75
-
75
+
76
76
  if (await wallet.connectWeb3(ethers)) {
77
77
  return wallet;
78
78
  }
@@ -81,22 +81,22 @@ export class Web3Wallet extends Wallet {
81
81
 
82
82
  async connectWeb3(ethers) {
83
83
  try {
84
- if (typeof window !== 'undefined' && (window.ethereum || window.web3)) {
84
+ if (typeof window !== "undefined" && (window.ethereum || window.web3)) {
85
85
  const provider = window.ethereum || window.web3.currentProvider;
86
-
86
+
87
87
  // Request account access
88
- const accounts = await provider.request({
89
- method: 'eth_requestAccounts'
88
+ const accounts = await provider.request({
89
+ method: "eth_requestAccounts",
90
90
  });
91
-
91
+
92
92
  if (accounts && accounts.length > 0) {
93
93
  this.address = accounts[0];
94
94
  this.provider = new ethers.providers.Web3Provider(provider);
95
95
  this.signer = this.provider.getSigner();
96
-
96
+
97
97
  // Get public key from first signature
98
98
  this.publicKey = await this.derivePublicKeyPlaceholder();
99
-
99
+
100
100
  return true;
101
101
  }
102
102
  }
@@ -108,11 +108,11 @@ export class Web3Wallet extends Wallet {
108
108
 
109
109
  async reconnectWeb3(ethers) {
110
110
  try {
111
- if (typeof window !== 'undefined' && (window.ethereum || window.web3)) {
111
+ if (typeof window !== "undefined" && (window.ethereum || window.web3)) {
112
112
  const provider = window.ethereum || window.web3.currentProvider;
113
113
  this.provider = new ethers.providers.Web3Provider(provider);
114
114
  this.signer = this.provider.getSigner();
115
-
115
+
116
116
  // Verify the address matches what we have stored
117
117
  const currentAddress = await this.signer.getAddress();
118
118
  if (currentAddress.toLowerCase() !== this.address.toLowerCase()) {
@@ -128,22 +128,26 @@ export class Web3Wallet extends Wallet {
128
128
 
129
129
  async sign(message, ethers) {
130
130
  if (!this.signer) {
131
- throw new Error('Web3 signer not available');
131
+ throw new Error("Web3 signer not available");
132
132
  }
133
-
133
+
134
134
  const signature = await this.signer.signMessage(message);
135
-
135
+
136
136
  // Always update public key from signature for Web3 wallets
137
137
  if (ethers) {
138
- this.publicKey = await this.derivePublicKeyFromSignature(message, signature, ethers);
138
+ this.publicKey = await this.derivePublicKeyFromSignature(
139
+ message,
140
+ signature,
141
+ ethers,
142
+ );
139
143
  }
140
-
144
+
141
145
  return signature;
142
146
  }
143
147
 
144
148
  async derivePublicKeyPlaceholder() {
145
149
  // Placeholder until we get a real signature
146
- return `0x04${this.address.slice(2)}${'0'.repeat(64)}`;
150
+ return `0x04${this.address.slice(2)}${"0".repeat(64)}`;
147
151
  }
148
152
 
149
153
  async derivePublicKeyFromSignature(message, signature, ethers) {
@@ -151,7 +155,7 @@ export class Web3Wallet extends Wallet {
151
155
  const messageHash = ethers.utils.hashMessage(message);
152
156
  return ethers.utils.recoverPublicKey(messageHash, signature);
153
157
  } catch (error) {
154
- console.error('Failed to derive public key from signature:', error);
158
+ console.error("Failed to derive public key from signature:", error);
155
159
  return this.derivePublicKeyPlaceholder();
156
160
  }
157
161
  }
@@ -161,7 +165,7 @@ export class Web3Wallet extends Wallet {
161
165
  export class BrowserWallet extends Wallet {
162
166
  constructor() {
163
167
  super();
164
- this.source = 'local';
168
+ this.source = "local";
165
169
  this.mnemonic = null;
166
170
  this.privateKey = null;
167
171
  this.signer = null;
@@ -171,7 +175,7 @@ export class BrowserWallet extends Wallet {
171
175
  return {
172
176
  ...super.toJSON(),
173
177
  mnemonic: this.mnemonic,
174
- privateKey: this.privateKey
178
+ privateKey: this.privateKey,
175
179
  };
176
180
  }
177
181
 
@@ -181,33 +185,33 @@ export class BrowserWallet extends Wallet {
181
185
  wallet.publicKey = data.publicKey;
182
186
  wallet.mnemonic = data.mnemonic;
183
187
  wallet.privateKey = data.privateKey;
184
-
188
+
185
189
  // Recreate the signer
186
190
  if (wallet.mnemonic) {
187
191
  wallet.signer = ethers.Wallet.fromMnemonic(wallet.mnemonic);
188
192
  }
189
-
193
+
190
194
  return wallet;
191
195
  }
192
196
 
193
197
  static async create(ethers) {
194
198
  const wallet = new BrowserWallet();
195
-
199
+
196
200
  // Generate new wallet
197
201
  const ethersWallet = ethers.Wallet.createRandom();
198
-
202
+
199
203
  wallet.address = ethersWallet.address;
200
- wallet.mnemonic = ethersWallet.mnemonic?.phrase || '';
204
+ wallet.mnemonic = ethersWallet.mnemonic?.phrase || "";
201
205
  wallet.publicKey = ethersWallet.publicKey;
202
206
  wallet.privateKey = ethersWallet.privateKey;
203
207
  wallet.signer = ethersWallet;
204
-
208
+
205
209
  return wallet;
206
210
  }
207
211
 
208
212
  async sign(message) {
209
213
  if (!this.signer) {
210
- throw new Error('Browser wallet signer not available');
214
+ throw new Error("Browser wallet signer not available");
211
215
  }
212
216
 
213
217
  return await this.signer.signMessage(message);
@@ -218,9 +222,9 @@ export class BrowserWallet extends Wallet {
218
222
  export class RivetWallet extends Wallet {
219
223
  constructor() {
220
224
  super();
221
- this.source = 'rivet';
225
+ this.source = "rivet";
222
226
  this.keyId = null;
223
- this.type = 'Browser'; // Browser, Contract, or Web3
227
+ this.type = "Browser"; // Browser, Contract, or Web3
224
228
  this.label = null;
225
229
  this.provider = null;
226
230
  this.createdAt = null;
@@ -243,7 +247,7 @@ export class RivetWallet extends Wallet {
243
247
  encryptedPrivateKey: this.encryptedPrivateKey,
244
248
  contractAddress: this.contractAddress,
245
249
  rivetAddress: this.rivetAddress,
246
- associations: this.associations
250
+ associations: this.associations,
247
251
  };
248
252
  }
249
253
 
@@ -252,7 +256,7 @@ export class RivetWallet extends Wallet {
252
256
  wallet.address = data.address;
253
257
  wallet.publicKey = data.publicKey;
254
258
  wallet.keyId = data.keyId;
255
- wallet.type = data.type || 'Browser';
259
+ wallet.type = data.type || "Browser";
256
260
  wallet.label = data.label;
257
261
  wallet.provider = data.provider;
258
262
  wallet.createdAt = data.createdAt;
@@ -270,14 +274,17 @@ export class RivetWallet extends Wallet {
270
274
 
271
275
  try {
272
276
  // Generate unique keyId
273
- wallet.keyId = 'rivet-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
277
+ wallet.keyId =
278
+ "rivet-" + Date.now() + "-" + Math.random().toString(36).substr(2, 9);
274
279
  wallet.createdAt = Date.now();
275
280
  wallet.lastUpdated = Date.now();
276
- wallet.label = 'Browser Wallet';
281
+ wallet.label = "Browser Wallet";
277
282
 
278
283
  // Check if Web Crypto API is available
279
284
  if (!window.crypto || !window.crypto.subtle) {
280
- console.warn('Web Crypto API not available, falling back to extractable keys');
285
+ console.warn(
286
+ "Web Crypto API not available, falling back to extractable keys",
287
+ );
281
288
  // Fallback to regular ethers wallet
282
289
  const ethersWallet = ethers.Wallet.createRandom();
283
290
  wallet.address = ethersWallet.address;
@@ -289,11 +296,11 @@ export class RivetWallet extends Wallet {
289
296
  // Generate non-extractable AES-GCM key for encrypting the secp256k1 private key
290
297
  const masterKey = await crypto.subtle.generateKey(
291
298
  {
292
- name: 'AES-GCM',
293
- length: 256
299
+ name: "AES-GCM",
300
+ length: 256,
294
301
  },
295
302
  false, // non-extractable!
296
- ['encrypt', 'decrypt']
303
+ ["encrypt", "decrypt"],
297
304
  );
298
305
 
299
306
  // Store master key in IndexedDB (non-extractable CryptoKey)
@@ -310,39 +317,41 @@ export class RivetWallet extends Wallet {
310
317
 
311
318
  const encryptedBuffer = await crypto.subtle.encrypt(
312
319
  {
313
- name: 'AES-GCM',
314
- iv: iv
320
+ name: "AES-GCM",
321
+ iv: iv,
315
322
  },
316
323
  masterKey,
317
- privateKeyBytes
324
+ privateKeyBytes,
318
325
  );
319
326
 
320
327
  // Store encrypted private key and IV
321
328
  wallet.encryptedPrivateKey = JSON.stringify({
322
329
  encrypted: ethers.utils.hexlify(new Uint8Array(encryptedBuffer)),
323
- iv: ethers.utils.hexlify(iv)
330
+ iv: ethers.utils.hexlify(iv),
324
331
  });
325
332
 
326
333
  return wallet;
327
334
  } catch (error) {
328
- console.error('Failed to create rivet wallet:', error);
335
+ console.error("Failed to create rivet wallet:", error);
329
336
  throw error;
330
337
  }
331
338
  }
332
339
 
333
340
  /**
334
- * Signs a message only (client-side)
335
- *
336
- * @param {object} message - blob of data to sign
337
- * @param {ethers} ethers - ethers.js instance
338
- * @returns {Promise<string>} Signed message as hex string
339
- */
341
+ * Signs a message only (client-side)
342
+ *
343
+ * @param {object} message - blob of data to sign
344
+ * @param {ethers} ethers - ethers.js instance
345
+ * @returns {Promise<string>} Signed message as hex string
346
+ */
340
347
  async sign(message, ethers) {
341
348
  try {
342
349
  // Retrieve master key from IndexedDB
343
350
  const masterKey = await RivetWallet.getMasterKey(this.keyId);
344
351
  if (!masterKey) {
345
- throw new Error('Master key not found - rivet may have been created in a different browser context');
352
+ throw new Error(
353
+ "Master key not found - rivet may have been created in a different browser context",
354
+ );
346
355
  }
347
356
 
348
357
  // Decrypt the private key
@@ -352,11 +361,11 @@ export class RivetWallet extends Wallet {
352
361
 
353
362
  const decryptedBuffer = await crypto.subtle.decrypt(
354
363
  {
355
- name: 'AES-GCM',
356
- iv: ivBytes
364
+ name: "AES-GCM",
365
+ iv: ivBytes,
357
366
  },
358
367
  masterKey,
359
- encryptedBytes
368
+ encryptedBytes,
360
369
  );
361
370
 
362
371
  const privateKey = ethers.utils.hexlify(new Uint8Array(decryptedBuffer));
@@ -367,7 +376,7 @@ export class RivetWallet extends Wallet {
367
376
 
368
377
  return signature;
369
378
  } catch (error) {
370
- console.error('Failed to sign message with rivet:', error);
379
+ console.error("Failed to sign message with rivet:", error);
371
380
  throw error;
372
381
  }
373
382
  }
@@ -384,11 +393,13 @@ export class RivetWallet extends Wallet {
384
393
  */
385
394
  async signTransaction(unsignedTx, ethers) {
386
395
  try {
387
- console.log('RivetWallet: Signing transaction');
396
+ console.log("RivetWallet: Signing transaction");
388
397
 
389
398
  const masterKey = await RivetWallet.getMasterKey(this.keyId);
390
399
  if (!masterKey) {
391
- throw new Error('Master key not found - rivet may have been created in a different browser context');
400
+ throw new Error(
401
+ "Master key not found - rivet may have been created in a different browser context",
402
+ );
392
403
  }
393
404
 
394
405
  const { encrypted, iv } = JSON.parse(this.encryptedPrivateKey);
@@ -397,11 +408,11 @@ export class RivetWallet extends Wallet {
397
408
 
398
409
  const decryptedBuffer = await crypto.subtle.decrypt(
399
410
  {
400
- name: 'AES-GCM',
401
- iv: ivBytes
411
+ name: "AES-GCM",
412
+ iv: ivBytes,
402
413
  },
403
414
  masterKey,
404
- encryptedBytes
415
+ encryptedBytes,
405
416
  );
406
417
 
407
418
  const privateKey = ethers.utils.hexlify(new Uint8Array(decryptedBuffer));
@@ -413,16 +424,15 @@ export class RivetWallet extends Wallet {
413
424
  // Validate that our address matches
414
425
  const addressToValidate = this.rivetAddress || this.address;
415
426
  if (signer.address.toLowerCase() !== addressToValidate.toLowerCase()) {
416
- throw new Error('Decrypted key does not match rivet address');
427
+ throw new Error("Decrypted key does not match rivet address");
417
428
  }
418
429
 
419
430
  const signedTx = await signer.signTransaction(unsignedTx);
420
431
 
421
- console.log('RivetWallet: Transaction signed successfully');
432
+ console.log("RivetWallet: Transaction signed successfully");
422
433
  return signedTx;
423
- }
424
- catch (error) {
425
- console.error('Failed to sign transaction with rivet:', error);
434
+ } catch (error) {
435
+ console.error("Failed to sign transaction with rivet:", error);
426
436
  throw error;
427
437
  }
428
438
  }
@@ -430,14 +440,14 @@ export class RivetWallet extends Wallet {
430
440
  // IndexedDB operations for storing non-extractable CryptoKey
431
441
  static async storeMasterKey(keyId, masterKey) {
432
442
  return new Promise((resolve, reject) => {
433
- const request = indexedDB.open('EpisteryRivets', 1);
443
+ const request = indexedDB.open("EpisteryRivets", 1);
434
444
 
435
445
  request.onerror = () => reject(request.error);
436
446
 
437
447
  request.onsuccess = () => {
438
448
  const db = request.result;
439
- const transaction = db.transaction(['masterKeys'], 'readwrite');
440
- const store = transaction.objectStore('masterKeys');
449
+ const transaction = db.transaction(["masterKeys"], "readwrite");
450
+ const store = transaction.objectStore("masterKeys");
441
451
 
442
452
  const putRequest = store.put({ keyId, masterKey });
443
453
 
@@ -454,8 +464,8 @@ export class RivetWallet extends Wallet {
454
464
 
455
465
  request.onupgradeneeded = (event) => {
456
466
  const db = event.target.result;
457
- if (!db.objectStoreNames.contains('masterKeys')) {
458
- db.createObjectStore('masterKeys', { keyPath: 'keyId' });
467
+ if (!db.objectStoreNames.contains("masterKeys")) {
468
+ db.createObjectStore("masterKeys", { keyPath: "keyId" });
459
469
  }
460
470
  };
461
471
  });
@@ -463,14 +473,14 @@ export class RivetWallet extends Wallet {
463
473
 
464
474
  static async getMasterKey(keyId) {
465
475
  return new Promise((resolve, reject) => {
466
- const request = indexedDB.open('EpisteryRivets', 1);
476
+ const request = indexedDB.open("EpisteryRivets", 1);
467
477
 
468
478
  request.onerror = () => reject(request.error);
469
479
 
470
480
  request.onsuccess = () => {
471
481
  const db = request.result;
472
- const transaction = db.transaction(['masterKeys'], 'readonly');
473
- const store = transaction.objectStore('masterKeys');
482
+ const transaction = db.transaction(["masterKeys"], "readonly");
483
+ const store = transaction.objectStore("masterKeys");
474
484
 
475
485
  const getRequest = store.get(keyId);
476
486
 
@@ -487,8 +497,8 @@ export class RivetWallet extends Wallet {
487
497
 
488
498
  request.onupgradeneeded = (event) => {
489
499
  const db = event.target.result;
490
- if (!db.objectStoreNames.contains('masterKeys')) {
491
- db.createObjectStore('masterKeys', { keyPath: 'keyId' });
500
+ if (!db.objectStoreNames.contains("masterKeys")) {
501
+ db.createObjectStore("masterKeys", { keyPath: "keyId" });
492
502
  }
493
503
  };
494
504
  });
@@ -504,24 +514,30 @@ export class RivetWallet extends Wallet {
504
514
  * @param {string} domain - Domain context for the deployment
505
515
  * @returns {Promise<string>} Contract address
506
516
  */
507
- async deployIdentityContract(ethers, providerConfig, domain = 'localhost') {
517
+ async deployIdentityContract(ethers, providerConfig, domain = "localhost") {
508
518
  try {
509
519
  // Get rootPath from Witness singleton
510
- const rootPath = (typeof Witness !== 'undefined' && Witness.instance?.rootPath) || '..';
520
+ const rootPath =
521
+ (typeof Witness !== "undefined" && Witness.instance?.rootPath) || "..";
511
522
 
512
523
  // Step 1: Prepare unsigned deployment transaction (server funds the wallet)
513
- const prepareResponse = await fetch(`${rootPath}/identity/prepare-deploy`, {
514
- method: 'POST',
515
- headers: { 'Content-Type': 'application/json' },
516
- body: JSON.stringify({
517
- clientAddress: this.address,
518
- domain: domain
519
- })
520
- });
524
+ const prepareResponse = await fetch(
525
+ `${rootPath}/epistery/identity/prepare-deploy`,
526
+ {
527
+ method: "POST",
528
+ headers: { "Content-Type": "application/json" },
529
+ body: JSON.stringify({
530
+ clientAddress: this.address,
531
+ domain: domain,
532
+ }),
533
+ },
534
+ );
521
535
 
522
536
  if (!prepareResponse.ok) {
523
537
  const error = await prepareResponse.json();
524
- throw new Error(`Failed to prepare deployment: ${error.error || prepareResponse.statusText}`);
538
+ throw new Error(
539
+ `Failed to prepare deployment: ${error.error || prepareResponse.statusText}`,
540
+ );
525
541
  }
526
542
 
527
543
  const { unsignedTransaction, metadata } = await prepareResponse.json();
@@ -530,34 +546,40 @@ export class RivetWallet extends Wallet {
530
546
  const signedTx = await this.signTransaction(unsignedTransaction, ethers);
531
547
 
532
548
  // Step 3: Submit signed transaction to blockchain
533
- const submitResponse = await fetch(`${rootPath}/data/submit-signed`, {
534
- method: 'POST',
535
- headers: { 'Content-Type': 'application/json' },
536
- body: JSON.stringify({
537
- signedTransaction: signedTx,
538
- operation: 'deployIdentityContract',
539
- metadata: metadata
540
- })
541
- });
549
+ const submitResponse = await fetch(
550
+ `${rootPath}/epistery/data/submit-signed`,
551
+ {
552
+ method: "POST",
553
+ headers: { "Content-Type": "application/json" },
554
+ body: JSON.stringify({
555
+ signedTransaction: signedTx,
556
+ operation: "deployIdentityContract",
557
+ metadata: metadata,
558
+ }),
559
+ },
560
+ );
542
561
 
543
562
  if (!submitResponse.ok) {
544
563
  const error = await submitResponse.json();
545
- throw new Error(`Failed to submit deployment: ${error.error || submitResponse.statusText}`);
564
+ throw new Error(
565
+ `Failed to submit deployment: ${error.error || submitResponse.statusText}`,
566
+ );
546
567
  }
547
568
 
548
569
  const receipt = await submitResponse.json();
549
570
 
550
571
  if (!receipt.contractAddress) {
551
- throw new Error('Contract deployment succeeded but no contract address in receipt');
572
+ throw new Error(
573
+ "Contract deployment succeeded but no contract address in receipt",
574
+ );
552
575
  }
553
576
 
554
577
  // Upgrade this rivet to use the contract
555
578
  this.upgradeToContract(receipt.contractAddress);
556
579
 
557
580
  return receipt.contractAddress;
558
-
559
581
  } catch (error) {
560
- console.error('Failed to deploy IdentityContract:', error);
582
+ console.error("Failed to deploy IdentityContract:", error);
561
583
  throw error;
562
584
  }
563
585
  }
@@ -576,7 +598,7 @@ export class RivetWallet extends Wallet {
576
598
  targetRivetAddress, // Token is bound to this specific address
577
599
  inviterRivetAddress: this.rivetAddress || this.address,
578
600
  timestamp: Date.now(),
579
- expiresAt: Date.now() + 3600000 // 1 hour
601
+ expiresAt: Date.now() + 3600000, // 1 hour
580
602
  };
581
603
 
582
604
  // Sign the payload to prove this is a legitimate invitation
@@ -585,7 +607,7 @@ export class RivetWallet extends Wallet {
585
607
 
586
608
  const token = {
587
609
  payload,
588
- signature
610
+ signature,
589
611
  };
590
612
 
591
613
  // Return base64-encoded token
@@ -607,15 +629,21 @@ export class RivetWallet extends Wallet {
607
629
 
608
630
  // Verify token hasn't expired
609
631
  if (Date.now() > payload.expiresAt) {
610
- throw new Error('Join token has expired');
632
+ throw new Error("Join token has expired");
611
633
  }
612
634
 
613
635
  // Get current rivet address
614
636
  const myRivetAddress = this.rivetAddress || this.address;
615
637
 
616
638
  // SECURITY: Verify this token was generated for THIS rivet's address
617
- if (payload.targetRivetAddress.toLowerCase() !== myRivetAddress.toLowerCase()) {
618
- throw new Error('This join token was not generated for your rivet address. Token is bound to: ' + payload.targetRivetAddress);
639
+ if (
640
+ payload.targetRivetAddress.toLowerCase() !==
641
+ myRivetAddress.toLowerCase()
642
+ ) {
643
+ throw new Error(
644
+ "This join token was not generated for your rivet address. Token is bound to: " +
645
+ payload.targetRivetAddress,
646
+ );
619
647
  }
620
648
 
621
649
  // The contract we're joining (from the token)
@@ -624,21 +652,24 @@ export class RivetWallet extends Wallet {
624
652
  // Verify the signature from the inviter
625
653
  const message = JSON.stringify(payload);
626
654
  const recoveredAddress = ethers.utils.verifyMessage(message, signature);
627
- if (recoveredAddress.toLowerCase() !== payload.inviterRivetAddress.toLowerCase()) {
628
- throw new Error('Invalid join token signature');
655
+ if (
656
+ recoveredAddress.toLowerCase() !==
657
+ payload.inviterRivetAddress.toLowerCase()
658
+ ) {
659
+ throw new Error("Invalid join token signature");
629
660
  }
630
661
 
631
662
  // Upgrade this rivet to use the contract as its identity
632
663
  this.upgradeToContract(targetContract);
633
664
 
634
- console.log('Rivet ready to join identity contract:', myRivetAddress);
665
+ console.log("Rivet ready to join identity contract:", myRivetAddress);
635
666
 
636
667
  return {
637
668
  contractAddress: targetContract,
638
- myRivetAddress: myRivetAddress
669
+ myRivetAddress: myRivetAddress,
639
670
  };
640
671
  } catch (error) {
641
- console.error('Failed to accept join token:', error);
672
+ console.error("Failed to accept join token:", error);
642
673
  throw error;
643
674
  }
644
675
  }
@@ -651,32 +682,32 @@ export class RivetWallet extends Wallet {
651
682
  static generateRivetName() {
652
683
  // Detect browser
653
684
  const userAgent = navigator.userAgent.toLowerCase();
654
- let browser = 'unknown';
655
-
656
- if (userAgent.includes('chrome') && !userAgent.includes('edg')) {
657
- browser = 'chrome';
658
- } else if (userAgent.includes('firefox')) {
659
- browser = 'firefox';
660
- } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
661
- browser = 'safari';
662
- } else if (userAgent.includes('edg')) {
663
- browser = 'edge';
664
- } else if (userAgent.includes('opr') || userAgent.includes('opera')) {
665
- browser = 'opera';
685
+ let browser = "unknown";
686
+
687
+ if (userAgent.includes("chrome") && !userAgent.includes("edg")) {
688
+ browser = "chrome";
689
+ } else if (userAgent.includes("firefox")) {
690
+ browser = "firefox";
691
+ } else if (userAgent.includes("safari") && !userAgent.includes("chrome")) {
692
+ browser = "safari";
693
+ } else if (userAgent.includes("edg")) {
694
+ browser = "edge";
695
+ } else if (userAgent.includes("opr") || userAgent.includes("opera")) {
696
+ browser = "opera";
666
697
  }
667
698
 
668
699
  // Detect OS
669
- let os = 'unknown';
670
- if (userAgent.includes('win')) {
671
- os = 'windows';
672
- } else if (userAgent.includes('mac')) {
673
- os = 'macos';
674
- } else if (userAgent.includes('linux')) {
675
- os = 'linux';
676
- } else if (userAgent.includes('android')) {
677
- os = 'android';
678
- } else if (userAgent.includes('iphone') || userAgent.includes('ipad')) {
679
- os = 'ios';
700
+ let os = "unknown";
701
+ if (userAgent.includes("win")) {
702
+ os = "windows";
703
+ } else if (userAgent.includes("mac")) {
704
+ os = "macos";
705
+ } else if (userAgent.includes("linux")) {
706
+ os = "linux";
707
+ } else if (userAgent.includes("android")) {
708
+ os = "android";
709
+ } else if (userAgent.includes("iphone") || userAgent.includes("ipad")) {
710
+ os = "ios";
680
711
  }
681
712
 
682
713
  // Get hostname
@@ -695,18 +726,23 @@ export class RivetWallet extends Wallet {
695
726
  try {
696
727
  // Normalize URL - ensure it has a protocol
697
728
  let normalizedURL = url.trim();
698
- if (!normalizedURL.startsWith('http://') && !normalizedURL.startsWith('https://')) {
729
+ if (
730
+ !normalizedURL.startsWith("http://") &&
731
+ !normalizedURL.startsWith("https://")
732
+ ) {
699
733
  normalizedURL = `https://${normalizedURL}`;
700
734
  }
701
735
 
702
736
  // Remove trailing slash if present
703
- normalizedURL = normalizedURL.replace(/\/$/, '');
737
+ normalizedURL = normalizedURL.replace(/\/$/, "");
704
738
 
705
739
  // Fetch epistery status from the site
706
740
  const response = await fetch(`${normalizedURL}/.well-known/epistery`);
707
741
 
708
742
  if (!response.ok) {
709
- throw new Error(`Failed to fetch epistery info from ${normalizedURL}. Status: ${response.status}`);
743
+ throw new Error(
744
+ `Failed to fetch epistery info from ${normalizedURL}. Status: ${response.status}`,
745
+ );
710
746
  }
711
747
 
712
748
  const data = await response.json();
@@ -715,13 +751,17 @@ export class RivetWallet extends Wallet {
715
751
  const rivetAddress = data.client?.walletAddress;
716
752
 
717
753
  if (!rivetAddress) {
718
- throw new Error(`No rivet address found at ${normalizedURL}. The site may not have Epistery enabled or no rivet is connected.`);
754
+ throw new Error(
755
+ `No rivet address found at ${normalizedURL}. The site may not have Epistery enabled or no rivet is connected.`,
756
+ );
719
757
  }
720
758
 
721
759
  return rivetAddress;
722
760
  } catch (error) {
723
- console.error('Failed to get rivet address from URL:', error);
724
- throw new Error(`Unable to get rivet address from "${url}": ${error.message}`);
761
+ console.error("Failed to get rivet address from URL:", error);
762
+ throw new Error(
763
+ `Unable to get rivet address from "${url}": ${error.message}`,
764
+ );
725
765
  }
726
766
  }
727
767
 
@@ -735,34 +775,46 @@ export class RivetWallet extends Wallet {
735
775
  * @param {string} domain - Domain context for the transaction
736
776
  * @returns {Promise<void>}
737
777
  */
738
- async addRivetToContract(rivetAddressToAdd, ethers, providerConfig, rivetName = '', domain = 'localhost') {
778
+ async addRivetToContract(
779
+ rivetAddressToAdd,
780
+ ethers,
781
+ providerConfig,
782
+ rivetName = "",
783
+ domain = "localhost",
784
+ ) {
739
785
  try {
740
786
  if (!this.contractAddress) {
741
- throw new Error('This rivet is not part of an identity contract');
787
+ throw new Error("This rivet is not part of an identity contract");
742
788
  }
743
789
 
744
790
  // Get rootPath from Witness singleton
745
- const rootPath = (typeof Witness !== 'undefined' && Witness.instance?.rootPath) || '..';
791
+ const rootPath =
792
+ (typeof Witness !== "undefined" && Witness.instance?.rootPath) || "..";
746
793
 
747
794
  // Generate name if not provided (empty string is considered "not provided")
748
795
  const finalRivetName = rivetName || RivetWallet.generateRivetName();
749
796
 
750
797
  // Step 1: Prepare unsigned transaction (server funds the wallet)
751
- const prepareResponse = await fetch(`${rootPath}/identity/prepare-add-rivet`, {
752
- method: 'POST',
753
- headers: { 'Content-Type': 'application/json' },
754
- body: JSON.stringify({
755
- signerAddress: this.rivetAddress || this.address,
756
- contractAddress: this.contractAddress,
757
- rivetAddressToAdd: rivetAddressToAdd,
758
- rivetName: finalRivetName,
759
- domain: domain
760
- })
761
- });
798
+ const prepareResponse = await fetch(
799
+ `${rootPath}/identity/prepare-add-rivet`,
800
+ {
801
+ method: "POST",
802
+ headers: { "Content-Type": "application/json" },
803
+ body: JSON.stringify({
804
+ signerAddress: this.rivetAddress || this.address,
805
+ contractAddress: this.contractAddress,
806
+ rivetAddressToAdd: rivetAddressToAdd,
807
+ rivetName: finalRivetName,
808
+ domain: domain,
809
+ }),
810
+ },
811
+ );
762
812
 
763
813
  if (!prepareResponse.ok) {
764
814
  const error = await prepareResponse.json();
765
- throw new Error(`Failed to prepare add rivet: ${error.error || prepareResponse.statusText}`);
815
+ throw new Error(
816
+ `Failed to prepare add rivet: ${error.error || prepareResponse.statusText}`,
817
+ );
766
818
  }
767
819
 
768
820
  const { unsignedTransaction, metadata } = await prepareResponse.json();
@@ -771,28 +823,37 @@ export class RivetWallet extends Wallet {
771
823
  const signedTx = await this.signTransaction(unsignedTransaction, ethers);
772
824
 
773
825
  // Step 3: Submit signed transaction to blockchain
774
- const submitResponse = await fetch(`${rootPath}/data/submit-signed`, {
775
- method: 'POST',
776
- headers: { 'Content-Type': 'application/json' },
777
- body: JSON.stringify({
778
- signedTransaction: signedTx,
779
- operation: 'addRivetToContract',
780
- metadata: metadata
781
- })
782
- });
826
+ const submitResponse = await fetch(
827
+ `${rootPath}/epistery/data/submit-signed`,
828
+ {
829
+ method: "POST",
830
+ headers: { "Content-Type": "application/json" },
831
+ body: JSON.stringify({
832
+ signedTransaction: signedTx,
833
+ operation: "addRivetToContract",
834
+ metadata: metadata,
835
+ }),
836
+ },
837
+ );
783
838
 
784
839
  if (!submitResponse.ok) {
785
840
  const error = await submitResponse.json();
786
- throw new Error(`Failed to submit add rivet: ${error.error || submitResponse.statusText}`);
841
+ throw new Error(
842
+ `Failed to submit add rivet: ${error.error || submitResponse.statusText}`,
843
+ );
787
844
  }
788
845
 
789
846
  const receipt = await submitResponse.json();
790
847
 
791
- console.log('Rivet added to identity contract:', rivetAddressToAdd, 'with name:', finalRivetName);
848
+ console.log(
849
+ "Rivet added to identity contract:",
850
+ rivetAddressToAdd,
851
+ "with name:",
852
+ finalRivetName,
853
+ );
792
854
  return receipt;
793
-
794
855
  } catch (error) {
795
- console.error('Failed to add rivet to contract:', error);
856
+ console.error("Failed to add rivet to contract:", error);
796
857
  throw error;
797
858
  }
798
859
  }
@@ -806,20 +867,27 @@ export class RivetWallet extends Wallet {
806
867
  async getRivetsInContract(ethers, providerConfig) {
807
868
  try {
808
869
  if (!this.contractAddress) {
809
- throw new Error('This rivet is not part of an identity contract');
870
+ throw new Error("This rivet is not part of an identity contract");
810
871
  }
811
872
 
812
873
  // Get rootPath from Witness singleton
813
- const rootPath = (typeof Witness !== 'undefined' && Witness.instance?.rootPath) || '..';
874
+ const rootPath =
875
+ (typeof Witness !== "undefined" && Witness.instance?.rootPath) || "..";
814
876
 
815
877
  const provider = new ethers.providers.JsonRpcProvider(providerConfig.rpc);
816
878
 
817
879
  // Load contract artifact
818
- const response = await fetch(`${rootPath}/artifacts/IdentityContract.json`);
880
+ const response = await fetch(
881
+ `${rootPath}/epistery/artifacts/IdentityContract.json`,
882
+ );
819
883
  const artifact = await response.json();
820
884
 
821
885
  // Connect to the identity contract (read-only, no signer needed)
822
- const contract = new ethers.Contract(this.contractAddress, artifact.abi, provider);
886
+ const contract = new ethers.Contract(
887
+ this.contractAddress,
888
+ artifact.abi,
889
+ provider,
890
+ );
823
891
 
824
892
  try {
825
893
  // Try to call getRivetsWithNames() (new contract version)
@@ -828,21 +896,23 @@ export class RivetWallet extends Wallet {
828
896
  // Combine addresses and names into objects
829
897
  return addresses.map((address, index) => ({
830
898
  address: address,
831
- name: names[index] || 'Unnamed Rivet'
899
+ name: names[index] || "Unnamed Rivet",
832
900
  }));
833
901
  } catch (error) {
834
902
  // Fallback to getRivets() for old contracts that don't have names
835
- console.warn('Contract does not support getRivetsWithNames(), falling back to getRivets()');
903
+ console.warn(
904
+ "Contract does not support getRivetsWithNames(), falling back to getRivets()",
905
+ );
836
906
  const addresses = await contract.getRivets();
837
907
 
838
908
  // Return addresses with default names
839
909
  return addresses.map((address) => ({
840
910
  address: address,
841
- name: 'Unnamed Rivet (old contract)'
911
+ name: "Unnamed Rivet (old contract)",
842
912
  }));
843
913
  }
844
914
  } catch (error) {
845
- console.error('Failed to get rivets from contract:', error);
915
+ console.error("Failed to get rivets from contract:", error);
846
916
  throw error;
847
917
  }
848
918
  }
@@ -854,18 +924,18 @@ export class RivetWallet extends Wallet {
854
924
  */
855
925
  upgradeToContract(contractAddress) {
856
926
  if (this.contractAddress) {
857
- throw new Error('Rivet is already using an identity contract');
927
+ throw new Error("Rivet is already using an identity contract");
858
928
  }
859
929
 
860
930
  this.rivetAddress = this.address; // Save original rivet address
861
931
  this.address = contractAddress; // Present contract address
862
932
  this.contractAddress = contractAddress;
863
- this.type = 'Contract';
933
+ this.type = "Contract";
864
934
  this.lastUpdated = Date.now();
865
935
  }
866
936
  }
867
937
 
868
938
  // Expose RivetWallet globally for browser access
869
- if (typeof window !== 'undefined') {
939
+ if (typeof window !== "undefined") {
870
940
  window.RivetWallet = RivetWallet;
871
941
  }