This site requires Javascript to be enabled.

Sudo execution

Sudo execution is a privileged smart contract entry point that can only be invoked by trusted native Cosmos modules. It cannot be called by users or other smart contracts. This functionality is particularly useful for enforcing governance proposals, system-level operations, and other actions that require elevated permissions. A contract must be instantiated before it can respond to sudo messages from governance or other native modules.

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

Understanding SudoMsg

The SudoMsg enum defines the different types of privileged messages that can be sent to a contract via the sudo entry point. These messages are only exposed to internal Cosmos SDK modules, ensuring that only trusted (native/Go) code in the blockchain can execute them.

Example SudoMsg

/// SudoMsg is only exposed for internal Cosmos SDK modules to call./// This is showing how we can expose "admin" functionality that cannot be called by/// external users or contracts, but only trusted (native/Go) code in the blockchain#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]#[serde(rename_all = "snake_case")]pub enum SudoMsg {    MoveFunds {        recipient: String,        amount: Vec<Coin>,    },}

Example sudo entry point

#[entry_point]pub fn sudo(_deps: DepsMut, _env: Env, msg: SudoMsg) -> Result<Response, ContractError> {    match msg {        SudoMsg::MoveFunds { recipient, amount } => {            let msg = BankMsg::Send {                to_address: recipient,                amount,            };            Ok(Response::new().add_message(msg))        }    }}

In this example:

  • SudoMsg::MoveFunds: This variant of SudoMsg allows the contract to move funds from the contract's balance to a specified recipient. The amount is a vector of Coin, representing the funds to be transferred.
  • sudo entry point: The sudo function processes incoming SudoMsg messages. If the message is MoveFunds, it creates a BankMsg::Send message to transfer the funds and returns it in a Response.

Testing sudo execution

Testing sudo functionality follows the same principles as testing other contract entry points, with the key difference that sudo can only be triggered by native modules, not directly by users.

When using cw-multi-test for integration testing, you'll need to add an additional call to the contract wrapper to support sudo testing:

Example: contract wrapper with sudo

pub fn contract_template() -> Box<dyn Contract<Empty>> {    let contract = ContractWrapper::new(        crate::contract::execute,        crate::contract::instantiate,        crate::contract::query,    );    let contract_with_sudo = contract.with_sudo(crate::contract::sudo);    Box::new(contract_with_sudo)}

Explanation

  1. ContractWrapper::new: This initializes the contract wrapper with the contract's execute, instantiate, and query functions.
  2. with_sudo: This method is called on the contract wrapper to add support for the sudo entry point, enabling it to handle SudoMsg during tests.

Testing sudo messages

Here is an example of how you might test the sudo functionality using cw-multi-test:

#[test]fn test_sudo_move_funds() {    let mut app = mock_app();    let contract_id = app.store_code(contract_template());    let owner = Addr::unchecked("owner");    let recipient = Addr::unchecked("recipient");    // Instantiate the contract    let contract_addr = app        .instantiate_contract(contract_id, owner.clone(), &InstantiateMsg {}, &[], "Test contract", None)        .unwrap();    // Prepare a sudo message to move funds    let sudo_msg = SudoMsg::MoveFunds {        recipient: recipient.to_string(),        amount: vec![coin(1000, "uusd")],    };    // Execute the sudo message    let res = app.sudo(contract_addr.clone(), &sudo_msg);    assert!(res.is_ok());    // Verify the funds were moved    let recipient_balance = app.wrap().query_balance(recipient, "uusd").unwrap();    assert_eq!(recipient_balance.amount, Uint128::new(1000));}

Explanation

  • mock_app: Initializes a simulated blockchain environment.
  • app.store_code: Stores the contract code in the simulated environment.
  • app.instantiate_contract: Instantiates the contract.
  • SudoMsg::MoveFunds: Prepares a sudo message to move funds.
  • app.sudo: Executes the sudo message as if it were sent by a trusted module.
  • app.wrap().query_balance: Queries the recipient's balance to verify that the funds were moved.

Best practices for sudo execution

  • Restrict Sudo Usage: Ensure that sudo is only used for operations that require elevated permissions and should not be exposed to external users or contracts.
  • Test Thoroughly: Thoroughly test all sudo functionality to ensure that privileged operations behave as expected and do not introduce vulnerabilities.
  • Documentation: Clearly document the sudo functionality in your contract to help other developers understand its purpose and usage.

By understanding and properly implementing sudo functionality, you can create more secure and robust CosmWasm contracts that can integrate with the broader Cosmos ecosystem in a trusted manner.