- Docs
- Smart Contracts
- Sudo execution
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. Theamount
is a vector ofCoin
, representing the funds to be transferred. - sudo entry point: The
sudo
function processes incomingSudoMsg
messages. If the message isMoveFunds
, it creates aBankMsg::Send
message to transfer the funds and returns it in aResponse
.
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
- ContractWrapper::new: This initializes the contract wrapper with the contract's
execute
,instantiate
, andquery
functions. - with_sudo: This method is called on the contract wrapper to add support for the
sudo
entry point, enabling it to handleSudoMsg
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.