Skip to main content

Session 15: dApp Architecture: From Wallet to Backend - Notes

Notes and architectural breakdown for building full-stack applications on Cardano using Mesh SDK and Blockfrost.

Overview

Building on Cardano requires a different mental model than traditional "server-centric" applications. In a dApp, the blockchain is the immutable source of truth, while the frontend, wallet, and backend act as specialized interfaces to that truth.


🏗 Sequence Diagram: The Full Flow

The following diagram illustrates the interaction between the four layers during a typical payment-to-mint lifecycle.


🧩 Deep Dive: The 4 Layers

1. User Wallet (The Signing Authority)

The wallet's primary job is Key Management. The dApp never sees the user's private key.

  • Protocol: CIP-30 defines how the browser talks to the wallet.
  • UX: Whenever a dApp wants to move funds, the wallet pops up a "Confirm" dialog. This is the ultimate security barrier.

2. Frontend (The Orchestrator)

The frontend uses the Mesh SDK to build transactions.

  • Transaction Building: The SDK gathers UTxOs from the wallet, adds outputs, and calculates fees.
  • State Management: The frontend must handle the "Waiting" UX, keeping the user informed while the blockchain confirms the transaction.

3. Backend (The Verification Engine)

The backend's role is Trustless Verification. It must never trust the frontend's claim that "I paid".

  • Verification Logic: The backend queries Blockfrost for the txHash and inspects the UTxO Outputs.
  • The Checklist:
    1. Does the transaction exist on-chain?
    2. Does it have enough confirmations (e.g., 2 for demo, 15+ for production)?
    3. Is there an output to our App Wallet Address?
    4. Does that output contain the correct ADA amount?

4. Blockchain (The Source of Truth)

The Cardano network (Ouroboros) provides the settlement layer.

  • Mempool: Where transactions live before being included in a block.
  • Propagation: The time it takes for a transaction to spread across nodes (usually a few seconds).
  • Finality: As more blocks are added on top of a transaction, it becomes mathematically impossible to reverse.

🔍 Code Spotlight: Verification Logic

The heart of this architecture lies in how the backend confirms a payment without trusting the user's claimed transaction state.

1. Polling via Blockfrost (poll-status.ts)

The frontend triggers a polling loop every 10 seconds. The backend then performs a "live" check on the blockchain:

// pages/api/poll-status.ts snippet
const { confirmed, confirmations, amountLovelace } = await verifyPayment(
txHash,
APP_WALLET_ADDRESS
);

if (!confirmed) {
// If not on-chain or not enough confirmations, keep waiting
const newStatus = confirmations > 0 ? "CONFIRMING" : "PENDING";
// ... update local record and return status
}

2. Identifying the Payment (blockfrost.ts)

Verification isn't just about the transaction existing; it's about checking the UTxO outputs to ensure the funds reached their destination.

// lib/blockfrost.ts logic
// 1. Get all outputs for the transaction
const utxos = await getTxUTxOs(txHash);

// 2. Find the specific output directed to our App Wallet
const paymentOutput = utxos.outputs.find(
(output) => output.address === appWalletAddress
);

// 3. Compare the sent ADA (lovelace) against the required price
const sentLovelace = paymentOutput?.amount.find(a => a.unit === "lovelace")?.quantity;
if (BigInt(sentLovelace) < BigInt(REQUIRED_LOVELACE)) {
throw new Error("Insufficient payment amount");
}

🛡 Security Best Practices

[!CAUTION] Server Wallet Security: In this demo, the server wallet mnemonic is stored in an environment variable. In production, use a dedicated signing service, HSM (Hardware Security Module), or a vault like HashiCorp Vault.

  • API Key Safety: Never expose your BLOCKFROST_PROJECT_ID in the frontend code. Keep it strictly on the server-side (.env).
  • Input Validation: Sanitize and validate every txHash received from the frontend to prevent injection or spam.
  • Rate Limiting: Implement rate limiting on your API routes to prevent users from flooding the polling engine.

🛰 Indexing & Event Listeners (Production Scale)

While polling Blockfrost is excellent for smaller dApps and demos, production systems require higher throughput and lower latency. This is achieved by shifting from a Pull mechanism (polling) to a Push mechanism (event-driven).

1. The Case for Indexers

Standard nodes provide raw chain data, but they aren't optimized for complex queries (e.g., "Find all NFTs owned by this address"). Indexers consume raw chain data and transform it into a searchable database.

2. Event Listeners (Oura)

Oura is a popular tool for "tailing" the Cardano blockchain. It listens for events in real-time and routes them to different sinks (Elasticsearch, Kafka, Webhooks).

  • Use Case: Real-time notifications ("Your payment was just received!") before the transaction is even fully confirmed.
  • Benefit: Extremely low latency compared to polling.

3. Purpose-Built Indexers

  • Kupo: A lightweight indexer focused on the UTxO model. Ideal for dApps that need to quickly find available funds or specific script outputs.
  • Ogmios: A bridge that provides a JSON-RPC interface to the Cardano node, making it easier for web-based tools to query the node directly.
  • Cardano DB Sync: The "heavyweight" champion. It mirrors the entire chain into a PostgreSQL database. Essential for analytics platforms or explorers.
  • Yaci Store: A developer-focused indexing solution providing high-level APIs for blockchain data, designed for rapid application development.

4. Hybrid Architecture

In many production dApps, companies use a hybrid approach:

  • Blockfrost for quick features and mobile compatibility.
  • Oura for real-time UI updates (Optimistic UI).
  • Kupo/Ogmios for complex transaction building and backend verification.

These notes belong to the Q2 2026 Developer Experience Working Group.