Files: contracts/token/src/lib.rs _enforce_max_balance_per_account; getter max_balance_per_account; frontend/app/dashboard/[contractId]/components/admin/PolicyCard.tsx; frontend/components/TokenStatusBanner.tsx
Issue: #323 fixed a real bricking bug the right way. The helper now reads the admin as an Option and gives up when it is absent:
let Some(admin) = env.storage().instance().get::<DataKey, Address>(&DataKey::Admin) else {
return; // contract is locked; cap no longer enforced
};
That was the correct choice between the two options Wave 8 offered, and transfers survive revoke_admin as promised. But the consequence is not surfaced anywhere. max_balance_per_account() keeps returning the stored percentage after the cap has stopped applying, so the getter reports a limit that is not enforced. PolicyCard presents the cap as a standing rule, and TokenStatusBanner frames immutability purely as a positive trust signal.
The result is a specific false assurance, aimed at the holders least able to verify it. An issuer sets a 5% whale cap, then revokes admin to make the token trustless — the two actions a cautious project takes together. Every surface then tells holders the token is immutable and whale-capped, when revoking admin is exactly what turned the cap off. A large holder can accumulate without limit while the dashboard says they cannot.
Fix: Make the getter honest: return None (or the stored value plus an enforced: bool) once Locked is set, and say so in the doc comment, which currently describes only the storage read. Then show it: PolicyCard should render the cap as inactive with the reason, and TokenStatusBanner's immutability row should name the trade-off rather than presenting it as unambiguously good. Add a test asserting a non-admin can exceed the cap after revoke_admin, which documents the semantic as intended rather than accidental.
Files:
contracts/token/src/lib.rs_enforce_max_balance_per_account; gettermax_balance_per_account;frontend/app/dashboard/[contractId]/components/admin/PolicyCard.tsx;frontend/components/TokenStatusBanner.tsxIssue: #323 fixed a real bricking bug the right way. The helper now reads the admin as an
Optionand gives up when it is absent:That was the correct choice between the two options Wave 8 offered, and transfers survive
revoke_adminas promised. But the consequence is not surfaced anywhere.max_balance_per_account()keeps returning the stored percentage after the cap has stopped applying, so the getter reports a limit that is not enforced.PolicyCardpresents the cap as a standing rule, andTokenStatusBannerframes immutability purely as a positive trust signal.The result is a specific false assurance, aimed at the holders least able to verify it. An issuer sets a 5% whale cap, then revokes admin to make the token trustless — the two actions a cautious project takes together. Every surface then tells holders the token is immutable and whale-capped, when revoking admin is exactly what turned the cap off. A large holder can accumulate without limit while the dashboard says they cannot.
Fix: Make the getter honest: return
None(or the stored value plus anenforced: bool) onceLockedis set, and say so in the doc comment, which currently describes only the storage read. Then show it:PolicyCardshould render the cap as inactive with the reason, andTokenStatusBanner's immutability row should name the trade-off rather than presenting it as unambiguously good. Add a test asserting a non-admin can exceed the cap afterrevoke_admin, which documents the semantic as intended rather than accidental.