Secure icrc ledgers for free 🧙

In the frozen shadows along the vast walls that mark the boundary between Degenland and the world beyond, I stand guard, much like the Night’s Watch of old. Forever bound to this duty, I often come across ancient ledgers, carried by brave souls, hoping their tokens might find a place in the annals of

Some of the problems devs come across while trying to pass the gate:

  • Can’t make their ledger source replicable well enough to prove it compiles to a wasm with a specific module hash (I don’t want to run scripts from anons without checking them first which means they have to either not have complicated obfuscated scripts or make a docker container of some sorts) There is a popular ledger going around with .sh file and a base64 encoded things that get eval-ed and that’s not something I will run
  • The ledgers are not tested. In my journey, I’ve come across awesome libraries that at particular versions somehow came short after a few thousand records and there is no way of knowing if they will perform unless someone tests them
  • Ledgers aren’t audited. The IC hack-hackathon era left us with a dozen ledgers of all sorts that people randomly use, but none of them were really audited. Even the beloved Psychodelic WICP ledger is massively flawed with the special ability to throw errors while transferring funds. I’ve notified organisations who were susceptible to the attack and they’ve got their handling fixed
  • Not blackholed. Such a ledger can be modified by its controllers and isn’t decentralized even tho it’s running inside a decentralized protocol.
  • No transaction history. No comment, but it’s bad.
  • Distribution - Unknown and unwitnessed

For the purpose of simplifying the explanation, let’s coin a term - Napkin ledger. That is a ledger that is as decentralized and secure as someone keeping account balances on a napkin. All of the LP in dexes paired with a napkin ledger can be stolen. You can’t really call a napkin ledger - crypto. It’s like a man dressed in Superman’s suit that is not Superman.

It seems to be not immediately obvious for not-so-obvious reasons:

  • A ledger that is not DAO-controlled or blackholed is a napkin ledger
  • A ledger that is blackholed, but nobody can verify its module hash comes from public source code is a napkin ledger
  • A ledger that is blackholed and verifiable, but has vulnerabilities is a napkin ledger
  • A ledger that is blackholed, verifiable with no vulnerabilities, but nobody witnessed its distribution is (stretching it a bit here) a napkin ledger or you could say Sam’s ledger.

I believe we need to protect the newly arriving users and not expose them to risks they don’t understand. It seems most who unknowingly took a risk that went boom will later blame Dfinity for ‘allowing it’.

There is a solution I believe and it seems to be pretty easy to come to. I am just summing 1+2 here. Yes, there is the SNS and it solves these, but it doesn’t fit everyone and also it won’t give you secure ledgers for utility tokens like @ellipticdao 's TAL

  1. NNS votes on SNS ledger wasms and stores them in a canister with a clear upgrade path with patches to the latest version. These are the ledgers SNS uses and are pretty secure and battle-tested.
  2. Someone can create a blackholed canister, that uses that registry and installs new ledgers with initial params. It will be the only controller and it will update these ledgers automatically with patches. (Casually blackholed ledgers will be struck with a specific version and that isn’t as good) That will make them as secure as SNS ledgers (Except the part that an SNS DAO can refuse a ledger upgrade). They will still need additional distribution contracts so they don’t become Sam’s ledger, but that’s a different problem.

What do you think of this? Is it something the SNS team is okay with and can guarantee that the wasm registry canister’s interface won’t change? @lara @bjoernek If it changes then the blackholed controlling canister will stop upgrading ledgers. It could also be NNS-governed. Maybe someone is already making that as we speak. SNSes also need additional ledgers.


In terms of user experience for newly arriving users, I think we need is a graphical interface to provide all that information, not everyone in around web3 has the technical knowledge to get into all the details, including myself.

Here’s some information I’d like to be displayed based on the frozen shadows along the vast walls that doesn’t mark the boundary between Degenland and the world beyond.

-The Ledger (Name or group, a description or what is it about)
-Is it DAO controlled, Blackholed, both, or none(napkin ledger)
-What exactly does it mean to be blackholed and napkin (tool tips)
-If its source code is Public or Private
-If the Hash Module can be verified that comes from the public source code (Verifiable)
-If the code has vulnerabilities (need list of Trusted auditors and only for ledgers with verifiable public source code)
-If vulnerabilities are found, list them and the potential risks around them
-If passed the Audit, who did it and why they are to be on the Trusted Auditor list
-If it had a public verifiable initial distribution, and if it does, preferably show in pie charts

Is there any project like this outside the IC? to take it as an example. I know there’s companies like Certik, but it’s all based on just paying their re-selling distributors. How come we can’t do something completely decentralized?

It seems like a big adventure to hop in, not only for code but also to find those auditors and make those ledgers to get verified.


I mentioned this topic area on a DSCVR space yesterday… I agree that we (IC collectively) need to tighten up our ledgers. I understand the why we’ve got to where we are - we’re about 12-18 months behind where we should be with token standards. ICRC-1 through 3 should have been agreed a long time ago.

However looking back doesn’t help where we are now. The honest truth is we’re far too vulnerable to bad actors using most (not all) of the token ledgers on IC. The key problem we have is that there isn’t an easy way to see/ audit the code a canister is running. I honestly think that this should be a higher priority than ckETH.

Can you imagine going onto the IC dashboard, searching a canister and having the option to view source code or at the very least know that the canister === a github repo.

I’m not a super leet coder and I could stick a backdoor into a ledger canister and deploy and update in 30min or less… without any change showing to the candid file/ public interface. This has just given me an idea - perhaps a multi-sig canister type could be created where a single developer principal cannot update a canister code (at the IC level)?

With ETH integration we’re going to get more scammers and at the moment our tendency for napkin ledgers is setting us up for a bad situation.

1 Like

Devs are responsible for making their builds easily and safely replicable. The dashboard can’t do the verification unless a project is a really simple almost one-file contract, like the verification I’ve put in Blast. It’s just hard for a dev to set it up properly. Basically, you build the project, get the hash of its binary, and compare it to the one working on the IC.

Not sure what you mean overall. We aren’t that vulnerable. From the ICPcoins in the list, only Tendy and Boxy aren’t secure and they are working on it. All the rest on the list are perfectly fine. There are other tokens in the ecosystem - most without teams, no sites, no liquidity in pools, whitepapers, or any info. Anyone can create a dozen of these in their lunch break. Some of them are decent, we haven’t listed everything good, but we are in the process of doing so.

Your interface doesn’t play a role in the security of a canister, module hashes and source codes do.

No need for a new canister type. You can have a canister that is the controller of another one and does this. It’s how DAOs work.

Fair point with canister being controlled by another canister… as long as the controlling canister is blackholed/ DAO upgradable.

Are all the canisters you mention blackholed or DAO controlled? I’m not talking about code specific vulnerabilities like the one you found. I’m talking about the ability for a dev to upgrade the canister with new code. For example sticking in a new update method which can alter the ledger state.

The risk is that a dev could in a couple of minutes push new code to the ledger and be away with the funds before anyone notices the module hash and source code don’t match.

I agree that source code/ module hashes are the key… however without monitoring this is next to useless. Sure you can verify my code when I launch but who’s checking the canister module hash every few minutes to see if I’ve been a naughty dev?

In essence I’m agreeing with you that there is a problem with the current way devs are creating/ using ledgers. I’m not critiquing your solution. :+1:

EDIT - Sorry I should say I like your solution BTW for getting verified code into the ledgers. Am I right in thinking that a dev could still upgrade the canister though?

Well, this tells me things need a lot more explaining. I’ve already planned on adding tooltips & badges for ‘DAO governed’, ‘blackholed’ etc, but it looks like I should prioritize it.
Yes, these are all secure. DAO controlled. OGY through their own DAO. XTC - Sonic SNS. The rest are NNS-governed.

Nope. With this approach nobody but the NNS can do that

I think we might be crossing wires here a bit. I’m not commenting on ICPCoins or specifically on the tokens you’ve included. Although I like that you are auditing the tokens you are adding :slight_smile:

I’m making a general point that on the IC it’s crazy easy to create a ledger or any smart contract, and then update the ledger with malicious code without anyone noticing. This is the issue.

EDIT - Sorry I missed the bit in your first post about the only controller being the canister installing the wasm. I’ve re-read and like your solution. Again, I’m agreeing that this is a problem. Your solution is a good work around.

I’d still like to see a way to routinely check or get alerted if a canister hash unexpectedly changes. I know cover protocol was trying to go down this route but that died when psych left.

1 Like

Oh, I see. Yes well, outside this list pretty much everything is non-blackholed, non-audited, and hard to verify. And some of the teams are good-intentioned, but they can’t afford to pay for an audit so they stay away from blackholing. Their only solution for getting a good secure ledger is with the mechanism I’ve proposed.

1 Like

A stream of random thoughts that don’t go anywhere and may muddle the situation further, but probably should be said.

The issue with trying to standardize the actual wasms of ledgers is something along the lines of ‘any sufficiently interesting token requires some unique functionality’. Sometimes this is simple. For example, the OGY canister uses the ICP canister, but we added a feature to allow ‘super principles’ that have ledger rights. We use this for the DIP20 passthrough and hope to deprecate it once we activate ICRC2 and the main DEXs can switch over. But we needed that like 16 months ago and if we’d needed to wait for ICRC2 we just wouldn’t have had DEX functionality.

Other functionality is more complicated. Like YC charging a % on each transaction, or other kinds of demurrage.

This makes the build and replicate super important. Psychedelic was working on Cover at one point and ICDevs sponsored a Chrome extension that would warn you if you were using a non-validated wasm: Complete: - Bounty #9 - Cover Browser Plugin

Of course, leaving it up to the user to install something like that is a usability nightmare.

One other consideration, that I don’t really know the answer to…AXON deploys tokens and the token is a subcontract…so it isn’t really compiled…well maybe it is…but it is put into the parent wasm and then deoplyed…I don’t know if you compile it separately if the wasms match. I think this may be a question for @claudio as he worked on the system upgrade features for motoko.

Never mind who is going to read and validate all these contracts/upgrades. DFINITY is likely the only player in the ecosystem who has the resources to write a rust-based contract and have it audited. But rust is an awful language for the ‘common person’ self-auditing. My personal opinion is that until system contracts are written in a ‘smart contract shaped’ language that is high level enough to be read by ‘most’(aka a “trustable mass of programmers”) there will be an almost unbridgeable trust gap. Right now that is likely motoko and solidity. I haven’t read many Azel contracts, but they at least have the advantage of being in a common language that ‘most’ programmers know.


I think most of the time having something basic that works is going to be okay. Yes, you can add things inside the ledger to get a speed boost and add special features similar to what YC did, but that’s a huge tradeoff that requires big teams to be done right.

Hi all,
very interesting discussion! As I was tagged, let me share some initial thoughts on this.

What do you think of this? Is it something the SNS team is okay with and can guarantee that the wasm registry canister’s interface won’t change? @lara @bjoernek If it changes then the blackholed controlling canister will stop upgrading ledgers.

I don’t think we have thought about this, so I think this is not a guarantee that is given right now.

Some potential difficulties that I see:

  • A blackholed canister always has the risk that it cannot be changed if any bugs / problems are found later. Of course on the other hand it provides stability / verifiability that lasts. I am sure you are aware, but thought it might be worth recalling for everyone.
  • An upgrade might sometimes require more than just a WASM. For example, one problem that is also not yet solved for SNSs is the following. The ledger canister can be upgraded to support ICRC-2, but for this an upgrade requires upgrade arguments. Similarly upgrade arguments are need for example to reset certain configurations in the ledger, e.g., the token symbol*. This is currently not supported by SNSs, as it is a bit unclear who would provide these arguments. For ICRC-2 support, one can argue that this could be set by NNS decision to the overall SNS framework - included in the next ledger version that SNSs can upgrade to. However, things like the token symbol need to be changed on an individual ledger-basis (some might even argue that it should not be supported at all). So due to these aspects
    • it might be conceivable that the SNS framework needs to be adapted in the future
    • I expect there might be similar challenges in your example (questions regarding who could provide args if this would ever be needed)

* I have to double-check this, but I think this is one of the examples that is already supported by upgrade arg


I’m not a super leet coder and I could stick a backdoor into a ledger canister and deploy and update in 30min or less… without any change showing to the candid file/ public interface. This has just given me an idea - perhaps a multi-sig canister type could be created where a single developer principal cannot update a canister code (at the IC level)?

I was wondering if you know the possibility to look at a canister’s “history”?

Note that this would not help with the attack that you describe below, where someone upgrades the code to something maliciously and runs away with the tokens.
However, it can help in making the canister more verifiable: without looking at the history I could look at a canister’s code today and tomorrow without noticing that in between it was running a version with a backdoor. By looking at the history, I can see if unexpected upgrades were done.


Yeah that’s interesting - I didn’t know that! I think canister history is certainly part of the security package along with alerts and verifying the canister’s hash against a codebase.

I would really love this to be a thing on ICP… given enough time I might be able to code this myself but I’m quite new to rust and still getting my IC dev skills up to speed.

@infu is a lot better at coding than me - do you fancy building a canister verification/ history/ alert system? You’ve got a good head for this kind of stuff!

I’ll help write the Dfinity grant request for you if needed (I honestly think this would be a no-brainer) and I’ve got an OpenChat bot (work in progress) which could handle the user alert side.

1 Like

fyi if you are going in this direction, maybe the following example of how to use the canister history might be useful: ?


Thanks, all information is going to be helpful.
Please let me know if some of these statements are wrong (I haven’t had the chance to go deep into the ledger yet. For now overall architecture will be good enough). Don’t feel obligated to explain too much. If you tell me where I am wrong, I can read the code and figure the rest:

  • A canister can set the NNS gov canister ‘rrkah-fqaaa-aaaaa-aaaaq-cai’ as controller and later anyone can make proposals of type ‘NNS Canister Upgrade’ to upgrade this canister (In this case - when there are unexpected changes in the upgrade system)
  • Ledger source code is
  • The ledger canister has the archive canister wasm inside and spawns its archive canisters with it.
  • Upgrading the ledger canister will ensure archive canisters get upgraded too.
  • The ledger doesn’t refill with cycles its archive canisters and another system has to do it.
  • The index canister has to be installed separately and attached to the ledger. It will read from archive canisters.
  • NNS canisters have all the wasms needed (ledger and index) in different versions.
  • One can start from any version as the first.
  • The upgrade path is one for all, there are no forks.
  • There is no chance for one canister upgrade to fail while others go through. It’s just a straight line patch after patch.

Not sure that’s going to bring much value. A lot of the ledgers don’t need monitoring since they are secured by the NNS. Also, funds can be drained from everywhere in 4sec after a malicious upgrade. So basically, that system will be monitoring non-blackholed non DAO governed ledgers? and sending SMSes to people that it’s all gone and they can’t do anything :slight_smile:
Maybe it will be still valid for other types of canisters, haven’t thought about that.
We are going to try to make a canister that installs NNS-blessed ledgers once the latest icpcoins upgrade is complete.


@infu Is there an official procedure to be listed on ICPCoins? Could that be made official? $SLICES 5000 team contacted you since September 24th and so far no response. It would be good to know if there is a procedure for a token to get listed. At your convenience, please check out $SLI open-source code here : 5000SLICES/5000-SLICES-AND-GOLDSLICE-DIP20-STANDARDS at master (

If by sub-contract you mean actor class, yes these are essentially compiled with the wasm embedded in the wasm of the importing actor (or actor class). The code should match that of the separately compiled actor class, bar some unforeseen compilation artifacts I’m unaware of.

1 Like