51712 sc insight yield distribution will revert if global module doesn t implement iyieldrestrictions
Submitted on: Aug 5th 2025 at 08:05:57 UTC by @WinSec for Attackathon | Plume Network
Report ID: #51712
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol
Impacts: Theft of unclaimed yield
Description
Brief/Intro
If the global module set in the router does not implement IYieldRestrictions (or the call fails), the developer attempted to handle that case with a try/catch. However, due to how Solidity handles function selector mismatches, the call can revert before reaching the catch block. As a result, distributeYield can revert entirely, preventing yield distribution for all holders.
Vulnerability Details
distributeYield distributes yield to ArcToken holders while skipping restricted accounts. It relies on _isYieldAllowed at multiple points in distributeYield. Example loop:
for (uint256 i = 0; i < holderCount; i++) {
address holder = $.holders.at(i);
if (_isYieldAllowed(holder)) {
effectiveTotalSupply += balanceOf(holder);
}
}The _isYieldAllowed implementation checks both a specificYieldModule and a globalYieldModule. Focusing on the global module:
address globalYieldModule = IRestrictionsRouter(routerAddr).getGlobalModuleAddress(RestrictionTypes.GLOBAL_SANCTIONS_TYPE);
if (globalYieldModule != address(0)) {
try IYieldRestrictions(globalYieldModule).isYieldAllowed(account) returns (bool globalAllowed) {
allowed = allowed && globalAllowed;
} catch {
// If global module doesn't implement IYieldRestrictions or call fails, treat as restricted?
// Or handle based on specific global module design.
// Current: Assume allowed if call fails/not implemented (less restrictive).
}
}Comments indicate the developer intended to treat the account as allowed if the call fails or is not implemented. However, if the contract set as globalYieldModule does not implement isYieldAllowed, the external call can revert due to a function selector mismatch, and Solidity's try/catch will not catch that — causing the entire distributeYield transaction to revert.
This is analogous to the issue described here: https://github.com/sherlock-audit/2024-04-teller-finance-judging/issues/178
Impact Details
Yield will not be distributed to any holders while the misconfigured global module remains set, causing potential loss/theft of unclaimed yield or denial of yield distribution.
Proof of Concept
Step 3: First call to _isYieldAllowed reaches the global module check
Attempted call:
address globalYieldModule = IRestrictionsRouter(routerAddr).getGlobalModuleAddress(RestrictionTypes.GLOBAL_SANCTIONS_TYPE);
if (globalYieldModule != address(0)) {
try IYieldRestrictions(globalYieldModule).isYieldAllowed(account) returns (bool globalAllowed) {
allowed = allowed && globalAllowed;
} catch {
// Intended fallback if call fails/not implemented
}
}Because wrongContract lacks the isYieldAllowed selector, the call cannot be dispatched.
References
Related issue: https://github.com/sherlock-audit/2024-04-teller-finance-judging/issues/178
Was this helpful?