Calling ICP ledger canister methods: backend or frontend?

This is probably going to sound stupid, but where should I be calling ICP ledger methods from?

Calling them from the backend (via intercanister calls) sounds most secure. But if so, how does that work? If an authenticated user clicks on a “Pay with ICP” button, which calls my backend, which in turn calls the ICP ledger canister, at what point does the user authorize the ICP transfer from the frontend?

Also, if anyone got any insights on this other question, I’d be most appreciative! :pray:


It depends on your application! If users are holding their own crypto in a wallet, many of them will prefer to hold their own funds. “Not your keys, not your crypto” is an adage that many people believe in, and will be reluctant to let a smart contract hold their tokens for them, particularly without high transparency, reproducible builds, and blackhole or dao control of the contract.

1 Like

It’s a dapp, so no tokens held by the app!
React frontend, motoko backend, both in separate canisters. Users log in with their Internet Identity.

What is your opinion in this case?

I suggest you call the approve function of ICP ledger and pass your canister id, so you can transfer funds from user wallet to your canister by calling the ledger from your canister.
So the flew would be like
1=> you call the approve function of ICP ledger from your frontend
2=> you call the transfer_from function from you canister to transfer the ICP.

1 Like

icrc2_transfer could be called periodically from the backend once I have the approval, so that makes sense, thanks.

But about approve: how would you call it from the frontend? Would you use the @dfinity/ledger-icrc library?

I tried using it like so (basically copy/pasted from this example):

const agent = await createAgent({
  //host: HOST,

const { metadata } = IcrcLedgerCanister.create({
  canisterId: process.env.CANISTER_ID_ICP_LEDGER,

const accountIdentifier = AccountIdentifier.fromPrincipal({
  principal: Principal.fromText(principal),
  //subAccount: principalSubaccount

const data = await metadata({});

but got a Invalid certificate: Signature verification failed error.
Any idea what that’s about ?

You can use agaent/js library to make calls to any canister on icp from you frontend.
Just to mention if you want to call the approve function you should do it with an identity from a wallet or etc, you can also call the function with anonymous identity but i dont think it makes much sense. So if you go through icp wallet documentations you will get some clarity of how to make calls to icp ledger with the principal of that wallet.