Zany Tweed Quail
Medium
The _valueOfUnderlying
function calculates the share amount to be minted to the share recipient; however, the calculations within the function are prone to overflow.
The issue originates in the _valueOfUnderlying
function, which calculates the share amount. However, improper handling of the calculation results in an integer overflow, causing it to reach the maximum value of uint256
. and it affects the addPrincipalToCommitmentGroup
`
function _valueOfUnderlying(uint256 amount, uint256 rate)
internal
pure
returns (uint256 value_)
{
if (rate == 0) {
return 0;
}
value_ = (amount * EXCHANGE_RATE_EXPANSION_FACTOR) / rate; @audit
}`
` function addPrincipalToCommitmentGroup(
uint256 _amount,
address _sharesRecipient
) external returns (uint256 sharesAmount_) {
//transfers the primary principal token from msg.sender into this contract escrow
principalToken.transferFrom(msg.sender, address(this), _amount);
sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); @audit It Revert here
totalPrincipalTokensCommitted += _amount;
//principalTokensCommittedByLender[msg.sender] += _amount;
//mint shares equal to _amount and give them to the shares recipient !!!
poolSharesToken.mint(_sharesRecipient, sharesAmount_);
}`
`
Integer Overflow on the _valueOfUnderlying
function which leads to overflow on the calculation of the share amount and it affects the addPrincipalToCommitmentGroup
function
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Overflow { uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; uint256 public totalSupply = 1e18; uint256 public totalPrincipalTokensCommitted = 100e36; uint256 public totalPrincipalTokensWithdrawn = 10e36;
uint256 public totalInterestCollected = 50e36;
int256 public tokenDifferenceFromLiquidations = 20e36;
function sharesExchangeRate() public view virtual returns (uint256 rate_) {
uint256 poolTotalEstimatedValue = getPoolTotalEstimatedValue();
if (totalSupply == 0) {
return EXCHANGE_RATE_EXPANSION_FACTOR; // 1 to 1 for first swap
}
rate_ = (poolTotalEstimatedValue * EXCHANGE_RATE_EXPANSION_FACTOR) / totalSupply;
}
function sharesExchangeRateInverse() public view virtual returns (uint256 rate_) {
return (EXCHANGE_RATE_EXPANSION_FACTOR * EXCHANGE_RATE_EXPANSION_FACTOR) / sharesExchangeRate();
}
function getPoolTotalEstimatedValue() public view returns (uint256 poolTotalEstimatedValue_) {
int256 poolTotalEstimatedValueSigned = int256(totalPrincipalTokensCommitted) + int256(totalInterestCollected)
+ int256(tokenDifferenceFromLiquidations) - int256(totalPrincipalTokensWithdrawn);
//if the poolTotalEstimatedValue_ is less than 0, we treat it as 0.
poolTotalEstimatedValue_ =
poolTotalEstimatedValueSigned > int256(0) ? uint256(poolTotalEstimatedValueSigned) : 0;
}
// Change the visibility to public for testing purpose
function _valueOfUnderlying(uint256 amount, uint256 rate) public pure returns (uint256 value_) {
if (rate == 0) {
return 0;
}
value_ = (amount * EXCHANGE_RATE_EXPANSION_FACTOR) / rate;
}
} `
// The contract
` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol"; import {Counter} from "../src/Counter.sol";
contract OverFlowTest is Test { Overflow public overflow;
function setUp() public {
overflow = new Overflow();
}
function testFuzz_valueofUnderlying(uint256 amount) public {
uint256 poolTotalEstimatedValue = overflow.getPoolTotalEstimatedValue();
console.log(poolTotalEstimatedValue);
uint256 rate = counter.sharesExchangeRate();
// console.log(rate);
uint256 value = counter._valueOfUnderlying(amount, rate);
console.log(value);
}
}
` `
Recheck the _valueOfUnderlying` function calculation to handle the shares amount calculation properly