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
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.
@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.
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.
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:
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.)
User calls notify_dfx(block_height, max_fee, from_subaccount, to_canister, to_subaccount) .
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:
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.
(messed up the first post, didn’t link it to yours, so I’m reposting).
Thanks for the quick reply. I had hoped there could be a simple function “on_receive” or something, that would be called on a canister (if the function exists), directly called by the asset holder (ICP or others), and would thus no longer need checking, as the caller would identify the asset type. This 2-step workflow and manual verification of transaction receipts is quite the bummer, to be honest.
I think I will build our canister so that it will require the asset holder canister to emit such an event to the receiver canister. Since the caller field in the notification call should be the asset holder, the source, token type, and integrity of the data is clearly verifiable by just checking the caller principal. Thus, we will not be supporting native ICP tokens, unless the ICP canister adds such notification functionality, or someone builds a wrapped ICP ledger that supports this kind of notification. I will post the exact specifications of the token standard I’m proposing as a reply to this post once it’s done.
But the ledger canister DOES have that sort of notification mechanism already like I explained above, only, it’s a hassle to use and not without issues. You don’t need example of verification logic in that repository to use it, the verification logic can be done by you, depending on your needs.
Let’s say you are selling an NFT in your canister for 20 ICP. First, user would call some method in your canister called init_purchase or similar, where you would lock up that NFT so it cannot be purchased by anyone else in the meantime and also assign the caller’s principal (which is the buyer) to that NFT id somewhere in your smart contract (some kind of principal → nat pair, probably a hashmap). Then, user would (in the same flow on the front-end get a screen where he would be asked to pay 20 ICP (this is the first transaction I mentioned above). Once this is completed, you call one more tx (which he doesn’t need to know is happening as you can call it automatically i believe once the ledger tx is done for the sakes of better user experience) which would go to notify_dfx and you would send a block number that you get from the first successful transaction that you did when the user sent over 20 ICP to the ledger canister. Ledger canister would then automatically call your own canister method called transaction_notification with the details of the transaction from the ledger that corresponds to the block number that you sent to it in the notify_dfx call. Then, you can just compare what was the principal of the caller that made a tx to the ledger canister in that block, whether amount paid was actually 20 ICP (cost of that NFT), check if that principal is actually the same as the principal you stored in your custom init_purchase method and if it was, you could assign him that NFT, since he already paid for it.
A real hassle, but i think one of the “easier” ways of doing it
Thanks for the feedback, but I’ve already made up my mind. The workflow you outlined only works for the ICP ledger and nothing else, I assume. I’d rather have support for a generic token type and people would have to wrap their incompatible tokens than only support ICP in a non-portable way. Also, I don’t want to have to harden my canister against double spends, if I can avoid them by design. And I don’t think non-atomic transfers are acceptable, it will probably not fit in well with the client logic we’re using, and locking resources on the blockchain side is a huge error source. The code I’m writing must be easily auditable and verifiable. It’s not my fault the ICP ledger canister cannot be used conveniently in a black-box way.