api-design

star 123

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

axone-protocol By axone-protocol schedule Updated 3/28/2026

name: api-design

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

license: BSD-3-Clause

metadata:

author: axone.xyz

version: "1.0"


CosmWasm API Design Best Practices

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,
Install via CLI
npx skills add https://github.com/axone-protocol/contracts --skill api-design
Repository Details
star Stars 123
call_split Forks 20
navigation Branch main
article Path SKILL.md
More from Creator
axone-protocol
axone-protocol Explore all skills →