Summary
InheritanceManager::buyOutEstateNFT() loops through the beneficiaries array and sends each user their allocated funds. When it reaches msg.sender, it performs an incorrect return statement, preventing any funds being transferred to users after that index. The vulnerable for loop in buyOutEstateNFT():
for (uint256 i = 0; i < beneficiaries.length; i++) {
if (msg.sender == beneficiaries[i]) {
return;
} else {
IERC20(assetToPay).safeTransfer(beneficiaries[i], finalAmount / divisor);
}
}
Impact
Incorrect fund allocation during on-chain NFT settlement, breaking a contract invariant.
Proof of Concept
Copy the following into InheritanceManager.t.sol and run the test:
function test_buyOutEstateNFTIndexBug() public {
vm.startPrank(owner);
im.addBeneficiery(alice);
im.addBeneficiery(bob);
im.addBeneficiery(charlie);
im.createEstateNFT("NFT1", 9e6, address(usdc));
vm.stopPrank();
vm.warp(1 + 90 days);
usdc.mint(alice, 6e6);
vm.startPrank(alice);
usdc.approve(address(im), 6e6);
im.inherit();
im.buyOutEstateNFT(1);
vm.stopPrank();
assertEq(0, usdc.balanceOf(bob));
assertEq(0, usdc.balanceOf(charlie));
assertEq(6e6, usdc.balanceOf(address(im)));
}
Expected result:
forge test --mt test_buyOutEstateNFTIndexBug -vvv
[⠢] Compiling...
[⠢] Compiling 1 files with Solc 0.8.26
[⠆] Solc 0.8.26 finished in 3.95s
Compiler run successful!
Ran 1 test for test/ProofOfConcept.t.sol:InheritanceManagerTest
[PASS] test_buyOutEstateNFTIndexBug() (gas: 379547)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.36ms (6.35ms CPU time)
Ran 1 test suite in 467.67ms (14.36ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
Recommendation
Remove the return statement. Use continue instead, or leave the if block empty. Alternatively, you can use if (beneficiaries[i] != msg.sender) { //code } and get rid of the else block.