Mint, Consume, and Create Notes
Using the Miden WebClient in TypeScript to mint, consume, and create notes
Overview
In the previous section, we initialized our repository and covered how to create an account and deploy a faucet. In this section, we will mint tokens from the faucet for Alice, consume the newly created notes, and demonstrate how to send assets to other accounts.
What we'll cover
- Minting assets from a faucet
- Consuming notes to fund an account
- Sending tokens to other users
Step 1: Minting tokens from the faucet
To mint notes with tokens from the faucet we created, Alice can use the WebClient's new_mint_transaction()
function.
Below is an example of a transaction request minting tokens from the faucet for Alice.
Add this snippet to the end of the webClient
function in the src/webClient.ts
file that we created in the previous chapter:
await client.fetch_and_cache_account_auth_by_pub_key(
AccountId.from_hex(faucetIdHex),
);
await client.sync_state();
console.log("Minting tokens to Alice...");
await client.new_mint_transaction(
AccountId.from_hex(aliceIdHex), // target wallet id
AccountId.from_hex(faucetIdHex), // faucet id
NoteType.public(), // note type
BigInt(1000), // amount
);
console.log("Waiting 15 seconds for transaction confirmation...");
await new Promise((resolve) => setTimeout(resolve, 15000));
await client.sync_state();
Step 2: Identifying consumable notes
Once Alice has minted a note from the faucet, she will eventually want to spend the tokens that she received in the note created by the mint transaction.
Minting a note from a faucet on Miden means a faucet account creates a new note targeted to the requesting account. The requesting account must consume this note for the assets to appear in their account.
To identify notes that are ready to consume, the Miden WebClient has a useful function get_consumable_notes
. It is also important to sync the state of the client before calling the get_consumable_notes
function.
Tip: If you know the expected number of notes after a transaction, use await
or a loop condition to verify their availability before calling get_consumable_notes
. This prevents unnecessary application idling.
Identifying which notes are available:
consumable_notes = await client.get_consumable_notes(accountId);
Step 3: Consuming multiple notes in a single transaction:
Now that we know how to identify notes ready to consume, let's consume the notes created by the faucet in a single transaction. After consuming the notes, Alice's wallet balance will be updated.
The following code snippet identifies and consumes notes in a single transaction.
Add this snippet to the end of the webClient
function in the src/webClient.ts
file:
await client.fetch_and_cache_account_auth_by_pub_key(
AccountId.from_hex(aliceIdHex),
);
const mintedNotes = await client.get_consumable_notes(
AccountId.from_hex(aliceIdHex),
);
const mintedNoteIds = mintedNotes.map((n) =>
n.input_note_record().id().to_string(),
);
console.log("Minted note IDs:", mintedNoteIds);
console.log("Consuming minted notes...");
await client.new_consume_transaction(
AccountId.from_hex(aliceIdHex), // account id
mintedNoteIds, // array of note ids to consume
);
await client.sync_state();
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 P2IDR (Pay to Id Reclaimable) variant which allows the creator of the note to reclaim the note after a certain block height.
In our example, Alice will now send 50 tokens to a different account.
Basic P2ID transfer
Now as an example, Alice will send some tokens to an account in a single transaction.
Add this snippet to the end of your file in the main()
function:
// send single P2ID note
const dummyIdHex = "0x599a54603f0cf9000000ed7a11e379";
console.log("Sending tokens to dummy account...");
await client.new_send_transaction(
AccountId.from_hex(aliceIdHex), // sender account id
AccountId.from_hex(dummyIdHex), // receiver account id
AccountId.from_hex(faucetIdHex), // faucet account id
NoteType.public(), // note type
BigInt(100), // amount
);
await client.sync_state();
Summary
Your src/webClient.ts
function should now look like this:
import {
WebClient,
AccountStorageMode,
AccountId,
NoteType,
} from "@demox-labs/miden-sdk";
const nodeEndpoint = "http://localhost:57291";
export async function webClient(): Promise<void> {
try {
// 1. Create client
const client = new WebClient();
await client.create_client(nodeEndpoint);
// 2. Sync and log block
const state = await client.sync_state();
console.log("Latest block number:", state.block_num());
// 3. Create Alice account (public, updatable)
console.log("Creating account for Alice");
const aliceAccount = await client.new_wallet(
AccountStorageMode.public(), // account type
true // mutability
);
const aliceIdHex = aliceAccount.id().to_string();
console.log("Alice's account ID:", aliceIdHex);
// 4. Create faucet
console.log("Creating faucet...");
const faucetAccount = await client.new_faucet(
AccountStorageMode.public(), // account type
false, // fungible
"MID", // symbol
8, // decimals
BigInt(1_000_000) // max supply
);
const faucetIdHex = faucetAccount.id().to_string();
console.log("Faucet account ID:", faucetIdHex);
// 5. Mint tokens to Alice
await client.fetch_and_cache_account_auth_by_pub_key(
AccountId.from_hex(faucetIdHex),
);
await client.sync_state();
console.log("Minting tokens to Alice...");
await client.new_mint_transaction(
AccountId.from_hex(aliceIdHex), // target wallet id
AccountId.from_hex(faucetIdHex), // faucet id
NoteType.public(), // note type
BigInt(1000), // amount
);
console.log("Waiting 15 seconds for transaction confirmation...");
await new Promise((resolve) => setTimeout(resolve, 15000));
await client.sync_state();
// 6. Fetch minted notes
await client.fetch_and_cache_account_auth_by_pub_key(
AccountId.from_hex(aliceIdHex),
);
const mintedNotes = await client.get_consumable_notes(
AccountId.from_hex(aliceIdHex),
);
const mintedNoteIds = mintedNotes.map((n) =>
n.input_note_record().id().to_string(),
);
console.log("Minted note IDs:", mintedNoteIds);
// 7. Consume minted notes
console.log("Consuming minted notes...");
await client.new_consume_transaction(
AccountId.from_hex(aliceIdHex), // account id
mintedNoteIds, // array of note ids to consume
);
await client.sync_state();
console.log("Notes consumed.");
// 8. Send tokens to a dummy account
const dummyIdHex = "0x599a54603f0cf9000000ed7a11e379";
console.log("Sending tokens to dummy account...");
await client.new_send_transaction(
AccountId.from_hex(aliceIdHex), // sender account id
AccountId.from_hex(dummyIdHex), // receiver account id
AccountId.from_hex(faucetIdHex), // faucet account id
NoteType.public(), // note type
BigInt(100), // amount
);
await client.sync_state();
console.log("Tokens sent.");
} catch (error) {
console.error("Error:", error);
throw error;
}
}
Let's run the src/webClient.ts
function again. Reload the page and click "Start WebClient".
Note: Currently there is a minor bug in the WebClient that produces a warning message, "Error inserting code with root" when creating multiple accounts. This is currently being fixed.
The output will look like this:
Latest block number: 4807
Alice's account ID: 0x1a20f4d1321e681000005020e69b1a
Creating faucet...
Faucet account ID: 0xaa86a6f05ae40b2000000f26054d5d
Minting tokens to Alice...
Waiting 15 seconds for transaction confirmation...
Minted note IDs: ['0x4edbb3d5dbdf6944f229a4711533114e0602ad48b70cda400993925c61f5bfaa']
Consuming minted notes...
Notes consumed.
Sending tokens to dummy account...
Tokens sent.
Resetting the MidenClientDB
The Miden webclient stores account and note data in the browser. To clear the account and node 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.");
})();
Running the example
To run a full working example navigate to the web-client
directory in the miden-tutorials repository and run the web application example:
cd web-client
pnpm i
pnpm run dev