Proxy Canister
This guide explains the proxy canister pattern, when you need it, and how to deploy and use a proxy on connected networks like IC mainnet.
Why a Proxy?
The IC protocol imposes two constraints on external clients (CLI tools, scripts, browser apps):
-
Clients cannot attach cycles to calls. Only canisters can fund inter-canister calls with cycles. A canister method that charges cycles for execution is therefore unreachable directly from a CLI tool.
-
Some management canister methods are canister-only. Certain
aaaaa-aamanagement canister methods — likecanister_infoorraw_rand— can only be called by other canisters, not by external clients.
The proxy canister solves both constraints. It accepts a proxy method call from an authorized caller, then forwards the call to the target canister as a canister-to-canister call. Cycles are deducted from the proxy’s own balance and attached to the forwarded call.
flowchart LR
A[You] -->|call| B[Proxy canister]
B -->|forward + cycles| C[Target canister]
When You Need a Proxy
You need a proxy canister whenever you:
- Attach cycles to a call — for example, topping up a canister or calling a pay-per-use method.
- Call a management canister method that is restricted to canister callers (e.g.
canister_info,raw_rand,create_canister,install_chunked_code). - Run icp-cli against IC mainnet and need cycles-funded operations.
Local Development: Automatic Proxy
On managed (local) networks, icp-cli automatically deploys a proxy canister and seeds it with cycles. You can use it immediately:
# Get the proxy canister principalicp network status --json | jq -r .proxy_canister_principal
# Forward a call with cyclesicp canister call my-canister method '(args)' \ --proxy $(icp network status --json | jq -r .proxy_canister_principal) \ --cycles 500_000_000_000You do not need to manage this proxy — icp-cli handles its lifecycle.
Connected Networks: Deploy Your Own Proxy
On connected networks (ic mainnet and custom networks), no proxy is provided. You must deploy one before you can forward cycles or reach canister-only methods.
Using the Proxy Template
The fastest path is the proxy template:
# Create a new project from the proxy templateicp new my-proxy --subfolder proxycd my-proxy
# Deploy to IC mainneticp deploy -e ic
# Export the proxy canister IDexport PROXY_ID=$(icp canister status -e ic --id-only proxy)The proxy canister starts with your deploying identity as its only controller.
Funding the Proxy
The proxy must hold enough cycles to cover forwarded calls. Top it up via the cycles ledger:
# Transfer 5T cycles to the proxyicp canister top-up $PROXY_ID --amount 5t -e icCheck the proxy balance:
icp canister status $PROXY_ID -e icUsing --proxy and --cycles
Pass --proxy <PRINCIPAL> to any icp-cli command that needs to go through the proxy. Add --cycles <AMOUNT> when the target operation requires cycles.
Canister Calls
# Call a method and attach 1T cyclesicp canister call my-canister charge_me '()' \ -e ic \ --proxy $PROXY_ID \ --cycles 1_000_000_000_000
# Call a canister-only management method (no cycles needed)icp canister status my-canister -e ic --proxy $PROXY_IDCanister Creation
# Create a new canister, funded with 3T cyclesicp canister create my-canister \ -e ic \ --proxy $PROXY_ID \ --cycles 3_000_000_000_000The new canister is created on the same subnet as the proxy.
Deployment
When deploying to mainnet, pass --proxy so that icp-cli can create canisters and call management methods:
icp deploy -e ic --proxy $PROXY_IDAuthorization
The proxy only accepts calls from its own controllers. Any call from a non-controller principal is rejected before it reaches the replicated state — protecting the proxy’s cycles from unauthorized use.
After deploying the proxy, verify your identity is a controller:
dfx canister status $PROXY_ID --network ic# oricp canister status $PROXY_ID -e icTo add another identity as a controller:
icp canister settings update $PROXY_ID --add-controller <PRINCIPAL> -e icHow the Proxy Works
The proxy canister exposes a single update method:
type ProxyArgs = record { canister_id : principal; method : text; args : blob; cycles : nat;};
service : { proxy : (ProxyArgs) -> (variant { Ok : record { result : blob }; Err : ... })}When icp-cli’s --proxy flag is set:
- The CLI Candid-encodes your original call arguments.
- It wraps them into a
ProxyArgsrecord alongside the target canister ID and--cyclesamount. - It sends this as a single update call to the proxy’s
proxymethod. - The proxy deducts the specified cycles from its own balance and forwards the call.
- The response bytes are decoded and returned to you as if you had called the target directly.
For management canister calls, the CLI also sets an effective_canister_id on the request to ensure the IC routes it to the correct subnet.
Migrating from the dfx Wallet
The dfx wallet served a similar purpose — it forwarded calls and funded canister creation — but was a more complex canister with an address book, event log, and custodian system. The proxy canister is the icp-cli equivalent: simpler, leaner, and controller-based.
If you have an existing dfx wallet with cycles that you want to reuse as a proxy, see Replacing the dfx Wallet Canister in the migration guide.
Keeping the Proxy Funded
The proxy pays cycles from its own balance for every forwarded call. Monitor the balance regularly and top it up before it runs out:
# Check balanceicp canister status $PROXY_ID -e ic
# Top up with 10T cyclesicp canister top-up $PROXY_ID --amount 10t -e icIf the proxy runs out of cycles, forwarded calls will return InsufficientCycles. The proxy itself will not be deleted — canisters freeze before they are deleted, and you can always top up a frozen canister.
Next Steps
- Deploying to Mainnet — Full mainnet deployment workflow
- Tokens and Cycles — Managing ICP and cycles with icp-cli