DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Valid

Large Precision Loss

Summary

Large Precision Loss

Vulnerability Details

The claimDittoMatchedReward function have uint256 protocolTime variable at line 162 in which LibOrders.getOffsetTime() is divided with 1 days LibOrders.getOffsetTime() / 1 days , after which the result is substracted in next line in uint256 elapsedTime.

The resultant is then added and multiplied in line 164 in uint256 totalReward.

function claimDittoMatchedReward(uint256 vault) external nonReentrant {
STypes.Vault storage Vault = s.vault[vault];
STypes.VaultUser storage VaultUser = s.vaultUser[vault][msg.sender];
// User's shares total
uint88 shares = VaultUser.dittoMatchedShares;
// Implicitly checks for a valid vault
if (shares <= 1) revert Errors.NoShares();
// Decrease by 1 wei to account for 1 wei gas saving technique
shares -= 1;
// Total token reward amount for limit orders
uint256 protocolTime = LibOrders.getOffsetTime() / 1 days;
uint256 elapsedTime = protocolTime - Vault.dittoMatchedTime;
uint256 totalReward =
Vault.dittoMatchedReward + elapsedTime * 1 days * Vault.dittoMatchedRate;
// User's proportion of the total token reward
uint256 sharesTotal = Vault.dittoMatchedShares;
uint256 userReward = shares.mul(totalReward).div(sharesTotal);
// Only update dittoMatchedTime when totalReward increases
if (elapsedTime > 0) {
Vault.dittoMatchedTime = uint16(protocolTime); // @dev(safe-cast)
}
// Update remaining records
Vault.dittoMatchedShares -= shares;
if ((totalReward - userReward) > type(uint96).max) revert Errors.InvalidAmount();
Vault.dittoMatchedReward = uint96(totalReward - userReward);
VaultUser.dittoMatchedShares = 1; // keep as non-zero to save gas
if (userReward > type(uint80).max) revert Errors.InvalidAmount();
VaultUser.dittoReward += uint80(userReward);
emit Events.ClaimDittoMatchedReward(vault, msg.sender);
}

Impact

Carrying out arthimetic operation in function claimDittoMatchedReward based on division of values, then subtracting it and after that multiplying the resultant cause large precision error or even loss of rewards for users.

Tools Used

Manual Code Review

Recommendations

The recommendation is made in area of first conducting multiplication of all values and carries out all other division arithmetic operation.

function claimDittoMatchedReward(uint256 vault) external nonReentrant {
STypes.Vault storage Vault = s.vault[vault];
STypes.VaultUser storage VaultUser = s.vaultUser[vault][msg.sender];
// User's shares total
uint88 shares = VaultUser.dittoMatchedShares;
// Implicitly checks for a valid vault
if (shares <= 1) revert Errors.NoShares();
// Decrease by 1 wei to account for 1 wei gas saving technique
shares -= 1;
// Total token reward amount for limit orders
- uint256 protocolTime = LibOrders.getOffsetTime() / 1 days;
+ uint256 protocolTime = LibOrders.getOffsetTime() * 1 days;
uint256 elapsedTime = protocolTime - Vault.dittoMatchedTime;
uint256 totalReward =
- Vault.dittoMatchedReward + elapsedTime * 1 days * Vault.dittoMatchedRate;
+ (Vault.dittoMatchedReward + elapsedTime * Vault.dittoMatchedRate) / 1 days;
// User's proportion of the total token reward
uint256 sharesTotal = Vault.dittoMatchedShares;
uint256 userReward = shares.mul(totalReward).div(sharesTotal);
// Only update dittoMatchedTime when totalReward increases
if (elapsedTime > 0) {
Vault.dittoMatchedTime = uint16(protocolTime); // @dev(safe-cast)
}
// Update remaining records
Vault.dittoMatchedShares -= shares;
if ((totalReward - userReward) > type(uint96).max) revert Errors.InvalidAmount();
Vault.dittoMatchedReward = uint96(totalReward - userReward);
VaultUser.dittoMatchedShares = 1; // keep as non-zero to save gas
if (userReward > type(uint80).max) revert Errors.InvalidAmount();
VaultUser.dittoReward += uint80(userReward);
emit Events.ClaimDittoMatchedReward(vault, msg.sender);
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-139

Support

FAQs

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