Transfer ICP - permissions problem

Hello everyone,

I have a question about transferring ICP in a canister. The Ledger canister ID is “ryjl3-tyaaa-aaaaa-aaaba-cai”. I tried to transfer ICP from one account to another, but I encountered a problem: “Transfer from this account is not allowed.” How can I get permission to transfer ICP from one account to another? I am trying to do this in Motoko code.

Thank you.

Are you using the transferFrom function? Can you share exactly what you are trying to do or provide some code?

Here is the function that I am trying to do

let Ledger = actor “ryjl3-tyaaa-aaaaa-aaaba-cai” : LT.Self;
let icp_fee : Nat64 = 10_000;

public func transferICP( transferFrom : Principal, transferTo : [Nat8],transferAmount : Nat64) : async LT.TransferResult {
let res = await Ledger.transfer({
memo: Nat64 = 0;
from_subaccount = ?Helpers.getSubaccount(transferFrom);
to = transferTo;
// The amount of ICP, minus the necessary transaction fee
amount = { e8s = transferAmount - icp_fee };
fee = { e8s = icp_fee };
created_at_time = ?{ timestamp_nanos = Nat64.fromNat(Int.abs(Time.now())) };
});
};

Thank you for sharing the code.

It looks like you might be trying to transfer assets from an account that the canister does not have permission to access directly. For such cases, you should follow the ICRC2 standard, which involves obtaining an allowance from the user’s principal before transferring on their behalf.

Here’s what you need to do:

  1. Get Allowance: The user must first approve the canister to spend a certain amount of ICP on their behalf. This is done using the approve function, where the user grants the canister permission to transfer a specified amount of ICP from their account.

  2. Transfer From: Once the allowance is set, you can use the transferFrom function to transfer ICP from the user’s account to the desired recipient.

Here’s an example of how you can modify your process to follow the ICRC2 standard:

Step 1: User Approves the Canister

The user must call an approve function to allow the canister to transfer funds:

  // Login user
  const agent = new HttpAgent({ identity });
  
  // Create an actor for the ledger canister
  const ledger = Actor.createActor(ledgerIDL, {
    agent,
    canisterId: ledgerCanisterId,
  });

  // Approve the canister to spend the specified amount
  const approveResult = await ledger.icrc2_approve({
    // This should be your canisterId
    spender: { owner: Principal.fromText(canisterId), subaccount: [] },
    amount: amount,
    from_subaccount : [],
    expected_allowance : [],
    expires_at : [],
    fee : [],
    memo : [],
    created_at_time : [],
  });

Step 2: Canister Transfers ICP

After approval, the canister can transfer the ICP:

public func transferICP(transferFrom: Account, transferTo: Account, transferAmount: Nat64) : async LT.TransferResult {
    let res = await Ledger.icrc2_transfer_from({
       from: transferFrom;
       to: transferTo;
       //..
    });
    return res;
}

Please ensure that the user has approved the canister to manage their ICP before attempting the transfer. This approach follows the ICRC2 standard and ensures proper authorization and security.

1 Like

Thanks bro appreciate it