Cardano is a UTxO-based PoS L1 with a deterministic fee model and Plutus/Aiken smart contracts. Browser dapps connect to user wallets via the CIP-30 standard (Lace, Nami, Eternl, Yoroi, Flint); transaction building is done with Lucid Evolution (`@lucid-evolution/lucid`) for a high-level fluent API or MeshJS (`@meshsdk/core` + `@meshsdk/react`) for a React-first toolkit, both of which sit on top of CSL (cardano-serialization-lib).
- 01Cardano dapps and Plutus / Aiken smart contracts
- 02CIP-30 wallet connection (Lace, Nami, Eternl, Yoroi)
- 03native token (multi-asset) minting and policy scripts
- 04stake delegation and Catalyst voting flows
- 05deterministic fee preview before user signs
- pnpm add @lucid-evolution/lucid @meshsdk/core @meshsdk/react
| Variable | Scope | Description |
|---|---|---|
| NEXT_PUBLIC_CARDANO_NETWORK | Client | `Mainnet`, `Preprod`, or `Preview` — selects network magic and address bech32 prefix. |
| BLOCKFROST_PROJECT_ID | Server | Blockfrost project ID for chain queries and tx submission (https://blockfrost.io). Server-side only. |
| NEXT_PUBLIC_KOIOS_URL | Client | Optional Koios API URL (https://api.koios.rest) for a free, decentralized alternative to Blockfrost. |
Use Lucid Evolution + MeshJS. Initialize Lucid with `const lucid = await Lucid(new Blockfrost('https://cardano-mainnet.blockfrost.io/api/v0', process.env.BLOCKFROST_PROJECT_ID!), 'Mainnet')` then connect a CIP-30 wallet: `const api = await window.cardano.lace.enable(); lucid.selectWallet.fromAPI(api)`. Build transactions fluently: `const tx = await lucid.newTx().pay.ToAddress(addr, { lovelace: 5_000_000n }).complete(); const signed = await tx.sign.withWallet().complete(); const hash = await signed.submit();`. For React UX use MeshJS — wrap in `<MeshProvider>` and use `useWallet()` plus `<CardanoWallet />` for the connector. Mint native tokens by attaching a minting policy: `tx.mintAssets({ [policyId + assetName]: 1n }).attach.MintingPolicy(script)`. Read on-chain state via `lucid.utxosAt(addr)` or Blockfrost endpoints.
- ⚑Cardano uses an extended UTxO (eUTxO) model — there are no account balances. Wallets hold a set of UTxOs; every transaction consumes specific inputs and produces new outputs, and concurrent txs that touch the same UTxO will fail with `BadInputsUTxO`.
- ⚑Min-ADA per UTxO: every output must hold at least ~1 ADA (computed from size); sending tokens without enough ADA attached fails with `OutputTooSmallUTxO`. Lucid's `.pay.ToAddress` auto-pads but custom builders must compute it.
- ⚑CIP-30 wallets inject `window.cardano.<walletName>` — names are not standardized (`lace`, `nami`, `eternl`, `yoroi`, `flint`); always feature-detect the available list and let the user pick rather than hardcoding one.
- ⚑Fees are deterministic and computed from tx size + script execution units (memory + steps); you must call `tx.complete()` (which runs Plutus scripts in the evaluator) before knowing the fee, and any change to the tx invalidates it.
- ⚑Native assets are first-class (no contract): tokens live as `{ policyId, assetName }` pairs inside UTxOs. Burning requires minting a negative quantity with the matching policy — there is no `burn` function on a token contract.
- ⚑Plutus script addresses use a different bech32 prefix (`addr1z…` vs payment `addr1q…`) — sending to the wrong address type works but locks funds at a script that may have no spending path.
- ⚑Two-phase transaction validation: phase-1 (fees, balance) runs on submission; phase-2 (Plutus script) runs at slot inclusion. A tx can be accepted by the mempool and still fail at phase-2 — collateral inputs are then consumed.