Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions web-client/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 80,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all"
}
123 changes: 74 additions & 49 deletions web-client/lib/incrementCounterContract.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,57 @@
// lib/incrementCounterContract.ts
export async function incrementCounterContract(): Promise<void> {
if (typeof window === "undefined") {
console.warn("webClient() can only run in the browser");
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 {
AccountId,
AssemblerUtils,
TransactionKernel,
AccountBuilder,
AccountComponent,
AccountStorageMode,
AccountType,
SecretKey,
StorageMap,
StorageSlot,
TransactionRequestBuilder,
TransactionScript,
WebClient,
} = await import("@demox-labs/miden-sdk");
} = await import('@demox-labs/miden-sdk');

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

// Counter contract code in Miden Assembly
const counterContractCode = `
use.miden::account
use.miden::active_account
use miden::native_account
use.std::sys

const.COUNTER_SLOT=0

# => []
#! Inputs: []
#! Outputs: [count]
export.get_count
push.COUNTER_SLOT
# => [index]

exec.account::get_item
exec.active_account::get_item
# => [count]

exec.sys::truncate_stack
# => []
# clean up stack
movdn.4 dropw
# => [count]
end

# => []
#! Inputs: []
#! Outputs: []
export.increment_count
push.COUNTER_SLOT
# => [index]

exec.account::get_item
exec.active_account::get_item
# => [count]

add.1
Expand All @@ -54,20 +62,18 @@ export async function incrementCounterContract(): Promise<void> {
push.COUNTER_SLOT
# [index, count+1]

exec.account::set_item
# => []
exec.native_account::set_item
# => [OLD_VALUE]

exec.sys::truncate_stack
dropw
# => []
end
`;
`;

// Building the counter contract
let assembler = TransactionKernel.assembler();

// Counter contract account id on testnet
const counterContractId = AccountId.fromBech32(
"mtst1qre73e6qcrfevqqngx8wewvveacqqjh8p2a",
const counterContractId = AccountId.fromHex(
'0xe59d8cd3c9ff2a0055da0b83ed6432',
);

// Reading the public state of the counter contract from testnet,
Expand All @@ -82,55 +88,74 @@ export async function incrementCounterContract(): Promise<void> {
}
}

const builder = client.createScriptBuilder();
const storageMap = new StorageMap();
const storageSlotMap = StorageSlot.map(storageMap);

const mappingAccountComponent = AccountComponent.compile(
counterContractCode,
builder,
[storageSlotMap],
).withSupportsAllTypes();

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

const secretKey = SecretKey.rpoFalconWithRNG(walletSeed);
const authComponent = AccountComponent.createAuthComponent(secretKey);

const accountBuilderResult = new AccountBuilder(walletSeed)
.accountType(AccountType.RegularAccountImmutableCode)
.storageMode(AccountStorageMode.public())
.withAuthComponent(authComponent)
.withComponent(mappingAccountComponent)
.build();

await client.addAccountSecretKeyToWebStore(secretKey);
await client.newAccount(accountBuilderResult.account, false);

await client.syncState();

const accountCodeLib = builder.buildLibrary(
'external_contract::counter_contract',
counterContractCode,
);

builder.linkDynamicLibrary(accountCodeLib);

// Building the transaction script which will call the counter contract
let txScriptCode = `
const txScriptCode = `
use.external_contract::counter_contract
begin
call.counter_contract::increment_count
call.counter_contract::increment_count
end
`;
`;

// Creating the library to call the counter contract
let counterComponentLib = AssemblerUtils.createAccountComponentLibrary(
assembler, // assembler
"external_contract::counter_contract", // library path to call the contract
counterContractCode, // account code of the contract
);

// Creating the transaction script
let txScript = TransactionScript.compile(
txScriptCode,
assembler.withLibrary(counterComponentLib),
);

// Creating a transaction request with the transaction script
let txIncrementRequest = new TransactionRequestBuilder()
const txScript = builder.compileTxScript(txScriptCode);
const txIncrementRequest = new TransactionRequestBuilder()
.withCustomScript(txScript)
.build();

// Executing the transaction script against the counter contract
let txResult = await client.newTransaction(
await client.submitNewTransaction(
counterContractAccount.id(),
txIncrementRequest,
);

// Submitting the transaction result to the node
await client.submitTransaction(txResult);

// Sync state
await client.syncState();

// Logging the count of counter contract
let counter = await client.getAccount(counterContractAccount.id());
const counter = await client.getAccount(counterContractAccount.id());

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

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

console.log("Count: ", counterValue);
console.log('Count: ', counterValue);
}