Search projects, hackathons, sponsors and tech

    ← All projects

    Reckn

    Reckn pays for reproduced work; a settled ENS record opens a Uniswap v4 pool.

    Demo

    About

    Reckn is an escrow for agent-to-agent payments where the arbiter is deterministic re-execution. A buyer funds a deal with a floor; an agent does the work; the fee is released only if replaying that work reproduces the agreed outcome. THERE IS NO KEY THAT CAN DECIDE OTHERWISE — the escrow has no owner, resolver, admin, pause or upgrade, settleWithProof is permissionless, and a build-time check enforces that rather than promising it. ERC-8004 reached Ethereum mainnet in January 2026 and gives agents identity and reputation. A second address can submit feedback without establishing that any work happened. We build the narrower condition here: a record's write right comes from a settlement. A completed job becomes a record under the agent's ENSv2 subname, and the right to write it is an Enhanced Access Control role that the settlement creates and then destroys. The agent cannot write it. Its client can, once, for that one record. Attempting otherwise reverts with EACUnauthorizedAccountRoles. The agent owns its name and holds only ROLE_RENEW on it: the subname is issued without ROLE_SET_RESOLVER, so it cannot repoint its own name away from the resolver that refuses it. See the residue paragraph below for the one thing that still overrides that. Then the record is spent. A Uniswap v4 beforeSwap hook reads it on chain, inside the swap, synchronously and with no CCIP-Read, and refuses a swap from an agent with no settled record. DELIVER, EARN A RECORD, AND THE RECORD IS THE PASS. WHY A POOL, AND WHY v4. A record that only says this agent did the work is a badge, and nobody has to look at it. In front of a pool it is a key: it decides whether you can trade. That is the difference between reputation and access. And the check runs INSIDE the swap, so it cannot be skipped and cannot be delegated -- read the record off-chain and then route the trade, and the party enforcing it is whoever operates the router, which is a party you have to trust in the one place we removed one. v4 is the first version where the pool itself carries that rule. ALL OF THIS IS ON SEPOLIA, NOT ON A FORK. The hook is at 0x68116b8086283E51227c61FD791b6Da1A4230080, its address mined so that its low bits are exactly the beforeSwap flag, on the real PoolManager with liquidity provided. Three transactions, the same sender and the same pool and the same swap, and the only thing that changes between them is whether the record exists: refused, then executed with 1.000000000000000000 in and 0.987158034397061298 out, then refused again once the record was cleared. Both refusals were re-simulated at their own blocks, so we can say WHO refused and WHY rather than that something failed: the PoolManager's ERC-7751 wrapper carries our hook as the target and our own NoSettledRecord as the reason. The proving pipeline ran here too. A fresh Groth16 proof of the re-execution was generated on 2026-09-26 in 416.56 seconds over 15,972,262 constraints, and forty minutes later it settled a deal on chain and paid an agent 250 USDC. Every other settlement in this repository was decided by a proof committed before the event, which is disclosed; this one was not. The job that is proven runs on v3 SwapRouter02, which is the execution we have measured through the zk guest. The pass is spent on v4. Earning and spending are different acts and deliberately use different venues. WHAT IS NOT CLOSED, SAID BEFORE YOU FIND IT. The settlement grants the right to write the record, not its contents: a buyer can write "reproduced" under a job whose proof said "failed". The contents are checkable by anyone against the escrow and the proof; they are not enforced. The hook cannot identify the swapper, because beforeSwap's sender is whoever unlocked the PoolManager rather than the trader, so a second agent could trade behind the first one's record. WE ASKED UNISWAP AND THEY ANSWERED: keep an allowlist of routers and call msgSender() on them. We are declining it, because an allowlist is a party you have to trust and this project exists to take that party out of a payment decision -- our own build check fails on the owner it would need. An ENSv2 setter role is scoped to the key alone and never to a name, which we measured; we pin the adapter to one name and put that name inside the record key, so a write onto a foreign name is unattributable rather than impossible. WE ASKED ENS TOO, AND THE ANSWER IS BETTER THAN OURS: partition names across resolver instances, because the resolver instance is the trust boundary. This deployment is already that in its smallest form, one resolver serving one name. And this is a testnet. THE RESIDUE, WHICH IS THE ONE THAT MATTERS. The account that is the agent also deployed the registry and the resolver, and root roles override every per-key role above. While it holds them, that account CAN write its own record and CAN repoint its own name — we measured both rather than assume either. Root is renounced before this is submitted, and renouncing is irreversible, which is why it is the last thing done and not the first. Do not take our word for whether it happened: the demo page runs those three calls in your browser every time it loads, and says which of them still succeed. Nothing above is a screenshot. The demo page reads Sepolia in your browser as it loads, and it reports the residue that is still open by asking the chain rather than by telling you. Every transaction is in a record generated from the chain, and a script checks in both directions that every link in the repository names a transaction the record holds and that every transaction the record holds is linked somewhere. PRE-EXISTING WORK IS DISCLOSED IN FULL BELOW. In short: this repository began on 2026-07-26; its product history runs to 501cbde on 2026-09-14, and everything dated after that is the pre-event design, measurement, and materials the disclosure describes. It was submitted to ETHOnline 2026, where it did not pass Round 1. The escrow, the guests and the proving pipeline are old. The ENS record surface, the v4 hook, and everything that joins them are the event's work. The full disclosure follows. ============================================================================== PRE-EXISTING WORK DISCLOSURE - reproduced in full, as the rules require ============================================================================== PRE-EXISTING WORK DISCLOSURE — RECKN, ETHGLOBAL TOKYO 2026 Event: ETHGlobal Tokyo 2026, 2026-09-25 -> 09-27, Toranomon Hills Forum. Track: Continuity. Submission type: Top 10 Finalist + Partner prizes. Partner prizes applied for: ENS, Uniswap Foundation. (Not 1inch.) Repository: https://github.com/psyto/reckn — public, and public since 2026-09-04. Prepared: 2026-09-21, from the repository, not from memory. How this document is filed. ETHGlobal's rules say "In all cases, you must disclose any pre-existing work in writing to the ETHGlobal team" and name no channel for doing so. This text is therefore reproduced in full in the submission form's description field, which is where the same rules put the other half of the requirement — "include full details in your submission (repo history, video, and description)". It also lives at this path in the public repository so the history and the disclosure are checkable side by side. AMENDMENT — 2026-09-26 13:00 JST Everything below §4 was written before the event and says "none of this exists". It exists now. The original text is left exactly as filed, because it is the declaration we made in advance and rewriting it into the past tense would destroy the only thing that makes a pre-event declaration worth anything. This section says what landed, and where a judge can check it. §4 said would be built - state - check it 1. a parent ENSv2 registry on Sepolia and agent subnames - done - 0x1Ad360D9..., agent.reckn.eth 2. the record contract — a settlement opens a one-record window and closes it - done - 0xA6966f9f5E72a1841b2d2A22Ec62a23D703202b8 3. a Uniswap v4 beforeSwap hook gating a pool on that record - done - 0x68116b8086283E51227c61FD791b6Da1A4230080, 3 swaps on chain 4. buyer-side binding, settlement wiring, RecknZkEscrow on Sepolia - done - 0x6d6a9deb..., and a proof generated during the event settled a deal 5. the acceptance-criteria test matrix - done - 18 tests against the deployed contracts 6. FEEDBACK.md, the Uniswap feedback form, README line numbers - all three done - FEEDBACK.md in the repository root; the feedback form submitted 2026-09-26 7. live demo, new screenshots, video - done. The demo page reads Sepolia in the reader's browser rather than showing a capture of it - psyto.github.io/reckn Two things in the original are now wrong in our favour and are corrected here, not deleted. - §3.1 cites the pre-event proving measurement of 497.40 s. A fresh proof was generated during the event on 2026-09-26 in 416.56 s, and it settled a deal on Sepolia forty minutes later. The pre-event figure stays disclosed as pre-event; the event figure is event work. - §1 lists scripts/no-keys.sh as complete pre-existing work. It was extended during the event to read the two new contracts, and a negative control was added that plants five dissimilar keys and requires the check to go red for each. The extension is event work; the original is not. Nothing was removed from the tree to make this look better. The commit history from eddac8d (2026-09-25 19:31 JST, recorded before the window opened) is the boundary, and every event commit is small, dated, and carries the AI co-author line. Attribution per file, including what the models got wrong, is in docs/tokyo-2026/AI-USE.md. ETHGlobal's rules require pre-existing work to be disclosed in writing, and name code, designs and assets. This document lists what exists before the event and states what will be written during it. Every count below was taken by running a command against the repository on 2026-09-21, and every one of them is checkable against the public history by anyone, including against us. Dates are not written here as a boundary either, for the same reason. "Everything dated 2026-09-21" was true for one day. The boundary is a commit: 501cbde, 2026-09-14. Everything after it is §3. A total is not written here on purpose. It was, twice, and each time the commit that corrected it moved it again — 310 -> 313 -> 314. A number that its own commit falsifies is worse than no number, so what is stated instead is the boundary: 310 through 501cbde on 2026-09-14, and everything after it is §3. git log answers the rest. 0. THE SHORT VERSION This project is not new. First commit - b8388f4, 2026-07-26 Commits before the event - 310 of product history through 501cbde, 2026-09-14, all public on master. Everything after it is the pre-event design, measurement and documents of §3 — including the commits that carry this file. It has been submitted to another hackathon - ETHOnline 2026 (async, 09-04 -> 09-16) — see §2 Pre-existing implementation - substantial: 26 Solidity files, 136 Rust files tracked Written for Tokyo before the event - design and measurement only — 2 specs, 2 reviews, 10 throwaway spike tests Submission code written for Tokyo - zero lines. Nothing in §4 exists We are not claiming to start from scratch, and the Continuity track is the reason we do not have to. What follows is the boundary, drawn where the rules draw it. 1. WHAT RECKN ALREADY IS (PRE-EXISTING, AND THE BULK OF THE REPOSITORY) An escrow for agent-to-agent payments whose arbiter is deterministic re-execution. A buyer funds a deal; the fee is released only if replaying the work reproduces the agreed outcome. component - state before the event zk-verdict/contracts/src/RecknZkEscrow.sol - complete. No owner / resolver / admin / pause / upgrade; settleWithProof is permissionless; refundAfterDeadline returns funds after 30 days to anyone who calls it, paying the caller nothing zk-verdict/contracts/src/RecknVerdictVerifier.sol - complete. verifyVerdict is public view zk-verdict/program-revm - complete. Real revm run in-guest over a prestate MPT-verified against the block state_root zk-verdict/program-svm - complete. Not used by this submission contracts/ (optimistic path) - complete. Not the submission's path ERC-8004 reputation surface, x402 payment surface - complete scripts/no-keys.sh - complete. A build-time check that the escrow has no key Arc testnet deployment - escrow deployed, 4 real settlements. Pre-existing, and not part of this submission dashboard/media/* (5 images) - ETHOnline/Arc assets. They will not be reused as Tokyo screenshots 2. PRIOR HACKATHON USE — DISCLOSED BECAUSE IT IS MATERIAL The same repository was submitted to ETHOnline 2026 (async, 2026-09-04 -> 09-16) on the Continuity "Ship a Feature" track, with its own written disclosure (docs/ethonline-2026/DISCLOSURE.md). It did not pass Round 1; partner-prize judging there ran to 2026-09-17. 179 commits fall inside that window and all of them are pre-existing work for Tokyo. The project was also prepared for Crypto World's Fair and withdrawn on 2026-09-14 (docs/cwf-2026/RETRACTED-2026-09-14.md). Reckn is not concurrently entered in any other competition during 2026-09-25 -> 09-27. 3. WRITTEN FOR TOKYO BEFORE THE EVENT — DESIGN AND MEASUREMENT ONLY ETHGlobal permits planning, design and research before an event and forbids building the submission. Everything in this section is design or measurement. None of it is submission code. what - when - detail docs/specs/012-uniswap-reexecution-slice.md - 2026-09-08 - design for a Uniswap re-execution slice. Superseded by 013, kept as the fallback docs/specs/013-settlement-granted-record-rights.md - from 2026-09-21 - the submission's design, at revision r4 docs/reviews/013-spec-r1.md, -r2.md - 2026-09-21 - two independent adversarial reviews of that design, each ending in a verdict. Both returned CHANGES; four blockers are recorded with the fixes spikes/tokyo-2026/ - 2026-09-21 - 10 throwaway test files, 1,506 lines. Fork tests against Sepolia and against the repository's own fixtures spikes/tokyo-2026/FINDINGS.md - 2026-09-21 - what the spikes measured, including two places where the design was wrong and was corrected docs/tokyo-2026/DEMO.md, VIDEO-RUNBOOK.md, SPEAKER-CARD.md, SPONSOR-CARDS.md - 2026-09-25, before 21:00 JST - script, capture plan, speaking aid, and in-person question cards. Preparation only: no submission UI, deployment, fixture, screenshot, or video exists in them 3.1 A MEASUREMENT TAKEN BEFORE THE EVENT, WHICH THE SUBMISSION WILL CITE On 2026-09-08, against Ethereum mainnet: a real SwapRouter02.exactInputSingle re-executed inside an unmodified guest — witness of 7 accounts / 12 storage slots / 141 MPT nodes, 13,006,200 cycles, Groth16 end-to-end 497.40 s. This is a pre-event measurement and will be presented as one. It is not event work. 3.2 WHAT THE SPIKES ARE, AND WHAT THEY ARE NOT They are instruments: they use cheatcodes, forked state and deliberately-failing control arms to answer whether a mechanism is possible. They are not the submission and will not be ported into it. Their dependencies (v4-core, contracts-v2, openzeppelin-contracts) are shallow clones under spikes/tokyo-2026/lib/, which is gitignored, as is the build output. They are disclosed in full, including the two findings that say the design was wrong, so that the record shows nothing unfavourable was withheld. 4. WHAT WILL BE WRITTEN DURING THE EVENT (2026-09-25 -> 09-27) None of this exists. It is the "substantive new features developed during the event" the Continuity track requires, and it is the list a judge should be able to check commit by commit. # - to build 1 - A parent ENSv2 registry on Sepolia, and agent subnames issued under it 2 - The record contract: a settlement opens a one-record write window for the deal's buyer, then closes it 3 - A Uniswap v4 beforeSwap hook that gates a pool on that record 4 - Buyer-side deal binding, settlement wiring, and RecknZkEscrow deployed to Sepolia 5 - The acceptance-criteria test matrix 6 - FEEDBACK.md, the Uniswap Developer Feedback Form, and a README section pointing at the contracts and line numbers 7 - A live demo, new screenshots, and the video RecknZkEscrow itself will not be modified. Its function surface is the project's central claim, and the event work sits outside it. 5. HOW THE BOUNDARY IS CHECKABLE RATHER THAN PROMISED - The repository is public and every commit is dated. A pre-event commit cannot later be presented as event work, and the history is as checkable against us as for us. - Event work is defined by the clock, not the calendar day: hacking begins 2026-09-25 at 21:00 JST, and only commits at or after that moment are event work. The 25th is not a boundary — registration opens at 13:00 and the sponsor workshops, dinner and opening ceremony fill the eight hours before hacking starts. Anything committed in that window is pre-event work and is disclosed as such. A commit hash is recorded as EVENT_START before the first of them. - Continuous commits during the event. The rules treat a single large final commit as unqualified by default, and this repository's practice is small commits per task. 6. HONEST NOTES - This project has a large pre-existing implementation, and the rules note that projects relying mostly on pre-existing work do not historically score as high. We are not hiding the ratio: the escrow, the guests and the proving pipeline are old; the ENS record surface, the v4 hook and everything that joins them are new. - The design in 013 has been through two independent review rounds that both returned CHANGES, and the spikes then showed two further places where its reasoning was wrong. Those corrections are in the document rather than removed from it. - docs/ethonline-2026/ describes a different event. It is kept, not deleted, and its claims are not this submission's claims. Contact: @psyto69 (Telegram) · psyto (GitHub) · Discord psyto7835. If any part of this disclosure is insufficient, we would rather be told before the event than judged on it afterwards.

    How it's made

    THE VERDICT. An SP1 zkVM guest runs real revm in-guest over a prestate MPT-verified against the block state_root — account proofs plus storage proofs — so the guest cannot be fed a state that never existed. Measured against Ethereum mainnet on 2026-09-08: a real SwapRouter02.exactInputSingle, witness of 7 accounts / 12 storage slots / 141 MPT nodes, 13,006,200 cycles, Groth16 end-to-end in 497.40 s on a laptop. That measurement predates the event and is disclosed as pre-existing. The pipeline also ran DURING the event: a fresh proof on 2026-09-26 in 416.56 s over 15,972,262 constraints, which then settled a deal on Sepolia and paid an agent 250 USDC forty minutes later. A Groth16 verifier checks the proof on chain and settleWithProof moves the money. The deal is bound to one execution — the token checked, the storage slot read (which is whose balance) and the calldata are all inside the binding, so a real proof of a different swap reverts with BindingMismatch() and nothing moves. DERIVING THE VERDICT WITHOUT BEING TOLD IT. The escrow's Deal has no outcome field, and the outcome exists only in an event, which contracts cannot read — and settlement is permissionless, so a third party may settle first. The record contract therefore re-verifies the proof itself through the verifier the funder named (pinned by codehash), and takes the outcome from verifyVerdict, never from its caller. It also refuses anything outside fundedAt + REFUND_AFTER, because a timeout refund writes the same terminal state as a real settlement and would otherwise be recordable as one. THE ENS SIDE. Each agent is a subname under a parent PermissionedRegistry we deploy, and its job records live on a Permissioned Resolver, which is itself an access-control surface. grantSetterRoles takes the setter's CALLDATA rather than a name, and decodeSetter returns the resource to revoke. We measured what that resource is derived from: the key alone. The same key under agent.reckn.eth and under victim.reckn.eth is one identical resource, so a setter role is never scoped to a single name. We handle it rather than claim otherwise -- the adapter's name is fixed at construction instead of taken from the caller, and the name is written inside the record key, so bytes placed on a foreign name sit under a key that names whose record it is and the canonical lookup for that name does not find them. The write onto a foreign name is not prevented; it is made unattributable. Records resolve through UniversalResolverV2, not by calling our resolver directly. THE UNISWAP SIDE. A beforeSwap-only hook, its address CREATE2-mined so uint160(hook) & ALL_HOOK_MASK == BEFORE_SWAP_FLAG, reads the record inside beforeSwap — synchronously, no CCIP-Read — and reverts NoSettledRecord. The PoolManager wraps that in ERC-7751 WrappedError, so our tests assert the wrapped target and reason, not the outer selector. beforeAddLiquidity is deliberately unflagged: THE GATE IS ON TRADING, NOT ON PROVIDING LIQUIDITY. HACKY AND WORTH MENTIONING. Before writing any of it we spent a day measuring against forked Sepolia, and the design was wrong twice. The deployed ENSv2 beta's ABI is not the main branch's (initialize and setText both differ); grantSetterRoles rejects a name with UnsupportedResolverProfile because its first argument is calldata; the resolver is UUPS, so an EIP-1167 clone dies inside onlyProxy with 210 gas and no revert data; and a name is an ERC-1155, so a contract owner without a receiver hook cannot register one. All of it is in FEEDBACK.md and spikes/tokyo-2026/FINDINGS.md.