@algorandfoundation/algokit-utils 9.2.0-beta.5 → 9.2.0-beta.6

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/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "**"
7
7
  ],
8
8
  "name": "@algorandfoundation/algokit-utils",
9
- "version": "9.2.0-beta.5",
9
+ "version": "9.2.0-beta.6",
10
10
  "private": false,
11
11
  "description": "A set of core Algorand utilities written in TypeScript and released via npm that make it easier to build solutions on Algorand.",
12
12
  "author": "Algorand Foundation",
@@ -32,6 +32,7 @@ export declare class KmdAccountManager {
32
32
  getWalletAccount(walletName: string, predicate?: (account: Record<string, any>) => boolean, sender?: string | Address): Promise<(TransactionSignerAccount & {
33
33
  account: SigningAccount;
34
34
  }) | undefined>;
35
+ private findWalletAccount;
35
36
  /**
36
37
  * 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.
37
38
  *
@@ -58,6 +58,11 @@ class KmdAccountManager {
58
58
  async getWalletAccount(walletName,
59
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
60
  predicate, sender) {
61
+ return this.findWalletAccount(walletName, predicate, sender);
62
+ }
63
+ async findWalletAccount(walletName,
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ predicateOrAddress, sender) {
61
66
  const kmd = await this.kmd();
62
67
  const walletsResponse = await kmd.listWallets();
63
68
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -67,21 +72,31 @@ class KmdAccountManager {
67
72
  }
68
73
  const walletId = wallet[0].id;
69
74
  const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token;
70
- const addresses = (await kmd.listKeys(walletHandle)).addresses;
71
- let i = 0;
72
- if (predicate) {
73
- for (i = 0; i < addresses.length; i++) {
74
- const address = addresses[i];
75
- const account = await this._clientManager.algod.accountInformation(address).do();
76
- if (predicate(account)) {
77
- break;
75
+ let matchedAddress = undefined;
76
+ if (predicateOrAddress && typeof predicateOrAddress === 'string') {
77
+ matchedAddress = predicateOrAddress;
78
+ }
79
+ else {
80
+ const addresses = (await kmd.listKeys(walletHandle)).addresses;
81
+ if (addresses.length > 0) {
82
+ if (predicateOrAddress && typeof predicateOrAddress === 'function') {
83
+ for (let i = 0; i < addresses.length; i++) {
84
+ const account = await this._clientManager.algod.accountInformation(addresses[i]).do();
85
+ if (predicateOrAddress(account)) {
86
+ matchedAddress = addresses[i];
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ else {
92
+ matchedAddress = addresses[0];
78
93
  }
79
94
  }
80
95
  }
81
- if (i >= addresses.length) {
96
+ if (!matchedAddress) {
82
97
  return undefined;
83
98
  }
84
- const accountKey = (await kmd.exportKey(walletHandle, '', addresses[i])).private_key;
99
+ const accountKey = (await kmd.exportKey(walletHandle, '', matchedAddress)).private_key;
85
100
  const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey);
86
101
  const account = algosdk.mnemonicToSecretKey(accountMnemonic);
87
102
  const signingAccount = new types_account.SigningAccount(account, sender);
@@ -155,11 +170,15 @@ class KmdAccountManager {
155
170
  if (!(await this._clientManager.isLocalNet())) {
156
171
  throw new Error("Can't get LocalNet dispenser account from non LocalNet network");
157
172
  }
158
- const dispenser = await this.getWalletAccount('unencrypted-default-wallet', (a) => a.status !== 'Offline' && a.amount > 1000000000);
159
- if (!dispenser) {
160
- throw new Error("Error retrieving LocalNet dispenser account; couldn't find the default account in KMD");
173
+ const genesisResponse = JSON.parse(await this._clientManager.algod.genesis().do());
174
+ const dispenserAddresses = genesisResponse.alloc.filter((a) => a.comment === 'Wallet1').map((a) => a.addr);
175
+ if (dispenserAddresses.length > 0) {
176
+ const dispenser = await this.findWalletAccount('unencrypted-default-wallet', dispenserAddresses[0]);
177
+ if (dispenser) {
178
+ return dispenser;
179
+ }
161
180
  }
162
- return dispenser;
181
+ throw new Error("Error retrieving LocalNet dispenser account; couldn't find the default account in KMD");
163
182
  }
164
183
  }
165
184
 
@@ -1 +1 @@
1
- {"version":3,"file":"kmd-account-manager.js","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 const kmd = await this.kmd()\n\n const walletsResponse = await kmd.listWallets()\n\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\n const walletId = wallet[0].id\n\n const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token\n const addresses = (await kmd.listKeys(walletHandle)).addresses\n\n let i = 0\n if (predicate) {\n for (i = 0; i < addresses.length; i++) {\n const address = addresses[i]\n const account = await this._clientManager.algod.accountInformation(address).do()\n if (predicate(account)) {\n break\n }\n }\n }\n\n if (i >= addresses.length) {\n return undefined\n }\n\n const accountKey = (await kmd.exportKey(walletHandle, '', addresses[i])).private_key\n\n const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey)\n\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\n const dispenser = await this.getWalletAccount('unencrypted-default-wallet', (a) => a.status !== 'Offline' && a.amount > 1_000_000_000)\n if (!dispenser) {\n throw new Error(\"Error retrieving LocalNet dispenser account; couldn't find the default account in KMD\")\n }\n\n return dispenser\n }\n}\n"],"names":["ClientManager","SigningAccount","Config","TransactionComposer","AlgoAmount"],"mappings":";;;;;;;;;AAOA;AACgE;MACnD,iBAAiB,CAAA;AAI5B;;;AAGG;AACH,IAAA,WAAA,CAAY,aAA4B,EAAA;AACtC,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa;AACnC,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,GAAG;;AAC7B,QAAA,MAAM;AACN,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;;;AAIzB,IAAA,MAAM,GAAG,GAAA;AACP,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;YAC3B,IAAI,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE;gBAC1C,MAAM,EAAE,SAAS,EAAE,GAAGA,iCAAa,CAAC,kCAAkC,EAAE;gBACxE,IAAI,SAAS,EAAE;oBACb,IAAI,CAAC,IAAI,GAAGA,iCAAa,CAAC,YAAY,CAAC,SAAS,CAAC;oBACjD,OAAO,IAAI,CAAC,IAAI;;;AAGpB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI;;AAGlB,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AACd,YAAA,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC;;QAGzF,OAAO,IAAI,CAAC,IAAI;;AAGlB;;;;;;;;;;;;;;;AAeG;IACI,MAAM,gBAAgB,CAC3B,UAAkB;;AAElB,IAAA,SAAqD,EACrD,MAAyB,EAAA;AAEzB,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;AAE5B,QAAA,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE;;AAG/C,QAAA,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,KAAK,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;AAChF,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,YAAA,OAAO,SAAS;;QAGlB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;AAE7B,QAAA,MAAM,YAAY,GAAG,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,mBAAmB;AACnF,QAAA,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS;QAE9D,IAAI,CAAC,GAAG,CAAC;QACT,IAAI,SAAS,EAAE;AACb,YAAA,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,gBAAA,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC;AAC5B,gBAAA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE;AAChF,gBAAA,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE;oBACtB;;;;AAKN,QAAA,IAAI,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE;AACzB,YAAA,OAAO,SAAS;;QAGlB,MAAM,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW;QAEpF,MAAM,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC;QAE/D,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,eAAe,CAAC;QAC5D,MAAM,cAAc,GAAG,IAAIC,4BAAc,CAAC,OAAO,EAAE,MAAM,CAAC;QAE1D,OAAO;AACL,YAAA,OAAO,EAAE,cAAc;YACvB,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,MAAM,EAAE,cAAc,CAAC,MAAM;SAC9B;;AAGH;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACI,IAAA,MAAM,wBAAwB,CACnC,IAAY,EACZ,QAAqB,EAAA;;QAGrB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAClD,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ;;AAGjB,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;;AAG5B,QAAA,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE;AAC7D,QAAA,MAAM,YAAY,GAAG,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,mBAAmB;AACnF,QAAA,MAAM,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC;;QAGnC,MAAM,OAAO,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAE;AAEpD,QAAAC,aAAM,CAAC,MAAM,CAAC,IAAI,CAChB,CAAA,kBAAA,EAAqB,IAAI,CAAwC,qCAAA,EAAA,OAAO,CAAC,IAAI,CAAA,0CAAA,EAC3E,QAAQ,EAAE,IAAI,IAAI,IACpB,CAAA,KAAA,CAAO,CACR;;AAGD,QAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,2BAA2B,EAAE;QAC1D,MAAM,IAAIC,kCAAmB,CAAC;AAC5B,YAAA,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,KAAK;AAChC,YAAA,SAAS,EAAE,MAAM,SAAS,CAAC,MAAM;AACjC,YAAA,kBAAkB,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC,EAAE,EAAE;SAChF;AACE,aAAA,UAAU,CAAC;YACV,MAAM,EAAE,QAAQ,IAAIC,uBAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YACzC,QAAQ,EAAE,OAAO,CAAC,IAAI;YACtB,MAAM,EAAE,SAAS,CAAC,IAAI;SACvB;AACA,aAAA,IAAI,EAAE;AAET,QAAA,OAAO,OAAO;;AAGhB;;;;;;;AAOG;AACI,IAAA,MAAM,2BAA2B,GAAA;QACtC,IAAI,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,EAAE;AAC7C,YAAA,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC;;QAGnF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,4BAA4B,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,GAAG,UAAa,CAAC;QACtI,IAAI,CAAC,SAAS,EAAE;AACd,YAAA,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC;;AAG1G,QAAA,OAAO,SAAS;;AAEnB;;;;"}
1
+ {"version":3,"file":"kmd-account-manager.js","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":["ClientManager","SigningAccount","Config","TransactionComposer","AlgoAmount"],"mappings":"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"}
@@ -56,6 +56,11 @@ class KmdAccountManager {
56
56
  async getWalletAccount(walletName,
57
57
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
58
  predicate, sender) {
59
+ return this.findWalletAccount(walletName, predicate, sender);
60
+ }
61
+ async findWalletAccount(walletName,
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ predicateOrAddress, sender) {
59
64
  const kmd = await this.kmd();
60
65
  const walletsResponse = await kmd.listWallets();
61
66
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -65,21 +70,31 @@ class KmdAccountManager {
65
70
  }
66
71
  const walletId = wallet[0].id;
67
72
  const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token;
68
- const addresses = (await kmd.listKeys(walletHandle)).addresses;
69
- let i = 0;
70
- if (predicate) {
71
- for (i = 0; i < addresses.length; i++) {
72
- const address = addresses[i];
73
- const account = await this._clientManager.algod.accountInformation(address).do();
74
- if (predicate(account)) {
75
- break;
73
+ let matchedAddress = undefined;
74
+ if (predicateOrAddress && typeof predicateOrAddress === 'string') {
75
+ matchedAddress = predicateOrAddress;
76
+ }
77
+ else {
78
+ const addresses = (await kmd.listKeys(walletHandle)).addresses;
79
+ if (addresses.length > 0) {
80
+ if (predicateOrAddress && typeof predicateOrAddress === 'function') {
81
+ for (let i = 0; i < addresses.length; i++) {
82
+ const account = await this._clientManager.algod.accountInformation(addresses[i]).do();
83
+ if (predicateOrAddress(account)) {
84
+ matchedAddress = addresses[i];
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ else {
90
+ matchedAddress = addresses[0];
76
91
  }
77
92
  }
78
93
  }
79
- if (i >= addresses.length) {
94
+ if (!matchedAddress) {
80
95
  return undefined;
81
96
  }
82
- const accountKey = (await kmd.exportKey(walletHandle, '', addresses[i])).private_key;
97
+ const accountKey = (await kmd.exportKey(walletHandle, '', matchedAddress)).private_key;
83
98
  const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey);
84
99
  const account = algosdk.mnemonicToSecretKey(accountMnemonic);
85
100
  const signingAccount = new SigningAccount(account, sender);
@@ -153,11 +168,15 @@ class KmdAccountManager {
153
168
  if (!(await this._clientManager.isLocalNet())) {
154
169
  throw new Error("Can't get LocalNet dispenser account from non LocalNet network");
155
170
  }
156
- const dispenser = await this.getWalletAccount('unencrypted-default-wallet', (a) => a.status !== 'Offline' && a.amount > 1000000000);
157
- if (!dispenser) {
158
- throw new Error("Error retrieving LocalNet dispenser account; couldn't find the default account in KMD");
171
+ const genesisResponse = JSON.parse(await this._clientManager.algod.genesis().do());
172
+ const dispenserAddresses = genesisResponse.alloc.filter((a) => a.comment === 'Wallet1').map((a) => a.addr);
173
+ if (dispenserAddresses.length > 0) {
174
+ const dispenser = await this.findWalletAccount('unencrypted-default-wallet', dispenserAddresses[0]);
175
+ if (dispenser) {
176
+ return dispenser;
177
+ }
159
178
  }
160
- return dispenser;
179
+ throw new Error("Error retrieving LocalNet dispenser account; couldn't find the default account in KMD");
161
180
  }
162
181
  }
163
182
 
@@ -1 +1 @@
1
- {"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 const kmd = await this.kmd()\n\n const walletsResponse = await kmd.listWallets()\n\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\n const walletId = wallet[0].id\n\n const walletHandle = (await kmd.initWalletHandle(walletId, '')).wallet_handle_token\n const addresses = (await kmd.listKeys(walletHandle)).addresses\n\n let i = 0\n if (predicate) {\n for (i = 0; i < addresses.length; i++) {\n const address = addresses[i]\n const account = await this._clientManager.algod.accountInformation(address).do()\n if (predicate(account)) {\n break\n }\n }\n }\n\n if (i >= addresses.length) {\n return undefined\n }\n\n const accountKey = (await kmd.exportKey(walletHandle, '', addresses[i])).private_key\n\n const accountMnemonic = algosdk.secretKeyToMnemonic(accountKey)\n\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\n const dispenser = await this.getWalletAccount('unencrypted-default-wallet', (a) => a.status !== 'Offline' && a.amount > 1_000_000_000)\n if (!dispenser) {\n throw new Error(\"Error retrieving LocalNet dispenser account; couldn't find the default account in KMD\")\n }\n\n return dispenser\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"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":";;;;;;;AAOA;AACgE;MACnD,iBAAiB,CAAA;AAI5B;;;AAGG;AACH,IAAA,WAAA,CAAY,aAA4B,EAAA;AACtC,QAAA,IAAI,CAAC,cAAc,GAAG,aAAa;AACnC,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,GAAG;;AAC7B,QAAA,MAAM;AACN,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;;;AAIzB,IAAA,MAAM,GAAG,GAAA;AACP,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;YAC3B,IAAI,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE;gBAC1C,MAAM,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,kCAAkC,EAAE;gBACxE,IAAI,SAAS,EAAE;oBACb,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC,SAAS,CAAC;oBACjD,OAAO,IAAI,CAAC,IAAI;;;AAGpB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI;;AAGlB,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AACd,YAAA,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC;;QAGzF,OAAO,IAAI,CAAC,IAAI;;AAGlB;;;;;;;;;;;;;;;AAeG;IACI,MAAM,gBAAgB,CAC3B,UAAkB;;AAElB,IAAA,SAAqD,EACrD,MAAyB,EAAA;QAEzB,OAAO,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC;;IAGtD,MAAM,iBAAiB,CAC7B,UAAkB;;AAElB,IAAA,kBAAyE,EACzE,MAAyB,EAAA;AAEzB,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;AAC5B,QAAA,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE;;AAE/C,QAAA,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,KAAK,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;AAChF,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,YAAA,OAAO,SAAS;;QAElB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;AAC7B,QAAA,MAAM,YAAY,GAAG,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,mBAAmB;QAEnF,IAAI,cAAc,GAAuB,SAAS;AAClD,QAAA,IAAI,kBAAkB,IAAI,OAAO,kBAAkB,KAAK,QAAQ,EAAE;YAChE,cAAc,GAAG,kBAAkB;;aAC9B;AACL,YAAA,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS;AAC9D,YAAA,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;AACxB,gBAAA,IAAI,kBAAkB,IAAI,OAAO,kBAAkB,KAAK,UAAU,EAAE;AAClE,oBAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACzC,wBAAA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;AACrF,wBAAA,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE;AAC/B,4BAAA,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC;4BAC7B;;;;qBAGC;AACL,oBAAA,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC;;;;QAKnC,IAAI,CAAC,cAAc,EAAE;AACnB,YAAA,OAAO,SAAS;;AAGlB,QAAA,MAAM,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,WAAW;QACtF,MAAM,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC;QAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,eAAe,CAAC;QAC5D,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC;QAE1D,OAAO;AACL,YAAA,OAAO,EAAE,cAAc;YACvB,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,MAAM,EAAE,cAAc,CAAC,MAAM;SAC9B;;AAGH;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACI,IAAA,MAAM,wBAAwB,CACnC,IAAY,EACZ,QAAqB,EAAA;;QAGrB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAClD,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ;;AAGjB,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;;AAG5B,QAAA,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE;AAC7D,QAAA,MAAM,YAAY,GAAG,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,mBAAmB;AACnF,QAAA,MAAM,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC;;QAGnC,MAAM,OAAO,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAE;AAEpD,QAAA,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,CAAA,kBAAA,EAAqB,IAAI,CAAwC,qCAAA,EAAA,OAAO,CAAC,IAAI,CAAA,0CAAA,EAC3E,QAAQ,EAAE,IAAI,IAAI,IACpB,CAAA,KAAA,CAAO,CACR;;AAGD,QAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,2BAA2B,EAAE;QAC1D,MAAM,IAAI,mBAAmB,CAAC;AAC5B,YAAA,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,KAAK;AAChC,YAAA,SAAS,EAAE,MAAM,SAAS,CAAC,MAAM;AACjC,YAAA,kBAAkB,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC,EAAE,EAAE;SAChF;AACE,aAAA,UAAU,CAAC;YACV,MAAM,EAAE,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YACzC,QAAQ,EAAE,OAAO,CAAC,IAAI;YACtB,MAAM,EAAE,SAAS,CAAC,IAAI;SACvB;AACA,aAAA,IAAI,EAAE;AAET,QAAA,OAAO,OAAO;;AAGhB;;;;;;;AAOG;AACI,IAAA,MAAM,2BAA2B,GAAA;QACtC,IAAI,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,EAAE;AAC7C,YAAA,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC;;AAEnF,QAAA,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAEhF;AACD,QAAA,MAAM,kBAAkB,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;AAC1G,QAAA,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE;AACjC,YAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,4BAA4B,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;YACnG,IAAI,SAAS,EAAE;AACb,gBAAA,OAAO,SAAS;;;AAIpB,QAAA,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC;;AAE3G;;;;"}