Mint, Consume, and Transfer Assets
Using the Miden WebClient in TypeScript to mint, consume, and transfer assets
Overview
In the previous tutorial, we set up the foundation - creating Alice's wallet and deploying a faucet. Now we'll put these to use by minting and transferring assets.
What we'll cover
- Minting assets from a faucet
- Consuming notes to fund an account
- Sending tokens to other users
Prerequisites
This tutorial builds directly on the previous one. Make sure you have:
- Completed the "Creating Accounts and Deploying Faucets" tutorial
- Your Next.js app with the Miden WebClient set up
Understanding Notes in Miden
Before we start coding, it's important to understand notes:
- Minting a note from a faucet does not automatically add the tokens to your account balance. It creates a note addressed to you.
- You must consume a note to add its tokens to your account balance.
- Until consumed, tokens exist in the note but aren't in your account yet.
Step 1: Mint tokens from the faucet
Let's mint some tokens for Alice. When we mint from a faucet, it creates a note containing the specified amount of tokens targeted to Alice's account.
Add this to the end of your createMintConsume
function in lib/createMintConsume.ts
:
// 4. Mint tokens from the faucet to Alice
await client.syncState();
console.log("Minting 1000 tokens to Alice...");
const mintTxRequest = client.newMintTransactionRequest(
alice.id(), // Target account (who receives the tokens)
faucet.id(), // Faucet account (who mints the tokens)
NoteType.Public, // Note visibility (public = on-chain)
BigInt(1000), // Amount to mint (in base units)
);
const mintTx = await client.newTransaction(faucet.id(), mintTxRequest);
await client.submitTransaction(mintTx);
// Wait for the transaction to be processed
console.log("Waiting 10 seconds for transaction confirmation...");
await new Promise((resolve) => setTimeout(resolve, 10000));
await client.syncState();
What's happening here?
- newMintTransactionRequest: Creates a request to mint tokens to Alice. Note that this is only possible to submit transactions on the faucets' behalf if the user controls the faucet (i.e. its keys are stored in the client).
- newTransaction: Locally executes and proves the transaction.
- submitTransaction: Sends the transaction to the network.
- Wait 10 seconds for the transaction to be included in a block.
Step 2: Find consumable notes
After minting, Alice has a note waiting for her but the tokens aren't in her account yet.
To identify notes that are ready to consume, the Miden WebClient provides the getConsumableNotes
function:
// 5. Find notes available for consumption
const consumableNotes = await client.getConsumableNotes(alice.id());
console.log(`Found ${consumableNotes.length} note(s) to consume`);
const noteIds = consumableNotes.map((note) =>
note.inputNoteRecord().id().toString(),
);
console.log("Consumable note IDs:", noteIds);
Step 3: Consume notes in a single transaction
Now let's consume the notes to add the tokens to Alice's account balance:
// 6. Consume the notes to add tokens to Alice's balance
console.log("Consuming notes...");
const consumeTxRequest = client.newConsumeTransactionRequest(noteIds);
const consumeTx = await client.newTransaction(alice.id(), consumeTxRequest);
await client.submitTransaction(consumeTx);
await client.syncState();
console.log("Notes consumed.");
Step 4: Sending tokens to other accounts
After consuming the notes, Alice has tokens in her wallet. Now, she wants to send tokens to her friends. She has two options: create a separate transaction for each transfer or batch multiple notes in a single transaction.
The standard asset transfer note on Miden is the P2ID note (Pay-to-Id). There is also the P2IDE (Pay-to-Id Extended) variant which allows for both timelocking the note (target can only spend the note after a certain block height) and for the note to be reclaimable (the creator of the note can reclaim the note after a certain block height).
Now that Alice has tokens in her account, she can send some to Bob:
// Add this import at the top of the file
import { NoteType } from "@demox-labs/miden-sdk";
// ...
// 7. Send tokens from Alice to Bob
const bobAccountId = "0x599a54603f0cf9000000ed7a11e379";
console.log("Sending 100 tokens to Bob...");
const sendTxRequest = client.newSendTransactionRequest(
alice.id(), // Sender account
AccountId.fromHex(bobAccountId), // Recipient account
faucet.id(), // Asset ID (faucet that created the tokens)
NoteType.Public, // Note visibility
BigInt(100), // Amount to send
);
const sendTx = await client.newTransaction(alice.id(), sendTxRequest);
await client.submitTransaction(sendTx);
console.log("Tokens sent successfully!");
Understanding P2ID notes
The transaction creates a P2ID (Pay-to-ID) note:
- It's the standard way to transfer assets in Miden
- The note is "locked" to Bob's account ID, i.e. only Bob can consume this note to receive the tokens
- Public notes are visible onchain; private notes would need to be shared offchain (e.g. via a private channel)
Summary
Here's the complete lib/createMintConsume.ts
file:
// lib/createMintConsume.ts
import { NoteType } from "@demox-labs/miden-sdk";
export async function createMintConsume(): Promise<void> {
if (typeof window === "undefined") {
console.warn("webClient() can only run in the browser");
return;
}
const { WebClient, AccountStorageMode, AccountId, NoteType } = 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 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());
await client.syncState();
// 4. Mint tokens from the faucet to Alice
console.log("Minting 1000 tokens to Alice...");
const mintTxRequest = client.newMintTransactionRequest(
alice.id(),
faucet.id(),
NoteType.Public,
BigInt(1000),
);
const mintTx = await client.newTransaction(faucet.id(), mintTxRequest);
await client.submitTransaction(mintTx);
console.log("Waiting 10 seconds for transaction confirmation...");
await new Promise((resolve) => setTimeout(resolve, 10000));
await client.syncState();
// 5. Find notes available for consumption
const consumableNotes = await client.getConsumableNotes(alice.id());
const noteIds = consumableNotes.map((note) =>
note.inputNoteRecord().id().toString(),
);
console.log("Consumable note IDs:", noteIds);
// 6. Consume the notes
console.log("Consuming notes...");
const consumeTxRequest = client.newConsumeTransactionRequest(noteIds);
const consumeTx = await client.newTransaction(alice.id(), consumeTxRequest);
await client.submitTransaction(consumeTx);
await client.syncState();
console.log("Notes consumed.");
// 7. Send tokens to Bob
const bobAccountId = "0x599a54603f0cf9000000ed7a11e379";
console.log("Sending 100 tokens to Bob...");
const sendTxRequest = client.newSendTransactionRequest(
alice.id(),
AccountId.fromHex(bobAccountId),
faucet.id(),
NoteType.Public,
BigInt(100),
);
const sendTx = await client.newTransaction(alice.id(), sendTxRequest);
await client.submitTransaction(sendTx);
console.log("Tokens sent successfully!");
}
Let's run the lib/createMintConsume.ts
function again. Reload the page and click "Start WebClient".
The output will look like this:
Latest block number: 4807
Creating account for Alice...
Alice ID: 0x1a20f4d1321e681000005020e69b1a
Creating faucet...
Faucet ID: 0xaa86a6f05ae40b2000000f26054d5d
Minting 1000 tokens to Alice...
Waiting 10 seconds for transaction confirmation...
Consumable note IDs: ['0x4edbb3d5dbdf694...']
Consuming notes...
Notes consumed.
Sending 100 tokens to Bob...
Tokens sent successfully!
Resetting the MidenClientDB
The Miden webclient stores account and note data in the browser. To clear the account and note data in the browser, paste this code snippet into the browser console:
(async () => {
const dbs = await indexedDB.databases(); // Get all database names
for (const db of dbs) {
await indexedDB.deleteDatabase(db.name);
console.log(`Deleted database: ${db.name}`);
}
console.log("All databases deleted.");
})();
What's next?
You've now learned the complete note lifecycle in Miden:
- Minting - Creating new tokens from a faucet (issued in notes)
- Consuming - Adding tokens from notes to an account
- Transferring - Sending tokens to other accounts
In the next tutorials, we'll explore:
- Creating multiple notes in a single transaction
- Delegated proving