🏗️

Module M007

Advanced DApp Architecture

DirectEd x CATS Hackathon
Aiken Development Workshop Series

Duration: 3 hours

Format: 1.5 hours lecture + 1.5 hours exercises

SLIDE 2

Module Overview - The Capstone!

Welcome to the final module! Time to bring everything together and build production-ready DApps!

What You've Learned

  • ✅ M001: Writing validators
  • ✅ M002: Mock transactions
  • ✅ M003: Validation logic
  • ✅ M004: Input validation
  • ✅ M005: State machines
  • ✅ M006: Minting & NFTs

Now You'll Master

  • 📘 Blueprint (plutus.json)
  • 🔗 Off-chain integration
  • 🏗️ Multi-validator systems
  • 🛒 Marketplace patterns
  • 💰 Staking & rewards
  • 🗳️ DAO governance
This Module: Learn how your on-chain validators become real applications that users can interact with!
SLIDE 3

Two Major Focus Areas

Part 1: Blueprint & Off-Chain Integration

Understanding the plutus.json blueprint file
How validators communicate with transaction builders
Constructor indexing for off-chain code
Generating addresses and policy IDs

+

Part 2: Advanced DApp Architecture

Multi-validator system design
Marketplace contracts with royalties
Staking & rewards mechanisms
DAO governance with voting

The Bridge: On-Chain → Off-Chain → Real Applications
SLIDE 4

Understanding plutus.json Blueprint

When you run aiken build, you get a plutus.json file. This is the critical interface between on-chain and off-chain!

⚙️

Aiken Code

(validators/*.ak)

📘

plutus.json

(blueprint)

💻

Off-Chain Code

(JavaScript/TS)

What's in plutus.json?

  • Compiled Code: CBOR hex of your validator
  • Validator Hash: Used for addresses & policy IDs
  • Datum/Redeemer Schemas: How to structure data
  • Constructor Indexes: Map types to numbers
SLIDE 5

Constructor Indexing: The Key Concept

Most critical concept: Off-chain code uses numeric indexes, not constructor names!

On-Chain (Aiken)

pub type Redeemer { IncrementCount // index 0 DecrementCount // index 1 CloseState // index 2 } // You write: when redeemer is { IncrementCount -> ... DecrementCount -> ... }

Off-Chain (JavaScript)

// To call IncrementCount const redeemer = { data: { alternative: 0, // index! fields: [] } }; // To call DecrementCount const redeemer = { data: { alternative: 1, // index! fields: [] } };
⚠️ Off-chain doesn't know "IncrementCount" - only knows index 0!
SLIDE 6

Reading the Blueprint

{ "validators": [ { "title": "counter.spend", "compiledCode": "590a3f0100003232323...", "hash": "69b9758387079fd00c05c7daa8fde367acdb84dbd9c35dfd05b701ad", "redeemer": { "schema": { "$ref": "#/definitions/counter/Redeemer" } } } ], "definitions": { "counter/Redeemer": { "title": "Redeemer", "anyOf": [ { "title": "IncrementCount", "dataType": "constructor", "index": 0, // ← This is what matters! "fields": [] }, { "title": "DecrementCount", "dataType": "constructor", "index": 1, // ← Use this off-chain! "fields": [] } ] } } }
Usage: Read index from blueprint, use in transaction builder!
SLIDE 7

Generating Script Addresses

The validator hash becomes a script address where funds can be locked.

Off-Chain Code (Mesh SDK)

import { serializePlutusScript } from "@meshsdk/core"; import fs from 'fs'; // 1. Read blueprint const blueprint = JSON.parse(fs.readFileSync("./plutus.json")); // 2. Get compiled code const scriptCbor = blueprint.validators[0].compiledCode; // 3. Generate script address const scriptAddress = serializePlutusScript( { code: scriptCbor, version: "V3" }, undefined, 0 // 0 = testnet, 1 = mainnet ).address; console.log("Script Address:", scriptAddress); // → addr_test1wz4me4szhp7l0qhd9yrlr0dvhe5kx70m2q3y7aw4ql2w7gssu5qwr
Result: This address is where users lock funds that your validator controls!
SLIDE 8

Generating Policy IDs

For minting validators, the hash becomes the policy ID for tokens.

In Blueprint

{ "validators": [ { "title": "my_nft.mint", "hash": "d5e6bf0500..." } ] }

Off-Chain Usage

const blueprint = JSON.parse( fs.readFileSync("./plutus.json") ); const mintingValidator = blueprint.validators.find( v => v.title.includes("mint") ); const policyId = mintingValidator.hash; // This is your token's PolicyId!

Full Token Identity

d5e6bf0500...hash...d4cc.MyNFT001

PolicyId (from hash) + AssetName = Unique Token

SLIDE 9

Multi-Validator DApp Architecture

Real DApps use multiple validators working together!

NFT Marketplace Example

Listing Validator

Manages NFT listings with prices

Offer Validator

Handles offers on listed NFTs

Royalty Validator

Enforces creator royalties

↓ All connected through ↓

Collection Policy

Validates NFT authenticity

Pattern: Each validator has specific responsibility, they communicate through transactions!
SLIDE 10

Parameter Dependency Tree

Validators are connected through parameters. Understanding this flow is critical!

Root: System Deployer (has initial UTxO) │ ┌───────┴───────┐ │ │ Oracle NFT Policy Admin PKH (uses: utxo_ref) │ │ │ ▼ │ Oracle Validator │ (param: oracle_nft) ──────┤ │ │ ▼ ▼ Collection NFT Policy (params: oracle_nft, admin_pkh, collection_name)

Initialization Order

  1. Deploy with UTxO reference
  2. Mint Oracle NFT (get policy ID)
  3. Lock Oracle NFT at validator
  4. Deploy collection with params
  5. System ready!

Why This Matters

• Can't deploy out of order
• Each depends on previous
• Document clearly!
• Test deployment flow

SLIDE 11

How Validators Communicate

Validators don't call each other - they communicate through transactions!

Pattern 1: Reference Inputs

Validator B reads state from Validator A's UTxO (without spending it)

// Consumer reads Oracle state expect Some(oracle_input) = list.find( tx.reference_inputs, fn(input) { has_oracle_nft(input) } )

Pattern 2: Token-Based Authorization

Validator requires specific token to be present in transaction

// Protected action requires admin token expect Some(_) = list.find( tx.inputs, fn(input) { has_admin_token(input) } )

Pattern 3: Datum Chain

Validator A creates output with datum that Validator B processes

// Create message output let message = MessageDatum { recipient: validator_b_address, data: payload }
SLIDE 12

Marketplace Contract Pattern

A marketplace allows users to list and sell NFTs with fees.

Listing Datum

pub type ListingDatum { seller: ByteArray, price: Int, nft_policy: ByteArray, nft_name: ByteArray, royalty_address: Option<Address>, royalty_pct: Int, }

Actions

  • List: Seller lists NFT
  • UpdatePrice: Change price
  • Delist: Remove listing
  • Purchase: Buyer buys

Purchase Flow

  1. Calculate marketplace fee
  2. Calculate royalty (if any)
  3. Seller gets: price - fees
  4. Validate all payments
  5. Transfer NFT to buyer

Fee Calculation

let fee = price * fee_pct / 10000 let royalty = price * royalty_pct / 10000 let seller_payment = price - fee - royalty
SLIDE 13

Marketplace Purchase Validation

validator marketplace(fee_address: Address, fee_pct: Int) { spend(datum_opt, redeemer, own_ref, self) { expect Some(listing) = datum_opt when redeemer is { Purchase -> { // Calculate fees let marketplace_fee = listing.price * fee_pct / 10000 let royalty_fee = when listing.royalty_address is { Some(_) -> listing.price * listing.royalty_pct / 10000 None -> 0 } let seller_payment = listing.price - marketplace_fee - royalty_fee // Validate seller receives payment let seller_paid = list.any( self.outputs, fn(output) { and { is_seller_address(output.address, listing.seller), lovelace_of(output.value) >= seller_payment, } } ) // Validate marketplace fee paid let marketplace_fee_paid = list.any( self.outputs, fn(output) { and { output.address == fee_address, lovelace_of(output.value) >= marketplace_fee, } } ) // Validate royalty paid (if applicable) let royalty_paid = when listing.royalty_address is { Some(royalty_addr) -> list.any(self.outputs, fn(output) { output.address == royalty_addr && lovelace_of(output.value) >= royalty_fee }) None -> True } seller_paid && marketplace_fee_paid && royalty_paid } // ... other actions } } }
SLIDE 14

Staking & Rewards System

Users lock tokens to earn time-based rewards.

Stake Datum

pub type StakeDatum { staker: ByteArray, staked_amount: Int, stake_token: (PolicyId, AssetName), stake_start_time: Int, last_claim_time: Int, }

Reward Formula

// Time-based rewards let time_staked = current_time - last_claim_time let days = time_staked / 86400000 let rewards = staked_amount * reward_rate * days

Actions

  • Stake: Lock tokens
  • ClaimRewards: Get rewards, continue staking
  • Unstake: Unlock + rewards
  • IncreaseStake: Add more

Key Validations

  • ✓ Correct reward calculation
  • ✓ Minimum stake period
  • ✓ Update last_claim_time
  • ✓ Preserve staked tokens
SLIDE 15

DAO Governance System

Token holders vote on proposals that govern the protocol.

1. Create Proposal

Lock proposal with initial state (votes: 0, status: Active)

2. Vote

Token holders vote (voting power = tokens held)

3. Finalize

After deadline: Check quorum & approval threshold

4. Execute

If passed: Execute proposal action (treasury, parameters, etc.)

SLIDE 16

DAO Proposal Structure

Proposal Datum

pub type ProposalDatum { proposal_id: Int, proposer: ByteArray, title: ByteArray, description: ByteArray, execution_script: ByteArray, creation_time: Int, voting_deadline: Int, votes_for: Int, votes_against: Int, status: ProposalStatus, } pub type ProposalStatus { Active Passed Rejected Executed }

Vote Validation

// Get voter's token holdings let voting_power = get_token_balance( tx.inputs, voter_pkh, governance_token ) // Increment vote tally let new_votes_for = current.votes_for + voting_power // Prevent double voting let hasnt_voted = !already_voted( tx.reference_inputs, proposal_id, voter_pkh )

Finalization

  • Check quorum (min participation)
  • Check approval (% for vs against)
  • Update status: Passed/Rejected
SLIDE 17

Production Considerations

Building production DApps requires optimization and planning.

Transaction Costs

  • Minimize script size
  • Optimize datum sizes
  • Batch operations
  • Reuse helper functions

Upgradability

  • Version control
  • Migration actions
  • Admin controls
  • Emergency shutdown

Error Handling

  • Explicit error messages
  • trace debugging
  • Validation checks
  • Clear failure paths

Testing Pyramid

┌─────────────┐ │ Integration │ ← Full system tests │ Tests │ └─────────────┘ ┌───────────────────┐ │ Multi-Validator │ ← Cross-validator tests │ Tests │ └───────────────────┘ ┌─────────────────────────┐ │ Single Validator │ ← Individual validator tests │ Tests │ └─────────────────────────┘ ┌───────────────────────────────┐ │ Unit Tests │ ← Helper function tests │ (Helper Functions) │ └───────────────────────────────┘
SLIDE 18

Deployment Checklist

Pre-Deployment

  • ☑️ All validators compile
  • ☑️ Comprehensive test suite (100% coverage)
  • ☑️ Security audit (if applicable)
  • ☑️ Parameter values finalized
  • ☑️ Deployment scripts tested on testnet

Deployment Steps

  1. Generate initialization UTxO
  2. Mint admin/oracle tokens
  3. Lock initial state
  4. Deploy parameterized validators
  5. Test live system
  6. Document addresses

Post-Deployment

  • ☑️ Monitor validator activity
  • ☑️ Track transaction costs
  • ☑️ Collect user feedback
  • ☑️ Maintain documentation
SLIDE 19

Hands-On Exercises

Build production-ready systems! 🏗️

Exercise 1: Blueprint Reading (20 min)

Read plutus.json, generate addresses, identify constructor indexes

Exercise 2: Multi-Validator Design (30 min)

Design escrow marketplace with parameter dependency tree

Exercise 3: Simple Marketplace (30 min)

Implement NFT marketplace with List, Delist, Purchase

Exercise 4: Basic Staking (30 min)

Build time-based staking with reward calculation

Exercise 5: DAO Proposals (30 min)

Implement proposal creation, voting, and finalization

SLIDE 20

Assignment M007 - Capstone Project

Build a Complete Production DApp

Choose ONE System

  • A: NFT Marketplace with Offers
  • B: Staking + Governance DAO
  • C: Freelancing Platform

Requirements

  • 3-4 validators
  • Parameter dependency tree
  • Off-chain integration code
  • 20+ comprehensive tests
  • Complete documentation

Deliverables

  • GitHub repository
  • Blueprint documentation
  • Architecture diagram
  • Testnet deployment
  • Integration guide
  • Video demo (optional)

Grading

  • 30% - Functionality
  • 20% - Code Quality
  • 15% - Testing
  • 15% - Architecture
  • 10% - Off-Chain
  • 10% - Documentation
Deadline: Before Final Workshop Presentations
SLIDE 21

Common Issues & Solutions

❌ Cannot find validator hash in blueprint

List all validators first: blueprint.validators.forEach(v => console.log(v.title, v.hash))

❌ Constructor index mismatch

Cross-reference blueprint definitions - use index, not constructor name

❌ Applied parameters produce wrong address

Parameter order must match exactly - check parameter types in blueprint

❌ Multi-validator transaction fails

Ensure reference inputs are included, check redeemer indices match input order

✓ Use trace for debugging complex systems

trace @"State" trace current_state trace @"Validation result" trace result
SLIDE 22

Key Takeaways

You can now:

✅ Understand plutus.json blueprint structure

✅ Map on-chain types to off-chain representations

✅ Design multi-validator DApp architectures

✅ Build marketplace contracts with royalties

✅ Implement staking and rewards systems

✅ Create DAO governance with voting

✅ Deploy production-ready DApps on Cardano!

SLIDE 23

Your Complete Journey

🎓

From Zero to DApp Developer

M000: Setup & Cardano Fundamentals
M001: First Validators → M002: Mock Transactions
M003: Validation Logic → M004: Input Validation
M005: State Machines → M006: Minting & NFTs
M007: Advanced DApp Architecture
YOU DID IT! 🎉
SLIDE 24

What's Next for You?

Immediate Next Steps

  1. Complete M007 assignment
  2. Deploy to testnet
  3. Build your portfolio
  4. Share your work!

Continue Learning

  • Study production DApps
  • Participate in hackathons
  • Join Cardano community
  • Read advanced patterns

Build Real Projects

  • 💎 NFT marketplace
  • 💰 DeFi protocols
  • 🗳️ DAO platforms
  • 🎮 GameFi applications
  • 🏛️ Identity solutions

Contribute & Grow

  • Open source contributions
  • Help other developers
  • Write tutorials
  • Join Catalyst proposals
You're now equipped to build the future of decentralized applications! 🚀
SLIDE 25

Essential Resources

Documentation

  • Aiken Documentation
  • Aiken Standard Library
  • Mesh SDK Docs
  • Lucid Evolution
  • Cardano Developer Portal

CIPs (Standards)

  • CIP-25: NFT Metadata
  • CIP-27: Royalties
  • CIP-30: Wallet Bridge
  • CIP-57: Plutus Blueprint

Community

  • Workshop Discord
  • Cardano Stack Exchange
  • Aiken GitHub Discussions
  • Mesh Community

Production Examples

  • JPG Store (Marketplace)
  • SundaeSwap (DEX)
  • MinSwap (DEX & Staking)
  • Liqwid (Lending)
Remember: The Cardano community is here to help. Don't hesitate to ask questions!
🎉

Congratulations!

You've Completed the Workshop Series!

You can now build production-ready DApps on Cardano!

From smart contracts to complete systems 🏗️

Master of on-chain AND off-chain 💎

DirectEd x CATS Hackathon

GO BUILD THE FUTURE! 🚀

/ 26