Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Scaled Allowance Mismatch Enables Over-Approval Exploit

Summary

The transferFrom function in RToken calculates a scaled transfer amount before checking the user’s allowance. This discrepancy allows spenders to exceed the user’s intended approval, resulting in unauthorized transfers of more tokens than permitted.

Vulnerability Details

The RToken contract uses a liquidity index to scale down transfer amounts before calling super.transferFrom. This design breaks the standard ERC20 allowance, which expects approve(spender, X) to limit the spender to exactly X tokens.

In RToken, a higher liquidity index reduces the amount checked against the allowance, letting attackers pass a large “underlying” amount and only check a smaller scaled value. The protocol’s logic permits a spender to transfer more real tokens than the user explicitly approved. This exploit relies on the liquidity index growing over time, rendering the mismatch increasingly significant.

Attackers supply a normal transfer amount that looks safe to the user but bypasses the intended allowance check because of the scaling step.

function transferFrom(address sender, address recipient, uint256 amount)
public
override(ERC20, IERC20)
returns (bool)
{
/*
* @audit: the user’s ‘amount’ is in “underlying” tokens, but here it’s scaled down
* by _liquidityIndex before checking allowance. As the liquidity index
* increases, only a smaller scaledAmount is used for the allowance check,
* allowing spenders to exceed the user’s approved limit in real tokens.
*/
uint256 scaledAmount = amount.rayDiv(_liquidityIndex);
return super.transferFrom(sender, recipient, scaledAmount);
}

Impact

Attackers can siphon tokens by carefully selecting an amount that appears within the “scaled” allowance, but which corresponds to a larger actual balance. The exploit is straightforward as soon as the allowance is set and the liquidity index differs substantially from 1-to-1 scaling. I've rated this as a high because it enables direct theft of user funds, and the likelihood is high because this mismatch appears any time transferFrom is used with a non-trivial liquidity index.

  1. Setup: Alice sets approve(spender, 100) believing it limits the spender to 100 RToken in normal units.

  2. Growing Liquidity Index: Over time, _liquidityIndex increases to 2x.

  3. Exploit: The attacker calls transferFrom(Alice, attacker, 100), but transferFrom internally scales 100 down to 50 before checking the allowance.

    1. If Alice only approved “100 tokens,” the actual check is only for “50 scaled tokens,” letting the attacker move 200 real tokens in multiple calls.

Tools Used

Manual Review

Recommendations

Align the allowance mechanism with scaled amounts or convert approve and transferFrom to share the same reference (scaled vs. underlying). One fix is ensuring the user’s approved amount is also scaled at approval time, so no mismatch occurs:

// Convert allowance from normal to scaled in `approve` or `transferFrom`
function transferFrom(address sender, address recipient, uint256 amount)
public
override
returns (bool)
{
// Convert 'amount' into scaled terms for allowance checking
uint256 scaledAmount = _toScaled(amount);
_checkAllowance(sender, msg.sender, scaledAmount);
// Then scale it back for the actual token move if needed
_transferScaled(sender, recipient, scaledAmount);
return true;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken approval mechanism unit mismatch: approve() uses scaled units while transferFrom() uses underlying units, allowing attackers to transfer more tokens than users intended to approve

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken approval mechanism unit mismatch: approve() uses scaled units while transferFrom() uses underlying units, allowing attackers to transfer more tokens than users intended to approve

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.