52669 sc low token minting is blocked for whitelisted addresses when transfersallowed is false
Submitted on Aug 12th 2025 at 11:04:26 UTC by @magtentic for Attackathon | Plume Network
Report ID: #52669
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol
Impacts: Contract fails to deliver promised returns, but doesn't lose value
Description
Brief / Intro
When transfersAllowed is disabled, the whitelist restriction module still allows whitelisted users to transfer tokens. However, minting new tokens to those same whitelisted users fails.
This occurs because address(0) (used as the source address for minting) is not and cannot be whitelisted, which causes the transfer validation to fail even if the recipient is whitelisted and the caller has the MINTER_ROLE.
Vulnerability Details
Whitelisted users cannot have tokens minted to them because address(0) will never be whitelisted. Within the ArcToken contract, _update checks whether a specificRestrictionModules is configured and uses it to validate the transfer:
function _update(address from, address to, uint256 amount) internal virtual override {
...
address specificTransferModule = $.specificRestrictionModules[RestrictionTypes.TRANSFER_RESTRICTION_TYPE];
if (specificTransferModule != address(0)) {
transferAllowed =
transferAllowed && ITransferRestrictions(specificTransferModule).isTransferAllowed(from, to, amount);
}
...
if (!transferAllowed) {
revert TransferRestricted();
}
...
}If transfersAllowed is false, a transfer will only be allowed if the restriction module explicitly approves it.
In the WhitelistRestrictions module, approval is based on whether both from and to are whitelisted. Attempts to whitelist address(0) are blocked:
function addToWhitelist(
address account
) external onlyRole(MANAGER_ROLE) {
if (account == address(0)) {
revert InvalidAddress();
}
...
}And batchAddToWhitelist explicitly skips zero address:
function batchAddToWhitelist(
address[] calldata accounts
) external onlyRole(MANAGER_ROLE) {
WhitelistStorage storage ws = _getWhitelistStorage();
for (uint256 i = 0; i < accounts.length; i++) {
address account = accounts[i];
if (account == address(0)) {
continue; // Skip zero address
}
...Because address(0) will never be whitelisted, isTransferAllowed(address(0), to, amount) returns false even when to is whitelisted and the caller has MINTER_ROLE. That causes mint operations to revert with TransferRestricted().
Impact Details
Minting tokens to whitelisted addresses fails when
transfersAllowedis disabled, even when the caller has theMINTER_ROLE.This unintentionally blocks minting in scenarios where the restriction system is otherwise configured to allow it.
It creates inconsistency between transfer and mint semantics for whitelisted users.
References
This is a resubmission of #49716 with an updated PoC and explanation. The project's own tests show expectations that minting/burning will work with temporary whitelist toggles (see the ArcToken test suite referenced below):
Test suite reference: https://github.com/plumenetwork/contracts/blob/main/arc/test/ArcToken.t.sol
An example comment from tests:
// 1. Mint tokens to nonWhitelisted1 (requires whitelisting temporarily or transfersAllowed=true)This implies tests expect temporary whitelisting or disabled transfer restrictions to allow minting — inconsistent with the observed behavior when transfersAllowed is false.
Proof of Concept
Steps to reproduce (summary)
Attempt to add
address(0)to the whitelist usingaddToWhitelist— this reverts withInvalidAddress().Add a user (Alice) to the whitelist and validate
isWhitelisted(alice).Disable transfers via the whitelist module (
setTransfersAllowed(false)), confirming transfers are restricted.Attempt to mint tokens to Alice — mint reverts with
TransferRestricted()becauseisTransferAllowed(address(0), alice, amount)is false.
Was this helpful?