pragma solidity 0.8.13; import "hardhat/console.sol"; contract AtomicityGuard { // address => block => status uint256 private constant _ENTERED = 1; mapping(address => mapping( uint256 => uint256)) ledger; modifier nonAtomic() { _nonAtomicBefore(); _; } function _nonAtomicBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(ledger[msg.sender][block.number] != _ENTERED, "AtomicityGuard: same block"); ledger[msg.sender][block.number] = _ENTERED; } } contract BufferGuard is AtomicityGuard { // address => block => status uint256 private blockBuffer; uint256 private constant _ENTERED = 1; mapping(address => uint256) lastSeen; constructor(uint256 _buff) { blockBuffer = _buff; } modifier buffered() { console.log(lastSeen[msg.sender]); console.log(block.number); _buffered(); _; } function _buffered() private nonAtomic { // On the first call to nonReentrant, _status will be _NOT_ENTERED if(lastSeen[msg.sender] > 0) { require(block.number > lastSeen[msg.sender] + blockBuffer, "BufferGuard: not buffered"); } lastSeen[msg.sender] = block.number; } } contract ImplementorBuffered is BufferGuard { constructor() BufferGuard(10) {} function deposit() external buffered { } function withdraw() external buffered { } } contract Implementor is AtomicityGuard { function deposit() external nonAtomic { } function withdraw() external nonAtomic { } } interface IImplementor { function deposit() external; function withdraw() external; } contract Exploiter { IImplementor c; constructor (address _i) { c = IImplementor(_i); } function atomic() external { c.deposit(); c.withdraw(); } function nonatomicDeposit() external { c.deposit(); } function nonatomicWithdraw() external { c.withdraw(); } } contract Exploiter2 { constructor (address _i) { IImplementor(_i).deposit(); IImplementor(_i).withdraw(); selfdestruct(payable(msg.sender)); } }