Using a cycles wallet
As discussed in tokens and cycles, ICP tokens can be converted into cycles to power canister operations. Cycles reflect the operational cost of communication, computation, and storage that dapps consume.
Unlike ICP tokens, cycles are only associated with canisters and not with user or developer principals. Because only canisters require cycles to perform operations and pay for the resources they use, users and developers can manage the distribution and ownership of cycles through a special type of canister called a cycles wallet.
Please note that the cycles wallet will be removed from dfx in a future release. We recommend using the cycles ledger feature instead, which simplifies the cycles management workflow.
The cycles wallet holds the cycles required to perform operations such as creating new canisters. These operations are executed using the canister principal of the cycles wallet instead of your user principal.
For the purposes of using the local canister execution environment, the SDK automatically creates a default cycles wallet for you in every project, and most of the operations performed using the cycles wallet happen behind the scenes. For example, the cycles wallet acts on your behalf to register canister principals and deploy canisters in the local canister execution environment.
In a production environment, however, you need to explicitly register and transfer cycles to new canisters, specify the principals that can act as custodians, and manage the principals with ownership rights. You can perform some of these tasks using the default cycles wallet dapp running in a web browser. Depending on the specific action you want to take, you can also perform these cycle and canister management tasks by running dfx wallet
commands in a terminal or by calling methods in the default cycles wallet canister directly. You can learn more about how to use dfx wallet
.
You should keep in mind, however, that calls to the cycles wallet canister are executed using the cycles wallet principal associated with the currently selected user identity. Depending on your currently selected identity and whether the principal associated with that identity has been added as a controller or a custodian for a wallet, you might see different results or be denied access to a specific method.
To check the identity you are currently using, run the following command:
dfx identity whoami
Controller and custodian roles
A user principal or canister principal can be assigned to a controller or custodian role.
The controller role this document talks about is NOT the same controller role usually meant when talking about the Internet Computer. Typically, the controller refers to a principal that controls a canister. Here, a controller is a wallet-internal role that happens to have the same name. For a more detailed differentiation, see this forum post.
Controllers
A controller is the most privileged role, and a principal assigned to the controller role can perform privileged tasks, including the following:
Add and remove other principals as controllers.
Authorize and de-authorize other principals as custodians.
Add entries to the cycles wallet address book.
Access the cycles wallet balance and all other wallet-related information.
Send cycles to other canisters.
Accept receipt of cycles from other canisters.
Rename the cycles wallet.
Create canisters and additional cycles wallets.
A canister can have a maximum of 10 controllers. Learn more here.
Custodians
A principal assigned to the custodian role can only perform a subset of cycles wallet management tasks, including the following:
Access the cycles wallet balance and all other wallet-related information.
Send cycles to other canisters.
Accept receipt of cycles from other canisters.
Create canisters.
Authorizing a principal as a custodian does not automatically grant the principal access to a cycles wallet. The identity assigned to the custodian role must also be assigned a cycles wallet principal. For example, if you authorize the identity alice_custodian
as a custodian of a cycles wallet (rwlgt-iiaaa-aaaaa-aaaaa-cai
) in a local project, that user would also need to be assigned to use that wallet with the dfx identity set-wallet rwlgt-iiaaa-aaaaa-aaaaa-cai
command.
Creating a cycles wallet
- Prerequisites
If you are deploying locally, your local cycles wallet is created in the background when you register a new canister principal using dfx canister create
or when you register, build, and deploy a canister with dfx deploy
.
If you are deploying on the mainnet, a cycles wallet must be manually created by converting ICP tokens to cycles, transferring the cycles to a new canister principal, and updating the canister with the default cycles wallet WebAssembly module (Wasm).
There are dapps that can help you convert ICP to cycles and create a new cycles wallet, e.g., NNS dapp.
Step 1: Create a new canister with cycles by transferring ICP tokens from your ledger account. Run the command:
dfx ledger --network ic create-canister <principal-identifier> --amount <icp-tokens>
This command converts the number of ICP tokens you specify for the --amount
argument into cycles, and associates the cycles with a new canister identifier controlled by the principal you specify.
If the transaction is successful, the ledger records the event, and you should see output such as:
Transfer sent at BlockHeight: 20
Canister created with id: "gastn-uqaaa-aaaae-aaafq-cai"
Step 2: Install the cycles wallet code in the newly-created canister ID by running the command:
dfx identity --network ic deploy-wallet <canister-identifier>
This command displays output such as:
Creating a wallet canister on ICP.
The wallet canister on the "ic" network for user "default" is "gastn-uqaaa-aaaae-aaafq-cai"
Listing wallets
You can use the following command to display the wallet's principal and role (contact, custodian, or controller). If the wallet has a name or a kind associated with it, this information will be returned from the following command as well:
dfx wallet addresses
This command displays output such as:
Id: hoqq7-3eo6j-dee4s-aiabk-6rqxw-kwgyo-rhru7-bdgmk-k5ipv-chkhx-cqe, Kind: Unknown, Role: Controller, Name: No name set.
Check the cycle balance
In the local canister execution environment or with a cycles wallet on the mainnet, you can use the following command to check the current cycles balance:
dfx wallet balance // Local
dfx wallet balance --network ic // Mainnet
Call the cycles wallet_balance
method
You can also check the cycles balance by calling the wallet_balance
method in the cycles wallet canister directly. For example, if your principal is a controller for the h5aet-waaaa-aaaab-qaamq-cai
cycles wallet, you can check the current cycle balance by running the following command:
dfx canister --network ic call h5aet-waaaa-aaaab-qaamq-cai wallet_balance
The command returns the balance using Candid format as a record with an amount field (represented by the hash 3\_573\_748\_184
) and a balance of 6,895,656,625,450 cycles like this:
(record { 3_573_748_184 = 6_895_656_625_450 })
Using your wallet
After you have used the dfx identity deploy-wallet
command to create a cycles wallet canister tied to an identity, you can use dfx wallet
commands to modify your cycles wallet settings, send cycles to other cycles wallets, and add or remove controllers and custodians.
For more details, see the dfx wallet reference.
You can also view the Candid file for the cycles wallet.
dfx
wallet functions
Use the dfx wallet
command with subcommands and flags to manage the cycles wallets of your identities and to send cycles to the wallets of other account cycles wallet canisters.
The basic syntax for running the dfx wallet commands is:
dfx wallet [option] <subcommand> [flag]
Depending on the dfx wallet
subcommand you specify, additional arguments, options, and flags might apply or be required. To view usage information for a specific dfx wallet
subcommand, specify the subcommand and the --help
flag. For example, to see usage information for dfx wallet
send, you can run the following command:
dfx wallet send --help
Adding a controller
An identity assigned the role of controller has the most privileges and can perform the following actions on the selected identity's cycles wallet. Identities that are added as controllers are also listed as custodians.
To add a controller, the following command can be used:
dfx wallet add-controller // Local
dfx wallet add-controller --network ic // Mainnet
Viewing controllers
To list the principals of the identities that are controllers of the selected identity's cycles wallet, the following command can be used:
dfx wallet controllers // Local
dfx wallet controllers --network ic // Mainnet
Removing a controller
To remove a controller from an identity's cycles wallet, the following command can be used:
dfx wallet remove-controller // Local
dfx wallet remove-controller --network ic // Mainnet
Authorizing a principal to be a custodian
To authorize a custodian for a cycles wallet, the following command can be used:
dfx wallet authorize PRINCIPAL_ID // Local
dfx wallet authorize PRINCIPAL_ID --network ic // Mainnet
Listing custodians
To list custodians of the currently selected identity's cycles wallet, the following command can be used:
dfx wallet custodians // Local
dfx wallet custodians --network ic // Mainnet
De-authorizing a principal from being a custodian
To de-authorize a custodian from a cycles wallet, the following command can be used:
dfx wallet deauthorize PRINCIPAL_ID // Local
dfx wallet deauthorize PRINCIPAL_ID --network ic // Mainnet
dfx wallet
reference
For reference information and examples that illustrate using dfx wallet commands, select an appropriate command.
add-controller
: Add a controller using the selected identity's principal.addresses
: Displays the address book of the cycles wallet.authorize
: Authorize a custodian by principal for the selected identity's cycles wallet.balance
: Displays the cycles wallet balance of the selected identity.controllers
: Displays a list of the selected identity's cycles wallet controllers.custodians
: Displays a list of the selected identity's cycles wallet custodians.deauthorize
: Deauthorize a cycles wallet custodian using the custodian's principal.help
: Displays a usage message and the help of the given subcommand(s).name
: Returns the name of the cycles wallet if you've used the dfx wallet set-name command.redeem-faucet-coupon
: Redeem a code at the cycles faucet.remove-controller
: Removes a specified controller from the selected identity's cycles wallet.send
: Sends a specified amount of cycles from the selected identity's cycles wallet to another cycles wallet using the destination wallet canister ID.set-name
: Specify a name for your cycles wallet.upgrade
: Upgrade the cycles wallet's Wasm module to the current Wasm bundled withdfx
.
Additional methods
The default cycles wallet canister includes additional methods that are not exposed as dfx wallet
commands. The additional methods support more advanced cycles management tasks such as creating new canisters and managing events.
Create a new cycles wallet from an existing cycles wallet
Use the wallet_create_wallet
method to create a new cycles wallet canister with an initial cycle balance and, optionally, with a specific principal as its controller. If you don’t specify a controlling principal, the cycles wallet you use to create the new wallet will be the new wallet’s controller.
For example, you can run this command to create a new wallet and assign a principal as a controller:
dfx canister --network ic call CYCLES_WALLET_CANISTER_ID wallet_create_wallet '(record { cycles = 5000000000000 : nat64; controller = principal "PRINCIPAL_ID"})'
The command returns the principal for the new wallet:
(record { 1_313_628_723 = principal "dcxxq-jqaaa-aaaab-qaavq-cai" })
Create a new canister
Use the wallet_create_canister
method to create a new canister. This method creates a new empty canister placeholder with an initial cycle balance and, optionally, with a specific principal as its controller. After you have registered the canister principal, you can install code for your canister as a follow-up step.
For example, you can run a command similar to the following to create a new wallet and assign a principal as a controller:
dfx canister --network ic call CYCLES_WALLET_CANISTER_ID wallet_create_canister '(record { cycles = 5000000000000 : nat64; controller = principal "PRINCIPAL_ID"})'
The command returns the principal for the new canister you created:
(record { 1_313_628_723 = principal "dxqg5-iyaaa-aaaab-qaawa-cai" })
Receive cycles from a canister
Use the wallet_receive
method as an endpoint to receive cycles.
Forward calls from a wallet
Use the wallet_call
method to forward calls using the cycles wallet principal as the caller.
Manage addresses
Use the following methods to manage address book entries:
add_address
:(address: AddressEntry) → ();
remove_address
:(address: principal) → ();
Manage events
Use the following methods to retrieve event and chart information.
get_events
:(opt record \{ from: opt nat32; to: opt nat32; \}) → (vec Event) query;
get_chart
:(opt record \{ count: opt nat32; precision: opt nat64; \} ) → (vec record \{ nat64; nat64; \}) query;
For example, you can use the get_events
method to return canister_create
and other events by running a command such as:
dfx canister call CYCLES_WALLET_CANISTER_ID get_events '(record {from = null; to = null})' --network ic
The output from the command is in Candid format, such as the example shown below:
(
vec { record { 23_515 = 0; 1_191_829_844 = variant { 4_271_600_268 = record { 23_515 = principal "tsqwz-udeik-5migd-ehrev-pvoqv-szx2g-akh5s-fkyqc-zy6q7-snav6-uqe"; 1_224_700_491 = null; 1_269_754_742 = variant { 4_218_395_836 };} }; 2_781_795_542 = 1_621_456_688_636_513_683;}; record { 23_515 = 1; 1_191_829_844 = variant { 4_271_600_268 = record { 23_515 = principal "ejta3-neil3-qek6c-i7rdw-sxreh-lypfe-v6hjg-6so7x-5ugze-3iohr-2qe"; 1_224_700_491 = null; 1_269_754_742 = variant { 2_494_206_670 };} }; 2_781_795_542 = 1_621_461_468_638_569_551;}; record { 23_515 = 2; 1_191_829_844 = variant { 1_205_528_161 = record { 2_190_693_645 = 11_000_000_000_000; 2_631_180_839 = principal "gvvca-vyaaa-aaaae-aaaga-cai";} }; 2_781_795_542 = 1_621_462_573_993_647_258;}; record { 23_515 = 3; 1_191_829_844 = variant { 1_205_528_161 = record { 2_190_693_645 = 11_000_000_000_000; 2_631_180_839 = principal "gsueu-yaaaa-aaaae-aaagq-cai";} }; 2_781_795_542 = 1_621_462_579_193_578_440;}; record { 23_515 = 4; 1_191_829_844 = variant { 1_955_698_212 = record { 2_190_693_645 = 0; 2_374_371_241 = "install_code"; 2_631_180_839 = principal "aaaaa-aa";} }; 2_781_795_542 = 1_621_462_593_047_590_026;}; record { 23_515 = 5; 1_191_829_844 = variant { 1_955_698_212 = record { 2_190_693_645 = 0; 2_374_371_241 = "install_code"; 2_631_180_839 = principal "aaaaa-aa";} }; 2_781_795_542 = 1_621_462_605_779_157_885;}; record { 23_515 = 6; 1_191_829_844 = variant { 1_955_698_212 = record { 2_190_693_645 = 0; 2_374_371_241 = "authorize"; 2_631_180_839 = principal "gsueu-yaaaa-aaaae-aaagq-cai";} }; 2_781_795_542 = 1_621_462_609_036_146_536;}; record { 23_515 = 7; 1_191_829_844 = variant { 1_955_698_212 = record { 2_190_693_645 = 0; 2_374_371_241 = "greet"; 2_631_180_839 = principal "gvvca-vyaaa-aaaae-aaaga-cai";} }; 2_781_795_542 = 1_621_463_144_066_333_270;}; record { 23_515 = 8; 1_191_829_844 = variant { 4_271_600_268 = record { 23_515 = principal "ejta3-neil3-qek6c-i7rdw-sxreh-lypfe-v6hjg-6so7x-5ugze-3iohr-2qe"; 1_224_700_491 = null; 1_269_754_742 = variant { 2_494_206_670 };} }; 2_781_795_542 = 1_621_463_212_828_477_570;}; record { 23_515 = 9; 1_191_829_844 = variant { 1_955_698_212 = record { 2_190_693_645 = 0; 2_374_371_241 = "wallet_balance"; 2_631_180_839 = principal "gastn-uqaaa-aaaae-aaafq-cai";} }; 2_781_795_542 = 1_621_878_637_071_884_946;}; record { 23_515 = 10; 1_191_829_844 = variant { 4_271_600_268 = record { 23_515 = principal "b5quc-npdph-l6qp4-kur4u-oxljq-7uddl-vfdo6-x2uo5-6y4a6-4pt6v-7qe"; 1_224_700_491 = null; 1_269_754_742 = variant { 4_218_395_836 };} }; 2_781_795_542 = 1_621_879_473_916_547_313;}; record { 23_515 = 11; 1_191_829_844 = variant { 313_999_214 = record { 1_136_829_802 = principal "gastn-uqaaa-aaaae-aaafq-cai"; 3_573_748_184 = 10_000_000_000;} }; 2_781_795_542 = 1_621_977_470_023_492_664;}; record { 23_515 = 12; 1_191_829_844 = variant { 2_171_739_429 = record { 25_979 = principal "gastn-uqaaa-aaaae-aaafq-cai"; 3_573_748_184 = 10_000_000_000; 4_293_698_680 = 0;} }; 2_781_795_542 = 1_621_977_470_858_839_320;};},
)
In this example, there are twelve event records. The Role
field (represented by the hash 1_269_754_742
) specifies whether a principal is a controller (represented by the hash 4_218_395_836
) or a custodian (represented by the hash 2_494_206_670
). The events in this example also illustrate an amount field (represented by the hash 3_573_748_184
) with a transfer of 10_000_000_000 cycles.