Skip to content

Commit fec4ec5

Browse files
feat(web-client): migrate all tutorials to MidenClient API (#164)
* chore(web-client): update SDK dependency to local build * feat(web-client): migrate counter contract tutorial to MidenClient API * feat(web-client): rewrite FPI tutorial as self-contained MidenClient example * docs: update web-client tutorials to use MidenClient API * fix(docs): format markdown files with prettier * refactor(tutorials): replace AccountType string literals with enum references * refactor(tutorials): replace NoteVisibility and StorageMode string literals with enum references * refactor(tutorials): remove forced .toHex()/.toString() conversions in API calls * refactor(tutorials): migrate web-client/lib TypeScript files to MidenClient API - Replace WebClient with MidenClient.create() - Replace syncState() with sync() - Replace newWallet/newFaucet with client.accounts.create() - Replace manual tx request builders with client.transactions.mint/consume/send/submit() - Replace getConsumableNotes() with client.notes.listAvailable() - Pass account objects directly instead of .id() to transaction methods - Use NoteVisibility enum instead of 'public' string literals - Update React files to pass account objects instead of string IDs * refactor(docs): pass account objects and NoteVisibility enum in TypeScript code snippets * fix(web-client): use localhost node and pass account objects directly to SDK * refactor(tutorials): use send({ authenticated: false }) + consume() for unauthenticated notes Replace the manual TransactionRequestBuilder loop with the new send() API: - unauthenticatedNoteTransfer.ts: replace 10-line builder pattern with send({ authenticated: false }) + consume({ notes: [note] }) - unauthenticatedNoteTransfer.tsx: replace useInternalTransfer/transferChain with explicit useSend + useConsume loop - unauthenticated_note_how_to.md: update both TypeScript and React code snippets * refactor(tutorials): use proverUrl shorthand at client creation Replace per-call TransactionProver.newLocalProver() with proverUrl: 'local' in MidenClient.create(). Removes the TransactionProver import from tutorial files. * refactor(tutorials): use rpcUrl shorthand 'local' in TS client tutorials
1 parent e148c6b commit fec4ec5

19 files changed

+913
-1289
lines changed

docs/src/web-client/counter_contract_tutorial.md

Lines changed: 128 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ In this tutorial, we will interact with a counter contract already deployed on c
1111

1212
Using a script, we will invoke the increment function within the counter contract to update the count. This tutorial provides a foundational understanding of interacting with custom smart contracts on Miden.
1313

14-
## What we'll cover
14+
## What we'll cover
1515

1616
- Interacting with a custom smart contract on Miden
1717
- Calling procedures in an account from a script
@@ -96,7 +96,7 @@ export default function Home() {
9696
}
9797
```
9898

99-
## Step 3 — Incrementing the Count of the Counter Contract
99+
## Step 3 — Incrementing the Count of the Counter Contract
100100

101101
Create the file `lib/incrementCounterContract.ts` and add the following code.
102102

@@ -117,139 +117,117 @@ export async function incrementCounterContract(): Promise<void> {
117117

118118
// dynamic import → only in the browser, so WASM is loaded client‑side
119119
const {
120-
Address,
121-
AccountBuilder,
122-
AccountComponent,
123-
AccountStorageMode,
124120
AccountType,
121+
Address,
125122
AuthSecretKey,
123+
StorageMode,
126124
StorageSlot,
127-
TransactionRequestBuilder,
128-
WebClient,
125+
MidenClient,
129126
} = await import('@miden-sdk/miden-sdk');
130127

131128
const nodeEndpoint = 'https://rpc.testnet.miden.io';
132-
const client = await WebClient.createClient(nodeEndpoint);
133-
console.log('Current block number: ', (await client.syncState()).blockNum());
129+
const client = await MidenClient.create({ rpcUrl: nodeEndpoint });
130+
console.log('Current block number: ', (await client.sync()).blockNum());
134131

135132
// Counter contract code in Miden Assembly
136133
const counterContractCode = `
137-
use miden::protocol::active_account
138-
use miden::protocol::native_account
139-
use miden::core::word
140-
use miden::core::sys
141-
142-
const COUNTER_SLOT = word("miden::tutorials::counter")
143-
144-
#! Inputs: []
145-
#! Outputs: [count]
146-
pub proc get_count
147-
push.COUNTER_SLOT[0..2] exec.active_account::get_item
148-
# => [count]
149-
150-
exec.sys::truncate_stack
151-
# => [count]
152-
end
153-
154-
#! Inputs: []
155-
#! Outputs: []
156-
pub proc increment_count
157-
push.COUNTER_SLOT[0..2] exec.active_account::get_item
158-
# => [count]
159-
160-
add.1
161-
# => [count+1]
162-
163-
push.COUNTER_SLOT[0..2] exec.native_account::set_item
164-
# => []
165-
166-
exec.sys::truncate_stack
167-
# => []
168-
end
134+
use miden::protocol::active_account
135+
use miden::protocol::native_account
136+
use miden::core::word
137+
use miden::core::sys
138+
139+
const COUNTER_SLOT = word("miden::tutorials::counter")
140+
141+
#! Inputs: []
142+
#! Outputs: [count]
143+
pub proc get_count
144+
push.COUNTER_SLOT[0..2] exec.active_account::get_item
145+
# => [count]
146+
147+
exec.sys::truncate_stack
148+
# => [count]
149+
end
150+
151+
#! Inputs: []
152+
#! Outputs: []
153+
pub proc increment_count
154+
push.COUNTER_SLOT[0..2] exec.active_account::get_item
155+
# => [count]
156+
157+
add.1
158+
# => [count+1]
159+
160+
push.COUNTER_SLOT[0..2] exec.native_account::set_item
161+
# => []
162+
163+
exec.sys::truncate_stack
164+
# => []
165+
end
169166
`;
170167

171-
// Building the counter contract
172168
// Counter contract account id on testnet
173169
const counterContractId = Address.fromBech32(
174170
'mtst1arjemrxne8lj5qz4mg9c8mtyxg954483',
175171
).accountId();
176172

177173
// Reading the public state of the counter contract from testnet,
178-
// and importing it into the WebClient
179-
let counterContractAccount = await client.getAccount(counterContractId);
180-
if (!counterContractAccount) {
181-
await client.importAccountById(counterContractId);
182-
await client.syncState();
183-
counterContractAccount = await client.getAccount(counterContractId);
184-
if (!counterContractAccount) {
185-
throw new Error(`Account not found after import: ${counterContractId}`);
186-
}
187-
}
174+
// and importing it into the client
175+
const counterContractAccount =
176+
await client.accounts.getOrImport(counterContractId);
188177

189-
const builder = client.createCodeBuilder();
190178
const counterSlotName = 'miden::tutorials::counter';
191-
const counterStorageSlot = StorageSlot.emptyValue(counterSlotName);
192179

193-
const counterComponentCode =
194-
builder.compileAccountComponentCode(counterContractCode);
195-
const counterAccountComponent = AccountComponent.compile(
196-
counterComponentCode,
197-
[counterStorageSlot],
198-
).withSupportsAllTypes();
180+
// Compile the counter component
181+
const counterAccountComponent = await client.compile.component({
182+
code: counterContractCode,
183+
slots: [StorageSlot.emptyValue(counterSlotName)],
184+
});
199185

200186
const walletSeed = new Uint8Array(32);
201187
crypto.getRandomValues(walletSeed);
202188

203-
const secretKey = AuthSecretKey.rpoFalconWithRNG(walletSeed);
204-
const authComponent =
205-
AccountComponent.createAuthComponentFromSecretKey(secretKey);
206-
207-
const accountBuilderResult = new AccountBuilder(walletSeed)
208-
.accountType(AccountType.RegularAccountImmutableCode)
209-
.storageMode(AccountStorageMode.public())
210-
.withAuthComponent(authComponent)
211-
.withComponent(counterAccountComponent)
212-
.build();
189+
const auth = AuthSecretKey.rpoFalconWithRNG(walletSeed);
213190

214-
await client.addAccountSecretKeyToWebStore(
215-
accountBuilderResult.account.id(),
216-
secretKey,
217-
);
218-
await client.newAccount(accountBuilderResult.account, false);
219-
220-
await client.syncState();
221-
222-
const accountCodeLib = builder.buildLibrary(
223-
'external_contract::counter_contract',
224-
counterContractCode,
225-
);
191+
// Create the counter contract account
192+
const account = await client.accounts.create({
193+
type: AccountType.ImmutableContract,
194+
storage: StorageMode.Public,
195+
seed: walletSeed,
196+
auth,
197+
components: [counterAccountComponent],
198+
});
226199

227-
builder.linkDynamicLibrary(accountCodeLib);
200+
await client.sync();
228201

229202
// Building the transaction script which will call the counter contract
230203
const txScriptCode = `
231-
use external_contract::counter_contract
232-
begin
233-
call.counter_contract::increment_count
234-
end
204+
use external_contract::counter_contract
205+
begin
206+
call.counter_contract::increment_count
207+
end
235208
`;
236209

237-
const txScript = builder.compileTxScript(txScriptCode);
238-
const txIncrementRequest = new TransactionRequestBuilder()
239-
.withCustomScript(txScript)
240-
.build();
210+
const script = await client.compile.txScript({
211+
code: txScriptCode,
212+
libraries: [
213+
{
214+
namespace: 'external_contract::counter_contract',
215+
code: counterContractCode,
216+
},
217+
],
218+
});
241219

242220
// Executing the transaction script against the counter contract
243-
await client.submitNewTransaction(
244-
counterContractAccount.id(),
245-
txIncrementRequest,
246-
);
221+
await client.transactions.execute({
222+
account: account.id(),
223+
script,
224+
});
247225

248226
// Sync state
249-
await client.syncState();
227+
await client.sync();
250228

251229
// Logging the count of counter contract
252-
const counter = await client.getAccount(counterContractAccount.id());
230+
const counter = await client.accounts.get(counterContractAccount.id());
253231

254232
// Here we get the first Word from storage of the counter contract
255233
// A word is comprised of 4 Felts, 2**64 - 2**32 + 1
@@ -343,6 +321,58 @@ This `NoAuth` component allows any user to interact with the smart contract with
343321

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

324+
### Compiling the account component
325+
326+
Use `client.compile.component()` to compile MASM code and its storage slots into an `AccountComponent`. Each call creates a fresh compiler instance so compilations are fully independent:
327+
328+
```ts
329+
const counterAccountComponent = await client.compile.component({
330+
code: counterContractCode,
331+
slots: [StorageSlot.emptyValue(counterSlotName)],
332+
});
333+
```
334+
335+
### Creating the contract account
336+
337+
Use `client.accounts.create()` with `type: AccountType.ImmutableContract` to build and register the contract. You must supply a `seed` (for deterministic ID derivation) and a raw `AuthSecretKey` — the client stores the key automatically:
338+
339+
```ts
340+
const auth = AuthSecretKey.rpoFalconWithRNG(walletSeed);
341+
342+
const account = await client.accounts.create({
343+
type: AccountType.ImmutableContract,
344+
storage: StorageMode.Public,
345+
seed: walletSeed,
346+
auth,
347+
components: [counterAccountComponent],
348+
});
349+
```
350+
351+
### Compiling and executing the custom script
352+
353+
Use `client.compile.txScript()` to compile a transaction script. Pass any needed libraries inline — the client links them dynamically:
354+
355+
```ts
356+
const script = await client.compile.txScript({
357+
code: txScriptCode,
358+
libraries: [
359+
{
360+
namespace: 'external_contract::counter_contract',
361+
code: counterContractCode,
362+
},
363+
],
364+
});
365+
```
366+
367+
Then execute it with `client.transactions.execute()`:
368+
369+
```ts
370+
await client.transactions.execute({
371+
account: account.id(),
372+
script,
373+
});
374+
```
375+
346376
### Custom script
347377

348378
This is the Miden assembly script that calls the `increment_count` procedure during the transaction.

0 commit comments

Comments
 (0)