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

Incorrect fund distribution in InheritanceManager::buyOutEstateNFT leading to loss of funds for beneficiaries

Summary

The InheritanceManager::buyOutEstateNFT function contains an error where the function prematurely exits when the buyer address is next in the beneficiaries loop. This occurs due to the return statement used inside the loop. As a result, the buyer does not pay the required amount to the remaining beneficiaries that are after the buyer in the array.

Impact

Loss of funds for other beneficiaries.

POC

Place the following test in /test/InheritanceManagerTest.t.sol:

function test_unpaid_nft() external {
address user2 = makeAddr("user2");
address user3 = makeAddr("user3");
vm.warp(1);
vm.startPrank(owner);
im.addBeneficiery(user1); // this is the first beneficiary
im.addBeneficiery(user2);
im.addBeneficiery(user3);
im.createEstateNFT("our beach-house", 3e6, address(usdc));
vm.stopPrank();
vm.warp(1 + 90 days);
uint256 user1InitialBalance = 4e6;
usdc.mint(user1, user1InitialBalance);
// other beneficiaries have 0 usdc before share
assertEq(usdc.balanceOf(user2), 0);
assertEq(usdc.balanceOf(user3), 0);
uint256 expectedShare = im.getNftValue(1) / 2;
uint256 user1FinalBalance = (im.getNftValue(1) / 3) * 2;
vm.startPrank(user1);
usdc.approve(address(im), 4e6);
im.inherit();
im.buyOutEstateNFT(1); // the first in the list buys the NFT
vm.stopPrank();
// user 2 & user 3 are supposed to have `expectedShare` as their balance
assertNotEq(usdc.balanceOf(user2), expectedShare);
assertNotEq(usdc.balanceOf(user3), expectedShare);
// but they have 0 because the loop & function returns prematurely
assertEq(usdc.balanceOf(user2), 0);
assertEq(usdc.balanceOf(user3), 0);
}

Tools Used

  • Manual Review

Recommendations

Use the continue statement instead of the return statement. Or put the transfer in an if block.

Using an if statement:

function buyOutEstateNFT(uint256 _nftID) external onlyBeneficiaryWithIsInherited {
uint256 value = nftValue[_nftID];
uint256 divisor = beneficiaries.length;
uint256 multiplier = beneficiaries.length - 1;
- uint256 finalAmount = (value / divisor) * multiplier;
+ uint256 finalAmount = (value * multiplier) / divisor;
IERC20(assetToPay).safeTransferFrom(msg.sender, address(this), finalAmount);
for (uint256 i = 0; i < beneficiaries.length; i++) {
- if (msg.sender == beneficiaries[i]) {
- return;
- } else {
- IERC20(assetToPay).safeTransfer(beneficiaries[i], finalAmount / divisor);
- }
+ if (msg.sender != beneficiaries[i]) {
+ IERC20(assetToPay).safeTransfer(beneficiaries[i], finalAmount / multiplier);
+ }
}
nft.burnEstate(_nftID);
}
Updates

Lead Judging Commences

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

buyOutNFT has return instead of continue

Support

FAQs

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