CosmWasm API Design Best Practices

SkillCommunication

Best practices for designing CosmWasm smart contract APIs. Use when defining message types, designing execute/query interfaces, or optimizing API ergonomics.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the CosmWasm API Design Best Practices skill

What this skill tells your AI

The instructions your AI receives, as published by axone-protocol/contracts in .agents/skills/api-design/SKILL.md and read by ahel’s review.

Core Principles

  1. Minimalism - Include only what's necessary; avoid bloated APIs
  2. Clarity - Names should be self-documenting
  3. Consistency - Follow established patterns across all contracts
  4. Documentation - Every public type and field must have doc comments

Message Type Patterns

InstantiateMsg

/// Contract instantiation message
#[cosmwasm_schema::cw_serde]
#[derive(Default)]
pub struct MyContractInstantiateMsg {
    /// Optional configuration parameter with sensible default
    #[serde(default)]
    pub some_config: Option<String>,
}

Guidelines:

  • Derive Default when possible for easier testing
  • Use #[serde(default)] for optional fields
  • Keep required fields minimal
  • Document each field

ExecuteMsg

/// Contract execute messages
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::ExecuteFns)]
pub enum MyContractExecuteMsg {
    /// Update the contract configuration
    UpdateConfig {
        /// New admin address (optional)
        new_admin: Option<String>,
    },
    /// Process an action with the given parameters
    ProcessAction {
        /// Unique identifier for the action
        action_id: String,
        /// Amount to process
        amount: Uint128,
    },
}

Guidelines:

  • Use verb-based names (Update, Process, Create, Remove)
  • Group related parameters in structs if >3 fields
  • Document each variant AND each field
  • Derive ExecuteFns for cw-orch integration

QueryMsg

/// Contract query messages
#[cosmwasm_schema::cw_serde]
#[derive(cw_orch::QueryFns, QueryResponses)]
pub enum MyContractQueryMsg {
    /// Get the current configuration
    #[returns(ConfigResponse)]
    Config {},

    /// Get item by ID
    #[returns(ItemResponse)]
    Item {
        /// The item identifier
        id: String,
    },

    /// List all items with pagination
    #[returns(ItemsResponse)]
    Items {
        /// Start after this ID for pagination
        start_after: Option<String>,
        /// Maximum number of items to return
        limit: Option<u32>,
    },
}

Guidelines:

  • Always include #[returns(ResponseType)] attribute
  • Use noun-based names for queries
  • Include pagination for list queries (start_after, limit)
  • Derive QueryFns and QueryResponses

Response Types

#[cosmwasm_schema::cw_serde]
pub struct ConfigResponse {
    /// Current admin address
    pub admin: Addr,
    /// Whether the contract is paused
    pub paused: bool,
}

#[cosmwasm_schema::cw_serde]
pub struct ItemsResponse {
    /// List of items
    pub items: Vec<ItemInfo>,
}

Guidelines:

  • Response types should mirror what clients need
  • Use specific types (Addr, Uint128) not strings
  • Document all fields

Abstract SDK Integration

Use the app_msg_types! macro to generate wrapper types:

use crate::contract::MyContract;
use cosmwasm_schema::QueryResponses;

// Generates ExecuteMsg, QueryMsg, InstantiateMsg wrappers
abstract_app::app_msg_types!(MyContract, MyContractExecuteMsg, MyContractQueryMsg);

Documentation Standards

Rust Doc Comments

/// Brief one-line description of the variant.
///
/// Optional longer description that explains:
/// - When to use this
/// - Side effects
/// - Related messages
///
/// # Errors
///
/// Returns `ContractError::Unauthorized` if caller is not admin.

Field Documentation

Every field must have a doc comment:

  • Describe what the field represents
  • Mention default values if applicable
  • Note any constraints (min/max values, format)

Serde Patterns

Optional Fields with Defaults

#[serde(default)]
pub optional_field: Option<String>,

#[serde(default = "default_limit")]
pub limit: u32,

fn default_limit() -> u32 {
    10
}

Flatten for Nested Configs

#[cosmwasm_schema::cw_serde]
pub struct InstantiateMsg {
    #[serde(flatten)]
    pub base_config: BaseConfig,
    pub custom_field: String,
}

Rename for JSON Clarity

#[serde(rename = "owner")]
pub owner_addr: Addr,

Signals

GitHub stars
123
Forks
19
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
api-design-axone-protocol
Source
github.com/axone-protocol/contracts