Enable Canisters to Hold ICP

Quick update on the ledger build instructions: The documentation PR is on review, you can try using this little script to get the canister and the Candid interfaces for now:

export IC_VERSION=a7058d009494bea7e1d898a3dd7b525922979039
curl -o ledger.wasm.gz https://download.dfinity.systems/ic/${IC_VERSION}/canisters/ledger-canister_notify-method.wasm.gz
gunzip ledger.wasm.gz
curl -o ledger.private.did https://raw.githubusercontent.com/dfinity/ic/${IC_VERSION}/rs/rosetta-api/ledger.did
curl -o ledger.public.did https://raw.githubusercontent.com/dfinity/ic/${IC_VERSION}/rs/rosetta-api/ledger_canister/ledger.did

This script fetches the ledger canister module that our CI pipeline built. This script will replace the Dockerfile in the first step of the instruction. You won’t need to build the canister from scratch anymore.


Yes, it worked. But now the problem is with the ledger-transfer example :grinning:. Are the instructions in the project correct?

1 Like

Indeed, the example was not in sync with the latest Ledger version.
I created a PR to fix that a couple of weeks ago: Update the ledger transfer example by roman-kashitsyn · Pull Request #130 · dfinity/examples · GitHub and I’ve just merged this PR to master. Could you please try the new version?


Okay, I’ll test it today or tomorrow.

1 Like

Tested it. There are still errors.

dfx canister call ledger_transfer canisterAddress '()'
The Replica returned an error: code 3, message: "Canister r7inp-6aaaa-aaaaa-aaabq-cai has no update method 'canisterAddress'"

It may be worth changing the dfx canister call ledger_transfer canisterAddress '()' command to dfx canister call ledger_transfer canisterAccount '()'. It seems to be working with her.
P.S. Sorry for my bad English :grinning:


This is the issue that I’m addressing in the Invoice Canister payments flow initiative


Looking at the NNS ledger candid interface, it seems that there still isn’t a way to query a block if you’re using Candid/Motoko: Principal ryjl3-tyaaa-aaaaa-aaaba-cai | ic.rocks

Sorry if I’m missing something, but it seems that I still need to trust the workaround canister (ockk2-xaaaa-aaaai-aaaua-cai) in order to validate transactions on chain?

1 Like

After doing some digging, the pattern Roman describes in his Community Conversation on canisters holding ICP provides what seems to be a good method for conducting payments onchain (my use case) without querying blocks.


I got an error. Anyon can help?

Many thanks,

Fwiw, I have pinged team to see if anyone can help with your error.


Many thanks, I used IC_VERSION=a7058d009494bea7e1d898a3dd7b525922979039

The error seems to indicate that your ledger.did file contains 404: Not Found, which is not valid Candid. I guess there was an error when you fetched it. What command did you use to fetch this file?

1 Like

Hello! Is there an instruction for setting up a canister that needs to be connected to ledger canister and manage icp on the main network?

1 Like

@bogwar Can the cycles minting canister give back the specific number of the cycles that it creates when topping up a canister? I know we can call for the exchange rate and make the calculation but it is possible that the exchange rate will change between the top-up call and the check-rate call, so it is better if the top-up call gives-back the number of the cycles that it mints.

Hi @levi,

Yes, I think we can return that info. I’ll added to the things to change in the new for notifications.

1 Like

Hi @bogwar, Great thanks!

Reposting this from another thread for visibility: I need to be notified in my canister when someone pays ICP to it. Is some kind of subscription or event handling feature already implemented? I need this in a grant that just got approved. We were under the assumption that this was implemented already, but I can’t find anything about it.

There’s some work on that here by the open-chat team. Maybe this works for you?

1 Like

It is one of the biggest “issues” right now. You can do this by using transaction_notification method in the canister to which the payment that goes into the ledger is initially sent.
This would be a general workflow if going this route:

  1. User calls send_dfx(memo, amount, fee, from_subaccount, to, created_at_time) on Ledger to send ICP to our receiving canister (we’ll break down the parameters of that function call shortly.)
  2. User calls notify_dfx(block_height, max_fee, from_subaccount, to_canister, to_subaccount) .
  3. TxAuth receives the notification from Ledger that was created by our notify_dfx call. We validate the data we are receiving from the Ledger, and add it to our list of authenticated transactions.

There is an example of this in this repository GitHub - sagacards/legends-minter
However, example does not cover any checking of the actual ledger transaction in the canister inside of the transaction_notification method (which you must do to confirm if a payment was properly sent with correct amount to your canister).

Takeaways and issues by using this approach are:

  1. On the client side (javascript), user needs to send two transactions.
    First is to send_dfx method of the ledger canister to send ICP for whatever he wants to purchase
    Second to notify_dfx ledger canister method after his first transaction was a success (where he would get the actual block number as response that he must use in this call)

If a user navigates away from the client dapp before second transaction has been sent, you would never get transaction_notification called in your canister that got those ICP’s that the user sent in the first transaction, hence not actually being able to check it and send the user whatever he has purchased by his first transaction. This would lead to you having to issue refunds or similar to people that did not complete the full process and / or perhaps state that there are no refunds for the purchases made if users do not complete the complete “checkout” process…

You also need to have a bunch of bloated logic that saves what the user is trying to purchase BEFORE he does the first send_dfx transaction (ie: save his principal or similar and the ID of the item he is trying to purchase so that you can compare the item with that ID price vs how much was sent in that transaction later on in the notification_transaction method once you get a confirmation of a payment back from the ledger. You also need to take care of “double spend” attack perhaps, where user might call notify_dfx with the same block number multiple times, which would make the transaction_notification trigger multiple times for the same transfer.


that’s a cool project, but sadly, it seems it is no longer maintained and I also couldn’t find transaction verification logic in there.