ckBTC Transfer from Canister After Approval

Hello Community,

We have some questions regarding Motoko and Canister.

We are using Motoko to build a Canister that will be used to transfer ICRC-1 & 2 token from our canister to other principal id.

However, we are stuck at the transfer stage after approval. Here’s the process:

First, we are using the ckBTC Canister ID mxzaz-hqaaa-aaaar-qaada-cai. We tried to approve ckBTC from the Canister and it worked fine.

We used this command for approval:
dfx canister call bza44-ciaaa-aaaan-qlvna-cai approve '(88)' --network ic

(variant { ok = 1_305_047 : nat })

So basically, we approve the Canister ID with the respective principal ID. You can see all the approved

Our canister id: bza44-ciaaa-aaaan-qlvna-cai

After approval from the ckBTC ledger, we are transferring it to another wallet (principal ID).

dfx canister call bza44-ciaaa-aaaan-qlvna-cai transfer '(30, principal "3zm3c-qhy2j-5vvce-il3fu-il4ai-wf5fs-bwwjp-dbary-numis-kgxui-rae")' --network ic

However, we are getting the following error:

(variant { err = “Unknown transfer error” })

We want to send ICRC1, ICRC2, and ICP through our Canister freely.

Can you please guide us on how we can solve this issue?

Thank you!

Can you show what canister/principal makes what calls to the ckBTC canister? What you show here is how you call your own canister which doesn’t really help to debug

1 Like

Hello @Severin,

Thank you for your response.

We are using the following canister to interact with the ckBTC ledger:

bza44-ciaaa-aaaan-qlvna-cai

ckBTC canister id

mxzaz-hqaaa-aaaar-qaada-cai

Below is the code. Initially, we call this function in the canister to approve ckBTC:

public shared ({ caller = owner }) func approve(amount : Nat) : async Result.Result<Nat,Text> {
    let args = {
        amount;
        created_at_time = null;
        expected_allowance = null;
        expires_at = null;
        fee = null;
        from_subaccount = null;
        memo = null;
        spender = { owner; subaccount = null };
    };
    let result_Trans = await ckbtc_ledger.icrc2_approve(args);
};

Following approval, I attempt a transfer using this principal ID:

uonvj-rdj5h-pft2p-pdo2w-qaolc-ojdyo-3btrc-o27pt-xg2lz-an2ga-qqe

Here is the transfer function:

public shared ({ caller }) func transfer(amount : Nat, to : Principal) : async Result.Result<Nat, Text> {
    let args = {
        to = { owner = to; subaccount = null };
        fee = null;
        spender_subaccount = null;
        from = { owner = caller; subaccount = null };
        memo = null;
        created_at_time = null;
        amount = amount;
    };
    let transf_Result = await ckbtc_ledger.icrc2_transfer_from(args);
};

However, during the transfer process, I’m encountering the following error:

(variant { err = "Unknown transfer error" })

When you make the icrc2_transfer_from call the ckBTC canister sees bza44-ciaaa-aaaan-qlvna-cai as the caller. This is different from e.g. ETH where the TX creator gets forwarded between contract calls. If you want to do that transfer you can use a standard icrc1_transfer, no need to go through the approval process

The approve function seems to work. If I check the approved amount using the principals you posted above I get (record { allowance = 100 : nat; expires_at = null })

I’ve tried that approach initially, but it didn’t work as expected. Instead of deducting ckBTC from the user’s wallet, it was taking the amount from the canister.

Here’s an example:

Transfer amount

I’m looking for a way to directly deduct from the user’s wallet and transfer it to another account, but it needs to go through our canister because we have additional data that needs to be passed along with the transaction.

If you want to spend the user’s ckBTC then the user needs to call icrc2_approve directly, and not through your canister

1 Like

I have also tried using the icrc2_approve function on the frontend side with the ckBTC ledger. However, I encountered an error while transferring the tokens. Here are the links to the repository and the live demo:

There are two arguments, but it’s still giving the
wrong number of message arguments' error.

there is no from field in ApproveArgs

export interface ApproveArgs {
  fee: [] | [bigint];
  memo: [] | [Uint8Array | number[]];
  from_subaccount: [] | [Uint8Array | number[]];
  created_at_time: [] | [bigint];
  amount: bigint;
  expected_allowance: [] | [bigint];
  expires_at: [] | [bigint];
  spender: Account;
}

address of the caller is fetched automatically by the ledger canister.

when the icrc2_approve function is called,
it will perform the step to build the from

fn icrc2_approve(arg: ApproveArgs) -> ... {
  let caller = ic_cdk::caller();
  let from = Account{
    owner: caller,
   subaccount: arg.from_subaccount,
  };
  .....
1 Like

The approve function is working fine. I am getting an error while transferring ckBTC.

I just approved it with this code

ckBTC Dashboard

@Severin, could you assist me with this issue ?

I don’t think canisters are that lenient to accept non declared argument.

Also the txn fee for CKBTC is 10000.
you won’t be able to transfer 100.

What is the error you’re currently stuck with? And can you link me to the lines where you make the transfer?

sure

here is the approve ckBTC code

Approve Code

here is the send ckBTC code
Transfer ckBTC code

when i try to transfer the ckBTC i am getting error

Wrong number of message arguments.

Approve: declaration says it takes an ApproveArgs, you create an ApproveArgs object here

Transfer: declaration says it takes a Nat and a Principal, but you construct an object with a amount and from. In Candid notation: expected: (nat, principal), you supply (record{amount: nat; to: principal}). The tuple is supposed to be 2 elements, but you give only 1, therefore Wrong number of arguments

Okay, got it

I tried to trigger the transaction, but got error

Unknown transfer error

which i define in the code the same error i got while using direct in dfx UI

For that I’ll ask some other people since this sounds like some ckBTC internal problem…

We would appreciate it if you could provide us with any updates regarding the issue we mentioned earlier. Our infrastructure and backend processes are in place; we just need a solution for the requested error. This will enable us to implement the proposed payment solution on ICP as soon as possible.

The Ledger doesn’t not return any Unknown transfer error. Where does this error come from?

Initially, I used the ckBTC approve function on the frontend to authorized my canister to access a specific amount of funds. Subsequently, I integrated the ckBTC canister into my contract for fund transfer purposes. I encountered an error from my canister during this process, and the relevant code is available in my GitHub repository here.

To summarize, our objective is to enable our canister to transfer ckBTC and, simultaneously, send additional values as needed.

It’s difficult to help without knowing the exact error. Can you change ckBTC-icp/src/fortune_cookie_backend/main.mo at 118d038fab05d256b9982b07ffbc447eb7cf9825 · karangoraniya/ckBTC-icp · GitHub to print out the error received from the Ledger and then try again? I’m sure that will tell us why it is not working.