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

Dust Amounts in Fund Distribution

Summary

The withdrawInheritedFunds function in the InheritanceManager contract uses simple division to distribute funds among beneficiaries. This approach creates dust amounts (small remainders) that remain locked in the contract when the total balance is not perfectly divisible by the number of beneficiaries.

Vulnerability Details

When distributing either ETH or ERC20 tokens, the function calculates the amount per beneficiary using simple division:

// For ETH:
uint256 ethAmountAvailable = address(this).balance;
uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
// For ERC20 tokens:
uint256 assetAmountAvailable = IERC20(_asset).balanceOf(address(this));
uint256 amountPerBeneficiary = assetAmountAvailable / divisor;

In both cases, if the total amount is not perfectly divisible by the number of beneficiaries (divisor), the remainder will be truncated due to Solidity's integer division behavior. This remainder stays in the contract with no mechanism to distribute it.

For example, if there are 3 beneficiaries and 100 tokens to distribute, each will get 33 tokens (100 ÷ 3 = 33.33...), leaving 1 token permanently stuck in the contract.

Impact

  1. Locked Funds: Small amounts of assets become permanently trapped in the contract, especially when distributing large amounts among many beneficiaries or with tokens having high decimal precision.

  2. Incomplete Inheritance: The goal of the contract is to fully distribute all assets upon inheritance, but this goal isn't achieved due to the dust amounts.

  3. Accumulation Over Time: If multiple tokens are withdrawn over time, the dust amounts can accumulate to significant values.

  4. Reduced Trust: Beneficiaries might be concerned that some funds are being withheld if they notice discrepancies between total balance and distributed amounts.

The severity is medium because while funds are lost, the impact is typically small relative to the total distributed amount, and the contract functionality still largely achieves its purpose.

Tools Used

Manual code review

Recommendations

  1. Distribute Remainder to Last Beneficiary:
    Modify the distribution logic to give the remainder to the last beneficiary.

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;
uint256 remainder = ethAmountAvailable % divisor;
for (uint256 i = 0; i < divisor; i++) {
address payable beneficiary = payable(beneficiaries[i]);
uint256 payment = amountPerBeneficiary;
// Add remainder to last beneficiary's share
if (i == divisor - 1) {
payment += remainder;
}
(bool success,) = beneficiary.call{value: payment}("");
require(success, "ETH transfer failed");
}
} else {
uint256 assetAmountAvailable = IERC20(_asset).balanceOf(address(this));
uint256 amountPerBeneficiary = assetAmountAvailable / divisor;
uint256 remainder = assetAmountAvailable % divisor;
for (uint256 i = 0; i < divisor; i++) {
uint256 payment = amountPerBeneficiary;
// Add remainder to last beneficiary's share
if (i == divisor - 1) {
payment += remainder;
}
IERC20(_asset).safeTransfer(beneficiaries[i], payment);
}
}
}
  1. Skip Zero Addresses:
    Additionally, to complement the fix for the earlier "gaps in beneficiaries array" issue:

// Skip address(0) beneficiaries when distributing funds
if (beneficiaries[i] != address(0)) {
// Perform transfer logic here
} else {
// Adjust divisor or redistribute the share
}
  1. Add Event Emission:
    Emit events to improve transparency:

event FundsDistributed(address asset, address beneficiary, uint256 amount);

These changes will ensure that all funds are properly distributed without leaving dust amounts in the contract.

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.