Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

`InheritanceManager.sol::withdrawInheritedFunds()` rounding error in calculating amount per beneficiary receives

Summary

amountPerBeneficiary in InheritanceManager.sol::withdrawInheritedFunds()may have rounding error in certain scenarios when calculating how much ETH or ERC20 token each beneficiary should receive. This is more impactful if the underlying currency has smaller decimals.

Vulnerability Details

amountPerBeneficiary divides the balance available in the contract by the number of beneficiaries. This could result in rounding error and funds to beneficiaries to not receive correct amount.

function withdrawInheritedFunds(address _asset) external {
if (!isInherited) {
revert NotYetInherited();
}
uint256 divisor = beneficiaries.length;
if (_asset == address(0)) {
uint256 ethAmountAvailable = address(this).balance;
@> uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
for (uint256 i = 0; i < divisor; i++) {
address payable beneficiary = payable(beneficiaries[i]);
(bool success,) = beneficiary.call{value: amountPerBeneficiary}("");
require(success, "something went wrong");
}
} else {
uint256 assetAmountAvailable = IERC20(_asset).balanceOf(address(this));
@> uint256 amountPerBeneficiary = assetAmountAvailable / divisor;
for (uint256 i = 0; i < divisor; i++) {
IERC20(_asset).safeTransfer(beneficiaries[i], amountPerBeneficiary);
}
}
}

Impact

function test_withdrawInheritedFundsRoundingError() public {
// Give the IM 10 ETH and 10 USDC
usdc.mint(address(im), 10e6);
vm.deal(address(im), 10e18);
console.log("IM initial ETH balance:", address(im).balance);
console.log("IM initial USDC balance:", usdc.balanceOf(address(im)));
// Create 2 more beneficiaries
address user2 = makeAddr("user2");
address user3 = makeAddr("user3");
// Add all 3 users as beneficiaries
vm.startPrank(owner);
im.addBeneficiery(user1);
im.addBeneficiery(user2);
im.addBeneficiery(user3);
vm.stopPrank();
// Fast forward 90days and start inherit
vm.warp(90 days + 1);
vm.startPrank(user1);
im.inherit();
// Withdraw Ether
im.withdrawInheritedFunds(address(0));
console.log("user1.ETHbalance:", user1.balance);
console.log("user2.ETHbalance:", user2.balance);
console.log("user3.ETHbalance:", user3.balance);
console.log("IM remaining ETH balance:", address(im).balance);
// Withdraw USDC
im.withdrawInheritedFunds(address(usdc));
vm.stopPrank();
console.log("user1.USDCbalance:", usdc.balanceOf(user1));
console.log("user2.USDCbalance:", usdc.balanceOf(user2));
console.log("user3.USDCbalance:", usdc.balanceOf(user3));
console.log("IM remaining USDC balance:", usdc.balanceOf(address(im)));
}

Results

[PASS] test_withdrawInheritedFundsRoundingError() (gas: 404882)
Logs:
IM initial ETH balance: 10000000000000000000
IM initial USDC balance: 10000000
user1.ETHbalance: 3333333333333333333
user2.ETHbalance: 3333333333333333333
user3.ETHbalance: 3333333333333333333
IM remaining ETH balance: 1
user1.USDCbalance: 3333333
user2.USDCbalance: 3333333
user3.USDCbalance: 3333333
IM remaining USDC balance: 1

Tools Used

Manual review

Recommendations

If the underlying ERC20 token has a small decimal, can perform multiplying to increase precision in handling fractional values more effectively.

Updates

Lead Judging Commences

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

truncation of integers

Support

FAQs

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