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.
- package/.parcel-cache/3e09f086f3c4d605-AssetGraph +0 -0
- package/.parcel-cache/5eac57ec674cdae8-AssetGraph +0 -0
- package/.parcel-cache/data.mdb +0 -0
- package/.parcel-cache/e43547b6c9167b58-RequestGraph +0 -0
- package/.parcel-cache/ecfe15d74834bbfd-BundleGraph +0 -0
- package/.parcel-cache/lock.mdb +0 -0
- package/.parcel-cache/snapshot-e43547b6c9167b58.txt +2 -0
- package/README.md +445 -0
- package/dist/browser/index.js +2456 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/index.d.ts +918 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2915 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2456 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +90 -0
- package/spec.md +257 -0
- package/src/index.ts +93 -0
- package/src/models/rostrum.entities.ts +159 -0
- package/src/models/transaction.entities.ts +46 -0
- package/src/models/wallet.entities.ts +42 -0
- package/src/network/RostrumProvider.ts +137 -0
- package/src/types.ts +0 -0
- package/src/utils/CommonUtils.ts +123 -0
- package/src/utils/TXUtils.ts +445 -0
- package/src/utils/TokenUtils.ts +75 -0
- package/src/utils/ValidationUtils.ts +86 -0
- package/src/utils/WalletUtils.ts +522 -0
- package/src/utils/WatchOnlyTXUtils.ts +275 -0
- package/src/wallet/Wallet.ts +397 -0
- package/src/wallet/WatchOnlyWallet.ts +169 -0
- package/src/wallet/accounts/AccountStore.ts +173 -0
- package/src/wallet/accounts/interfaces/BaseAccountInterface.ts +56 -0
- package/src/wallet/accounts/models/DappAccount.ts +80 -0
- package/src/wallet/accounts/models/DefaultAccount.ts +96 -0
- package/src/wallet/accounts/models/VaultAccount.ts +81 -0
- package/src/wallet/transactions/WalletTransactionCreator.ts +145 -0
- package/src/wallet/transactions/WatchOnlyTransactionCreator.ts +189 -0
- package/src/wallet/transactions/interfaces/TransactionCreator.ts +438 -0
- package/tests/core/tx/transactioncreator.test.ts +455 -0
- package/tests/core/tx/wallettransactioncreator.test.ts +362 -0
- package/tests/core/tx/watchonlytransactioncreator.test.ts +258 -0
- package/tests/core/wallet/accountstore.test.ts +341 -0
- package/tests/core/wallet/wallet.test.ts +69 -0
- package/tests/core/watchonlywallet/watchonlywallet.test.ts +251 -0
- package/tests/index.test.ts +12 -0
- package/tsconfig.json +113 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {Networkish, Networks, TransactionBuilder} from "libnexa-ts";
|
|
2
|
+
import WatchOnlyTransactionCreator from "./transactions/WatchOnlyTransactionCreator";
|
|
3
|
+
import {rostrumProvider} from "../network/RostrumProvider";
|
|
4
|
+
import {WatchOnlyAddress} from "../models/wallet.entities";
|
|
5
|
+
import {isArray, isBuffer, isNil, isObject, isString} from "lodash-es";
|
|
6
|
+
import ValidationUtils from "../utils/ValidationUtils";
|
|
7
|
+
import {isValidNexaAddress} from "../utils/WalletUtils";
|
|
8
|
+
import {SubscribeCallback} from "@vgrunner/electrum-cash";
|
|
9
|
+
import {isNullOrEmpty} from "../utils/CommonUtils";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* WatchOnlyWallet provides functionality for monitoring and creating transactions
|
|
13
|
+
* for addresses without storing private keys. This allows users to track balances
|
|
14
|
+
* and create unsigned transactions that can be signed elsewhere.
|
|
15
|
+
*/
|
|
16
|
+
export default class WatchOnlyWallet {
|
|
17
|
+
/** The blockchain network this wallet operates on */
|
|
18
|
+
private readonly _network: Networkish
|
|
19
|
+
/** Array of addresses to monitor without their private keys */
|
|
20
|
+
private readonly _addressesToWatch: WatchOnlyAddress[];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new WatchOnlyWallet instance
|
|
24
|
+
* @param addressesToWatch Array of addresses to monitor
|
|
25
|
+
* @param network Optional network name (defaults to mainnet)
|
|
26
|
+
* @throws Error if validation fails
|
|
27
|
+
*/
|
|
28
|
+
constructor(addressesToWatch: WatchOnlyAddress[], network?: string) {
|
|
29
|
+
// Validate network parameter
|
|
30
|
+
if (network !== undefined && !isString(network)) {
|
|
31
|
+
throw new Error('Network must be a string');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (network !== undefined && isString(network)) {
|
|
35
|
+
const n = Networks.get(network)
|
|
36
|
+
if (n === undefined) {
|
|
37
|
+
throw new Error(`Invalid network: ${network}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this._network = Networks.get(network) ?? Networks.mainnet;
|
|
42
|
+
// Validate addressesToWatch parameter
|
|
43
|
+
this._addressesToWatch = this.validateAddressesToWatch(addressesToWatch);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validates the addresses to watch array
|
|
48
|
+
* @param addressesToWatch Array of addresses to validate
|
|
49
|
+
* @returns Validated array of WatchOnlyAddress objects
|
|
50
|
+
* @throws Error if validation fails
|
|
51
|
+
*/
|
|
52
|
+
private validateAddressesToWatch(addressesToWatch: WatchOnlyAddress[]): WatchOnlyAddress[] {
|
|
53
|
+
if (addressesToWatch === null || addressesToWatch === undefined) {
|
|
54
|
+
throw new Error('addresesToWatch is required');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!isArray(addressesToWatch)) {
|
|
58
|
+
throw new Error('addressesToWatch must be an array');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if array is not empty
|
|
62
|
+
if (addressesToWatch.length === 0) {
|
|
63
|
+
throw new Error('addressesToWatch cannot be empty');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Validate each address object
|
|
67
|
+
const validatedAddresses: WatchOnlyAddress[] = [];
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < addressesToWatch.length; i++) {
|
|
70
|
+
const addr = addressesToWatch[i];
|
|
71
|
+
|
|
72
|
+
// Check if address is an object
|
|
73
|
+
if (!isObject(addr) || isArray(addr)) {
|
|
74
|
+
throw new Error(`addressesToWatch[${i}] must be an object`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if address property exists and is a string
|
|
78
|
+
if (!addr.hasOwnProperty('address') || !isString(addr.address)) {
|
|
79
|
+
throw new Error(`addressesToWatch[${i}].address must be a string`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if address is not empty
|
|
83
|
+
if (addr.address.trim() === '') {
|
|
84
|
+
throw new Error(`addressesToWatch[${i}].address cannot be empty`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Validate address format
|
|
88
|
+
if (!isValidNexaAddress(addr.address, this._network)) {
|
|
89
|
+
throw new Error(`addressesToWatch[${i}].address is not a valid NEXA address: ${addr.address}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate optional xPub property
|
|
93
|
+
if (addr.xPub !== undefined && !isObject(addr.xPub)) {
|
|
94
|
+
throw new Error(`addressesToWatch[${i}].xPub must be a PublicKey object`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Validate optional derivationPath property
|
|
98
|
+
if (addr.derivationPath !== undefined && !isString(addr.derivationPath)) {
|
|
99
|
+
throw new Error(`addressesToWatch[${i}].derivationPath must be a string`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check for duplicate addresses
|
|
103
|
+
const isDuplicate = validatedAddresses.some(existingAddr =>
|
|
104
|
+
existingAddr.address === addr.address
|
|
105
|
+
);
|
|
106
|
+
if (isDuplicate) {
|
|
107
|
+
throw new Error(`Duplicate address found: ${addr.address}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
validatedAddresses.push({
|
|
111
|
+
address: addr.address.trim(),
|
|
112
|
+
xPub: addr.xPub,
|
|
113
|
+
derivationPath: addr.derivationPath
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return validatedAddresses;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Creates a new transaction creator for this watch-only wallet
|
|
122
|
+
* @param x Optional transaction data - can be a TransactionBuilder, hex string, or Buffer
|
|
123
|
+
* @returns WatchOnlyTransactionCreator configured with wallet's addresses and network
|
|
124
|
+
*/
|
|
125
|
+
public newTransaction(x?: TransactionBuilder | string | Buffer): WatchOnlyTransactionCreator {
|
|
126
|
+
let tx: WatchOnlyTransactionCreator;
|
|
127
|
+
|
|
128
|
+
// Handle different input types for creating transactions
|
|
129
|
+
if (x instanceof TransactionBuilder) {
|
|
130
|
+
// Use existing TransactionBuilder instance
|
|
131
|
+
tx = new WatchOnlyTransactionCreator(x);
|
|
132
|
+
} else if (isString(x)) {
|
|
133
|
+
// Parse transaction from hex string
|
|
134
|
+
tx = new WatchOnlyTransactionCreator().parseTxHex(x);
|
|
135
|
+
} else if (isBuffer(x) && !isNil(x)) {
|
|
136
|
+
// Parse transaction from buffer
|
|
137
|
+
tx = new WatchOnlyTransactionCreator().parseTxBuffer(x);
|
|
138
|
+
} else {
|
|
139
|
+
// Create new empty transaction
|
|
140
|
+
tx = new WatchOnlyTransactionCreator();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Configure transaction with wallet's addresses and network
|
|
144
|
+
return tx.from(this._addressesToWatch).onNetwork(this._network);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Broadcasts a signed transaction to the network
|
|
149
|
+
* @param transaction Hex-encoded signed transaction
|
|
150
|
+
* @returns Promise resolving to transaction ID
|
|
151
|
+
* @throws Error if transaction is invalid or broadcast fails
|
|
152
|
+
*/
|
|
153
|
+
public async sendTransaction(transaction: string): Promise<string> {
|
|
154
|
+
ValidationUtils.validateArgument(isString(transaction), 'transaction must be present and valid')
|
|
155
|
+
return rostrumProvider.broadcast(transaction)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public async subscribeToAddressNotifications(callback: SubscribeCallback): Promise<void>{
|
|
159
|
+
await rostrumProvider.subscribeToAddresses(this._addressesToWatch.map(addr => addr.address), callback)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets the list of addresses being watched
|
|
164
|
+
* @returns Array of watched addresses (copy to prevent mutation)
|
|
165
|
+
*/
|
|
166
|
+
public getWatchedAddresses(): WatchOnlyAddress[] {
|
|
167
|
+
return [...this._addressesToWatch];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import bigDecimal from "js-big-decimal";
|
|
2
|
+
import {BaseAccount} from "./interfaces/BaseAccountInterface";
|
|
3
|
+
import {
|
|
4
|
+
AccountType,
|
|
5
|
+
discoverNexaAccount,
|
|
6
|
+
generateAccountKey,
|
|
7
|
+
generateKeyAndAddress, generateKeysAndAddresses,
|
|
8
|
+
getNextAccountIndex
|
|
9
|
+
} from "../../utils/WalletUtils";
|
|
10
|
+
import {HDPrivateKey} from "libnexa-ts";
|
|
11
|
+
import DAppAccount from "./models/DappAccount";
|
|
12
|
+
import VaultAccount from "./models/VaultAccount";
|
|
13
|
+
import DefaultAccount from "./models/DefaultAccount";
|
|
14
|
+
import {AccountIndexes, AddressKey} from "../../models/wallet.entities";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* AccountStore manages a collection of wallet accounts of different types.
|
|
18
|
+
* It provides functionality to create, import, export, and manage accounts
|
|
19
|
+
* including DApp accounts, Vault accounts, and Default NEXA accounts.
|
|
20
|
+
*/
|
|
21
|
+
export default class AccountStore {
|
|
22
|
+
|
|
23
|
+
/** Map storing all accounts indexed by their unique store key */
|
|
24
|
+
private readonly _accounts: Map<string, BaseAccount>
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new AccountStore instance
|
|
28
|
+
* Initializes an empty map to store accounts
|
|
29
|
+
*/
|
|
30
|
+
public constructor() {
|
|
31
|
+
this._accounts = new Map<string, BaseAccount>()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generates a unique store key for an account based on its type and index
|
|
36
|
+
* @param accountType The type of account (DAPP, VAULT, or DEFAULT)
|
|
37
|
+
* @param index The account index
|
|
38
|
+
* @returns Unique string key for storing the account
|
|
39
|
+
*/
|
|
40
|
+
private getAccountStoreKey(accountType: AccountType, index: number): String {
|
|
41
|
+
switch (accountType){
|
|
42
|
+
case AccountType.DAPP_ACCOUNT:
|
|
43
|
+
// DApp accounts use format: "2.index"
|
|
44
|
+
return String(accountType + '.' + index);
|
|
45
|
+
case AccountType.VAULT_ACCOUNT:
|
|
46
|
+
// Vault accounts use format: "1.index"
|
|
47
|
+
return String(accountType + '.' + index);
|
|
48
|
+
default:
|
|
49
|
+
// Default accounts use just the index
|
|
50
|
+
return String(index)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new account of the specified type
|
|
56
|
+
* @param accountType Type of account to create (DAPP, VAULT, or DEFAULT)
|
|
57
|
+
* @param masterKey Master HD private key for deriving account keys
|
|
58
|
+
* @returns Promise resolving to the created account
|
|
59
|
+
*/
|
|
60
|
+
async createAccount(accountType: AccountType, masterKey: HDPrivateKey): Promise<BaseAccount> {
|
|
61
|
+
// Get the next available index for this account type
|
|
62
|
+
const nextIndex = await getNextAccountIndex(accountType, masterKey);
|
|
63
|
+
const accountStoreKey = this.getAccountStoreKey(accountType, nextIndex)
|
|
64
|
+
|
|
65
|
+
// Check if account already exists
|
|
66
|
+
const indexExists = this._accounts.get(String(accountStoreKey))
|
|
67
|
+
if(indexExists) {
|
|
68
|
+
return indexExists
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
switch (accountType){
|
|
72
|
+
case AccountType.DAPP_ACCOUNT:
|
|
73
|
+
// Create DApp account (purpose 2)
|
|
74
|
+
let dappAccountKey = generateAccountKey(masterKey, 2);
|
|
75
|
+
const dAppAccount = new DAppAccount(2, nextIndex, generateKeyAndAddress(dappAccountKey, nextIndex))
|
|
76
|
+
await dAppAccount.loadBalances();
|
|
77
|
+
this._accounts.set(dAppAccount.getAccountStoreKey(), dAppAccount)
|
|
78
|
+
return dAppAccount
|
|
79
|
+
case AccountType.VAULT_ACCOUNT:
|
|
80
|
+
// Create Vault account (purpose 1)
|
|
81
|
+
let vaultAccountKey = generateAccountKey(masterKey, 1);
|
|
82
|
+
const vaultAccount = new VaultAccount(1, nextIndex, generateKeyAndAddress(vaultAccountKey, nextIndex))
|
|
83
|
+
await vaultAccount.loadBalances();
|
|
84
|
+
this._accounts.set(vaultAccount.getAccountStoreKey(), vaultAccount)
|
|
85
|
+
return vaultAccount
|
|
86
|
+
default:
|
|
87
|
+
// Create default NEXA account with receive and change addresses
|
|
88
|
+
let nexaAccountKey = generateAccountKey(masterKey, nextIndex);
|
|
89
|
+
const nexaAccountIndexes: AccountIndexes = { rIndex: 0, cIndex: 0 };
|
|
90
|
+
const nexaAccount = new DefaultAccount(nextIndex, nexaAccountIndexes, generateKeysAndAddresses(nexaAccountKey, nexaAccountIndexes.rIndex + 1, nexaAccountIndexes.rIndex + 20, nexaAccountIndexes.cIndex + 1, nexaAccountIndexes.cIndex + 20))
|
|
91
|
+
await nexaAccount.loadBalances()
|
|
92
|
+
this._accounts.set(nexaAccount.getAccountStoreKey(), nexaAccount)
|
|
93
|
+
return nexaAccount;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Finds the private key associated with a given address across all accounts
|
|
99
|
+
* @param address The address to search for
|
|
100
|
+
* @returns The AddressKey containing the private key, or null if not found
|
|
101
|
+
*/
|
|
102
|
+
findKeyForAddress(address: string): AddressKey | null {
|
|
103
|
+
// Search through all accounts
|
|
104
|
+
for (const [_, account] of this._accounts.entries()) {
|
|
105
|
+
// Combine receive and change keys for this account
|
|
106
|
+
const allKeys = account.accountKeys.receiveKeys.concat(account.accountKeys.changeKeys)
|
|
107
|
+
|
|
108
|
+
// Check each key for a matching address
|
|
109
|
+
for(const key of allKeys){
|
|
110
|
+
if(key.address == address) {
|
|
111
|
+
return key
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Imports an existing account into the store
|
|
120
|
+
* @param accountData The account data to import
|
|
121
|
+
* @throws Error if an account with the same key already exists
|
|
122
|
+
*/
|
|
123
|
+
importAccount(accountData: BaseAccount): void {
|
|
124
|
+
let index: string = accountData.getAccountStoreKey()
|
|
125
|
+
if(this._accounts.get(index)) {
|
|
126
|
+
throw Error('Account already exists!')
|
|
127
|
+
}
|
|
128
|
+
this._accounts.set(String(index), accountData)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Exports account data by index
|
|
133
|
+
* @param accountIndex The account index to export
|
|
134
|
+
* @returns The account data
|
|
135
|
+
* @throws Error if the account doesn't exist
|
|
136
|
+
*/
|
|
137
|
+
exportAccount(accountIndex: string): BaseAccount {
|
|
138
|
+
if(!this._accounts.get(accountIndex)) {
|
|
139
|
+
throw Error('Cannot find account!')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return this._accounts.get(accountIndex)!
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Removes an account from the store
|
|
147
|
+
* @param accountIndex The account index to remove
|
|
148
|
+
* @throws Error if the account doesn't exist
|
|
149
|
+
*/
|
|
150
|
+
removeAccount(accountIndex: string): void {
|
|
151
|
+
if(!this._accounts.get(accountIndex)) {
|
|
152
|
+
throw Error('Cannot find account!')
|
|
153
|
+
}
|
|
154
|
+
this._accounts.delete(accountIndex)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns all accounts in the store
|
|
159
|
+
* @returns Map of account store keys to BaseAccount objects
|
|
160
|
+
*/
|
|
161
|
+
listAccounts(): Map<string, BaseAccount> {
|
|
162
|
+
return this._accounts
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Retrieves a specific account by its index
|
|
167
|
+
* @param index The account index to retrieve
|
|
168
|
+
* @returns The account if found, undefined otherwise
|
|
169
|
+
*/
|
|
170
|
+
getAccount(index:string): BaseAccount | undefined {
|
|
171
|
+
return this._accounts.get(index)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {AccountIndexes, AccountKeys, AddressKey, Balance} from "../../../models/wallet.entities";
|
|
2
|
+
import DefaultAccount from "../models/DefaultAccount";
|
|
3
|
+
import bigDecimal from "js-big-decimal";
|
|
4
|
+
import {AccountType} from "../../../utils/WalletUtils";
|
|
5
|
+
import {PrivateKey} from "libnexa-ts";
|
|
6
|
+
import {TransactionEntity} from "../../../models/transaction.entities";
|
|
7
|
+
|
|
8
|
+
export abstract class BaseAccount {
|
|
9
|
+
get transactions(): Map<string, TransactionEntity> {
|
|
10
|
+
return this._transactions;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
set transactions(value: Map<string, TransactionEntity>) {
|
|
14
|
+
this._transactions = value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected readonly _bip44Account: number;
|
|
18
|
+
private _balance?: Balance;
|
|
19
|
+
private _tokenBalances?: Record<string, Balance> = {};
|
|
20
|
+
private _transactions : Map<string, TransactionEntity> = new Map<string, TransactionEntity>()
|
|
21
|
+
|
|
22
|
+
protected constructor(_bip44Account: number) {
|
|
23
|
+
this._bip44Account = _bip44Account;
|
|
24
|
+
this._balance = {
|
|
25
|
+
confirmed: 0,
|
|
26
|
+
unconfirmed: 0
|
|
27
|
+
}
|
|
28
|
+
this._tokenBalances = {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
abstract get accountIndexes(): AccountIndexes;
|
|
32
|
+
abstract get accountKeys(): AccountKeys;
|
|
33
|
+
abstract getNewChangeAddress(): string;
|
|
34
|
+
abstract getNewAddress(): string;
|
|
35
|
+
abstract getAccountStoreKey(): string;
|
|
36
|
+
abstract getAccountType(): AccountType;
|
|
37
|
+
abstract loadBalances(): Promise<void>;
|
|
38
|
+
abstract getKeyFromAddress(address:string): AddressKey;
|
|
39
|
+
abstract getTransactions(fromHeight?: number, address?:string): Promise<Map<string, TransactionEntity>>;
|
|
40
|
+
|
|
41
|
+
get balance(): Balance {
|
|
42
|
+
return this._balance!;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set balance(value: Balance) {
|
|
46
|
+
this._balance = value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get tokenBalances(): Record<string, Balance> {
|
|
50
|
+
return this._tokenBalances!;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
set tokenBalances(value: Record<string, Balance>) {
|
|
54
|
+
this._tokenBalances = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {AccountIndexes, AccountKeys, AddressKey} from "../../../models/wallet.entities";
|
|
2
|
+
import {BaseAccount} from "../interfaces/BaseAccountInterface";
|
|
3
|
+
import {
|
|
4
|
+
AccountType, classifyTransaction,
|
|
5
|
+
fetchTotalBalance,
|
|
6
|
+
fetchTransactionsHistory,
|
|
7
|
+
sumBalance,
|
|
8
|
+
sumTokensBalance
|
|
9
|
+
} from "../../../utils/WalletUtils";
|
|
10
|
+
import {PrivateKey} from "libnexa-ts";
|
|
11
|
+
import {TransactionEntity} from "../../../models/transaction.entities";
|
|
12
|
+
|
|
13
|
+
export default class DAppAccount extends BaseAccount {
|
|
14
|
+
|
|
15
|
+
private readonly _accountIndex: number;
|
|
16
|
+
private readonly _accountKey: AddressKey;
|
|
17
|
+
|
|
18
|
+
constructor(bip44Account: number, accountIndex: number, addressKey: AddressKey) {
|
|
19
|
+
super(bip44Account)
|
|
20
|
+
this._accountIndex = accountIndex;
|
|
21
|
+
this._accountKey = addressKey
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// this is used in AccountStore.ts to get the key to be used in the map for this account
|
|
25
|
+
public getAccountStoreKey(): string {
|
|
26
|
+
return String(this._bip44Account + '.' + this._accountIndex);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getAccountType(): AccountType {
|
|
30
|
+
return AccountType.DAPP_ACCOUNT;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getNewAddress() {
|
|
34
|
+
return this._accountKey.address;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public getNewChangeAddress(): string {
|
|
38
|
+
return this._accountKey.address;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get accountIndexes(): AccountIndexes {
|
|
42
|
+
return {
|
|
43
|
+
rIndex: this._accountIndex,
|
|
44
|
+
cIndex: 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get accountKeys(): AccountKeys {
|
|
49
|
+
return {
|
|
50
|
+
receiveKeys: [this._accountKey],
|
|
51
|
+
changeKeys: [this._accountKey]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async loadBalances(): Promise<void> {
|
|
56
|
+
let balances = await fetchTotalBalance([this._accountKey]);
|
|
57
|
+
let tokenBalances = [this._accountKey].map(k => k.tokensBalance);
|
|
58
|
+
super.balance = sumBalance(balances)
|
|
59
|
+
super.tokenBalances = sumTokensBalance(tokenBalances)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getKeyFromAddress(address: string): AddressKey {
|
|
63
|
+
return this._accountKey;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getTransactions(fromHeight?: number, address?: string): Promise<Map<string, TransactionEntity>> {
|
|
67
|
+
let txPromises:TransactionEntity[] = []
|
|
68
|
+
|
|
69
|
+
const transactions = await fetchTransactionsHistory([address ?? this._accountKey.address], fromHeight ?? 0)
|
|
70
|
+
for (let tx of transactions.txs.values()) {
|
|
71
|
+
let t = await classifyTransaction(tx, [address ?? this._accountKey.address])
|
|
72
|
+
txPromises.push(t)
|
|
73
|
+
}
|
|
74
|
+
await Promise.all(txPromises)
|
|
75
|
+
for (let txEntity of txPromises){
|
|
76
|
+
this.transactions.set(txEntity.txId, txEntity)
|
|
77
|
+
}
|
|
78
|
+
return this.transactions
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {AccountIndexes, AccountKeys, AddressKey} from "../../../models/wallet.entities";
|
|
2
|
+
import {BaseAccount} from "../interfaces/BaseAccountInterface";
|
|
3
|
+
import {
|
|
4
|
+
AccountType, classifyTransaction,
|
|
5
|
+
fetchTotalBalance,
|
|
6
|
+
fetchTransactionsHistory,
|
|
7
|
+
sumBalance,
|
|
8
|
+
sumTokensBalance
|
|
9
|
+
} from "../../../utils/WalletUtils";
|
|
10
|
+
import {PrivateKey} from "libnexa-ts";
|
|
11
|
+
import {TransactionEntity} from "../../../models/transaction.entities";
|
|
12
|
+
|
|
13
|
+
export default class DefaultAccount extends BaseAccount {
|
|
14
|
+
|
|
15
|
+
private readonly _accountIndexes: AccountIndexes; // either a list of indexes or a single bip44 account index
|
|
16
|
+
private _accountKeys: AccountKeys; // either a list of keys or a single key
|
|
17
|
+
|
|
18
|
+
constructor(bip44Account: number, accountIndexes: AccountIndexes, accountKeys: AccountKeys) {
|
|
19
|
+
super(bip44Account)
|
|
20
|
+
if (accountIndexes.rIndex < 0) {
|
|
21
|
+
throw new Error(`Can not create nexa account with an rindex of ${accountIndexes.rIndex}. must be >= 0.`);
|
|
22
|
+
}
|
|
23
|
+
if (accountIndexes.cIndex < 0) {
|
|
24
|
+
throw new Error(`Can not create nexa account with an cindex of ${accountIndexes.cIndex}. must be >= 0.`);
|
|
25
|
+
}
|
|
26
|
+
this._accountIndexes = accountIndexes;
|
|
27
|
+
this._accountKeys = accountKeys;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// this is used in AccountStore.ts to get the key to be used in the map for this account
|
|
31
|
+
public getAccountStoreKey() {
|
|
32
|
+
return String(this._bip44Account);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getAccountType(): AccountType {
|
|
36
|
+
return AccountType.NEXA_ACCOUNT
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public getNewAddress() {
|
|
40
|
+
return this._accountKeys.receiveKeys[this._accountKeys.receiveKeys.length - 1]?.address ?? '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public getNewChangeAddress(): string {
|
|
44
|
+
return this._accountKeys.changeKeys[this._accountKeys.changeKeys.length - 1]?.address ?? ''
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get accountIndexes(): AccountIndexes {
|
|
48
|
+
return this._accountIndexes;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get accountKeys(): AccountKeys {
|
|
52
|
+
return this._accountKeys;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async loadBalances(): Promise<void> {
|
|
56
|
+
let balances = await fetchTotalBalance(this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys));
|
|
57
|
+
let tokenBalances = this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys).map(k => k.tokensBalance);
|
|
58
|
+
super.balance = sumBalance(balances)
|
|
59
|
+
super.tokenBalances = sumTokensBalance(tokenBalances)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getKeyFromAddress(address: string): AddressKey {
|
|
63
|
+
const allKeys = this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys)
|
|
64
|
+
const keyFound = allKeys.find(key => key.address === address)
|
|
65
|
+
return keyFound!
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getTransactions(fromHeight?: number, address?: string): Promise<Map<string, TransactionEntity>> {
|
|
69
|
+
|
|
70
|
+
let receiveAddresses = this.accountKeys.receiveKeys.map(ak => ak.address);
|
|
71
|
+
let changeAddresses = this.accountKeys.changeKeys.map(ak => ak.address);
|
|
72
|
+
let allAddresses = receiveAddresses.concat(changeAddresses);
|
|
73
|
+
|
|
74
|
+
let rTxs = fetchTransactionsHistory(address != null ? [address]: receiveAddresses, fromHeight ?? 0);
|
|
75
|
+
let cTxs = fetchTransactionsHistory(address != null ? [address] : receiveAddresses, fromHeight ?? 0);
|
|
76
|
+
|
|
77
|
+
let [rData, cData] = await Promise.all([rTxs, cTxs]);
|
|
78
|
+
|
|
79
|
+
let txHistory = rData.txs;
|
|
80
|
+
for (let tx of cData.txs.values()) {
|
|
81
|
+
txHistory.set(tx.tx_hash, tx);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let txPromises: TransactionEntity[] = [];
|
|
85
|
+
for (let tx of txHistory.values()) {
|
|
86
|
+
let t = await classifyTransaction(tx, allAddresses);
|
|
87
|
+
txPromises.push(t);
|
|
88
|
+
}
|
|
89
|
+
await Promise.all(txPromises)
|
|
90
|
+
|
|
91
|
+
for (let txEntity of txPromises){
|
|
92
|
+
this.transactions.set(txEntity.txId, txEntity)
|
|
93
|
+
}
|
|
94
|
+
return this.transactions
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {AccountIndexes, AccountKeys, AddressKey} from "../../../models/wallet.entities";
|
|
2
|
+
import {BaseAccount} from "../interfaces/BaseAccountInterface";
|
|
3
|
+
import {
|
|
4
|
+
AccountType, classifyTransaction,
|
|
5
|
+
fetchTotalBalance,
|
|
6
|
+
fetchTransactionsHistory,
|
|
7
|
+
sumBalance,
|
|
8
|
+
sumTokensBalance
|
|
9
|
+
} from "../../../utils/WalletUtils";
|
|
10
|
+
import {PrivateKey} from "libnexa-ts";
|
|
11
|
+
import {TransactionEntity} from "../../../models/transaction.entities";
|
|
12
|
+
import {rostrumProvider} from "../../../network/RostrumProvider";
|
|
13
|
+
|
|
14
|
+
export default class VaultAccount extends BaseAccount {
|
|
15
|
+
|
|
16
|
+
private readonly _accountIndex: number;
|
|
17
|
+
private readonly _accountKey: AddressKey;
|
|
18
|
+
|
|
19
|
+
constructor(bip44Account: number, accountIndex: number, addressKey: AddressKey) {
|
|
20
|
+
super(bip44Account)
|
|
21
|
+
this._accountIndex = accountIndex;
|
|
22
|
+
this._accountKey = addressKey;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// this is used in AccountStore.ts to get the key to be used in the map for this account
|
|
26
|
+
public getAccountStoreKey(): string {
|
|
27
|
+
return String(this._bip44Account + '.' + this._accountIndex);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public getAccountType(): AccountType {
|
|
31
|
+
return AccountType.VAULT_ACCOUNT
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public getNewAddress() {
|
|
35
|
+
return this._accountKey.address
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getNewChangeAddress(): string {
|
|
39
|
+
return this._accountKey.address
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get accountIndexes(): AccountIndexes {
|
|
43
|
+
return {
|
|
44
|
+
rIndex: this._accountIndex,
|
|
45
|
+
cIndex: 0
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get accountKeys(): AccountKeys {
|
|
50
|
+
return {
|
|
51
|
+
receiveKeys: [this._accountKey],
|
|
52
|
+
changeKeys: [this._accountKey]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async loadBalances(): Promise<void> {
|
|
57
|
+
let balances = await fetchTotalBalance([this._accountKey]);
|
|
58
|
+
let tokenBalances = [this._accountKey].map(k => k.tokensBalance);
|
|
59
|
+
super.balance = sumBalance(balances)
|
|
60
|
+
super.tokenBalances = sumTokensBalance(tokenBalances)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getKeyFromAddress(address: string): AddressKey {
|
|
64
|
+
return this._accountKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getTransactions(fromHeight?: number, address?: string): Promise<Map<string, TransactionEntity>> {
|
|
68
|
+
let txPromises:TransactionEntity[] = []
|
|
69
|
+
|
|
70
|
+
const transactions = await fetchTransactionsHistory([address ?? this._accountKey.address], fromHeight ?? 0)
|
|
71
|
+
for (let tx of transactions.txs.values()) {
|
|
72
|
+
let t = await classifyTransaction(tx, [address ?? this._accountKey.address])
|
|
73
|
+
txPromises.push(t)
|
|
74
|
+
}
|
|
75
|
+
await Promise.all(txPromises)
|
|
76
|
+
for (let txEntity of txPromises){
|
|
77
|
+
this.transactions.set(txEntity.txId, txEntity)
|
|
78
|
+
}
|
|
79
|
+
return this.transactions
|
|
80
|
+
}
|
|
81
|
+
}
|