# Branding
URL: /about/branding
Download official Mesh logos and assets for your projects, presentations, and integrations.
***
title: "Branding"
description: "Download official Mesh logos and assets for your projects, presentations, and integrations."
----------------------------------------------------------------------------------------------------------
import Link from "next/link";
import Image from "next/image";
Use the official Mesh brand assets when you reference Mesh in your application, documentation, or marketing materials. This page provides all logo variations and guidelines for proper usage.
## Logo Downloads
Download the Mesh logo in your preferred format and color scheme. Use SVG files for web applications where possible, as they scale to any size without quality loss.
### Standard Logos
Choose between black and white versions depending on your background color.
### Logos with Title
Use these versions when you need the Mesh name alongside the logo.
Download black logo with title:
SVG
Download white logo with title:
SVG
### Logo with Background
Use this version for social media profiles or when you need a standalone image with built-in contrast.
Download logo with background:
PNG
## Available Formats
| Format | Best For | Sizes Available |
| ------ | ----------------------- | ----------------- |
| SVG | Web, print, any scale | Vector (scalable) |
| PNG | Social media, documents | 16x16 to 512x512 |
## Usage Guidelines
When using Mesh branding:
* **Maintain aspect ratio** - Do not stretch or distort the logo
* **Ensure contrast** - Use the black logo on light backgrounds and white logo on dark backgrounds
* **Provide spacing** - Leave adequate clear space around the logo
* **No modifications** - Do not alter colors, add effects, or combine with other graphics
## Related Resources
* [Support Us](/about/support-us) - Ways to contribute to the Mesh project
* [Project Catalyst](/about/catalyst) - Our funded proposals and development roadmap
# About Mesh SDK for Cardano
URL: /about
Mesh is an open-source, production-ready TypeScript SDK for Cardano blockchain development. Transaction builders, wallet integrations, and data services for building dApps. Built and maintained by an active community of builders.
***
title: "About Mesh SDK for Cardano"
description: "Mesh is an open-source, production-ready TypeScript SDK for Cardano blockchain development. Transaction builders, wallet integrations, and data services for building dApps. Built and maintained by an active community of builders."
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import Image from 'next/image';
import Link from 'fumadocs-core/link';
import {
SiDiscord,
SiX,
SiGithub
} from "@icons-pack/react-simple-icons";
import { GlobeAltIcon } from '@heroicons/react/24/outline';
import {team} from "@/data/team";
import { cn } from "@/lib/cn";
import { buttonVariants } from "@/components/ui/button";
Mesh Builder Community
{
team.map((member) => (
{member.name}
{member.twitter && }
{member.linkedin && (
)}
))
}
All Contributors
What are we working on?
Check our GitHub repositories to see what we are currently working on.
Incorporation
MeshJS Pte. Ltd. is a company registered in Singapore since 2023, with the registration number (UEN): 202344120W.
Ecosystem
Mesh SDK is built for the Cardano blockchain, implementing key standards including CIP-30 (wallet connectivity), CIP-25 (NFT metadata), and CIP-95 (governance). Learn more about Cardano's eUTXO model.
Status
Stay up to date with our latest releases, tests and build status.
Published on NPM
Build status
Publish status
# Support Us
URL: /about/support-us
Help grow the Mesh ecosystem through community engagement, contributions, and advocacy.
***
title: "Support Us"
description: "Help grow the Mesh ecosystem through community engagement, contributions, and advocacy."
------------------------------------------------------------------------------------------------------
import Link from 'fumadocs-core/link';
import Image from 'next/image';
import { cn } from '@/lib/cn';
import { buttonVariants } from '@/components/ui/button';
Mesh is an open-source project maintained by developers who believe in making Cardano accessible to everyone. Your support helps us continue building tools that empower the next generation of blockchain applications.
## Ways to Support
### Follow on Twitter
Stay informed about new releases, features, and ecosystem updates. Engaging with our posts helps spread awareness to other developers.
Follow @meshsdk
### Star the GitHub Repository
GitHub stars help developers discover Mesh when searching for Cardano development tools. Each star increases our visibility in the ecosystem.
Star on GitHub
### Join the Discord Community
Connect with other Mesh developers, get help with your projects, and contribute to discussions about new features. Our Discord is the fastest way to get support.
Join Discord
### Add the Mesh Badge
Display the Mesh badge in your application to show users you build on reliable infrastructure. The badge links to Mesh and helps grow our developer community.
```tsx
import { MeshBadge } from '@meshsdk/react';
export default function Page() {
return (
<>
>
);
}
```
### Donate
Financial contributions help fund development, infrastructure, and community events. Every donation directly supports open-source Cardano tooling.
Donation options coming soon
## Other Ways to Contribute
| Contribution | Impact |
| ---------------- | -------------------------------------- |
| Report bugs | Help us identify and fix issues faster |
| Submit PRs | Add features or fix problems directly |
| Write tutorials | Help other developers learn Mesh |
| Answer questions | Support newcomers in Discord |
| Vote in Catalyst | Fund continued development |
## Related Resources
* [Project Catalyst](/about/catalyst) - Track our funded proposals
* [Branding Assets](/about/branding) - Download official logos
* [GitHub Issues](https://github.com/MeshJS/mesh/issues) - Report bugs or request features
# MeshAI Chatbot
URL: /ai/chatbot
Get instant answers about Mesh SDK using contextual AI retrieval
***
title: "MeshAI Chatbot"
description: "Get instant answers about Mesh SDK using contextual AI retrieval"
-------------------------------------------------------------------------------
import Video from "@/components/ui/Video";
MeshAI is an AI-powered chatbot that answers your Mesh SDK questions instantly. It uses contextual retrieval to search through documentation, code examples, and starter templates to provide accurate, up-to-date responses.
## Overview
MeshAI combines vector embeddings with large language models to deliver accurate answers without hallucination. When you ask a question, the system:
1. Converts your query into a vector embedding
2. Searches Mesh documentation for semantically similar content
3. Retrieves the most relevant context
4. Generates a response grounded in actual Mesh documentation
## Use Cases
* **Quick API lookups** - Find method signatures, parameters, and return types
* **Code generation** - Get working code snippets for common tasks
* **Concept explanations** - Understand Cardano and Mesh concepts in plain language
* **Troubleshooting** - Describe errors and receive solutions
## Quick Start
1. Navigate to the [Mesh documentation site](https://meshjs.dev)
2. Click the chat icon in the bottom-right corner
3. Type your question and press Enter
No API keys or setup required.
## Example Response
In this example, MeshAI creates a transaction that transfers ADA back to the sender's own address. The chatbot generates complete, working code with proper imports.
## Tips for Better Results
| Do | Don't |
| -------------------------------------------------------------------- | ------------------------------------------ |
| Ask specific questions: "How do I mint an NFT with a Plutus policy?" | Ask vague questions: "How do I use Mesh?" |
| Include context: "I'm building a DEX and need to..." | Assume the AI knows your project |
| Ask for code examples: "Show me how to..." | Ask only for explanations without examples |
## How Contextual Retrieval Works
Traditional RAG (Retrieval-Augmented Generation) retrieves chunks of documentation based on keyword matching. MeshAI uses **contextual retrieval**, which:
* Embeds chunks with surrounding context for better semantic understanding
* Ranks results by relevance to your specific question
* Filters out tangentially related content
* Synthesizes responses from multiple relevant sources
This approach reduces hallucination and ensures answers are grounded in actual Mesh documentation.
## Limitations
* **Scope** - MeshAI only answers questions about Mesh SDK. For general Cardano questions, consult the [Cardano Developer Portal](https://developers.cardano.org/).
* **Real-time data** - The chatbot does not access live blockchain data. For queries about specific transactions or addresses, use a blockchain explorer.
* **Code execution** - MeshAI generates code but does not execute it. Always test generated code in your development environment.
## Related Resources
* [MCP Server](/docs/ai/mcp) - Get AI assistance directly in your IDE
* [Agent Skills](/docs/ai/skills) - Deep API knowledge for AI coding assistants
* [llms.txt](/docs/ai/llms-txt) - AI-friendly documentation for any tool
# AI Integration
URL: /ai
Build Cardano applications faster with AI-powered tools for documentation, code generation, and debugging
***
title: "AI Integration"
description: "Build Cardano applications faster with AI-powered tools for documentation, code generation, and debugging"
icon: SparklesIcon
------------------
import { linksAi } from "@/data/links-ai";
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
Mesh provides AI-powered tools that accelerate Cardano development. Whether you prefer chatbots, IDE integrations, or CLI-based assistants, you can access Mesh documentation and code examples directly in your workflow.
## Available Tools
## Choose Your Integration
| Tool | Best For | Setup Time |
| ---------------------------------- | ----------------------------------------------------------------- | ---------- |
| [MeshAI Chatbot](/docs/ai/chatbot) | Quick answers without leaving the browser | None |
| [MCP Server](/docs/ai/mcp) | Real-time coding assistance in VS Code, Cursor, or Claude Desktop | 2 minutes |
| [Agent Skills](/docs/ai/skills) | Deep API knowledge for AI coding assistants | 1 minute |
| [llms.txt](/docs/ai/llms-txt) | Universal AI-friendly documentation for any tool | 1 minute |
## Why Use AI with Mesh?
Building on Cardano requires understanding complex concepts like UTxO management, Plutus scripts, and transaction building. AI tools help you:
* **Get instant answers** - Ask questions about Mesh API in natural language
* **Generate working code** - Describe what you want to build and get valid TypeScript
* **Debug faster** - Explain errors and get solutions without searching forums
* **Learn patterns** - Discover best practices through AI-guided examples
# llms.txt
URL: /ai/llms-txt
AI-friendly documentation file for code editors like Cursor, Windsurf, and other AI tools
***
title: "llms.txt"
description: "AI-friendly documentation file for code editors like Cursor, Windsurf, and other AI tools"
--------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
Mesh provides an `llms.txt` file that contains all documentation and code examples in a standardized, AI-friendly format. You can plug this file into any AI code editor to get instant access to up-to-date Mesh documentation.
## Overview
The `llms.txt` file follows an [emerging standard](https://llmstxt.org/) for providing documentation to large language models. It contains:
* Complete API reference for all Mesh packages
* Working code examples with imports
* Common patterns and best practices
* Troubleshooting guides
Because the file uses standardized markdown formatting, AI tools can parse and index it consistently across different platforms.
## Use Cases
* **Cursor** - Add as a doc source for context-aware code completion
* **Windsurf** - Reference in conversations for accurate Mesh code generation
* **ChatGPT/Claude** - Upload as context for Mesh-specific questions
* **Custom AI pipelines** - Parse programmatically for RAG systems
## Quick Start
### Cursor
1. Open Cursor settings
2. Navigate to **Features** > **Docs**
3. Click **Add new doc**
4. Paste: `https://meshjs.dev/llms.txt`
5. Click **Add**
Once indexed, reference Mesh docs in any chat by typing `@Docs` and selecting the Mesh documentation.
### Windsurf
1. In a Windsurf chat, type: `@https://meshjs.dev/llms.txt`
2. The AI now has full Mesh context for your conversation
### ChatGPT or Claude
1. Download the file: meshjs.dev/llms.txt
2. Upload to your conversation as a file attachment
3. Ask questions about Mesh SDK
### Programmatic Access
```typescript
import { fetch } from "undici";
async function getMeshDocs(): Promise {
const response = await fetch("https://meshjs.dev/llms.txt");
return response.text();
}
```
## File Location
Access the file directly at: meshjs.dev/llms.txt
## Benefits Over Traditional Docs
| Traditional Documentation | llms.txt |
| ----------------------------------- | -------------------------------- |
| Requires manual searching | AI searches contextually |
| May be outdated in AI training data | Always current |
| Split across multiple pages | Single, complete file |
| Formatted for humans only | Optimized for both humans and AI |
## How to Use in Your Projects
Once you add `llms.txt` to your AI tool, you can ask questions like:
* "How do I build a transaction that sends 5 ADA using Mesh?"
* "Show me how to connect a browser wallet with CIP-30"
* "Generate a minting transaction for an NFT collection"
* "What's the correct order of methods for MeshTxBuilder?"
The AI responds with accurate, up-to-date code based on the latest Mesh documentation.
## Troubleshooting
### File not indexing in Cursor
* Verify the URL is correct: `https://meshjs.dev/llms.txt`
* Check your internet connection
* Try removing and re-adding the doc source
* Restart Cursor after adding
### AI gives outdated information
* Re-index the documentation in your AI tool's settings
* The `llms.txt` file updates with each Mesh release
### File is too large for upload
* Some AI tools have file size limits
* Use the MCP server instead for real-time access: [MCP Setup](/docs/ai/mcp)
## Related Resources
* [MCP Server](/docs/ai/mcp) - Real-time AI integration for VS Code, Cursor, and Claude Desktop
* [Agent Skills](/docs/ai/skills) - Deep Mesh knowledge for AI coding assistants
* [MeshAI Chatbot](/docs/ai/chatbot) - Browser-based AI assistant
# MCP Server
URL: /ai/mcp
Access Mesh documentation and get coding assistance directly in your editor
***
title: "MCP Server"
description: "Access Mesh documentation and get coding assistance directly in your editor"
------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
The Mesh MCP (Model Context Protocol) server provides real-time access to Mesh documentation and AI-powered coding assistance directly in VS Code, Cursor, or Claude Desktop.
## Overview
MCP is an open protocol that connects AI assistants to external data sources. The Mesh MCP server gives your AI assistant:
* Full access to Mesh SDK documentation
* Context-aware code generation for Cardano transactions
* Debugging assistance with Mesh-specific error handling
* Up-to-date API reference without relying on training data
## Use Cases
* **Code completion** - Get accurate Mesh method suggestions as you type
* **Transaction building** - Generate complete transaction code from natural language
* **Error debugging** - Paste errors and receive Mesh-specific solutions
* **API exploration** - Ask about available methods and their parameters
## Quick Start
### Cursor (One-Click Install)
Click the button below to install Mesh MCP in Cursor:
After clicking, configure your `API_KEY` and `MODEL` in Cursor's MCP settings.
### Claude Desktop (CLI)
If you have the `claude` CLI installed:
```bash
claude mcp add-json mesh-mcp '{
"command": "npx",
"args": ["-y", "meshjs-mcp"],
"env": {
"API_KEY": "your-api-key",
"MODEL": "your-preferred-model"
}
}'
```
### VS Code / Manual Setup
Add this configuration to your MCP server settings file:
```json
{
"servers": {
"mesh-mcp": {
"name": "mesh-mcp-server",
"command": "npx",
"args": ["-y", "meshjs-mcp"],
"env": {
"API_KEY": "your-api-key",
"MODEL": "your-preferred-model"
}
}
}
}
```
## Configuration
### Required Environment Variables
| Variable | Description | Example |
| --------- | ---------------------------------------------- | -------------------------------------------------- |
| `API_KEY` | Your API key from OpenAI, Anthropic, or Google | `sk-...` or `your-key` |
| `MODEL` | The model to use for responses | `gpt-4o`, `claude-sonnet-4-20250514`, `gemini-pro` |
### Supported Providers
The MCP server works with any of these AI providers:
* **OpenAI** - Use models like `gpt-4o`, `gpt-4-turbo`
* **Anthropic** - Use models like `claude-sonnet-4-20250514`, `claude-3-haiku`
* **Google** - Use models like `gemini-pro`, `gemini-1.5-pro`
## Starting the Server
After saving your configuration, you must start the server manually:
### VS Code
1. Open Command Palette (`Cmd+Shift+P` on macOS, `Ctrl+Shift+P` on Windows/Linux)
2. Search for "MCP: List Servers"
3. Select "mesh-mcp"
4. Choose "Run Server"
### Cursor
1. Open Command Palette
2. Search for "MCP: List Servers"
3. Select "mesh-mcp" and run it
### Claude Desktop
The server starts automatically on launch if configured correctly. Check MCP logs if issues arise.
## Usage Examples
Once the server is running, ask your AI assistant Mesh-related questions:
**Build a transaction:**
```
Create a transaction that sends 10 ADA to addr1qy...
```
**Debug an error:**
```
I'm getting "missing required signer" - what does this mean?
```
**Explore the API:**
```
What methods does MeshTxBuilder have for Plutus scripts?
```
## Complete Example
In this example, the AI generates a complete transaction with proper imports, UTxO selection, and signing flow.
## Troubleshooting
### Server not starting
* Verify Node.js is installed: `node --version`
* Check that npx works: `npx --version`
* Ensure your API key is valid
### "Connection refused" error
* The server may not be running. Follow the [Starting the Server](#starting-the-server) steps.
* Check for port conflicts with other services
### Responses are slow or incomplete
* Try a faster model (e.g., `gpt-4o-mini` instead of `gpt-4o`)
* Check your API provider's rate limits
* Ensure stable internet connection
### API key errors
* Verify your key is correct and has available credits
* Check the key matches your chosen provider (OpenAI key for OpenAI models, etc.)
## Related Resources
* [Agent Skills](/docs/ai/skills) - Deep Mesh knowledge for AI coding assistants
* [llms.txt](/docs/ai/llms-txt) - AI-friendly documentation for any tool
* [MeshAI Chatbot](/docs/ai/chatbot) - Browser-based AI assistant
* [MCP Protocol Specification](https://modelcontextprotocol.io/) - Learn more about MCP
# Agent Skills for AI Coding Assistants
URL: /ai/skills
Give your AI coding assistant deep knowledge of Mesh SDK for faster Cardano development - works with Claude Code, Cursor, Codex, and 37+ tools
***
title: "Agent Skills for AI Coding Assistants"
description: "Give your AI coding assistant deep knowledge of Mesh SDK for faster Cardano development - works with Claude Code, Cursor, Codex, and 37+ tools"
-------------------------------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
Agent Skills give your AI coding assistant deep knowledge of Mesh SDK so it can write correct Cardano code without searching documentation. Instead of looking up method signatures, transaction patterns, or CIP standards yourself, you describe what you want and your assistant produces working code.
Compatible with Claude Code, Cursor, GitHub Copilot, Codex, and 37+ other tools.
| Without Skills | With Skills Installed |
| ------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Read docs to find the right API method | AI already knows the full API |
| Trial-and-error for transaction patterns | Ask "build a transaction that sends 5 ADA" and get working code |
| Debug cryptic Cardano errors alone | Troubleshooting guides built into AI context |
| Forget method ordering (e.g., `spendingPlutusScriptV3()` before `txIn()`) | AI knows the correct order |
| Look up CIP standards | AI understands CIP-30, CIP-8, etc. |
| Manually translate Aiken types to MeshTxBuilder code | Give it your Aiken types and get correct transaction code |
## Available Skills
| Skill | Package | What It Covers |
| ------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `mesh-transaction` | `@meshsdk/transaction` | MeshTxBuilder API, sending ADA, minting NFTs and tokens, Plutus script spending, staking, governance, DRep registration, Aiken integration |
| `mesh-wallet` | `@meshsdk/wallet` | Browser wallets (Eternl, Nami, Lace, Flint, Yoroi), headless server-side wallets, transaction signing, CIP-30, CIP-8 data signing |
| `mesh-core-cst` | `@meshsdk/core-cst` | CBOR serialization, Plutus data conversion, address parsing, CIP-8 message signing, native script hashing, `applyParamsToScript` |
Each skill includes a complete API reference, working code recipes, and a troubleshooting guide.
## Quick Start
Install all Mesh skills with a single command:
```bash
npx skills add MeshJS/skills
```
The CLI auto-detects which AI tools you have installed (Claude Code, Cursor, Codex, etc.) and places skills in the correct directory. To install a specific skill only:
```bash
npx skills add MeshJS/skills --skill mesh-transaction
```
## How It Works
Each skill contains a description that tells your AI assistant when to activate it. When you ask something like *"help me build a transaction with MeshTxBuilder"*, the assistant recognizes the topic, loads the relevant skill, and responds with accurate API usage, correct method ordering, and working code patterns.
You don't need to reference skills manually. The assistant loads them automatically based on context.
## Example Conversation
After installing skills, you can ask your assistant questions like:
* *"Build a transaction that sends 5 ADA to addr1qy..."*
* *"How do I mint an NFT with a Plutus minting policy?"*
* *"Help me connect a browser wallet using CIP-30"*
* *"Show me how to interact with a Plutus V3 script"*
* *"Why am I getting 'missing required signer' error?"*
* *"How do I register as a DRep and vote on a proposal?"*
* *"Here are my Aiken types, build the MeshTxBuilder transaction for me"*
Here is what a response looks like with skills loaded:
**You:** Send 5 ADA to an address
**AI Assistant:**
```typescript
import { MeshTxBuilder } from '@meshsdk/transaction';
const txBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const utxos = await provider.fetchAddressUTxOs(walletAddress);
const unsignedTx = await txBuilder
.txOut('addr_test1qp...', [
{ unit: 'lovelace', quantity: '5000000' }
])
.changeAddress(walletAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
```
## Aiken Smart Contract Support
The `mesh-transaction` skill includes a complete Aiken-to-MeshTxBuilder mapping guide. When you share your Aiken type definitions, your AI assistant knows how to translate them into the correct MeshJS data helpers — picking the right format (`conStr` for datums, `mConStr` for redeemers), mapping constructor indices to variant order, and choosing the correct type parameters.
For example, given this Aiken type:
```
type MarketAction {
Buy
Cancel
UpdatePrice { new_price: Int }
}
```
Your AI assistant knows that:
* `Buy` is variant index 0 → `mConStr0([])`
* `Cancel` is variant index 1 → `mConStr1([])`
* `UpdatePrice` is variant index 2 with a field → `mConStr2([75_000_000])`
Combined with the skill's transaction-building knowledge, it produces the full MeshTxBuilder chain with the correct method ordering, collateral handling, and datum/redeemer placement — so you can focus on your contract logic instead of figuring out the MeshJS data format conventions.
## Skill File Structure
Each skill follows the Agent Skills specification:
```
mesh-transaction/
SKILL.md # Entry point - description, overview, quick reference
TRANSACTION.md # Complete MeshTxBuilder API reference
AIKEN-MAPPING.md # Aiken smart contract integration guide
PATTERNS.md # Common recipes with working code
TROUBLESHOOTING.md # Error solutions and debugging
mesh-wallet/
SKILL.md # Entry point - description, overview, quick reference
WALLET.md # Complete wallet API reference
PATTERNS.md # Common recipes with working code
TROUBLESHOOTING.md # Error solutions and debugging
mesh-core-cst/
SKILL.md # Entry point - description, overview, quick reference
CORE-CST.md # Complete core-cst API reference
PATTERNS.md # Common recipes with working code
TROUBLESHOOTING.md # Error solutions and debugging
```
## Source Code
Browse MeshJS/skills on GitHub
# Write a Smart Contract
URL: /aiken/first-script
Create your first Aiken validator with datum and redeemer validation
***
title: "Write a Smart Contract"
description: "Create your first Aiken validator with datum and redeemer validation"
-----------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
## Overview
In this guide, you create an Aiken validator that locks assets on-chain. The validator requires a specific message ("Hello, World!") and the owner's signature to unlock the funds.
**What you'll build:**
* A validator with custom datum (owner's key hash)
* Redeemer validation (message check)
* Transaction signature verification
**Prerequisites:**
* Aiken CLI installed
* Basic understanding of Cardano transactions
## Quick Start
Create and compile a validator in 3 steps.
```bash
aiken new meshjs/hello_world
cd hello_world
```
Create `validators/hello_world.ak`:
```rust
use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/transaction.{ScriptContext}
pub type Datum {
owner: VerificationKeyHash,
}
pub type Redeemer {
msg: ByteArray,
}
validator hello_world {
spend(
datum: Option,
redeemer: Redeemer,
_own_ref: Data,
tx: transaction.Transaction,
) {
expect Some(d) = datum
let must_say_hello = redeemer.msg == "Hello, World!"
let must_be_signed = list.has(tx.extra_signatories, d.owner)
must_say_hello && must_be_signed
}
}
```
```bash
aiken build
```
## Step-by-Step Guide
### Step 1: Create a New Project
Initialize an Aiken project with the standard structure.
```bash
aiken new meshjs/hello_world
cd hello_world
```
Verify the project was created:
```bash
aiken check
```
### Step 2: Define Your Types
Create a new file `validators/hello_world.ak`. Start by defining the datum and redeemer types.
```rust
use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/transaction.{ScriptContext}
/// The datum stores the owner's verification key hash.
/// Only the owner can unlock funds from this script.
pub type Datum {
owner: VerificationKeyHash,
}
/// The redeemer contains the message that must match "Hello, World!"
pub type Redeemer {
msg: ByteArray,
}
```
**Key points:**
* `VerificationKeyHash` is a 28-byte hash of a public key
* Custom types make your contract self-documenting
* Comments with `///` appear in generated documentation
### Step 3: Write the Validator Logic
Add the validator function that checks the unlock conditions.
```rust
validator hello_world {
spend(
datum: Option,
redeemer: Redeemer,
_own_ref: Data,
tx: transaction.Transaction,
) {
// Unwrap the datum - fail if None
expect Some(d) = datum
// Check 1: Redeemer message must be "Hello, World!"
let must_say_hello = redeemer.msg == "Hello, World!"
// Check 2: Transaction must be signed by the owner
let must_be_signed = list.has(tx.extra_signatories, d.owner)
// Both conditions must be true
must_say_hello && must_be_signed
}
}
```
**What this validator does:**
1. Extracts the datum containing the owner's key hash
2. Verifies the redeemer message equals "Hello, World!"
3. Confirms the transaction is signed by the owner
4. Returns `True` only if both conditions pass
### Step 4: Compile the Contract
Build the project to generate the Plutus blueprint.
```bash
aiken build
```
This creates `plutus.json` in the project root. The blueprint contains:
```json
{
"validators": [
{
"title": "hello_world.hello_world.spend",
"compiledCode": "5901a2...",
"hash": "abc123..."
}
]
}
```
### Step 5: Verify with Type Checking
Run the type checker to ensure your code is valid.
```bash
aiken check
```
If there are errors, fix them before proceeding. Common issues:
* Missing imports
* Type mismatches in datum/redeemer
* Incorrect function signatures
## Complete Validator Code
Here's the full `validators/hello_world.ak` file:
```rust
use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/transaction.{ScriptContext}
/// The datum stores the owner's verification key hash.
/// Only the owner can unlock funds from this script.
pub type Datum {
owner: VerificationKeyHash,
}
/// The redeemer contains the unlock message.
pub type Redeemer {
msg: ByteArray,
}
/// A simple validator that requires:
/// 1. The redeemer message to be "Hello, World!"
/// 2. The transaction to be signed by the datum owner
validator hello_world {
spend(
datum: Option,
redeemer: Redeemer,
_own_ref: Data,
tx: transaction.Transaction,
) {
expect Some(d) = datum
let must_say_hello = redeemer.msg == "Hello, World!"
let must_be_signed = list.has(tx.extra_signatories, d.owner)
must_say_hello && must_be_signed
}
}
```
## Using the Compiled Script with Mesh
After building, integrate the compiled script with your TypeScript application.
```typescript
import {
applyParamsToScript,
resolvePlutusScriptAddress,
PlutusScript,
} from "@meshsdk/core";
import blueprint from "./hello_world/plutus.json";
// Get the compiled code from the blueprint
const compiledCode = blueprint.validators[0].compiledCode;
// Apply parameters (none in this case, but required for CBOR encoding)
const scriptCbor = applyParamsToScript(compiledCode, []);
// Create the script object
const script: PlutusScript = {
code: scriptCbor,
version: "V3",
};
// Resolve the script address (0 = mainnet, 1 = preprod/preview)
const scriptAddress = resolvePlutusScriptAddress(script, 0);
console.log("Script Address:", scriptAddress);
```
## Troubleshooting
### aiken build fails
Check for syntax errors with `aiken check`. Common issues:
```rust
// Wrong: Missing comma
pub type Datum {
owner: VerificationKeyHash
name: ByteArray // <- Missing comma
}
// Correct
pub type Datum {
owner: VerificationKeyHash,
name: ByteArray,
}
```
### Validator returns False unexpectedly
Debug by testing each condition separately:
```rust
validator hello_world {
spend(datum: Option, redeemer: Redeemer, _own_ref: Data, tx: transaction.Transaction) {
expect Some(d) = datum
// Debug: Log which condition fails
trace @"Checking message..."
let must_say_hello = redeemer.msg == "Hello, World!"
trace @"Checking signature..."
let must_be_signed = list.has(tx.extra_signatories, d.owner)
must_say_hello && must_be_signed
}
}
```
### Import errors
Ensure you import from the correct modules:
```rust
// Correct imports for Aiken v1.1+
use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/transaction.{ScriptContext}
```
## Related Links
* Build Transactions - Lock and unlock assets with this validator
* Getting Started - Installation and CLI reference
* Aiken Language Tour - Complete syntax reference
* Official Hello World Example - Aiken's tutorial
# Getting Started
URL: /aiken/getting-started
Install Aiken and set up your development environment
***
title: "Getting Started"
description: "Install Aiken and set up your development environment"
--------------------------------------------------------------------
import Link from "fumadocs-core/link";
## Overview
This guide walks you through installing the Aiken CLI and configuring your development environment. After completing these steps, you can write, compile, and test Aiken smart contracts.
**Prerequisites:**
* macOS, Linux, or Windows with WSL
* A code editor (VS Code recommended)
* Node.js 18+ for Mesh integration
## Quick Start
Install Aiken and verify your setup in under 5 minutes.
```bash
# Install Aiken (macOS/Linux)
curl -sSfL https://install.aiken-lang.org | bash
aikup
# Verify installation
aiken --version
```
## Step-by-Step Installation
### Option 1: Using aikup (macOS and Linux)
The `aikup` utility downloads and manages Aiken versions. This is the recommended installation method.
**Step 1: Install aikup**
```bash
curl -sSfL https://install.aiken-lang.org | bash
```
**Step 2: Install the latest Aiken version**
```bash
aikup
```
**Step 3: Verify installation**
```bash
aiken --version
```
You should see output like `aiken v1.x.x`.
### Option 2: Build from Source (All Platforms)
Build Aiken from source using Cargo. This method works on all platforms including Windows.
**Step 1: Install Rust**
If you don't have Rust installed, get it from rustup.rs.
```bash
# Verify Rust installation
rustc --version
cargo --version
```
**Step 2: Install Aiken via Cargo**
```bash
cargo install aiken
```
**Step 3: Verify installation**
```bash
aiken --version
```
## Editor Setup
### VS Code Extension
Install the Aiken extension for syntax highlighting, code snippets, and error checking.
**Installation options:**
1. Search for "aiken" in the VS Code Extensions tab
2. Or install directly from the VS Code Marketplace
**Features:**
* Syntax highlighting for `.ak` files
* Inline error checking
* Code snippets for common patterns
* Go-to-definition support
## CLI Reference
Use these commands to develop and test your smart contracts.
### aiken new
Create a new Aiken project with the standard directory structure.
```bash
aiken new your-org/project-name
cd project-name
```
### aiken build
Compile your validators and generate the `plutus.json` blueprint file.
```bash
aiken build
```
The blueprint contains:
* Compiled validator code (CBOR-encoded)
* Type information for datum and redeemer
* Hash digests for each validator
### aiken check
Type-check your project and run tests.
```bash
aiken check
```
Use this command frequently during development to catch errors early.
### aiken docs
Generate HTML documentation from your project's comments.
```bash
aiken docs
```
### aiken blueprint
Utility functions for working with the compiled blueprint.
```bash
# Generate an address for a validator
aiken blueprint address
# Convert blueprint to different formats
aiken blueprint convert
```
## Complete Example
Create a new project and verify the setup.
```bash
# Create project
aiken new meshjs/hello_world
cd hello_world
# Check the project structure
ls -la
# Run the type checker
aiken check
# Build the project
aiken build
# Verify the blueprint was generated
cat plutus.json
```
## Project Structure
After running `aiken new`, your project looks like this:
```
hello_world/
aiken.toml # Project configuration
lib/ # Shared library code
validators/ # Validator scripts
plutus.json # Generated blueprint (after build)
```
**Key files:**
* `aiken.toml` - Project metadata and dependencies
* `validators/*.ak` - Your validator scripts
* `plutus.json` - Compiled output for use with Mesh
## Troubleshooting
### Command not found: aiken
The Aiken binary is not in your PATH. Add it manually:
```bash
# For aikup installations
export PATH="$HOME/.aiken/bin:$PATH"
# Add to your shell profile for persistence
echo 'export PATH="$HOME/.aiken/bin:$PATH"' >> ~/.bashrc
```
### Build fails with type errors
Run `aiken check` to see detailed error messages. Common issues:
1. **Missing imports** - Add required modules at the top of your file
2. **Type mismatches** - Verify datum/redeemer types match your validator signature
3. **Syntax errors** - Check for missing commas or brackets
### VS Code extension not working
1. Reload VS Code after installing the extension
2. Verify the file has a `.ak` extension
3. Check the extension is enabled for the workspace
### plutus.json not generating
Ensure your project has at least one validator in the `validators/` directory. Empty projects don't generate a blueprint.
## Related Links
* Write Your First Smart Contract - Create a validator
* Build Transactions - Interact with contracts using Mesh
* Aiken Language Tour - Learn Aiken syntax
* Official Installation Guide - More installation options
# Aiken
URL: /aiken
Build and deploy Cardano smart contracts with Aiken and Mesh
***
title: "Aiken"
description: "Build and deploy Cardano smart contracts with Aiken and Mesh"
icon: "icons/aiken.png"
-----------------------
import { linksAiken } from "@/data/links-aiken";
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
## Overview
Aiken is a functional programming language designed specifically for Cardano smart contract development. It compiles to Plutus Core and prioritizes security, efficiency, and developer experience.
Mesh provides seamless integration with Aiken, allowing you to:
* Compile Aiken contracts and use them in your TypeScript applications
* Build transactions that interact with Aiken validators
* Apply parameters to scripts and resolve addresses
**When to use Aiken with Mesh:**
* You need to write custom on-chain logic (validators, minting policies)
* You want a modern, type-safe language for smart contracts
* You prefer functional programming patterns
## Quick Start
Write and deploy an Aiken smart contract in 4 steps.
**1. Install Aiken CLI**
```bash
curl -sSfL https://install.aiken-lang.org | bash
aikup
```
**2. Create a new project**
```bash
aiken new meshjs/hello_world
cd hello_world
```
**3. Write your validator**
```rust
// validators/hello_world.ak
validator hello_world {
spend(datum: Data, redeemer: Data, context: Data) {
True
}
}
```
**4. Build and integrate with Mesh**
```bash
aiken build
```
```typescript
import { MeshTxBuilder, applyParamsToScript, resolvePlutusScriptAddress } from "@meshsdk/core";
import blueprint from "./plutus.json";
const scriptCbor = applyParamsToScript(blueprint.validators[0].compiledCode, []);
const scriptAddress = resolvePlutusScriptAddress({ code: scriptCbor, version: "V3" }, 0);
```
## What's Included
## Related
* [Providers](/providers) - Connect to blockchain data sources
* [React Integration](/react) - UI components and hooks for React apps
* [Smart Contracts](/smart-contracts) - Pre-built contract interactions
* [Guides](/guides) - Step-by-step tutorials
# Build Your First Aiken Smart Contract
URL: /guides/aiken
Write a Cardano smart contract with Aiken and interact with it using Mesh SDK. Learn to lock and unlock assets on-chain.
***
title: "Build Your First Aiken Smart Contract"
description: "Write a Cardano smart contract with Aiken and interact with it using Mesh SDK. Learn to lock and unlock assets on-chain."
---------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
In this guide, you build a complete "Hello World" smart contract on Cardano. You write the on-chain validator in [Aiken](https://aiken-lang.org/), a modern smart contract language for Cardano, then use Mesh SDK to create transactions that lock and unlock assets on the [eUTXO model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/).
### What you will build
* An Aiken smart contract with datum and redeemer validation
* A transaction that locks ADA at the script address
* A transaction that unlocks ADA with the correct redeemer
### What you will learn
* Aiken syntax for Cardano validators
* How datums and redeemers work in Plutus contracts
* Building Plutus transactions with Mesh SDK
### Prerequisites
* [Rust](https://www.rust-lang.org/tools/install) installed (for Aiken)
* Node.js 18+ installed
* Basic TypeScript knowledge
* A Cardano wallet browser extension
### Resources
* Live demo
* GitHub repository
### Time to complete
60 minutes
## Quick Start
Clone the complete template:
```bash
git clone https://github.com/MeshJS/aiken-next-ts-template
cd aiken-next-ts-template
npm install
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) to interact with the deployed contract.
## Step-by-Step Guide
### Step 1: Install Aiken
**On Linux or macOS:**
```bash
curl -sSfL https://install.aiken-lang.org | bash
aikup
```
**On any platform (via Cargo):**
```bash
cargo install aiken
```
Verify the installation:
```bash
aiken -V
```
**What to expect:** Aiken version information displays in the terminal.
### Step 2: Create an Aiken project
Create a new Aiken project:
```bash
aiken new meshjs/hello_world
cd hello_world
```
Verify the project structure:
```bash
aiken check
```
**What to expect:** A new directory with `aiken.toml` and a `validators` folder.
### Step 3: Write the validator
Create `validators/hello_world.ak`:
```rust
use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{ScriptContext}
use aiken/transaction/credential.{VerificationKey}
/// The datum stores the owner's public key hash
type Datum {
owner: Hash,
}
/// The redeemer must contain the magic message
type Redeemer {
msg: ByteArray,
}
/// The validator allows spending only when:
/// 1. The redeemer message is "Hello, World!"
/// 2. The transaction is signed by the owner
validator {
fn hello_world(datum: Datum, redeemer: Redeemer, context: ScriptContext) -> Bool {
let must_say_hello = redeemer.msg == "Hello, World!"
let must_be_signed =
list.has(context.transaction.extra_signatories, datum.owner)
must_say_hello && must_be_signed
}
}
```
**What to expect:** A validator file that enforces two conditions for spending.
### Step 4: Compile the contract
Build the Aiken project:
```bash
aiken build
```
**What to expect:** A `plutus.json` file is generated. This is the [CIP-0057 Plutus blueprint](https://cips.cardano.org/cip/CIP-57) containing your compiled validator.
### Step 5: Set up the frontend
Create a new Next.js project with Mesh SDK (see the Next.js guide):
```bash
npx meshjs my-aiken-app
cd my-aiken-app
npm install cbor
```
Copy `plutus.json` from your Aiken project to `src/data/plutus.json`.
**What to expect:** A Next.js project ready for smart contract integration.
### Step 6: Load the contract
Create `src/lib/contract.ts`:
```typescript
import {
resolvePlutusScriptAddress,
resolvePaymentKeyHash,
resolveDataHash,
} from "@meshsdk/core";
import type { PlutusScript, Data } from "@meshsdk/core";
import cbor from "cbor";
import plutusBlueprint from "@/data/plutus.json";
// Encode the compiled code to CBOR format for Mesh
const scriptCbor = cbor
.encode(Buffer.from(plutusBlueprint.validators[0].compiledCode, "hex"))
.toString("hex");
// Create the PlutusScript object
export const script: PlutusScript = {
code: scriptCbor,
version: "V2",
};
// Get the script address (0 = testnet, 1 = mainnet)
export const scriptAddress = resolvePlutusScriptAddress(script, 0);
// Helper to create the datum
export function createDatum(ownerPubKeyHash: string): Data {
return {
alternative: 0,
fields: [ownerPubKeyHash],
};
}
// Helper to create the redeemer
export function createRedeemer(): Data {
return {
alternative: 0,
fields: ["Hello, World!"],
};
}
```
**What to expect:** Utility functions for working with the contract.
### Step 7: Lock assets at the script
Create a component that locks ADA at the script address:
```typescript
import { MeshTxBuilder, KoiosProvider, resolvePaymentKeyHash } from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";
import { script, scriptAddress, createDatum } from "@/lib/contract";
export function LockFunds() {
const { wallet } = useWallet();
async function lockAda() {
const provider = new KoiosProvider("preprod");
// Get wallet info
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const usedAddresses = await wallet.getUsedAddresses();
const ownerPubKeyHash = resolvePaymentKeyHash(usedAddresses[0]);
// Create the datum with owner's public key hash
const datum = createDatum(ownerPubKeyHash);
// Build the transaction
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.txOut(scriptAddress, [{ unit: "lovelace", quantity: "5000000" }])
.txOutDatumHashValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
// Sign and submit
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log("Locked 5 ADA at script. TX:", txHash);
return txHash;
}
return (
);
}
```
**What to expect:** A button that sends 5 ADA to the script address with a datum containing your public key hash.
### Step 8: Unlock assets from the script
Create a component that unlocks ADA from the script:
```typescript
import {
MeshTxBuilder,
KoiosProvider,
resolvePaymentKeyHash,
resolveDataHash,
} from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";
import { script, scriptAddress, createDatum, createRedeemer } from "@/lib/contract";
export function UnlockFunds() {
const { wallet } = useWallet();
async function unlockAda(lockTxHash: string) {
const provider = new KoiosProvider("preprod");
// Get wallet info
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const collateral = await wallet.getCollateral();
const usedAddresses = await wallet.getUsedAddresses();
const ownerPubKeyHash = resolvePaymentKeyHash(usedAddresses[0]);
// Recreate the original datum
const datum = createDatum(ownerPubKeyHash);
const dataHash = resolveDataHash(datum);
// Find the UTxO at the script address
const scriptUtxos = await provider.fetchAddressUTxOs(scriptAddress, "lovelace");
const lockedUtxo = scriptUtxos.find(
(utxo) => utxo.output.dataHash === dataHash
);
if (!lockedUtxo) {
throw new Error("No locked UTxO found for this wallet");
}
// Create the redeemer with the magic message
const redeemer = createRedeemer();
// Build the unlock transaction
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(lockedUtxo.input.txHash, lockedUtxo.input.outputIndex)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txInScript(script.code)
.txOut(changeAddress, lockedUtxo.output.amount)
.requiredSignerHash(ownerPubKeyHash)
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
// Sign with partial signing (required for Plutus scripts)
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Unlocked ADA from script. TX:", txHash);
return txHash;
}
return (
);
}
```
**What to expect:** A button that withdraws the locked ADA using the correct redeemer message.
## Complete Example
Here is a complete page component:
```typescript
import { useState } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
import {
MeshTxBuilder,
KoiosProvider,
resolvePaymentKeyHash,
resolveDataHash,
} from "@meshsdk/core";
import { script, scriptAddress, createDatum, createRedeemer } from "@/lib/contract";
export default function HelloWorldContract() {
const { wallet, connected } = useWallet();
const [lockTxHash, setLockTxHash] = useState("");
const [loading, setLoading] = useState(false);
const provider = new KoiosProvider("preprod");
async function lockAda() {
setLoading(true);
try {
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const usedAddresses = await wallet.getUsedAddresses();
const ownerPubKeyHash = resolvePaymentKeyHash(usedAddresses[0]);
const datum = createDatum(ownerPubKeyHash);
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.txOut(scriptAddress, [{ unit: "lovelace", quantity: "5000000" }])
.txOutDatumHashValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
setLockTxHash(txHash);
} finally {
setLoading(false);
}
}
async function unlockAda() {
setLoading(true);
try {
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const collateral = await wallet.getCollateral();
const usedAddresses = await wallet.getUsedAddresses();
const ownerPubKeyHash = resolvePaymentKeyHash(usedAddresses[0]);
const datum = createDatum(ownerPubKeyHash);
const dataHash = resolveDataHash(datum);
const scriptUtxos = await provider.fetchAddressUTxOs(scriptAddress, "lovelace");
const lockedUtxo = scriptUtxos.find((u) => u.output.dataHash === dataHash);
if (!lockedUtxo) throw new Error("No locked UTxO found");
const redeemer = createRedeemer();
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(lockedUtxo.input.txHash, lockedUtxo.input.outputIndex)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txInScript(script.code)
.txOut(changeAddress, lockedUtxo.output.amount)
.requiredSignerHash(ownerPubKeyHash)
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
await wallet.submitTx(signedTx);
} finally {
setLoading(false);
}
}
return (
## Next Steps
After completing a guide:
* Explore the [Transaction Builder API](/apis/txbuilder) for advanced transaction patterns
* Check the [Wallet APIs](/apis/wallets) for complete wallet functionality
* Join the [Mesh Discord](https://discord.gg/dH48jH3BKa) for community support
## Related Links
* [API Reference](/apis) - Complete SDK documentation
* [GitHub Repository](https://github.com/MeshJS/mesh) - Source code and examples
* [Cardano Developer Portal](https://developers.cardano.org/) - Cardano ecosystem resources
# Mesh vs Cardano SDK Alternatives
URL: /guides/mesh-vs-alternatives
Compare Mesh SDK with cardano-serialization-lib, Lucid, Pallas, and other Cardano development tools to choose the right one for your project.
***
title: "Mesh vs Cardano SDK Alternatives"
description: "Compare Mesh SDK with cardano-serialization-lib, Lucid, Pallas, and other Cardano development tools to choose the right one for your project."
------------------------------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Choosing the right Cardano SDK impacts your development speed, maintainability, and application performance. This guide compares Mesh with alternative tools to help you make an informed decision.
### What you will learn
* How each Cardano development tool differs
* When to choose Mesh over alternatives
* How to combine multiple tools effectively
### Time to complete
10 minutes reading
## Quick Comparison
| Tool | Language | Level | Best For |
| ----------------------------- | ---------- | ----- | --------------------------- |
| **Mesh** | TypeScript | High | Full-stack dApp development |
| **cardano-serialization-lib** | Rust/WASM | Low | Transaction serialization |
| **Lucid** | TypeScript | High | Transaction building |
| **Pallas** | Rust | Low | Backend infrastructure |
| **cardano-cli** | Shell | Low | Node operations |
| **Aiken** | Aiken | N/A | Smart contract authoring |
## Mesh vs cardano-serialization-lib
**cardano-serialization-lib (CSL)** is Emurgo's Rust library compiled to WebAssembly. It provides low-level primitives for constructing and serializing Cardano transactions.
### When to choose CSL
* Maximum performance where WASM speed matters
* Custom transaction formats or experimental features
* Building your own higher-level abstraction
* Wallet implementations needing fine-grained control
### When to choose Mesh
* Building web applications or dApp frontends
* Need pre-built React or Svelte components
* Want provider abstraction and easy switching
* Developing with Aiken smart contracts
* Value comprehensive documentation
### Feature comparison
| Feature | Mesh | CSL |
| -------------------- | ------------------ | ------------------- |
| Transaction building | High-level builder | Manual construction |
| Wallet connection | Built-in CIP-30 | Must implement |
| Provider integration | Multiple included | DIY |
| UI components | React/Svelte | None |
| Learning curve | Gentle | Steep |
| Bundle size | Includes deps | Minimal WASM |
**Note:** Mesh uses CSL internally for serialization. You get CSL's reliability with Mesh's usability.
## Mesh vs Lucid
**Lucid** is another TypeScript SDK for Cardano with similar goals to Mesh.
### Architectural differences
**Lucid** combines provider and wallet in a single configured instance:
```typescript
// Lucid pattern
const lucid = await Lucid.new(provider, "Preprod");
await lucid.selectWallet(walletApi);
```
**Mesh** separates these concerns:
```typescript
// Mesh pattern
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
const provider = new BlockfrostProvider("key");
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const txBuilder = new MeshTxBuilder({ fetcher: provider });
```
This separation enables:
* Swapping providers without changing wallet code
* Using different wallets with the same transaction logic
* Testing transactions without blockchain connectivity
### Feature comparison
| Feature | Mesh | Lucid |
| ----------------- | ------------------------ | ----------- |
| React components | Official package | Community |
| Svelte support | Official package | Community |
| Provider options | 5+ providers | 3 providers |
| Testing utilities | TxTester, OfflineFetcher | Emulator |
| Aiken integration | Blueprint parsing | Manual |
| Maintenance | Active | Varies |
### Migration path
If you have a Lucid application, see the migration guide.
## Mesh vs Pallas
**Pallas** is a Rust library for low-level Cardano network interaction - chain sync, block parsing, and transaction submission.
### When to choose Pallas
* Blockchain indexers and explorers
* Custom node implementations
* High-performance data processing
* Rust-based backend services
### When to choose Mesh
* Web application development
* Wallet interactions
* Transaction building in JavaScript
* Rapid prototyping
These tools complement rather than compete. Use Pallas for backend infrastructure and Mesh for client-facing applications.
## Mesh vs cardano-cli
**cardano-cli** is the official command-line tool for Cardano node operations.
### When to use cardano-cli
* Stake pool operations
* Direct node interaction and queries
* Scripting operational workflows
* Protocol parameter inspection
### When to use Mesh
* Building web and mobile applications
* Programmatic transaction construction
* Integrating wallets into applications
* Any scenario requiring JavaScript/TypeScript
For backend scripts, use Mesh in Node.js rather than parsing CLI output.
## Mesh and Aiken
**Aiken** is a smart contract language for Cardano, not a transaction library. You need both for dApp development.
### The relationship
Aiken compiles smart contracts. You still need a tool to:
* Build transactions that interact with contracts
* Serialize datums and redeemers
* Query the blockchain for UTxOs
* Connect to wallets for signing
Mesh provides Aiken integration:
* Parse Aiken blueprints for type-safe interaction
* Apply parameters to parameterized validators
* Construct properly formatted datums and redeemers
* Test transactions with contract validation
**Use Aiken for writing contracts, Mesh for building transactions that use them.**
## Decision Guide
### Choose Mesh when you are
* Building a web application or dApp frontend
* Want TypeScript with strong type safety
* Need pre-built React or Svelte components
* Developing with Aiken smart contracts
* Prefer higher-level abstractions
* Value comprehensive documentation
### Choose alternatives when you need
* **CSL** - Maximum performance or building low-level tooling
* **Lucid** - Already invested in Lucid ecosystem
* **Pallas** - Building Rust backend infrastructure
* **cardano-cli** - Operating stake pools or direct node interaction
### Combining tools
Many projects combine tools:
* Mesh for frontend + Pallas for backend indexing
* Mesh for most transactions + CSL for specific optimizations
* Mesh for development + cardano-cli for deployment scripts
## Getting Started with Mesh
If Mesh fits your needs:
1. Next.js Integration Guide - Build your first Cardano dApp
2. Transaction Builder API - Learn transaction construction
3. Aiken Integration Guide - Work with smart contracts
The Mesh SDK balances developer productivity with capability. Most Cardano dApps can be built entirely with Mesh, reserving lower-level tools for specific optimization needs.
## References
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources and SDK overview
* [Cardano Developer Portal: SDKs and Tools](https://developers.cardano.org/tools/) - Comprehensive listing of Cardano development tools
* [MeshJS GitHub Repository](https://github.com/MeshJS/mesh) - Mesh SDK source code, issues, and contributions
## Related Links
* Getting Started with Next.js
* Migrating from Lucid
* Transaction Builder API
* Aiken Smart Contracts
# Migrate from Lucid to Mesh
URL: /guides/migrate-from-lucid
Step-by-step migration guide from Lucid to Mesh SDK with API mapping, code examples, and common patterns.
***
title: "Migrate from Lucid to Mesh"
description: "Step-by-step migration guide from Lucid to Mesh SDK with API mapping, code examples, and common patterns."
------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
In this guide, you migrate an existing Cardano application from Lucid to Mesh SDK. Mesh separates transaction building, wallet interaction, and blockchain queries into distinct components, giving you more flexibility in how you structure your application.
### What you will learn
* How Lucid concepts map to Mesh equivalents
* Key differences in API design and architecture
* Step-by-step migration patterns for common operations
* How to update tests and smart contract code
### Prerequisites
* An existing application using Lucid
* Node.js 18+ installed
* Basic TypeScript knowledge
### Time to complete
2 hours (varies based on application complexity)
## Quick Start
Install Mesh packages alongside your existing Lucid installation:
```bash
npm install @meshsdk/core @meshsdk/react
```
You can migrate incrementally, using both libraries during the transition.
## Step-by-Step Guide
### Step 1: Understand the architectural differences
**Lucid** combines provider and wallet in a single configured instance:
```typescript
// Lucid pattern
const lucid = await Lucid.new(
new Blockfrost("https://cardano-preprod.blockfrost.io/api", "key"),
"Preprod"
);
await lucid.selectWallet(walletApi);
const tx = await lucid.newTx().payToAddress(...).complete();
```
**Mesh** separates these concerns:
```typescript
// Mesh pattern
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
const provider = new BlockfrostProvider("key");
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const txBuilder = new MeshTxBuilder({ fetcher: provider });
```
**Why this matters:** You can swap providers without changing wallet code, and test transactions without blockchain connectivity.
### Step 2: Install Mesh packages
Add Mesh to your project:
```bash
npm install @meshsdk/core @meshsdk/react
```
For React applications, `@meshsdk/react` provides wallet components and hooks.
**What to expect:** Both Lucid and Mesh can coexist during migration.
### Step 3: Replace provider setup
**Lucid:**
```typescript
import { Blockfrost, Lucid } from "lucid-cardano";
const lucid = await Lucid.new(
new Blockfrost("https://cardano-preprod.blockfrost.io/api", "project_key"),
"Preprod"
);
```
**Mesh:**
```typescript
import { BlockfrostProvider } from "@meshsdk/core";
// Preprod
const provider = new BlockfrostProvider("project_key_preprod");
// Mainnet
const providerMainnet = new BlockfrostProvider("project_key_mainnet");
// Preview
const providerPreview = new BlockfrostProvider("project_key_preview");
```
Mesh auto-detects the network from your API key prefix. Alternative providers:
```typescript
import { KoiosProvider, MaestroProvider, OgmiosProvider } from "@meshsdk/core";
const koios = new KoiosProvider("preprod");
const maestro = new MaestroProvider({ apiKey: "key", network: "Preprod" });
const ogmios = new OgmiosProvider("ws://localhost:1337");
```
**What to expect:** Provider instances ready to pass to transaction builders.
### Step 4: Update wallet connection
**Lucid:**
```typescript
const api = await window.cardano.eternl.enable();
lucid.selectWallet(api);
const address = await lucid.wallet.address();
const utxos = await lucid.wallet.getUtxos();
```
**Mesh:**
```typescript
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const address = await wallet.getChangeAddressBech32();
const usedAddresses = await wallet.getUsedAddressesBech32();
const utxos = await wallet.getUtxosMesh();
```
**React hooks (Mesh):**
```typescript
import { CardanoWallet, useWallet } from "@meshsdk/react";
function WalletConnect() {
const { wallet, connected, connect } = useWallet();
if (!connected) {
return ;
}
return
Connected
;
}
```
**What to expect:** Wallet instances with CIP-30 standard methods.
### Step 5: Migrate transaction building
**Lucid payment transaction:**
```typescript
const tx = await lucid
.newTx()
.payToAddress("addr_test1...", { lovelace: 5000000n })
.complete();
const signedTx = await tx.sign().complete();
const txHash = await signedTx.submit();
```
**Mesh payment transaction:**
```typescript
import { MeshTxBuilder } from "@meshsdk/core";
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const utxos = await wallet.getUtxosMesh();
const changeAddress = await wallet.getChangeAddressBech32();
const unsignedTx = await txBuilder
.txOut("addr_test1...", [{ unit: "lovelace", quantity: "5000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
```
**Key differences:**
* Mesh uses explicit input/output methods (`txIn`, `txOut`)
* Amounts are strings, not BigInt
* Signing returns a new string, does not mutate
* Change address must be set explicitly
**What to expect:** Transaction hashes on successful submission.
### Step 6: Migrate native token minting
**Lucid:**
```typescript
const { paymentCredential } = lucid.utils.getAddressDetails(address);
const mintingPolicy = lucid.utils.nativeScriptFromJson({
type: "sig",
keyHash: paymentCredential.hash,
});
const policyId = lucid.utils.mintingPolicyToId(mintingPolicy);
const tx = await lucid
.newTx()
.mintAssets({ [policyId + tokenName]: 1n })
.validTo(Date.now() + 100000)
.attachMintingPolicy(mintingPolicy)
.complete();
```
**Mesh:**
```typescript
import {
MeshTxBuilder,
ForgeScript,
resolveScriptHash,
stringToHex,
} from "@meshsdk/core";
const address = await wallet.getChangeAddressBech32();
const forgingScript = ForgeScript.withOneSignature(address);
const policyId = resolveScriptHash(forgingScript);
const tokenNameHex = stringToHex("MyToken");
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const utxos = await wallet.getUtxosMesh();
const unsignedTx = await txBuilder
.mint("1", policyId, tokenNameHex)
.mintingScript(forgingScript)
.txOut(address, [{ unit: policyId + tokenNameHex, quantity: "1" }])
.changeAddress(address)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
```
**What to expect:** NFTs minted with Mesh's ForgeScript utilities.
### Step 7: Migrate smart contract interactions
**Lucid locking funds:**
```typescript
const tx = await lucid
.newTx()
.payToContract(scriptAddress, { inline: datum }, { lovelace: 5000000n })
.complete();
```
**Mesh locking funds:**
```typescript
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.txOut(scriptAddress, [{ unit: "lovelace", quantity: "5000000" }])
.txOutInlineDatumValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
```
**Lucid unlocking funds:**
```typescript
const tx = await lucid
.newTx()
.collectFrom([utxo], redeemer)
.attachSpendingValidator(validator)
.complete();
```
**Mesh unlocking funds:**
```typescript
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(utxo.input.txHash, utxo.input.outputIndex)
.txInInlineDatumPresent()
.txInRedeemerValue(redeemer)
.txInScript(scriptCbor)
.txOut(recipientAddress, utxo.output.amount)
.requiredSignerHash(pubKeyHash)
.txInCollateral(
collateral.input.txHash,
collateral.input.outputIndex,
collateral.output.amount,
collateral.output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true); // true for partial signing
```
**What to expect:** Smart contract transactions with explicit script and datum handling.
### Step 8: Migrate data serialization
**Lucid:**
```typescript
import { Data } from "lucid-cardano";
const datum = Data.to({ owner: pubKeyHash });
const parsed = Data.from(onchainDatum);
```
**Mesh:**
```typescript
import { serializePlutusData, deserializePlutusData } from "@meshsdk/core";
import type { Data } from "@meshsdk/core";
// For simple datums, use the Data type directly
const datum: Data = {
alternative: 0,
fields: [pubKeyHash],
};
// For complex serialization
const cbor = serializePlutusData(datum);
const parsed = deserializePlutusData(onchainDatum);
```
**What to expect:** CBOR-encoded datum and redeemer values.
### Step 9: Migrate tests
**Lucid Emulator:**
```typescript
import { Emulator, Lucid } from "lucid-cardano";
const emulator = new Emulator([{ address, assets: { lovelace: 100_000_000n } }]);
const lucid = await Lucid.new(emulator);
```
**Mesh testing:**
```typescript
import { OfflineFetcher, MeshTxBuilder } from "@meshsdk/core";
// Mock blockchain state
const offlineFetcher = new OfflineFetcher();
offlineFetcher.addUTxOs([
{
input: { txHash: "abc...", outputIndex: 0 },
output: { address, amount: [{ unit: "lovelace", quantity: "100000000" }] },
},
]);
const txBuilder = new MeshTxBuilder({ fetcher: offlineFetcher });
```
For local development, use Yaci:
```typescript
import { YaciProvider } from "@meshsdk/core";
const yaci = new YaciProvider("http://localhost:8080");
```
**What to expect:** Test environments without live blockchain connectivity.
## API Mapping Reference
| Lucid | Mesh | Notes |
| ------------------------------ | -------------------------------------------------- | ----------------------------- |
| `Lucid.new()` | `new BlockfrostProvider()` + `new MeshTxBuilder()` | Separate provider and builder |
| `lucid.selectWallet()` | `MeshCardanoBrowserWallet.enable()` | Returns wallet instance |
| `lucid.newTx()` | `new MeshTxBuilder()` | Builder pattern |
| `tx.payToAddress()` | `txBuilder.txOut()` | Output construction |
| `tx.payToContract()` | `txBuilder.txOut()` + `txOutInlineDatumValue()` | With datum |
| `tx.mintAssets()` | `txBuilder.mint()` + `mintingScript()` | Minting |
| `tx.collectFrom()` | `txBuilder.txIn()` + script methods | Script spending |
| `tx.attachSpendingValidator()` | `txBuilder.txInScript()` | Attach validator |
| `tx.complete()` | `txBuilder.complete()` | Build transaction |
| `tx.sign().complete()` | `wallet.signTx()` | Returns signed CBOR |
| `signedTx.submit()` | `wallet.submitTx()` | Submit to network |
| `Data.to()` | `serializePlutusData()` | Serialize datum |
| `Data.from()` | `deserializePlutusData()` | Parse datum |
| `Emulator` | `OfflineFetcher` | Testing |
## Complete Example
Here is a complete migration example for a payment transaction:
**Before (Lucid):**
```typescript
import { Blockfrost, Lucid } from "lucid-cardano";
async function sendPayment() {
const lucid = await Lucid.new(
new Blockfrost("https://cardano-preprod.blockfrost.io/api", "key"),
"Preprod"
);
const api = await window.cardano.eternl.enable();
lucid.selectWallet(api);
const tx = await lucid
.newTx()
.payToAddress("addr_test1...", { lovelace: 5000000n })
.complete();
const signedTx = await tx.sign().complete();
const txHash = await signedTx.submit();
console.log("TX:", txHash);
}
```
**After (Mesh):**
```typescript
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
async function sendPayment() {
const provider = new BlockfrostProvider("key_preprod");
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const utxos = await wallet.getUtxosMesh();
const changeAddress = await wallet.getChangeAddressBech32();
const txBuilder = new MeshTxBuilder({ fetcher: provider, submitter: provider });
const unsignedTx = await txBuilder
.txOut("addr_test1...", [{ unit: "lovelace", quantity: "5000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("TX:", txHash);
}
```
## Troubleshooting
### Type errors with amounts
**Cause:** Lucid uses BigInt for amounts, Mesh uses strings.
**Solution:** Convert amounts when migrating:
```typescript
// Lucid
{ lovelace: 5000000n }
// Mesh
[{ unit: "lovelace", quantity: "5000000" }]
```
### Transaction signing fails
**Cause:** Missing the partial signing flag for Plutus scripts.
**Solution:** Pass `true` as the second argument to `signTx()`:
```typescript
const signedTx = await wallet.signTx(unsignedTx, true);
```
### Missing change address error
**Cause:** Mesh requires explicit change address.
**Solution:** Always set the change address:
```typescript
const changeAddress = await wallet.getChangeAddress();
txBuilder.changeAddress(changeAddress);
```
### Script validation fails
**Cause:** Datum or redeemer structure does not match contract expectations.
**Solution:** Verify your Data structure matches the Aiken/Plutus types:
```typescript
const datum: Data = {
alternative: 0, // Constructor index
fields: [value1, value2], // Field values
};
```
### UTxO not found errors
**Cause:** UTxOs fetched at different times may be spent.
**Solution:** Fetch UTxOs immediately before building the transaction:
```typescript
const utxos = await wallet.getUtxosMesh();
// Build transaction immediately after
const unsignedTx = await txBuilder
.selectUtxosFrom(utxos)
.complete();
```
## Related Links
* Transaction Builder API
* Smart Contract Transactions
* BrowserWallet API
* Providers Documentation
* Mesh Discord for migration support
# Mint NFTs on Cardano with Node.js
URL: /guides/minting-on-nodejs
Build a server-side NFT minting application using Mesh SDK. Mint tokens and distribute them to multiple recipients.
***
title: "Mint NFTs on Cardano with Node.js"
description: "Build a server-side NFT minting application using Mesh SDK. Mint tokens and distribute them to multiple recipients."
----------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Server-side minting lets you create Cardano NFTs without a browser. This is ideal for automated token distribution, airdrops, and backend services. This guide uses the [CIP-25](https://cips.cardano.org/cip/CIP-0025) metadata standard for NFT token metadata on Cardano.
### What you will build
* A Node.js application that mints multiple NFTs
* Automated distribution to multiple recipient addresses
* CIP-25 compliant metadata handling
### Prerequisites
* Node.js 18+ installed
* A Blockfrost API key ([get one free](https://blockfrost.io/))
* A funded Cardano wallet (mnemonic or CLI keys)
### Time to complete
30 minutes
## Quick Start
Clone the example repository:
```bash
git clone https://github.com/MeshJS/examples
cd examples/nodejs-minting
npm install
```
Add your credentials to the config and run `npm start`.
## Step-by-Step Guide
### Step 1: Set up the project
Create a new Node.js project:
```bash
mkdir nodejs-minting
cd nodejs-minting
npm init -y
npm install typescript @meshsdk/core --save
npm install --save-dev typescript
npx tsc --init
```
**What to expect:** A TypeScript project with Mesh SDK installed.
### Step 2: Configure TypeScript
Update `tsconfig.json`:
```json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
}
}
```
Update `package.json`:
```json
{
"type": "module",
"scripts": {
"start": "tsc && node ./dist/main.js"
}
}
```
**What to expect:** TypeScript configured for ES modules.
### Step 3: Define NFT metadata
Create `src/metadata.ts`:
```typescript
export interface NFTMetadata {
name: string;
image: string;
mediaType: string;
description: string;
artist?: string;
attributes?: Record;
}
export const metadata: Record = {
MeshToken01: {
name: "Mesh Token 1",
image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua",
mediaType: "image/jpg",
description: "A purple coin from the Mesh collection.",
artist: "Mesh (https://meshjs.dev/)",
},
MeshToken02: {
name: "Mesh Token 2",
image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua",
mediaType: "image/jpg",
description: "A gold coin from the Mesh collection.",
artist: "Mesh (https://meshjs.dev/)",
},
MeshToken03: {
name: "Mesh Token 3",
image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua",
mediaType: "image/jpg",
description: "A coin with M logo from the Mesh collection.",
artist: "Mesh (https://meshjs.dev/)",
},
};
```
**What to expect:** CIP-25 compliant metadata for three tokens.
### Step 4: Specify recipients
Create `src/recipients.ts`:
```typescript
// Map of recipient address to token name
export const recipients: Record = {
"addr_test1vpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0c7e4cxr": "MeshToken01",
"addr_test1qqlcxawu4gxarenqvdqyw0tqyjy69mrgsmfqhm6h65jwm4vvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmyscxry4r": "MeshToken02",
"addr_test1qq5tay78z9l77vkxvrvtrv70nvjdk0fyvxmqzs57jg0vq6wk3w9pfppagj5rc4wsmlfyvc8xs7ytkumazu9xq49z94pqzl95zt": "MeshToken03",
};
```
**What to expect:** Each recipient receives one specific token.
### Step 5: Create the minting script
Create `src/main.ts`:
```typescript
import {
MeshTxBuilder,
ForgeScript,
BlockfrostProvider,
resolveScriptHash,
stringToHex,
} from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
import type { AssetMetadata } from "@meshsdk/core";
import { metadata } from "./metadata.js";
import { recipients } from "./recipients.js";
// Configuration - replace with your values
const BLOCKFROST_KEY = "your_blockfrost_preprod_key";
const NETWORK_ID = 0; // 0 = testnet, 1 = mainnet
// Wallet mnemonic (24 words)
const MNEMONIC = [
"your", "twenty", "four", "word", "mnemonic",
"phrase", "goes", "here", "replace", "with",
"actual", "words", "from", "your", "wallet",
"that", "has", "been", "funded", "with",
"test", "ada", "from", "faucet",
];
async function main() {
// Initialize provider
const provider = new BlockfrostProvider(BLOCKFROST_KEY);
// Initialize wallet
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: NETWORK_ID,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
mnemonic: MNEMONIC,
});
const walletAddress = await wallet.getChangeAddressBech32();
console.log("Wallet address:", walletAddress);
// Create forging script from wallet address
const forgingScript = ForgeScript.withOneSignature(walletAddress);
const policyId = resolveScriptHash(forgingScript);
console.log("Policy ID:", policyId);
// Get UTxOs
const utxos = await wallet.getUtxosMesh();
if (utxos.length === 0) {
throw new Error("No UTxOs available. Fund the wallet first.");
}
console.log("Available UTxOs:", utxos.length);
// Build the transaction
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
// Prepare transaction metadata
const txMetadata: Record> = {
[policyId]: {},
};
// Add mint and output for each recipient
for (const [recipientAddress, assetName] of Object.entries(recipients)) {
const assetMetadata = metadata[assetName];
if (!assetMetadata) {
console.warn(`No metadata found for ${assetName}, skipping`);
continue;
}
const tokenNameHex = stringToHex(assetName);
const assetUnit = policyId + tokenNameHex;
console.log(`Minting ${assetName} for ${recipientAddress.slice(0, 20)}...`);
// Add metadata for this token
txMetadata[policyId][assetName] = assetMetadata;
// Add mint instruction and output
txBuilder
.mint("1", policyId, tokenNameHex)
.mintingScript(forgingScript)
.txOut(recipientAddress, [{ unit: assetUnit, quantity: "1" }]);
}
// Complete the transaction
const unsignedTx = await txBuilder
.metadataValue(721, txMetadata)
.changeAddress(walletAddress)
.selectUtxosFrom(utxos)
.complete();
// Sign and submit
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("");
console.log("Transaction submitted successfully!");
console.log("Transaction hash:", txHash);
console.log(`View on explorer: https://preprod.cardanoscan.io/transaction/${txHash}`);
}
main().catch((error) => {
console.error("Error:", error.message);
process.exit(1);
});
```
**What to expect:** A complete minting script that distributes tokens to multiple recipients.
### Step 6: Run the script
Execute the minting:
```bash
npm start
```
**What to expect:** Console output showing each mint operation and the final transaction hash.
## Complete Example
Here is the complete project structure:
```
nodejs-minting/
package.json
tsconfig.json
src/
main.ts
metadata.ts
recipients.ts
```
**package.json:**
```json
{
"name": "nodejs-minting",
"type": "module",
"scripts": {
"start": "tsc && node ./dist/main.js"
},
"dependencies": {
"@meshsdk/core": "^1.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
```
## Next Steps
* Minting API reference - Advanced minting options
* Multi-signature minting - Coordinate multiple signers
* Headless Wallet options - Different key types
* Mint a collection - Batch minting with Bun
## Troubleshooting
### No UTxOs available
**Cause:** The wallet has no funds.
**Solution:** Fund the wallet using the [testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet) or transfer ADA from another wallet.
### Transaction too large
**Cause:** Too many mints or outputs in one transaction.
**Solution:** Split the recipients into multiple batches and submit separate transactions.
### Invalid address
**Cause:** Recipient address is malformed or for wrong network.
**Solution:** Verify addresses are valid Cardano addresses for the correct network (testnet or mainnet).
### Mnemonic validation failed
**Cause:** The mnemonic phrase is incorrect or incomplete.
**Solution:** Ensure you have exactly 24 words (or 15/12 for some wallets) with correct spelling.
### Policy ID mismatch
**Cause:** The forging script changed between mints.
**Solution:** The policy ID is deterministic based on the wallet address. Ensure you use the same wallet for all mints of a collection.
## References
* [CIP-25: Media NFT Metadata Standard](https://cips.cardano.org/cip/CIP-0025) - The Cardano standard for NFT metadata used in this guide
* [Cardano Developer Portal: Native Tokens](https://developers.cardano.org/docs/native-tokens/) - Official documentation on Cardano's native token system
* [Cardano Developer Portal](https://developers.cardano.org) - Official development resources and guides
## Related Links
* Minting API Reference
* Headless Wallet Documentation
* CIP-25 Metadata Standard
* Blockchain Providers
# Build Multi-Signature Minting Transactions
URL: /guides/multisig-minting
Implement multi-sig NFT minting with Mesh SDK. Coordinate signatures between browser and server wallets.
***
title: "Build Multi-Signature Minting Transactions"
description: "Implement multi-sig NFT minting with Mesh SDK. Coordinate signatures between browser and server wallets."
-----------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Multi-signature (multi-sig) transactions require multiple parties to sign before submission. This is essential for NFT minting services where users pay for minting but your application controls the minting policy. Cardano's [native multi-asset](https://developers.cardano.org/docs/native-tokens/) system enables minting without smart contracts, using native scripts for policy control.
### What you will build
* A client-side component that connects user wallets
* A server-side API that builds minting transactions
* A complete multi-sig signing flow
### The multi-sig minting flow
1. **User wallet** provides payment UTxOs
2. **Server** builds the transaction using user inputs
3. **User** partially signs the transaction
4. **Server** adds its signature and submits
### Prerequisites
* A Next.js application with Mesh SDK (see Next.js guide)
* A server-side wallet (mnemonic or CLI keys)
* A Blockfrost API key
### Time to complete
45 minutes
## Quick Start
View the complete implementation:
* Full source code on GitHub
## Step-by-Step Guide
### Step 1: Connect the user wallet
Create a client component to connect the user's browser wallet:
```typescript
// components/MintButton.tsx
"use client";
import { useState } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
import { experimentalSelectUtxos } from "@meshsdk/core";
import type { UTxO, Unit, Quantity } from "@meshsdk/core";
const MINTING_FEE = "5000000"; // 5 ADA
export function MintButton() {
const { wallet, connected } = useWallet();
const [loading, setLoading] = useState(false);
const [txHash, setTxHash] = useState("");
async function handleMint() {
if (!connected) return;
setLoading(true);
try {
// Get user wallet info
const recipientAddress = await wallet.getChangeAddress();
const utxos = await wallet.getUtxos();
// Select UTxOs for the minting fee
const assetMap = new Map();
assetMap.set("lovelace", MINTING_FEE);
const selectedUtxos = experimentalSelectUtxos(assetMap, utxos, "5000000");
// Send to server to build transaction
const response = await fetch("/api/mint", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
recipientAddress,
utxos: selectedUtxos,
}),
});
const { unsignedTx } = await response.json();
// User signs with partial signing enabled
const signedTx = await wallet.signTx(unsignedTx, true);
// Send back to server for final signature and submission
const submitResponse = await fetch("/api/mint/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ signedTx }),
});
const { txHash } = await submitResponse.json();
setTxHash(txHash);
} catch (error) {
console.error("Minting failed:", error);
} finally {
setLoading(false);
}
}
return (
);
}
```
## Next Steps
* Smart contract transactions - NFT marketplace patterns
* Minting API reference - Advanced minting options
* Production deployment - Security best practices
## Troubleshooting
### User UTxOs already spent
**Cause:** The user's UTxOs were spent between selection and submission.
**Solution:** Implement retry logic or show an error asking the user to try again.
### Signature verification failed
**Cause:** The transaction was modified after the user signed.
**Solution:** Never modify the transaction after the user signs. Build the complete transaction before returning it to the client.
### Insufficient funds
**Cause:** Selected UTxOs do not cover the minting fee plus transaction fee.
**Solution:** Increase the UTxO selection amount to include buffer for fees:
```typescript
const selectedUtxos = experimentalSelectUtxos(assetMap, utxos, "7000000");
```
### CORS errors
**Cause:** API routes are not accessible from the client.
**Solution:** Ensure your Next.js API routes are correctly configured. In App Router, use `route.ts` files.
### Mnemonic not found
**Cause:** Environment variables are not loaded.
**Solution:** For Next.js, ensure:
* Variables are in `.env.local` (not `.env`)
* Server-only variables do not have `NEXT_PUBLIC_` prefix
* Restart the dev server after adding variables
## Security Considerations
* **Never expose mnemonics** in client-side code
* **Validate user UTxOs** before building transactions
* **Set reasonable minting fees** to cover costs and prevent abuse
* **Implement rate limiting** on API endpoints
* **Log minting events** for auditing
## References
* [Cardano Developer Portal: Native Tokens](https://developers.cardano.org/docs/native-tokens/) - Official documentation on Cardano's native multi-asset system
* [CIP-25: Media NFT Metadata Standard](https://cips.cardano.org/cip/CIP-0025) - The Cardano standard for NFT metadata
* [CIP-30: dApp-Wallet Web Bridge](https://cips.cardano.org/cip/CIP-0030) - The standard enabling partial signing for multi-sig transactions
## Related Links
* Complete example code
* Transaction Builder API
* Production Deployment
# Build Your First Cardano dApp with Next.js
URL: /guides/nextjs
Create a Cardano dApp using Next.js and Mesh SDK. Connect wallets, query assets, and build your first blockchain application.
***
title: "Build Your First Cardano dApp with Next.js"
description: "Create a Cardano dApp using Next.js and Mesh SDK. Connect wallets, query assets, and build your first blockchain application."
--------------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
In this guide, you build a Next.js application that connects to Cardano wallets and displays user assets. This is the foundation for any Cardano dApp. Mesh SDK implements the [CIP-30](https://cips.cardano.org/cip/CIP-0030) dApp-wallet web bridge standard, providing a consistent interface across all compatible Cardano wallets.
### What you will build
* A Next.js application with Mesh SDK integration
* Wallet connection using the CardanoWallet component
* Asset display for connected wallets
### Prerequisites
* Node.js 18+ installed
* A Cardano wallet browser extension (Eternl, Nami, or Lace)
* Basic React/Next.js knowledge
### Time to complete
30 minutes
## Quick Start
Use the Mesh CLI to scaffold a complete project instantly:
```bash
npx meshjs your-app-name
cd your-app-name
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) to see your dApp. Skip the rest of this guide if you use the CLI.
## Step-by-Step Setup
### Step 1: Create a Next.js project
Create a new Next.js application with TypeScript:
```bash
npx create-next-app@latest my-cardano-dapp --typescript
cd my-cardano-dapp
```
When prompted, select these options:
| Option | Recommended |
| ---------------- | --------------------- |
| ESLint | Yes |
| Tailwind CSS | Yes |
| `src/` directory | Yes |
| App Router | No (use Pages Router) |
| Turbopack | No |
| Import alias | No |
**What to expect:** A new `my-cardano-dapp` directory with the Next.js project structure.
### Step 2: Verify the setup
Start the development server:
```bash
npm run dev
```
**What to expect:** Open [http://localhost:3000](http://localhost:3000) to see the default Next.js welcome page. Press `Ctrl+C` to stop the server.
### Step 3: Install Mesh SDK
Install the Mesh packages:
```bash
npm install @meshsdk/core @meshsdk/react
```
* `@meshsdk/core` - Core blockchain functionality (wallets, transactions, providers)
* `@meshsdk/react` - React components and hooks for wallet integration
**What to expect:** The packages install without errors. Your `package.json` now includes both dependencies.
### Step 4: Add MeshProvider
The `MeshProvider` context manages wallet state across your application.
Open `src/pages/_app.tsx` and replace its contents:
```tsx
import "@/styles/globals.css";
import "@meshsdk/react/styles.css";
import type { AppProps } from "next/app";
import { MeshProvider } from "@meshsdk/react";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
**What to expect:** Your app now has access to wallet state through React context.
### Step 5: Create the wallet connection page
Replace the contents of `src/pages/index.tsx`:
```tsx
import { useState } from "react";
import type { NextPage } from "next";
import { CardanoWallet, useWallet } from "@meshsdk/react";
const Home: NextPage = () => {
const { connected, wallet } = useWallet();
const [assets, setAssets] = useState(null);
const [loading, setLoading] = useState(false);
async function getAssets() {
if (wallet) {
setLoading(true);
const walletAssets = await wallet.getAssets();
setAssets(walletAssets);
setLoading(false);
}
}
return (
My Cardano dApp
{connected && (
{assets && (
{JSON.stringify(assets, null, 2)}
)}
)}
);
};
export default Home;
```
**What to expect:** A page with a wallet connection button. After connecting, a button appears to fetch and display wallet assets.
### Step 6: Test your dApp
Start the development server:
```bash
npm run dev
```
1. Open [http://localhost:3000](http://localhost:3000)
2. Click the wallet button to connect your Cardano wallet
3. Approve the connection in your wallet extension
4. Click "Get Wallet Assets" to see your tokens
**What to expect:** Your wallet connects and displays any assets you hold. If you have no assets, the list will be empty or show only ADA.
## Complete Example
Here is the complete code for both files:
**src/pages/\_app.tsx**
```tsx
import "@/styles/globals.css";
import "@meshsdk/react/styles.css";
import type { AppProps } from "next/app";
import { MeshProvider } from "@meshsdk/react";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
**src/pages/index.tsx**
```tsx
import { useState } from "react";
import type { NextPage } from "next";
import { CardanoWallet, useWallet } from "@meshsdk/react";
const Home: NextPage = () => {
const { connected, wallet } = useWallet();
const [assets, setAssets] = useState(null);
const [loading, setLoading] = useState(false);
async function getAssets() {
if (wallet) {
setLoading(true);
const walletAssets = await wallet.getAssets();
setAssets(walletAssets);
setLoading(false);
}
}
return (
My Cardano dApp
{connected && (
{assets && (
{JSON.stringify(assets, null, 2)}
)}
)}
);
};
export default Home;
```
## Next Steps
Now that you have a working dApp foundation:
* Display wallet address and ADA balance - Explore the BrowserWallet API
* Build transactions - Send ADA and tokens
* Add authentication - Verify wallet ownership with signatures
* Integrate smart contracts - Work with Aiken validators
## Troubleshooting
### Wallet not appearing
**Cause:** No Cardano wallet extension is installed.
**Solution:** Install a Cardano wallet extension like [Eternl](https://eternl.io/), [Nami](https://namiwallet.io/), or [Lace](https://www.lace.io/).
### Buffer is not defined error
**Cause:** Mesh uses Node.js APIs that require polyfills in the browser.
**Solution:** See the polyfill configuration guide for your framework.
### Connection rejected
**Cause:** The user declined the wallet connection request.
**Solution:** This is expected behavior. Prompt the user to try again if they want to connect.
### Assets not loading
**Cause:** The wallet may be on a different network or have no assets.
**Solution:** Ensure your wallet is on the correct network (mainnet or testnet) and has assets to display.
## References
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources and documentation
* [CIP-30: Cardano dApp-Wallet Web Bridge](https://cips.cardano.org/cip/CIP-0030) - The standard that Mesh SDK implements for wallet connectivity
* [Cardano eUTXO Model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/) - Understanding Cardano's extended UTXO transaction model
* [MeshJS GitHub Repository](https://github.com/MeshJS/mesh) - Mesh SDK source code and contributions
## Related Links
* BrowserWallet API - Complete wallet interaction methods
* Transaction Builder - Build and submit transactions
* Cardano Testnet Faucet - Get free test ADA
* Svelte Integration - Use Mesh with Svelte
# Mint an NFT Collection on Cardano
URL: /guides/nft-collection
Create native NFT assets on Cardano using Mesh SDK and Bun. Learn CIP-25 metadata, native scripts, and transaction building.
***
title: "Mint an NFT Collection on Cardano"
description: "Create native NFT assets on Cardano using Mesh SDK and Bun. Learn CIP-25 metadata, native scripts, and transaction building."
-------------------------------------------------------------------------------------------------------------------------------------------
import Youtube from "@/components/ui/Youtube";
import { HowToSchema } from "@/components/seo";
import Link from "fumadocs-core/link";
## Overview
In this guide, you mint an NFT collection on Cardano using native scripts. Cardano's [native multi-asset](https://developers.cardano.org/docs/native-tokens/) system allows minting tokens without smart contracts, making them simpler and cheaper to create. Token metadata follows the [CIP-25](https://cips.cardano.org/cip/CIP-0025) standard.
### What you will build
* A minting script that creates multiple NFTs in one transaction
* CIP-25 compliant metadata for marketplace compatibility
* A time-locked minting policy that expires after a set slot
### What you will learn
* How Cardano native tokens work without smart contracts
* Building minting transactions with Mesh SDK
* Implementing CIP-25 metadata standards
### Prerequisites
* [Bun](https://bun.sh/docs/installation) installed
* A Blockfrost API key ([get one free](https://blockfrost.io/))
* Basic TypeScript knowledge
### Time to complete
45 minutes
## Quick Start
If you want to start with a working example:
```bash
git clone https://github.com/MeshJS/examples
cd examples/minting
bun install
```
Add your credentials to `.env` and run `bun run index.ts`.
## Step-by-Step Guide
### Step 1: Set up the project
Install Bun if you have not already:
```bash
curl -fsSL https://bun.sh/install | bash
```
Create and initialize a new project:
```bash
mkdir nft-collection
cd nft-collection
bun init -y
bun add @meshsdk/core
```
**What to expect:** A new project with `package.json` and `@meshsdk/core` installed.
### Step 2: Generate a wallet
Create `scripts/generate-wallet.ts`:
```typescript
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
import { generateMnemonic } from "@meshsdk/core";
// Generate a new mnemonic phrase
const words = generateMnemonic(256);
console.log("Mnemonic phrase:");
console.log(words.join(" "));
console.log("");
// Create wallet from mnemonic
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
mnemonic: words,
networkId: 0, // 0 = testnet
walletAddressType: AddressType.Base,
});
const address = await wallet.getChangeAddressBech32();
console.log("Wallet address:");
console.log(address);
```
Run the script:
```bash
bun run scripts/generate-wallet.ts
```
**What to expect:** A 24-word mnemonic phrase and a wallet address starting with `addr_test1`.
### Step 3: Fund your wallet
1. Copy the wallet address from the previous step
2. Go to the [Cardano testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet)
3. Paste your address and request test ADA
4. Wait 1-2 minutes for the transaction to confirm
**What to expect:** Your wallet receives test ADA (usually 10,000 tADA).
### Step 4: Configure environment
Create a `.env` file:
```bash
MNEMONIC=your twenty four word mnemonic phrase goes here
BLOCKFROST_KEY=your_blockfrost_preprod_api_key
```
**What to expect:** Your credentials are stored securely and not committed to git.
### Step 5: Create the minting script
Create `index.ts`:
```typescript
import {
MeshTxBuilder,
BlockfrostProvider,
ForgeScript,
resolveScriptHash,
stringToHex,
deserializeAddress,
} from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
import type { NativeScript, AssetMetadata } from "@meshsdk/core";
// Load environment variables
const BLOCKFROST_KEY = process.env.BLOCKFROST_KEY!;
const MNEMONIC = process.env.MNEMONIC!;
if (!BLOCKFROST_KEY || !MNEMONIC) {
throw new Error("Missing BLOCKFROST_KEY or MNEMONIC in environment");
}
// Initialize provider and wallet
const provider = new BlockfrostProvider(BLOCKFROST_KEY);
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
mnemonic: MNEMONIC.split(" "),
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
});
const address = await wallet.getChangeAddressBech32();
const utxos = await wallet.getUtxosMesh();
const { pubKeyHash } = deserializeAddress(address);
console.log("Wallet address:", address);
console.log("Available UTxOs:", utxos.length);
if (utxos.length === 0) {
throw new Error("No UTxOs available. Fund the wallet first.");
}
```
**What to expect:** The script initializes the wallet and provider.
### Step 6: Define the native script
Add the native script (minting policy) to `index.ts`:
```typescript
// Define the native script (minting policy)
// This policy requires your signature AND expires at a specific slot
const LOCK_SLOT = 90000000; // Adjust this to a future slot on preprod
const nativeScript: NativeScript = {
type: "all",
scripts: [
{
type: "before",
slot: LOCK_SLOT.toString(),
},
{
type: "sig",
keyHash: pubKeyHash,
},
],
};
const forgingScript = ForgeScript.fromNativeScript(nativeScript);
const policyId = resolveScriptHash(forgingScript);
console.log("Policy ID:", policyId);
```
The native script enforces two conditions:
* `before` - Minting is only valid before the specified slot
* `sig` - The transaction must be signed by your wallet
**What to expect:** A deterministic policy ID derived from the script.
### Step 7: Create the metadata function
Add the CIP-25 metadata generator:
```typescript
type NFTMetadata = {
name: string;
image: string;
mediaType: string;
description?: string;
attributes?: Record;
files?: Array<{
mediaType: string;
name: string;
src: string;
}>;
};
function createMetadata(
name: string,
imageIpfs: string,
attributes?: Record
): NFTMetadata {
return {
name,
image: imageIpfs,
mediaType: "image/png",
description: `${name} from my NFT collection`,
attributes,
files: [
{
mediaType: "image/png",
name,
src: imageIpfs,
},
],
};
}
```
**What to expect:** A function that generates CIP-25 compliant metadata.
### Step 8: Build the minting transaction
Add the transaction building logic:
```typescript
// Prepare metadata for all NFTs
const collectionMetadata: {
[policyId: string]: { [assetName: string]: NFTMetadata };
} = { [policyId]: {} };
const txBuilder = new MeshTxBuilder({ fetcher: provider });
// Mint 9 NFTs
const COLLECTION_SIZE = 9;
const IMAGE_IPFS = "ipfs://QmPS4PBvpGc2z6Dd6JdYqfHrKnURjtRGPTJWdhnAXNA8bQ";
for (let i = 1; i <= COLLECTION_SIZE; i++) {
const tokenName = `MyNFT #${i}`;
const tokenNameHex = stringToHex(tokenName);
// Add mint instruction
txBuilder
.mint("1", policyId, tokenNameHex)
.mintingScript(forgingScript);
// Add metadata
collectionMetadata[policyId][tokenName] = createMetadata(
tokenName,
IMAGE_IPFS,
{ edition: i, rarity: i <= 3 ? "rare" : "common" }
);
}
// Complete and submit the transaction
const unsignedTx = await txBuilder
.metadataValue(721, collectionMetadata)
.changeAddress(address)
.invalidHereafter(LOCK_SLOT)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Transaction submitted!");
console.log("Transaction hash:", txHash);
console.log(`View on explorer: https://preprod.cardanoscan.io/transaction/${txHash}`);
```
**What to expect:** The transaction mints 9 NFTs and sends them to your wallet.
### Step 9: Run the minting script
Execute the complete script:
```bash
bun run index.ts
```
**What to expect:** Console output showing the transaction hash. After 1-2 minutes, your NFTs appear in your wallet.
## Complete Example
Here is the complete `index.ts`:
```typescript
import {
MeshTxBuilder,
BlockfrostProvider,
ForgeScript,
resolveScriptHash,
stringToHex,
deserializeAddress,
} from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
import type { NativeScript } from "@meshsdk/core";
// Configuration
const BLOCKFROST_KEY = process.env.BLOCKFROST_KEY!;
const MNEMONIC = process.env.MNEMONIC!;
const LOCK_SLOT = 90000000;
const COLLECTION_SIZE = 9;
const IMAGE_IPFS = "ipfs://QmPS4PBvpGc2z6Dd6JdYqfHrKnURjtRGPTJWdhnAXNA8bQ";
if (!BLOCKFROST_KEY || !MNEMONIC) {
throw new Error("Missing environment variables");
}
// Types
type NFTMetadata = {
name: string;
image: string;
mediaType: string;
description?: string;
attributes?: Record;
files?: Array<{ mediaType: string; name: string; src: string }>;
};
function createMetadata(
name: string,
imageIpfs: string,
attributes?: Record
): NFTMetadata {
return {
name,
image: imageIpfs,
mediaType: "image/png",
description: `${name} from my NFT collection`,
attributes,
files: [{ mediaType: "image/png", name, src: imageIpfs }],
};
}
async function main() {
// Initialize
const provider = new BlockfrostProvider(BLOCKFROST_KEY);
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
mnemonic: MNEMONIC.split(" "),
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
});
const address = await wallet.getChangeAddressBech32();
const utxos = await wallet.getUtxosMesh();
const { pubKeyHash } = deserializeAddress(address);
console.log("Wallet:", address);
console.log("UTxOs:", utxos.length);
// Create minting policy
const nativeScript: NativeScript = {
type: "all",
scripts: [
{ type: "before", slot: LOCK_SLOT.toString() },
{ type: "sig", keyHash: pubKeyHash },
],
};
const forgingScript = ForgeScript.fromNativeScript(nativeScript);
const policyId = resolveScriptHash(forgingScript);
console.log("Policy ID:", policyId);
// Build transaction
const collectionMetadata: Record> = {
[policyId]: {},
};
const txBuilder = new MeshTxBuilder({ fetcher: provider });
for (let i = 1; i <= COLLECTION_SIZE; i++) {
const tokenName = `MyNFT #${i}`;
const tokenNameHex = stringToHex(tokenName);
txBuilder.mint("1", policyId, tokenNameHex).mintingScript(forgingScript);
collectionMetadata[policyId][tokenName] = createMetadata(tokenName, IMAGE_IPFS, {
edition: i,
rarity: i <= 3 ? "rare" : "common",
});
}
const unsignedTx = await txBuilder
.metadataValue(721, collectionMetadata)
.changeAddress(address)
.invalidHereafter(LOCK_SLOT)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Success! TX:", txHash);
console.log(`https://preprod.cardanoscan.io/transaction/${txHash}`);
}
main().catch(console.error);
```
## Next Steps
* **Add unique images** - Upload artwork to IPFS and use unique hashes per NFT
* **Expand metadata** - Add more attributes following the [CIP-25 specification](https://cips.cardano.org/cip/CIP-25)
* **Deploy to mainnet** - Change `networkId` to `1` and use a mainnet Blockfrost key
* Multi-signature minting - Let users pay for minting
## Troubleshooting
### Slot has already passed
**Cause:** The `LOCK_SLOT` value is in the past.
**Solution:** Check the current slot on [preprod.cardanoscan.io](https://preprod.cardanoscan.io/) and set `LOCK_SLOT` to a value at least a few hours in the future.
### Transaction too large
**Cause:** Minting too many NFTs in one transaction.
**Solution:** Reduce `COLLECTION_SIZE` or split into multiple transactions. A single transaction can typically handle 20-50 mints depending on metadata size.
### Invalid metadata
**Cause:** Metadata does not follow CIP-25 format.
**Solution:** Ensure the metadata structure matches CIP-25 requirements. The top-level key must be the policy ID, and each asset must have `name` and `image` fields.
### Insufficient funds
**Cause:** Not enough ADA to cover the transaction fee and min-UTXO requirements.
**Solution:** Request more test ADA from the faucet. Each NFT requires approximately 1.5 ADA for the min-UTXO deposit.
## References
* [CIP-25: Media NFT Metadata Standard](https://cips.cardano.org/cip/CIP-0025) - The Cardano standard for NFT metadata used in this guide
* [Cardano Developer Portal: Native Tokens](https://developers.cardano.org/docs/native-tokens/) - Official documentation on Cardano's native multi-asset system
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources and tools
## Related Links
* CIP-25 Metadata Standard - Official NFT metadata specification
* Cardano Testnet Faucet - Get free test ADA
* Blockfrost - API provider for Cardano
* Server-Side Minting - Mint and distribute tokens
* Minting API Reference - Advanced minting options
# Fix Node.js Polyfill Errors in Browser Projects
URL: /guides/node-specific-imports
Resolve 'Buffer is not defined' and Node.js polyfill errors in React, Vue, Svelte, and Angular with Mesh SDK.
***
title: "Fix Node.js Polyfill Errors in Browser Projects"
description: "Resolve 'Buffer is not defined' and Node.js polyfill errors in React, Vue, Svelte, and Angular with Mesh SDK."
----------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
When building Cardano dApps for the browser, you may encounter errors like `Buffer is not defined`. This happens because Mesh SDK uses Node.js APIs that are not available in browsers by default.
### Common error messages
```
Uncaught ReferenceError: Buffer is not defined
Module not found: Can't resolve 'buffer'
ReferenceError: TextEncoder is not defined
```
### Affected modules
| Module | Purpose |
| ----------------------------- | ------------------------ |
| `Buffer` | Binary data handling |
| `TextEncoder` / `TextDecoder` | String encoding |
| `crypto` | Cryptographic operations |
| `process` | Environment variables |
| `stream` | Data streaming |
Modern bundlers (Webpack 5, Vite) do not automatically polyfill these. This guide shows you how to fix these errors for each framework.
### Time to complete
15 minutes
## What Are Polyfills?
Polyfills are packages that implement Node.js functionality for browsers. You configure your bundler to use these browser-compatible versions instead of Node.js modules.
Install the common polyfills:
```bash
npm install buffer process stream-browserify crypto-browserify --save
```
## Framework-Specific Solutions
### Next.js
Next.js uses Webpack internally. Add the polyfill plugin:
```bash
npm install node-polyfill-webpack-plugin --save-dev
```
Update `next.config.js`:
```javascript
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
// Only add polyfills for client-side
if (!isServer) {
config.plugins.push(new NodePolyfillPlugin());
}
return config;
},
};
module.exports = nextConfig;
```
**What to expect:** The build completes without polyfill errors.
### Vite (React, Vue, Svelte)
Vite uses Rollup. Install the polyfill plugin:
```bash
npm install rollup-plugin-polyfill-node --save-dev
```
Update `vite.config.ts`:
```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; // or vue, svelte
import rollupNodePolyFill from "rollup-plugin-polyfill-node";
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
plugins: [rollupNodePolyFill()],
},
},
resolve: {
alias: {
buffer: "rollup-plugin-polyfill-node/polyfills/buffer-es6",
process: "rollup-plugin-polyfill-node/polyfills/process-es6",
},
},
});
```
If you still see errors, add this to your entry file:
```typescript
import { Buffer } from "buffer";
globalThis.Buffer = globalThis.Buffer || Buffer;
```
**What to expect:** The dev server and production build work correctly.
### Webpack 5
Webpack 5 removed automatic Node.js polyfills. Choose one approach:
**Option 1: Use NodePolyfillPlugin (recommended)**
```bash
npm install node-polyfill-webpack-plugin --save-dev
```
Update `webpack.config.js`:
```javascript
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = {
plugins: [
new NodePolyfillPlugin(),
],
};
```
**Option 2: Manual resolve.fallback**
```bash
npm install buffer process stream-browserify --save
```
Update `webpack.config.js`:
```javascript
module.exports = {
resolve: {
fallback: {
buffer: require.resolve("buffer/"),
process: require.resolve("process/browser"),
stream: require.resolve("stream-browserify"),
},
},
};
```
**What to expect:** Webpack builds without module resolution errors.
### Create React App (CRA)
CRA uses Webpack 5 but does not expose the config. Use CRACO to override:
```bash
npm install @craco/craco node-polyfill-webpack-plugin --save-dev
```
Update `package.json` scripts:
```json
{
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test"
}
}
```
Create `craco.config.js`:
```javascript
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = {
webpack: {
plugins: {
add: [new NodePolyfillPlugin()],
},
},
};
```
**What to expect:** CRA builds with polyfills applied.
### Angular
Angular uses Webpack internally. Use `ngx-build-plus` to extend the config:
```bash
npm install ngx-build-plus node-polyfill-webpack-plugin buffer process stream-browserify --save-dev
```
Update `angular.json`:
```json
{
"projects": {
"my-app": {
"architect": {
"build": {
"builder": "ngx-build-plus:browser"
},
"serve": {
"builder": "ngx-build-plus:dev-server"
}
}
}
}
}
```
Create `webpack.config.js`:
```javascript
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = {
plugins: [new NodePolyfillPlugin()],
};
```
Build with the extra config:
```bash
ng build --extra-webpack-config webpack.config.js
```
**What to expect:** Angular builds with Node.js polyfills.
### SvelteKit
SvelteKit uses Vite. Follow the Vite instructions above, but update `svelte.config.js`:
```javascript
import adapter from "@sveltejs/adapter-auto";
import { vitePreprocess } from "@sveltejs/kit/vite";
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
},
};
export default config;
```
And `vite.config.ts`:
```typescript
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import rollupNodePolyFill from "rollup-plugin-polyfill-node";
export default defineConfig({
plugins: [sveltekit()],
build: {
rollupOptions: {
plugins: [rollupNodePolyFill()],
},
},
});
```
**What to expect:** SvelteKit handles Node.js modules correctly.
## Complete Example: Next.js
Here is a complete Next.js setup:
**package.json:**
```json
{
"dependencies": {
"@meshsdk/core": "^1.0.0",
"@meshsdk/react": "^1.0.0",
"next": "14.0.0",
"react": "18.2.0"
},
"devDependencies": {
"node-polyfill-webpack-plugin": "^3.0.0"
}
}
```
**next.config.js:**
```javascript
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.plugins.push(new NodePolyfillPlugin());
}
return config;
},
};
module.exports = nextConfig;
```
## Next Steps
* Build your first dApp - Complete Next.js integration
* Svelte integration - Use Mesh with Svelte
* BrowserWallet API - Connect browser wallets
## Troubleshooting
### Error persists after configuration
**Cause:** Bundler cache contains old build artifacts.
**Solution:** Clear the cache and rebuild:
```bash
# Next.js
rm -rf .next node_modules/.cache
npm run build
# Vite
rm -rf node_modules/.vite
npm run build
```
### Multiple Buffer definitions
**Cause:** Different packages include conflicting Buffer implementations.
**Solution:** Ensure only one Buffer polyfill is used:
```typescript
// Add to entry file
import { Buffer } from "buffer";
if (typeof window !== "undefined") {
window.Buffer = window.Buffer || Buffer;
}
```
### Runtime errors despite successful build
**Cause:** Polyfills are configured for build but not dev server.
**Solution:** Ensure polyfill configuration applies to both build and dev. For Vite, add to `optimizeDeps`:
```typescript
export default defineConfig({
optimizeDeps: {
include: ["buffer", "process"],
},
});
```
### Cannot resolve 'fs' or 'path'
**Cause:** Server-only modules are imported in client code.
**Solution:** These modules cannot be polyfilled. Use dynamic imports or conditional loading:
```typescript
// Mark as client-only in Next.js
"use client";
// Or use dynamic import
const MeshComponent = dynamic(() => import("./MeshComponent"), {
ssr: false,
});
```
## Related Links
* Next.js Integration Guide
* Webpack Resolve Fallback
* Vite Build Options
# Deploy Cardano dApps to Production
URL: /guides/production-deployment
Security best practices, deployment checklists, and error handling for production Cardano dApps with Mesh SDK.
***
title: "Deploy Cardano dApps to Production"
description: "Security best practices, deployment checklists, and error handling for production Cardano dApps with Mesh SDK."
-----------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Deploying Cardano dApps to production requires attention to security, reliability, and user experience. This guide covers essential practices for enterprise-grade deployments.
### What you will learn
* Private key management best practices
* Transaction validation patterns
* Provider configuration and failover
* Error handling strategies
* Monitoring and alerting setup
### Prerequisites
* A working Cardano dApp (see Next.js guide)
* Basic understanding of deployment environments
* Access to production infrastructure
### Time to complete
90 minutes to review and implement
## Security Best Practices
### Private Key Management
**Never expose private keys in client-side code.** For production applications:
**Browser wallets for users:**
Let users manage their own keys through wallet extensions. The `MeshCardanoBrowserWallet` class handles CIP-30 wallet connections without your application touching private keys:
```typescript
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
// User controls their own keys
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
const signedTx = await wallet.signTx(unsignedTx, false);
```
**Server-side signing:**
When your application must sign transactions (automated systems, treasury):
```typescript
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Store mnemonic in environment variables or secret manager
const mnemonic = process.env.TREASURY_MNEMONIC;
// Never log or expose the mnemonic
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: 1,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
mnemonic: mnemonic.split(" "),
});
```
**Security checklist:**
* [ ] Use environment variables for credentials
* [ ] Run signing services in isolated network segments
* [ ] Implement audit logging for all signing operations
* [ ] Consider hardware security modules (HSMs) for high-value operations
* [ ] Rotate keys periodically
### Transaction Validation
Validate all parameters before building transactions:
```typescript
function validateTransaction(params: {
recipient: string;
amount: string;
}) {
// Check amount is positive and within bounds
const lovelace = BigInt(params.amount);
if (lovelace <= 0n) {
throw new Error("Amount must be positive");
}
if (lovelace > 45_000_000_000_000_000n) {
throw new Error("Amount exceeds maximum supply");
}
// Validate address format
if (!params.recipient.startsWith("addr")) {
throw new Error("Invalid Cardano address");
}
// Check minimum UTxO requirement
if (lovelace < 1_000_000n) {
throw new Error("Amount below minimum UTxO");
}
}
```
### API Key Protection
Never expose provider API keys in client-side code:
```typescript
// BAD: Key exposed in browser bundle
const provider = new BlockfrostProvider("proj_live_abc123");
// GOOD: Proxy through your backend
const response = await fetch("/api/blockchain/utxos", {
method: "POST",
body: JSON.stringify({ address }),
});
```
**API route example:**
```typescript
// app/api/blockchain/utxos/route.ts
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider(process.env.BLOCKFROST_KEY!);
export async function POST(request: Request) {
const { address } = await request.json();
const utxos = await provider.fetchAddressUTxOs(address);
return Response.json(utxos);
}
```
## Deployment Checklist
### Pre-Launch Infrastructure
| Task | Status |
| -------------------------------------------------------- | ------ |
| Provider API keys stored securely (not in client bundle) | \[ ] |
| Backend proxy configured for blockchain queries | \[ ] |
| Rate limiting implemented on API endpoints | \[ ] |
| Error monitoring and alerting configured | \[ ] |
| Load testing completed for expected traffic | \[ ] |
### Smart Contracts
| Task | Status |
| ----------------------------------------------- | ------ |
| Contracts audited by independent party | \[ ] |
| Testnet deployment validated thoroughly | \[ ] |
| Contract addresses verified on mainnet | \[ ] |
| Reference scripts deployed if used | \[ ] |
| Monitoring configured for contract interactions | \[ ] |
### Application
| Task | Status |
| ---------------------------------------------------- | ------ |
| Console.log statements removed from production build | \[ ] |
| Source maps disabled or properly secured | \[ ] |
| CSP headers configured appropriately | \[ ] |
| HTTPS enforced with valid certificates | \[ ] |
| Input validation comprehensive | \[ ] |
## Provider Configuration
### Provider Selection
For production applications, consider:
| Provider | Strength | Consideration |
| ------------------ | ------------------------------ | ---------------------------- |
| Blockfrost | Reliability, documentation | Rate limits on free tier |
| Koios | Free tier, decentralized | Self-hosted option available |
| Maestro | Rich data, marketplace support | Enterprise pricing |
| Self-hosted Ogmios | Full control, no rate limits | Infrastructure overhead |
### Provider Failover
Implement automatic failover when providers fail:
```typescript
class ResilientProvider implements IFetcher {
private providers: IFetcher[];
private currentIndex = 0;
constructor(providers: IFetcher[]) {
this.providers = providers;
}
async fetchAddressUTxOs(address: string, asset?: string) {
for (let attempt = 0; attempt < this.providers.length; attempt++) {
try {
const index = (this.currentIndex + attempt) % this.providers.length;
return await this.providers[index].fetchAddressUTxOs(address, asset);
} catch (error) {
console.warn(`Provider ${attempt} failed, trying next`);
}
}
throw new Error("All providers failed");
}
// Implement other IFetcher methods similarly
}
// Usage
const provider = new ResilientProvider([
new BlockfrostProvider(process.env.BLOCKFROST_KEY!),
new KoiosProvider("mainnet"),
]);
```
### Network Selection
| Network | Use Case |
| ------- | -------------------------------- |
| Preview | Testing new features, unstable |
| Preprod | Production-like testing, staging |
| Mainnet | Production deployment |
## Error Handling
### Transaction Failures
Handle failures gracefully with user-friendly messages:
```typescript
async function submitTransaction(unsignedTx: string) {
try {
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
return { success: true, txHash };
} catch (error: any) {
// Parse and categorize errors
if (error.message.includes("INSUFFICIENT_FUNDS")) {
return {
success: false,
error: "Insufficient funds. Please add more ADA to your wallet.",
};
}
if (error.message.includes("UTXO_ALREADY_SPENT")) {
return {
success: false,
error: "Transaction conflict. Please try again.",
};
}
if (error.message.includes("User rejected")) {
return {
success: false,
error: "Transaction cancelled.",
};
}
// Log technical details for debugging
console.error("Transaction failed:", error);
return {
success: false,
error: "Transaction failed. Please try again later.",
};
}
}
```
### UTxO Contention
In high-traffic applications, UTxOs may be spent between query and submission:
```typescript
async function buildWithRetry(
buildFn: () => Promise,
maxRetries = 3
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await buildFn();
} catch (error: any) {
if (
error.message.includes("UTXO_ALREADY_SPENT") &&
attempt < maxRetries - 1
) {
// Wait briefly and retry with fresh UTxOs
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
continue;
}
throw error;
}
}
}
```
## Performance Optimization
### Bundle Size
Minimize client bundle size:
```typescript
// Import specific functions instead of entire package
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
// Use dynamic imports for wallet-specific features
const ConnectWallet = dynamic(() => import("./ConnectWallet"), {
ssr: false,
});
```
### Caching
Reduce redundant blockchain queries:
```typescript
class CachedProvider implements IFetcher {
private provider: IFetcher;
private cache = new Map();
constructor(provider: IFetcher) {
this.provider = provider;
}
async fetchProtocolParameters(epoch?: number) {
const cacheKey = `protocol-${epoch || "current"}`;
const cached = this.cache.get(cacheKey);
if (cached && cached.expiry > Date.now()) {
return cached.data;
}
const data = await this.provider.fetchProtocolParameters(epoch);
// Cache for 1 hour (protocol params rarely change)
this.cache.set(cacheKey, {
data,
expiry: Date.now() + 3600000,
});
return data;
}
// Implement other methods with appropriate cache TTLs
}
```
## Monitoring
### Key Metrics
Track these metrics for production dApps:
**Transaction metrics:**
* Submission success rate
* Average confirmation time
* Failed transaction breakdown by error type
* Transaction fee distribution
**User metrics:**
* Wallet connection success rate
* Transaction abandonment rate
* Error frequency by type
**Infrastructure metrics:**
* Provider response times
* API endpoint latency
* Error rates by endpoint
### Alerting
Configure alerts for:
* Sustained transaction failure rates above 5%
* Provider connectivity issues lasting > 1 minute
* Smart contract interaction anomalies
* Unusual transaction patterns
## Next Steps
* Configure providers for your deployment
* Review smart contract security
* Join the community for support
## Troubleshooting
### Transactions timing out
**Cause:** Network congestion or provider issues.
**Solution:** Implement longer timeouts and retry logic. Consider using multiple providers for redundancy.
### Wallet connection failing in production
**Cause:** CSP headers blocking wallet extension communication.
**Solution:** Configure Content Security Policy to allow wallet connections:
```javascript
// next.config.js
const securityHeaders = [
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self' 'unsafe-eval';",
},
];
```
### Rate limiting errors
**Cause:** Exceeding provider API limits.
**Solution:** Implement request queuing, caching, or upgrade to a higher tier. Consider self-hosted options for high-traffic applications.
## Related Links
* Blockchain Providers
* Smart Contract Security
* Mesh Discord Community
# Prove Wallet Ownership with Message Signing
URL: /guides/prove-wallet-ownership
Implement Sign-in with Cardano authentication using CIP-8 message signing and Mesh SDK.
***
title: "Prove Wallet Ownership with Message Signing"
description: "Implement Sign-in with Cardano authentication using CIP-8 message signing and Mesh SDK."
------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Wallet ownership verification lets users prove they control a Cardano address by signing a message with their private key. This uses the [CIP-8](https://cips.cardano.org/cip/CIP-0008) message signing standard and the [CIP-30](https://cips.cardano.org/cip/CIP-0030) wallet interface. Together, these standards form the foundation for "Sign in with Cardano" authentication.
### What you will build
* A nonce generation system for replay attack prevention
* Client-side message signing with CIP-8
* Server-side signature verification
### Common use cases
* **User authentication** - Replace passwords with wallet-based sign-in
* **Action authorization** - Verify consent for off-chain operations
* **Access control** - Gate content to specific wallet holders
### Prerequisites
* A Next.js application with Mesh SDK
* Basic understanding of authentication flows
### Time to complete
30 minutes
## How It Works

The authentication flow:
1. User connects wallet and provides their address
2. Server generates a unique nonce and stores it
3. User signs the nonce with their private key
4. Server verifies the signature matches the claimed address
5. Server issues a session token (JWT, cookie, etc.)
The nonce prevents replay attacks - each authentication attempt requires a fresh signature.
## Step-by-Step Guide
### Step 1: Get the user address
Connect the wallet and get the user's address on the client:
```typescript
// components/SignIn.tsx
"use client";
import { CardanoWallet, useWallet } from "@meshsdk/react";
export function SignIn() {
const { wallet, connected } = useWallet();
async function startLogin() {
if (!connected) return;
// Get the user's first used address
const userAddress = (await wallet.getUsedAddresses())[0];
// Send to server to get a nonce
const response = await fetch("/api/auth/nonce", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: userAddress }),
});
const { nonce } = await response.json();
console.log("Received nonce:", nonce);
}
return (
{connected && (
)}
);
}
```
**What to expect:** The wallet connects and sends the address to your server.
### Step 2: Generate a nonce on the server
Create an API route that generates and stores nonces:
```typescript
// app/api/auth/nonce/route.ts
import { NextResponse } from "next/server";
import { generateNonce } from "@meshsdk/core";
// In production, use a database
const nonceStore = new Map();
export async function POST(request: Request) {
const { address } = await request.json();
// Generate a nonce with a custom message prefix
const nonce = generateNonce("Sign this message to authenticate with MyApp: ");
// Store with 5-minute expiration
nonceStore.set(address, {
nonce,
expires: Date.now() + 5 * 60 * 1000,
});
return NextResponse.json({ nonce });
}
// Export for use in verification
export { nonceStore };
```
**What to expect:** A unique nonce is generated and stored for each address.
### Step 3: Sign the nonce with the wallet
Update the client to sign the nonce:
```typescript
// components/SignIn.tsx
"use client";
import { useState } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
export function SignIn() {
const { wallet, connected } = useWallet();
const [status, setStatus] = useState<"idle" | "signing" | "verifying" | "authenticated">("idle");
async function handleSignIn() {
if (!connected) return;
try {
setStatus("signing");
// Get user address
const userAddress = (await wallet.getUsedAddresses())[0];
// Get nonce from server
const nonceRes = await fetch("/api/auth/nonce", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: userAddress }),
});
const { nonce } = await nonceRes.json();
// Sign the nonce with the wallet
const signature = await wallet.signData(nonce, userAddress);
// Verify signature on server
setStatus("verifying");
const verifyRes = await fetch("/api/auth/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
address: userAddress,
signature,
}),
});
const { success, token } = await verifyRes.json();
if (success) {
setStatus("authenticated");
// Store token, update state, redirect, etc.
console.log("Authenticated! Token:", token);
} else {
setStatus("idle");
console.error("Authentication failed");
}
} catch (error) {
console.error("Sign-in error:", error);
setStatus("idle");
}
}
return (
{status === "signing" &&
Please sign the message in your wallet...
}
{status === "verifying" &&
Verifying signature...
}
{status === "authenticated" &&
Welcome! You are authenticated.
}
);
}
```
**What to expect:** The wallet prompts the user to sign a message.
### Step 4: Verify the signature on the server
Create an API route that verifies signatures:
```typescript
// app/api/auth/verify/route.ts
import { NextResponse } from "next/server";
import { checkSignature } from "@meshsdk/core";
import { nonceStore } from "../nonce/route";
export async function POST(request: Request) {
const { address, signature } = await request.json();
// Retrieve stored nonce
const stored = nonceStore.get(address);
if (!stored) {
return NextResponse.json(
{ success: false, error: "No nonce found for address" },
{ status: 400 }
);
}
// Check expiration
if (Date.now() > stored.expires) {
nonceStore.delete(address);
return NextResponse.json(
{ success: false, error: "Nonce expired" },
{ status: 400 }
);
}
// Verify the signature
const isValid = checkSignature(stored.nonce, signature, address);
// Always invalidate nonce after use (prevent replay)
nonceStore.delete(address);
if (!isValid) {
return NextResponse.json(
{ success: false, error: "Invalid signature" },
{ status: 401 }
);
}
// Create session token (use your preferred method)
const token = createSessionToken(address);
return NextResponse.json({ success: true, token });
}
function createSessionToken(address: string): string {
// In production, use proper JWT signing
return Buffer.from(JSON.stringify({
address,
iat: Date.now(),
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
})).toString("base64");
}
```
**What to expect:** The server verifies the signature and issues a session token.
## Complete Example
Here is a complete implementation with all components:
**components/AuthProvider.tsx:**
```typescript
"use client";
import { createContext, useContext, useState, ReactNode } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
interface AuthContextType {
isAuthenticated: boolean;
address: string | null;
signIn: () => Promise;
signOut: () => void;
}
const AuthContext = createContext(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const { wallet, connected } = useWallet();
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [address, setAddress] = useState(null);
async function signIn() {
if (!connected) return;
const userAddress = (await wallet.getUsedAddresses())[0];
// Get nonce
const nonceRes = await fetch("/api/auth/nonce", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: userAddress }),
});
const { nonce } = await nonceRes.json();
// Sign nonce
const signature = await wallet.signData(nonce, userAddress);
// Verify
const verifyRes = await fetch("/api/auth/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: userAddress, signature }),
});
const { success } = await verifyRes.json();
if (success) {
setIsAuthenticated(true);
setAddress(userAddress);
}
}
function signOut() {
setIsAuthenticated(false);
setAddress(null);
}
return (
{children}
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error("useAuth must be used within AuthProvider");
return context;
}
```
**Usage in a page:**
```typescript
import { CardanoWallet, useWallet } from "@meshsdk/react";
import { useAuth } from "@/components/AuthProvider";
export default function ProtectedPage() {
const { connected } = useWallet();
const { isAuthenticated, address, signIn, signOut } = useAuth();
if (!connected) {
return ;
}
if (!isAuthenticated) {
return (
);
}
return (
Welcome, {address?.slice(0, 16)}...
{/* Protected content here */}
);
}
```
## Next Steps
* Production deployment - Security best practices
* BrowserWallet API - More wallet methods
* CIP-8 Specification - Message signing standard
## Troubleshooting
### Signature verification fails
**Cause:** The address used for signing does not match the one verified.
**Solution:** Ensure you use the same address for both operations:
```typescript
// Use the same address for signing and verification
const address = (await wallet.getUsedAddresses())[0];
const signature = await wallet.signData(nonce, address);
// Send this exact address to the server
```
### Nonce expired
**Cause:** User took too long to sign.
**Solution:** Increase the expiration time or implement retry logic:
```typescript
// Increase to 10 minutes
expires: Date.now() + 10 * 60 * 1000,
```
### User rejected signing
**Cause:** User clicked "Cancel" in the wallet popup.
**Solution:** Catch the error and allow retry:
```typescript
try {
const signature = await wallet.signData(nonce, address);
} catch (error) {
if (error.message.includes("User rejected")) {
// Show "Please sign to continue" message
}
}
```
### Invalid signature format
**Cause:** The signature object is not serialized correctly.
**Solution:** The `signData` method returns a signature object. Pass it directly to `checkSignature`:
```typescript
// Client
const signature = await wallet.signData(nonce, address);
// signature is { signature: string, key: string }
// Server
checkSignature(nonce, signature, address);
// Not checkSignature(nonce, signature.signature, address)
```
## Security Best Practices
* **Always regenerate nonces** - A used nonce must never be valid again
* **Set short expiration** - 5-10 minutes maximum for unused nonces
* **Use HTTPS** - Protect the signature during transmission
* **Validate addresses** - Check format before database operations
* **Rate limit requests** - Prevent brute force attempts
* **Log authentication events** - Monitor for suspicious activity
## References
* [CIP-8: Message Signing](https://cips.cardano.org/cip/CIP-0008) - The Cardano standard for signing and verifying arbitrary data with private keys
* [CIP-30: dApp-Wallet Web Bridge](https://cips.cardano.org/cip/CIP-0030) - The standard enabling wallet connectivity in web applications
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources
## Related Links
* CIP-8 Message Signing
* BrowserWallet API
* Production Deployment
# Build NFT Marketplace Smart Contract Transactions
URL: /guides/smart-contract-transactions
Build transactions for listing, purchasing, canceling, and updating NFTs with Cardano smart contracts using Mesh SDK.
***
title: "Build NFT Marketplace Smart Contract Transactions"
description: "Build transactions for listing, purchasing, canceling, and updating NFTs with Cardano smart contracts using Mesh SDK."
------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
In this guide, you build the transaction patterns for an NFT marketplace on Cardano. You learn how to construct listing, purchasing, canceling, and updating transactions using Plutus smart contracts on the [eUTXO model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/).
### What you will build
* Listing transactions that lock NFTs at a script address
* Purchase transactions that pay sellers and transfer ownership
* Cancel and update transactions for seller management
### What you will learn
* Locking assets at script addresses with datums
* Spending from scripts with redeemers
* Authorization patterns with required signers
### Prerequisites
* A deployed Plutus smart contract (see Aiken guide)
* Next.js application with Mesh SDK
* Understanding of datums and redeemers
### Time to complete
60 minutes
## Quick Start
This guide uses a simple marketplace contract. View the complete example:
* GitHub examples repository
## Step-by-Step Guide
### Step 1: Load the Plutus script
Load your compiled marketplace contract:
```typescript
import { resolvePlutusScriptAddress } from "@meshsdk/core";
import type { PlutusScript, Data } from "@meshsdk/core";
// Your compiled Plutus script in CBOR format
const scriptCbor = "590795..."; // Full CBOR hex string
const script: PlutusScript = {
code: scriptCbor,
version: "V2",
};
// Get the script address (0 = testnet, 1 = mainnet)
const scriptAddress = resolvePlutusScriptAddress(script, 0);
```
**What to expect:** A script address where marketplace UTxOs are stored.
### Step 2: Define the datum structure
The datum stores sale information:
```typescript
import { resolvePaymentKeyHash } from "@meshsdk/core";
function createListingDatum(
sellerAddress: string,
priceLovelace: string,
policyId: string,
assetName: string
): Data {
return {
alternative: 0,
fields: [
resolvePaymentKeyHash(sellerAddress), // seller pubkey hash
priceLovelace, // price in lovelace
policyId, // NFT policy ID
assetName, // NFT asset name (hex)
],
};
}
```
**What to expect:** A datum constructor for marketplace listings.
### Step 3: List an asset for sale
Send the NFT to the script address with the listing datum:
```typescript
import { MeshTxBuilder, KoiosProvider, resolvePaymentKeyHash } from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";
async function listNFT(
policyId: string,
assetName: string,
priceLovelace: string
) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const sellerAddress = (await wallet.getUsedAddresses())[0];
// Create the listing datum
const datum = createListingDatum(
sellerAddress,
priceLovelace,
policyId,
assetName
);
const assetUnit = policyId + assetName;
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
// Send NFT to script with datum
.txOut(scriptAddress, [{ unit: assetUnit, quantity: "1" }])
.txOutDatumHashValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log("Listed NFT:", txHash);
return txHash;
}
```
**What to expect:** The NFT is locked at the script address with sale information in the datum.
### Step 4: Find a listing UTxO
Helper function to find a specific listing:
```typescript
import { resolveDataHash } from "@meshsdk/core";
async function findListingUtxo(
scriptAddress: string,
assetUnit: string,
datum: Data
) {
const provider = new KoiosProvider("preprod");
const utxos = await provider.fetchAddressUTxOs(scriptAddress, assetUnit);
if (utxos.length === 0) {
throw new Error("No listing found for this asset");
}
// Match by datum hash
const dataHash = resolveDataHash(datum);
const listingUtxo = utxos.find(
(utxo) => utxo.output.dataHash === dataHash
);
if (!listingUtxo) {
throw new Error("Listing not found with matching datum");
}
return listingUtxo;
}
```
**What to expect:** A function to locate specific marketplace listings.
### Step 5: Cancel a listing
Only the seller can cancel. Use redeemer alternative 1:
```typescript
async function cancelListing(
policyId: string,
assetName: string,
priceLovelace: string
) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const sellerAddress = (await wallet.getUsedAddresses())[0];
const collateral = await wallet.getCollateral();
// Recreate the original datum
const datum = createListingDatum(
sellerAddress,
priceLovelace,
policyId,
assetName
);
const assetUnit = policyId + assetName;
const listingUtxo = await findListingUtxo(scriptAddress, assetUnit, datum);
// Cancel redeemer (alternative 1)
const redeemer: Data = { alternative: 1, fields: [] };
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(listingUtxo.input.txHash, listingUtxo.input.outputIndex)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txInScript(script.code)
// Return NFT to seller
.txOut(sellerAddress, listingUtxo.output.amount)
// Seller must sign
.requiredSignerHash(resolvePaymentKeyHash(sellerAddress))
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Cancelled listing:", txHash);
return txHash;
}
```
**What to expect:** The NFT returns to the seller's wallet.
### Step 6: Purchase a listed asset
Buyer pays the seller and receives the NFT. Use redeemer alternative 0:
```typescript
async function purchaseNFT(
sellerAddress: string,
policyId: string,
assetName: string,
priceLovelace: string
) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const buyerAddress = (await wallet.getUsedAddresses())[0];
const collateral = await wallet.getCollateral();
// Reconstruct the listing datum
const datum = createListingDatum(
sellerAddress,
priceLovelace,
policyId,
assetName
);
const assetUnit = policyId + assetName;
const listingUtxo = await findListingUtxo(scriptAddress, assetUnit, datum);
// Buy redeemer (alternative 0)
const redeemer: Data = { alternative: 0, fields: [] };
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(listingUtxo.input.txHash, listingUtxo.input.outputIndex)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txInScript(script.code)
// Send NFT to buyer
.txOut(buyerAddress, [{ unit: assetUnit, quantity: "1" }])
// Pay seller
.txOut(sellerAddress, [{ unit: "lovelace", quantity: priceLovelace }])
// Buyer must sign
.requiredSignerHash(resolvePaymentKeyHash(buyerAddress))
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Purchased NFT:", txHash);
return txHash;
}
```
**What to expect:** The buyer receives the NFT and the seller receives payment.
### Step 7: Update listing price
Redeem the UTxO and re-list with a new price:
```typescript
async function updateListingPrice(
policyId: string,
assetName: string,
currentPrice: string,
newPrice: string
) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const sellerAddress = (await wallet.getUsedAddresses())[0];
const collateral = await wallet.getCollateral();
// Current listing datum
const currentDatum = createListingDatum(
sellerAddress,
currentPrice,
policyId,
assetName
);
// New listing datum with updated price
const newDatum = createListingDatum(
sellerAddress,
newPrice,
policyId,
assetName
);
const assetUnit = policyId + assetName;
const listingUtxo = await findListingUtxo(scriptAddress, assetUnit, currentDatum);
// Update uses cancel redeemer (alternative 1)
const redeemer: Data = { alternative: 1, fields: [] };
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(listingUtxo.input.txHash, listingUtxo.input.outputIndex)
.txInDatumValue(currentDatum)
.txInRedeemerValue(redeemer)
.txInScript(script.code)
// Re-list at script with new datum
.txOut(scriptAddress, [{ unit: assetUnit, quantity: "1" }])
.txOutDatumHashValue(newDatum)
.requiredSignerHash(resolvePaymentKeyHash(sellerAddress))
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Updated listing price:", txHash);
return txHash;
}
```
**What to expect:** The listing is updated with the new price while remaining at the script address.
## Complete Example
Here is a complete React component with all marketplace actions:
```typescript
import { useState } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
import {
MeshTxBuilder,
KoiosProvider,
resolvePaymentKeyHash,
resolveDataHash,
resolvePlutusScriptAddress,
} from "@meshsdk/core";
import type { PlutusScript, Data, UTxO } from "@meshsdk/core";
// Your marketplace script
const script: PlutusScript = {
code: "590795...", // Your compiled script
version: "V2",
};
const scriptAddress = resolvePlutusScriptAddress(script, 0);
export default function Marketplace() {
const { wallet, connected } = useWallet();
const [loading, setLoading] = useState(false);
// Implement list, cancel, purchase, update functions here
// using the patterns shown above
return (
NFT Marketplace
{connected && (
{/* Add more action buttons */}
)}
);
}
```
## Next Steps
* Write custom validators with Aiken
* Build a vesting contract
* Advanced transaction patterns
## Troubleshooting
### Script validation failed
**Cause:** Datum or redeemer does not match contract expectations.
**Solution:** Ensure the datum fields match your contract's expected structure. Check that the redeemer alternative matches the action (0 for buy, 1 for cancel in this example).
### UTxO not found
**Cause:** The listing was already purchased or cancelled.
**Solution:** Verify the listing exists before building the transaction. Implement a refresh mechanism in your UI.
### Collateral required error
**Cause:** No collateral set in the wallet.
**Solution:** Enable collateral in wallet settings. Most wallets require at least 5 ADA:
```typescript
const collateral = await wallet.getCollateral();
if (collateral.length === 0) {
throw new Error("Please set collateral in your wallet");
}
```
### Authorization failed
**Cause:** The signer does not match the datum's seller address.
**Solution:** Ensure `requiredSignerHash` matches the original seller for cancel/update operations.
## References
* [Cardano eUTXO Model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/) - Understanding the extended UTXO model that enables deterministic smart contracts
* [Aiken Smart Contract Language](https://aiken-lang.org/) - Official documentation for writing Cardano validators
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources and smart contract guides
## Related Links
* Getting Started with Aiken
* Vesting Contract Guide
* Transaction Builder API
# Run Standalone Cardano Scripts with TypeScript
URL: /guides/standalone
Execute TypeScript scripts directly to interact with Cardano using Mesh SDK. Build, sign, and submit transactions without a framework.
***
title: "Run Standalone Cardano Scripts with TypeScript"
description: "Execute TypeScript scripts directly to interact with Cardano using Mesh SDK. Build, sign, and submit transactions without a framework."
-----------------------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
Standalone scripts let you interact with Cardano without a web framework. This is ideal for testing, automation, backend services, and quick prototypes. The Mesh SDK provides a TypeScript-first interface to the [Cardano blockchain](https://developers.cardano.org), handling transaction construction, signing, and submission.
### What you will build
* A minimal TypeScript project that runs directly with `tsx`
* A script that creates a wallet and sends a transaction
* Zero-config TypeScript execution
### Prerequisites
* Node.js 18+ installed
* A Blockfrost API key (free tier available)
* A funded Cardano wallet (testnet or mainnet)
### Time to complete
15 minutes
## Quick Start
Clone the template repository for the fastest setup:
```bash
git clone https://github.com/MeshJS/standalone-template
cd standalone-template
npm install
```
Add your credentials to `.env` and run:
```bash
npm run dev
```
## Step-by-Step Setup
### Step 1: Create the project
Create a new directory and initialize the project:
```bash
mkdir mesh-script
cd mesh-script
npm init -y
```
**What to expect:** A `package.json` file is created.
### Step 2: Configure package.json
Update `package.json` with ES module settings:
```json
{
"name": "mesh-script",
"type": "module",
"scripts": {
"dev": "tsx index.ts"
},
"dependencies": {}
}
```
**What to expect:** The project is configured to use ES modules and run TypeScript directly.
### Step 3: Install dependencies
Install the required packages:
```bash
npm install tsx @meshsdk/core
```
* `tsx` - Runs TypeScript files directly without compilation
* `@meshsdk/core` - Mesh SDK core functionality
**What to expect:** Both packages install successfully.
### Step 4: Create the script
Create `index.ts`:
```typescript
import {
BlockfrostProvider,
MeshTxBuilder,
} from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Configure the blockchain provider
const provider = new BlockfrostProvider("YOUR_BLOCKFROST_KEY");
// Initialize wallet with mnemonic
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: 0, // 0 = testnet, 1 = mainnet
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
mnemonic: [
"your", "twelve", "word", "mnemonic",
"phrase", "goes", "here", "replace",
"with", "actual", "words", "now",
],
});
// Get wallet info
const utxos = await wallet.getUtxosMesh();
const changeAddress = await wallet.getChangeAddressBech32();
console.log("Wallet address:", changeAddress);
console.log("UTxO count:", utxos.length);
// Build and send a transaction
const txBuilder = new MeshTxBuilder({
fetcher: provider,
});
const recipientAddress = "addr_test1qp2k7wnshzngpqw0xmy33hvexw4aeg60yr79x3yeeqt3s2uvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysdp6yv3";
const unsignedTx = await txBuilder
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "1000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Transaction submitted:", txHash);
```
**What to expect:** A TypeScript file that sends 1 ADA to a recipient address.
### Step 5: Configure credentials
Before running, replace the placeholder values:
1. Get a Blockfrost API key from [blockfrost.io](https://blockfrost.io/)
2. Generate a wallet mnemonic using `generateMnemonic()` from `@meshsdk/core` or use an existing one
3. Fund the wallet using the [Cardano testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet)
**What to expect:** Your script is configured with valid credentials.
### Step 6: Run the script
Execute the script:
```bash
npm run dev
```
**What to expect:** The console displays:
```
Wallet address: addr_test1q...
UTxO count: 3
Transaction submitted: abc123def456...
```
## Complete Example
Here is the complete `index.ts` with environment variables:
```typescript
import {
BlockfrostProvider,
MeshTxBuilder,
} from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Load from environment variables for security
const BLOCKFROST_KEY = process.env.BLOCKFROST_KEY!;
const MNEMONIC = process.env.MNEMONIC!;
if (!BLOCKFROST_KEY || !MNEMONIC) {
console.error("Missing BLOCKFROST_KEY or MNEMONIC environment variables");
process.exit(1);
}
const provider = new BlockfrostProvider(BLOCKFROST_KEY);
async function main() {
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
mnemonic: MNEMONIC.split(" "),
});
const utxos = await wallet.getUtxosMesh();
const changeAddress = await wallet.getChangeAddressBech32();
console.log("Wallet address:", changeAddress);
console.log("Available UTxOs:", utxos.length);
if (utxos.length === 0) {
console.error("No UTxOs available. Fund the wallet first.");
process.exit(1);
}
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const recipientAddress = "addr_test1qp2k7wnshzngpqw0xmy33hvexw4aeg60yr79x3yeeqt3s2uvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysdp6yv3";
const unsignedTx = await txBuilder
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "1000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Transaction submitted:", txHash);
console.log(`View on explorer: https://preprod.cardanoscan.io/transaction/${txHash}`);
}
main().catch(console.error);
```
Run with environment variables:
```bash
BLOCKFROST_KEY=your_key MNEMONIC="your twelve word phrase" npm run dev
```
## Next Steps
* Learn transaction building options - Multiple outputs, metadata, minting
* Mint NFTs server-side - Create tokens without a browser
* Explore Headless Wallet options - Private keys, mnemonic
* Interact with smart contracts - Lock and unlock funds
## Troubleshooting
### No UTxOs available
**Cause:** The wallet has no funds.
**Solution:** Fund the wallet using the [Cardano testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet) or transfer ADA from another wallet.
### Invalid Blockfrost key
**Cause:** The API key is incorrect or for the wrong network.
**Solution:** Verify the key at [blockfrost.io](https://blockfrost.io/). Use a Preprod key for testnet (`networkId: 0`) or Mainnet key for mainnet (`networkId: 1`).
### Transaction failed
**Cause:** Various issues like insufficient funds, invalid addresses, or network problems.
**Solution:** Check the error message. Common fixes:
* Ensure you have enough ADA (at least 2 ADA for most transactions)
* Verify the recipient address is valid
* Wait and retry if the network is congested
### TypeScript errors
**Cause:** Type mismatches or missing dependencies.
**Solution:** Ensure you have the latest `@meshsdk/core` installed:
```bash
npm install @meshsdk/core@latest
```
## References
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources, SDKs, and getting started guides
* [Cardano eUTXO Model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/) - Understanding Cardano's transaction model
* [MeshJS GitHub Repository](https://github.com/MeshJS/mesh) - Mesh SDK source code and contributions
## Related Links
* Complete template on GitHub
* Transaction Builder API
* Headless Wallet API
* Blockchain Providers
# Build a Vesting Smart Contract
URL: /guides/vesting
Implement a time-locked vesting contract using Aiken and Mesh SDK. Lock funds with scheduled withdrawal.
***
title: "Build a Vesting Smart Contract"
description: "Implement a time-locked vesting contract using Aiken and Mesh SDK. Lock funds with scheduled withdrawal."
-----------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
import { HowToSchema } from "@/components/seo";
## Overview
A vesting contract locks funds until a specified time, then allows a beneficiary to withdraw. This is commonly used for employee compensation, token distributions, and escrow arrangements. This contract runs on Cardano's [extended UTXO (eUTXO) model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/), which enables deterministic script execution and predictable transaction fees.
### What you will build
* An Aiken validator with time-based release
* Deposit transactions that lock funds
* Withdrawal transactions after the lock period
### What you will learn
* Cardano's time handling with validity intervals
* Owner vs beneficiary access patterns
* Building time-constrained transactions
### Prerequisites
* Aiken CLI installed (see Aiken guide)
* Next.js application with Mesh SDK
* Understanding of datums and redeemers
### Time to complete
60 minutes
## Quick Start
Clone the complete example:
```bash
git clone https://github.com/MeshJS/examples
cd examples/aiken-vesting
```
## Step-by-Step Guide
### Step 1: Define the vesting datum
Create the datum type that configures vesting parameters:
```rust
// lib/vesting/types.ak
pub type VestingDatum {
/// POSIX time in milliseconds when funds unlock
lock_until: Int,
/// Owner's public key hash (can withdraw anytime)
owner: ByteArray,
/// Beneficiary's public key hash (can withdraw after lock_until)
beneficiary: ByteArray,
}
```
The datum contains:
* `lock_until` - POSIX timestamp (ms) when funds become available
* `owner` - Can withdraw at any time (emergency access)
* `beneficiary` - Can withdraw only after the lock expires
**What to expect:** A type definition for vesting configuration.
### Step 2: Write the validator
Create `validators/vesting.ak`:
```rust
use aiken/transaction.{ScriptContext, Spend}
use vesting/types.{VestingDatum}
// Helper to check if a key signed the transaction
fn key_signed(signatories: List, key: ByteArray) -> Bool {
list.has(signatories, key)
}
// Helper to check if transaction is valid after a certain time
fn valid_after(validity_range, lock_until: Int) -> Bool {
when validity_range.lower_bound.bound_type is {
Finite(tx_earliest_time) -> tx_earliest_time > lock_until
_ -> False
}
}
validator {
pub fn vesting(datum: VestingDatum, _redeemer: Data, ctx: ScriptContext) {
when ctx.purpose is {
Spend(_) -> or {
// Owner can always withdraw
key_signed(ctx.transaction.extra_signatories, datum.owner),
// Beneficiary can withdraw after lock period
and {
key_signed(ctx.transaction.extra_signatories, datum.beneficiary),
valid_after(ctx.transaction.validity_range, datum.lock_until),
},
}
_ -> False
}
}
}
```
The validator allows withdrawal when:
1. The **owner** signs (can withdraw anytime), OR
2. The **beneficiary** signs AND current time is after `lock_until`
**What to expect:** A validator file with time-based access control.
### Step 3: Write tests
Create `validators/vesting_test.ak`:
```rust
use vesting
test owner_can_withdraw_early() {
// Owner should be able to withdraw before lock_until
True
}
test beneficiary_cannot_withdraw_early() {
// Beneficiary should NOT be able to withdraw before lock_until
True
}
test beneficiary_can_withdraw_after_lock() {
// Beneficiary should be able to withdraw after lock_until
True
}
```
Run the tests:
```bash
aiken check
```
**What to expect:** All tests pass (implement actual test logic for production).
### Step 4: Compile the contract
Build the Aiken project:
```bash
aiken build
```
**What to expect:** A `plutus.json` blueprint file is generated.
### Step 5: Set up the frontend
Create a contract helper file `src/lib/vesting.ts`:
```typescript
import {
resolvePlutusScriptAddress,
resolvePaymentKeyHash,
deserializeAddress,
} from "@meshsdk/core";
import type { PlutusScript, Data } from "@meshsdk/core";
import cbor from "cbor";
import plutusBlueprint from "@/data/plutus.json";
// Load compiled contract
const scriptCbor = cbor
.encode(Buffer.from(plutusBlueprint.validators[0].compiledCode, "hex"))
.toString("hex");
export const script: PlutusScript = {
code: scriptCbor,
version: "V2",
};
export const scriptAddress = resolvePlutusScriptAddress(script, 0);
// Create vesting datum
export function createVestingDatum(
lockUntilMs: number,
ownerAddress: string,
beneficiaryAddress: string
): Data {
const { pubKeyHash: ownerHash } = deserializeAddress(ownerAddress);
const { pubKeyHash: beneficiaryHash } = deserializeAddress(beneficiaryAddress);
return {
alternative: 0,
fields: [lockUntilMs, ownerHash, beneficiaryHash],
};
}
```
**What to expect:** Helper functions for working with the vesting contract.
### Step 6: Deposit funds
Create the deposit transaction:
```typescript
import { MeshTxBuilder, KoiosProvider } from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";
import { script, scriptAddress, createVestingDatum } from "@/lib/vesting";
async function depositFunds(
amountLovelace: string,
lockDurationMinutes: number,
beneficiaryAddress: string
) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const ownerAddress = (await wallet.getUsedAddresses())[0];
// Calculate lock time
const lockUntilMs = Date.now() + lockDurationMinutes * 60 * 1000;
// Create datum
const datum = createVestingDatum(lockUntilMs, ownerAddress, beneficiaryAddress);
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
// Lock funds at script with inline datum
.txOut(scriptAddress, [{ unit: "lovelace", quantity: amountLovelace }])
.txOutInlineDatumValue(datum)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log("Deposited funds:", txHash);
console.log("Unlocks at:", new Date(lockUntilMs).toISOString());
return { txHash, lockUntilMs };
}
```
**What to expect:** Funds are locked at the script address with vesting parameters.
### Step 7: Withdraw funds
Create the withdrawal transaction:
```typescript
import {
MeshTxBuilder,
KoiosProvider,
deserializeAddress,
unixTimeToEnclosingSlot,
SLOT_CONFIG_NETWORK,
} from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";
import { script, scriptAddress, createVestingDatum } from "@/lib/vesting";
async function withdrawFunds(depositTxHash: string) {
const provider = new KoiosProvider("preprod");
const { wallet } = useWallet();
// Find the vesting UTxO
const scriptUtxos = await provider.fetchUTxOs(depositTxHash);
const vestingUtxo = scriptUtxos[0];
if (!vestingUtxo) {
throw new Error("Vesting UTxO not found");
}
// Parse the inline datum
const plutusData = vestingUtxo.output.plutusData;
// Extract lock_until from datum
const lockUntilMs = plutusData.fields[0].int;
// Check if we can withdraw
const now = Date.now();
if (now < lockUntilMs) {
throw new Error(`Cannot withdraw yet. Unlocks in ${Math.ceil((lockUntilMs - now) / 60000)} minutes`);
}
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const collateral = await wallet.getCollateral();
const { pubKeyHash } = deserializeAddress(changeAddress);
// Calculate validity interval (must be after lock_until)
const invalidBefore = unixTimeToEnclosingSlot(
Math.max(lockUntilMs, now - 15000),
SLOT_CONFIG_NETWORK.preprod
) + 1;
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const unsignedTx = await txBuilder
.spendingPlutusScriptV2()
.txIn(
vestingUtxo.input.txHash,
vestingUtxo.input.outputIndex,
vestingUtxo.output.amount,
scriptAddress
)
.spendingReferenceTxInInlineDatumPresent()
.spendingReferenceTxInRedeemerValue("") // Empty redeemer
.txInScript(script.code)
.txOut(changeAddress, [])
.txInCollateral(
collateral[0].input.txHash,
collateral[0].input.outputIndex,
collateral[0].output.amount,
collateral[0].output.address
)
.invalidBefore(invalidBefore)
.requiredSignerHash(pubKeyHash)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Withdrawn funds:", txHash);
return txHash;
}
```
**What to expect:** Funds are transferred to the beneficiary's wallet.
## Complete Example
Here is a complete React component:
```typescript
import { useState } from "react";
import { CardanoWallet, useWallet } from "@meshsdk/react";
export default function VestingApp() {
const { wallet, connected } = useWallet();
const [depositTxHash, setDepositTxHash] = useState("");
const [loading, setLoading] = useState(false);
async function handleDeposit() {
setLoading(true);
try {
const beneficiary = "addr_test1..."; // Beneficiary address
const result = await depositFunds("10000000", 5, beneficiary); // 10 ADA, 5 min lock
setDepositTxHash(result.txHash);
} finally {
setLoading(false);
}
}
async function handleWithdraw() {
setLoading(true);
try {
await withdrawFunds(depositTxHash);
} finally {
setLoading(false);
}
}
return (
Vesting Contract
{connected && (
{depositTxHash && (
<>
Deposit TX: {depositTxHash}
>
)}
)}
);
}
```
## Next Steps
* View a successful withdrawal transaction
* Full source code
* More contract patterns
## Troubleshooting
### Cannot withdraw yet
**Cause:** Current time is before `lock_until`.
**Solution:** Wait for the lock period to expire. The `invalidBefore` constraint must be after `lock_until` for the transaction to be valid.
### Validity interval error
**Cause:** The `invalidBefore` slot is in the past or incorrectly calculated.
**Solution:** Ensure you use the correct slot config for your network:
```typescript
import { SLOT_CONFIG_NETWORK } from "@meshsdk/core";
// For preprod testnet
const slotConfig = SLOT_CONFIG_NETWORK.preprod;
// For mainnet
const slotConfig = SLOT_CONFIG_NETWORK.mainnet;
```
### Owner vs beneficiary withdrawal
**Cause:** Wrong signer for the withdrawal type.
**Solution:** The owner can withdraw anytime by signing. The beneficiary must:
1. Sign the transaction
2. Set `invalidBefore` to a slot after `lock_until`
### Inline datum not found
**Cause:** The UTxO was created with a datum hash instead of inline datum.
**Solution:** Use `txOutInlineDatumValue` for deposits:
```typescript
.txOut(scriptAddress, amount)
.txOutInlineDatumValue(datum) // Use inline datum
// NOT .txOutDatumHashValue(datum)
```
## References
* [Cardano eUTXO Model](https://docs.cardano.org/about-cardano/learn/eutxo-explainer/) - How Cardano's transaction model enables deterministic smart contracts
* [Aiken Smart Contract Language](https://aiken-lang.org/) - Official documentation for the Aiken language used in this guide
* [Cardano Developer Portal](https://developers.cardano.org) - Official Cardano development resources
* [CIP-0057: Plutus Blueprint](https://cips.cardano.org/cip/CIP-57) - The standard for compiled Plutus contract blueprints
## Related Links
* Complete source code
* Getting Started with Aiken
* Smart Contract Transactions
# Hydra - Layer 2 Scaling for Cardano
URL: /hydra
Build fast, low-cost applications with Hydra Head protocol. Process thousands of transactions per second while maintaining Cardano's security guarantees.
***
title: "Hydra - Layer 2 Scaling for Cardano"
description: "Build fast, low-cost applications with Hydra Head protocol. Process thousands of transactions per second while maintaining Cardano's security guarantees."
icon: "icons/hydra.svg"
-----------------------
import { linksHydra } from "@/data/links-hydra";
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
## Overview
Hydra is a Layer 2 scaling solution for Cardano that enables near-instant, low-cost transactions between participants. By opening a "Hydra Head" (a state channel), you can process thousands of transactions per second while maintaining the security guarantees of the Cardano mainnet.
### When to use Hydra
* **High-frequency transactions**: Gaming, micropayments, or real-time applications requiring sub-second finality
* **Cost-sensitive applications**: Reduce transaction fees by batching settlements on Layer 1
* **Private transactions**: Keep transaction details off-chain until final settlement
* **Interactive applications**: Multi-party protocols requiring rapid state updates
### Key benefits
| Feature | Description |
| -------------- | ------------------------------------------------------------- |
| **Speed** | Near-instant transaction confirmation within the Head |
| **Low cost** | Minimal fees inside the Head; only pay L1 fees for open/close |
| **Security** | Funds are always secured by Cardano's consensus |
| **Isomorphic** | Use the same transaction format as Layer 1 |
## Quick start
Install the Mesh Hydra package and connect to a running Hydra node:
```typescript
import { HydraProvider, HydraInstance } from "@meshsdk/hydra";
import { BlockfrostProvider } from "@meshsdk/core";
// Initialize the blockchain provider for L1 operations
const blockfrost = new BlockfrostProvider("");
// Connect to your Hydra node
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
// Create a Hydra instance for head operations
const hydraInstance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
// Connect and initialize the head
await hydraProvider.connect();
await hydraProvider.init();
```
## Documentation
## How Hydra works
1. **Initialize**: Participants agree to open a Hydra Head
2. **Commit**: Each participant commits funds from Layer 1 to the Head
3. **Transact**: Process unlimited transactions instantly within the Head
4. **Close**: Submit the final state back to Layer 1
5. **Fanout**: Distribute funds according to the final Head state
## Related resources
* [Hydra Instance API](/docs/hydra/instance) - Detailed API reference for HydraInstance
* [End-to-end Tutorial](/docs/hydra/tutorial) - Complete walkthrough from setup to settlement
* [Hydra Protocol Documentation](https://hydra.family/head-protocol/) - Official Hydra protocol specifications
* [Cardano Providers](/providers) - Configure blockchain providers for L1 operations
# Hydra Instance API
URL: /hydra/instance
Complete API reference for HydraInstance - commit funds, manage UTxOs, and interact with Hydra Heads programmatically.
***
title: "Hydra Instance API"
description: "Complete API reference for HydraInstance - commit funds, manage UTxOs, and interact with Hydra Heads programmatically."
-------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
## Overview
The `HydraInstance` class provides methods to interact with a Hydra Head after initialization. Use it to commit funds, manage incremental deposits, and handle blueprint transactions for advanced use cases like script UTxOs.
### When to use HydraInstance
* Committing funds to a new or existing Hydra Head
* Managing incremental deposits to an open Head
* Handling complex transactions with script UTxOs
* Building custom commit workflows
## Prerequisites
Before using HydraInstance, ensure you have:
* A running Hydra node with API access
* A configured blockchain provider (Blockfrost, Maestro, etc.)
* A wallet with funds for committing to the Head
* The `@meshsdk/hydra` package installed
## Quick start
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { BlockfrostProvider } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Set up providers
const blockfrost = new BlockfrostProvider("");
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
// Create the Hydra instance
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
// Connect to the Hydra node
await hydraProvider.connect();
```
## Setup
### Installation
```bash
npm install @meshsdk/hydra @meshsdk/core
```
### Configuration
The `HydraInstance` constructor accepts the following options:
| Parameter | Type | Description |
| ----------- | --------------- | ----------------------------------------------- |
| `provider` | `HydraProvider` | Connected HydraProvider instance |
| `fetcher` | `IFetcher` | Blockchain provider for fetching UTxOs |
| `submitter` | `ISubmitter` | Blockchain provider for submitting transactions |
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { BlockfrostProvider } from "@meshsdk/core";
const blockfrost = new BlockfrostProvider("");
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
```
## API reference
### commitEmpty()
Request an empty commit transaction to open the Head without committing funds. Use this when you only want to receive funds on Layer 2.
**Returns:** `Promise` - Transaction CBOR hex ready for signing
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { MeshCardanoHeadlessWallet } from "@meshsdk/wallet";
const commit = await instance.commitEmpty();
const signedTx = await wallet.signTx(commit, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Empty commit submitted:", txHash);
```
### commitFunds(txHash, outputIndex)
Commit a specific UTxO to the Hydra Head. The committed funds become available on Layer 2 after the Head opens.
**Parameters:**
| Name | Type | Description |
| ------------- | -------- | ------------------------------------ |
| `txHash` | `string` | Transaction hash containing the UTxO |
| `outputIndex` | `number` | Index of the output to commit |
**Returns:** `Promise` - Transaction CBOR hex ready for partial signing
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { MeshCardanoHeadlessWallet } from "@meshsdk/wallet";
const txHash = "abc123...";
const outputIndex = 0;
const commitTx = await instance.commitFunds(txHash, outputIndex);
const signedTx = await wallet.signTx(commitTx, true); // partial sign
const commitTxHash = await wallet.submitTx(signedTx);
console.log("Funds committed:", commitTxHash);
```
### commitBlueprint(txHash, outputIndex, hydraTransaction)
Commit a Cardano transaction blueprint to the Hydra Head. Use this for advanced scenarios like committing script UTxOs.
**Parameters:**
| Name | Type | Description |
| ------------------ | -------- | ---------------------------- |
| `txHash` | `string` | Transaction hash of the UTxO |
| `outputIndex` | `number` | Index of the output |
| `hydraTransaction` | `object` | Blueprint transaction object |
**hydraTransaction object:**
| Property | Type | Description |
| ------------- | -------- | -------------------------------------- |
| `cborHex` | `string` | Unsigned transaction CBOR |
| `description` | `string` | Human-readable description |
| `type` | `string` | Transaction era (e.g., "Tx ConwayEra") |
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet } from "@meshsdk/wallet";
// Build the blueprint transaction
const txBuilder = new MeshTxBuilder({
fetcher: blockfrost,
verbose: true,
});
const unsignedTx = await txBuilder
.txIn(txHash, outputIndex)
.setFee("0")
.changeAddress(address)
.selectUtxosFrom(utxos)
.complete();
// Commit the blueprint
const commitTx = await instance.commitBlueprint(txHash, outputIndex, {
cborHex: unsignedTx,
description: "Commit script UTxO",
type: "Tx ConwayEra",
});
const signedTx = await wallet.signTx(commitTx, false);
const commitTxHash = await wallet.submitTx(signedTx);
console.log("Blueprint committed:", commitTxHash);
```
### incrementalCommitFunds(txHash, outputIndex)
Add additional UTxOs to an already open Hydra Head. The deposit period is configured by the `--deposit-period` hydra-node parameter.
**Parameters:**
| Name | Type | Description |
| ------------- | -------- | ------------------------------------ |
| `txHash` | `string` | Transaction hash containing the UTxO |
| `outputIndex` | `number` | Index of the output to commit |
**Returns:** `Promise` - Transaction CBOR hex ready for signing
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
const txHash = "def456...";
const outputIndex = 0;
const incrementalTx = await instance.incrementalCommitFunds(txHash, outputIndex);
const signedTx = await wallet.signTx(incrementalTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Incremental commit submitted:", txHash);
```
For more details on incremental commits, see the Hydra documentation.
### incrementalBlueprintCommit(txHash, outputIndex, hydraTransaction)
Add a blueprint transaction to an open Hydra Head incrementally.
**Parameters:**
| Name | Type | Description |
| ------------------ | -------- | ---------------------------- |
| `txHash` | `string` | Transaction hash of the UTxO |
| `outputIndex` | `number` | Index of the output |
| `hydraTransaction` | `object` | Blueprint transaction object |
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
const incrementalTx = await instance.incrementalBlueprintCommit(
txHash,
outputIndex,
{
cborHex: unsignedTx,
description: "Incremental blueprint commit",
type: "Tx ConwayEra",
}
);
const signedTx = await wallet.signTx(incrementalTx);
const txHash = await wallet.submitTx(signedTx);
```
## Complete example
This example demonstrates the full workflow of connecting to a Hydra node and committing funds when the Head is initializing:
```typescript
import { HydraInstance, HydraProvider } from "@meshsdk/hydra";
import { BlockfrostProvider } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Initialize providers
const blockfrost = new BlockfrostProvider("");
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
// Set up wallet
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: blockfrost,
submitter: blockfrost,
mnemonic: ["your", "mnemonic", "words", "..."],
});
// Connect to Hydra
await hydraProvider.connect();
await hydraProvider.init();
// Listen for Head status and commit when initializing
hydraProvider.onMessage(async (message) => {
const status =
message.tag === "Greetings"
? { headStatus: message.headStatus }
: { tag: message.tag };
if (
status.tag === "HeadIsInitializing" ||
status.headStatus === "Initializing"
) {
// Get a UTxO to commit
const utxos = await wallet.getUtxosMesh();
const utxoToCommit = utxos[0];
// Commit funds to the Head
const commitTx = await instance.commitFunds(
utxoToCommit.input.txHash,
utxoToCommit.input.outputIndex
);
const signedTx = await wallet.signTx(commitTx, true);
const txHash = await wallet.submitTx(signedTx);
console.log("Committed funds to Head:", txHash);
}
if (status.tag === "HeadIsOpen") {
console.log("Head is now open! Ready for L2 transactions.");
}
});
```
## Troubleshooting
### "Connection refused" when connecting to Hydra node
Ensure your Hydra node is running and the API port is accessible:
```bash
# Check if Hydra node is running
curl http://localhost:4001/protocol-parameters
```
### Commit transaction fails with "UTxO not found"
The UTxO you are trying to commit may have been spent. Fetch fresh UTxOs before committing:
```typescript
const utxos = await blockfrost.fetchAddressUTxOs(address);
const validUtxo = utxos[0];
```
### "Head is not initializing" error
You can only commit funds during the `HeadIsInitializing` phase. Check the Head status:
```typescript
hydraProvider.onMessage((message) => {
console.log("Current status:", message.tag || message.headStatus);
});
```
### Partial signing issues
When committing funds, use partial signing (`true` as second parameter):
```typescript
const signedTx = await wallet.signTx(commitTx, true); // Enable partial signing
```
## Related resources
* [End-to-end Tutorial](/docs/hydra/tutorial) - Complete walkthrough of Hydra Head lifecycle
* [Hydra Overview](/docs/hydra) - Introduction to Hydra and its benefits
* [Hydra Protocol Docs](https://hydra.family/head-protocol/docs/how-to/incremental-commit) - Official incremental commit documentation
* [Cardano Providers](/providers) - Configure blockchain providers
# End-to-End Hydra Tutorial
URL: /hydra/tutorial
Learn how to open a Hydra Head between two participants, execute Layer 2 transactions, and settle back to Cardano mainnet.
***
title: "End-to-End Hydra Tutorial"
description: "Learn how to open a Hydra Head between two participants, execute Layer 2 transactions, and settle back to Cardano mainnet."
-----------------------------------------------------------------------------------------------------------------------------------------
import Link from "fumadocs-core/link";
## Overview
This tutorial walks you through the complete lifecycle of a Hydra Head on Cardano's preprod testnet. You will learn how to:
* Set up Hydra nodes for two participants
* Open a Hydra Head and commit funds
* Execute instant transactions on Layer 2
* Close the Head and settle funds on Layer 1
This tutorial is adapted from the official Hydra documentation.
## Prerequisites
Before starting, ensure you have:
| Requirement | Description |
| ------------------ | ---------------------------------------------- |
| **Cardano node** | Running node with `cardano-cli` access |
| **Hydra node** | Installed and configured `hydra-node` binary |
| **Test ADA** | At least 100 tADA per participant on preprod |
| **Network access** | Two machines or terminals that can communicate |
| **Mesh SDK** | `@meshsdk/hydra` and `@meshsdk/core` packages |
### Installation
```bash
npm install @meshsdk/hydra @meshsdk/core
```
For Hydra node setup, see the official installation guide or use the Docker demo setup.
## Quick start
If you already have Hydra nodes running, connect to a Head with Mesh:
```typescript
import { HydraProvider, HydraInstance } from "@meshsdk/hydra";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
// Connect to Hydra
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
const blockfrost = new BlockfrostProvider("");
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
await hydraProvider.connect();
```
## Step 1: Generate keys and fund wallets
Each Hydra participant needs two key pairs:
1. **Cardano keys**: For Layer 1 operations and paying fees
2. **Hydra keys**: For signing snapshots within the Head
### Generate Cardano keys
Create keys for Alice:
```bash
mkdir -p credentials
# Node keys (for hydra-node identity and fees)
cardano-cli address key-gen \
--verification-key-file credentials/alice-node.vk \
--signing-key-file credentials/alice-node.sk
cardano-cli address build \
--payment-verification-key-file credentials/alice-node.vk \
--out-file credentials/alice-node.addr \
--testnet-magic 1
# Funds keys (for committing to the Head)
cardano-cli address key-gen \
--verification-key-file credentials/alice-funds.vk \
--signing-key-file credentials/alice-funds.sk
cardano-cli address build \
--payment-verification-key-file credentials/alice-funds.vk \
--out-file credentials/alice-funds.addr \
--testnet-magic 1
```
Create keys for Bob:
```bash
# Node keys
cardano-cli address key-gen \
--verification-key-file credentials/bob-node.vk \
--signing-key-file credentials/bob-node.sk
cardano-cli address build \
--payment-verification-key-file credentials/bob-node.vk \
--out-file credentials/bob-node.addr \
--testnet-magic 1
# Funds keys
cardano-cli address key-gen \
--verification-key-file credentials/bob-funds.vk \
--signing-key-file credentials/bob-funds.sk
cardano-cli address build \
--payment-verification-key-file credentials/bob-funds.vk \
--out-file credentials/bob-funds.addr \
--testnet-magic 1
```
### Generate Hydra keys
```bash
hydra-node gen-hydra-key --output-file credentials/alice-hydra
hydra-node gen-hydra-key --output-file credentials/bob-hydra
```
### Fund the wallets
Use the Cardano testnet faucet to fund the addresses:
| Address | Minimum Amount |
| ------------------ | -------------- |
| `alice-node.addr` | 30 tADA |
| `bob-node.addr` | 30 tADA |
| `alice-funds.addr` | Any amount |
| `bob-funds.addr` | Any amount |
### Configure protocol parameters
Fetch and modify the protocol parameters to eliminate fees inside the Head:
```bash
cardano-cli query protocol-parameters \
--testnet-magic 1 \
--socket-path "${CARDANO_NODE_SOCKET_PATH}" \
--out-file protocol-parameters.json
```
Edit `protocol-parameters.json` and set these values to `0`:
* `txFeeFixed`
* `txFeePerByte`
* `executionUnitPrices.priceMemory`
* `executionUnitPrices.priceSteps`
## Step 2: Configure Hydra nodes
Start the Hydra node for each participant with the correct configuration.
### Alice's node
```bash
hydra-node \
--node-id alice-node \
--api-host 0.0.0.0 \
--api-port 4001 \
--listen 172.16.239.10:5001 \
--monitoring-port 6001 \
--peer 172.16.239.20:5001 \
--hydra-scripts-tx-id \
--cardano-signing-key credentials/alice-node.sk \
--cardano-verification-key credentials/bob-node.vk \
--hydra-signing-key credentials/alice-hydra.sk \
--hydra-verification-key credentials/bob-hydra.vk \
--ledger-protocol-parameters protocol-parameters.json \
--testnet-magic 1 \
--node-socket "${CARDANO_NODE_SOCKET_PATH}" \
--contestation-period 300s
```
### Bob's node
```bash
hydra-node \
--node-id bob-node \
--api-host 0.0.0.0 \
--api-port 4002 \
--listen 172.16.239.20:5001 \
--monitoring-port 6001 \
--peer 172.16.239.10:5001 \
--hydra-scripts-tx-id \
--cardano-signing-key credentials/bob-node.sk \
--cardano-verification-key credentials/alice-node.vk \
--hydra-signing-key credentials/bob-hydra.sk \
--hydra-verification-key credentials/alice-hydra.vk \
--ledger-protocol-parameters protocol-parameters.json \
--testnet-magic 1 \
--node-socket "${CARDANO_NODE_SOCKET_PATH}" \
--contestation-period 300s
```
### Configuration reference
| Parameter | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--node-id` | Unique identifier for the node |
| `--api-port` | Port for the HTTP/WebSocket API |
| `--listen` | Address for peer-to-peer communication |
| `--peer` | Other participant's listen address |
| `--hydra-scripts-tx-id` | Reference to published Hydra scripts (see networks.json) |
| `--contestation-period` | Time window for contesting the final state |
For complete configuration options, see the Hydra configuration documentation.
## Step 3: Open a Hydra Head
### Connect with Mesh
```typescript
import { HydraProvider, HydraInstance } from "@meshsdk/hydra";
import { BlockfrostProvider } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
const blockfrost = new BlockfrostProvider("");
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001", // Alice's API
});
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
// Connect to the Hydra node
await hydraProvider.connect();
```
### Initialize the Head
```typescript
await hydraProvider.init();
```
### Commit funds
Set up your wallet and commit funds when the Head is initializing:
```typescript
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
const wallet = await MeshCardanoHeadlessWallet.fromCliKeys({
networkId: 0, // testnet
walletAddressType: AddressType.Base,
fetcher: blockfrost,
submitter: blockfrost,
paymentSkey: "credentials/alice-funds.sk",
});
// Listen for Head status changes
hydraProvider.onMessage(async (message) => {
const status =
message.tag === "Greetings"
? { headStatus: message.headStatus }
: { tag: message.tag };
if (
status.tag === "HeadIsInitializing" ||
status.headStatus === "Initializing"
) {
// Get UTxOs to commit
const utxos = await wallet.getUtxosMesh();
const utxo = utxos[0];
// Commit funds
const commitTx = await instance.commitFunds(
utxo.input.txHash,
utxo.input.outputIndex
);
const signedTx = await wallet.signTx(commitTx, true, false);
const txHash = await wallet.submitTx(signedTx);
console.log("Committed funds:", txHash);
}
if (status.tag === "HeadIsOpen") {
console.log("Head is open! Ready for L2 transactions.");
}
});
```
### Head status flow
| Status | Description |
| -------------------- | ------------------------------- |
| `HeadIsInitializing` | Head creation in progress |
| `Committed` | Participant has committed funds |
| `HeadIsOpen` | Head is open for transactions |
When all participants commit, the Head opens automatically.
## Step 4: Transact on Layer 2
With the Head open, you can execute transactions instantly with zero fees.
### Fetch UTxOs in the Head
```typescript
// Get all UTxOs in the Head
const allUtxos = await hydraProvider.fetchUTxOs();
// Get UTxOs for a specific address
const aliceUtxos = await hydraProvider.fetchAddressUTxOs(aliceAddress);
```
### Build and submit a transaction
Send ADA from Alice to Bob inside the Head:
```typescript
import { MeshTxBuilder } from "@meshsdk/core";
// Fetch protocol parameters from the Head
const protocolParams = await hydraProvider.fetchProtocolParameters();
// Get Alice's UTxOs
const aliceAddress = await wallet.getChangeAddressBech32();
const utxos = await hydraProvider.fetchAddressUTxOs(aliceAddress);
// Build the transaction
const txBuilder = new MeshTxBuilder({
fetcher: hydraProvider,
submitter: hydraProvider,
isHydra: true,
params: protocolParams,
});
const unsignedTx = await txBuilder
.txOut(bobAddress, [{ unit: "lovelace", quantity: "3000000" }])
.changeAddress(aliceAddress)
.selectUtxosFrom(utxos)
.setNetwork("preprod")
.complete();
// Sign and submit
const signedTx = await wallet.signTx(unsignedTx, false);
const txHash = await hydraProvider.submitTx(signedTx);
console.log("L2 transaction submitted:", txHash);
```
### Transaction status flow
| Status | Description |
| ------------------- | --------------------------------------- |
| `NewTx` | Transaction submitted to the Head |
| `TxValid` | All nodes validated the transaction |
| `SnapshotConfirmed` | New state confirmed by all participants |
If validation fails, you receive a `TxInvalid` message with the reason.
## Step 5: Close the Head
When you are done transacting, close the Head to settle funds on Layer 1.
### Initiate close
Any participant can close the Head:
```typescript
await hydraProvider.close();
```
### Contestation period
After closing, there is a contestation period (configured with `--contestation-period`) during which:
* Participants can contest with a more recent snapshot
* No new transactions can be submitted
* The final state is locked for settlement
### Fanout to Layer 1
After the contestation deadline, distribute the final state to Layer 1:
```typescript
await hydraProvider.fanout();
```
### Verify final balances
Check that funds are back on Layer 1:
```typescript
const aliceBalance = await blockfrost.fetchAddressUTxOs(aliceAddress);
const bobBalance = await blockfrost.fetchAddressUTxOs(bobAddress);
console.log("Alice L1 UTxOs:", aliceBalance);
console.log("Bob L1 UTxOs:", bobBalance);
```
## Complete example
Here is the full workflow in a single script:
```typescript
import { HydraProvider, HydraInstance } from "@meshsdk/hydra";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
async function main() {
// Setup providers
const blockfrost = new BlockfrostProvider("");
const hydraProvider = new HydraProvider({
httpUrl: "http://localhost:4001",
});
const instance = new HydraInstance({
provider: hydraProvider,
fetcher: blockfrost,
submitter: blockfrost,
});
// Setup wallet
const wallet = await MeshCardanoHeadlessWallet.fromCliKeys({
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: blockfrost,
submitter: blockfrost,
paymentSkey: "credentials/alice-funds.sk",
});
const aliceAddress = await wallet.getChangeAddressBech32();
const bobAddress = "addr_test1..."; // Bob's address
// Connect to Hydra
await hydraProvider.connect();
// Handle Head lifecycle
hydraProvider.onMessage(async (message) => {
switch (message.tag) {
case "HeadIsInitializing":
console.log("Head initializing, committing funds...");
const utxos = await wallet.getUtxosMesh();
const commitTx = await instance.commitFunds(
utxos[0].input.txHash,
utxos[0].input.outputIndex
);
const signedCommit = await wallet.signTx(commitTx, true, false);
await wallet.submitTx(signedCommit);
break;
case "HeadIsOpen":
console.log("Head is open, sending transaction...");
const pp = await hydraProvider.fetchProtocolParameters();
const l2Utxos = await hydraProvider.fetchAddressUTxOs(aliceAddress);
const txBuilder = new MeshTxBuilder({
fetcher: hydraProvider,
submitter: hydraProvider,
isHydra: true,
params: pp,
});
const unsignedTx = await txBuilder
.txOut(bobAddress, [{ unit: "lovelace", quantity: "5000000" }])
.changeAddress(aliceAddress)
.selectUtxosFrom(l2Utxos)
.setNetwork("preprod")
.complete();
const signedTx = await wallet.signTx(unsignedTx, false);
await hydraProvider.submitTx(signedTx);
break;
case "SnapshotConfirmed":
console.log("Transaction confirmed, closing Head...");
await hydraProvider.close();
break;
case "ReadyToFanout":
console.log("Contestation period ended, fanning out...");
await hydraProvider.fanout();
break;
case "HeadIsFinalized":
console.log("Head finalized! Funds are back on L1.");
break;
}
});
// Initialize the Head
await hydraProvider.init();
}
main().catch(console.error);
```
## Head lifecycle reference
| Phase | Status | Description |
| ---------- | -------------------- | ------------------------------ |
| Initialize | `HeadIsInitializing` | Head creation started |
| Commit | `Committed` | Funds committed by participant |
| Open | `HeadIsOpen` | Head open for transactions |
| Transact | `SnapshotConfirmed` | L2 transaction confirmed |
| Close | `HeadIsClosed` | Head closing initiated |
| Contest | `ReadyToFanout` | Contestation period ended |
| Fanout | `HeadIsFinalized` | Funds settled on L1 |
## Troubleshooting
### Hydra node won't start
Check that the Cardano node socket path is correct and the node is synced:
```bash
cardano-cli query tip --testnet-magic 1 --socket-path "${CARDANO_NODE_SOCKET_PATH}"
```
### Nodes can't connect to each other
Ensure firewall rules allow traffic on the listen ports (5001 in the examples) and verify the peer IP addresses are correct.
### Commit transaction rejected
Verify that:
* The UTxO exists and has not been spent
* The address matches your signing key
* You have enough ADA to cover the L1 transaction fee
### Transaction rejected in Head
Common reasons:
* Insufficient funds in the Head
* Invalid transaction format
* UTxO already spent in a previous transaction
Check the `TxInvalid` message for specific error details.
## Related resources
* [Hydra Instance API](/docs/hydra/instance) - Detailed API reference
* [Hydra Overview](/docs/hydra) - Introduction and architecture
* [Official Hydra Tutorial](https://hydra.family/head-protocol/docs/tutorial) - Full setup guide
* [Hydra Configuration](https://hydra.family/head-protocol/docs/configuration) - All node options
* [Cardano Providers](/providers) - Configure blockchain providers
# Midnight
URL: /midnight
Build privacy-preserving dApps with zero-knowledge proofs on the Midnight Network
***
title: "Midnight"
description: "Build privacy-preserving dApps with zero-knowledge proofs on the Midnight Network"
icon: "icons/midnight.svg"
--------------------------
import { linksMidnight } from "@/data/links-midnight";
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
Midnight is a zero-knowledge privacy network that enables you to build confidential smart contracts and transactions on Cardano. With Mesh SDK's Midnight integration, you can create privacy-preserving decentralized applications without deep cryptographic expertise.
## Why Midnight?
| Feature | Benefit |
| -------------------------------- | ------------------------------------------------ |
| **Zero-knowledge proofs** | Verify data without revealing it |
| **Confidential smart contracts** | Execute logic while keeping inputs private |
| **Cardano integration** | Leverage Cardano's security and ecosystem |
| **TypeScript SDK** | Full type safety and modern developer experience |
## When to use Midnight
Use Midnight when you need:
* **Financial privacy**: Hide transaction amounts while proving validity
* **Identity verification**: Prove attributes (age, citizenship) without revealing personal data
* **Confidential voting**: Enable verifiable elections with secret ballots
* **Private DeFi**: Build lending, staking, or trading with hidden positions
## Quick start
Install the Midnight setup package:
```bash
npm install @meshsdk/midnight-setup
```
Connect to a wallet and deploy a contract:
```typescript
import { MidnightSetupAPI } from '@meshsdk/midnight-setup';
import { setupProviders } from './providers';
const providers = await setupProviders();
const api = await MidnightSetupAPI.deployContract(providers, contractInstance);
console.log('Contract deployed at:', api.deployedContractAddress);
```
## Explore the documentation
## Related resources
* [Midnight Network Documentation](https://docs.midnight.network/) - Official Midnight docs
* [Mesh SDK Documentation](https://midnight.meshjs.dev/en) - Full SDK reference
* [Lace Beta Wallet](https://chromewebstore.google.com/detail/lace-midnight-preview/hgeekaiplokcnmakghbdfbgnlfheichg) - Browser wallet for Midnight
* [GitHub Repository](https://github.com/MeshJS/midnight-setup) - Source code and examples
# Cardano NFT Minting and Development
URL: /nft
Build, mint, and manage NFTs on Cardano with Mesh SDK. Complete tools for collections, marketplaces, and Plutus-based NFT contracts.
***
title: "Cardano NFT Minting and Development"
description: "Build, mint, and manage NFTs on Cardano with Mesh SDK. Complete tools for collections, marketplaces, and Plutus-based NFT contracts."
icon: PhotoIcon
---------------
import Link from "fumadocs-core/link";
import { Card, CardDescription, CardTitle } from "@/components/ui/card";
Create NFTs on Cardano using Mesh's complete toolkit for minting, metadata management, and marketplace integration. Whether you build a simple minting page or a full marketplace, Mesh provides the APIs you need.
## Why Build NFTs on Cardano?
Cardano's native token model offers significant advantages over smart-contract-based NFT systems:
| Feature | Cardano Native NFTs | Ethereum ERC-721 |
| ------------------ | --------------------------- | ------------------------ |
| Minting complexity | No smart contract required | Smart contract mandatory |
| Transaction fees | Predictable, low cost | Variable gas fees |
| Token security | First-class network citizen | Contract-dependent |
| Batch operations | Native support | Gas-intensive |
| Metadata storage | On-chain or IPFS | Usually IPFS only |
## Core Capabilities
### Mint Native NFTs
Create NFTs using native scripts without deploying smart contracts. Mesh handles policy ID generation, metadata attachment, and transaction construction automatically.
**Best for:**
* Art and collectibles
* Collection launches with fixed supply
* Event tickets and POAPs
* Membership tokens
```tsx
import { MeshTxBuilder, ForgeScript } from '@meshsdk/core';
// Create a time-locked minting policy
const forgingScript = ForgeScript.withPubKeyHash(walletAddress);
const tx = new MeshTxBuilder({ fetcher, submitter });
await tx
.mint('1', policyId, 'MyNFT')
.mintingScript(forgingScript)
.metadataValue(721, {
[policyId]: {
MyNFT: {
name: 'My First NFT',
image: 'ipfs://...',
description: 'Minted with Mesh'
}
}
})
.complete();
```
### Plutus NFT Contracts
Add custom validation logic for advanced minting rules, royalty enforcement, or dynamic metadata updates.
**Enables:**
* Royalties on secondary sales
* Time-locked minting windows
* Whitelist access control
* Dynamic metadata (CIP-68)
* Burn-to-redeem mechanics
### Metadata Standards
Mesh supports both Cardano NFT metadata standards:
| Standard | Storage | Mutability | Use Case |
| -------- | -------------------- | ---------- | ------------------------------ |
| CIP-25 | Transaction metadata | Immutable | Static NFTs, art, collectibles |
| CIP-68 | Datum on-chain | Mutable | Gaming assets, evolving NFTs |
## Build NFT Marketplaces
Mesh provides the building blocks for complete marketplace functionality:
* **Listings** - Lock NFTs in escrow contracts with asking prices
* **Purchases** - Atomic payment and NFT transfer
* **Auctions** - Bidding systems with time-based resolution
* **Royalties** - Automated creator payments on each sale
* **Collections** - Query and organize NFTs by policy ID
## Getting Started
NFT Collection GuideStep-by-step tutorial for minting your first NFT collectionMarketplace ContractBuild a decentralized NFT marketplace with PlutusPlutus NFT MintingCreate NFTs with custom on-chain validation rulesMinting API ReferenceComplete API documentation for minting and burning
## Common Patterns
### Collection Policy
Use a single policy ID for all NFTs in a collection. Distinguish individual tokens with unique asset names. This enables easy collection queries and verification.
```tsx
// All NFTs share the same policy ID
const policyId = 'abc123...';
// Asset names differentiate tokens
const nft1 = { policyId, assetName: 'Token001' };
const nft2 = { policyId, assetName: 'Token002' };
```
### IPFS Metadata
Store images and extended metadata on IPFS. Reference the content hash in on-chain metadata for cost efficiency while maintaining decentralization.
```tsx
const metadata = {
name: 'My NFT',
image: 'ipfs://QmXyz...', // IPFS content hash
attributes: [
{ trait_type: 'Background', value: 'Blue' },
{ trait_type: 'Rarity', value: 'Legendary' }
]
};
```
### Lazy Minting
Defer minting until purchase to reduce upfront costs for large collections. The NFT is created when a buyer completes payment.
### Reveal Mechanics
Mint with placeholder metadata, then update to final artwork after a reveal event using CIP-68 datum updates.
## Provider Integration
Query NFT data through multiple blockchain providers:
| Provider | Capabilities |
| ---------- | ----------------------------------- |
| Blockfrost | Metadata queries, ownership lookup |
| Koios | Policy and asset endpoints |
| Maestro | Rich NFT data, marketplace listings |
| Custom | Build your own indexer integration |
See the [Providers documentation](/providers) for configuration details.
## Community Resources
* **Discord** - Real-time support from Mesh developers
* **GitHub** - Example projects and issue tracking
* **Twitter** - Updates on new NFT features
Start building your NFT project today with Mesh.
# Getting Started with React
URL: /react/getting-started
Install Mesh React, configure the provider, and connect your first Cardano wallet in minutes.
***
title: "Getting Started with React"
description: "Install Mesh React, configure the provider, and connect your first Cardano wallet in minutes."
------------------------------------------------------------------------------------------------------------
import Link from "next/link";
## Overview
This guide walks you through setting up Mesh React in your application. By the end, you will have a working wallet connection button that lets users connect their Cardano wallets.
**You will learn how to:**
* Install the `@meshsdk/react` package
* Configure the `MeshProvider` context
* Add a wallet connection button
* Access connected wallet data
## Quick Start
Get a wallet connection working in under 2 minutes:
```tsx
// pages/_app.tsx (Next.js Pages Router)
import "@meshsdk/react/styles.css";
import { MeshProvider } from "@meshsdk/react";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
```tsx
// pages/index.tsx
import { CardanoWallet, useWallet } from "@meshsdk/react";
export default function Home() {
const { connected, wallet } = useWallet();
return (
{connected &&
Wallet connected!
}
);
}
```
## Installation
### Option 1: Mesh CLI (Recommended)
The fastest way to start is with the Mesh CLI, which scaffolds a complete project:
```bash
npx meshjs your-app-name
```
Select the React template when prompted. This creates a new project with Mesh pre-configured.
### Option 2: Manual Installation
Add the package to an existing project:
```bash
npm install @meshsdk/react
```
```bash
yarn add @meshsdk/react
```
```bash
pnpm add @meshsdk/react
```
## Setup
### Step 1: Add the CSS
Import the Mesh styles in your application root. This applies default styling to all Mesh components.
**Next.js Pages Router** (`pages/_app.tsx`):
```tsx
import "@meshsdk/react/styles.css";
```
**Next.js App Router** (`app/layout.tsx`):
```tsx
import "@meshsdk/react/styles.css";
```
### Step 2: Add the MeshProvider
Wrap your application with `MeshProvider` to enable wallet context throughout your app.
**Next.js Pages Router:**
```tsx
// pages/_app.tsx
import "@meshsdk/react/styles.css";
import { MeshProvider } from "@meshsdk/react";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
**Next.js App Router:**
```tsx
// app/layout.tsx
import "@meshsdk/react/styles.css";
import { MeshProvider } from "@meshsdk/react";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
### Step 3: Add the Wallet Button
Add `CardanoWallet` anywhere in your app to show a wallet connection button:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Header() {
return (
);
}
```
## API Reference
### MeshProvider
The context provider that enables wallet functionality throughout your application.
#### Props
| Prop | Type | Default | Description |
| ---------- | ----------- | -------- | --------------------------- |
| `children` | `ReactNode` | Required | Your application components |
#### Usage
```tsx
import { MeshProvider } from "@meshsdk/react";
function App({ children }) {
return {children};
}
```
### CardanoWallet
A pre-built button component that handles wallet selection and connection.
#### Props
| Prop | Type | Default | Description |
| -------------------- | --------------------- | ------------------ | ------------------------------------------ |
| `label` | `string` | `"Connect Wallet"` | Button text before connection |
| `isDark` | `boolean` | `false` | Enable dark mode styling |
| `persist` | `boolean` | `false` | Remember wallet choice across sessions |
| `onConnected` | `() => void` | `undefined` | Callback fired after successful connection |
| `web3Services` | `object` | `undefined` | Mesh Web3 Services configuration |
| `cardanoPeerConnect` | `object` | `undefined` | CIP-45 WebRTC configuration |
| `burnerWallet` | `object` | `undefined` | Enable burner wallet for testing |
| `injectFn` | `() => Promise` | `undefined` | Custom wallet injection (MetaMask Snaps) |
#### Usage Examples
**Basic usage:**
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
**With dark mode:**
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
**With custom label:**
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
**With session persistence:**
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
**With connection callback:**
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
const handleConnected = () => {
console.log("Wallet connected successfully");
// Redirect, fetch data, etc.
};
return ;
}
```
### useWallet Hook
Access wallet state and connection functions from any component.
#### Return Values
| Property | Type | Description |
| ------------ | ------------------------------------------------ | ----------------------------------- |
| `wallet` | `MeshCardanoBrowserWallet` | Wallet instance with CIP-30 methods |
| `connected` | `boolean` | Whether a wallet is connected |
| `connecting` | `boolean` | Whether connection is in progress |
| `name` | `string` | Name of the connected wallet |
| `state` | `"NOT_CONNECTED" \| "CONNECTING" \| "CONNECTED"` | Current connection state |
| `connect` | `(walletName: string) => void` | Connect to a specific wallet |
| `disconnect` | `() => void` | Disconnect the current wallet |
| `error` | `Error \| null` | Connection error if any |
#### Usage
```tsx
import { useWallet } from "@meshsdk/react";
export default function WalletInfo() {
const { connected, name, wallet, disconnect } = useWallet();
if (!connected) {
return
No wallet connected
;
}
return (
Connected to: {name}
);
}
```
## Customization
### Dark Mode
Enable dark mode styling with the `isDark` prop:
```tsx
```
### Custom Button Label
Change the button text:
```tsx
```
### Session Persistence
Save the user's wallet choice and auto-connect on return visits:
```tsx
```
### Mesh Web3 Services
Integrate [Mesh Web3 Services](https://utxos.dev/) for enhanced features:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("");
export default function Page() {
return (
);
}
```
### CIP-45 WebRTC Connection
Enable decentralized wallet communication via WebRTC:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return (
);
}
```
### Burner Wallet (Testing)
Enable temporary wallets for development and testing:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("");
export default function Page() {
return (
);
}
```
### MetaMask Snaps Integration
Add custom wallet injection for MetaMask Snaps:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { checkIfMetamaskInstalled } from "./metamask-utils";
export default function Page() {
return (
await checkIfMetamaskInstalled("preprod")}
/>
);
}
```
## Complete Example
A full example combining multiple features:
```tsx
// pages/_app.tsx
import "@meshsdk/react/styles.css";
import { MeshProvider } from "@meshsdk/react";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
```tsx
// pages/index.tsx
import { CardanoWallet, useWallet, useLovelace } from "@meshsdk/react";
export default function Home() {
const { connected, name, disconnect } = useWallet();
const lovelace = useLovelace();
const handleConnected = () => {
console.log("Wallet connected!");
};
return (
My Cardano dApp
{!connected ? (
) : (
Connected to: {name}
Balance: {lovelace ? parseInt(lovelace) / 1_000_000 : 0} ADA
)}
);
}
```
## Troubleshooting
### "No wallets found"
**Problem:** The wallet list is empty even with wallets installed.
**Solutions:**
1. Ensure the wallet browser extension is installed and enabled
2. Refresh the page after installing a new wallet
3. Check that you're on a supported browser (Chrome, Brave, Firefox)
### "MeshProvider not found"
**Problem:** Hooks throw an error about missing provider.
**Solution:** Wrap your app with `MeshProvider` at the root level:
```tsx
// pages/_app.tsx
import { MeshProvider } from "@meshsdk/react";
export default function App({ Component, pageProps }) {
return (
);
}
```
### Styles not loading
**Problem:** Components appear unstyled.
**Solution:** Import the CSS file in your app root:
```tsx
import "@meshsdk/react/styles.css";
```
### Wallet connection fails silently
**Problem:** Clicking connect does nothing.
**Solutions:**
1. Check browser console for errors
2. Ensure the wallet extension is unlocked
3. Verify the user has an account set up in their wallet
4. Check the `error` property from `useWallet()` for details
### TypeScript errors with wallet methods
**Problem:** TypeScript doesn't recognize wallet methods.
**Solution:** The `wallet` object from `useWallet()` is a `MeshCardanoBrowserWallet` instance. Import types from `@meshsdk/wallet`:
```tsx
import type { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
```
## Related Links
* [UI Components](/docs/react/ui-components) - Pre-built wallet and badge components
* [Wallet Hooks](/docs/react/wallet-hooks) - All available React hooks
* [Browser Wallet API](/apis/wallets/browserwallet) - Full CIP-30 wallet interface
* [Providers](/providers) - Blockchain data providers
* [GitHub Repository](https://github.com/MeshJS/mesh) - Source code and examples
# Cardano React Components
URL: /react
Build Cardano dApps with pre-built React components and hooks for wallet connections, transactions, and blockchain interactions.
***
title: "Cardano React Components"
description: "Build Cardano dApps with pre-built React components and hooks for wallet connections, transactions, and blockchain interactions."
icon: ComputerDesktopIcon
-------------------------
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
## Overview
Mesh React provides a complete toolkit for building Cardano decentralized applications with React. You get pre-built UI components for wallet connections, React hooks for accessing wallet state, and utilities for blockchain interactions.
**Use Mesh React when you need to:**
* Connect users to their Cardano wallets (Nami, Eternl, Flint, etc.)
* Display wallet balances and assets
* Build transaction signing flows
* Create multi-wallet support in your dApp
## Quick Start
Install the package and wrap your app with the provider:
```tsx
import "@meshsdk/react/styles.css";
import { MeshProvider, CardanoWallet } from "@meshsdk/react";
function App() {
return (
);
}
```
## What's Included
Getting StartedInstallation, setup, and your first wallet connectionUI ComponentsPre-built wallet buttons, badges, and connection modalsWallet HooksReact hooks for wallet state, assets, addresses, and networksBrowser Wallet APIFull CIP-30 wallet API for transactions and signing
## Installation
Install using your preferred package manager:
```bash
npm install @meshsdk/react
```
```bash
yarn add @meshsdk/react
```
```bash
pnpm add @meshsdk/react
```
Or scaffold a complete project with the Mesh CLI:
```bash
npx meshjs your-app-name
```
## Requirements
* React 18 or later
* Next.js 13+ (App Router or Pages Router)
* A Cardano wallet browser extension (Nami, Eternl, Flint, Lace, etc.)
## Related Resources
* [Browser Wallet API](/apis/wallets/browserwallet) - Full CIP-30 wallet interface
* [Transaction Builder](/apis/transaction) - Build and sign transactions
* [Providers](/providers) - Blockchain data providers (Blockfrost, Koios, etc.)
* [Mesh CLI](/getting-started/mesh-cli) - Project scaffolding and utilities
# Wallet UI Components
URL: /react/ui-components
Pre-built React components for wallet connections, badges, and common Cardano dApp UI patterns.
***
title: "Wallet UI Components"
description: "Pre-built React components for wallet connections, badges, and common Cardano dApp UI patterns."
--------------------------------------------------------------------------------------------------------------
import Link from "next/link";
## Overview
Mesh provides ready-to-use React components for common Cardano dApp patterns. These components handle wallet connections, display branding, and provide consistent UI across your application.
**Available components:**
* `CardanoWallet` - A complete wallet connection button with modal
* `MeshBadge` - A "Powered by Mesh" badge for your app
## Quick Start
Add a wallet connection button to your app:
```tsx
import { MeshProvider, CardanoWallet } from "@meshsdk/react";
import "@meshsdk/react/styles.css";
export default function App() {
return (
);
}
```
## Installation
```bash
npm install @meshsdk/react
```
Import the styles in your app root:
```tsx
import "@meshsdk/react/styles.css";
```
***
## CardanoWallet
A complete wallet connection component that displays available wallets and handles the connection flow.
### Props
| Prop | Type | Default | Description |
| -------------------- | -------------------------- | ------------------ | --------------------------------------------- |
| `label` | `string` | `"Connect Wallet"` | Button text when no wallet is connected |
| `isDark` | `boolean` | `false` | Enable dark mode styling |
| `persist` | `boolean` | `false` | Save wallet choice and auto-connect on return |
| `onConnected` | `() => void` | `undefined` | Callback fired after successful connection |
| `web3Services` | `Web3ServicesConfig` | `undefined` | Mesh Web3 Services integration |
| `cardanoPeerConnect` | `CardanoPeerConnectConfig` | `undefined` | CIP-45 WebRTC configuration |
| `burnerWallet` | `BurnerWalletConfig` | `undefined` | Enable temporary test wallets |
| `injectFn` | `() => Promise` | `undefined` | Custom wallet injection function |
### Basic Usage
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
### Dark Mode
Apply dark theme styling:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
### Custom Label
Change the button text:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
### Session Persistence
Remember the user's wallet choice and automatically reconnect:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return ;
}
```
### Connection Callback
Execute code after successful wallet connection:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
const handleConnected = () => {
console.log("Wallet connected!");
// Fetch user data, redirect, etc.
};
return ;
}
```
### Mesh Web3 Services
Integrate [Mesh Web3 Services](https://utxos.dev/) for enhanced features like transaction submission and data fetching:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("");
export default function Page() {
return (
);
}
```
### CIP-45 WebRTC Connection
Enable decentralized peer-to-peer wallet communication:
```tsx
import { CardanoWallet } from "@meshsdk/react";
export default function Page() {
return (
);
}
```
### Burner Wallet
Enable temporary wallets for development and testing. The private keys are generated and stored on the user's device:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("");
export default function Page() {
return (
);
}
```
### MetaMask Snaps
Inject custom wallet functionality for MetaMask Snaps integration:
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { checkIfMetamaskInstalled } from "./metamask-utils";
export default function Page() {
return (
await checkIfMetamaskInstalled("preprod")}
/>
);
}
```
### Complete Example with All Options
```tsx
import { CardanoWallet } from "@meshsdk/react";
import { BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("");
export default function Page() {
const handleConnected = () => {
console.log("Wallet connected successfully");
};
return (
);
}
```
### Building a Custom Wallet Component
For advanced customization beyond what `CardanoWallet` provides, build your own component using the [wallet hooks](/docs/react/wallet-hooks):
```tsx
import { useWallet, useWalletList } from "@meshsdk/react";
export default function CustomWalletConnect() {
const { connect, disconnect, connected, name } = useWallet();
const wallets = useWalletList();
if (connected) {
return (
Connected: {name}
);
}
return (
{wallets.map((wallet) => (
))}
);
}
```
See the [source code](https://github.com/MeshJS/mesh/blob/main/packages/mesh-react/src/cardano-wallet/index.tsx) for implementation details.
***
## MeshBadge
Display a "Powered by Mesh" badge in your application to show your support.
### Props
| Prop | Type | Default | Description |
| -------- | --------- | ------- | ------------------------ |
| `isDark` | `boolean` | `false` | Enable dark mode styling |
### Basic Usage
```tsx
import { MeshBadge } from "@meshsdk/react";
export default function Footer() {
return (
);
}
```
### Dark Mode
```tsx
import { MeshBadge } from "@meshsdk/react";
export default function Footer() {
return (
);
}
```
### Complete Footer Example
```tsx
import { MeshBadge } from "@meshsdk/react";
export default function Footer() {
return (
);
}
```
***
## Complete Example
A full application using all UI components:
```tsx
// pages/_app.tsx
import "@meshsdk/react/styles.css";
import { MeshProvider } from "@meshsdk/react";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
```tsx
// pages/index.tsx
import { CardanoWallet, MeshBadge, useWallet, useLovelace } from "@meshsdk/react";
export default function Home() {
const { connected, name } = useWallet();
const lovelace = useLovelace();
return (
{/* Header */}
My Cardano dApp
{/* Main Content */}
{connected ? (
Welcome!
Wallet: {name}
Balance: {lovelace ? (parseInt(lovelace) / 1_000_000).toFixed(2) : "0"} ADA
) : (
Connect your wallet to get started
Click the button above to connect your Cardano wallet.
)}
{/* Footer */}
);
}
```
***
## Troubleshooting
### Components appear unstyled
**Problem:** CardanoWallet or MeshBadge render without styling.
**Solution:** Import the CSS file in your app root:
```tsx
// pages/_app.tsx or app/layout.tsx
import "@meshsdk/react/styles.css";
```
### "Cannot find MeshProvider" error
**Problem:** Components throw an error about missing context.
**Solution:** Wrap your app with `MeshProvider`:
```tsx
import { MeshProvider } from "@meshsdk/react";
export default function App({ Component, pageProps }) {
return (
);
}
```
### Wallet modal doesn't open
**Problem:** Clicking the wallet button does nothing.
**Solutions:**
1. Check browser console for JavaScript errors
2. Ensure you have wallet extensions installed
3. Verify the CSS is loaded (button should have hover effects)
### Dark mode doesn't match my theme
**Problem:** The component dark mode doesn't match your app's theme.
**Solution:** Build a custom component using the [wallet hooks](/docs/react/wallet-hooks) for full control over styling:
```tsx
import { useWallet, useWalletList } from "@meshsdk/react";
// Your fully custom styled component
export function MyWalletButton() {
const { connect, connected } = useWallet();
const wallets = useWalletList();
// ... your custom implementation
}
```
### onConnected fires before wallet is ready
**Problem:** The callback fires but wallet methods aren't available yet.
**Solution:** Use the `wallet` object from `useWallet()` which is available after `connected` becomes `true`:
```tsx
import { useWallet } from "@meshsdk/react";
import { useEffect } from "react";
export default function Page() {
const { connected, wallet } = useWallet();
useEffect(() => {
if (connected && wallet) {
// Wallet is ready to use
wallet.getBalance().then(console.log);
}
}, [connected, wallet]);
return ;
}
```
***
## Related Links
* [Getting Started](/docs/react/getting-started) - Installation and setup
* [Wallet Hooks](/docs/react/wallet-hooks) - React hooks for wallet data
* [Browser Wallet API](/apis/wallets/browserwallet) - Full CIP-30 interface
* [GitHub Source](https://github.com/MeshJS/mesh/tree/main/packages/mesh-react) - Component source code
# Wallet Hooks
URL: /react/wallet-hooks
React hooks for accessing wallet state, balances, addresses, assets, and network information in your Cardano dApp.
***
title: "Wallet Hooks"
description: "React hooks for accessing wallet state, balances, addresses, assets, and network information in your Cardano dApp."
---------------------------------------------------------------------------------------------------------------------------------
import Link from "next/link";
## Overview
Mesh provides React hooks that let you access wallet data and state from any component. These hooks simplify building Cardano dApps by handling wallet connections, balance queries, and state management.
**Available hooks:**
| Hook | Purpose |
| --------------- | ----------------------------------- |
| `useWallet` | Wallet connection state and methods |
| `useWalletList` | List of installed wallet extensions |
| `useAddress` | Connected wallet's address |
| `useAssets` | All assets in the wallet |
| `useLovelace` | ADA balance in lovelace |
| `useNetwork` | Connected network (mainnet/testnet) |
## Quick Start
Access wallet state from any component:
```tsx
import { useWallet, useLovelace, useAddress } from "@meshsdk/react";
export default function WalletInfo() {
const { connected, name } = useWallet();
const lovelace = useLovelace();
const address = useAddress();
if (!connected) {
return
Connect your wallet to continue
;
}
return (
Wallet: {name}
Balance: {parseInt(lovelace || "0") / 1_000_000} ADA
Address: {address}
);
}
```
## Installation
```bash
npm install @meshsdk/react
```
Wrap your app with the provider:
```tsx
import { MeshProvider } from "@meshsdk/react";
export default function App({ Component, pageProps }) {
return (
);
}
```
***
## useWallet
The primary hook for wallet connection state and methods. Returns the wallet instance, connection state, and functions to connect/disconnect.
### Return Values
| Property | Type | Description |
| ------------ | ------------------------------------------------ | -------------------------------------- |
| `wallet` | `MeshCardanoBrowserWallet \| null` | Wallet instance with CIP-30 methods |
| `connected` | `boolean` | `true` if a wallet is connected |
| `connecting` | `boolean` | `true` while connection is in progress |
| `name` | `string` | Name of the connected wallet |
| `state` | `"NOT_CONNECTED" \| "CONNECTING" \| "CONNECTED"` | Current connection state |
| `connect` | `(walletName: string) => Promise` | Connect to a specific wallet |
| `disconnect` | `() => void` | Disconnect the current wallet |
| `error` | `Error \| null` | Error object if connection failed |
### Basic Usage
```tsx
import { useWallet } from "@meshsdk/react";
export default function WalletStatus() {
const { connected, name, connecting, error } = useWallet();
if (connecting) {
return
Connecting...
;
}
if (error) {
return
Error: {error.message}
;
}
if (!connected) {
return
No wallet connected
;
}
return
Connected to {name}
;
}
```
### Programmatic Connection
```tsx
import { useWallet, useWalletList } from "@meshsdk/react";
export default function WalletSelector() {
const { connect, disconnect, connected, name } = useWallet();
const wallets = useWalletList();
if (connected) {
return (
Connected: {name}
);
}
return (
Select a wallet:
{wallets.map((wallet) => (
))}
);
}
```
### Using the Wallet Instance
The `wallet` object is a [MeshCardanoBrowserWallet](/apis/wallets/browserwallet) instance with full CIP-30 support:
```tsx
import { useWallet } from "@meshsdk/react";
export default function SignMessage() {
const { wallet, connected } = useWallet();
const handleSign = async () => {
if (!wallet) return;
const address = await wallet.getChangeAddress();
const signature = await wallet.signData(address, "Hello, Cardano!");
console.log("Signature:", signature);
};
if (!connected) {
return
);
}
```
***
## useAssets
Returns all assets in the connected wallet from all UTXOs.
### Return Value
| Type | Description |
| ---------------------- | ----------------------------------------------- |
| `Asset[] \| undefined` | Array of assets or `undefined` if not connected |
Each asset has the structure:
```tsx
type Asset = {
unit: string; // Policy ID + asset name (hex)
quantity: string; // Amount as string
};
```
### Basic Usage
```tsx
import { useAssets } from "@meshsdk/react";
export default function WalletAssets() {
const assets = useAssets();
if (!assets) {
return
);
}
```
***
## Troubleshooting
### Hooks return undefined
**Problem:** All hooks return `undefined` even after connecting.
**Solutions:**
1. Ensure `MeshProvider` wraps your app at the root level
2. Wait for `connected` to be `true` before accessing data
3. Check that the wallet extension is unlocked
```tsx
import { useWallet, useLovelace } from "@meshsdk/react";
export default function SafeComponent() {
const { connected } = useWallet();
const lovelace = useLovelace();
// Always check connected first
if (!connected) {
return
Please connect your wallet
;
}
// Then check if data is loaded
if (!lovelace) {
return
Loading balance...
;
}
return
Balance: {lovelace}
;
}
```
### useWalletList returns empty array
**Problem:** No wallets appear in the list.
**Solutions:**
1. Ensure wallet extensions are installed and enabled
2. Check that you're testing in a browser (not SSR)
3. Refresh the page after installing a new wallet
### "Cannot read properties of undefined"
**Problem:** Accessing wallet properties throws errors.
**Solution:** Use optional chaining and null checks:
```tsx
const { wallet, connected } = useWallet();
// Safe access
const handleAction = async () => {
if (!connected || !wallet) return;
const address = await wallet.getChangeAddress();
};
```
### Balance doesn't update after transaction
**Problem:** `useLovelace` shows stale data after sending/receiving.
**Solution:** Wallet hooks auto-refresh, but you can force a reconnection:
```tsx
const { disconnect, connect, name } = useWallet();
const refreshWallet = async () => {
disconnect();
await new Promise((r) => setTimeout(r, 100));
await connect(name);
};
```
### TypeScript type errors
**Problem:** TypeScript doesn't recognize hook return types.
**Solution:** Import types from `@meshsdk/wallet` and `@meshsdk/core`:
```tsx
import type { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
import type { Asset } from "@meshsdk/core";
import { useWallet, useAssets } from "@meshsdk/react";
const { wallet } = useWallet() as { wallet: MeshCardanoBrowserWallet | null };
const assets = useAssets() as Asset[] | undefined;
```
***
## Related Links
* [Getting Started](/docs/react/getting-started) - Installation and setup
* [UI Components](/docs/react/ui-components) - Pre-built wallet button and badge
* [Browser Wallet API](/apis/wallets/browserwallet) - Full CIP-30 wallet interface
* [Transaction Builder](/apis/transaction) - Build and sign transactions
* [GitHub Source](https://github.com/MeshJS/mesh/tree/main/packages/mesh-react) - Hook implementations
# Developer Resources
URL: /resources/developer-resources
Tools, communities, and resources for Cardano developers building with Mesh.
***
title: "Developer Resources"
description: "Tools, communities, and resources for Cardano developers building with Mesh."
-------------------------------------------------------------------------------------------
import { Card, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import Link from 'next/link';
Find the tools, documentation, and community resources you need to build Cardano applications with Mesh.
## Mesh documentation
| Resource | Description |
| ----------------------------------- | --------------------------------- |
| [Getting Started](/guides) | Set up your first Mesh project |
| [API Reference](/apis) | Complete API documentation |
| [Smart Contracts](/smart-contracts) | Build with Plutus and Aiken |
| [React Components](/react) | UI components for dApps |
| [Providers](/providers) | Connect to Cardano infrastructure |
## Code and examples
| Resource | Description |
| ----------------------------------------------------------- | ----------------------------- |
| [GitHub Repository](https://github.com/MeshJS/mesh) | Source code and contributions |
| [Example Projects](https://github.com/MeshJS/examples) | Working code samples |
| [Starter Templates](https://github.com/MeshJS/mesh-starter) | Boilerplate projects |
| [NPM Package](https://www.npmjs.com/package/@meshsdk/core) | Package downloads |
## Community
Discord
Real-time help and discussions with the Mesh community.
Join Discord
Twitter
Follow [@meshsdk](https://twitter.com/meshsdk) for announcements and tips.
Follow on Twitter
GitHub Discussions
Technical discussions, questions, and ideas.
Join Discussions
Stack Overflow
Ask technical questions tagged with `meshsdk`.
View Questions
## Cardano ecosystem
### Official resources
| Resource | Description |
| -------------------------------------------------- | ------------------------ |
| [Cardano.org](https://cardano.org) | Official Cardano website |
| [Cardano Documentation](https://docs.cardano.org) | Protocol documentation |
| [Developer Portal](https://developers.cardano.org) | Development guides |
| [Cardano Forum](https://forum.cardano.org) | Community discussions |
### Block explorers
| Explorer | Features |
| ------------------------------------- | --------------------------------- |
| [CardanoScan](https://cardanoscan.io) | Comprehensive blockchain explorer |
| [Cexplorer](https://cexplorer.io) | Detailed blockchain data |
| [Pool.pm](https://pool.pm) | Native assets and NFT explorer |
| [AdaStat](https://adastat.net) | Analytics and statistics |
## Learning
### Courses
| Provider | Focus |
| ---------------------------------------------------------------- | ---------------------------- |
| [Emurgo Academy](https://education.emurgo.io) | Professional Cardano courses |
| [Gimbalabs](https://gimbalabs.com) | Community-driven learning |
| [Plutus Pioneers](https://plutus-pioneer-program.readthedocs.io) | Smart contract development |
### Documentation
| Resource | Topic |
| -------------------------------------------------- | -------------------------------- |
| [Developer Portal](https://developers.cardano.org) | Comprehensive development guides |
| [Essential Cardano](https://essentialcardano.io) | Curated resources |
| [Aiken Lang](https://aiken-lang.org) | Aiken smart contract language |
## AI assistance
Mesh includes built-in AI features:
* **[AI Chat](/ai)** - Ask questions, get instant answers
* **Code generation** - Generate Mesh code from descriptions
* **Documentation search** - Semantic search across docs
Access Mesh AI from any documentation page or visit the [AI page](/ai).
## Contributing
### How to contribute
1. **Code** - Fork, improve, submit PRs
2. **Documentation** - Fix typos, add examples
3. **Bug reports** - Help identify issues
4. **Feature requests** - Suggest improvements
5. **Community** - Answer questions on Discord
See the [Contributing Guide](https://github.com/MeshJS/mesh/blob/main/CONTRIBUTING.md).
### Support Mesh
* [GitHub Sponsors](https://github.com/sponsors/MeshJS)
* [Support page](/about/support-us)
## Quick links
***
**Ready to build?** Start with the [Getting Started Guide](/guides) or explore the [API Documentation](/apis).
# FAQ
URL: /resources/faq
Answers to common questions about Mesh SDK for Cardano development.
***
title: "FAQ"
description: "Answers to common questions about Mesh SDK for Cardano development."
----------------------------------------------------------------------------------
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
## Getting started
### What is Mesh?
Mesh is an open-source TypeScript SDK for building Cardano applications. It provides wallet integrations, transaction builders, React components, and smart contract tools to help you ship dApps faster.
### Why use Mesh?
| Benefit | Details |
| ------------------ | ------------------------------------------------- |
| Lightweight | Less than 60kB bundle size |
| Production-ready | 1M+ downloads, battle-tested |
| Developer-friendly | Intuitive APIs with TypeScript support |
| Comprehensive | Wallet integration, transactions, smart contracts |
| Always current | Updated with Cardano network changes |
### Is Mesh free?
Yes. Mesh is open-source under Apache 2.0 and MIT licenses. Use it for personal or commercial projects without licensing fees. View the source on [GitHub](https://github.com/MeshJS/mesh).
### What are the requirements?
* Node.js 16+
* npm, yarn, or bun
* TypeScript 4.5+ (recommended)
* React 18+ (for React components)
***
## Installation
### How do I install Mesh?
```bash
npm install @meshsdk/core
```
For React projects, also install the React bindings:
```bash
npm install @meshsdk/react
```
### Does Mesh work with Next.js?
Yes. Mesh supports both App Router and Pages Router. See the [Next.js integration guide](/guides/nextjs) for setup instructions.
### What frameworks are supported?
| Framework | Package |
| ---------- | ----------------- |
| React | `@meshsdk/react` |
| Next.js | `@meshsdk/react` |
| Svelte | `@meshsdk/svelte` |
| Vue.js | `@meshsdk/core` |
| Node.js | `@meshsdk/core` |
| Vanilla JS | `@meshsdk/core` |
***
## Wallets
### Which wallets are supported?
Mesh supports all CIP-30 compliant wallets:
* Eternl
* Lace
* Yoroi
* Typhon
* Nami
* Flint
* And any other CIP-30 wallet
New CIP-30 wallets work automatically.
### How do I connect a wallet?
Use the `CardanoWallet` component for the simplest integration:
```tsx
import { CardanoWallet } from '@meshsdk/react';
export default function App() {
return (
console.log('Connected!')}
/>
);
}
```
See [wallet hooks documentation](/react/wallet-hooks) for advanced patterns.
### Can I build a wallet app?
Yes. The `MeshCardanoHeadlessWallet` class supports:
* Mnemonic phrase generation
* Private key import
* cardano-cli compatibility
* Multi-signature workflows
* Browser extension development
See the [Wallet documentation](/apis/wallets).
***
## Transactions
### How do I build transactions?
Use `MeshTxBuilder` for intuitive transaction construction:
```typescript
import { MeshTxBuilder } from '@meshsdk/core';
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const unsignedTx = await txBuilder
.txOut('addr1...', [{ unit: 'lovelace', quantity: '1000000' }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
```
See the [MeshTxBuilder documentation](/apis/txbuilder).
### Does Mesh support smart contracts?
Yes. Mesh provides full support for Plutus and Aiken smart contracts:
* Transaction building with script execution
* Datum and redeemer handling
* Script validation
* Multi-asset operations
See [Smart Contracts guides](/smart-contracts) and [Aiken tutorials](/aiken).
### Can I mint NFTs?
Yes. Mesh supports:
* Single NFT minting
* Collection minting
* Multi-signature minting
* CIP-25 and CIP-68 metadata
See the [Minting guide](/guides/nft-collection).
***
## Development
### How do I test my dApp?
| Environment | Use case |
| -------------------- | -------------------------------------- |
| Preprod/Preview | Free test ADA, real network conditions |
| [Yaci DevNet](/yaci) | Local development, fast iteration |
| Mainnet | Production deployment |
### Where can I find examples?
* [Documentation](/docs) - Guides with code samples
* [GitHub examples](https://github.com/MeshJS/examples) - Working projects
* [API pages](/apis) - Interactive demos
### How do I get help?
| Channel | Best for |
| -------------------------------------------------------------------- | ----------------------------- |
| [Discord](https://discord.gg/dH48jH3BKa) | Real-time help |
| [GitHub Issues](https://github.com/MeshJS/mesh/issues) | Bug reports, feature requests |
| [Stack Overflow](https://stackoverflow.com/questions/tagged/meshsdk) | Searchable Q\&A |
| [Twitter](https://twitter.com/meshsdk) | Updates and announcements |
***
## Production
### Is Mesh production-ready?
Yes. Mesh powers live Cardano applications with:
* 1M+ downloads
* Comprehensive error handling
* TypeScript type safety
* Less than 60kB bundle size
* Regular updates for network changes
### How do I optimize bundle size?
Mesh supports tree-shaking. To minimize bundle size:
1. Import only what you need
2. Use code splitting
3. Enable production builds
4. Use Next.js automatic optimization
### Does Mesh work offline?
| Feature | Offline | Online required |
| ---------------------- | ------- | --------------- |
| Transaction building | Yes | No |
| Address generation | Yes | No |
| Key management | Yes | No |
| Blockchain queries | No | Yes |
| Transaction submission | No | Yes |
| UTXO fetching | No | Yes |
***
## Commercial use
### Can I use Mesh commercially?
Yes. Mesh is dual-licensed under Apache 2.0 and MIT. Build and sell applications without licensing fees.
### How can I contribute?
* Submit [bug reports](https://github.com/MeshJS/mesh/issues)
* Open [pull requests](https://github.com/MeshJS/mesh)
* Improve documentation
* Help others on Discord
* [Support financially](/about/support-us)
See the [contributing guide](https://github.com/MeshJS/mesh/blob/main/CONTRIBUTING.md).
***
## Still have questions?
* [Discord community](https://discord.gg/dH48jH3BKa)
* [GitHub Discussions](https://github.com/MeshJS/mesh/discussions)
* [Documentation](/docs)
# Resources
URL: /resources
Comprehensive courses, tutorials, and resources for Cardano developers.
***
title: "Resources"
description: "Comprehensive courses, tutorials, and resources for Cardano developers."
icon: AcademicCapIcon
---------------------
import {linksLearn} from "@/data/links-course";
import {metaGuides} from "@/data/links-guides";
import Link from "next/link";
import {
Card,
CardDescription,
CardTitle,
} from "@/components/ui/card";
Find everything you need to build on Cardano with Mesh. Access courses, tutorials, developer tools, and community resources to accelerate your development journey.
## Learning paths