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

Dust Funds Locked in Contract

The Dust Funds Locked in Contract vulnerability arises due to the way the withdrawInheritedFunds function handles fund distribution. Here's a detailed explanation:

Root Cause: Integer Division Truncation

The contract uses integer division to split funds equally among beneficiaries. For example:

uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
  • If the total funds (ethAmountAvailable) are not perfectly divisible by the number of beneficiaries (divisor), the division truncates the remainder.

  • Example:

    • Total ETH: 101 wei

    • Beneficiaries: 3

    • Each receives: 101 / 3 = 33 wei (truncated)

    • Remainder: 101 % 3 = 2 wei (locked forever).


** Impact of Locked Dust**

a. Permanent Fund Loss

  • The leftover "dust" (remainder) remains in the contract indefinitely.

  • No mechanism exists to recover these funds, making them permanently inaccessible.

b. Accumulation Over Time

  • Repeated withdrawals with small remainders can accumulate, leading to non-trivial losses.

  • Example: 10 withdrawals with 1 wei dust each → 10 wei lost (≈ $0.02 at ETH price of$2,000).

c. State Bloat

  • Ethereum nodes must track contracts with non-zero balances, contributing to network state bloat.

d. User Trust Issues

  • Users may perceive the contract as flawed if funds are "lost" due to truncation, even if the amounts are small.


Code Analysis

Flawed Withdrawal Logic

function withdrawInheritedFunds(address _asset) external {
// ...
uint256 divisor = beneficiaries.length;
uint256 amountPerBeneficiary = ethAmountAvailable / divisor; // Truncates remainder
for (uint256 i = 0; i < divisor; i++) {
// Send `amountPerBeneficiary` to each beneficiary
}
}
  • Problem: The remainder (ethAmountAvailable % divisor) is never distributed.


** Attack Scenario**

  1. Contract Balance: 101 wei

  2. Beneficiaries: [Alice, Bob]

  3. Withdrawal:

    • 101 / 2 = 50 wei per beneficiary (truncated).

    • Total sent: 100 wei.

    • Locked Dust: 1 wei (stuck forever).


** Mitigation Strategies**

a. Distribute Remainder to Last Beneficiary

Modify the loop to add the remainder to the last beneficiary’s share:

uint256 remainder = ethAmountAvailable % divisor;
for (uint256 i = 0; i < divisor; i++) {
uint256 amount = amountPerBeneficiary;
if (i == divisor - 1) {
amount += remainder; // Add remainder to last beneficiary
}
// Send `amount` to beneficiary[i]
}

b. Use SafeMath for Precision

For ERC20 tokens, use fixed-point arithmetic (e.g., PRBMath) to avoid truncation.

c. Dust Recovery Function

Add a function to let the owner/beneficiaries claim residual dust:

function recoverDust(address _asset) external {
require(isInherited, "Not inherited");
if (_asset == address(0)) {
payable(msg.sender).transfer(address(this).balance);
} else {
IERC20(_asset).safeTransfer(msg.sender, IERC20(_asset).balanceOf(address(this)));
}
}


** Conclusion**

The Dust Funds Locked in Contract issue is a design flaw that leads to permanent loss of residual funds. While individual losses are small, the cumulative effect and impact on user trust justify addressing it through remainder distribution or recovery mechanisms.

Updates

Lead Judging Commences

0xtimefliez Lead Judge 9 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.

Give us feedback!