Calldata-Metered Payout

calldata-metered-payout · conditional-settlement · 1159 B runtime · funds-movement

scores

source

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract CalldataMeteredPayout {
    bytes1 private constant M0 = 0xba;
    bytes1 private constant M1 = 0xad;
    bytes1 private constant M2 = 0xbe;
    bytes1 private constant M3 = 0xef;

    uint256 private constant MAX_CALLDATA = 2048;

    uint256 public immutable ratePerByte;
    mapping(address => uint256) private _lastClaimBlock;

    event Claimed(address indexed claimer, uint256 calldataLength, uint256 words, uint256 payout);

    constructor(uint256 _ratePerByte) payable {
        ratePerByte = _ratePerByte;
    }

    receive() external payable {}

    fallback() external {
        bytes calldata data = msg.data;

        require(data.length <= MAX_CALLDATA, "calldata too large");
        require(data.length >= 4, "missing magic prefix");
        require(data[0] == M0 && data[1] == M1 && data[2] == M2 && data[3] == M3, "invalid magic prefix");
        require(_lastClaimBlock[msg.sender] != block.number, "already claimed this block");

        _lastClaimBlock[msg.sender] = block.number;

        uint256 words = data.length / 32;
        uint256 payout = words * ratePerByte * 32;
        require(payout <= address(this).balance, "insufficient balance");

        (bool ok, ) = msg.sender.call{value: payout}("");
        require(ok, "transfer failed");

        emit Claimed(msg.sender, data.length, words, payout);
    }
}