Asset Create
Description
Section titled “Description”This example demonstrates how to create a new Algorand Standard Asset (ASA) using the transact package. It shows the low-level transaction construction pattern with:
- Transaction class with TransactionType.AssetConfig
- AssetConfigTransactionFields with all configuration options
- Retrieving created asset ID from pending transaction info
- Verifying asset parameters and creator holdings
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 transact/03-asset-create.ts/** * Example: Asset Create * * This example demonstrates how to create a new Algorand Standard Asset (ASA) using * the transact package. It shows the low-level transaction construction pattern with: * - Transaction class with TransactionType.AssetConfig * - AssetConfigTransactionFields with all configuration options * - Retrieving created asset ID from pending transaction info * - Verifying asset parameters and creator holdings * * Prerequisites: * - LocalNet running (via `algokit localnet start`) */
import { AlgorandClient } from '@algorandfoundation/algokit-utils';import type { PendingTransactionResponse } from '@algorandfoundation/algokit-utils/algod-client';import { Transaction, TransactionType, assignFee, type AssetConfigTransactionFields,} from '@algorandfoundation/algokit-utils/transact';import { createAlgodClient, printHeader, printInfo, printStep, printSuccess, shortenAddress, waitForConfirmation,} from '../shared/utils.js';
/** * Gets a funded account from LocalNet's KMD wallet */async function getLocalNetFundedAccount(algorand: AlgorandClient) { return await algorand.account.kmd.getLocalNetDispenserAccount();}
async function main() { printHeader('Asset Create Example');
// Step 1: Initialize clients printStep(1, 'Initialize Algod Client'); const algod = createAlgodClient(); const algorand = AlgorandClient.defaultLocalNet(); printInfo('Connected to LocalNet Algod');
// Step 2: Get a funded account from KMD (creator) printStep(2, 'Get Funded Account from KMD (Asset Creator)'); const creator = await getLocalNetFundedAccount(algorand); printInfo(`Creator address: ${shortenAddress(creator.addr.toString())}`);
// Step 3: Get suggested transaction parameters printStep(3, 'Get Suggested Transaction Parameters'); const suggestedParams = await algod.suggestedParams(); printInfo(`First valid round: ${suggestedParams.firstValid}`); printInfo(`Last valid round: ${suggestedParams.lastValid}`); printInfo(`Min fee: ${suggestedParams.minFee} microALGO`);
// Step 4: Define asset configuration with all fields printStep(4, 'Define Asset Configuration');
// Asset parameters const assetTotal = 1_000_000_000_000n; // 1 million units with 6 decimals const assetDecimals = 6; const assetName = 'Example Token'; const assetUnitName = 'EXMPL'; const assetUrl = 'https://example.com/asset'; const defaultFrozen = false;
// Asset configuration fields - all addresses set to creator for this example const assetConfigFields: AssetConfigTransactionFields = { assetId: 0n, // 0 indicates asset creation total: assetTotal, decimals: assetDecimals, defaultFrozen: defaultFrozen, assetName: assetName, unitName: assetUnitName, url: assetUrl, // Management addresses - all set to creator manager: creator.addr, // Can reconfigure asset reserve: creator.addr, // Holds non-minted units freeze: creator.addr, // Can freeze/unfreeze accounts clawback: creator.addr, // Can clawback assets };
printInfo(`Asset name: ${assetName}`); printInfo(`Unit name: ${assetUnitName}`); printInfo( `Total supply: ${assetTotal} (${Number(assetTotal) / Math.pow(10, assetDecimals)} ${assetUnitName})`, ); printInfo(`Decimals: ${assetDecimals}`); printInfo(`Default frozen: ${defaultFrozen}`); printInfo(`URL: ${assetUrl}`); printInfo(`Manager: ${shortenAddress(creator.addr.toString())}`); printInfo(`Reserve: ${shortenAddress(creator.addr.toString())}`); printInfo(`Freeze: ${shortenAddress(creator.addr.toString())}`); printInfo(`Clawback: ${shortenAddress(creator.addr.toString())}`);
// Step 5: Create asset config transaction printStep(5, 'Create Asset Config Transaction'); const transaction = new Transaction({ type: TransactionType.AssetConfig, sender: creator.addr, firstValid: suggestedParams.firstValid, lastValid: suggestedParams.lastValid, genesisHash: suggestedParams.genesisHash, genesisId: suggestedParams.genesisId, assetConfig: assetConfigFields, });
printInfo(`Transaction type: ${transaction.type}`);
// Step 6: Assign fee using suggested params printStep(6, 'Assign Transaction Fee'); const transactionWithFee = assignFee(transaction, { feePerByte: suggestedParams.fee, minFee: suggestedParams.minFee, }); printInfo(`Assigned fee: ${transactionWithFee.fee} microALGO`);
// Step 7: Sign the transaction printStep(7, 'Sign Transaction'); const signedTxns = await creator.signer([transactionWithFee], [0]); const txId = transactionWithFee.txId(); printInfo(`Transaction ID: ${txId}`); printInfo('Transaction signed successfully');
// Step 8: Submit transaction and wait for confirmation printStep(8, 'Submit Transaction and Wait for Confirmation'); await algod.sendRawTransaction(signedTxns); printInfo('Transaction submitted to network');
// Wait for confirmation using the utility function const pendingInfo = (await waitForConfirmation(algod, txId)) as PendingTransactionResponse; printInfo(`Transaction confirmed in round: ${pendingInfo.confirmedRound}`);
// Step 9: Retrieve created asset ID from pending transaction info printStep(9, 'Retrieve Created Asset ID'); const assetId = pendingInfo.assetId; if (!assetId) { throw new Error('Asset ID not found in pending transaction response'); } printInfo(`Created asset ID: ${assetId}`); printSuccess(`Asset created with ID: ${assetId}`);
// Step 10: Verify asset exists with correct parameters using algod.assetById() printStep(10, 'Verify Asset Parameters'); const asset = await algod.assetById(assetId); printInfo(`Asset ID from API: ${asset.id}`); printInfo(`Creator: ${asset.params.creator}`); printInfo(`Total: ${asset.params.total}`); printInfo(`Decimals: ${asset.params.decimals}`); printInfo(`Name: ${asset.params.name}`); printInfo(`Unit Name: ${asset.params.unitName}`); printInfo(`URL: ${asset.params.url}`); printInfo(`Default Frozen: ${asset.params.defaultFrozen ?? false}`); printInfo(`Manager: ${asset.params.manager}`); printInfo(`Reserve: ${asset.params.reserve}`); printInfo(`Freeze: ${asset.params.freeze}`); printInfo(`Clawback: ${asset.params.clawback}`);
// Verify all parameters match const creatorAddress = creator.addr.toString(); if (asset.params.total !== assetTotal) { throw new Error(`Total mismatch: expected ${assetTotal}, got ${asset.params.total}`); } if (asset.params.decimals !== assetDecimals) { throw new Error(`Decimals mismatch: expected ${assetDecimals}, got ${asset.params.decimals}`); } if (asset.params.name !== assetName) { throw new Error(`Name mismatch: expected ${assetName}, got ${asset.params.name}`); } if (asset.params.unitName !== assetUnitName) { throw new Error(`Unit name mismatch: expected ${assetUnitName}, got ${asset.params.unitName}`); } if (asset.params.url !== assetUrl) { throw new Error(`URL mismatch: expected ${assetUrl}, got ${asset.params.url}`); } if (asset.params.creator !== creatorAddress) { throw new Error(`Creator mismatch: expected ${creatorAddress}, got ${asset.params.creator}`); } if (asset.params.manager !== creatorAddress) { throw new Error(`Manager mismatch: expected ${creatorAddress}, got ${asset.params.manager}`); } if (asset.params.reserve !== creatorAddress) { throw new Error(`Reserve mismatch: expected ${creatorAddress}, got ${asset.params.reserve}`); } if (asset.params.freeze !== creatorAddress) { throw new Error(`Freeze mismatch: expected ${creatorAddress}, got ${asset.params.freeze}`); } if (asset.params.clawback !== creatorAddress) { throw new Error(`Clawback mismatch: expected ${creatorAddress}, got ${asset.params.clawback}`); }
printSuccess('All asset parameters verified correctly!');
// Step 11: Verify creator holds total supply printStep(11, 'Verify Creator Holds Total Supply'); const accountAssetInfo = await algod.accountAssetInformation(creatorAddress, assetId);
if (!accountAssetInfo.assetHolding) { throw new Error('Creator does not have asset holding'); }
const creatorBalance = accountAssetInfo.assetHolding.amount; printInfo( `Creator balance: ${creatorBalance} (${Number(creatorBalance) / Math.pow(10, assetDecimals)} ${assetUnitName})`, ); printInfo(`Total supply: ${assetTotal}`);
if (creatorBalance !== assetTotal) { throw new Error(`Creator balance ${creatorBalance} does not match total supply ${assetTotal}`); }
printSuccess(`Creator holds entire supply: ${creatorBalance} units`); printSuccess('Asset create example completed successfully!');}
main().catch(error => { console.error('Error:', error); process.exit(1);});