// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract HeartbeatPruneRegistry { error AlreadyRegistered(); error NotRegistered(); error Expired(); error NotExpired(); error ZeroPledge(); error Reentrant(); error GracePeriodZero(); error EthSendFailed(); struct Entry { uint64 lastSeen; uint128 pledge; } mapping(address => Entry) public entries; uint256 public immutable GRACE_PERIOD; uint256 private _locked = 1; event Registered(address indexed agent, uint256 pledge); event Heartbeat(address indexed agent, uint256 timestamp); event Unregistered(address indexed agent, uint256 refund); event Pruned(address indexed agent, address indexed keeper, uint256 bounty); modifier nonReentrant() { if (_locked != 1) revert Reentrant(); _locked = 2; _; _locked = 1; } constructor(uint256 gracePeriod) { if (gracePeriod == 0) revert GracePeriodZero(); GRACE_PERIOD = gracePeriod; } function register() external payable { if (entries[msg.sender].lastSeen != 0) revert AlreadyRegistered(); if (msg.value == 0) revert ZeroPledge(); entries[msg.sender] = Entry({ lastSeen: uint64(block.timestamp), pledge: uint128(msg.value) }); emit Registered(msg.sender, msg.value); } function heartbeat() external { Entry storage e = entries[msg.sender]; if (e.lastSeen == 0) revert NotRegistered(); if (block.timestamp >= uint256(e.lastSeen) + GRACE_PERIOD) revert Expired(); e.lastSeen = uint64(block.timestamp); emit Heartbeat(msg.sender, block.timestamp); } function unregister() external nonReentrant { Entry storage e = entries[msg.sender]; if (e.lastSeen == 0) revert NotRegistered(); if (block.timestamp >= uint256(e.lastSeen) + GRACE_PERIOD) revert Expired(); uint128 pledge = e.pledge; delete entries[msg.sender]; _sendETH(msg.sender, pledge); emit Unregistered(msg.sender, pledge); } function prune(address agent) external nonReentrant { Entry storage e = entries[agent]; if (e.lastSeen == 0) revert NotRegistered(); if (block.timestamp < uint256(e.lastSeen) + GRACE_PERIOD) revert NotExpired(); uint128 bounty = e.pledge; delete entries[agent]; _sendETH(msg.sender, bounty); emit Pruned(agent, msg.sender, bounty); } function _sendETH(address to, uint256 amount) private { (bool ok, ) = to.call{value: amount}(""); if (!ok) revert EthSendFailed(); } }