From Zero to First Contract
OpenJDK or Oracle JDK
With Daml extension
All platforms supported
Via rustup
rustup target add wasm32-unknown-unknown
For contract optimization
# macOS / Linux
curl -sSL https://get.daml.com/ | sh
# Windows (PowerShell as Admin)
iwr -useb https://get.daml.com/install.ps1 | iex
# Verify installation
daml version
# Create new project
daml new my-first-daml
# Enter project directory
cd my-first-daml
# Project structure created:
# ├── daml.yaml # Project config
# ├── daml/
# │ └── Main.daml # Main contract file
# └── .daml/ # Build artifacts
# Build the project
daml build
# Run tests
daml test
# Start interactive sandbox
daml start
# Sandbox running at:
# - Ledger API: localhost:6865
# - JSON API: localhost:7575
# Install Daml extension
code --install-extension digitalasset.daml
# Open project in VS Code
code .
# Features:
# - Syntax highlighting
# - Type checking
# - Go to definition
# - Script execution
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Add WASM target
rustup target add wasm32-unknown-unknown
# Install cargo-generate for templates
cargo install cargo-generate
# Verify
rustc --version
cargo --version
# Generate from official template
cargo generate --git https://github.com/CosmWasm/cw-template.git \
--name my-first-cosmwasm
cd my-first-cosmwasm
# Project structure:
# ├── Cargo.toml # Rust dependencies
# ├── src/
# │ ├── contract.rs # Main contract logic
# │ ├── msg.rs # Message types
# │ ├── state.rs # State definitions
# │ ├── error.rs # Custom errors
# │ └── lib.rs # Module exports
# └── examples/ # Schema generation
# Run tests
cargo test
# Build WASM (unoptimized)
cargo wasm
# Build optimized WASM for deployment
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/optimizer:0.15.0
# Output: artifacts/my_first_cosmwasm.wasm
# Install wasmd (CosmWasm-enabled chain)
git clone https://github.com/CosmWasm/wasmd.git
cd wasmd
make install
# Initialize local chain
wasmd init mynode --chain-id testing
# Create test account
wasmd keys add wallet
# Start local node
wasmd start
-- daml/Main.daml
module Main where
-- A simple counter that can be incremented
template Counter
with
owner : Party
count : Int
where
signatory owner
-- Increment the counter
choice Increment : ContractId Counter
controller owner
do
create this with count = count + 1
-- Reset to zero
choice Reset : ContractId Counter
controller owner
do
create this with count = 0
-- Get current value (non-consuming)
nonconsuming choice GetValue : Int
controller owner
do
return count
-- Test script
testCounter : Script ()
testCounter = script do
alice <- allocateParty "Alice"
-- Create counter starting at 0
cid <- submit alice do
createCmd Counter with
owner = alice
count = 0
-- Increment 3 times
cid <- submit alice do exerciseCmd cid Increment
cid <- submit alice do exerciseCmd cid Increment
cid <- submit alice do exerciseCmd cid Increment
-- Check value
value <- submit alice do exerciseCmd cid GetValue
assert (value == 3)
-- Reset
cid <- submit alice do exerciseCmd cid Reset
-- Verify reset
Some counter <- queryContractId alice cid
assert (counter.count == 0)
daml test
// src/contract.rs
use cosmwasm_std::{
entry_point, to_json_binary, Binary, Deps, DepsMut,
Env, MessageInfo, Response, StdResult,
};
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};
const COUNT: Item = Item::new("count");
#[derive(Serialize, Deserialize)]
pub struct InstantiateMsg {
pub initial_count: i32,
}
#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
Increment {},
Reset {},
}
#[derive(Serialize, Deserialize)]
pub enum QueryMsg {
GetCount {},
}
#[derive(Serialize, Deserialize)]
pub struct CountResponse {
pub count: i32,
}
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult {
COUNT.save(deps.storage, &msg.initial_count)?;
Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("count", msg.initial_count.to_string()))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: ExecuteMsg,
) -> StdResult {
match msg {
ExecuteMsg::Increment {} => {
let count = COUNT.load(deps.storage)?;
COUNT.save(deps.storage, &(count + 1))?;
Ok(Response::new().add_attribute("action", "increment"))
}
ExecuteMsg::Reset {} => {
COUNT.save(deps.storage, &0)?;
Ok(Response::new().add_attribute("action", "reset"))
}
}
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult {
match msg {
QueryMsg::GetCount {} => {
let count = COUNT.load(deps.storage)?;
to_json_binary(&CountResponse { count })
}
}
}
cargo test
# Start Canton
bin/canton -c canton.conf
# In Canton console:
canton> participant1.dars.upload("my-project.dar")
canton> participant1.domains.connect_local(mydomain)
# Or via Daml Script:
daml script \
--dar my-project.dar \
--script-name Setup:initialize \
--ledger-host localhost \
--ledger-port 6865
# Store contract code
wasmd tx wasm store artifacts/counter.wasm \
--from wallet \
--gas auto \
--gas-adjustment 1.3 \
-y
# Get code ID from tx result
CODE_ID=1
# Instantiate contract
wasmd tx wasm instantiate $CODE_ID \
'{"initial_count": 0}' \
--from wallet \
--label "my-counter" \
--admin $(wasmd keys show wallet -a) \
-y
# Query contract address
wasmd query wasm list-contract-by-code $CODE_ID
# Execute increment
wasmd tx wasm execute $CONTRACT_ADDR \
'{"increment": {}}' \
--from wallet -y
# Query count
wasmd query wasm contract-state smart $CONTRACT_ADDR \
'{"get_count": {}}'