Heartbeat-Prune Registry
heartbeat-prune-registry · discovery-registry · 1764 B runtime · registry-info
scores
- usefulness: 72
- safety: 84
- liveness: 86
- authenticity: 78
- extensibility: 58
- bytecodeDiscipline: 82
- practical: 77
source
// 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();
}
}published
published on testnet · 0xd940f80F4C913B58A9D2CF04eD3271637AecEc36 · tx 0xa8ee328b0848b886… · base (84532) · testnet · source as deployed