UTXO Model
Understand Cardano's UTXO model and how Mesh SDK simplifies UTXO management.
The UTXO (Unspent Transaction Output) model is Cardano's fundamental architecture. Unlike Ethereum's accounts, Cardano represents value as discrete outputs that you consume and recreate in each transaction. Mesh abstracts this complexity.
The concept
Ethereum: account model
Account: 0x123...
Balance: 100 ETHSending 10 ETH updates the number in place. Simple, but limits parallelization.
Cardano: UTXO model
UTXO 1: 50 ADA (from tx abc123)
UTXO 2: 30 ADA (from tx def456)
UTXO 3: 20 ADA (from tx ghi789)
Total: 100 ADATo send 10 ADA:
- Select UTXOs as inputs (consumed entirely)
- Create output for recipient (10 ADA)
- Create change output (remaining minus fees)
Example: Using UTXO 2 (30 ADA) to send 10 ADA:
Inputs: UTXO 2 (30 ADA) - consumed entirely
Outputs:
- Recipient: 10 ADA (new UTXO)
- Change: ~19.8 ADA (new UTXO, minus fee)UTXO 2 is destroyed. Two new UTXOs are created.
Why UTXO
| Advantage | Description |
|---|---|
| Deterministic validation | Transaction validity depends only on inputs, not global state |
| Simple verification | Light clients verify without tracking all accounts |
| Natural parallelism | Transactions on different UTXOs execute in parallel |
| Privacy features | New addresses per transaction complicate analysis |
| Explicit resources | Outputs declare contents; no unexpected state changes |
Extended UTXO (eUTXO)
Cardano extends Bitcoin's UTXO for smart contracts:
| Feature | Purpose |
|---|---|
| Datums | Arbitrary data attached to UTXOs for script state |
| Validators | Scripts defining spending conditions |
| Redeemers | Arguments provided when spending script UTXOs |
| Reference inputs | Read UTXOs without consuming them |
| Reference scripts | On-chain scripts referenced by hash |
Example UTXO with datum:
{
address: "addr_script1...",
value: { lovelace: 5000000 },
datum: { owner: "addr1...", deadline: 1234567 }
}How Mesh handles UTXOs
Mesh lets you describe intent instead of managing UTXOs manually.
Basic transaction
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
const provider = new BlockfrostProvider("<your-api-key>");
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const unsignedTx = await txBuilder
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "10000000" }])
.changeAddress(await wallet.getChangeAddressBech32())
.selectUtxosFrom(await wallet.getUtxosMesh())
.complete();
// Mesh automatically handles:
// - Optimal UTXO selection
// - Fee calculation
// - Change output creation
// - Minimum UTXO requirementsWhat selectUtxosFrom does
Mesh selects UTXOs that:
- Satisfy required output value
- Minimize transaction size (fewer inputs = lower fees)
- Avoid unnecessary wallet fragmentation
- Handle tokens correctly (included in change if not sent)
Native token handling
Tokens are bundled with ADA in UTXOs. Mesh handles this automatically:
// Wallet has: UTXO 1 (10 ADA + 100 TokenA), UTXO 2 (5 ADA)
// You want to send 3 ADA
const tx = await txBuilder
.txOut(recipient, [{ unit: "lovelace", quantity: "3000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
// Mesh selects UTXO 2 to avoid TokenA complexity
// Or ensures TokenA goes to change if UTXO 1 is neededMinimum UTXO requirements
Every UTXO needs minimum ADA (~1-2 ADA depending on contents):
const tx = await txBuilder
.txOut(recipient, [
{ unit: policyId + assetName, quantity: "1" }
// Mesh adds minimum required ADA automatically
])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();View wallet UTXOs
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const utxos = await wallet.getUtxosMesh();
utxos.forEach(utxo => {
console.log(`UTXO: ${utxo.input.txHash}#${utxo.input.outputIndex}`);
console.log(` Value: ${utxo.output.amount}`);
});Smart contract UTXOs
Lock funds to script
const tx = await txBuilder
.txOut(scriptAddress, [{ unit: "lovelace", quantity: "10000000" }])
.txOutDatumHashValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Unlock funds from script
const tx = await txBuilder
.spendingPlutusScript(languageVersion)
.txIn(scriptUtxo.input.txHash, scriptUtxo.input.outputIndex)
.txInScript(scriptCbor)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txOut(recipient, outputValue)
.txInCollateral(collateralTxHash, collateralIndex)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Reference inputs
Read UTXO without consuming it:
const tx = await txBuilder
.readOnlyTxInReference(refTxHash, refIndex)
.txOut(recipient, outputValue)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Common pitfalls
UTXO fragmentation
Many small transactions create many small UTXOs. Consolidate periodically:
const utxos = await wallet.getUtxos();
const total = utxos.reduce((sum, u) =>
sum + BigInt(u.output.amount.find(a => a.unit === "lovelace")?.quantity || 0),
BigInt(0)
);
const tx = await txBuilder
.txOut(await wallet.getChangeAddress(), [
{ unit: "lovelace", quantity: total.toString() }
])
.selectUtxosFrom(utxos)
.complete();Concurrent transaction conflicts
Building multiple transactions from the same UTXOs causes conflicts:
// Wrong: both use same UTXOs
const utxos = await wallet.getUtxos();
const tx1 = await txBuilder.selectUtxosFrom(utxos).complete();
const tx2 = await txBuilder.selectUtxosFrom(utxos).complete(); // Conflict!
// Correct: wait for confirmation, then fetch fresh UTXOsToken bundles
Tokens are always bundled with ADA. You must send minimum ADA with any token:
const tx = await txBuilder
.txOut(recipient, [
{ unit: "lovelace", quantity: "1500000" }, // Minimum ADA required
{ unit: tokenUnit, quantity: "50" }
])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Related topics
| Topic | Link |
|---|---|
| Transaction failures | /resources/challenges/transaction-failures |
| Coin selection | /resources/challenges/coin-selection |
| Wallet integration | /resources/challenges/wallet-integration |
| Transaction builder | /resources/solutions/transaction-builder |