UNPKG

@dolaned/wallet-sdk-ts

Version:

Wallet SDK for the Nexa blockchain

174 lines (158 loc) 6.93 kB
import bigDecimal from "js-big-decimal"; import {BaseAccount} from "./interfaces/BaseAccountInterface"; import { AccountType, discoverNexaAccount, generateAccountKey, generateKeyAndAddress, generateKeysAndAddresses, getNextAccountIndex } from "../../utils/WalletUtils"; import {HDPrivateKey} from "libnexa-ts"; import DAppAccount from "./models/DappAccount"; import VaultAccount from "./models/VaultAccount"; import DefaultAccount from "./models/DefaultAccount"; import {AccountIndexes, AddressKey} from "../../models/wallet.entities"; /** * AccountStore manages a collection of wallet accounts of different types. * It provides functionality to create, import, export, and manage accounts * including DApp accounts, Vault accounts, and Default NEXA accounts. */ export default class AccountStore { /** Map storing all accounts indexed by their unique store key */ private readonly _accounts: Map<string, BaseAccount> /** * Creates a new AccountStore instance * Initializes an empty map to store accounts */ public constructor() { this._accounts = new Map<string, BaseAccount>() } /** * Generates a unique store key for an account based on its type and index * @param accountType The type of account (DAPP, VAULT, or DEFAULT) * @param index The account index * @returns Unique string key for storing the account */ private getAccountStoreKey(accountType: AccountType, index: number): String { switch (accountType){ case AccountType.DAPP_ACCOUNT: // DApp accounts use format: "2.index" return String(accountType + '.' + index); case AccountType.VAULT_ACCOUNT: // Vault accounts use format: "1.index" return String(accountType + '.' + index); default: // Default accounts use just the index return String(index) } } /** * Creates a new account of the specified type * @param accountType Type of account to create (DAPP, VAULT, or DEFAULT) * @param masterKey Master HD private key for deriving account keys * @returns Promise resolving to the created account */ async createAccount(accountType: AccountType, masterKey: HDPrivateKey): Promise<BaseAccount> { // Get the next available index for this account type const nextIndex = await getNextAccountIndex(accountType, masterKey); const accountStoreKey = this.getAccountStoreKey(accountType, nextIndex) // Check if account already exists const indexExists = this._accounts.get(String(accountStoreKey)) if(indexExists) { return indexExists } switch (accountType){ case AccountType.DAPP_ACCOUNT: // Create DApp account (purpose 2) let dappAccountKey = generateAccountKey(masterKey, 2); const dAppAccount = new DAppAccount(2, nextIndex, generateKeyAndAddress(dappAccountKey, nextIndex)) await dAppAccount.loadBalances(); this._accounts.set(dAppAccount.getAccountStoreKey(), dAppAccount) return dAppAccount case AccountType.VAULT_ACCOUNT: // Create Vault account (purpose 1) let vaultAccountKey = generateAccountKey(masterKey, 1); const vaultAccount = new VaultAccount(1, nextIndex, generateKeyAndAddress(vaultAccountKey, nextIndex)) await vaultAccount.loadBalances(); this._accounts.set(vaultAccount.getAccountStoreKey(), vaultAccount) return vaultAccount default: // Create default NEXA account with receive and change addresses let nexaAccountKey = generateAccountKey(masterKey, nextIndex); const nexaAccountIndexes: AccountIndexes = { rIndex: 0, cIndex: 0 }; const nexaAccount = new DefaultAccount(nextIndex, nexaAccountIndexes, generateKeysAndAddresses(nexaAccountKey, nexaAccountIndexes.rIndex + 1, nexaAccountIndexes.rIndex + 20, nexaAccountIndexes.cIndex + 1, nexaAccountIndexes.cIndex + 20)) await nexaAccount.loadBalances() this._accounts.set(nexaAccount.getAccountStoreKey(), nexaAccount) return nexaAccount; } } /** * Finds the private key associated with a given address across all accounts * @param address The address to search for * @returns The AddressKey containing the private key, or null if not found */ findKeyForAddress(address: string): AddressKey | null { // Search through all accounts for (const [_, account] of this._accounts.entries()) { // Combine receive and change keys for this account const allKeys = account.accountKeys.receiveKeys.concat(account.accountKeys.changeKeys) // Check each key for a matching address for(const key of allKeys){ if(key.address == address) { return key } } } return null } /** * Imports an existing account into the store * @param accountData The account data to import * @throws Error if an account with the same key already exists */ importAccount(accountData: BaseAccount): void { let index: string = accountData.getAccountStoreKey() if(this._accounts.get(index)) { throw Error('Account already exists!') } this._accounts.set(String(index), accountData) } /** * Exports account data by index * @param accountIndex The account index to export * @returns The account data * @throws Error if the account doesn't exist */ exportAccount(accountIndex: string): BaseAccount { if(!this._accounts.get(accountIndex)) { throw Error('Cannot find account!') } return this._accounts.get(accountIndex)! } /** * Removes an account from the store * @param accountIndex The account index to remove * @throws Error if the account doesn't exist */ removeAccount(accountIndex: string): void { if(!this._accounts.get(accountIndex)) { throw Error('Cannot find account!') } this._accounts.delete(accountIndex) } /** * Returns all accounts in the store * @returns Map of account store keys to BaseAccount objects */ listAccounts(): Map<string, BaseAccount> { return this._accounts } /** * Retrieves a specific account by its index * @param index The account index to retrieve * @returns The account if found, undefined otherwise */ getAccount(index:string): BaseAccount | undefined { return this._accounts.get(index) } }