Duplicate entries cause a beneficiary to receive multiple shares. For example, if an address appears twice in an array of three beneficiaries, they would receive 2/3 of the funds instead of the intended 1/3.
pragma solidity 0.8.26;
import "forge-std/Test.sol";
import "../src/InheritanceManager.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockToken is ERC20 {
constructor() ERC20("Mock Token", "MOCK") {
_mint(msg.sender, 1000000 * 10**18);
}
}
contract PoCTest is Test {
InheritanceManager public inheritanceManager;
address public owner;
address public beneficiary1;
address public beneficiary2;
address public beneficiary3;
MockToken public mockToken;
function setUp() public {
inheritanceManager = new InheritanceManager();
mockToken = new MockToken();
owner = inheritanceManager.getOwner();
beneficiary1 = address(0x1);
beneficiary2 = address(0x2);
beneficiary3 = address(0x3);
vm.deal(beneficiary1, 10 ether);
vm.deal(beneficiary2, 10 ether);
vm.deal(beneficiary3, 10 ether);
mockToken.transfer(address(inheritanceManager), 1000 * 10**18);
}
function getBeneficiariesLength() public view returns (uint256) {
bytes32 data = vm.load(address(inheritanceManager), bytes32(uint256(5)));
return uint256(data);
}
function test_BeneficiariesLengthInconsistencies() public {
console.log("----- INITIAL SETUP -----");
vm.startPrank(owner);
inheritanceManager.addBeneficiery(beneficiary1);
console.log("Added beneficiary1 (0x1)");
inheritanceManager.addBeneficiery(beneficiary1);
console.log("Added beneficiary1 (0x1) again - DUPLICATE");
inheritanceManager.addBeneficiery(beneficiary2);
console.log("Added beneficiary2 (0x2)");
inheritanceManager.addBeneficiery(beneficiary3);
console.log("Added beneficiary3 (0x3)");
console.log("Size of beneficiaries array :", getBeneficiariesLength());
vm.stopPrank();
console.log("\n----- VERIFYING DUPLICATE ENTRIES -----");
uint256 firstIndexOfB1 = inheritanceManager._getBeneficiaryIndex(beneficiary1);
console.log("First occurrence of beneficiary1 at index:", firstIndexOfB1);
bool foundDuplicate = false;
uint256 secondIndexOfB1 = 0;
for (uint256 i = firstIndexOfB1 + 1; i < 4; i++) {
inheritanceManager.removeBeneficiary(address(uint160(i)));
uint256 checkIndex = inheritanceManager._getBeneficiaryIndex(beneficiary1);
if (checkIndex != firstIndexOfB1) {
foundDuplicate = true;
secondIndexOfB1 = i;
break;
}
}
assertTrue(foundDuplicate, "...");
console.log("Found duplicate of beneficiary1 at index:", secondIndexOfB1);
console.log("Size of beneficiaries array :", getBeneficiariesLength());
console.log("\n----- DELETING AN ENTRY -----");
console.log("Removing beneficiary3 (0x3)");
inheritanceManager.removeBeneficiary(beneficiary3);
uint256 indexAfterRemoval = inheritanceManager._getBeneficiaryIndex(beneficiary3);
console.log("After removal, _getBeneficiaryIndex for beneficiary3 returns:", indexAfterRemoval);
console.log("Size of beneficiaries array :", getBeneficiariesLength());
}
function test_DuplicateBeneficiaryReceivesMultipleShares() public {
console.log("\n----- FUND DISTRIBUTION TO DUPLICATE BENEFICIARIES -----");
vm.startPrank(owner);
inheritanceManager.addBeneficiery(beneficiary1);
inheritanceManager.addBeneficiery(beneficiary1);
inheritanceManager.addBeneficiery(beneficiary2);
vm.stopPrank();
console.log("Added beneficiary1 twice and beneficiary2 once");
console.log("Size of beneficiaries array:", getBeneficiariesLength());
uint256 initialContractBalance = mockToken.balanceOf(address(inheritanceManager));
uint256 initialBeneficiary1Balance = mockToken.balanceOf(beneficiary1);
uint256 initialBeneficiary2Balance = mockToken.balanceOf(beneficiary2);
console.log("Initial contract token balance:", initialContractBalance);
console.log("Initial beneficiary1 balance:", initialBeneficiary1Balance);
console.log("Initial beneficiary2 balance:", initialBeneficiary2Balance);
console.log("\n----- TRIGGERING INHERITANCE -----");
vm.warp(block.timestamp + 100 days);
vm.prank(beneficiary1);
inheritanceManager.inherit();
console.log("After inherit(), isInherited =", inheritanceManager.getIsInherited());
vm.prank(beneficiary1);
inheritanceManager.withdrawInheritedFunds(address(mockToken));
uint256 finalContractBalance = mockToken.balanceOf(address(inheritanceManager));
uint256 finalBeneficiary1Balance = mockToken.balanceOf(beneficiary1);
uint256 finalBeneficiary2Balance = mockToken.balanceOf(beneficiary2);
console.log("\n----- FINAL BALANCES AFTER DISTRIBUTION -----");
console.log("Final contract token balance:", finalContractBalance);
console.log("Final beneficiary1 balance:", finalBeneficiary1Balance);
console.log("Final beneficiary2 balance:", finalBeneficiary2Balance);
uint256 beneficiary1Received = finalBeneficiary1Balance - initialBeneficiary1Balance;
uint256 beneficiary2Received = finalBeneficiary2Balance - initialBeneficiary2Balance;
console.log("\n----- DISTRIBUTION ANALYSIS -----");
console.log("Beneficiary1 received:", beneficiary1Received);
console.log("Beneficiary2 received:", beneficiary2Received);
assertTrue(beneficiary1Received > beneficiary2Received, "Beneficiary1 should receive more funds due to duplicate entries");
uint256 ratio = beneficiary1Received / beneficiary2Received;
assertEq(ratio, 2, "Distribution ratio should be 2:1 for duplicate vs single beneficiary");
console.log("Distribution ratio (beneficiary1:beneficiary2):", ratio, ":1");
console.log("VULNERABILITY CONFIRMED: Duplicate beneficiaries receive multiple shares!");
}
}
These changes will ensure fair and accurate fund distribution and prevent both duplicate beneficiaries and issues with removed entries.