Summary
katana init rollup (with --cartridge.controllers) embeds the Cartridge Controller
account classes into the rollup genesis, but they land on-chain at non-canonical
class hashes. The Controller keychain deploys an account at the canonical class
hash for its version, so the genesis-seeded classes are unusable for auto-deploy —
starknet_getClassHashAt/the deploy fails with "Class with hash 0x… is not declared",
and the account can never deploy on the appchain.
This is a follow-up to #584 ("declare Controller account classes in rollup genesis"):
that PR seeds the classes, but the genesis serialization round-trip changes their
computed class hashes, so the on-chain hashes don't match the canonical artifacts.
Impact
A Cartridge Controller cannot auto-deploy (and therefore cannot sign) on a
katana init rollup appchain out of the box, even with --cartridge.controllers.
Both examples/cross-chain-game and examples/cross-chain-dungeon work around it by
re-declaring the canonical controller artifacts at boot (scripts/declare-controller-class.ts).
Evidence
Appchain genesis (.run/chain-config/genesis.json) declares 10 classes as inline sierra.
Computing each one's class hash (starknet.js computeContractClassHash) yields hashes
that match none of the canonical controller artifacts:
genesis[0] -> 0x335d906ee6dac6d9c3b1733ddd514f494982aa9ec4d180af7e603893da7d9b7
genesis[1] -> 0x3f7111ba26a37797004efe45f9a9dac4e14dd07c5dbe41f9a174d96df29469e
... (8 more, all non-canonical) ...
Canonical hashes (from crates/contracts/contracts/controller/account_sdk/artifacts/classes):
controller.latest / controller.v1.0.9 -> 0x743c83c41ce99ad470aa308823f417b2141e02e04571f5c0004e743556e7faf
controller.v1.0.8 -> 0x0511dd75da368f5311134dee2356356ac4da1538d2ad18aa66d57c47e3757d59
On the running appchain, starknet_getClass for either canonical hash returns
"Class hash not found" until the artifact is re-declared via a normal declare tx.
Likely root cause
The genesis stores classes as inline sierra; the JSON (de)serialization round-trip
re-encodes the sierra program (felt/number formatting, field ordering), so the class
hash computed at genesis load differs from the hash of the original artifact. #584
declares the round-tripped classes, landing the shifted hashes rather than the
canonical ones the keychain deploys.
Repro
katana init rollup --id <X> ... then run with --cartridge.controllers.
starknet_getClass for 0x743c8… (controller.latest) -> not found.
- Connect a Cartridge Controller and send a tx -> account auto-deploy reverts with
"Class with hash 0x… is not declared".
Workaround (current)
Re-declare the on-disk canonical artifacts at boot. Note a Controller account is pinned
to the class version it was created with (e.g. v1.0.8), and the keychain deploys
it at that version — so all bundled versions must be declared, not just latest:
for (const v of allControllerVersions) {
await account.declareIfNot({ contract: `${v}.contract_class.json`, casm: `${v}.compiled_contract_class.json` });
}
Suggested fix
Make init rollup preserve canonical class hashes through the genesis round-trip
(declare/seed from the original artifact bytes, or store the class hash explicitly and
verify it round-trips), so genesis-seeded controller classes are queryable at their
canonical hashes and no boot-time re-declare is needed.
Summary
katana init rollup(with--cartridge.controllers) embeds the Cartridge Controlleraccount classes into the rollup genesis, but they land on-chain at non-canonical
class hashes. The Controller keychain deploys an account at the canonical class
hash for its version, so the genesis-seeded classes are unusable for auto-deploy —
starknet_getClassHashAt/the deploy fails with "Class with hash 0x… is not declared",and the account can never deploy on the appchain.
This is a follow-up to #584 ("declare Controller account classes in rollup genesis"):
that PR seeds the classes, but the genesis serialization round-trip changes their
computed class hashes, so the on-chain hashes don't match the canonical artifacts.
Impact
A Cartridge Controller cannot auto-deploy (and therefore cannot sign) on a
katana init rollupappchain out of the box, even with--cartridge.controllers.Both
examples/cross-chain-gameandexamples/cross-chain-dungeonwork around it byre-declaring the canonical controller artifacts at boot (
scripts/declare-controller-class.ts).Evidence
Appchain genesis (
.run/chain-config/genesis.json) declares 10 classes as inline sierra.Computing each one's class hash (starknet.js
computeContractClassHash) yields hashesthat match none of the canonical controller artifacts:
Canonical hashes (from
crates/contracts/contracts/controller/account_sdk/artifacts/classes):controller.latest/controller.v1.0.9->0x743c83c41ce99ad470aa308823f417b2141e02e04571f5c0004e743556e7fafcontroller.v1.0.8->0x0511dd75da368f5311134dee2356356ac4da1538d2ad18aa66d57c47e3757d59On the running appchain,
starknet_getClassfor either canonical hash returns"Class hash not found" until the artifact is re-declared via a normal declare tx.
Likely root cause
The genesis stores classes as inline sierra; the JSON (de)serialization round-trip
re-encodes the sierra program (felt/number formatting, field ordering), so the class
hash computed at genesis load differs from the hash of the original artifact. #584
declares the round-tripped classes, landing the shifted hashes rather than the
canonical ones the keychain deploys.
Repro
katana init rollup --id <X> ...then run with--cartridge.controllers.starknet_getClassfor0x743c8…(controller.latest) -> not found."Class with hash 0x… is not declared".
Workaround (current)
Re-declare the on-disk canonical artifacts at boot. Note a Controller account is pinned
to the class version it was created with (e.g.
v1.0.8), and the keychain deploysit at that version — so all bundled versions must be declared, not just
latest:Suggested fix
Make
init rolluppreserve canonical class hashes through the genesis round-trip(declare/seed from the original artifact bytes, or store the class hash explicitly and
verify it round-trips), so genesis-seeded controller classes are queryable at their
canonical hashes and no boot-time re-declare is needed.