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