nexa-wallet-sdk 0.1.2

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.
Files changed (48) hide show
  1. package/.parcel-cache/3e09f086f3c4d605-AssetGraph +0 -0
  2. package/.parcel-cache/5eac57ec674cdae8-AssetGraph +0 -0
  3. package/.parcel-cache/data.mdb +0 -0
  4. package/.parcel-cache/e43547b6c9167b58-RequestGraph +0 -0
  5. package/.parcel-cache/ecfe15d74834bbfd-BundleGraph +0 -0
  6. package/.parcel-cache/lock.mdb +0 -0
  7. package/.parcel-cache/snapshot-e43547b6c9167b58.txt +2 -0
  8. package/README.md +445 -0
  9. package/dist/browser/index.js +2456 -0
  10. package/dist/browser/index.js.map +1 -0
  11. package/dist/index.d.ts +918 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +2915 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/index.mjs +2456 -0
  16. package/dist/index.mjs.map +1 -0
  17. package/package.json +90 -0
  18. package/spec.md +257 -0
  19. package/src/index.ts +93 -0
  20. package/src/models/rostrum.entities.ts +159 -0
  21. package/src/models/transaction.entities.ts +46 -0
  22. package/src/models/wallet.entities.ts +42 -0
  23. package/src/network/RostrumProvider.ts +137 -0
  24. package/src/types.ts +0 -0
  25. package/src/utils/CommonUtils.ts +123 -0
  26. package/src/utils/TXUtils.ts +445 -0
  27. package/src/utils/TokenUtils.ts +75 -0
  28. package/src/utils/ValidationUtils.ts +86 -0
  29. package/src/utils/WalletUtils.ts +522 -0
  30. package/src/utils/WatchOnlyTXUtils.ts +275 -0
  31. package/src/wallet/Wallet.ts +397 -0
  32. package/src/wallet/WatchOnlyWallet.ts +169 -0
  33. package/src/wallet/accounts/AccountStore.ts +173 -0
  34. package/src/wallet/accounts/interfaces/BaseAccountInterface.ts +56 -0
  35. package/src/wallet/accounts/models/DappAccount.ts +80 -0
  36. package/src/wallet/accounts/models/DefaultAccount.ts +96 -0
  37. package/src/wallet/accounts/models/VaultAccount.ts +81 -0
  38. package/src/wallet/transactions/WalletTransactionCreator.ts +145 -0
  39. package/src/wallet/transactions/WatchOnlyTransactionCreator.ts +189 -0
  40. package/src/wallet/transactions/interfaces/TransactionCreator.ts +438 -0
  41. package/tests/core/tx/transactioncreator.test.ts +455 -0
  42. package/tests/core/tx/wallettransactioncreator.test.ts +362 -0
  43. package/tests/core/tx/watchonlytransactioncreator.test.ts +258 -0
  44. package/tests/core/wallet/accountstore.test.ts +341 -0
  45. package/tests/core/wallet/wallet.test.ts +69 -0
  46. package/tests/core/watchonlywallet/watchonlywallet.test.ts +251 -0
  47. package/tests/index.test.ts +12 -0
  48. package/tsconfig.json +113 -0
@@ -0,0 +1,275 @@
1
+ import { rostrumProvider } from '../network/RostrumProvider';
2
+ import { WatchOnlyAddress } from '../models/wallet.entities';
3
+ import { MAX_INT64, isNullOrEmpty } from './CommonUtils';
4
+ import {
5
+ Address,
6
+ AddressType,
7
+ GroupToken,
8
+ Networkish,
9
+ Transaction,
10
+ TransactionBuilder,
11
+ UnitUtils,
12
+ UTXO
13
+ } from "libnexa-ts";
14
+ import { PermissionLabel, TxOptions } from "../models/transaction.entities";
15
+ import {dupAuthority, isAuthFit} from "./TokenUtils";
16
+
17
+ const MAX_INPUTS_OUTPUTS = 250;
18
+
19
+ export async function watchOnlyPopulateNexaInputsAndChange(txBuilder: TransactionBuilder, addresses: WatchOnlyAddress[], totalTxValue: bigint, options: TxOptions): Promise<string[]> {
20
+ if (isNullOrEmpty(addresses)) {
21
+ throw new Error("Not enough Nexa balance.");
22
+ }
23
+
24
+ let usedAddresses = new Set<string>();
25
+ let origAmount = options.isConsolidate ? 0 : Number(totalTxValue);
26
+
27
+ for (let item of addresses) {
28
+ let utxos = await rostrumProvider.getNexaUtxos(item.address);
29
+ for (let utxo of utxos) {
30
+ let input: UTXO = {
31
+ outpoint: utxo.outpoint_hash,
32
+ address: item.address,
33
+ satoshis: utxo.value,
34
+ templateData: options.templateData
35
+ }
36
+ txBuilder.from(input);
37
+
38
+ if (!usedAddresses.has(item.address)) {
39
+ usedAddresses.add(item.address);
40
+ }
41
+
42
+ if (options.isConsolidate) {
43
+ // need to handle change
44
+ txBuilder.change(options.toChange ?? item.address);
45
+
46
+ if (txBuilder.transaction.inputs.length > MAX_INPUTS_OUTPUTS) {
47
+ return Array.from(usedAddresses.values());
48
+ }
49
+ } else {
50
+ let tx = txBuilder.transaction;
51
+ if (tx.inputs.length > MAX_INPUTS_OUTPUTS) {
52
+ throw new Error("Too many inputs. Consider consolidate transactions or reduce the send amount.");
53
+ }
54
+
55
+ let unspent = tx.getUnspentValue();
56
+ if (unspent < 0n) {
57
+ continue;
58
+ }
59
+
60
+ if (unspent == 0n && options.feeFromAmount) {
61
+ let txFee = tx.estimateRequiredFee();
62
+ tx.updateOutputAmount(0, origAmount - txFee);
63
+ return Array.from(usedAddresses.values());
64
+ }
65
+
66
+ txBuilder.change(options.toChange ?? item.address);
67
+ if (options.feeFromAmount) {
68
+ let hasChange = tx.getChangeOutput();
69
+ let txFee = tx.estimateRequiredFee();
70
+ tx.updateOutputAmount(0, origAmount - txFee);
71
+
72
+ // edge case where change added after update
73
+ if (!hasChange && tx.getChangeOutput()) {
74
+ txFee = tx.estimateRequiredFee();
75
+ tx.updateOutputAmount(0, origAmount - txFee);
76
+ }
77
+ }
78
+
79
+ // check again after change output manipulation
80
+ if (tx.getUnspentValue() < tx.estimateRequiredFee()) {
81
+ // try to add more utxos to satisfy the minimum fee
82
+ continue;
83
+ }
84
+ return Array.from(usedAddresses.values());
85
+ }
86
+ }
87
+ }
88
+
89
+ if (options.isConsolidate) {
90
+ if (usedAddresses.size > 0) {
91
+ return Array.from(usedAddresses.values());
92
+ }
93
+ throw new Error("Not enough Nexa balance.");
94
+ }
95
+
96
+ let err = {
97
+ errorMsg: "Not enough Nexa balance.",
98
+ amount: UnitUtils.formatNEXA(Number(totalTxValue)),
99
+ fee: UnitUtils.formatNEXA(txBuilder.transaction.estimateRequiredFee())
100
+ }
101
+
102
+ throw new Error(JSON.stringify(err));
103
+ }
104
+
105
+ export async function watchOnlyPopulateTokenInputsAndChange(txBuilder: TransactionBuilder, addresses: WatchOnlyAddress[], token: string, outTokenAmount: bigint): Promise<string[]> {
106
+ if (isNullOrEmpty(addresses)) {
107
+ throw new Error("Not enough token balance.");
108
+ }
109
+
110
+ let usedKeys = new Set<string>();
111
+ let inTokenAmount = 0n;
112
+
113
+ for (let item of addresses) {
114
+ let utxos = await rostrumProvider.getTokenUtxos(item.address, token);
115
+ for (let utxo of utxos) {
116
+ if (utxo.token_amount < 0) {
117
+ continue;
118
+ }
119
+ txBuilder.from({
120
+ outpoint: utxo.outpoint_hash,
121
+ address: item.address,
122
+ satoshis: utxo.value,
123
+ groupId: utxo.group,
124
+ groupAmount: BigInt(utxo.token_amount),
125
+ });
126
+
127
+ inTokenAmount = inTokenAmount + BigInt(utxo.token_amount);
128
+ if (!usedKeys.has(item.address)) {
129
+ usedKeys.add(item.address);
130
+ }
131
+
132
+ if (inTokenAmount > MAX_INT64) {
133
+ throw new Error("Token inputs exceeded max amount. Consider sending in small chunks");
134
+ }
135
+ if (txBuilder.transaction.inputs.length > MAX_INPUTS_OUTPUTS) {
136
+ throw new Error("Too many inputs. Consider consolidating transactions or reduce the send amount.");
137
+ }
138
+
139
+ if (inTokenAmount == outTokenAmount) {
140
+ return Array.from(usedKeys.values());
141
+ }
142
+ if (inTokenAmount > outTokenAmount) {
143
+ // change
144
+ txBuilder.to(item.address, Transaction.DUST_AMOUNT, token, inTokenAmount - outTokenAmount);
145
+ return Array.from(usedKeys.values());
146
+ }
147
+ }
148
+ }
149
+
150
+ throw new Error("Not enough token balance");
151
+ }
152
+
153
+ export async function watchOnlyBuildCreateGroupTransaction(
154
+ txBuilder: TransactionBuilder,
155
+ addresses: WatchOnlyAddress[],
156
+ opReturnData: string,
157
+ network: Networkish
158
+ ): Promise<string[]> {
159
+ let outpoint = '', idHex = '';
160
+ let usedKeys: string[] = [];
161
+ for (let item of addresses) {
162
+ let utxos = await rostrumProvider.getNexaUtxos(item.address);
163
+ for (let utxo of utxos) {
164
+ txBuilder.from({
165
+ outpoint: utxo.outpoint_hash,
166
+ address: item.address,
167
+ satoshis: utxo.value
168
+ });
169
+
170
+ if (isNullOrEmpty(outpoint)) {
171
+ outpoint = utxo.outpoint_hash;
172
+ let id = GroupToken.findGroupId(Buffer.from(outpoint, 'hex'), Buffer.from(opReturnData, 'hex'), GroupToken.authFlags.ACTIVE_FLAG_BITS);
173
+ const groupId = new Address(id.hashBuffer, network, AddressType.GroupIdAddress).toString()
174
+ txBuilder.to(item.address, Transaction.DUST_AMOUNT, groupId, GroupToken.authFlags.ACTIVE_FLAG_BITS | id.nonce)
175
+ idHex = id.hashBuffer.toString('hex');
176
+ usedKeys.push(item.address);
177
+ return usedKeys
178
+ }
179
+ }
180
+ }
181
+
182
+ throw new Error("Not enough Nexa balance.");
183
+ }
184
+
185
+ export async function watchOnlyPrepareDeleteTransaction(txBuilder: TransactionBuilder, addresses: WatchOnlyAddress[], outpoint: string): Promise<string[]> {
186
+ let utxo = await rostrumProvider.getUtxo(outpoint);
187
+ let address = utxo.addresses[0];
188
+
189
+ txBuilder.from({
190
+ outpoint: outpoint,
191
+ address: address,
192
+ satoshis: utxo.amount
193
+ });
194
+
195
+ let addrKey = addresses.find(k => k.address === address);
196
+
197
+ if (!addrKey) {
198
+ throw new Error('UTXO associated key not found in the wallet');
199
+ }
200
+ return [addrKey.address];
201
+ }
202
+
203
+ export async function watchOnlyPopulateTokenAuth(txBuilder: TransactionBuilder, addresses: WatchOnlyAddress[], token: string, perm: PermissionLabel, subgroup = ''): Promise<string[]> {
204
+ for (let item of addresses) {
205
+ let utxos = await rostrumProvider.getTokenUtxos(item.address, token);
206
+ for (let utxo of utxos) {
207
+ if (!isAuthFit(utxo.token_amount, perm)) {
208
+ continue;
209
+ }
210
+
211
+ txBuilder.from({
212
+ outpoint: utxo.outpoint_hash,
213
+ address: item.address,
214
+ satoshis: utxo.value
215
+ });
216
+
217
+ if (perm === 'subgroup') {
218
+ txBuilder.to(item.address, Transaction.DUST_AMOUNT, subgroup, dupAuthority(utxo.token_amount, false));
219
+ }
220
+
221
+ // if renew flag included, we don't want to burn it
222
+ if (GroupToken.allowsRenew(BigInt.asUintN(64, BigInt(utxo.token_amount)))) {
223
+ txBuilder.to(item.address, Transaction.DUST_AMOUNT, token, dupAuthority(utxo.token_amount));
224
+ }
225
+
226
+ return [item.address];
227
+ }
228
+ }
229
+
230
+ throw new Error("The requested authority not found");
231
+ }
232
+
233
+ export async function watchOnlyPopulateAndDuplicateTokenAuths(txBuilder: TransactionBuilder, addresses: WatchOnlyAddress[], token: string, perms: PermissionLabel[], toAddr?: string): Promise<string[]> {
234
+ let usedAddresses: string[] = [];
235
+
236
+ let reqiredPerms = new Set(perms);
237
+ reqiredPerms.add('authorise');
238
+
239
+ for (let item of addresses) {
240
+ let utxos = await rostrumProvider.getTokenUtxos(item.address, token);
241
+ for (let utxo of utxos) {
242
+ if (utxo.token_amount > 0) {
243
+ continue;
244
+ }
245
+
246
+ let found = false;
247
+ for (let perm of reqiredPerms) {
248
+ if (isAuthFit(utxo.token_amount, perm)) {
249
+ reqiredPerms.delete(perm);
250
+ found = true;
251
+ }
252
+ }
253
+
254
+ if (!found) {
255
+ continue;
256
+ }
257
+
258
+ txBuilder.from({
259
+ outpoint: utxo.outpoint_hash,
260
+ address: item.address,
261
+ satoshis: utxo.value
262
+ });
263
+ usedAddresses.push(item.address);
264
+
265
+ // duplicate
266
+ txBuilder.to(toAddr != null ? toAddr : item.address, Transaction.DUST_AMOUNT, token, dupAuthority(utxo.token_amount));
267
+
268
+ if (reqiredPerms.size === 0) {
269
+ return usedAddresses;
270
+ }
271
+ }
272
+ }
273
+
274
+ throw new Error("The required authorities not found");
275
+ }
@@ -0,0 +1,397 @@
1
+ import {HDPrivateKey, Message, Networkish, Networks, TransactionBuilder,} from "libnexa-ts";
2
+ import * as Bip39 from 'bip39'
3
+ import {rostrumProvider} from "../network/RostrumProvider";
4
+ import {AccountType, discoverWallet,} from "../utils/WalletUtils";
5
+ import {isBuffer, isNil, isString} from "lodash-es";
6
+ import WalletTransactionCreator from "./transactions/WalletTransactionCreator";
7
+ import AccountStore from "./accounts/AccountStore";
8
+ import {BaseAccount} from "./accounts/interfaces/BaseAccountInterface";
9
+ import ValidationUtils from "../utils/ValidationUtils";
10
+ import {AddressKey} from "../models/wallet.entities";
11
+
12
+ /**
13
+ * Main Wallet class for managing Nexa blockchain wallet operations
14
+ *
15
+ * This class provides comprehensive wallet functionality including:
16
+ * - Creating wallets from seed phrases or private keys
17
+ * - Account discovery and management
18
+ * - Transaction creation and signing
19
+ * - Message signing and verification
20
+ * - Multi-account support with different account types
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Create a new wallet with random seed phrase
25
+ * const wallet = Wallet.create();
26
+ *
27
+ * // Restore wallet from existing seed phrase
28
+ * const wallet = Wallet.fromSeedPhrase('your twelve word seed phrase here');
29
+ *
30
+ * // Initialize wallet (discovers accounts and balances)
31
+ * await wallet.initialize();
32
+ *
33
+ * // Create a new account
34
+ * const account = await wallet.newAccount('DefaultAccount');
35
+ *
36
+ * // Create and send a transaction
37
+ * const tx = wallet.newTransaction(account)
38
+ * .to('nexa:address', 1000000) // 1 NEXA in satoshis
39
+ * .sign();
40
+ *
41
+ * const txId = await wallet.sendTransaction(tx.toHex());
42
+ * ```
43
+ */
44
+ export default class Wallet {
45
+
46
+ /** The master HD private key derived from the seed phrase */
47
+ private readonly masterKey!: HDPrivateKey;
48
+
49
+ /** Store for managing wallet accounts */
50
+ private _accountStore: AccountStore;
51
+
52
+ /** The blockchain network this wallet operates on */
53
+ private readonly _network: Networkish
54
+
55
+ /** The BIP39 seed phrase used to generate this wallet (if created from phrase) */
56
+ private readonly phrase?: string;
57
+
58
+ /**
59
+ * Creates a new Wallet instance
60
+ *
61
+ * @param data - Optional wallet data:
62
+ * - undefined: Generate new random seed phrase
63
+ * - string: Use as BIP39 seed phrase
64
+ * - HDPrivateKey: Use as master key directly
65
+ * @param network - Network name ('mainnet', 'testnet', 'regtest'). Defaults to 'mainnet'
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // Create new wallet with random seed
70
+ * const wallet = new Wallet();
71
+ *
72
+ * // Create from seed phrase
73
+ * const wallet = new Wallet('abandon abandon abandon...');
74
+ *
75
+ * // Create from master key
76
+ * const masterKey = HDPrivateKey.fromString('xprv...');
77
+ * const wallet = new Wallet(masterKey);
78
+ *
79
+ * // Create on testnet
80
+ * const wallet = new Wallet(undefined, 'testnet');
81
+ * ```
82
+ */
83
+ constructor(data?: string | HDPrivateKey | undefined, network?: string) {
84
+ this._network = Networks.get(network) ?? Networks.mainnet
85
+ this._accountStore = new AccountStore()
86
+ if(isNil(data)) {
87
+ this.phrase = Bip39.generateMnemonic(128, undefined, Bip39.wordlists.english)
88
+ const seed = Bip39.mnemonicToSeedSync(this.phrase, '')
89
+
90
+ const masterKey = HDPrivateKey.fromSeed(seed, this._network ?? Networks.mainnet)
91
+ this.masterKey = masterKey.deriveChild(44, true).deriveChild(29223, true)
92
+ } else if(data instanceof HDPrivateKey) {
93
+ this.masterKey = data
94
+ } else if (isString(data)) {
95
+ this.phrase = data
96
+ const seed = Bip39.mnemonicToSeedSync(this.phrase, '')
97
+
98
+ const masterKey = HDPrivateKey.fromSeed(seed, this._network ?? Networks.mainnet)
99
+ this.masterKey = masterKey.deriveChild(44, true).deriveChild(29223, true)
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Create a new wallet with a randomly generated seed phrase
105
+ *
106
+ * This is the recommended way to create a new wallet for first-time users.
107
+ * The generated seed phrase should be securely stored by the user.
108
+ *
109
+ * @returns A new Wallet instance with a random 12-word seed phrase
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const wallet = Wallet.create();
114
+ * console.log(wallet.export().phrase); // Store this securely!
115
+ * ```
116
+ */
117
+ public static create(): Wallet {
118
+ return new Wallet()
119
+ }
120
+
121
+ /**
122
+ * Create a wallet from an existing BIP39 seed phrase
123
+ *
124
+ * Use this method to restore a wallet from a previously generated seed phrase.
125
+ * The seed phrase should be a valid BIP39 mnemonic.
126
+ *
127
+ * @param phrase - The BIP39 seed phrase (12 or 24 words)
128
+ * @param network - Optional network name ('mainnet', 'testnet', 'regtest')
129
+ * @returns A new Wallet instance restored from the seed phrase
130
+ * @throws {Error} If the seed phrase is invalid or not provided
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const wallet = Wallet.fromSeedPhrase(
135
+ * 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
136
+ * 'testnet'
137
+ * );
138
+ * ```
139
+ */
140
+ public static fromSeedPhrase(phrase: string, network?: string): Wallet {
141
+ ValidationUtils.validateArgument(isString(phrase), 'seedphrase must be provided')
142
+ return new Wallet(phrase, network)
143
+ }
144
+
145
+ /**
146
+ * Create a wallet from an extended private key (xpriv)
147
+ *
148
+ * Use this method to create a wallet from a master private key in extended format.
149
+ * This is useful for advanced users who want to use a specific key derivation.
150
+ *
151
+ * @param xpriv - The extended private key string (starts with 'xprv')
152
+ * @param network - Optional network name ('mainnet', 'testnet', 'regtest')
153
+ * @returns A new Wallet instance using the provided master key
154
+ * @throws {Error} If the private key is invalid or not provided
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const wallet = Wallet.fromXpriv(
159
+ * 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
160
+ * );
161
+ * ```
162
+ */
163
+ public static fromXpriv(xpriv: string, network?: string): Wallet {
164
+ ValidationUtils.validateArgument(isString(xpriv), 'private key must be provided')
165
+ const masterKey = HDPrivateKey.fromString(xpriv)
166
+ return new Wallet(masterKey, network)
167
+ }
168
+
169
+ /**
170
+ * Initialize the wallet by discovering accounts and loading balances
171
+ *
172
+ * This method performs account discovery using the BIP44 derivation path
173
+ * and scans for existing accounts with transaction history or balances.
174
+ * Must be called before using the wallet's accounts.
175
+ *
176
+ * @returns Promise that resolves when initialization is complete
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const wallet = Wallet.fromSeedPhrase('your seed phrase');
181
+ * await wallet.initialize();
182
+ *
183
+ * // Now you can access discovered accounts
184
+ * const accounts = wallet.accountStore.listAccounts();
185
+ * ```
186
+ */
187
+ public async initialize(): Promise<void> {
188
+ const walletAccounts: BaseAccount[] = await discoverWallet(this.masterKey)
189
+ for(const account of walletAccounts){
190
+ this._accountStore.importAccount(account)
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Create a new transaction builder for this wallet
196
+ *
197
+ * @param fromAccount - The account to send the transaction from
198
+ * @param x - Optional existing transaction data:
199
+ * - TransactionBuilder: Use existing transaction builder
200
+ * - string: Parse from hex string
201
+ * - Buffer: Parse from binary buffer
202
+ * - undefined: Create new empty transaction
203
+ * @returns A new WalletTransactionCreator instance
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * const account = wallet.accountStore.getAccount(0);
208
+ * const tx = wallet.newTransaction(account)
209
+ * .to('nexa:address', 1000000) // 1 NEXA
210
+ * .sign();
211
+ *
212
+ * // Or from existing transaction hex
213
+ * const tx = wallet.newTransaction(account, 'raw_tx_hex')
214
+ * .sign();
215
+ * ```
216
+ */
217
+ public newTransaction(fromAccount: BaseAccount, x?: TransactionBuilder | string | Buffer): WalletTransactionCreator {
218
+ let tx: WalletTransactionCreator;
219
+
220
+ if (x instanceof TransactionBuilder) {
221
+ tx = new WalletTransactionCreator(fromAccount, x);
222
+ } else if (isString(x)) {
223
+ tx = new WalletTransactionCreator(fromAccount).parseTxHex(x);
224
+ } else if (isBuffer(x) && !isNil(x)) {
225
+ tx = new WalletTransactionCreator(fromAccount).parseTxBuffer(x);
226
+ } else {
227
+ tx = new WalletTransactionCreator(fromAccount);
228
+ }
229
+
230
+ return tx.onNetwork(this._network);
231
+ }
232
+
233
+ /**
234
+ * Create a new account for this wallet
235
+ *
236
+ * @param accountType - The type of account to create:
237
+ * - 'DefaultAccount': Standard account for general use
238
+ * - 'VaultAccount': Secured account with additional protection
239
+ * - 'DappAccount': Account optimized for dApp interactions
240
+ * @returns Promise that resolves to the newly created account
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const defaultAccount = await wallet.newAccount('DefaultAccount');
245
+ * const vaultAccount = await wallet.newAccount('VaultAccount');
246
+ * const dappAccount = await wallet.newAccount('DappAccount');
247
+ * ```
248
+ */
249
+ public async newAccount(accountType: AccountType): Promise<BaseAccount>{
250
+ return await this.accountStore.createAccount(accountType, this.masterKey)
251
+ }
252
+
253
+ /**
254
+ * Broadcast a signed transaction to the Nexa network
255
+ *
256
+ * @param transaction - The signed transaction in hex format
257
+ * @returns Promise that resolves to the transaction ID (txid)
258
+ * @throws {Error} If the transaction is invalid or broadcast fails
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const tx = wallet.newTransaction(account)
263
+ * .to('nexa:address', 1000000)
264
+ * .sign();
265
+ *
266
+ * const txId = await wallet.sendTransaction(tx.toHex());
267
+ * console.log('Transaction sent:', txId);
268
+ * ```
269
+ */
270
+ public async sendTransaction(transaction: string): Promise<string> {
271
+ ValidationUtils.validateArgument(isString(transaction), 'transaction must be present and valid')
272
+ return rostrumProvider.broadcast(transaction)
273
+ }
274
+
275
+ /**
276
+ * Sign a message using a specific address from this wallet
277
+ *
278
+ * The message is signed using the private key associated with the given address.
279
+ * This can be used for authentication or to prove ownership of an address.
280
+ *
281
+ * @param message - The message to sign
282
+ * @param addressToUse - The address whose private key should sign the message
283
+ * @returns The signature as a base64-encoded string
284
+ * @throws {Error} If the address is not owned by this wallet
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const account = wallet.accountStore.getAccount(0);
289
+ * const address = account.getReceiveAddress();
290
+ * const signature = wallet.signMessage('Hello World', address);
291
+ * ```
292
+ */
293
+ public signMessage(message: string, addressToUse: string): string {
294
+ let msg = new Message(message);
295
+ const addressKey = this.accountStore.findKeyForAddress(addressToUse)
296
+ ValidationUtils.validateArgument(isNil(addressKey), "You dont own this private key")
297
+ return msg.sign(addressKey?.key.privateKey!)
298
+ }
299
+
300
+ /**
301
+ * Verify a message signature against an address
302
+ *
303
+ * This method can verify signatures created by any address, not just addresses
304
+ * owned by this wallet. It's useful for verifying messages from other parties.
305
+ *
306
+ * @param message - The original message that was signed
307
+ * @param signature - The signature to verify (base64-encoded)
308
+ * @param address - The address that supposedly signed the message
309
+ * @returns true if the signature is valid, false otherwise
310
+ * @throws {Error} If any parameters are missing or invalid
311
+ *
312
+ * @example
313
+ * ```typescript
314
+ * const isValid = wallet.verifyMessage(
315
+ * 'Hello World',
316
+ * 'signature_string',
317
+ * 'nexa:address'
318
+ * );
319
+ * console.log('Signature valid:', isValid);
320
+ * ```
321
+ */
322
+ public verifyMessage(message: string, signature: string, address: string): boolean {
323
+ ValidationUtils.validateArgument(!isNil(message), 'message is required')
324
+ ValidationUtils.validateArgument(!isNil(signature), 'signature is required')
325
+ ValidationUtils.validateArgument(!isNil(address), 'address is required ')
326
+ let msg = new Message(message);
327
+ const addressKey = this.accountStore.findKeyForAddress(address)
328
+ ValidationUtils.validateArgument(isNil(addressKey), "You dont own this private key")
329
+ return msg.verify(address, signature)
330
+ }
331
+
332
+ /**
333
+ * Export the wallet data for backup or storage
334
+ *
335
+ * Returns an object containing the wallet's seed phrase, master key, and accounts.
336
+ * This data can be used to restore the wallet later. The seed phrase should be
337
+ * stored securely as it provides full access to the wallet.
338
+ *
339
+ * @returns Object containing wallet data
340
+ * @property {string} phrase - The BIP39 seed phrase (if available)
341
+ * @property {HDPrivateKey} masterKey - The master private key
342
+ * @property {BaseAccount[]} accounts - Array of discovered accounts
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * const walletData = wallet.export();
347
+ *
348
+ * // Store the seed phrase securely
349
+ * const seedPhrase = walletData.phrase;
350
+ *
351
+ * // Later, restore the wallet
352
+ * const restoredWallet = Wallet.fromSeedPhrase(seedPhrase);
353
+ * ```
354
+ */
355
+ public export(): any {
356
+ return {
357
+ phrase: this.phrase,
358
+ masterKey: this.masterKey,
359
+ accounts: this._accountStore.listAccounts(),
360
+ accountIndexes: this.accountStore.listAccounts().keys()
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Get the account store for managing wallet accounts
366
+ *
367
+ * The account store provides methods to create, import, and manage accounts
368
+ * within this wallet. Each account has its own set of addresses and keys.
369
+ *
370
+ * @returns The wallet's account store
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * const accountStore = wallet.accountStore;
375
+ * const accounts = accountStore.listAccounts();
376
+ * const firstAccount = accountStore.getAccount(0);
377
+ * ```
378
+ */
379
+ get accountStore(): AccountStore {
380
+ return this._accountStore;
381
+ }
382
+
383
+ /**
384
+ * Get the network this wallet is operating on
385
+ *
386
+ * @returns The network object (mainnet, testnet, or regtest)
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * const network = wallet.network;
391
+ * console.log('Network:', network.name);
392
+ * ```
393
+ */
394
+ get network(): Networkish {
395
+ return this._network;
396
+ }
397
+ }