Abstract checklists are easy to nod along to and easy to forget. A worked example sticks. So here's one: we took a real, open-source escrow contract — a small project building smart escrow for AI-agent payments, live on a testnet — and read it line by line, then asked what our $2 static pre-check would do to it. This is the honest, unvarnished version: what the contract gets right, what the rules flag, and what no pattern list can catch.
It's an escrow for agent-to-agent transactions. A buyer locks USDC in a contract, naming a seller and a timeout. The seller claims the payment after delivering the service. If the seller never claims and the timeout elapses, the buyer can refund themselves. It's Solidity 0.8.24, uses OpenZeppelin, and — relevant for this post — it's exactly the class of code that goes live before anyone pays for an audit.
Most of the "scary" Solidity patterns are already handled correctly. A static checker that only screams about reentrancy would come back empty here, and that's the right result:
ReentrancyGuard and every state-mutating function (createPolicy, claimPolicy, refundPolicy) is marked nonReentrant.SafeERC20 (safeTransferFrom, safeTransfer), so a token that returns false instead of reverting won't silently pass.claimPolicy and refundPolicy both verify the policy exists, is still Active, and that the caller is the right party, before touching the status flag.If you're a solo builder and you've already reached for ReentrancyGuard and SafeERC20, you're ahead of a meaningful fraction of deployed code. A pre-check that ignores that and just lists "you're not using guards" would be wrong.
This is where a deterministic pre-check earns its two dollars. Three things surface, and none of them is a "gotcha" you'd need a formal audit to find — they're the kind of thing a rule list catches in seconds:
Look at the check:
require(timeout > 0, "Timeout must be positive");
That's the only constraint. A buyer can set timeout to a year, or ten years, or a value so large the funds are effectively locked forever. There's no < MAX_TIMEOUT. The refund path is:
block.timestamp >= policy.createdAt + policy.timeout
In Solidity 0.8.x that addition reverts on overflow (built-in checked arithmetic), so the worst case isn't a wrap-around exploit — it's a liveness failure: a typo'd or over-optimistic timeout locks the buyer's USDC indefinitely with no way out. A static rule for "unbounded configuration parameter" flags this immediately. It's a usability-and-liveness issue, not a theft vector, and it's the difference between "this works" and "this works until someone fat-fingers a constant."
claimPolicy requires only that the caller is the named seller and the policy is active:
require(msg.sender == policy.seller, "Only seller can claim"); policy.status = PolicyStatus.Claimed; usdc.safeTransfer(policy.seller, policy.amount);
There's no window, no buyer consent, no delivery evidence. The seller can call this one second after the policy is created and pull the full amount. The refund path only fires when the seller doesn't claim — so if the seller claims and runs, the buyer has no recourse at all. For an escrow whose entire job is "protect the buyer from a failed transaction," this is the central trust assumption left implicit. A pre-check flags the missing mutual-consent / dispute-window pattern; fixing it is a design decision, not a one-line patch.
The project describes itself as an insurance protocol, but there's no premium pool and no reserve fund anywhere in the contract — no premium, no reserve, no claim-against-a-pool path. It's a two-party escrow. That's a legitimate and useful primitive, but the "refund" only protects the buyer in the one case where the seller doesn't claim — which is precisely not the failure mode the project says it's insuring against ("up to 30% of agent transactions fail"). Naming a plain escrow "insurance" overstates what it covers, and a pre-deploy pass is a cheap moment to catch that mismatch between the README and the bytecode.
Being honest about the boundary is the point. A static pre-check is not a substitute for a formal audit, and here's the concrete line:
That's why the deeper pass exists as a separate tier: the $20 AI audit reads for the business-logic and cross-contract flaws — uninitialized owners, account confusion, missing signer auth, and the economic reasoning a pattern list can't do.
This particular contract is a good one to study because it's representative: a solo builder doing most things right, shipping to testnet, one step from mainnet, and carrying a couple of assumptions that a rule list surfaces in seconds. If you've got a contract in that position, the pre-check is the cheapest way to see your own blind spots named.
The endpoints are live and pay-per-call — no API key, no signup, the 402 challenge carries the price and you pay in USDC over x402:
tx.origin auth, delegatecall, selfdestruct, timestamp dependence, assert misuse, and unbounded parameters. Same code, same result every run.See the case where the pre-check caught a reentrancy before deploy: the reentrancy case study, or the full landing page at minia2a.uk/smart-contract-audit.