Summary
Vulnerability Details
The withdrawInheritedFunds function contains a critical flaw in its asset distribution logic that permanently traps residual funds in the contract. The issue stems from:
uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
for (uint256 i = 0; i < divisor; i++) {
}
POC
Test Case:
Contract holds 101 wei ETH
3 beneficiaries exist
withdrawInheritedFunds() called
Result:
Each beneficiary receives 33 wei (33 × 3 = 99)
2 wei permanently stuck in contract
Key Issue:
Integer division truncates remainders (e.g., 101 wei ÷ 3 beneficiaries = 33 wei each, leaving 2 wei stranded)
No mechanism to handle residual amounts after equal division
Impact
Permanent Fund Loss: Residual amounts remain locked in contract forever
Accumulation Risk: Repeated distributions compound stranded funds
Protocol Leakage: Creates unrecoverable "dust" across all asset types (ETH/ERC20s)
Tools Used
Manual Review
Recommendations
Solution 1: Last Beneficiary Gets Residual
uint256 total = asset.balanceOf(address(this));
uint256 perBeneficiary = total / divisor;
uint256 residual = total - (perBeneficiary * divisor);
for (uint i; i < divisor; i++) {
uint256 amount = perBeneficiary;
if (i == divisor - 1) amount += residual;
}
Solution 2: Weighted Distribution (Advanced)
uint256 totalShares = calculateTotalShares();
for (uint i; i < divisor; i++) {
uint256 share = (beneficiaryShares[i] * total) / totalShares;
}
Solution 3: Full Amount Safety Check
require(
(perBeneficiary * divisor) == total,
"Amount must be exactly divisible"
);