Skip to content

Asset Create

← Back to Transactions

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

From the repository root:

Terminal window
cd examples
npm run example transact/03-asset-create.ts

View source on GitHub

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