Skip to content

Token: the whale cap silently stops being enforced once admin is revoked #406

Description

@zachyo

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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions