Transfer Cycles from Canister Wallet to Principal Default Wallet

Hi, I’d like to transfer cycles from a canister to a Principal’s wallet canister. I’ve setup the cycles_hello project, and it contains the following canisters:

The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
"cycles_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
"cycles_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"

I have managed to send cycles from rwlgt-iiaaa-aaaaa-aaaaa-cai to the cycles_hello canister rrkah-fqaaa-aaaaa-aaaaq-cai, but I am unable to send cycles from the canister rrkah-fqaaa-aaaaa-aaaaq-cai back to rwlgt-iiaaa-aaaaa-aaaaa-cai.

In the cycles_hello main.mo file, I created the following function, but I was unsure how to call wallet_receive() on wallet canister rwlgt-iiaaa-aaaaa-aaaaa-cai:

public shared(msg) func wallet_send(amount : Nat) : async { accepted: Nat } {
  Cycles.add(amount);
  let p = msg.caller;
  p.wallet_receive();
  { accepted = amount };
};

I tried to update the wallet_send() to accept a function, but I could not figure out how to call the method from the command line:

public shared(msg) func wallet_send(amount : Nat, benefit: shared () -> ()) : async { accepted: Nat } {
  Cycles.add(amount);
  benefit();
  { accepted = amount };
};

I tried to call the method with the following command but received an error:

dfx canister call rrkah-fqaaa-aaaaa-aaaaq-cai wallet_send '(10000, func "wallet_received")'

Invalid argument: Invalid Candid values: Candid parser error: Unrecognized token `RParen` found at 30:31
Expected one of "."

@flyq suggested:

“Yes, you need to update the canister code, which contains a function that call actor(“your wallet”).wallet_receive() with Cycles.add(amount).”

Since I am trying to transfer cycles back to a default wallet canister, I’m not sure where I would put that code. Any help would be greatly appreciated.

I am referencing these three developer pages:
https://sdk.dfinity.org/docs/language-guide/cycles.html
https://sdk.dfinity.org/docs/developers-guide/default-wallet.html
https://sdk.dfinity.org/docs/developers-guide/tutorials/simple-cycles.html

After RTFM’ing, I figured out the issue and how to pass functions to a canister via dfx canister. The original code that accepts a function works correctly in local dev, but please make sure to add access control in production(!!!). Here’s the working snippet to pass a function to dfx canister:

dfx canister call rrkah-fqaaa-aaaaa-aaaaq-cai wallet_send '(256_000_000_000, func "rwlgt-iiaaa-aaaaa-aaaaa-cai".wallet_receive)'

As shown in the opening post, in canister rrkah-fqaaa-aaaaa-aaaaq-cai, I created a wallet_send() function that accepts an amount of cycles and a function. It simply adds cycles to a wallet then executes the function and returns the amount of cycles that the sender intended to transfer. To pass a function in Candid IDL, you must pass a supported type, canister id, followed by a dot (.), and function name:

func "w7x7r-cok77-xa".hello
func "w7x7r-cok77-xa"."☃"
func "aaaaa-aa".create_canister

Read more about Candid Types below:

To read a more detailed help for dfx canister call, read the following link:

To learn how to add access control, view the tutorial here:

3 Likes