Skip to content

Commit cf68ba8

Browse files
authored
Merge pull request #145 from 0xMiden/keinberger/v12-tutorials-cleanup
chore(web-client): v12 tutorials clean up
2 parents f8627c2 + bc141b3 commit cf68ba8

13 files changed

+1353
-2637
lines changed

docs/src/web-client/.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"printWidth": 80,
3+
"tabWidth": 2,
4+
"singleQuote": true,
5+
"trailingComma": "all"
6+
}

docs/src/web-client/about.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Web Client"
2+
title: 'Web Client'
33
sidebar_position: 1
44
---
55

docs/src/web-client/counter_contract_tutorial.md

Lines changed: 115 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
---
2-
title: "Incrementing the Count of the Counter Contract"
2+
title: 'Incrementing the Count of the Counter Contract'
33
sidebar_position: 5
44
---
55

6-
# Incrementing the Count of the Counter Contract
7-
86
_Using the Miden WebClient to interact with a custom smart contract_
97

108
## Overview
@@ -22,7 +20,7 @@ Using a script, we will invoke the increment function within the counter contrac
2220

2321
- Node `v20` or greater
2422
- Familiarity with TypeScript
25-
- `pnpm`
23+
- `yarn`
2624

2725
This tutorial assumes you have a basic understanding of Miden assembly. To quickly get up to speed with Miden assembly (MASM), please play around with running basic Miden assembly programs in the [Miden playground](https://0xmiden.github.io/examples/).
2826

@@ -31,7 +29,7 @@ This tutorial assumes you have a basic understanding of Miden assembly. To quick
3129
1. Create a new Next.js app with TypeScript:
3230

3331
```bash
34-
npx create-next-app@latest miden-web-app --typescript
32+
yarn create next-app@latest miden-web-app --typescript
3533
```
3634

3735
Hit enter for all terminal prompts.
@@ -44,7 +42,7 @@ This tutorial assumes you have a basic understanding of Miden assembly. To quick
4442

4543
3. Install the Miden WebClient SDK:
4644
```bash
47-
pnpm i @demox-labs/miden-sdk@0.11.1
45+
yarn add @demox-labs/miden-sdk@0.12.3
4846
```
4947

5048
**NOTE!**: Be sure to add the `--webpack` command to your `package.json` when running the `dev script`. The dev script should look like this:
@@ -63,9 +61,9 @@ This tutorial assumes you have a basic understanding of Miden assembly. To quick
6361
Add the following code to the `app/page.tsx` file. This code defines the main page of our web application:
6462

6563
```tsx
66-
"use client";
67-
import { useState } from "react";
68-
import { incrementCounterContract } from "../lib/incrementCounterContract";
64+
'use client';
65+
import { useState } from 'react';
66+
import { incrementCounterContract } from '../lib/incrementCounterContract';
6967

7068
export default function Home() {
7169
const [isIncrementCounter, setIsIncrementCounter] = useState(false);
@@ -88,8 +86,8 @@ export default function Home() {
8886
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"
8987
>
9088
{isIncrementCounter
91-
? "Working..."
92-
: "Tutorial #3: Increment Counter Contract"}
89+
? 'Working...'
90+
: 'Tutorial #3: Increment Counter Contract'}
9391
</button>
9492
</div>
9593
</div>
@@ -112,77 +110,80 @@ Copy and paste the following code into the `lib/incrementCounterContract.ts` fil
112110
```ts
113111
// lib/incrementCounterContract.ts
114112
export async function incrementCounterContract(): Promise<void> {
115-
if (typeof window === "undefined") {
116-
console.warn("webClient() can only run in the browser");
113+
if (typeof window === 'undefined') {
114+
console.warn('webClient() can only run in the browser');
117115
return;
118116
}
119117

120118
// dynamic import → only in the browser, so WASM is loaded client‑side
121119
const {
122120
AccountId,
123-
AssemblerUtils,
124-
TransactionKernel,
121+
AccountBuilder,
122+
AccountComponent,
123+
AccountStorageMode,
124+
AccountType,
125+
SecretKey,
126+
StorageMap,
127+
StorageSlot,
125128
TransactionRequestBuilder,
126-
TransactionScript,
127129
WebClient,
128-
} = await import("@demox-labs/miden-sdk");
130+
} = await import('@demox-labs/miden-sdk');
129131

130-
const nodeEndpoint = "https://rpc.testnet.miden.io";
132+
const nodeEndpoint = 'https://rpc.testnet.miden.io';
131133
const client = await WebClient.createClient(nodeEndpoint);
132-
console.log("Current block number: ", (await client.syncState()).blockNum());
134+
console.log('Current block number: ', (await client.syncState()).blockNum());
133135

134136
// Counter contract code in Miden Assembly
135137
const counterContractCode = `
136-
use.miden::account
137-
use.std::sys
138+
use.miden::active_account
139+
use miden::native_account
140+
use.std::sys
138141
139-
const.COUNTER_SLOT=0
142+
const.COUNTER_SLOT=0
140143
141-
#! Inputs: []
142-
#! Outputs: [count]
143-
export.get_count
144-
push.COUNTER_SLOT
145-
# => [index]
144+
#! Inputs: []
145+
#! Outputs: [count]
146+
export.get_count
147+
push.COUNTER_SLOT
148+
# => [index]
146149
147-
exec.account::get_item
148-
# => [count]
150+
exec.active_account::get_item
151+
# => [count]
149152
150-
# clean up stack
151-
movdn.4 dropw
152-
# => [count]
153-
end
153+
# clean up stack
154+
movdn.4 dropw
155+
# => [count]
156+
end
154157
155-
#! Inputs: []
156-
#! Outputs: []
157-
export.increment_count
158-
push.COUNTER_SLOT
159-
# => [index]
158+
#! Inputs: []
159+
#! Outputs: []
160+
export.increment_count
161+
push.COUNTER_SLOT
162+
# => [index]
160163
161-
exec.account::get_item
162-
# => [count]
164+
exec.active_account::get_item
165+
# => [count]
163166
164-
add.1
165-
# => [count+1]
167+
add.1
168+
# => [count+1]
166169
167-
debug.stack
170+
debug.stack
168171
169-
push.COUNTER_SLOT
170-
# [index, count+1]
172+
push.COUNTER_SLOT
173+
# [index, count+1]
171174
172-
exec.account::set_item
173-
# => [OLD_VALUE]
175+
exec.native_account::set_item
176+
# => [OLD_VALUE]
174177
175-
dropw
176-
# => []
177-
end
178-
`;
178+
dropw
179+
# => []
180+
end
181+
`;
179182

180183
// Building the counter contract
181-
let assembler = TransactionKernel.assembler();
182-
183184
// Counter contract account id on testnet
184-
const counterContractId = AccountId.fromBech32(
185-
"mtst1qre73e6qcrfevqqngx8wewvveacqqjh8p2a",
185+
const counterContractId = AccountId.fromHex(
186+
'0xe59d8cd3c9ff2a0055da0b83ed6432',
186187
);
187188

188189
// Reading the public state of the counter contract from testnet,
@@ -197,64 +198,83 @@ export async function incrementCounterContract(): Promise<void> {
197198
}
198199
}
199200

200-
// Building the transaction script which will call the counter contract
201-
let txScriptCode = `
202-
use.external_contract::counter_contract
203-
begin
204-
call.counter_contract::increment_count
205-
end
206-
`;
207-
208-
// Creating the library to call the counter contract
209-
let counterComponentLib = AssemblerUtils.createAccountComponentLibrary(
210-
assembler, // assembler
211-
"external_contract::counter_contract", // library path to call the contract
212-
counterContractCode, // account code of the contract
213-
);
201+
const builder = client.createScriptBuilder();
202+
const storageMap = new StorageMap();
203+
const storageSlotMap = StorageSlot.map(storageMap);
204+
205+
const mappingAccountComponent = AccountComponent.compile(
206+
counterContractCode,
207+
builder,
208+
[storageSlotMap],
209+
).withSupportsAllTypes();
210+
211+
const walletSeed = new Uint8Array(32);
212+
crypto.getRandomValues(walletSeed);
213+
214+
const secretKey = SecretKey.rpoFalconWithRNG(walletSeed);
215+
const authComponent = AccountComponent.createAuthComponent(secretKey);
216+
217+
const accountBuilderResult = new AccountBuilder(walletSeed)
218+
.accountType(AccountType.RegularAccountImmutableCode)
219+
.storageMode(AccountStorageMode.public())
220+
.withAuthComponent(authComponent)
221+
.withComponent(mappingAccountComponent)
222+
.build();
223+
224+
await client.addAccountSecretKeyToWebStore(secretKey);
225+
await client.newAccount(accountBuilderResult.account, false);
214226

215-
// Creating the transaction script
216-
let txScript = TransactionScript.compile(
217-
txScriptCode,
218-
assembler.withLibrary(counterComponentLib),
227+
await client.syncState();
228+
229+
const accountCodeLib = builder.buildLibrary(
230+
'external_contract::counter_contract',
231+
counterContractCode,
219232
);
220233

221-
// Creating a transaction request with the transaction script
222-
let txIncrementRequest = new TransactionRequestBuilder()
234+
builder.linkDynamicLibrary(accountCodeLib);
235+
236+
// Building the transaction script which will call the counter contract
237+
const txScriptCode = `
238+
use.external_contract::counter_contract
239+
begin
240+
call.counter_contract::increment_count
241+
end
242+
`;
243+
244+
const txScript = builder.compileTxScript(txScriptCode);
245+
const txIncrementRequest = new TransactionRequestBuilder()
223246
.withCustomScript(txScript)
224247
.build();
225248

226249
// Executing the transaction script against the counter contract
227-
let txResult = await client.newTransaction(
250+
await client.submitNewTransaction(
228251
counterContractAccount.id(),
229252
txIncrementRequest,
230253
);
231254

232-
// Submitting the transaction result to the node
233-
await client.submitTransaction(txResult);
234-
235255
// Sync state
236256
await client.syncState();
237257

238258
// Logging the count of counter contract
239-
let counter = await client.getAccount(counterContractAccount.id());
259+
const counter = await client.getAccount(counterContractAccount.id());
240260

241261
// Here we get the first Word from storage of the counter contract
242262
// A word is comprised of 4 Felts, 2**64 - 2**32 + 1
243-
let count = counter?.storage().getItem(0);
263+
const count = counter?.storage().getItem(0);
244264

245265
// Converting the Word represented as a hex to a single integer value
246266
const counterValue = Number(
247-
BigInt("0x" + count!.toHex().slice(-16).match(/../g)!.reverse().join("")),
267+
BigInt('0x' + count!.toHex().slice(-16).match(/../g)!.reverse().join('')),
248268
);
249269

250-
console.log("Count: ", counterValue);
270+
console.log('Count: ', counterValue);
251271
}
252272
```
253273

254274
To run the code above in our frontend, run the following command:
255275

256276
```
257-
pnpm run dev
277+
yarn dev
258278
```
259279

260280
Open the browser console and click the button "Increment Counter Contract".
@@ -287,7 +307,8 @@ incrementCounterContract.ts:153 Count: 3
287307
8. Calls `sys::truncate_stack` to truncate the stack to size 16.
288308

289309
```masm
290-
use.miden::account
310+
use.miden::active_account
311+
use miden::native_account
291312
use.std::sys
292313
293314
const.COUNTER_SLOT=0
@@ -298,7 +319,7 @@ export.get_count
298319
push.COUNTER_SLOT
299320
# => [index]
300321
301-
exec.account::get_item
322+
exec.active_account::get_item
302323
# => [count]
303324
304325
# clean up stack
@@ -312,7 +333,7 @@ export.increment_count
312333
push.COUNTER_SLOT
313334
# => [index]
314335
315-
exec.account::get_item
336+
exec.active_account::get_item
316337
# => [count]
317338
318339
add.1
@@ -323,7 +344,7 @@ export.increment_count
323344
push.COUNTER_SLOT
324345
# [index, count+1]
325346
326-
exec.account::set_item
347+
exec.native_account::set_item
327348
# => [OLD_VALUE]
328349
329350
dropw
@@ -333,13 +354,11 @@ end
333354

334355
**Note**: _It's a good habit to add comments below each line of MASM code with the expected stack state. This improves readability and helps with debugging._
335356

336-
### Concept of function visibility and modifiers in Miden smart contracts
337-
338-
The `export.increment_count` function in our Miden smart contract behaves like an "external" Solidity function without a modifier, meaning any user can call it to increment the contract's count. This is because it calls `account::incr_nonce` during execution. For internal procedures, use the `proc` keyword as opposed to `export`.
357+
### Authentication Component
339358

340-
If the `increment_count` procedure did not call the `account::incr_nonce` procedure during its execution, only the deployer of the counter contract would be able to increment the count of the smart contract (if the RpoFalcon512 component was added to the account, in this case we didn't add it).
359+
**Important**: Starting with Miden Client 0.10.0, all accounts must have an authentication component. For smart contracts that don't require authentication (like our counter contract), we use a `NoAuth` component.
341360

342-
In essence, if a procedure performs a state change in the Miden smart contract, and does not call `account::incr_nonce` at some point during its execution, this function can be equated to having an `onlyOwner` Solidity modifer, meaning only the user with knowledge of the private key of the account can execute transactions that result in a state change.
361+
This `NoAuth` component allows any user to interact with the smart contract without requiring signature verification.ivate key of the account can execute transactions that result in a state change.
343362

344363
**Note**: _Adding the `account::incr_nonce` to a state changing procedure allows any user to call the procedure._
345364

@@ -361,8 +380,8 @@ To run a full working example navigate to the `web-client` directory in the [mid
361380

362381
```bash
363382
cd web-client
364-
pnpm i
365-
pnpm run start
383+
yarn install
384+
yarn start
366385
```
367386

368387
### Resetting the `MidenClientDB`
@@ -376,6 +395,6 @@ The Miden webclient stores account and note data in the browser. If you get erro
376395
await indexedDB.deleteDatabase(db.name);
377396
console.log(`Deleted database: ${db.name}`);
378397
}
379-
console.log("All databases deleted.");
398+
console.log('All databases deleted.');
380399
})();
381400
```

0 commit comments

Comments
 (0)