Should ICP’s Wasm code-section and 50,000-function limits be revisited?

I would appreciate some guidance from DFINITY’s execution and replica teams about the current Wasm structural limits and the practical cost of modestly increasing them.

We have spent several months systematically reducing the Wasm footprint of Toko and its underlying open-source frameworks. The relevant Toko Project Instance build is now only slightly above the documented 10 MiB code-section ceiling, but it is also approaching the separate limit of 50,000 declared Wasm functions.

I believe we can probably force it below 10 MiB. My concern is that we are now making increasingly architectural decisions around two relatively tight compiler-output limits, rather than around the natural application, consistency and scaling boundaries.

First: what is the limit currently?

The ICP resource-limit documentation currently states:

Limit Documented value
Total Wasm module size 100 MiB
Wasm code section 10 MiB
Declared Wasm functions 50,000
Exported update/query methods 1,000
Complexity of one function body 1,000,000 Wasm instructions

However, the recent dfinity/ic validation source appears to define:

MAX_CODE_SECTION_SIZE_IN_BYTES = 12 * 1024 * 1024;

Could someone confirm:

  1. whether production subnets currently enforce 10 MiB or 12 MiB;

  2. whether local tooling and PocketIC enforce the same value;

  3. whether the documentation is behind the implementation; and

  4. whether there are any subnet-version differences during rollout?

If production already accepts 12 MiB, part of our problem may simply be a documentation or local-replica mismatch. It would not, however, resolve the independent 50,000-function limit.

What is actually inside this canister?

The current staging Candid surface for the Toko Project Instance contains 183 exported service methods. This is well below the 1,000-method limit, but an exported Candid method does not compile to one Wasm function.

The application implements programmable collections, tokens, generators, assets, vendors, claims, whitelists, role assignment, project administration and the associated read and mutation APIs.

Each public Rust endpoint can retain a much larger transitive graph:

  • Candid decoding and encoding for its request and response types;

  • authorization and caller-context handling;

  • async wrappers and generated future state machines;

  • error and response conversions;

  • entity-specific database adapters;

  • generic instantiations for different models and value types;

  • query, mutation and validation paths;

  • framework lifecycle, readiness and observability integration.

Consequently, the public-method count and the declared-Wasm-function count are very different quantities. The approximately 50,000 functions are primarily internal compiler output, not 50,000 independently designed application operations.

IcyDB

Toko embeds IcyDB, rather than calling a separate database canister.

IcyDB provides the canister-local persistence layer, including:

  • stable-memory row and index storage;

  • journaled atomic mutation;

  • recovery and accepted-schema reconciliation;

  • primary, composite, unique, filtered and expression indexes;

  • relations and constraints;

  • typed and dynamic reads and mutations;

  • a planner, executor and admission layer;

  • pagination, ordering, aggregates, DISTINCT and expression evaluation;

  • schema evolution, integrity checks, metrics and snapshots;

  • an optional reduced SQL frontend over the same execution engine.

Only a few IcyDB administration methods are exported by this particular canister, but the application’s ordinary typed operations still use the embedded storage, validation, planning and execution machinery.

Moving this into a remote “database canister” would not be a neutral binary-size optimization. It would turn local operations into asynchronous distributed transactions, remove canister-local atomicity and introduce additional authorization, replay, latency and failure boundaries.

Canic

Toko also uses Canic, which provides the runtime and fleet layer:

  • lifecycle restoration and readiness fencing;

  • role-specific configuration;

  • topology and placement;

  • canister provisioning and scaling;

  • delegated authentication;

  • cycle monitoring and funding;

  • artifact distribution;

  • durable replay and idempotency;

  • recovery and operational observability.

Canic has already been aggressively reduced. Its current architecture compiles role-specific runtime projections rather than retaining the complete Fleet configuration in every canister. Ordinary managed roles now expose two consolidated Canic methods—command and status—rather than retaining a large family of separate framework endpoints.

That reduction helps the public interface, but the role still needs the implementation behind the capabilities it actually uses.

Why not simply split the canister again?

Toko is already a multi-canister system. Canic distributes it across Roots, hubs, registries, shards, project instances and supporting canisters.

We are not arguing that every feature should reside in one giant canister. Canisters should be separated where there is a genuine scaling, ownership, availability or trust boundary.

Splitting a coherent Project Instance purely to satisfy compiler-output limits would introduce:

  • asynchronous inter-canister calls where operations are currently synchronous;

  • loss of database-level atomicity across the split;

  • partial-failure, retry and idempotency protocols;

  • additional authorization and delegation boundaries;

  • higher latency and cycle consumption;

  • more deployment, recovery and version-coordination state;

  • a larger operational and security audit surface.

That may occasionally be the correct architecture, but it should not become mandatory simply because a mature Rust application produces 10.1 MiB of executable code or 50,001 small functions.

What would raising 10 MiB to 12 MiB actually change?

A change from 10 MiB to 12 MiB increases the maximum code section by 20%.

It would not increase:

  • the per-update or per-query instruction allowance;

  • Wasm heap or stable-memory limits;

  • the 100 MiB total-module limit;

  • the number of exported methods;

  • the complexity permitted in one function;

  • the amount of computation that one message can execute.

It therefore does not give an application 20% more execution capacity per call.

The main costs would instead occur during installation and compilation:

  • up to 20% more code to validate and instrument;

  • potentially higher Wasmtime compilation time;

  • greater peak memory usage during compilation;

  • a larger compiled native-code cache;

  • slightly more state-storage and synchronization traffic;

  • a larger worst-case input for repeated or adversarial installation attempts.

Those are real resource and liveness considerations. In particular, host-side validation and compilation cost should not automatically be assumed to be covered by Wasm instruction metering.

However, this looks like a measurable engineering question rather than an obvious semantic or consensus obstacle. The effect should be benchmarkable against current subnet hardware and the production compiler pipeline.

The function-count ceiling is a separate issue

Increasing only the code-section allowance would not solve a module that declares more than 50,000 functions.

The two limits can also push optimization in opposing directions. Inlining or merging functions may reduce the function count while increasing code bytes and individual-function complexity. Outlining, generic specialization and size-oriented transformations can produce the opposite result.

We therefore end up trying to fit compiler output into a narrow rectangle:

code section <= 10 MiB
declared functions <= 50,000

rather than optimizing against one resource model that reflects the actual validation and compilation cost.

The function limit may be at least as important as the byte limit because every function can carry parser, validator, compiler and native-code metadata even when its body is small. I am not suggesting that it should be raised blindly, only that it should be assessed independently.

Suggested next step

Would DFINITY be willing to evaluate the actual Toko Wasm artifact with the production replica toolchain?

We can provide the exact raw, non-gzipped module privately. Useful measurements would include:

  • validation time;

  • instrumentation time;

  • Wasmtime compilation time;

  • peak compilation RSS;

  • resulting native-code size;

  • install and upgrade latency;

  • comparison against deliberately adversarial modules with similar byte and function counts.

If those measurements show adequate margin, my preference would be:

  1. confirm and document a 12 MiB code-section limit;

  2. consider modest additional function-count headroom based on measurements;

  3. retain the existing per-function and per-message execution protections; and

  4. longer-term, consider a weighted compilation budget or proportional installation charge instead of two unrelated binary ceilings.

We will continue reducing Toko regardless. This is not a request to avoid application-side optimization. It is a question about whether the current ceilings still reflect the capabilities and resource model of the modern IC execution environment—and whether forcing an already distributed application into still smaller artificial components creates more complexity than a carefully measured increase would cost the protocol.

@dominicwilliams get out of the damn steakhouse or ski slopes (depends on hemisphere) and get somebody to answer this

They don’t want to help—they’re already too busy making AI slop.
@bjoern, @bjoernek, and the rest of the crew are fully booked.

Yeah sadly they disregard anything too technical as AI slop, you can’t win. You’d think they’d take the time to read what I wrote.

Try this again, it worked pretty well last time :wink:

Off topic mate. A foundation can be a bunch of scammers and also listen to credible feedback about their software at the same time, it’s not mutually exclusive.

Honestly, reposting a post you got hidden just to provoke Adam says more about you than it does about him.

Off topic mate, honestly you are not helping.

Says more about Adam then Jerry in his last video.

Sorry what AI slop are you talking about? Any comments on the actual substance of this post?

Vlad, Wenzel must’ve upgraded your spy equipment. :joy:
You’re monitoring me and Adam with more dedication than anyone monitors the actual project. :skull: