Signer Configuration
Description
Section titled “Description”This example demonstrates how to configure transaction signers on AlgorandClient:
- setDefaultSigner() to set a fallback signer for all transactions
- setSignerFromAccount() to register a signer from an Account object
- setSigner() to register a signer for a specific address
- How signers are automatically used when sending transactions
- Registering multiple signers for different accounts
- How the default signer is used when no specific signer is registered
- getSigner() and getAccount() to retrieve previously registered signers
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/03-signer-config.ts/** * Example: Signer Configuration * * This example demonstrates how to configure transaction signers on AlgorandClient: * - setDefaultSigner() to set a fallback signer for all transactions * - setSignerFromAccount() to register a signer from an Account object * - setSigner() to register a signer for a specific address * - How signers are automatically used when sending transactions * - Registering multiple signers for different accounts * - How the default signer is used when no specific signer is registered * - getSigner() and getAccount() to retrieve previously registered signers * * Prerequisites: * - LocalNet running (via `algokit localnet start`) */
import { AlgorandClient, algo } from '@algorandfoundation/algokit-utils';import { printError, printHeader, printInfo, printStep, printSuccess, shortenAddress,} from '../shared/utils.js';
async function main() { printHeader('Signer Configuration 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: Create random test accounts printStep(1, 'Create random test accounts using algorand.account.random()'); printInfo('algorand.account.random() creates a new account with a randomly generated keypair'); printInfo('The account is automatically registered with its signer in the AccountManager');
const account1 = algorand.account.random(); const account2 = algorand.account.random(); const account3 = algorand.account.random();
printInfo(`\nCreated accounts:`); printInfo(` Account 1: ${shortenAddress(account1.addr.toString())}`); printInfo(` Account 2: ${shortenAddress(account2.addr.toString())}`); printInfo(` Account 3: ${shortenAddress(account3.addr.toString())}`);
printSuccess('Created 3 random test accounts');
// Step 2: Fund the accounts from the dispenser printStep(2, 'Fund accounts from the LocalNet dispenser'); printInfo('Using the dispenser account to fund our test accounts');
const dispenser = await algorand.account.dispenserFromEnvironment(); printInfo(`Dispenser address: ${shortenAddress(dispenser.addr.toString())}`);
// Fund all three accounts const fundAmount = algo(10); printInfo(`\nFunding each account with ${fundAmount.algo} ALGO...`);
for (const account of [account1, account2, account3]) { await algorand.send.payment({ sender: dispenser.addr, receiver: account.addr, amount: fundAmount, }); }
printSuccess('Funded all accounts with 10 ALGO each');
// Step 3: Demonstrate setSignerFromAccount() printStep(3, 'Demonstrate setSignerFromAccount() - Register signer from an account object'); printInfo( 'setSignerFromAccount() registers the signer from an account that can sign transactions', ); printInfo('This is useful when you have an account object and want to register it for signing');
// Create a new AlgorandClient to demonstrate registering signers explicitly const algorand2 = AlgorandClient.defaultLocalNet();
// Register account1's signer printInfo(`\nRegistering signer for Account 1: ${shortenAddress(account1.addr.toString())}`); algorand2.setSignerFromAccount(account1);
printInfo('Now Account 1 can be used as a sender in transactions');
// Send a payment from account1 to account2 const payment1Result = await algorand2.send.payment({ sender: account1.addr, receiver: account2.addr, amount: algo(1), });
printInfo(`\nPayment transaction sent:`); printInfo(` From: ${shortenAddress(account1.addr.toString())}`); printInfo(` To: ${shortenAddress(account2.addr.toString())}`); printInfo(` Amount: 1 ALGO`); printInfo(` Transaction ID: ${payment1Result.txIds[0]}`); printInfo(` Confirmed in round: ${payment1Result.confirmation.confirmedRound}`);
printSuccess('Successfully sent payment using registered signer');
// Step 4: Demonstrate setSigner() - Register signer for a specific address printStep(4, 'Demonstrate setSigner() - Register signer for a specific address'); printInfo('setSigner(address, signer) registers a TransactionSigner for a specific address'); printInfo('This gives you fine-grained control over which signer to use for each address');
// Create another AlgorandClient to demonstrate setSigner const algorand3 = AlgorandClient.defaultLocalNet();
// Register account2's signer using setSigner printInfo(`\nRegistering signer for Account 2 using setSigner():`); printInfo(` Address: ${shortenAddress(account2.addr.toString())}`); algorand3.setSigner(account2.addr, account2.signer);
// Send a payment from account2 to account3 const payment2Result = await algorand3.send.payment({ sender: account2.addr, receiver: account3.addr, amount: algo(0.5), });
printInfo(`\nPayment transaction sent:`); printInfo(` From: ${shortenAddress(account2.addr.toString())}`); printInfo(` To: ${shortenAddress(account3.addr.toString())}`); printInfo(` Amount: 0.5 ALGO`); printInfo(` Transaction ID: ${payment2Result.txIds[0]}`); printInfo(` Confirmed in round: ${payment2Result.confirmation.confirmedRound}`);
printSuccess('Successfully sent payment using setSigner()');
// Step 5: Demonstrate setDefaultSigner() printStep(5, 'Demonstrate setDefaultSigner() - Set a fallback signer for all transactions'); printInfo( 'setDefaultSigner() sets a signer that will be used when no specific signer is registered', ); printInfo('This is useful when you have a primary account that signs most transactions');
// Create a new AlgorandClient and set account3 as the default signer const algorand4 = AlgorandClient.defaultLocalNet();
printInfo( `\nSetting Account 3 as the default signer: ${shortenAddress(account3.addr.toString())}`, ); algorand4.setDefaultSigner(account3);
// Now we can send a transaction from account3 without explicitly registering it // The default signer will be used const payment3Result = await algorand4.send.payment({ sender: account3.addr, receiver: account1.addr, amount: algo(0.25), });
printInfo(`\nPayment transaction sent using default signer:`); printInfo(` From: ${shortenAddress(account3.addr.toString())}`); printInfo(` To: ${shortenAddress(account1.addr.toString())}`); printInfo(` Amount: 0.25 ALGO`); printInfo(` Transaction ID: ${payment3Result.txIds[0]}`); printInfo(` Confirmed in round: ${payment3Result.confirmation.confirmedRound}`);
printSuccess('Successfully sent payment using default signer');
// Step 6: Demonstrate multiple signers with default fallback printStep(6, 'Demonstrate multiple signers with default fallback'); printInfo('You can register multiple signers and have a default as fallback'); printInfo('Specific signers take precedence over the default signer');
// Create a new AlgorandClient with multiple signers const algorand5 = AlgorandClient.defaultLocalNet();
// Set account1 as the default signer printInfo(`\nSetting up signers:`); printInfo(` Default signer: Account 1 (${shortenAddress(account1.addr.toString())})`); algorand5.setDefaultSigner(account1);
// Also register account2's signer explicitly printInfo(` Registered signer: Account 2 (${shortenAddress(account2.addr.toString())})`); algorand5.setSignerFromAccount(account2);
// Send from account2 (uses registered signer) printInfo(`\nSending from Account 2 (uses registered signer):`); const payment4Result = await algorand5.send.payment({ sender: account2.addr, receiver: account3.addr, amount: algo(0.1), }); printInfo(` Transaction ID: ${payment4Result.txIds[0]}`); printInfo(` Confirmed in round: ${payment4Result.confirmation.confirmedRound}`);
// Send from account1 (uses default signer) printInfo(`\nSending from Account 1 (uses default signer):`); const payment5Result = await algorand5.send.payment({ sender: account1.addr, receiver: account3.addr, amount: algo(0.1), }); printInfo(` Transaction ID: ${payment5Result.txIds[0]}`); printInfo(` Confirmed in round: ${payment5Result.confirmation.confirmedRound}`);
printSuccess('Successfully demonstrated signer priority (specific > default)');
// Step 7: Error handling - No signer registered printStep(7, 'Error handling - Attempting to send without a registered signer'); printInfo('When no signer is registered for an address and no default signer is set,'); printInfo('an error will be thrown when trying to send a transaction');
// Create a new AlgorandClient without any signers const algorand6 = AlgorandClient.defaultLocalNet(); const unregisteredAccount = algorand6.account.random();
printInfo( `\nAttempting to send from unregistered account: ${shortenAddress(unregisteredAccount.addr.toString())}`, ); printInfo('(Note: algorand.account.random() automatically registers the signer,'); printInfo(' but if we create a fresh client and only have the address, it will fail)');
// Create yet another client that doesn't have the signer registered const algorand7 = AlgorandClient.defaultLocalNet();
try { // Try to send from the address without registering a signer await algorand7.send.payment({ sender: unregisteredAccount.addr, // This address has no signer in algorand7 receiver: account1.addr, amount: algo(0.01), }); printInfo('Unexpectedly succeeded'); } catch (error) { printSuccess('Caught expected error when no signer is registered'); printInfo(`Error message: ${error instanceof Error ? error.message : String(error)}`); }
// Step 8: Method chaining printStep(8, 'Method chaining - Configure signers fluently'); printInfo('All signer methods return the AlgorandClient, allowing method chaining');
const algorand8 = AlgorandClient.defaultLocalNet() .setDefaultSigner(account1) .setSignerFromAccount(account2) .setSigner(account3.addr, account3.signer);
printInfo(`\nConfigured AlgorandClient with chained calls:`); printInfo(` .setDefaultSigner(account1)`); printInfo(` .setSignerFromAccount(account2)`); printInfo(` .setSigner(account3.addr, account3.signer)`);
// Verify all signers work const balances: Record<string, bigint> = {}; for (const [name, account] of [ ['Account 1', account1], ['Account 2', account2], ['Account 3', account3], ] as const) { const info = await algorand8.account.getInformation(account.addr); balances[name] = info.balance.microAlgo; }
printInfo(`\nCurrent balances:`); for (const [name, balance] of Object.entries(balances)) { printInfo(` ${name}: ${(Number(balance) / 1_000_000).toFixed(6)} ALGO`); }
printSuccess('Successfully configured AlgorandClient with method chaining');
// Step 9: Retrieve signers and accounts with getSigner() and getAccount() printStep(9, 'Retrieve signers and accounts with getSigner() and getAccount()'); printInfo( 'algorand.account.getSigner(address) retrieves the TransactionSigner registered for an address', ); printInfo( 'algorand.account.getAccount(address) retrieves the full AddressWithSigner for an address', );
// Retrieve the signer for account1 (registered via method chaining in step 8) const retrievedSigner = algorand8.account.getSigner(account1.addr); printInfo(`\nRetrieved signer for Account 1: ${shortenAddress(account1.addr.toString())}`); printSuccess('getSigner() returned a TransactionSigner');
// Retrieve the full AddressWithSigner for account2 const retrievedAccount = algorand8.account.getAccount(account2.addr); printInfo( `\nRetrieved account for Account 2: ${shortenAddress(retrievedAccount.addr.toString())}`, ); printSuccess('getAccount() returned AddressWithSigner (addr + signer)');
// Use the retrieved signer in a transaction via addTransaction const txnWithRetrievedSigner = await algorand8.send.payment({ sender: account1.addr, receiver: account2.addr, amount: algo(0.05), signer: retrievedSigner, }); printInfo(`\nPayment sent using retrieved signer:`); printInfo(` Transaction ID: ${txnWithRetrievedSigner.txIds[0]}`); printInfo(` Confirmed in round: ${txnWithRetrievedSigner.confirmation.confirmedRound}`);
printSuccess('Successfully retrieved and used signers via getSigner() and getAccount()');
// Step 10: Summary printStep(10, 'Summary'); printInfo('Signer configuration methods:'); printInfo(''); printInfo('setDefaultSigner(signer):'); printInfo(' - Sets a fallback signer for all transactions'); printInfo(' - Used when no specific signer is registered for an address'); printInfo(' - Accepts TransactionSigner or AddressWithTransactionSigner'); printInfo(''); printInfo('setSignerFromAccount(account):'); printInfo(' - Registers a signer from an account object'); printInfo(' - Account must have addr and signer properties'); printInfo(' - Supports Account, LogicSigAccount, MultisigAccount, etc.'); printInfo(''); printInfo('setSigner(address, signer):'); printInfo(' - Registers a TransactionSigner for a specific address'); printInfo(' - Gives fine-grained control over signing'); printInfo(''); printInfo('getSigner(address):'); printInfo(' - Retrieves the TransactionSigner registered for an address'); printInfo(' - Falls back to default signer if no specific signer registered'); printInfo(''); printInfo('getAccount(address):'); printInfo(' - Retrieves the AddressWithSigner for an address'); printInfo(' - Returns both the address and its registered signer'); printInfo(''); printInfo('Signer resolution order:'); printInfo(' 1. Specific signer registered for the address'); printInfo(' 2. Default signer (if set)'); printInfo(' 3. Error thrown if no signer found'); printInfo(''); printInfo('Best practices:'); printInfo(' - Use algorand.account.random() for test accounts (auto-registers signer)'); printInfo(' - Set a default signer for your primary signing account'); printInfo(' - Register additional signers as needed for multi-account workflows'); printInfo(' - Method chaining makes configuration concise and readable');
printSuccess('Signer Configuration 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