live on hedera:testnetETHGlobal ETHOnline 2026 · Hedera — AI & Agentic Payments

Retainer

Your agent's access renews itself on-chain at 3am, with nobody awake.

x402Blocky402Hedera Schedule ServiceHIP-1215SolidityNext.jsv1.2.0
00:24 00:03 00:00 00:00 60:00 until it closes
retainer.edycu.devv1.2.0
what the ledger records
1signatureper subscription · the only thing the agent ever signs
37unattended renewalsby the network · no user · no server · no cron
one tick per CONTRACTCALL · scheduled=true · SUCCESS · mirror node · 2026-09-09
0.0.104060833
0.0.104141677
0.0.1041584527live

Each one a transaction no account submitted. The amber one reverted — disclosed, slide 10.

retainer.edycu.devv1.2.0
the problem

Someone is always awake.

A research agent meters the live HBAR/USD feed. At 03:00 its window ends.

x402 lets an agent pay for a call — not subscribe. Every renewal needs someone awake.

A human
clicks. At 3am.
A cron job
somebody keeps alive.
00:09until it closes
402
Payment Required
retainer.edycu.devv1.2.0
retainer.edycu.dev
The live view's instrument at the moment a renewal fires: the ring closed mint, the readout 01:00, the label renewed by the network, the caption Nothing was paid. Nothing was signed., and the receipt chips +4 ℏ subscribe and 1.54896 ℏ renewed.
the solution

The network renews it.

The access window is an on-chain subscription the Hedera Schedule Service extends by itself. The agent signs once. Nothing signs again.

one of the 27 on the live contract · HashScan
1788844334.069565823
retainer.edycu.devv1.2.0
how it works

Two rails. One join.

PAYMENT RAIL — ONCE, AT THE START Agent signs once Resource server Next.js · reads hasAccess(agent) Blocky402 facilitator co-signs as fee payer Hedera testnet TransferTransaction ① GET /api/retainer/access ② 402 · x402 exact · HBAR ③ sign · ④ retry with PAYMENT-SIGNATURE ⑥ 200 ⑤ settle THE JOIN ⑦ — THE SERVER FORWARDS THE SETTLED PAYMENT RetainerAccess.subscribeFor(agent){ value } RENEWAL RAIL — UNATTENDED Every later request one chain read: hasAccess(agent) → 200 · paidThisRequest: false — also after the window expired RetainerAccess.sol charge period 1 · set expiresAt hasScheduleCapacity(expiresAt, gas) scheduleCall(this, expiresAt, gas, renew(agent)) Hedera Schedule Service HIP-1215 · system contract 0x16b at expiresAt, with no caller: CONTRACTCALL · scheduled=true · SUCCESS payer = the contract itself arms fires renew(agent): charge · extend · refill meter · re-arm
retainer.edycu.devv1.2.0
the recorded run · unedited

Step 5 is the claim.

BASE_URL=https://retainer.edycu.dev yarn tsx scripts/retainer-agent.ts
1) cold request
  HTTP 402

2) paying via x402 — settled by Blocky402
  ✅ settled · tx 0.0.7162784@1788830067.404863715

3) server forwards the payment → subscribeFor
  subscribed · tx 0x97b7…b3cc

4) same request again
  warm request: HTTP 200  paidThisRequest=false
     window 116s remaining · renewal scheduled at 0x…9eE9d4

5) waiting 165s past expiry — sending NOTHING
  post-expiry request: HTTP 200  paidThisRequest=false
     window 70s remaining · renewal scheduled at 0x…9eE9E5
1Cold agent → 402.a real x402 challenge
2One signature.Blocky402 settles it on Hedera
3Payment → subscribeFor.the join: settled HBAR becomes a subscription
4Same request → 200. Nothing paid.
5165 s after expiry → still 200.the schedule address changed: the next renewal is already armed
docs/proof.md · unedited · recorded 2026-09-08 01:14 UTC against 0.0.10414167, the deployment current at that moment
retainer.edycu.devv1.2.0
hedera schedule service · HIP-1215 · 0x16b

Three methods, all load‑bearing.

scheduleCall(this, expiresAt, gas, renew(agent))
Arms the next renewal. This is the product. Without it: a cron job.
hasScheduleCapacity(expiresAt, gas)
Asked before arming — a full second lapses visibly instead of dying silently.
deleteSchedule(pending)
cancel() releases the booked call; the gas comes back.

Metered, not flat.

Calls are counted on-chain by meter(agent); the renewal refills the meter.

Lapses loudly.

Always a reason, never silence: "balance will not cover the next period"

And stays lapsed.

Nothing re-arms itself: renew() reverts NotSubscribed(), fund() only adds money. Subscribing again is the way back.

retainer.edycu.devv1.2.0
why this, why now

Everyone builds the payment. Almost nobody ships the renewal.

the crowded question · the bounty's own example #1
How does the agent pay per call?
402 → sign → retry.
our question
What happens when access expires at 3am?
Possible since HIP-1215 · system contract 0x16b.
hedera bounty · gates met
  • Live x402 service on Hedera testnetBlocky402-settled
  • Real paid request, end to endtx 0.0.7162784@1788840225.936068496
  • Public repo · README: setup, architecture, payment flow
  • +Extra points: metering · Scheduled Transactions · discoverablemeter(agent) on-chain · 37 unattended renewals · OpenAPI → MCP tools
retainer.edycu.devv1.2.0
measured on hedera testnet · mirror node

Re-arming is 97% of the cost.

renewal that re-arms the nextrenew() + scheduleCall into 0x16b
1.54896 ℏ154,896,000 tinybar
renewal that does not re-armrenew() then Lapsed — no scheduleCall
0.0507 ℏ5,067,825 tinybar · ~30× less

Same function, executed twice by the network. The only difference: one called scheduleCall. Buying the next wake-up is the price. The live deployment repeats it: 1.5404 vs 0.0522 ℏ.

At 1 ℏ a period, Retainer loses money on every renewal. Break-even ≈ 1.55 ℏ a period — one setTerms() call by the seller, no redeploy. Written up in docs/gas-economics.md.

retainer.edycu.devv1.2.0
receipts · re-verifiable against the public mirror node

Verify it on Hedera, not our word.

contract, live
0.0.10415845 · 0x433050c9bd203FBdd49FAB6b5E20eD3E1FB2a931
a renewal the network executed
1788844334.069565823
CONTRACTCALL · scheduled=true · SUCCESS
x402 payment, via Blocky402
0.0.7162784@1788840225.936068496
unattended renewals
37 three deployments · 3 + 7 + 27 · counted 2026-09-09
reverted, disclosed
1 scheduled call · 1788840415.078121802 · Insolvent()
gas, testnet
subscribe() 1,582,554 (limit 2,000,000) · deploy 968,564
tests
64 contract · 239 app · 58 e2e · 202,059 amounts
HSS methods
3 scheduleCall · hasScheduleCapacity · deleteSchedule
hashscan.io/testnet/transaction/1788844334.069565823
HashScan transaction card: type CONTRACT CALL, result SUCCESS, contract id 0.0.10415845, payer account 0.0.7314364, charged fee 1.54036168 HBAR, scheduled True.
the same receipt, from the public mirror node
$ curl -s "https://testnet.mirrornode.hedera.com/api/v1/transactions?timestamp=1788844334.069565823" \
  | jq '.transactions[0] | {scheduled, result, entity_id}'
{ "scheduled": true, "result": "SUCCESS", "entity_id": "0.0.10415845" }

Stated plainly: headline costs are from 0.0.10406083; the live contract repeats them within 0.6%. Its one reverted call hit our own solvency guard — disclosed. Testnet only. Not audited.

retainer.edycu.devv1.2.0
openapi 3.1 · mcp tools · a recipe that cross-checks the ledger

An agent checks the claim.

1
retainer.edycu.dev/openapi.jsonPublished by the product itself. The spec is the interface.
2
Gateway + MCP server, generated from itgetAccess · getStatus · info · externalDocs — paid call 1000 millicents, chain read 0.
3
A second gateway over Hedera’s Mirror NodefindScheduledExecutions. Unpublished — that API is Hedera’s, not ours.
4
Recipe: “Verify self-renewing agent access on Hedera”Reads the vendor’s claim, then asks the ledger whether the scheduler executed it.
the saved run — the vendor asserts, the network confirms
{
  "verification_result": "verified",
  "access_status": true,
  "scheduled_renewals_found": 8,
  "newest_scheduled_call": "1788941916.005290514"
}

Found while building it: /api/v1/contracts/{id}/results never lists a scheduled execution. Renewal 1788940215.030907876 is missing there, and present under /transactions?account.id=… as scheduled: true.

The first recipe read that endpoint, found nothing, and looked right. Audit a HIP-1215 contract by account, not by contract results.

retainer.edycu.devv1.2.0
the ask · 30 seconds · no account, no key

Three commands. Then judge it.

1A cold agent is chargedcurl -i "https://retainer.edycu.dev/api/retainer/access?agent=0x0000000000000000000000000000000000000abc"
2An agent that has paid — read from chain, open or lapsedcurl -s "https://retainer.edycu.dev/api/retainer/status?agent=0xD14CA86A1483e9b2147a7B86fB74D437d3d2Cc66"
3A renewal the network executed, on HashScanhashscan.io/testnet/transaction/1788844334.069565823

Access that keeps itself alive.

Edy Cu · solo builder
github.com/edycutjong · retainer.edycu.dev
retainer.edycu.devv1.2.0
←→01 / 12
ESC overview · P presenter · C contrast · F fullscreen
elapsed
00:00
next