Skip to main content

Project Scaffolding

The xyz init command creates a complete CosmWasm contract project with all the necessary files and configurations.

Usage

xyz init <project-name>

Options

FlagDescriptionDefault
--templateContract templatecw20
--forceOverwrite existing directoryfalse

Create a Project

xyz init my-contract
Creating CosmWasm project: my-contract

Files created:
  my-contract/Cargo.toml
  my-contract/src/lib.rs
  my-contract/src/contract.rs
  my-contract/src/msg.rs
  my-contract/src/state.rs
  my-contract/src/error.rs
  my-contract/.gitignore
  my-contract/README.md

Project created successfully!

Next steps:
  cd my-contract
  xyz program build

Project Structure

my-contract/
├── Cargo.toml           # Rust dependencies and build config
├── src/
│   ├── lib.rs          # Contract entry point exports
│   ├── contract.rs     # Main contract logic
│   ├── msg.rs          # Message type definitions
│   ├── state.rs        # State storage items
│   └── error.rs        # Custom error types
├── .gitignore          # Git ignore file
└── README.md           # Build and deploy instructions

File Contents

Cargo.toml

[package]
name = "my-contract"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
cosmwasm-std = "2.0"
cosmwasm-schema = "2.0"
cw-storage-plus = "2.0"
schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = "1.0"

[profile.release]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
codegen-units = 1
panic = 'abort'
incremental = false
overflow-checks = true

src/lib.rs

pub mod contract;
pub mod error;
pub mod msg;
pub mod state;

pub use crate::error::ContractError;

src/msg.rs

use cosmwasm_schema::{cw_serde, QueryResponses};

#[cw_serde]
pub struct InstantiateMsg {
    pub count: i32,
}

#[cw_serde]
pub enum ExecuteMsg {
    Increment {},
    Decrement {},
    Reset { count: i32 },
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
    #[returns(CountResponse)]
    GetCount {},
}

#[cw_serde]
pub struct CountResponse {
    pub count: i32,
}

src/state.rs

use cw_storage_plus::Item;

pub const COUNT: Item<i32> = Item::new("count");

src/error.rs

use cosmwasm_std::StdError;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ContractError {
    #[error("{0}")]
    Std(#[from] StdError),

    #[error("Unauthorized")]
    Unauthorized {},
}

src/contract.rs

use cosmwasm_std::{
    entry_point, to_json_binary, Binary, Deps, DepsMut,
    Env, MessageInfo, Response, StdResult,
};

use crate::error::ContractError;
use crate::msg::{CountResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::COUNT;

#[entry_point]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response, ContractError> {
    COUNT.save(deps.storage, &msg.count)?;
    Ok(Response::new().add_attribute("method", "instantiate"))
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::Increment {} => {
            COUNT.update(deps.storage, |c| -> StdResult<_> { Ok(c + 1) })?;
            Ok(Response::new().add_attribute("method", "increment"))
        }
        ExecuteMsg::Decrement {} => {
            COUNT.update(deps.storage, |c| -> StdResult<_> { Ok(c - 1) })?;
            Ok(Response::new().add_attribute("method", "decrement"))
        }
        ExecuteMsg::Reset { count } => {
            COUNT.save(deps.storage, &count)?;
            Ok(Response::new().add_attribute("method", "reset"))
        }
    }
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetCount {} => {
            let count = COUNT.load(deps.storage)?;
            to_json_binary(&CountResponse { count })
        }
    }
}

Templates

CW20 Template (Default)

Token contract template based on CW20 standard:
xyz init my-token --template cw20
Creates a fungible token contract with:
  • Token minting
  • Transfers
  • Balance queries
  • Allowances

Overwrite Existing

To overwrite an existing directory:
xyz init my-contract --force
This will delete all existing files in the directory!

Next Steps

After scaffolding:
# Navigate to project
cd my-contract

# Build the contract
xyz program build

# Start local network
xyz localnet start

# Deploy
xyz program deploy artifacts/my_contract.wasm --from alice

Customizing the Contract

Add New Execute Messages

  1. Add variant to ExecuteMsg in src/msg.rs:
#[cw_serde]
pub enum ExecuteMsg {
    Increment {},
    Decrement {},
    Reset { count: i32 },
    // Add new message
    Double {},
}
  1. Handle in src/contract.rs:
ExecuteMsg::Double {} => {
    COUNT.update(deps.storage, |c| -> StdResult<_> { Ok(c * 2) })?;
    Ok(Response::new().add_attribute("method", "double"))
}

Add New Queries

  1. Add variant to QueryMsg in src/msg.rs:
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
    #[returns(CountResponse)]
    GetCount {},
    #[returns(IsPositiveResponse)]
    IsPositive {},
}

#[cw_serde]
pub struct IsPositiveResponse {
    pub is_positive: bool,
}
  1. Handle in src/contract.rs:
QueryMsg::IsPositive {} => {
    let count = COUNT.load(deps.storage)?;
    to_json_binary(&IsPositiveResponse {
        is_positive: count > 0
    })
}

Add State

  1. Define in src/state.rs:
use cw_storage_plus::{Item, Map};

pub const COUNT: Item<i32> = Item::new("count");
pub const OWNER: Item<Addr> = Item::new("owner");
pub const SCORES: Map<&Addr, u32> = Map::new("scores");
  1. Use in contract:
// Save owner during instantiate
OWNER.save(deps.storage, &info.sender)?;

// Read owner
let owner = OWNER.load(deps.storage)?;

// Save to map
SCORES.save(deps.storage, &user_addr, &100)?;

// Read from map
let score = SCORES.load(deps.storage, &user_addr)?;

Troubleshooting

Use --force to overwrite:
xyz init my-contract --force
Check write permissions for the current directory.