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-devkitinstalled
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;
}
}