{"version":3,"file":"kmd-account-manager.mjs","sources":["../../src/types/kmd-account-manager.ts"],"sourcesContent":["import algosdk, { Address } from 'algosdk'\nimport { Config } from '../config'\nimport { SigningAccount, TransactionSignerAccount } from './account'\nimport { AlgoAmount } from './amount'\nimport { ClientManager } from './client-manager'\nimport { TransactionComposer } from './composer'\n\n/** Provides abstractions over a [KMD](https://github.com/algorand/go-algorand/blob/master/daemon/kmd/README.md) instance\n * that makes it easier to get and manage accounts using KMD. */\nexport class KmdAccountManager {\n  private _clientManager: Omit<ClientManager, 'kmd'>\n  private _kmd?: algosdk.Kmd | null\n\n  /**\n   * Create a new KMD manager.\n   * @param clientManager A ClientManager client to use for algod and kmd clients\n   */\n  constructor(clientManager: ClientManager) {\n    this._clientManager = clientManager\n    try {\n      this._kmd = clientManager.kmd\n    } catch {\n      this._kmd = undefined\n    }\n  }\n\n  async kmd(): Promise<algosdk.Kmd> {\n    if (this._kmd === undefined) {\n      if (await this._clientManager.isLocalNet()) {\n        const { kmdConfig } = ClientManager.getConfigFromEnvironmentOrLocalNet()\n        if (kmdConfig) {\n          this._kmd = ClientManager.getKmdClient(kmdConfig)\n          return this._kmd\n        }\n      }\n      this._kmd = null\n    }\n\n    if (!this._kmd) {\n      throw new Error('Attempt to use Kmd client in AlgoKit instance with no Kmd configured')\n    }\n\n    return this._kmd\n  }\n\n  /**\n   * Returns an Algorand signing account with private key loaded from the given KMD wallet (identified by name).\n   *\n   * @param walletName The name of the wallet to retrieve an account from\n   * @param predicate An optional filter to use to find the account (otherwise it will return a random account from the wallet)\n   * @param sender The optional sender address to use this signer for (aka a rekeyed account)\n   * @example Get default funded account in a LocalNet\n   *\n   * ```typescript\n   * const defaultDispenserAccount = await kmdAccountManager.getWalletAccount(\n   *   'unencrypted-default-wallet',\n   *   a => a.status !== 'Offline' && a.amount > 1_000_000_000\n   * )\n   * ```\n   * @returns The signing account (with private key loaded) or undefined if no matching wallet or account was found\n   */\n  public async getWalletAccount(\n    walletName: string,\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    predicate?: (account: Record<string, any>) => boolean,\n    sender?: string | Address,\n  ): Promise<(TransactionSignerAccount & { account: SigningAccount }) | undefined> {\n    return this.findWalletAccount(walletName, predicate, sender)\n  }\n\n  private async findWalletAccount(\n    walletName: string,\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    predicateOrAddress?: ((account: Record<string, any>) => boolean) | string,\n    sender?: string | Address,\n  ): Promise<(TransactionSignerAccount & { account: SigningAccount }) | undefined> {\n    const kmd = await this.kmd()\n    const walletsResponse = await kmd.listWallets()\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const wallet = walletsResponse.wallets.filter((w: any) => w.name === walletName)\n    if (wallet.length === 0) {\n      return undefined\n    }\n    const walletId = wallet[0].id\n    const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token\n\n    let matchedAddress: string | undefined = undefined\n    if (predicateOrAddress && typeof predicateOrAddress === 'string') {\n      matchedAddress = predicateOrAddress\n    } else {\n      const addresses = (await kmd.listKeys(walletHandle)).addresses\n      if (addresses.length > 0) {\n        if (predicateOrAddress && typeof predicateOrAddress === 'function') {\n          for (let i = 0; i < addresses.length; i++) {\n            const account = await this._clientManager.algod.accountInformation(addresses[i]).do()\n            if (predicateOrAddress(account)) {\n              matchedAddress = addresses[i]\n              break\n            }\n          }\n        } else {\n          matchedAddress = addresses[0]\n        }\n      }\n    }\n\n    if (!matchedAddress) {\n      return undefined\n    }\n\n    const accountKey = (await kmd.exportKey(walletHandle, '', matchedAddress)).private_key\n    const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey)\n    const account = algosdk.mnemonicToSecretKey(accountMnemonic)\n    const signingAccount = new SigningAccount(account, sender)\n\n    return {\n      account: signingAccount,\n      addr: signingAccount.addr,\n      signer: signingAccount.signer,\n    }\n  }\n\n  /**\n   * Gets an account with private key loaded from a KMD wallet of the given name, or alternatively creates one with funds in it via a KMD wallet of the given name.\n   *\n   * This is useful to get idempotent accounts from LocalNet without having to specify the private key (which will change when resetting the LocalNet).\n   *\n   * This significantly speeds up local dev time and improves experience since you can write code that *just works* first go without manual config in a fresh LocalNet.\n   *\n   * If this is used via `mnemonicAccountFromEnvironment`, then you can even use the same code that runs on production without changes for local development!\n   *\n   * @param name The name of the wallet to retrieve / create\n   * @param fundWith The number of Algo to fund the account with when it gets created, if not specified then 1000 ALGO will be funded from the dispenser account\n   *\n   * @example\n   * ```typescript\n   * // Idempotently get (if exists) or create (if it doesn't exist yet) an account by name using KMD\n   * // if creating it then fund it with 2 ALGO from the default dispenser account\n   * const newAccount = await kmdAccountManager.getOrCreateWalletAccount('account1', (2).algo())\n   * // This will return the same account as above since the name matches\n   * const existingAccount = await kmdAccountManager.getOrCreateWalletAccount('account1')\n   * ```\n   *\n   * @returns An Algorand account with private key loaded - either one that already existed in the given KMD wallet, or a new one that is funded for you\n   */\n  public async getOrCreateWalletAccount(\n    name: string,\n    fundWith?: AlgoAmount,\n  ): Promise<TransactionSignerAccount & { account: SigningAccount }> {\n    // Get an existing account from the KMD wallet\n    const existing = await this.getWalletAccount(name)\n    if (existing) {\n      return existing\n    }\n\n    const kmd = await this.kmd()\n\n    // None existed: create the KMD wallet instead\n    const walletId = (await kmd.createWallet(name, '')).wallet.id\n    const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token\n    await kmd.generateKey(walletHandle)\n\n    // Get the account from the new KMD wallet\n    const account = (await this.getWalletAccount(name))!\n\n    Config.logger.info(\n      `LocalNet account '${name}' doesn't yet exist; created account ${account.addr} with keys stored in KMD and funding with ${\n        fundWith?.algo ?? 1000\n      } ALGO`,\n    )\n\n    // Fund the account from the dispenser\n    const dispenser = await this.getLocalNetDispenserAccount()\n    await new TransactionComposer({\n      algod: this._clientManager.algod,\n      getSigner: () => dispenser.signer,\n      getSuggestedParams: () => this._clientManager.algod.getTransactionParams().do(),\n    })\n      .addPayment({\n        amount: fundWith ?? AlgoAmount.Algo(1000),\n        receiver: account.addr,\n        sender: dispenser.addr,\n      })\n      .send()\n\n    return account\n  }\n\n  /**\n   * Returns an Algorand account with private key loaded for the default LocalNet dispenser account (that can be used to fund other accounts).\n   * @example\n   * ```typescript\n   * const dispenser = await kmdAccountManager.getLocalNetDispenserAccount()\n   * ```\n   * @returns The default LocalNet dispenser account\n   */\n  public async getLocalNetDispenserAccount() {\n    if (!(await this._clientManager.isLocalNet())) {\n      throw new Error(\"Can't get LocalNet dispenser account from non LocalNet network\")\n    }\n    const genesisResponse = JSON.parse(await this._clientManager.algod.genesis().do()) as {\n      alloc: Array<{ addr: string; comment: string }>\n    }\n    const dispenserAddresses = genesisResponse.alloc.filter((a) => a.comment === 'Wallet1').map((a) => a.addr)\n    if (dispenserAddresses.length > 0) {\n      const dispenser = await this.findWalletAccount('unencrypted-default-wallet', dispenserAddresses[0])\n      if (dispenser) {\n        return dispenser\n      }\n    }\n\n    throw new Error(\"Error retrieving LocalNet dispenser account; couldn't find the default account in KMD\")\n  }\n}\n"],"names":[],"mappings":"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"}