epistery 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,213 @@
1
+ /*
2
+ * Wallet - Base class for client wallets
3
+ *
4
+ * Handles wallet creation, persistence, and signing for Epistery
5
+ */
6
+
7
+ // Base Wallet class
8
+ export class Wallet {
9
+ constructor() {
10
+ this.address = null;
11
+ this.publicKey = null;
12
+ this.source = null;
13
+ }
14
+
15
+ // Serialize only the essential data for persistence
16
+ toJSON() {
17
+ return {
18
+ address: this.address,
19
+ publicKey: this.publicKey,
20
+ source: this.source
21
+ };
22
+ }
23
+
24
+ // Factory method to create appropriate wallet type from saved data
25
+ static async fromJSON(data, ethers) {
26
+ if (data.source === 'web3') {
27
+ return await Web3Wallet.fromJSON(data, ethers);
28
+ } else if (data.source === 'local') {
29
+ return await BrowserWallet.fromJSON(data, ethers);
30
+ }
31
+ throw new Error(`Unknown wallet source: ${data.source}`);
32
+ }
33
+
34
+ // Abstract methods - must be implemented by subclasses
35
+ async sign(message) {
36
+ throw new Error('sign() must be implemented by subclass');
37
+ }
38
+
39
+ static async create(ethers) {
40
+ throw new Error('create() must be implemented by subclass');
41
+ }
42
+ }
43
+
44
+ // Web3 Wallet (MetaMask, etc.)
45
+ export class Web3Wallet extends Wallet {
46
+ constructor() {
47
+ super();
48
+ this.source = 'web3';
49
+ this.signer = null;
50
+ this.provider = null;
51
+ }
52
+
53
+ toJSON() {
54
+ // Only persist the essential data, not the complex objects
55
+ return {
56
+ ...super.toJSON(),
57
+ // Don't serialize signer/provider - they'll be recreated
58
+ };
59
+ }
60
+
61
+ static async fromJSON(data, ethers) {
62
+ const wallet = new Web3Wallet();
63
+ wallet.address = data.address;
64
+ wallet.publicKey = data.publicKey;
65
+
66
+ // Attempt to reconnect to Web3 provider
67
+ await wallet.reconnectWeb3(ethers);
68
+ return wallet;
69
+ }
70
+
71
+ static async create(ethers) {
72
+ const wallet = new Web3Wallet();
73
+
74
+ if (await wallet.connectWeb3(ethers)) {
75
+ return wallet;
76
+ }
77
+ return null; // Connection failed
78
+ }
79
+
80
+ async connectWeb3(ethers) {
81
+ try {
82
+ if (typeof window !== 'undefined' && (window.ethereum || window.web3)) {
83
+ const provider = window.ethereum || window.web3.currentProvider;
84
+
85
+ // Request account access
86
+ const accounts = await provider.request({
87
+ method: 'eth_requestAccounts'
88
+ });
89
+
90
+ if (accounts && accounts.length > 0) {
91
+ this.address = accounts[0];
92
+ this.provider = new ethers.providers.Web3Provider(provider);
93
+ this.signer = this.provider.getSigner();
94
+
95
+ // Get public key from first signature
96
+ this.publicKey = await this.derivePublicKeyPlaceholder();
97
+
98
+ return true;
99
+ }
100
+ }
101
+ return false;
102
+ } catch (error) {
103
+ return false;
104
+ }
105
+ }
106
+
107
+ async reconnectWeb3(ethers) {
108
+ try {
109
+ if (typeof window !== 'undefined' && (window.ethereum || window.web3)) {
110
+ const provider = window.ethereum || window.web3.currentProvider;
111
+ this.provider = new ethers.providers.Web3Provider(provider);
112
+ this.signer = this.provider.getSigner();
113
+
114
+ // Verify the address matches what we have stored
115
+ const currentAddress = await this.signer.getAddress();
116
+ if (currentAddress.toLowerCase() !== this.address.toLowerCase()) {
117
+ this.address = currentAddress;
118
+ }
119
+ return true;
120
+ }
121
+ return false;
122
+ } catch (error) {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ async sign(message, ethers) {
128
+ if (!this.signer) {
129
+ throw new Error('Web3 signer not available');
130
+ }
131
+
132
+ const signature = await this.signer.signMessage(message);
133
+
134
+ // Always update public key from signature for Web3 wallets
135
+ if (ethers) {
136
+ this.publicKey = await this.derivePublicKeyFromSignature(message, signature, ethers);
137
+ }
138
+
139
+ return signature;
140
+ }
141
+
142
+ async derivePublicKeyPlaceholder() {
143
+ // Placeholder until we get a real signature
144
+ return `0x04${this.address.slice(2)}${'0'.repeat(64)}`;
145
+ }
146
+
147
+ async derivePublicKeyFromSignature(message, signature, ethers) {
148
+ try {
149
+ const messageHash = ethers.utils.hashMessage(message);
150
+ return ethers.utils.recoverPublicKey(messageHash, signature);
151
+ } catch (error) {
152
+ console.error('Failed to derive public key from signature:', error);
153
+ return this.derivePublicKeyPlaceholder();
154
+ }
155
+ }
156
+ }
157
+
158
+ // Browser Wallet (Local Storage)
159
+ export class BrowserWallet extends Wallet {
160
+ constructor() {
161
+ super();
162
+ this.source = 'local';
163
+ this.mnemonic = null;
164
+ this.privateKey = null;
165
+ this.signer = null;
166
+ }
167
+
168
+ toJSON() {
169
+ return {
170
+ ...super.toJSON(),
171
+ mnemonic: this.mnemonic,
172
+ privateKey: this.privateKey
173
+ };
174
+ }
175
+
176
+ static async fromJSON(data, ethers) {
177
+ const wallet = new BrowserWallet();
178
+ wallet.address = data.address;
179
+ wallet.publicKey = data.publicKey;
180
+ wallet.mnemonic = data.mnemonic;
181
+ wallet.privateKey = data.privateKey;
182
+
183
+ // Recreate the signer
184
+ if (wallet.mnemonic) {
185
+ wallet.signer = ethers.Wallet.fromMnemonic(wallet.mnemonic);
186
+ }
187
+
188
+ return wallet;
189
+ }
190
+
191
+ static async create(ethers) {
192
+ const wallet = new BrowserWallet();
193
+
194
+ // Generate new wallet
195
+ const ethersWallet = ethers.Wallet.createRandom();
196
+
197
+ wallet.address = ethersWallet.address;
198
+ wallet.mnemonic = ethersWallet.mnemonic?.phrase || '';
199
+ wallet.publicKey = ethersWallet.publicKey;
200
+ wallet.privateKey = ethersWallet.privateKey;
201
+ wallet.signer = ethersWallet;
202
+
203
+ return wallet;
204
+ }
205
+
206
+ async sign(message) {
207
+ if (!this.signer) {
208
+ throw new Error('Browser wallet signer not available');
209
+ }
210
+
211
+ return await this.signer.signMessage(message);
212
+ }
213
+ }