Status and boundary
The application is ready for a human-controlled acceptance test, subject to a reviewed and verified mainnet router deployment. No mainnet router was deployed in this milestone. Do not use the local Anvil address from the evidence on mainnet.
The real API has been checked without paying. A complete acquisition-to-payment-preparation run succeeded with the actual API and POOL4 state on a local Ethereum fork, starting from zero IMD and stopping at awaiting_payment_signature. This is not evidence of real paid admission. Browser-wallet signing and live settlement remain acceptance steps for the operator.
Only job.open is exposed by checkout. The first flow produces a research-report with three citations, artifacts/report.md as its output and github:false. The user supplies its objective. A free-form objective without an explicit skill/output was rejected by the current input planner during verification; the explicit report input passed. No deploy, escrow, extra token or fee is part of this flow.
Before funding a wallet
- Obtain an independent review of the router and payment integration. Resolve any required findings. Review
SECURITY.mdandSTATIC_ANALYSIS.md; static analysis alone is not an audit. - An authorized human must separately arrange the production deployment. This milestone does not execute it. Verify the deployed source, compiler settings and runtime against the reviewed artifact, including immutables. Run the read-only verification script described in
DEPLOYMENT.md. A getter returning the expected address is not proof of identical code. - Recheck the official IMD API documentation, capabilities, OpenAPI, and POOL4 documentation. Re-run the fork suite at a current block. Stop if configuration or payment rules changed.
- Create a new ordinary Ethereum wallet in your own wallet application. It must have no contract code or EIP-7702 delegation. Never paste its seed or private key into the website, a terminal command, the backend, a configuration file or this chat. Do not use Anvil's public test accounts on mainnet.
- Choose a small ETH spending budget yourself. Fund only the disposable wallet, allowing for the displayed maximum swap amount plus network gas for the swap and exact Permit2 approval. Begin with zero IMD. No fixed funding amount is recommended by the code; the live estimate and network costs determine it.
Configure locally, with payment disabled
In frontend/.env.local:
MAINNET_RPC_URL=YOUR_ETHEREUM_READ_RPC
NEXT_PUBLIC_ROUTER_ADDRESS=YOUR_VERIFIED_MAINNET_ROUTER
NEXT_PUBLIC_ROUTER_CODE_HASH=0xYOUR_REVIEWED_RUNTIME_CODE_HASH
ROUTER_LAUNCH_ENABLED=true
IMD_PAYMENT_SUBMISSION_ENABLED=false
The runtime hash must be obtained from the verified reviewed deployment, not blindly copied from an arbitrary address. script/verify-mainnet.cjs rechecks the external IMD market; it does not approve your router's code hash for you.
Run from frontend:
npm ci
npm run test
npm run build
npm run start
Open http://127.0.0.1:3000/checkout in a browser with your wallet extension. Keep the server on localhost during this controlled test. The public landing page is separate. Payment is blocked on both the screen and server while IMD_PAYMENT_SUBMISSION_ENABLED=false.
First stage: buy IMD, then stop
- Connect the disposable wallet and select Ethereum mainnet. Check its address against the wallet application.
- Enter a harmless objective, for example: “Write a short sourced report comparing deterministic unit tests and Ethereum mainnet-fork tests. Do not deploy contracts or execute transactions.”
- Click Get my price — no payment. This calls discovery, capabilities, check, quote and the unsigned challenge. It does not pay or execute a job. Read any blockers instead of trying to bypass them.
- Inspect the six fields: required IMD, current IMD, missing IMD, estimated ETH, maximum ETH and slippage. With zero IMD, missing must equal required. Prices come from the API, never a constant.
- Open the prepared input and verify the job objective, report skill/output, and
github:false. Check the IMD asset, recipient, quote hash and expiry. These are payment-review terms, not a destination for a manual token transfer. - If the estimate is older than 30 seconds, click Check status / refresh balance. Select your slippage tolerance. Maximum ETH excludes gas.
- Click Pay with ETH — prepare funds only after reviewing the maximum. In the wallet, confirm Ethereum, the verified router address and ETH value. Sign the transaction yourself. The router delivers IMD to this same wallet and refunds unused ETH.
- Wait for two confirmations. Verify the IMD balance is at least the required amount and the screen reaches
imd_acquired. If the receipt or balance is uncertain, use the saved transaction hash and refresh; do not buy again blindly. - Click Review job payment — no charge. It rechecks status, balance, saved input and the live challenge. It must reach
awaiting_payment_signature. Stop here for the safe milestone demonstration. IMD is still in the wallet, with no new Permit2 approval and no signed payment.
Second stage: human-confirmed first paid job
Proceed only when the responsible human explicitly authorizes this real expenditure. This stage is not executed by the agent.
- Keep the same browser tab/session and record the order ID. For recovery, the browser session holds a random request bearer token, request key and later signed bytes. These are sensitive authorization material: do not publish them, screenshots of them or the session-storage JSON. If you need a backup, save that session record privately under your control. It contains no wallet private key.
- Stop the local server. Set
IMD_PAYMENT_SUBMISSION_ENABLED=true, rebuild and restart. This is an operator setting; it never submits a payment itself. Reload the same tab and click Check status / refresh balance before proceeding. - If the unpaid quote expired, create a fresh unpaid quote. Your acquired IMD remains available; a sufficient balance skips buying. Do not create a replacement order while any prior payment submission or swap is unresolved.
- Prepare the challenge again. The SDK derives
deadline = current Unix time + accepts[0].maxTimeoutSeconds. It must remain strictly beforequote.expiresAt. The code refuses a window that cannot fit; it does not useexpiresAt - 5or modify the challenge. A ten-second remaining-time guard is a conservative client safety margin, not a claim about the server's exact minimum. - Review the prepared job, asset, amount, payment recipient and quote hash. Tick the review checkbox. If needed, click Approve exact IMD amount and confirm the ERC-20 approval to the official Permit2 contract in your wallet. This is a separate on-chain transaction. There is no unlimited approval added by this app.
- Click Sign payment authorizations. The connected payer wallet signs both. Inspect the Permit2 token/amount/spender/recipient/deadline and the QuoteApproval resource, requester scope, quote ID/hash, payment hash, action, asset/amount/payTo and quote expiry. Reject anything unexpected. The first signature authorizes a transfer and must be treated as sensitive even though it is not an on-chain transaction yet.
- After both signatures, the application stops. It has not sent them to IMD. Read the final terms and tick I explicitly authorize spending this IMD to admit this job now.
- Click Confirm payment and start job yourself. This sends the exact signed payload through the same-origin proxy. The backend is only a forwarder, never a signer. IMD's facilitator may now settle the payment.
- Use Check status / refresh balance until the result is
job_admitted. The API'spayment_pendingandadmission_pendingare represented aspayment_pendinglocally. Confirm ajob.openadmission result withkind:joband a job ID. Record the order ID, settlement transaction hash and job ID, excluding bearer tokens and signatures. Follow the official job URL from the verified result to inspect execution; admission does not guarantee successful work. - Disable the server's payment flag again after the test. Review any remaining Permit2 allowance in your wallet and revoke it manually if you no longer need it. Keep sufficient ETH for that operation.
Failure handling
- Before payment submission: a cancelled signature, expired quote or API error cannot undo the purchase; purchased IMD stays in the wallet. Swap fees and gas have still been spent.
- After an ambiguous signed submission: payment may already have settled even if the response was lost. Check the same order. Do not claim the IMD is untouched until settlement is known, do not create another payment automatically, and never generate another permit merely to retry. Recover and resubmit the same saved bytes only when the API still reports
quotedand the authorization remains valid, using another explicit confirmation. - If signatures have expired, do not edit deadlines or re-sign an already attempted payment. Establish the old order's terminal settlement status first. This MVP blocks starting a new order while an attempted submission remains unresolved. Once the API confirms admission with a job ID, the user may explicitly start another job; a fresh request key is created and old signatures are discarded. Record the previous job ID before doing so.
- Receipt timeout: retain the hash and recover it. A confirmed revert clears that pending hash; gas may have been charged but the atomic swap did not buy IMD.
- Wallet/network changes: reconnect the original payer and recover. Never sign an order created for a different wallet.
- Closing the browser session can lose the bearer token. Preserve it privately before closing. The current website does not implement account-wide order recovery; consult IMD's documented paid-by lookup if needed. Do not pay again just because local state was lost.
Reproduce the no-value fork demonstration
Use a separate terminal for a fresh local Anvil process. Never expose Anvil publicly and never connect a funded mainnet wallet to it.
anvil --silent --fork-url YOUR_ETHEREUM_RPC --fork-block-number 26126161 --chain-id 1 --host 127.0.0.1 --port 8545
From frontend, after forge build from the repository root:
node node_modules/tsx/dist/cli.mjs scripts/verify-job-fork.ts
The script hardcodes localhost for wallet transactions, checks that the node identifies as Anvil, impersonates a fresh EOA locally, and models selection of that account. It uses the production client/state machine and wallet adapter, real API check/quote/challenge, and actual POOL4 execution on the fork. Payment submission and signing remain disabled. It writes docs/evidence/milestone-client-fork.json without the request secret. Restart Anvil before repeating so the test begins with zero IMD. This test is not a browser-extension wallet acceptance test.