Skip to content

fix(token): make the whale cap getter honest and surface the revoke-admin trade-off - #445

Merged
zachyo merged 1 commit into
soropad:masterfrom
khaylebfortune:whale-cap
Sep 2, 2026
Merged

fix(token): make the whale cap getter honest and surface the revoke-admin trade-off#445
zachyo merged 1 commit into
soropad:masterfrom
khaylebfortune:whale-cap

Conversation

@khaylebfortune

Copy link
Copy Markdown
Contributor

Closes #406

Summary

revoke_admin deactivates the max-balance-per-account (whale) cap as an intentional side effect of removing the admin role — mint/transfer stop enforcing it so the token stays fully transferable (the fix shipped for #323). But nothing surfaced that consequence. max_balance_per_account() kept returning the stored percentage, PolicyCard presented the cap as a standing rule, and TokenStatusBanner framed immutability purely as a trust win. Holders were told the token is both immutable and whale-capped, when revoking admin is exactly what turned the cap off.

This PR makes every surface tell the truth:

Contract (contracts/token/src/lib.rs)

  • max_balance_per_account() now returns None once the contract is locked, even if a percentage was stored, so the getter never reports a limit that is not being enforced. Doc comment updated to say so.
  • _enforce_max_balance_per_account() now actually gives up when the admin is absent. The code read the admin as an Option but still enforced the cap for non-admin recipients after revoke — contradicting the documented behavior. It now uses the let Some(admin) = … else { return } pattern: once revoke_admin removes the admin, the cap is no longer enforced and holders may exceed it.

Test (contracts/token/src/lib.rs)

  • test_non_admin_can_exceed_cap_after_revoke: sets a 10% cap, verifies a non-admin transfer beyond the cap is rejected while it is active, then calls revoke_admin, verifies max_balance_per_account() returns None, and verifies the same non-admin can now accumulate past the former cap — locking in the semantic as intended, not accidental.
  • This also fixes the pre-existing test_holder_actions_still_work_after_revoke, which was failing on master for exactly this reason (the cap still fired after revoke).

Frontend

  • PolicyCard now takes a locked prop and renders the whale cap as inactive when admin has been revoked, with a yellow warning explaining that revoking admin control turned the cap off and a single holder can now accumulate past it (or, when no cap was set, that the cap can no longer be enforced). An inactive cap is no longer shown in the active/blue styling.
  • TokenStatusBanner immutability row now names the trade-off instead of describing end-of-admin purely as a "strong trust signal": supply/admin permissions can no longer change, but any whale-protection cap is also off.

Testing

  • cargo test -p soroban-token: 116 passed, including the new test and the previously-failing test_holder_actions_still_work_after_revoke.
  • The 11 remaining failures are pre-existing on master (stale #[should_panic(expected = "…")] strings vs. the typed TokenError panics) and are unrelated to this change.
  • cargo fmt --check and cargo clippy produce only pre-existing toolchain-version diffs/warnings already present on master; my changes add no new ones.
  • Frontend tsc --noEmit and jest show the same pre-existing failures on master; my touched files introduce no new type errors or test failures.

Files changed

  • contracts/token/src/lib.rs
  • contracts/token/test_snapshots/test/test_non_admin_can_exceed_cap_after_revoke.1.json
  • frontend/app/dashboard/[contractId]/components/AdminPanel.tsx
  • frontend/app/dashboard/[contractId]/components/admin/PolicyCard.tsx
  • frontend/components/TokenStatusBanner.tsx

Once revoke_admin is called the max-balance-per-account cap stops being
enforced, but max_balance_per_account kept returning the stored percentage
and the dashboard presented it as an active limit. Make the getter return
None once the contract is locked, give up (not enforce) in the transfer/mint
path when no admin exists, and name the trade-off in the dashboard.

Closes soropad#406

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@khaylebfortune Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@zachyo
zachyo merged commit 3d7a7f8 into soropad:master Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants