Skip to content

Account Manager

← Back to Algorand Client

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
  • LocalNet running (via algokit localnet start)

From the repository root:

Terminal window
cd examples
npm run example algorand_client/05-account-manager.ts

View source on GitHub

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)
})