detent
Ecosystem/02 of 08

The On-chain Lock

Why it is in the ecosystem. The second lock. The chain refuses any transaction without a valid signed receipt for exactly that calldata.

receipt binding REALvalidator DEMOwho pays: nobody separately; the lock is included in the Enforce subscription and is the guarantee that subscription sells

What it is

DEMO
DetentLock.sol · ERC-7579 validator · validateUserOp, in five lines
  1. 1
    bytes32 want = keccak256(op.callData);
    recompute the hash over the exact calldata the account is about to execute
  2. 2
    require(r.actionHash == want, "no receipt for this calldata");
    the receipt must name this calldata, byte for byte. one byte off, revert.
  3. 3
    require(r.verdict == ALLOW && ecrecover(digest(r), r.sig) == engineKey, "not our engine");
    the verdict is ALLOW and the signature is the engine's, over an EIP-712 digest of the receipt fields
  4. 4
    require(!used[r.nonce] && r.expiresAt > block.timestamp, "stale");
    one receipt, one execution, inside its window
  5. 5
    used[r.nonce] = true; return _validateOwner(ownerSig, userOpHash);
    only then does the owner's own signature get checked. else: revert.

Line 2 is the whole product. The checker can be wrong, bribed or offline; the chain still refuses anything without a receipt for exactly these bytes. Two locks, not one.

Try it in this browser

calldata and hashes REAL verdict REAL
the transfer the receipt binds to
calldata · transfer(address,uint256)
0xa9059cbb0000000000000000000000007a1f4e0c2d9b8a7f6e5d4c3b2a1908f7e6d5c4b30000000000000000000000000000000000000000000000000000000007270e00
keccak256(calldata) · what the receipt binds
0x5757cb78802e614c4356c836559e19ca320d11a916bbf4e347327d4a933af348
flip one byte of calldata
receipt.actionHash (bound at verdict time)
0x5757cb78802e614c4356c836559e19ca320d11a916bbf4e347327d4a933af348
keccak256(calldata the account would execute)
0x5757cb78802e614c4356c836559e19ca320d11a916bbf4e347327d4a933af348
hashes match. the receipt fits this calldata and line 2 passes.

Fire on chain · valid and invalid, side by side

with a receipt · expected: success
DEMOcoming with the on-chain lock

The transfer above, submitted through the smart account with the ALLOW receipt in the signature field. The validator passes and the transfer executes. Basescan link shown here.

The ERC-7579 validator ships in build stage 4 (EntryPoint v0.7 on Base Sepolia). Until it is deployed this beat stays a designed placeholder rather than a staged revert, because a fake Basescan link would be worse than none.

without a receipt · expected: revert
DEMOcoming with the on-chain lock

The identical transfer, submitted with an empty receipt. The validator reverts on line 2 and the revert, with its reason string, is shown here with its Basescan link.

The ERC-7579 validator ships in build stage 4 (EntryPoint v0.7 on Base Sepolia). Until it is deployed this beat stays a designed placeholder rather than a staged revert, because a fake Basescan link would be worse than none.

the aha: One byte of calldata changed, and the same receipt stopped working.