Build on Asentum
Deploy Your First Contract
Estimated read time: 10 minutes
Prerequisites
- Node.js 20+ installed
- An Asentum wallet — see Wallets & Accounts
- Some testnet ASE for gas (grab some from the testnet faucet)
- A text editor — VS Code, Vim, anything you like writing JavaScript in
Write the contract
An Asentum smart contract is plain JavaScript: a script whose last expression is an object literal. No compiler. No DSL. No new syntax. Save the following as counter.js:
// The last expression is an object: its methods are the contract.
({
// init does not run at deploy. Call it once, right after.
init() {
assert(!storage.has('owner'), 'already initialized');
storage.set('owner', msg.sender);
storage.set('count', '0');
},
increment() {
// Storage holds strings: convert to BigInt for math, back to a string to store.
const next = BigInt(storage.get('count')) + 1n;
storage.set('count', next.toString());
return next.toString();
},
reset() {
assert(msg.sender === storage.get('owner'), 'unauthorized');
storage.set('count', '0');
},
get() {
return storage.get('count') || '0';
},
})That's the entire contract. Note: state lives in storage, which stores strings only, so the count is kept as a decimal string and read back with BigInt(). msg.sender is the caller, and this is not available in methods. The contract runs inside a Hardened JavaScript (SES) sandbox: it can't touch the network, the filesystem, the clock, or random sources. It's just data and logic.
Test it locally
Paste the contract into the IDE to deploy and call it on testnet from the browser, or run it in memory with the VM package and Vitest as shown in Testing Contracts.
Deploy to testnet
asentum deploy counter.js --from <account> --init-args '[]'The CLI signs the deploy transaction with your account, broadcasts it, then sends the init call (with the arguments from --init-args) and prints the contract's ase1 address once both are confirmed. Leave out --init-args to deploy without calling init; you can call it later with asentum call.
Your contract is now immutable. The source you just deployed is what runs forever, unless you explicitly built in a proxy pattern with a configurable upgrade authority.
Call your contract
From the SDK:
import { AsentumClient, AsentumContract } from '@asentum/sdk';
const client = new AsentumClient('https://testnet.asentum.com');
const counter = new AsentumContract(client, 'ase1...your-contract');
// Mutating call (signs a transaction with a connected wallet)
await (await counter.connect(wallet).send('increment')).wait();
// Read-only call (no signature, no gas)
const current = await counter.view('get');
console.log('Counter is now', current); // a string, e.g. "1"Cross-contract calls use the E(address).method() syntax. Calls execute synchronously and return the callee's value directly. A call-stack guard blocks reentrancy: if the target tries to call back into a contract already on the stack, the call reverts. From your application code (not from another contract), the SDK handles this transparently.
Verify the source
Verification on Asentum is free, automatic, and trivial. Because contracts deploy as plain JavaScript source (not bytecode), verification is just a hash compare:
hash(submitted_source) == deployed_code_hashThe block explorer shows your contract source directly with syntax highlighting, comments, everything you wrote. Anyone can hash the file locally with any BLAKE3 implementation and confirm it matches the deployed code.
Next steps
- Read the contract model: cross-contract calls, immutability, source verification, modular pricing
- SDK & CLI reference — every command, every API method
- Interact with a contract — for users of contracts you didn't write