Summary
The mint and burn functions in the RToken contract incorrectly return the amount of scaled tokens minted or burned. The functions currently return amountToMint and amount, respectively, which are not scaled.
Vulnerability Details
The mint function in the RToken contract is:
function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
if (amountToMint == 0) {
return (false, 0, 0, 0);
}
uint256 amountScaled = amountToMint.rayDiv(index);
...
_mint(onBehalfOf, amountToMint.toUint128());
emit Mint(caller, onBehalfOf, amountToMint, index);
return (isFirstMint, amountToMint, totalSupply(), amountScaled);
}
The second value in the return statement is amountToMint, which is not scaled. It should return amountScaled instead.
Similarly, the burn function in the RToken contract is:
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
...
uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128();
_burn(from, amount.toUint128());
...
emit Burn(from, receiverOfUnderlying, amount, index);
return (amount, totalSupply(), amount);
}
The first value in the return statement is amount, which is not scaled. It should return amountScaled instead.
Scaling in _update Function
The _update function in the RToken contract scales the amount by the normalized income (liquidity index) for all operations (mint, burn, transfer):
function _update(address from, address to, uint256 amount) internal override {
uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
super._update(from, to, scaledAmount);
}
This ensures that the amount is correctly scaled by the liquidity index before any operation.
Links to the issues:
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L184
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L140
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L309
Impact
This issue can lead to incorrect reporting of the amount of scaled tokens minted or burned, potentially causing issues with token accounting and user balances.
Tools Used
Manual code review.
Recommendations
Update the return statements in the mint and burn functions to correctly return the scaled amount. The corrected functions should be:
Corrected mint Function
function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
if (amountToMint == 0) {
return (false, 0, 0, 0);
}
uint256 amountScaled = amountToMint.rayDiv(index);
...
_mint(onBehalfOf, amountToMint.toUint128());
...
return (isFirstMint, amountScaled, totalSupply(), amountScaled);
}
Corrected burn Function
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
...
_burn(from, amount.toUint128());
...
uint256 amountScaled = amountToMint.rayDiv(index);
return (amountScaled, totalSupply(), amount);
}
This ensures that the correct scaled amount is returned by the mint and burn functions.