Documentation
Under The Hood

Under The Hood: How Evolution Works

Fundamentals

Evolution library simplifies the process of building, signing, and submitting Cardano transactions. This document explains the key components and processes that happen "under the hood".

ComponentDescription
InputsExisting UTxOs from your wallet that the transaction will consume. Evolution library automatically selects these to cover your output amounts plus transaction fees. Don't forget that each UTxO can only be spent once.
OutputsDefine the new UTxOs that your transaction will create. Each output must contain a minimum amount of ADA, which is determined by the current protocol parameters.
FeesCalculated automatically based on your transaction's size and any execution units used (for smart contracts). Evolution library handles this calculation for you, and fees are -for now- always (looking at you babel fees (opens in a new tab)) paid in ADA (lovelace).
Transaction WitnessesCryptographic signatures that prove you own the input UTxOs. These are added when you sign the transaction, either with your wallet or a private key.

lovelace is the smallest unit of ADA (1 Ada = 1,000,000 lovelace)

Every UTxO on Cardano must hold a minimum amount of Ada (lovelace), which increases with UTxO size (in bytes). This is particularly relevant for native tokens, as their data adds to the UTxO size.

const minLovelace = calculateMinLovelaceFromUTxO(
  coinsPerUtxoByte, // from protocol parameters
  utxo
);

Minimum Ada requirement acts as "rent" for blockchain storage, ensuring UTxOs cover their storage costs and preventing spam from tiny UTxOs (dust), which could bloat the blockchain state and degrade performance.

Transaction Building

Coin Selection Algorithm

When .complete() is called, Evolution library internally queries available UTxOs in your wallet and selects "optimal" UTxOs to cover certain criteria for successful transaction building.

To be more specific, let's look at a basic case where you have these UTxOs in your wallet:

[
  { amount: 3_000_000n }, // 3 ADA
  { amount: 10_000_000n }, // 10 ADA
  { amount: 1_000_000n }, // 1 ADA
];
💡

What happens when you send 5 ADA?

  • 10 ADA UTxO will get selected (minimizing number of inputs)
  • 5 ADA goes to the recipient
  • ~0.18 ADA covers transaction fees
  • ~4.82 ADA returns to your wallet as change

Fee Calculation

Fees are automatically calculated based on a few things. It can be about transaction size in bytes, number of inputs and outputs or execution units (if using smart contracts, more on that later).

ComponentDescription
Base Fee CalculationUses protocol parameters minFeeA (per byte) and minFeeB (constant)
Script Execution CostsAdditional fees for Plutus script execution
Reference Script HandlingFees for including reference scripts
  • The base fee uses two protocol parameters, minFeeA (fee per byte of transaction size) and minFeeB (constant fee added to every transaction)
  • Additional fees are added for reference scripts based on their size
💡

When you need precise fee handling (e.g., sending maximum amount from a wallet), you can:

// Draft the transaction to calculate the fee
const draftTx = await lucid
  .newTx()
  .pay.ToAddress("addr_test...", { lovelace: amount })
  .complete();
 
const fee = (await draftTx.complete()).toTransaction().body().fee();
 
// Build the final transaction using calculated fee
const finalTx = await lucid
  .newTx()
  .pay.ToAddress("addr_test...", { lovelace: amount - fee })
  .complete();

Integrations

CML

CML (Cardano Multiplatform Library) provides low-level primitives for Cardano blockchain interactions, and Lucid Evolution heavily leverages CML for various core functionality.

  • Fee Estimation: Evolution uses CML for base fee calculation, while handling specific cases like script execution fees (via local UPLC or providers) and reference script fees internally.

  • Key Management: For operations like deriving addresses from private keys, Evolution provides high-level APIs (like makeWalletFromPrivateKey), but you can also use CML directly for more granular control:

const pubKeyHash = CML.PrivateKey.from_bech32(privateKey)
  .to_public()
  .hash()
  .to_hex();

While Evolution abstracts most CML complexity, understanding that CML powers the core operations can help when you need fine-grained control or are debugging transaction issues.

Noble Hashes

Lucid Evolution doesn't expose hash functions directly. Instead, it recommends using the noble-hashes (opens in a new tab) library for cryptographic hashing needs, which is audited and secure.

  • Compatible with both Node.js and browser environments
  • Provides implementations of common hash functions including blake2b which is compatible with Aiken's on-chain hash functions

When working with smart contracts, ensure you're using the same hash function and parameters as your on-chain code. For example, if using Aiken's blake2b_224, configure noble-hashes accordingly.