@txnlab/use-wallet-mnemonic 5.0.0-rc.3 → 5.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @txnlab/use-wallet-mnemonic
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@txnlab/use-wallet-mnemonic)](https://www.npmjs.com/package/@txnlab/use-wallet-mnemonic)
4
+ [![License](https://img.shields.io/github/license/TxnLab/use-wallet)](https://github.com/TxnLab/use-wallet/blob/main/LICENSE)
5
+
6
+ Mnemonic Wallet adapter for [use-wallet](https://github.com/TxnLab/use-wallet), the Algorand wallet integration library.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @txnlab/use-wallet @txnlab/use-wallet-mnemonic
12
+ ```
13
+
14
+ If you use a framework adapter (`@txnlab/use-wallet-react`, `-vue`, `-solid`, or `-svelte`), install it in place of `@txnlab/use-wallet`.
15
+
16
+ ## Usage
17
+
18
+ ```typescript
19
+ import { WalletManager } from '@txnlab/use-wallet'
20
+ import { mnemonic } from '@txnlab/use-wallet-mnemonic'
21
+
22
+ const manager = new WalletManager({
23
+ wallets: [mnemonic()]
24
+ })
25
+ ```
26
+
27
+ **For testing only** — never use with real assets. Not available on MainNet. Supports scoped private key access via `withPrivateKey`. Options: `persistToStorage`, `promptForMnemonic`.
28
+
29
+ The factory also accepts an optional `metadata` option to override the wallet's display name and icon.
30
+
31
+ ### Visit [txnlab.gitbook.io/use-wallet](https://txnlab.gitbook.io/use-wallet) for docs, guides, and examples!
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import algosdk from "algosdk";
2
2
  import { AdapterConstructorParams, BaseWallet, WalletAccount, WalletMetadata } from "@txnlab/use-wallet/adapter";
3
- import { WalletAdapterConfig } from "@txnlab/use-wallet";
3
+ import { WalletAdapterConfig, WalletFactoryOptions } from "@txnlab/use-wallet";
4
4
 
5
5
  //#region src/adapter.d.ts
6
6
  interface MnemonicConstructor {
@@ -50,7 +50,7 @@ declare class MnemonicAdapter extends BaseWallet<MnemonicOptions> {
50
50
  //#endregion
51
51
  //#region src/index.d.ts
52
52
  declare const WALLET_ID: "mnemonic";
53
- declare function mnemonic(options?: MnemonicOptions): WalletAdapterConfig;
53
+ declare function mnemonic(options?: MnemonicOptions & WalletFactoryOptions): WalletAdapterConfig;
54
54
  //#endregion
55
55
  export { MnemonicAdapter, type MnemonicOptions, WALLET_ID, mnemonic };
56
56
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -195,11 +195,15 @@ var MnemonicAdapter = class extends BaseWallet {
195
195
  //#region src/index.ts
196
196
  const WALLET_ID = "mnemonic";
197
197
  function mnemonic(options) {
198
+ const { metadata, ...adapterOptions } = options ?? {};
198
199
  return {
199
200
  id: WALLET_ID,
200
- metadata: MnemonicAdapter.defaultMetadata,
201
+ metadata: {
202
+ ...MnemonicAdapter.defaultMetadata,
203
+ ...metadata
204
+ },
201
205
  Adapter: MnemonicAdapter,
202
- options,
206
+ options: Object.keys(adapterOptions).length > 0 ? adapterOptions : void 0,
203
207
  capabilities: { excludedNetworks: ["mainnet"] }
204
208
  };
205
209
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/icon.ts","../src/adapter.ts","../src/index.ts"],"sourcesContent":["export const icon = `\n<svg viewBox=\"0 0 400 400\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect fill=\"#525252\" width=\"400\" height=\"400\" />\n <path fill=\"#FFFFFF\" d=\"M309.2,309.3H275l-22.2-82.7l-47.9,82.7h-38.3l73.9-128l-11.9-44.5l-99.6,172.6H90.8L217.1,90.6 h33.5l14.7,54.3h34.6l-23.6,41L309.2,309.3z\" />\n</svg>\n`\n","import algosdk from 'algosdk'\nimport {\n BaseWallet,\n flattenTxnGroup,\n isSignedTxn,\n isTransactionArray,\n zeroMemory,\n type AdapterConstructorParams,\n type WalletAccount,\n type WalletMetadata,\n type WalletState\n} from '@txnlab/use-wallet/adapter'\n\ninterface MnemonicConstructor {\n persistToStorage?: boolean\n promptForMnemonic: () => Promise<string | null>\n}\n\nexport type MnemonicOptions = Partial<Pick<MnemonicConstructor, 'promptForMnemonic'>> &\n Omit<MnemonicConstructor, 'promptForMnemonic'>\n\nexport const LOCAL_STORAGE_MNEMONIC_KEY = '@txnlab/use-wallet:v5_mnemonic'\n\nimport { icon } from './icon'\n\nconst ICON = `data:image/svg+xml;base64,${btoa(icon)}`\n\nexport class MnemonicAdapter extends BaseWallet<MnemonicOptions> {\n private account: algosdk.Account | null = null\n private mnemonicOptions: MnemonicConstructor\n\n constructor(params: AdapterConstructorParams<MnemonicOptions>) {\n super(params)\n\n const {\n persistToStorage = false,\n promptForMnemonic = () => Promise.resolve(prompt('Enter 25-word mnemonic passphrase:'))\n } = this.options || {}\n\n this.mnemonicOptions = { persistToStorage, promptForMnemonic }\n\n if (this.mnemonicOptions.persistToStorage) {\n this.logger.warn(\n 'Persisting mnemonics to storage is insecure. Any private key mnemonics used should never hold real Algos (i.e., on MainNet). Use with caution!'\n )\n }\n }\n\n static defaultMetadata: WalletMetadata = {\n name: 'Mnemonic',\n icon: ICON\n }\n\n private loadMnemonicFromStorage(): string | null {\n if (typeof localStorage === 'undefined') return null\n return localStorage.getItem(LOCAL_STORAGE_MNEMONIC_KEY)\n }\n\n private saveMnemonicToStorage(mnemonic: string): void {\n if (typeof localStorage === 'undefined') return\n localStorage.setItem(LOCAL_STORAGE_MNEMONIC_KEY, mnemonic)\n }\n\n private removeMnemonicFromStorage(): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(LOCAL_STORAGE_MNEMONIC_KEY)\n }\n\n private checkMainnet(): void {\n try {\n const network = this.activeNetworkConfig\n if (!network.isTestnet) {\n this.logger.warn(\n 'The Mnemonic wallet provider is insecure and intended for testing only. Any private key mnemonics used should never hold real Algos (i.e., on MainNet).'\n )\n throw new Error('Production network detected. Aborting.')\n }\n } catch (error) {\n this.disconnect()\n throw error\n }\n }\n\n private async initializeAccount(): Promise<algosdk.Account> {\n let mnemonic = this.loadMnemonicFromStorage()\n if (!mnemonic) {\n mnemonic = await this.mnemonicOptions.promptForMnemonic()\n if (!mnemonic) {\n this.account = null\n this.logger.error('No mnemonic provided')\n throw new Error('No mnemonic provided')\n }\n\n if (this.mnemonicOptions.persistToStorage) {\n this.logger.warn('Mnemonic saved to localStorage.')\n this.saveMnemonicToStorage(mnemonic)\n }\n }\n\n const account = algosdk.mnemonicToSecretKey(mnemonic)\n this.account = account\n return account\n }\n\n public connect = async (): Promise<WalletAccount[]> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n this.logger.info('Connecting...')\n const account = await this.initializeAccount()\n\n const walletAccount = {\n name: `${this.metadata.name} Account`,\n address: account.addr.toString()\n }\n\n const walletState: WalletState = {\n accounts: [walletAccount],\n activeAccount: walletAccount\n }\n\n this.store.addWallet(walletState)\n\n this.logger.info('Connected successfully', walletState)\n return [walletAccount]\n }\n\n public disconnect = async (): Promise<void> => {\n this.logger.info('Disconnecting...')\n this.onDisconnect()\n this.account = null\n this.removeMnemonicFromStorage()\n this.logger.info('Disconnected')\n }\n\n public resumeSession = async (): Promise<void> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n const walletState = this.store.getWalletState()\n\n // No session to resume\n if (!walletState) {\n this.logger.info('No session to resume')\n return\n }\n\n this.logger.info('Resuming session...')\n\n // If persisting to storage is enabled, then resume session\n if (this.mnemonicOptions.persistToStorage) {\n try {\n await this.initializeAccount()\n this.logger.info('Session resumed successfully')\n } catch (error: any) {\n this.logger.error('Error resuming session:', error.message)\n this.disconnect()\n throw error\n }\n } else {\n // Otherwise, do not resume session, disconnect instead\n this.logger.info('No session to resume, disconnecting...')\n this.disconnect()\n }\n }\n\n private processTxns(\n txnGroup: algosdk.Transaction[],\n indexesToSign?: number[]\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txn, index) => {\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n const signer = txn.sender.toString()\n const canSignTxn = signer === this.account!.addr.toString()\n\n if (isIndexMatch && canSignTxn) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n private processEncodedTxns(\n txnGroup: Uint8Array[],\n indexesToSign?: number[]\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txnBuffer, index) => {\n const decodedObj = algosdk.msgpackRawDecode(txnBuffer)\n const isSigned = isSignedTxn(decodedObj)\n\n const txn: algosdk.Transaction = isSigned\n ? algosdk.decodeSignedTransaction(txnBuffer).txn\n : algosdk.decodeUnsignedTransaction(txnBuffer)\n\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n const signer = txn.sender.toString()\n const canSignTxn = !isSigned && signer === this.account!.addr.toString()\n\n if (isIndexMatch && canSignTxn) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n public canUsePrivateKey = true\n\n /**\n * Provide scoped access to the private key via a callback.\n *\n * The callback receives a copy of the 64-byte Algorand secret key.\n * The copy is guaranteed to be zeroed from memory when the callback\n * completes, whether it succeeds or throws.\n *\n * **Note:** This method is blocked on MainNet. The Mnemonic wallet is intended\n * for development and testing only. For production use, see Web3Auth which\n * supports `withPrivateKey` on all networks.\n *\n * @example\n * ```typescript\n * const result = await wallet.withPrivateKey(async (secretKey) => {\n * // secretKey is a 64-byte Uint8Array\n * return doSomethingWith(secretKey)\n * })\n * // secretKey is zeroed at this point\n * ```\n */\n public withPrivateKey = async <T>(\n callback: (secretKey: Uint8Array) => Promise<T>\n ): Promise<T> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n if (!this.account) {\n this.logger.error('Mnemonic wallet not connected')\n throw new Error('Mnemonic wallet not connected')\n }\n\n this.logger.debug('withPrivateKey: Providing private key access...')\n\n // Create a copy of the secret key for the consumer\n const skCopy = new Uint8Array(this.account.sk)\n\n try {\n return await callback(skCopy)\n } finally {\n // SECURITY: Always zero the consumer's copy\n zeroMemory(skCopy)\n }\n }\n\n public signTransactions = async <T extends algosdk.Transaction[] | Uint8Array[]>(\n txnGroup: T | T[],\n indexesToSign?: number[]\n ): Promise<(Uint8Array | null)[]> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n try {\n this.logger.debug('Signing transactions...', { txnGroup, indexesToSign })\n let txnsToSign: algosdk.Transaction[] = []\n\n // Determine type and process transactions for signing\n if (isTransactionArray(txnGroup)) {\n const flatTxns: algosdk.Transaction[] = flattenTxnGroup(txnGroup)\n txnsToSign = this.processTxns(flatTxns, indexesToSign)\n } else {\n const flatTxns: Uint8Array[] = flattenTxnGroup(txnGroup as Uint8Array[])\n txnsToSign = this.processEncodedTxns(flatTxns, indexesToSign)\n }\n\n // Sign transactions\n const signedTxns = txnsToSign.map((txn) => txn.signTxn(this.account!.sk))\n this.logger.debug('Transactions signed successfully', { signedTxns })\n return signedTxns\n } catch (error: any) {\n this.logger.error('Error signing transactions:', error.message)\n throw error\n }\n }\n}\n","import { MnemonicAdapter } from './adapter'\nimport type { MnemonicOptions } from './adapter'\nimport type { WalletAdapterConfig } from '@txnlab/use-wallet'\n\nexport const WALLET_ID = 'mnemonic' as const\n\nexport function mnemonic(options?: MnemonicOptions): WalletAdapterConfig {\n return {\n id: WALLET_ID,\n metadata: MnemonicAdapter.defaultMetadata,\n Adapter: MnemonicAdapter as unknown as WalletAdapterConfig['Adapter'],\n options: options as unknown as Record<string, unknown> | undefined,\n capabilities: { excludedNetworks: ['mainnet'] }\n }\n}\n\nexport { MnemonicAdapter }\nexport type { MnemonicOptions }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/icon.ts","../src/adapter.ts","../src/index.ts"],"sourcesContent":["export const icon = `\n<svg viewBox=\"0 0 400 400\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect fill=\"#525252\" width=\"400\" height=\"400\" />\n <path fill=\"#FFFFFF\" d=\"M309.2,309.3H275l-22.2-82.7l-47.9,82.7h-38.3l73.9-128l-11.9-44.5l-99.6,172.6H90.8L217.1,90.6 h33.5l14.7,54.3h34.6l-23.6,41L309.2,309.3z\" />\n</svg>\n`\n","import algosdk from 'algosdk'\nimport {\n BaseWallet,\n flattenTxnGroup,\n isSignedTxn,\n isTransactionArray,\n zeroMemory,\n type AdapterConstructorParams,\n type WalletAccount,\n type WalletMetadata,\n type WalletState\n} from '@txnlab/use-wallet/adapter'\n\ninterface MnemonicConstructor {\n persistToStorage?: boolean\n promptForMnemonic: () => Promise<string | null>\n}\n\nexport type MnemonicOptions = Partial<Pick<MnemonicConstructor, 'promptForMnemonic'>> &\n Omit<MnemonicConstructor, 'promptForMnemonic'>\n\nexport const LOCAL_STORAGE_MNEMONIC_KEY = '@txnlab/use-wallet:v5_mnemonic'\n\nimport { icon } from './icon'\n\nconst ICON = `data:image/svg+xml;base64,${btoa(icon)}`\n\nexport class MnemonicAdapter extends BaseWallet<MnemonicOptions> {\n private account: algosdk.Account | null = null\n private mnemonicOptions: MnemonicConstructor\n\n constructor(params: AdapterConstructorParams<MnemonicOptions>) {\n super(params)\n\n const {\n persistToStorage = false,\n promptForMnemonic = () => Promise.resolve(prompt('Enter 25-word mnemonic passphrase:'))\n } = this.options || {}\n\n this.mnemonicOptions = { persistToStorage, promptForMnemonic }\n\n if (this.mnemonicOptions.persistToStorage) {\n this.logger.warn(\n 'Persisting mnemonics to storage is insecure. Any private key mnemonics used should never hold real Algos (i.e., on MainNet). Use with caution!'\n )\n }\n }\n\n static defaultMetadata: WalletMetadata = {\n name: 'Mnemonic',\n icon: ICON\n }\n\n private loadMnemonicFromStorage(): string | null {\n if (typeof localStorage === 'undefined') return null\n return localStorage.getItem(LOCAL_STORAGE_MNEMONIC_KEY)\n }\n\n private saveMnemonicToStorage(mnemonic: string): void {\n if (typeof localStorage === 'undefined') return\n localStorage.setItem(LOCAL_STORAGE_MNEMONIC_KEY, mnemonic)\n }\n\n private removeMnemonicFromStorage(): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(LOCAL_STORAGE_MNEMONIC_KEY)\n }\n\n private checkMainnet(): void {\n try {\n const network = this.activeNetworkConfig\n if (!network.isTestnet) {\n this.logger.warn(\n 'The Mnemonic wallet provider is insecure and intended for testing only. Any private key mnemonics used should never hold real Algos (i.e., on MainNet).'\n )\n throw new Error('Production network detected. Aborting.')\n }\n } catch (error) {\n this.disconnect()\n throw error\n }\n }\n\n private async initializeAccount(): Promise<algosdk.Account> {\n let mnemonic = this.loadMnemonicFromStorage()\n if (!mnemonic) {\n mnemonic = await this.mnemonicOptions.promptForMnemonic()\n if (!mnemonic) {\n this.account = null\n this.logger.error('No mnemonic provided')\n throw new Error('No mnemonic provided')\n }\n\n if (this.mnemonicOptions.persistToStorage) {\n this.logger.warn('Mnemonic saved to localStorage.')\n this.saveMnemonicToStorage(mnemonic)\n }\n }\n\n const account = algosdk.mnemonicToSecretKey(mnemonic)\n this.account = account\n return account\n }\n\n public connect = async (): Promise<WalletAccount[]> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n this.logger.info('Connecting...')\n const account = await this.initializeAccount()\n\n const walletAccount = {\n name: `${this.metadata.name} Account`,\n address: account.addr.toString()\n }\n\n const walletState: WalletState = {\n accounts: [walletAccount],\n activeAccount: walletAccount\n }\n\n this.store.addWallet(walletState)\n\n this.logger.info('Connected successfully', walletState)\n return [walletAccount]\n }\n\n public disconnect = async (): Promise<void> => {\n this.logger.info('Disconnecting...')\n this.onDisconnect()\n this.account = null\n this.removeMnemonicFromStorage()\n this.logger.info('Disconnected')\n }\n\n public resumeSession = async (): Promise<void> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n const walletState = this.store.getWalletState()\n\n // No session to resume\n if (!walletState) {\n this.logger.info('No session to resume')\n return\n }\n\n this.logger.info('Resuming session...')\n\n // If persisting to storage is enabled, then resume session\n if (this.mnemonicOptions.persistToStorage) {\n try {\n await this.initializeAccount()\n this.logger.info('Session resumed successfully')\n } catch (error: any) {\n this.logger.error('Error resuming session:', error.message)\n this.disconnect()\n throw error\n }\n } else {\n // Otherwise, do not resume session, disconnect instead\n this.logger.info('No session to resume, disconnecting...')\n this.disconnect()\n }\n }\n\n private processTxns(\n txnGroup: algosdk.Transaction[],\n indexesToSign?: number[]\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txn, index) => {\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n const signer = txn.sender.toString()\n const canSignTxn = signer === this.account!.addr.toString()\n\n if (isIndexMatch && canSignTxn) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n private processEncodedTxns(\n txnGroup: Uint8Array[],\n indexesToSign?: number[]\n ): algosdk.Transaction[] {\n const txnsToSign: algosdk.Transaction[] = []\n\n txnGroup.forEach((txnBuffer, index) => {\n const decodedObj = algosdk.msgpackRawDecode(txnBuffer)\n const isSigned = isSignedTxn(decodedObj)\n\n const txn: algosdk.Transaction = isSigned\n ? algosdk.decodeSignedTransaction(txnBuffer).txn\n : algosdk.decodeUnsignedTransaction(txnBuffer)\n\n const isIndexMatch = !indexesToSign || indexesToSign.includes(index)\n const signer = txn.sender.toString()\n const canSignTxn = !isSigned && signer === this.account!.addr.toString()\n\n if (isIndexMatch && canSignTxn) {\n txnsToSign.push(txn)\n }\n })\n\n return txnsToSign\n }\n\n public canUsePrivateKey = true\n\n /**\n * Provide scoped access to the private key via a callback.\n *\n * The callback receives a copy of the 64-byte Algorand secret key.\n * The copy is guaranteed to be zeroed from memory when the callback\n * completes, whether it succeeds or throws.\n *\n * **Note:** This method is blocked on MainNet. The Mnemonic wallet is intended\n * for development and testing only. For production use, see Web3Auth which\n * supports `withPrivateKey` on all networks.\n *\n * @example\n * ```typescript\n * const result = await wallet.withPrivateKey(async (secretKey) => {\n * // secretKey is a 64-byte Uint8Array\n * return doSomethingWith(secretKey)\n * })\n * // secretKey is zeroed at this point\n * ```\n */\n public withPrivateKey = async <T>(\n callback: (secretKey: Uint8Array) => Promise<T>\n ): Promise<T> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n if (!this.account) {\n this.logger.error('Mnemonic wallet not connected')\n throw new Error('Mnemonic wallet not connected')\n }\n\n this.logger.debug('withPrivateKey: Providing private key access...')\n\n // Create a copy of the secret key for the consumer\n const skCopy = new Uint8Array(this.account.sk)\n\n try {\n return await callback(skCopy)\n } finally {\n // SECURITY: Always zero the consumer's copy\n zeroMemory(skCopy)\n }\n }\n\n public signTransactions = async <T extends algosdk.Transaction[] | Uint8Array[]>(\n txnGroup: T | T[],\n indexesToSign?: number[]\n ): Promise<(Uint8Array | null)[]> => {\n // Throw error if MainNet is active\n this.checkMainnet()\n\n try {\n this.logger.debug('Signing transactions...', { txnGroup, indexesToSign })\n let txnsToSign: algosdk.Transaction[] = []\n\n // Determine type and process transactions for signing\n if (isTransactionArray(txnGroup)) {\n const flatTxns: algosdk.Transaction[] = flattenTxnGroup(txnGroup)\n txnsToSign = this.processTxns(flatTxns, indexesToSign)\n } else {\n const flatTxns: Uint8Array[] = flattenTxnGroup(txnGroup as Uint8Array[])\n txnsToSign = this.processEncodedTxns(flatTxns, indexesToSign)\n }\n\n // Sign transactions\n const signedTxns = txnsToSign.map((txn) => txn.signTxn(this.account!.sk))\n this.logger.debug('Transactions signed successfully', { signedTxns })\n return signedTxns\n } catch (error: any) {\n this.logger.error('Error signing transactions:', error.message)\n throw error\n }\n }\n}\n","import { MnemonicAdapter } from './adapter'\nimport type { MnemonicOptions } from './adapter'\nimport type { WalletAdapterConfig, WalletFactoryOptions } from '@txnlab/use-wallet'\n\nexport const WALLET_ID = 'mnemonic' as const\n\nexport function mnemonic(options?: MnemonicOptions & WalletFactoryOptions): WalletAdapterConfig {\n const { metadata, ...adapterOptions } = options ?? {}\n return {\n id: WALLET_ID,\n metadata: { ...MnemonicAdapter.defaultMetadata, ...metadata },\n Adapter: MnemonicAdapter as unknown as WalletAdapterConfig['Adapter'],\n options:\n Object.keys(adapterOptions).length > 0\n ? (adapterOptions as unknown as Record<string, unknown>)\n : undefined,\n capabilities: { excludedNetworks: ['mainnet'] }\n }\n}\n\nexport { MnemonicAdapter }\nexport type { MnemonicOptions }\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@txnlab/use-wallet-mnemonic",
3
- "version": "5.0.0-rc.3",
3
+ "version": "5.0.0-rc.4",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -8,12 +8,16 @@
8
8
  "description": "Mnemonic wallet adapter for @txnlab/use-wallet",
9
9
  "author": "Doug Richar <drichar@gmail.com>",
10
10
  "license": "MIT",
11
+ "engines": {
12
+ "node": ">=22"
13
+ },
11
14
  "repository": {
12
15
  "type": "git",
13
16
  "url": "git+https://github.com/TxnLab/use-wallet.git",
14
17
  "directory": "packages/wallets/mnemonic"
15
18
  },
16
19
  "type": "module",
20
+ "sideEffects": false,
17
21
  "main": "./dist/index.js",
18
22
  "types": "./dist/index.d.ts",
19
23
  "exports": {
@@ -30,10 +34,10 @@
30
34
  "algosdk": "^3.0.0"
31
35
  },
32
36
  "devDependencies": {
33
- "algosdk": "3.5.2",
37
+ "algosdk": "3.6.0",
34
38
  "tsdown": "0.21.0",
35
39
  "typescript": "5.9.3",
36
- "@txnlab/use-wallet": "5.0.0-rc.3"
40
+ "@txnlab/use-wallet": "5.0.0-rc.4"
37
41
  },
38
42
  "scripts": {
39
43
  "build": "tsdown",