Creating Accounts and Deploying Faucets
Using the Miden WebClient in TypeScript to create accounts and deploy faucets
Overview
In this tutorial, we'll build a simple Next.js application that demonstrates the fundamentals of interacting with the Miden blockchain using the WebClient SDK. We'll walk through creating a Miden account for Alice and deploying a fungible faucet contract that can mint tokens. This sets the foundation for more complex operations like issuing assets and transferring them between accounts.
What we'll cover
- Understanding the difference between public and private accounts & notes
- Instantiating the Miden client
- Creating new accounts (public or private)
- Deploying a faucet to fund an account
Prerequisites
- Node
v20
or greater - Familiarity with TypeScript
pnpm
Public vs. private accounts & notes
Before we dive into code, a quick refresher:
- Public accounts: The account's data and code are stored on-chain and are openly visible, including its assets.
- Private accounts: The account's state and logic are off-chain, only known to its owner.
- Public notes: The note's state is visible to anyone - perfect for scenarios where transparency is desired.
- Private notes: The note's state is stored off-chain, you will need to share the note data with the relevant parties (via email or Telegram) for them to be able to consume the note.
Important: In Miden, "accounts" and "smart contracts" can be used interchangeably due to native account abstraction. Every account is programmable and can contain custom logic.
It is useful to think of notes on Miden as "cryptographic cashier's checks" that allow users to send tokens. If the note is private, the note transfer is only known to the sender and receiver.
Step 1: Initialize your Next.js project
-
Create a new Next.js app with TypeScript:
npx create-next-app@latest miden-web-app --typescript
Hit enter for all terminal prompts.
-
Change into the project directory:
cd miden-web-app
-
Install the Miden WebClient SDK:
pnpm install @demox-labs/miden-sdk@0.10.1
NOTE!: Be sure to remove the --turbopack
command from your package.json
when running the dev script
. The dev script should look like this:
package.json
"scripts": {
"dev": "next dev",
...
}
Step 2: Set up the WebClient
The WebClient is your gateway to interact with the Miden blockchain. It handles state synchronization, transaction creation, and proof generation. Let's set it up.
Create lib/createMintConsume.ts
First, we'll create a separate file for our blockchain logic. In the project root, create a folder lib/
and inside it createMintConsume.ts
:
mkdir -p lib
touch lib/createMintConsume.ts
// lib/createMintConsume.ts
export async function createMintConsume(): Promise<void> {
if (typeof window === "undefined") {
console.warn("webClient() can only run in the browser");
return;
}
// dynamic import → only in the browser, so WASM is loaded client‑side
const { WebClient, AccountStorageMode, AccountId, NoteType } = await import(
"@demox-labs/miden-sdk"
);
// Connect to Miden testnet RPC endpoint
const nodeEndpoint = "https://rpc.testnet.miden.io:443";
const client = await WebClient.createClient(nodeEndpoint);
// 1. Sync with the latest blockchain state
// This fetches the latest block header and state commitments
const state = await client.syncState();
console.log("Latest block number:", state.blockNum());
// At this point, your client is connected and synchronized
// Ready to create accounts and deploy contracts!
}
Since we will be handling proof generation in the browser, it will be slower than proof generation handled by the Rust client. Check out the tutorial on delegated proving to speed up proof generation in the browser.
Step 3: Create the User Interface
Now let's create a simple UI that will trigger our blockchain interactions. We'll replace the default Next.js page with a button that calls our createMintConsume()
function.
Edit app/page.tsx
to call createMintConsume()
on a button click:
"use client";
import { useState } from "react";
import { createMintConsume } from "../lib/createMintConsume";
export default function Home() {
const [isCreatingNotes, setIsCreatingNotes] = useState(false);
const handleCreateMintConsume = async () => {
setIsCreatingNotes(true);
await createMintConsume();
setIsCreatingNotes(false);
};
return (
<main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-900 via-gray-800 to-black text-slate-800 dark:text-slate-100">
<div className="text-center">
<h1 className="text-4xl font-semibold mb-4">Miden Web App</h1>
<p className="mb-6">Open your browser console to see WebClient logs.</p>
<div className="max-w-sm w-full bg-gray-800/20 border border-gray-600 rounded-2xl p-6 mx-auto flex flex-col gap-4">
<button
onClick={handleCreateMintConsume}
className="w-full px-6 py-3 text-lg cursor-pointer bg-transparent border-2 border-orange-600 text-white rounded-lg transition-all hover:bg-orange-600 hover:text-white"
>
{isCreatingNotes
? "Working..."
: "Tutorial #1: Create a wallet and deploy a faucet"}
</button>
</div>
</div>
</main>
);
}
Step 4: Create Alice's Wallet Account
Now we'll create Alice's account. Let's create a public account so we can easily track her transactions.
Back in lib/createMintConsume.ts
, extend the createMintConsume()
function:
// lib/createMintConsume.ts
export async function createMintConsume(): Promise<void> {
if (typeof window === "undefined") {
console.warn("webClient() can only run in the browser");
return;
}
const { WebClient, AccountStorageMode } = await import(
"@demox-labs/miden-sdk"
);
const nodeEndpoint = "https://rpc.testnet.miden.io:443";
const client = await WebClient.createClient(nodeEndpoint);
// 1. Sync with the latest blockchain state
const state = await client.syncState();
console.log("Latest block number:", state.blockNum());
// 2. Create Alice's account
console.log("Creating account for Alice…");
const alice = await client.newWallet(
AccountStorageMode.public(), // Public: account state is visible on-chain
true // Mutable: account code can be upgraded later
);
console.log("Alice ID:", alice.id().toString());
}
Step 5: Deploy a Fungible Faucet
A faucet in Miden is a special type of account that can mint new tokens. Think of it as your own token factory. Let's deploy one that will create our custom "MID" tokens.
Add this code after creating Alice's account:
// 3. Deploy a fungible faucet
// A faucet is an account that can mint new tokens
console.log("Creating faucet…");
const faucetAccount = await client.newFaucet(
AccountStorageMode.public(), // Public: faucet operations are transparent
false, // Immutable: faucet rules cannot be changed
"MID", // Token symbol (like ETH, BTC, etc.)
8, // Decimals (8 means 1 MID = 100,000,000 base units)
BigInt(1_000_000) // Max supply: total tokens that can ever be minted
);
console.log("Faucet account ID:", faucetAccount.id().toString());
console.log("Setup complete.");
Understanding Faucet Parameters:
- Storage Mode: We use
public()
so anyone can verify the faucet's minting operations - Mutability: Set to
false
to ensure the faucet rules can't be changed after deployment - Token Symbol: A short identifier for your token (e.g., "MID", "USDC", "DAI")
- Decimals: Determines the smallest unit of your token. With 8 decimals, 1 MID = 10^8 base units
- Max Supply: The maximum number of tokens that can ever exist
Note: When tokens are minted from a faucet, they're created as "notes" - Miden's version of UTXOs. Each note contains tokens and can have specific spending conditions.
Summary
In this tutorial, we've successfully:
- Set up a Next.js application with the Miden WebClient SDK
- Connected to the Miden testnet
- Created a wallet account for Alice
- Deployed a fungible faucet that can mint custom tokens
Your final lib/createMintConsume.ts
should look like:
// lib/createMintConsume.ts
export async function createMintConsume(): Promise<void> {
if (typeof window === "undefined") {
console.warn("webClient() can only run in the browser");
return;
}
const { WebClient, AccountStorageMode } = await import(
"@demox-labs/miden-sdk"
);
const nodeEndpoint = "https://rpc.testnet.miden.io:443";
const client = await WebClient.createClient(nodeEndpoint);
// 1. Sync with the latest blockchain state
const state = await client.syncState();
console.log("Latest block number:", state.blockNum());
// 2. Create Alice's account
console.log("Creating account for Alice…");
const alice = await client.newWallet(AccountStorageMode.public(), true);
console.log("Alice ID:", alice.id().toString());
// 3. Deploy a fungible faucet
console.log("Creating faucet…");
const faucet = await client.newFaucet(
AccountStorageMode.public(),
false,
"MID",
8,
BigInt(1_000_000),
);
console.log("Faucet ID:", faucet.id().toString());
console.log("Setup complete.");
}
Running the example
cd miden-web-app
npm i
npm run dev
Open http://localhost:3000 in your browser, click Tutorial #1: Create a wallet and deploy a faucet, and check the browser console (F12 or right-click → Inspect → Console):
Latest block: 2247
Creating account for Alice…
Alice ID: 0xd70b2072c6495d100000869a8bacf2
Creating faucet…
Faucet ID: 0x2d7e506fb88dde200000a1386efec8
Setup complete.
What's Next?
Now that you have:
- A wallet account for Alice that can hold tokens
- A faucet that can mint new MID tokens
In the next tutorial, we'll:
- Mint tokens from the faucet to Alice's account
- Consume notes
- Transfer tokens between accounts