@provablehq/aleo-wallet-adaptor-fox 0.1.1-alpha.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,398 @@
1
+ import {
2
+ Account,
3
+ Network,
4
+ TransactionOptions,
5
+ TransactionStatusResponse,
6
+ } from '@provablehq/aleo-types';
7
+ import {
8
+ AleoDeployment,
9
+ WalletDecryptPermission,
10
+ WalletName,
11
+ WalletReadyState,
12
+ } from '@provablehq/aleo-wallet-standard';
13
+ import {
14
+ BaseAleoWalletAdapter,
15
+ MethodNotImplementedError,
16
+ WalletConnectionError,
17
+ WalletDecryptionError,
18
+ WalletDecryptionNotAllowedError,
19
+ WalletDisconnectionError,
20
+ WalletError,
21
+ WalletNotConnectedError,
22
+ WalletSignMessageError,
23
+ WalletTransactionError,
24
+ } from '@provablehq/aleo-wallet-adaptor-core';
25
+ import {
26
+ AleoTransaction,
27
+ LEO_NETWORK_MAP,
28
+ LeoWallet,
29
+ LeoWalletAdapterConfig,
30
+ } from '@provablehq/aleo-wallet-adaptor-leo';
31
+
32
+ export interface FoxWindow extends Window {
33
+ foxwallet?: { aleo?: LeoWallet };
34
+ }
35
+
36
+ /**
37
+ * Fox wallet adapter
38
+ */
39
+ export class FoxWalletAdapter extends BaseAleoWalletAdapter {
40
+ /**
41
+ * The wallet name
42
+ */
43
+ readonly name = 'Fox Wallet' as WalletName<'Fox Wallet'>;
44
+
45
+ /**
46
+ * The wallet URL
47
+ */
48
+ url = 'https://foxwallet.com/download';
49
+
50
+ /**
51
+ * The wallet icon (base64-encoded SVG)
52
+ */
53
+ readonly icon =
54
+ 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTAwIiBoZWlnaHQ9IjkwMCIgdmlld0JveD0iMCAwIDkwMCA5MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI5MDAiIGhlaWdodD0iOTAwIiByeD0iNDUwIiBmaWxsPSJibGFjayIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTU3Ny4yNDkgMjE1Ljk3NUM1MzkuOTU2IDE5Ni4yMzIgNTExLjY0NiAxNjEuNTQ5IDUwMC40NjggMTE5Ljg2OEM0OTcuMDIxIDEzMi42MTMgNDk1LjI0NSAxNDUuOTg0IDQ5NS4yNDUgMTU5Ljc3NEM0OTUuMjQ1IDE3MC41MzMgNDk2LjM5NCAxODAuOTggNDk4LjQ4MyAxOTEuMTEzQzQ5OC40ODMgMTkxLjExMyA0OTguNDgzIDE5MS4xMTMgNDk4LjQ4MyAxOTEuMjE3QzQ5OC40ODMgMTkxLjMyMiA0OTguNTg4IDE5MS41MzEgNDk4LjU4OCAxOTEuNjM1QzUwMS40MDggMjA1LjIxNiA1MDYuMDA1IDIxOC4wNjUgNTEyLjE2OCAyMjkuOTc0QzQ5OS4wMDYgMjIwLjI1OCA0ODcuMzA2IDIwOC42NjMgNDc3LjQ4NiAxOTUuNjA1QzQ2NC4zMjMgMjk3LjY2NyA1MDEuNDA4IDQwMy45MDcgNTY5LjIwNiA0NzMuODk4QzY1Ny42ODcgNTc2LjkgNTc2LjEgNzUxLjY2OSA0MzguMjA3IDc0Ny4wNzNDMjQzLjA2OCA3NDguNzQ0IDIwOS42MzkgNDYxLjM2MiAzOTYuODM5IDQxNi4yMzRMMzk2LjczNSA0MTUuNzExQzQ0Ni42NjkgMzk5LjUxOSA0NzAuMDY5IDM2Ny4wMzEgNDc0LjE0MyAzMjQuODI3QzQwMi4wNjMgMzgzLjIyMyAyODguMTk2IDMxMC44MjkgMzExLjgwNSAyMjAuMTU0QzQxLjI0MjUgMzUzLjM0NiAxNDEuNzM3IDc4NS40MTEgNDQ4LjQ0NSA3ODAuMDgzQzU4Mi4wNTUgNzgwLjA4MyA2OTUuMDg1IDY5MS43MDYgNzMyLjE3IDU3MC4yMTRDNzc2LjQ2MyA0MjguNTYxIDcwNC44IDI3Ny42MDkgNTc3LjI0OSAyMTUuOTc1WiIgZmlsbD0iIzEyRkU3NCIvPgo8L3N2Zz4K';
55
+ /**
56
+ * The window object
57
+ */
58
+ private _window: FoxWindow | undefined;
59
+
60
+ /**
61
+ * Current network
62
+ */
63
+ network: Network = Network.MAINNET;
64
+
65
+ /**
66
+ * The wallet's decrypt permission
67
+ */
68
+ decryptPermission: WalletDecryptPermission = WalletDecryptPermission.NoDecrypt;
69
+
70
+ /**
71
+ * Public key
72
+ */
73
+ private _publicKey: string = '';
74
+
75
+ _readyState: WalletReadyState =
76
+ typeof window === 'undefined' || typeof document === 'undefined'
77
+ ? WalletReadyState.UNSUPPORTED
78
+ : WalletReadyState.NOT_DETECTED;
79
+
80
+ /**
81
+ * Fox wallet instance
82
+ */
83
+ private _foxWallet: LeoWallet | undefined;
84
+
85
+ /**
86
+ * Create a new Fox wallet adapter
87
+ * @param config Adapter configuration
88
+ */
89
+ constructor(config?: LeoWalletAdapterConfig) {
90
+ super();
91
+ console.debug('FoxWalletAdapter constructor', config);
92
+ this.network = Network.MAINNET;
93
+ this._checkAvailability();
94
+ this._foxWallet = this._window?.foxwallet?.aleo;
95
+ if (config?.isMobile) {
96
+ this.url = `https://app.leo.app/browser?url=${config.mobileWebviewUrl}`;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Check if Fox wallet is available
102
+ */
103
+ private _checkAvailability(): void {
104
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
105
+ this.readyState = WalletReadyState.UNSUPPORTED;
106
+ return;
107
+ }
108
+
109
+ this._window = window as FoxWindow;
110
+
111
+ if (this._window.foxwallet?.aleo) {
112
+ this.readyState = WalletReadyState.INSTALLED;
113
+ } else {
114
+ // Check if user is on a mobile device
115
+ const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
116
+ if (isMobile) {
117
+ this.readyState = WalletReadyState.LOADABLE;
118
+ }
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Connect to Fox wallet
124
+ * @returns The connected account
125
+ */
126
+ async connect(
127
+ network: Network,
128
+ decryptPermission: WalletDecryptPermission,
129
+ programs?: string[],
130
+ ): Promise<Account> {
131
+ try {
132
+ if (this.readyState !== WalletReadyState.INSTALLED) {
133
+ throw new WalletConnectionError('Fox Wallet is not available');
134
+ }
135
+
136
+ if (network !== Network.MAINNET) {
137
+ throw new WalletConnectionError('Fox Wallet only support mainnet');
138
+ }
139
+
140
+ // Call connect and extract address safely
141
+ try {
142
+ await this._foxWallet?.connect(decryptPermission, LEO_NETWORK_MAP[network], programs);
143
+ this.network = network;
144
+ } catch (error: unknown) {
145
+ if (
146
+ error instanceof Object &&
147
+ 'name' in error &&
148
+ (error.name === 'InvalidParamsAleoWalletError' ||
149
+ error.name !== 'NotGrantedAleoWalletError')
150
+ ) {
151
+ // TODO: Handle wrongNetwork at WalletProvider level?
152
+ throw new WalletConnectionError(
153
+ 'Connection failed: Likely due to a difference in configured network and the selected wallet network. Configured network: ' +
154
+ network,
155
+ );
156
+ }
157
+
158
+ throw new WalletConnectionError(
159
+ error instanceof Error ? error.message : 'Connection failed',
160
+ );
161
+ }
162
+
163
+ this._publicKey = this._foxWallet?.publicKey || '';
164
+ if (!this._publicKey) {
165
+ throw new WalletConnectionError('No address returned from wallet');
166
+ }
167
+
168
+ const account: Account = {
169
+ address: this._publicKey,
170
+ };
171
+
172
+ this.account = account;
173
+ this.decryptPermission = decryptPermission;
174
+ this.emit('connect', account);
175
+
176
+ return account;
177
+ } catch (err: Error | unknown) {
178
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
179
+ throw new WalletConnectionError(err instanceof Error ? err.message : 'Connection failed');
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Disconnect from Fox wallet
185
+ */
186
+ async disconnect(): Promise<void> {
187
+ try {
188
+ await this._foxWallet?.disconnect();
189
+ this._publicKey = '';
190
+ this.account = undefined;
191
+ this.emit('disconnect');
192
+ } catch (err: Error | unknown) {
193
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
194
+ throw new WalletDisconnectionError(
195
+ err instanceof Error ? err.message : 'Disconnection failed',
196
+ );
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Sign a transaction with Fox wallet
202
+ * @param options Transaction options
203
+ * @returns The signed transaction
204
+ */
205
+ async signMessage(message: Uint8Array): Promise<Uint8Array> {
206
+ if (!this._publicKey || !this.account) {
207
+ throw new WalletNotConnectedError();
208
+ }
209
+
210
+ try {
211
+ // Pass only the parameters expected by the Fox SDK
212
+ const signature = await this._foxWallet?.signMessage(message);
213
+
214
+ if (!signature) {
215
+ throw new WalletSignMessageError('Failed to sign message');
216
+ }
217
+
218
+ return signature.signature;
219
+ } catch (error: Error | unknown) {
220
+ throw new WalletSignMessageError(
221
+ error instanceof Error ? error.message : 'Failed to sign message',
222
+ );
223
+ }
224
+ }
225
+
226
+ async decrypt(
227
+ cipherText: string,
228
+ tpk?: string,
229
+ programId?: string,
230
+ functionName?: string,
231
+ index?: number,
232
+ ) {
233
+ if (!this._foxWallet || !this._publicKey) {
234
+ throw new WalletNotConnectedError();
235
+ }
236
+ switch (this.decryptPermission) {
237
+ case WalletDecryptPermission.NoDecrypt:
238
+ throw new WalletDecryptionNotAllowedError();
239
+ case WalletDecryptPermission.UponRequest:
240
+ case WalletDecryptPermission.AutoDecrypt:
241
+ case WalletDecryptPermission.OnChainHistory: {
242
+ try {
243
+ const result = await this._foxWallet.decrypt(
244
+ cipherText,
245
+ tpk,
246
+ programId,
247
+ functionName,
248
+ index,
249
+ );
250
+ return result.text;
251
+ } catch (error: Error | unknown) {
252
+ throw new WalletDecryptionError(
253
+ error instanceof Error ? error.message : 'Failed to decrypt',
254
+ );
255
+ }
256
+ }
257
+ default:
258
+ throw new WalletDecryptionError();
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Execute a transaction with Fox wallet
264
+ * @param options Transaction options
265
+ * @returns The executed temporary transaction ID
266
+ */
267
+ async executeTransaction(options: TransactionOptions): Promise<{ transactionId: string }> {
268
+ if (!this._publicKey || !this.account) {
269
+ throw new WalletNotConnectedError();
270
+ }
271
+
272
+ try {
273
+ const requestData = {
274
+ address: this._publicKey,
275
+ chainId: LEO_NETWORK_MAP[this.network],
276
+ fee: options.fee ? options.fee : 0.001,
277
+ feePrivate: options.privateFee ?? false,
278
+ transitions: [
279
+ {
280
+ program: options.program,
281
+ functionName: options.function,
282
+ inputs: options.inputs,
283
+ },
284
+ ],
285
+ } as AleoTransaction;
286
+
287
+ const result = await this._foxWallet?.requestTransaction(requestData);
288
+
289
+ if (!result?.transactionId) {
290
+ throw new WalletTransactionError('Could not create transaction');
291
+ }
292
+
293
+ return {
294
+ transactionId: result.transactionId,
295
+ };
296
+ } catch (error: Error | unknown) {
297
+ console.error('Fox Wallet executeTransaction error', error);
298
+ if (error instanceof WalletError) {
299
+ throw error;
300
+ }
301
+ throw new WalletTransactionError(
302
+ error instanceof Error ? error.message : 'Failed to execute transaction',
303
+ );
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Get transaction status
309
+ * @param transactionId The transaction ID
310
+ * @returns The transaction status
311
+ */
312
+ async transactionStatus(transactionId: string): Promise<TransactionStatusResponse> {
313
+ if (!this._publicKey || !this.account) {
314
+ throw new WalletNotConnectedError();
315
+ }
316
+
317
+ try {
318
+ const result = await this._foxWallet?.transactionStatus(transactionId);
319
+
320
+ if (!result?.status) {
321
+ throw new WalletTransactionError('Could not get transaction status');
322
+ }
323
+
324
+ return {
325
+ status: result.status,
326
+ };
327
+ } catch (error: Error | unknown) {
328
+ throw new WalletTransactionError(
329
+ error instanceof Error ? error.message : 'Failed to get transaction status',
330
+ );
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Request records from Fox wallet
336
+ * @param program The program to request records from
337
+ * @param includePlaintext Whether to include plaintext on each record
338
+ * @returns The records
339
+ */
340
+ async requestRecords(program: string, includePlaintext: boolean): Promise<unknown[]> {
341
+ if (!this._publicKey || !this.account) {
342
+ throw new WalletNotConnectedError();
343
+ }
344
+
345
+ try {
346
+ const result = includePlaintext
347
+ ? await this._foxWallet?.requestRecordPlaintexts(program)
348
+ : await this._foxWallet?.requestRecords(program);
349
+
350
+ return result?.records || [];
351
+ } catch (error: Error | unknown) {
352
+ throw new WalletError(error instanceof Error ? error.message : 'Failed to request records');
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Switch the network
358
+ * @param network The network to switch to
359
+ */
360
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
361
+ async switchNetwork(_network: Network): Promise<void> {
362
+ console.error('Fox Wallet does not support switching networks');
363
+ throw new MethodNotImplementedError('switchNetwork');
364
+ }
365
+
366
+ /**
367
+ * Execute a deployment
368
+ * @param deployment The deployment to execute
369
+ * @returns The executed transaction ID
370
+ */
371
+ async executeDeployment(deployment: AleoDeployment): Promise<{ transactionId: string }> {
372
+ try {
373
+ if (!this._foxWallet || !this._publicKey) throw new WalletNotConnectedError();
374
+ try {
375
+ const result = await this._foxWallet?.requestDeploy({
376
+ address: this._publicKey,
377
+ chainId: LEO_NETWORK_MAP[this.network],
378
+ program: deployment.program,
379
+ fee: deployment.fee,
380
+ feePrivate: deployment.feePrivate,
381
+ });
382
+ if (!result?.transactionId) {
383
+ throw new WalletTransactionError('Could not create deployment');
384
+ }
385
+ return {
386
+ transactionId: result.transactionId,
387
+ };
388
+ } catch (error: Error | unknown) {
389
+ throw new WalletTransactionError(
390
+ error instanceof Error ? error.message : 'Failed to execute deployment',
391
+ );
392
+ }
393
+ } catch (error: Error | unknown) {
394
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
395
+ throw error;
396
+ }
397
+ }
398
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './FoxWalletAdapter';