Account Manager
Description
Section titled “Description”This example demonstrates how to use the account manager to create, import, and manage accounts including:
- algorand.account.random() to generate a new random account
- algorand.account.fromMnemonic() to import from 25-word mnemonic
- algorand.account.fromEnvironment() to load from env var
- algorand.account.fromKmd() to get account from KMD wallet
- algorand.account.multisig() to create a multisig account
- algorand.account.logicsig() to create a logic signature account
- algorand.account.rekeyed() to create a rekeyed account reference
- algorand.account.getInformation() to fetch account details from network
- algorand.account.ensureFunded() to ensure account has minimum balance
- algorand.account.ensureFundedFromEnvironment() for dispenser funding
Prerequisites
Section titled “Prerequisites”- LocalNet running (via
algokit localnet start)
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example algorand_client/05-account-manager.ts/** * Example: Account Manager * * This example demonstrates how to use the account manager to create, import, * and manage accounts including: * - algorand.account.random() to generate a new random account * - algorand.account.fromMnemonic() to import from 25-word mnemonic * - algorand.account.fromEnvironment() to load from env var * - algorand.account.fromKmd() to get account from KMD wallet * - algorand.account.multisig() to create a multisig account * - algorand.account.logicsig() to create a logic signature account * - algorand.account.rekeyed() to create a rekeyed account reference * - algorand.account.getInformation() to fetch account details from network * - algorand.account.ensureFunded() to ensure account has minimum balance * - algorand.account.ensureFundedFromEnvironment() for dispenser funding * * Prerequisites: * - LocalNet running (via `algokit localnet start`) */
import { AlgorandClient, algo } from '@algorandfoundation/algokit-utils';import { MultisigMetadata } from '@algorandfoundation/algokit-utils/transact';import { formatAlgo, formatBytes, loadTealSource, printError, printHeader, printInfo, printStep, printSuccess, shortenAddress,} from '../shared/utils.js';
async function main() { printHeader('Account Manager Example');
// Initialize client and verify LocalNet is running const algorand = AlgorandClient.defaultLocalNet();
try { await algorand.client.algod.status(); printSuccess('Connected to LocalNet'); } catch (error) { printError( `Failed to connect to LocalNet: ${error instanceof Error ? error.message : String(error)}`, ); printInfo('Make sure LocalNet is running (e.g., algokit localnet start)'); return; }
// Step 1: Generate a random account with algorand.account.random() printStep(1, 'Generate random account with algorand.account.random()'); printInfo('random() creates a new account with a randomly generated keypair'); printInfo('The account is automatically registered with its signer in AccountManager');
const randomAccount = algorand.account.random();
printInfo(`\nRandom account created:`); printInfo(` addr (Address object): ${randomAccount.addr.toString()}`); printInfo(` toString(): ${randomAccount.toString()} (same as addr.toString())`); printInfo(` publicKey: ${formatBytes(randomAccount.publicKey)}`); printInfo(` signer: [TransactionSigner function] - automatically registered`);
printSuccess('Generated random account');
// Step 2: Import account from 25-word mnemonic with algorand.account.fromMnemonic() printStep(2, 'Import account from mnemonic with algorand.account.fromMnemonic()'); printInfo('fromMnemonic() loads an account from a 25-word mnemonic secret'); printInfo('WARNING: Never commit mnemonics to source control!');
// Generate a test mnemonic for demo purposes // In practice, you would load this from environment variables or secure storage const { mnemonicFromSeed } = await import('@algorandfoundation/algokit-algo25');
// Create a random 32-byte seed and generate a mnemonic from it const randomSeed = crypto.getRandomValues(new Uint8Array(32)); const demoMnemonic = mnemonicFromSeed(randomSeed);
printInfo( `\nExample mnemonic (first 5 words): "${demoMnemonic.split(' ').slice(0, 5).join(' ')}..."`, );
// Import using the mnemonic const mnemonicAccount = algorand.account.fromMnemonic(demoMnemonic);
printInfo(`\nMnemonic account imported:`); printInfo(` addr: ${shortenAddress(mnemonicAccount.addr.toString())}`); printInfo(` signer: [TransactionSigner function] - ready for signing`);
printSuccess('Imported account from mnemonic');
// Step 3: Load account from environment with algorand.account.fromEnvironment() printStep(3, 'Load account from environment with algorand.account.fromEnvironment()'); printInfo('fromEnvironment() loads account based on environment variable conventions:'); printInfo(''); printInfo('Non-LocalNet convention:'); printInfo(' - Loads {NAME}_MNEMONIC as mnemonic secret'); printInfo(' - Optionally loads {NAME}_SENDER for rekeyed accounts'); printInfo(''); printInfo('LocalNet convention:'); printInfo(' - Creates/retrieves a KMD wallet named {NAME}'); printInfo(' - Auto-funds with 1000 ALGO by default');
// On LocalNet, this will create a wallet named "DEMO" and fund it const envAccount = await algorand.account.fromEnvironment('DEMO', algo(10));
printInfo(`\nEnvironment account loaded (LocalNet wallet "DEMO"):`); printInfo(` addr: ${shortenAddress(envAccount.addr.toString())}`);
// Verify it was funded const envInfo = await algorand.account.getInformation(envAccount.addr); printInfo(` balance: ${formatAlgo(envInfo.balance)}`);
printSuccess('Loaded account from environment');
// Step 4: Get account from KMD wallet with algorand.account.fromKmd() printStep(4, 'Get account from KMD wallet with algorand.account.fromKmd()'); printInfo('fromKmd() retrieves an account from a KMD wallet by name'); printInfo('Optional predicate filter to find specific accounts');
// Get an account from the default LocalNet wallet const kmdAccount = await algorand.account.fromKmd( 'unencrypted-default-wallet', a => a.status !== 'Offline' && a.amount > 1_000_000_000, // Filter: online accounts with > 1000 ALGO );
printInfo(`\nKMD account retrieved:`); printInfo(` addr: ${shortenAddress(kmdAccount.addr.toString())}`); printInfo(` wallet: unencrypted-default-wallet`);
const kmdInfo = await algorand.account.getInformation(kmdAccount.addr); printInfo(` balance: ${formatAlgo(kmdInfo.balance)}`);
printSuccess('Retrieved account from KMD wallet');
// Step 5: Create a multisig account with algorand.account.multisig() printStep(5, 'Create multisig account with algorand.account.multisig()'); printInfo('multisig() creates a multisig account from multiple sub-signers'); printInfo('Requires: version, threshold (min signatures), and participant addresses');
// Create 3 accounts for the multisig const msig1 = algorand.account.random(); const msig2 = algorand.account.random(); const msig3 = algorand.account.random();
const multisigParams: MultisigMetadata = { version: 1, // Multisig version (always 1) threshold: 2, // Require 2 of 3 signatures addrs: [msig1.addr, msig2.addr, msig3.addr], // Participant addresses (order matters!) };
// Create multisig with 2 sub-signers (accounts 1 and 2) const multisigAccount = algorand.account.multisig(multisigParams, [msig1, msig2]);
printInfo(`\nMultisig account created:`); printInfo(` addr: ${shortenAddress(multisigAccount.addr.toString())}`); printInfo(` version: ${multisigParams.version}`); printInfo(` threshold: ${multisigParams.threshold} of ${multisigParams.addrs.length}`); printInfo(` participants:`); printInfo(` 1: ${shortenAddress(msig1.addr.toString())}`); printInfo(` 2: ${shortenAddress(msig2.addr.toString())}`); printInfo(` 3: ${shortenAddress(msig3.addr.toString())}`); printInfo(` signer: [MultisigSigner function] - signs with accounts 1 and 2`);
printSuccess('Created multisig account');
// Step 6: Create a logic signature account with algorand.account.logicsig() printStep(6, 'Create logic signature account with algorand.account.logicsig()'); printInfo('logicsig() creates an account backed by a compiled TEAL program'); printInfo('The program defines the conditions under which transactions are approved');
// Load TEAL program that always approves (for demo purposes only!) // In production, use meaningful logic that validates transactions const tealSource = loadTealSource('always-approve.teal');
// Compile the TEAL program using algorand.app.compileTeal() const compileResult = await algorand.app.compileTeal(tealSource); const program = compileResult.compiledBase64ToBytes;
// Create the logic signature account const logicsigAccount = algorand.account.logicsig(program);
printInfo(`\nLogic signature account created:`); printInfo(` addr: ${shortenAddress(logicsigAccount.addr.toString())}`); printInfo(` program hash: ${logicsigAccount.addr.toString().slice(0, 16)}...`); printInfo(` program size: ${program.length} bytes`); printInfo(` signer: [LogicSigSigner function] - evaluates TEAL program`); printInfo(''); printInfo('Note: Logic sig address is derived from the program hash'); printInfo('Anyone can send transactions from this address if the program approves');
printSuccess('Created logic signature account');
// Step 7: On-chain rekey with algorand.account.rekeyAccount() printStep(7, 'On-chain rekey with algorand.account.rekeyAccount()'); printInfo('rekeyAccount() performs an on-chain rekey transaction'); printInfo( 'After rekeying, transactions from the original account are signed by the auth account', );
// Create an account that will be the "auth" account (the one that signs) const authAccount = algorand.account.random();
// Fund both accounts so we can demonstrate the rekey printInfo(`\nFunding accounts for rekey demonstration...`); await algorand.account.ensureFundedFromEnvironment(randomAccount.addr, algo(5)); await algorand.account.ensureFundedFromEnvironment(authAccount.addr, algo(5)); printInfo(` randomAccount: ${shortenAddress(randomAccount.addr.toString())} (funded)`); printInfo(` authAccount: ${shortenAddress(authAccount.addr.toString())} (funded)`);
// Perform the on-chain rekey: randomAccount will now be signed by authAccount printInfo(`\nRekeying randomAccount to authAccount...`); const rekeyResult = await algorand.account.rekeyAccount(randomAccount.addr, authAccount); printInfo(` txId: ${rekeyResult.txIds[0]}`); printInfo(` confirmed in round: ${rekeyResult.confirmation.confirmedRound}`); printSuccess('On-chain rekey completed');
// Verify the rekey by checking on-chain account info const rekeyedInfo = await algorand.account.getInformation(randomAccount.addr); printInfo(`\nOn-chain verification:`); printInfo( ` authAddr: ${rekeyedInfo.authAddr ? shortenAddress(rekeyedInfo.authAddr.toString()) : 'none'}`, );
// Send a payment from randomAccount — now signed by authAccount automatically printInfo(`\nSending payment from rekeyed account to verify...`); const rekeyPayment = await algorand.send.payment({ sender: randomAccount.addr, receiver: authAccount.addr, amount: algo(1), }); printInfo(` txId: ${rekeyPayment.txIds[0]}`); printInfo(` confirmed in round: ${rekeyPayment.confirmation.confirmedRound}`); printSuccess('Payment from rekeyed account succeeded (signed by authAccount)');
// Also show rekeyed() for creating a manual reference printInfo(`\nYou can also create a rekeyed reference manually with rekeyed():`); const rekeyedAccount = algorand.account.rekeyed(randomAccount.addr, authAccount); printInfo(` sender addr: ${shortenAddress(rekeyedAccount.addr.toString())}`); printInfo(` auth account: ${shortenAddress(authAccount.addr.toString())}`); printInfo(` signer: Uses authAccount's signer`); printInfo(''); printInfo('Note: rekeyAccount() auto-registers the rekeyed signer,'); printInfo('so rekeyed() is only needed if you set up the reference without the on-chain call');
printSuccess('Demonstrated on-chain rekey flow');
// Step 8: Fetch account information with algorand.account.getInformation() printStep(8, 'Fetch account info with algorand.account.getInformation()'); printInfo('getInformation() fetches current account status from the network'); printInfo('Returns balance, min balance, rewards, opted-in assets/apps, and more');
// Get the dispenser account to demonstrate const dispenser = await algorand.account.dispenserFromEnvironment(); const accountInfo = await algorand.account.getInformation(dispenser.addr);
printInfo(`\nAccount information for dispenser:`); printInfo(` address: ${shortenAddress(accountInfo.address.toString())}`); printInfo(` balance: ${formatAlgo(accountInfo.balance)}`); printInfo(` minBalance: ${formatAlgo(accountInfo.minBalance)}`); printInfo( ` spendable: ${formatAlgo(accountInfo.balance.microAlgo - accountInfo.minBalance.microAlgo)} (balance - minBalance)`, ); printInfo(` pendingRewards: ${formatAlgo(accountInfo.pendingRewards)}`); printInfo(` totalRewards: ${formatAlgo(accountInfo.rewards)}`); printInfo(` status: ${accountInfo.status}`); printInfo(` validAsOfRound: ${accountInfo.validAsOfRound}`); printInfo(` totalAppsOptedIn: ${accountInfo.totalAppsOptedIn}`); printInfo(` totalAssetsOptedIn: ${accountInfo.totalAssetsOptedIn}`); printInfo(` totalCreatedApps: ${accountInfo.totalCreatedApps}`); printInfo(` totalCreatedAssets: ${accountInfo.totalCreatedAssets}`); if (accountInfo.authAddr) { printInfo(` authAddr (rekey): ${accountInfo.authAddr}`); }
printSuccess('Fetched account information');
// Step 9: Ensure account is funded with algorand.account.ensureFunded() printStep(9, 'Ensure account is funded with algorand.account.ensureFunded()'); printInfo('ensureFunded() funds an account to have a minimum spending balance'); printInfo('Only sends funds if needed (idempotent)'); printInfo('minSpendingBalance is the balance ABOVE the minimum balance requirement');
// Create a new account to fund const accountToFund = algorand.account.random();
printInfo(`\nNew account: ${shortenAddress(accountToFund.addr.toString())}`);
// Check initial balance const beforeInfo = await algorand.account.getInformation(accountToFund.addr); printInfo(`Initial balance: ${formatAlgo(beforeInfo.balance)}`);
// Ensure it has at least 5 ALGO to spend const fundResult = await algorand.account.ensureFunded( accountToFund.addr, dispenser.addr, algo(5), // Minimum spending balance (above min balance requirement) );
if (fundResult) { printInfo(`\nFunding transaction:`); printInfo(` txId: ${fundResult.transactionId}`); printInfo(` amountFunded: ${formatAlgo(fundResult.amountFunded)}`); } else { printInfo(`No funding needed - account already has sufficient balance`); }
// Check new balance const afterInfo = await algorand.account.getInformation(accountToFund.addr); printInfo(`New balance: ${formatAlgo(afterInfo.balance)}`); printInfo(`Min balance: ${formatAlgo(afterInfo.minBalance)}`); printInfo( `Spendable: ${formatAlgo(afterInfo.balance.microAlgo - afterInfo.minBalance.microAlgo)}`, );
// Call again to show it's idempotent const fundResult2 = await algorand.account.ensureFunded( accountToFund.addr, dispenser.addr, algo(5), );
if (!fundResult2) { printInfo(`\nSecond call: No funding needed (idempotent)`); }
printSuccess('Demonstrated ensureFunded()');
// Step 10: Ensure funded from environment with algorand.account.ensureFundedFromEnvironment() printStep( 10, 'Ensure funded from environment with algorand.account.ensureFundedFromEnvironment()', ); printInfo('ensureFundedFromEnvironment() uses the dispenser from environment variables'); printInfo('On LocalNet: uses default LocalNet dispenser'); printInfo('On other networks: uses DISPENSER_MNEMONIC env var');
// Create another account to fund const accountToFund2 = algorand.account.random();
printInfo(`\nNew account: ${shortenAddress(accountToFund2.addr.toString())}`);
// Fund using environment dispenser const envFundResult = await algorand.account.ensureFundedFromEnvironment( accountToFund2.addr, algo(2), // Minimum spending balance { minFundingIncrement: algo(5) }, // But fund at least 5 ALGO when funding );
if (envFundResult) { printInfo(`\nFunding from environment:`); printInfo(` txId: ${envFundResult.transactionId}`); printInfo(` amountFunded: ${formatAlgo(envFundResult.amountFunded)}`); printInfo(` Note: minFundingIncrement(5) > minSpendingBalance(2)`); }
const afterInfo2 = await algorand.account.getInformation(accountToFund2.addr); printInfo(`Final balance: ${formatAlgo(afterInfo2.balance)}`);
printSuccess('Demonstrated ensureFundedFromEnvironment()');
// Step 11: Account properties summary printStep(11, 'Account properties summary'); printInfo('All account types share common properties:'); printInfo(''); printInfo('addr (Address):'); printInfo(' - The Address object containing the account address'); printInfo(' - Use .toString() to get the 58-character string representation'); printInfo(''); printInfo('publicKey (Uint8Array):'); printInfo(' - The 32-byte public key'); printInfo(' - Used for cryptographic operations'); printInfo(''); printInfo('signer (TransactionSigner):'); printInfo(' - Function that signs transaction groups'); printInfo(' - Automatically used when sending transactions'); printInfo(''); printInfo('Different account types may have additional properties:'); printInfo(' - MultisigAccount: .account.params (multisig metadata)'); printInfo(' - LogicSigAccount: .account (underlying logic sig)'); printInfo(' - Rekeyed: .account (underlying auth account)');
// Demonstrate accessing properties printInfo(`\nExample - Random account properties:`); printInfo(` randomAccount.addr: ${randomAccount.addr}`); printInfo(` randomAccount.toString(): ${randomAccount.toString()}`); printInfo(` randomAccount.publicKey: Uint8Array(${randomAccount.publicKey.length})`); printInfo(` randomAccount.signer: [Function]`);
printInfo(`\nExample - Multisig account properties:`); printInfo(` multisigAccount.addr: ${shortenAddress(multisigAccount.addr.toString())}`); printInfo(` multisigAccount.account.params: { version: 1, threshold: 2, addrs: [...] }`);
printSuccess('Account properties summary complete');
// Step 12: Summary printStep(12, 'Summary'); printInfo('Account creation methods:'); printInfo(''); printInfo('random():'); printInfo(' - Generates new random keypair'); printInfo(' - Account is automatically tracked for signing'); printInfo(' - Returns Address & AddressWithSigners'); printInfo(''); printInfo('fromMnemonic(mnemonic, sender?):'); printInfo(' - Imports from 25-word mnemonic'); printInfo(' - Optional sender for rekeyed accounts'); printInfo(' - Returns AddressWithTransactionSigner'); printInfo(''); printInfo('fromEnvironment(name, fundWith?):'); printInfo(' - LocalNet: creates/gets KMD wallet, auto-funds'); printInfo(' - Other: loads {NAME}_MNEMONIC env var'); printInfo(' - Returns AddressWithTransactionSigner'); printInfo(''); printInfo('fromKmd(walletName, predicate?, sender?):'); printInfo(' - Gets account from KMD wallet by name'); printInfo(' - Optional predicate to filter accounts'); printInfo(' - Returns AddressWithTransactionSigner'); printInfo(''); printInfo('multisig(params, subSigners):'); printInfo(' - Creates multisig from sub-signers'); printInfo(' - Returns Address & AddressWithTransactionSigner'); printInfo(''); printInfo('logicsig(program, args?):'); printInfo(' - Creates logic signature account'); printInfo(' - Returns Address & AddressWithTransactionSigner'); printInfo(''); printInfo('rekeyed(sender, authAccount):'); printInfo(' - Creates rekeyed account reference'); printInfo(' - Returns Address & AddressWithTransactionSigner'); printInfo(''); printInfo('Account operations:'); printInfo(''); printInfo('getInformation(address):'); printInfo(' - Fetches account details from network'); printInfo(' - Returns AccountInformation with balance, etc.'); printInfo(''); printInfo('ensureFunded(accountToFund, dispenser, minSpending):'); printInfo(' - Funds account to have min spending balance'); printInfo(' - Idempotent - only funds if needed'); printInfo(''); printInfo('ensureFundedFromEnvironment(accountToFund, minSpending):'); printInfo(' - Same as ensureFunded but uses env dispenser'); printInfo(' - LocalNet: default dispenser, Other: DISPENSER_MNEMONIC');
printSuccess('Account Manager example completed!');}
main().catch(error => { printError(`Unhandled error: ${error instanceof Error ? error.message : String(error)}`); process.exit(1);});Other examples in Algorand Client
Section titled “Other examples in Algorand Client”- Client Instantiation
- AlgoAmount Utility
- Signer Configuration
- Suggested Params Configuration
- Account Manager
- Send Payment
- Send Asset Operations
- Send Application Operations
- Create Transaction (Unsigned Transactions)
- Transaction Composer (Atomic Transaction Groups)
- Asset Manager
- App Manager
- App Deployer
- Client Manager
- Error Transformers
- Transaction Leases