This is Part 3 of our guide to building a Cardano transaction application with Next.js and Weld. In this section, you'll implement transaction building and submission functionality.
Now for the best part: With our wallet integration in place, we're now ready to implement the transaction functionality. This involves creating API endpoints for transaction building and submission, as well as building the user interface components for transaction input and status feedback.
Transaction Flow Overview
For a comprehensive explanation of Cardano transaction concepts, please refer to our . In this tutorial, we'll focus on implementing the following streamlined transaction flow:
Get Wallet Data: Retrieve the user's wallet address and UTXOs
Build Transaction: Call Anvil API to construct the transaction
Sign Transaction: Use the connected wallet to sign the transaction
Submit Transaction: Send the signed transaction to the Cardano network
Display Result: Show the transaction ID and success/error feedback
Implementation Steps
1. Create Anvil API Utility Functions
Instead of calling the Anvil API directly, we'll create utility functions that offer several advantages including type safety, error handling, and secure API key management through Next.js server components.
Our implementation will include three key functions:
callAnvilApi: A generic request handler with proper error management
buildTransaction: Creates transactions with sender addresses and payment details
submitTransaction: Sends signed transactions to the Cardano network
2. Create API Endpoints for Transaction Building and Submission
Next.js offers a built-in API routes system through its App Router architecture that allows us to create serverless functions. For this application, we'll create two API endpoints to handle transaction building and submission.
Transaction Building Endpoint
First, let's create the endpoint that builds a transaction with our Anvil API utility:
// src/app/api/transaction/build/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { buildTransaction, BuildTransactionParams } from '@/utils/anvil-api';
/**
* Build a Cardano transaction using Anvil API
*
* This endpoint builds a transaction with the provided inputs and outputs
* It requires:
* - changeAddress: The sender's address for change
* - utxos: Array of UTXOs from the wallet
* - outputs: Where to send the ADA/assets with amounts
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { changeAddress, utxos, outputs } = body as BuildTransactionParams;
if (!changeAddress || !utxos || !outputs) {
return NextResponse.json(
{ error: "Missing required parameters" },
{ status: 400 }
);
}
// Call utility function to build the transaction
const transactionData = await buildTransaction({ changeAddress, utxos, outputs });
// Return the transaction data for signing
return NextResponse.json({
hash: transactionData.hash,
complete: transactionData.complete,
stripped: transactionData.stripped,
witnessSet: transactionData.witnessSet
});
} catch (error) {
console.error("Error building transaction:", error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : "Failed to build transaction" },
{ status: 500 }
);
}
}
Transaction Submission Endpoint
Next, let's create the endpoint that submits a signed transaction to the Cardano network:
// src/app/api/transaction/submit/route.ts
import { NextRequest, NextResponse } from "next/server";
import { submitTransaction, SubmitTransactionParams } from "@/utils/anvil-api";
/**
* Submit a signed Cardano transaction to the blockchain using Anvil API
*
* This endpoint accepts:
* - transaction: The transaction CBOR (can be unsigned)
* - signatures: Optional array of signatures from the wallet
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { transaction, signatures } = body as SubmitTransactionParams;
if (!transaction) {
return NextResponse.json(
{ error: "Missing transaction" },
{ status: 400 },
);
}
// Call utility function to submit the transaction
const submissionData = await submitTransaction({ transaction, signatures });
// Return the transaction hash and success message
return NextResponse.json({
hash: submissionData.txHash,
message: "Transaction submitted successfully",
});
} catch (error) {
console.error("Error submitting transaction:", error);
return NextResponse.json(
{
error:
error instanceof Error
? error.message
: "Failed to submit transaction",
},
{ status: 500 },
);
}
}
3. Create a Custom Hook for Transaction Management
To cleanly manage our transaction logic, we'll create a reusable custom hook that:
Tracks the entire transaction lifecycle with multiple status states
Manages API calls to our transaction endpoints
Handles wallet integration for signing
Provides consistent error handling and user feedback
Exposes a simple interface for components to use
This pattern keeps our UI components focused on presentation rather than complex transaction logic.
Now, let's create the UI component for transactions. This component will:
Provide inputs for recipient address and ADA amount
Display transaction status and results
Handle form validation and submission
Show a link to a blockchain explorer after successful transactions
The form integrates with our custom hook from the previous step to manage transaction state and processing. It also demonstrates proper error handling and user feedback throughout the transaction flow.
Navigate to your application (usually at http://localhost:3000)
Connect your wallet following the instructions from Part 2
Test the transaction flow:
Enter a valid recipient address (use a testnet address for testing)
Enter a small amount of ADA (e.g., 1-2 ADA)
Click the "Send" button
Observe the transaction states: Building → Signing → Submitting → Success
Verify the transaction ID appears and links to a block explorer
Make sure you're using a testnet wallet with testnet ADA for testing. Never use real ADA or mainnet addresses during development and testing.
Troubleshooting
Common Transaction Errors
Insufficient Funds
Ensure your wallet has enough testnet ADA for the transaction and fees
Try sending a smaller amount
Invalid Address
Verify you're using a valid Cardano address format
Check that you're using a testnet address when testing on testnet
Signature Failure
Make sure your wallet is unlocked
Try reconnecting your wallet if signing fails
Check that your wallet is enabled for dApp interactions
Network Issues
Check your internet connection
Verify the Anvil API is available and responding (Use /health endpoint)
Deployment Considerations
When deploying your application to production:
Environment Variables: Ensure your production environment has the correct API URLs and keys
Network Selection: Update the NEXT_PUBLIC_NETWORK to mainnet for production use
Error Handling: Implement more robust error handling and user feedback
Security: Ensure your API keys are properly secured and not exposed to clients
Conclusion
Congratulations! You've successfully built a complete Cardano transaction application using Next.js and Weld. Your application can now:
Connect to Cardano wallets
Build and sign transactions
Submit transactions to the Cardano network
Display transaction results
You've completed all three parts of this guide! You now have a working Cardano transaction application that demonstrates the core functionality needed for Cardano dApp development.
Next Steps
To further enhance your application, consider exploring:
See the for more details on how these API routes work.