This site requires Javascript to be enabled.

Entry points

In CosmWasm, entry points are the core functions that handle interactions with smart contracts. These entry points enable contracts to process different types of messages and queries, forming the backbone of contract functionality.

Primary entry points

The three primary entry points in a CosmWasm contract are:

  • Instantiate: Handles contract initialization based on the parameters provided in the InstantiateMsg struct.
  • Execute: Manages operational commands as defined by the ExecuteMsg enum, using pattern matching to determine the action to perform.
  • Query: Processes read-only queries as defined by the QueryMsg enum, also using pattern matching to return relevant data.

Each of these entry points has a specific signature that defines how it interacts with the contract’s environment and state.

Instantiate

The instantiate function is called when the contract is first deployed. It sets up the initial state of the contract based on the provided InstantiateMsg.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn instantiate(    deps: DepsMut,    env: Env,    info: MessageInfo,    msg: InstantiateMsg,) -> Result<Response, ContractError> {    // Initialization logic here}

Key points

  • DepsMut: Provides mutable access to the contract’s dependencies, such as storage.
  • Env: Contains environment information, such as block height and timestamp.
  • MessageInfo: Includes information about the message sender and funds sent with the message.
  • InstantiateMsg: The message containing parameters needed to set up the contract.

The instantiate function typically returns a Result<Response, ContractError>, indicating whether the initialization was successful or if it encountered an error.

Execute

The execute function handles operational messages sent to the contract after it has been instantiated. The specific action to take is determined by pattern matching on the ExecuteMsg enum.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn execute(    deps: DepsMut,    env: Env,    info: MessageInfo,    msg: ExecuteMsg,) -> Result<Response, ContractError> {    match msg {        // Match against different ExecuteMsg variants and handle them. These are examples:        ExecuteMsg::Transfer { recipient, amount } => try_transfer(deps, env, info, recipient, amount),        ExecuteMsg::Burn { amount } => try_burn(deps, env, info, amount),        ExecuteMsg::Mint { recipient, amount } => try_mint(deps, env, info, recipient, amount),    }}

Key points

  • ExecuteMsg: Enum that defines all the possible actions the contract can perform.
  • Pattern Matching: The function must match every variant in the ExecuteMsg enum and provide the corresponding logic for each action.

The execute function also returns a Result<Response, ContractError>, where Response can include messages, attributes, and events triggered by the execution.

Query

The query function allows external entities to request data from the contract without modifying its state. Queries are defined by the QueryMsg enum and are handled similarly to execute, using pattern matching.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn query(    deps: Deps,    env: Env,    msg: QueryMsg,) -> StdResult<Binary> {    match msg {        // Match against different QueryMsg variants and handle them. The following are some examples        QueryMsg::GetBalance { address } => query_balance(deps, address),        QueryMsg::GetTokenInfo {} => query_token_info(deps),    }}

Key points

  • Deps: Provides read-only access to the contract’s dependencies, allowing queries without modifying state.
  • QueryMsg: Enum that defines the possible queries the contract can handle.
  • StdResult: The query function returns a StdResult<Binary>, where the Binary result contains the serialized response to the query.

In CosmWasm, in addition to the three primary entry points—instantiate, execute, and query—there are several other important entry points that can be implemented depending on the needs of your smart contract. These additional entry points handle more specialized contract interactions, such as migrations, sudo commands, and replies to submessages.

Other entry points

Migrate

The migrate entry point is used when upgrading a contract to a new version. It allows you to change the contract's state or logic without redeploying a new contract from scratch. This is particularly useful in situations where a contract has been deployed with bugs or needs new features added.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn migrate(    deps: DepsMut,    env: Env,    msg: MigrateMsg,) -> Result<Response, ContractError> {    // Migration logic here}

Key points

  • MigrateMsg: Custom message struct that defines the parameters needed for migration.
  • State Upgrades: The migrate function allows you to modify the existing state, perform any necessary migrations, and return a Response.
  • Backward Compatibility: Ensure that the migration maintains backward compatibility with the existing state and data structures.

Sudo

The sudo entry point is designed for privileged actions that can only be triggered by the contract’s governance or admin. This entry point bypasses normal user permissions and is useful for administrative tasks like adjusting parameters, pausing the contract, or other critical operations.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn sudo(    deps: DepsMut,    env: Env,    msg: SudoMsg,) -> Result<Response, ContractError> {    // Sudo command logic here}

This is used within Archway's Callback, CW-ICA, CW-Fees and CW-Errors modules.

Key Points

  • SudoMsg: Custom message struct that defines the privileged commands that can be executed.
  • Restricted Access: This function is not typically accessible to regular users but rather to the governance module or an admin.
  • Critical Actions: Use this entry point for actions that require a high level of trust and should be carefully controlled.

Reply

The reply entry point is triggered in response to submessages (SubMsg) that your contract sends out. This is particularly useful when your contract needs to handle the result of a cross-contract call or any asynchronous operation.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn reply(    deps: DepsMut,    env: Env,    msg: Reply,) -> Result<Response, ContractError> {    // Handle submessage reply logic here}

Key points

  • Reply: The Reply struct contains information about the result of the submessage, including any events or errors.
  • Submessage Handling: Use the reply entry point to handle the results of operations that require confirmation, such as contract creation or token transfers.
  • Error Handling: This entry point allows you to gracefully handle errors or unexpected results from submessages.

IBCPacketReceive

The ibc_packet_receive entry point is used to handle incoming IBC (Inter-Blockchain Communication) packets. This function allows your contract to process data sent from another blockchain via IBC.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn ibc_packet_receive(    deps: DepsMut,    env: Env,    packet: IbcPacket,) -> Result<IbcReceiveResponse, ContractError> {    // Handle IBC packet receive logic here}

Key points

  • IbcPacket: The IbcPacket struct contains the data sent from another chain.
  • Inter-Blockchain Communication: This entry point is essential for contracts that need to interact with other blockchains via the IBC protocol.
  • Acknowledgment: After processing the packet, the function typically returns an acknowledgment.

IBCPacketAck

The ibc_packet_ack entry point is called when your contract receives an acknowledgment of a packet it sent via IBC. This allows your contract to handle the success or failure of the packet transmission.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn ibc_packet_ack(    deps: DepsMut,    env: Env,    ack: IbcAcknowledgement,) -> Result<Response, ContractError> {    // Handle IBC packet acknowledgment logic here}

Key points

  • IbcAcknowledgement: This struct contains the acknowledgment data for the IBC packet.
  • Error Handling: If the packet failed, you can handle the error and potentially retry or revert actions.

IBCPacketTimeout

The ibc_packet_timeout entry point is triggered when an IBC packet fails to be delivered within the specified time. This entry point allows your contract to handle timeouts and take appropriate action.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn ibc_packet_timeout(    deps: DepsMut,    env: Env,    packet: IbcPacket,) -> Result<Response, ContractError> {    // Handle IBC packet timeout logic here}

Key points

  • Timeout Handling: Use this entry point to clean up or revert actions when a packet fails to be delivered in time.
  • Retry Mechanism: You may implement a retry mechanism or alert the user about the failure.

IBCChannelOpen

The ibc_channel_open entry point is used when an IBC channel is opened. It is used to verify and accept a new channel connection between chains.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn ibc_channel_open(    deps: DepsMut,    env: Env,    channel: IbcChannel,) -> Result<(), ContractError> {    // Handle IBC channel open logic here}

Key points

  • Channel Verification: Ensure that the channel is valid and that your contract is prepared to handle communication through this channel.

IBCChannelClose

The ibc_channel_close entry point is triggered when an IBC channel is closed. This function allows your contract to clean up state or finalize any operations related to the closed channel.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn ibc_channel_close(    deps: DepsMut,    env: Env,    channel: IbcChannel,) -> Result<(), ContractError> {    // Handle IBC channel close logic here}

Key points

  • Cleanup: Ensure that any resources tied to the channel are cleaned up and that the contract state is consistent.

Best Practices for entry points

  1. Keep Entry Points Lightweight: While entry points are essential, they should primarily act as routers that delegate work to other functions. This keeps the entry point functions clean and easy to maintain.
  2. Handle All Enum Variants: Ensure that your execute and query functions handle all variants of the ExecuteMsg and QueryMsg enums, respectively. This prevents unhandled cases that could lead to runtime errors.
  3. Error Handling: Use Result and StdResult consistently to handle errors gracefully. Ensure that all potential failure points return meaningful errors, making it easier to debug and maintain your contract.
  4. Use Environment Data Wisely: The Env struct provides valuable information about the blockchain state (e.g., block height, time). Use this data judiciously, especially in scenarios where timing or block height might impact the contract’s logic.
  5. Modularize Logic: For complex contracts, consider breaking down the logic into smaller, modular functions that are called from the main entry points. This approach makes your codebase easier to navigate and test.
  6. Testing Entry Points: Ensure that you write comprehensive tests for all entry points. Given their critical role in contract interactions, thorough testing helps prevent bugs and ensures reliable performance.