Skip to main content

Your First Transaction

Let's build, sign, and submit your first Cardano transaction using Cardano DevKit.

Prerequisites

  • Local devnet running (npx cardano-devkit start)
  • Node.js project with cardano-devkit installed

Complete Example

import { createDevKit, WalletHelper, createDevnetManager } from 'cardano-devkit';

async function main() {
// 1. Initialize DevKit
const devKit = createDevKit({
network: 'LocalDevnet',
debug: true // Enable logging
});
const lucid = await devKit.init();

// 2. Create sender wallet
const senderSeed = WalletHelper.generateSeed();
lucid.selectWallet.fromSeed(senderSeed);
const senderAddress = await lucid.wallet().address();
console.log('Sender:', senderAddress);

// 3. Create receiver wallet
const receiverSeed = WalletHelper.generateSeed();
const receiverKey = WalletHelper.seedToPrivateKey(receiverSeed);
const receiverAddress = await lucid
.selectWallet.fromPrivateKey(receiverKey)
.wallet()
.address();
console.log('Receiver:', receiverAddress);

// Switch back to sender
lucid.selectWallet.fromSeed(senderSeed);

// 4. Fund sender via faucet
const devnet = createDevnetManager();
await devnet.topup(senderAddress, 100_000_000n); // 100 ADA
console.log('Sender funded with 100 ADA');

// Wait for transaction to be confirmed
await new Promise(r => setTimeout(r, 2000));

// 5. Build transaction
const tx = await lucid
.newTx()
.pay.ToAddress(receiverAddress, { lovelace: 25_000_000n }) // 25 ADA
.complete();

// 6. Sign transaction
const signedTx = await tx.sign.withWallet().complete();

// 7. Submit transaction
const txHash = await signedTx.submit();
console.log('Transaction submitted:', txHash);

// 8. Wait for confirmation
await lucid.awaitTx(txHash);
console.log('Transaction confirmed!');

// 9. Check balances
const senderUtxos = await lucid.utxosAt(senderAddress);
const receiverUtxos = await lucid.utxosAt(receiverAddress);

const senderBalance = senderUtxos.reduce(
(sum, utxo) => sum + utxo.assets.lovelace,
0n
);
const receiverBalance = receiverUtxos.reduce(
(sum, utxo) => sum + utxo.assets.lovelace,
0n
);

console.log('Sender balance:', Number(senderBalance) / 1_000_000, 'ADA');
console.log('Receiver balance:', Number(receiverBalance) / 1_000_000, 'ADA');
}

main().catch(console.error);

Understanding the Code

1. DevKit Initialization

const devKit = createDevKit({ network: 'LocalDevnet' });
const lucid = await devKit.init();

This creates a DevKit instance configured for local development and initializes a Lucid Evolution instance with the appropriate provider.

2. Wallet Management

const seed = WalletHelper.generateSeed();
lucid.selectWallet.fromSeed(seed);

WalletHelper provides utilities for:

  • Generating BIP-39 mnemonic seeds
  • Converting seeds to private keys
  • Validating seed phrases

3. Funding (Local Devnet Only)

const devnet = createDevnetManager();
await devnet.topup(address, 100_000_000n);

The devnet faucet provides unlimited test ADA. For testnets, use the Cardano Faucet.

4. Transaction Building

Lucid Evolution uses a fluent builder pattern:

const tx = await lucid
.newTx()
.pay.ToAddress(receiverAddress, { lovelace: 25_000_000n })
.complete();

The complete() method:

  • Selects UTXOs from your wallet
  • Calculates fees
  • Balances the transaction
  • Returns a transaction ready to sign

5. Signing & Submission

const signedTx = await tx.sign.withWallet().complete();
const txHash = await signedTx.submit();

The wallet automatically signs with all required keys.

Common Patterns

Sending Multiple Outputs

const tx = await lucid
.newTx()
.pay.ToAddress(address1, { lovelace: 10_000_000n })
.pay.ToAddress(address2, { lovelace: 20_000_000n })
.pay.ToAddress(address3, { lovelace: 30_000_000n })
.complete();

Adding Metadata

const tx = await lucid
.newTx()
.pay.ToAddress(receiverAddress, { lovelace: 5_000_000n })
.attachMetadata(674, { msg: "Hello from Cardano DevKit!" })
.complete();

Sending Native Tokens

const tx = await lucid
.newTx()
.pay.ToAddress(receiverAddress, {
lovelace: 2_000_000n,
[policyId + assetName]: 100n
})
.complete();

Error Handling

try {
const signedTx = await tx.sign.withWallet().complete();
const txHash = await signedTx.submit();
await lucid.awaitTx(txHash);
} catch (error) {
if (error.message.includes('Insufficient funds')) {
console.error('Not enough ADA to complete transaction');
} else if (error.message.includes('UTxO already spent')) {
console.error('UTxO conflict - refresh and retry');
} else {
throw error;
}
}

Next Steps