ICP management canister
Overview
The ICP management canister is used to provide access to system features, such as managing canisters, integrating with Bitcoin, making HTTPS outcalls, signing with threshold signatures, and securely retrieving randomness.
The management canister is a virtual canister in the sense that it does not exist with an isolated state or Wasm module, but rather is implemented as part of ICP itself. For most practical purposes, the management canister behaves like a canister, i.e. it can accept messages from users or other canisters and can respond to those messages just like any other canister.
The address of the ICP management canister is aaaaa-aa
(i.e. the empty blob). It is present on any ICP subnet. By using this address, a canister can access the management canister either on the local or remote subnet -- ICP will take care of routing the request to the appropriate subnet transparently. The cost of making the call is charged to the calling canister just like any other inter-canister call.
Certain functionalities, such as threshold signing or access to the Bitcoin APIs, are only enabled on certain subnets.
The management canister can also be called by users. Most methods are only permitted to be called by the controllers of the canister. Certain calls can only be made by a canister and are not available for users (e.g. raw_rand
or deposit_cycles
). When a user makes a call to the management canister, the cost of processing the request is charged to the canister that is being managed.
Read more about the complete interface of the management canister.
Why does the management canister exist?
You might be familiar with the System API that allows canisters to interact with other canisters and perform special actions like sending messages or transferring cycles. A natural expectation would be to provide canister management through the same System API. However, the System API only works for synchronous calls that can be returned immediately. Some of the canister management operations are asynchronous in nature – the controlled canister might not even be in the same subnet as the controller. Therefore, an asynchronous API is needed.
Providing this functionality through a virtual canister, i.e. the management canister, allows for ICP to reuse a lot of the mechanics that are already available and provides a familiar approach to users and canisters to access this API through inter-canister calls.
How can you interact with the management canister?
Since the management canister behaves like a canister, users or other canisters can call its provided interfaces like any other canister on ICP.
Here are some examples calling the management canister:
- Motoko
- Rust
- TypeScript
- Python
actor {
let ic = actor "aaaaa-aa" : IC.Self;
public func createCanister(settings: ?canister_settings) : async () {
await ic.create_canister({canister_id = canisterId});
canister_id : canister_id;
};
};
#[ic_cdk_macros::update]
let (canister,) : (candid::Principal,) = ic_cdk::api::call(candid::Principal::management_canister(), "create_canister", ()).await?;
import { call, update } from 'azle';
import {
CreateCanisterArgs,
CreateCanisterResult
} from 'azle/canisters/management';
export default class {
@update([], CreateCanisterResult)
async executeCreateCanister(): Promise<CreateCanisterResult> {
return await call('aaaaa-aa', 'create_canister', {
paramIdlTypes: [CreateCanisterArgs],
returnIdlType: CreateCanisterResult,
args: [{ settings: [], sender_canister_version: [] }],
payment: 50_000_000_000_000n
});
}
}
from kybra import (
Async,
CallResult,
match,
Principal,
update,
Variant,
)
from kybra.canisters.management import (
CreateCanisterResult,
management_canister,
)
from typing import TypedDict
class ExecuteCreateCanisterResult(Variant, total=False):
Ok: CreateCanisterResult
Err: str
class State(TypedDict):
created_canister_id: Principal
state: State = {"created_canister_id": Principal.from_str("aaaaa-aa")}
@update
def execute_create_canister() -> Async[ExecuteCreateCanisterResult]:
create_canister_result_call_result: CallResult[
CreateCanisterResult
] = yield management_canister.create_canister({"settings": None}).with_cycles(
50_000_000_000_000
)
def handle_ok(
create_canister_result: CreateCanisterResult,
) -> ExecuteCreateCanisterResult:
return {"Ok": create_canister_result}
return match(
create_canister_result_call_result,
{"Ok": handle_ok, "Err": lambda err: {"Err": err}},
)
Kybra canisters must be deployed from a Python virtual environment. Learn more in the Kybra docs.
You can also use the ic-management JavaScript library to interact with the management canister.
Typical workflows involving the management canister
The management canister offers a variety of endpoints that provide access to special features available on ICP. These can be grouped into a few main areas. Let's explore some of those.
Managing canisters
The most commonly used APIs of the management canister are those that are related to managing canisters. These include operations like creating or installing canisters, changing their settings or uninstalling and deleting canisters. These operations are typically only permitted to be called by controllers of the canister.
Signing and submitting Bitcoin transactions
One of the special features of ICP is its direct integration with the Bitcoin network. This is exposed to users through a set of API methods on the management canister that allow users to sign transactions with threshold ECDSA signatures and submit transactions to the Bitcoin network, all done through smart contracts on ICP. The relevant APIs are sign_with_ecdsa and bitcoin_send_transaction.
Read more about ICP's direct integration with Bitcoin.
HTTPS outcalls
One more special feature of ICP is the ability for canisters to make http requests to off-chain systems using the HTTPS outcalls feature. This is available through the management canister's http_request API. HTTPS outcalls enable canisters to obtain off-chain data, call RPC services of other blockchain networks, or interact with Web2 services and enterprise IT systems off-chain.
Retrieving randomness
Canisters on ICP can request random bits via the management canister's raw_rand API. These random bits come from ICP's own random tape that is also used in its consensus algorithm. A key property of the randomness returned by the management canister is that it's unknown at the time of request submission. ICP will only resolve this call in the next execution round (using the latest value coming from its random tape), which makes it impossible to predict the value of these random bits.