CosmWasm API Design Best Practices
SkillCommunicationBest 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.
No other account needed.
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
- Minimalism - Include only what's necessary; avoid bloated APIs
- Clarity - Names should be self-documenting
- Consistency - Follow established patterns across all contracts
- 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
Defaultwhen 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
ExecuteFnsfor 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
QueryFnsandQueryResponses
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