Use ETH to acquire exactly the IMD missing for an IdentityMD action. IMD is delivered to the user's wallet; that same wallet then completes IdentityMD's normal payment flow.
Status: MVP contract verified in local and Ethereum mainnet-fork tests. Not deployed on mainnet. The real API and full client have been exercised through payment preparation on a local fork; no real IdentityMD payment has been executed. This is neither an independent audit nor production certification.
What it solves
Users can acquire IMD without leaving the application to buy it manually. The application reads current prices and the wallet balance, then purchases only the difference. The contract does not know the price of any job.
The purchase uses exact output: it delivers the requested amount or reverts completely. The maximum ETH input includes the market fee; Ethereum gas is paid separately. The router adds no fee.
Flow
flowchart TD
A[User selects an action] --> B[API: current amount and request]
B --> C[Calculate missing IMD]
C --> D{Is IMD missing?}
D -- Yes --> E[Confirm maximum ETH input]
E --> F[Router: exact-output POOL4 purchase]
F --> G[IMD to wallet and unused ETH refunded]
D -- No --> H[Normal IdentityMD payment]
G --> H
H --> I[Same wallet signs Permit2 and QuoteApproval]
I --> J[Check action status]
Sending IMD directly to IdentityMD's recipient does not replace payment: the protocol requires the payer's signatures. Acquisition and payment therefore remain separate.
Quick start
The launch identity is IMD Flow. / is the English presentation website with a read-only live-requirement demo, official IMD references, and a planned Stockereum token section. The demo retrieves current job/workflow/oracle requirements and uses a visitor-entered example balance; it cannot move funds. /preview remains an entirely illustrative calculator, and /docs contains English documentation. /checkout is the English job.open preparation flow and is available in safe mode by default. Swaps require the operator release gate and a verified deployment; signed payment submission requires a separate server flag. See LIVE_IMD_TEST.md and the milestone report. The planned project token is separate from IMD and is not implemented or required by this router. See the project brief.
Requires Foundry and Node.js 22.9 or later (validated with Node 24.18.0). Solidity libraries are included in the ZIP. For a Git clone:
git submodule update --init
cp .env.example .env
In PowerShell, use Copy-Item .env.example .env. Set your Ethereum endpoint in MAINNET_RPC_URL. Do not add private keys; tests do not require them.
forge fmt --check
forge build
forge test --no-match-contract RouterForkTest -vvv
forge test -vvv
forge test --match-contract RouterForkTest -vvv
The last two commands require MAINNET_RPC_URL and IMD_JOB_AMOUNT, fetched live from capabilities. In PowerShell: $env:IMD_JOB_AMOUNT=((Invoke-RestMethod https://api.imd.fun/requests/capabilities).actions | Where-Object action -eq job.open).payment.amount. The new fork test intentionally fails when this live input is missing. The fork uses the latest block by default. To reproduce the recorded evidence, set MAINNET_FORK_BLOCK=26126161; remove that variable to use current state. The fork suite fails when RPC is missing and is never silently skipped.
PowerShell example: $env:MAINNET_RPC_URL='YOUR_URL' and $env:MAINNET_FORK_BLOCK='26126161'. The provider must serve historical state for that block. Test funds exist only in the local fork.
cd frontend
npm ci
cp .env.example .env.local
npm run test
npm run typecheck
npm run build
npm run dev
Also configure MAINNET_RPC_URL in frontend/.env.local for the purchase integration. The presentation website and illustrative preview work without RPC. Open http://127.0.0.1:3000. Leave NEXT_PUBLIC_ROUTER_ADDRESS empty and ROUTER_LAUNCH_ENABLED=false: purchasing remains disabled until an operator completes the release requirements in LAUNCH_WEBSITE.md. Both an explicit release flag and a valid nonzero address are required. The wallet adapter additionally checks NEXT_PUBLIC_ROUTER_CODE_HASH against the reviewed deployment. Leave IMD_PAYMENT_SUBMISSION_ENABLED=false for safe preparation; this also blocks signed submissions in the server proxy. Never treat an address from a simulation as a deployed contract. The frontend supports Ethereum only; changing NEXT_PUBLIC_CHAIN_ID does not enable other networks.
Contract
File: src/IMDUniversalPaymentRouter.sol. The constructor takes no arguments and checks chain ID 1, deployed code, and the official market configuration.
function buyIMDWithETH(
uint256 imdAmountOut,
uint256 maxEthIn,
address recipient,
uint256 deadline
) external payable returns (uint256 ethSpent);
imdAmountOut: IMD base units, with 18 decimals. Must be greater than zero and at mostint128.max.maxEthIn: wei; must equalmsg.valueexactly.recipient: receives IMD and may be a different wallet. The zero address, router itself, and PoolManager are prohibited.deadline: last permitted Unix timestamp, inclusive.- Returns actual spending. Emits
IMDPurchased(buyer,recipient,address(0),ethSpent,imdAmountOut). unlockCallbackis exposed because Uniswap requires it; it works only once during an authenticated purchase. It is not an arbitrary execution entry point.
Getters: imd, poolManager, hook, POOL_ID, and poolKey. No owner, proxy, spending approvals, rescue function, or configuration changes. V2 requires a separate deployment.
Completed verification
| Check | Result |
|---|---|
| Solidity 0.8.26 formatting and compilation | Passed |
| Complete Foundry suite | 49 tests, 0 failed, 0 skipped |
| Dedicated fork suite | 10 tests, including 256 fuzz runs |
| Unit fuzzing | 3 properties × 256 runs |
| Invariants | 2 properties together: 128 sequences / 4096 calls |
| Frontend, signatures, release gate, and live demo | 43 tests, 0 failed |
| TypeScript and Next.js build | Passed |
| Slither 0.11.3 | 65 final findings, all recorded; see SECURITY.md |
| npm audit | 0 vulnerabilities reported in the saved check |
The 10 fork tests are included in the 49; they are not additional to that total. Fuzz counts represent additional iterations, not test names. Logs are in docs/evidence/.
Measured gas
Fork block 26126161, compiler 0.8.26, optimizer 200, Cancun EVM:
| Requested IMD | Measured call gas |
|---|---|
| 0.000001 | 263,560 |
| 1 | 263,624 |
| 100 | 263,983 |
Measured with gasleft() around the purchase, after quoting in the same test: some accesses are already warm. This excludes base transaction cost and is not a promise of real transaction gas. The hook may perform additional work depending on state. These amounts are test cases, not action prices.
Limitations and trust
The POOL4 owner can withdraw liquidity. IMD depends on its bridge infrastructure. A purchase may revert because of insufficient liquidity, spending limits, price changes, or hook failure. Funds are reverted on failed purchases, but actual gas fees are not refunded. Price tolerance allows MEV up to the selected maximum.
Forced ETH or tokens accidentally sent to the router cannot be rescued. Normal purchases neither use nor accumulate those balances. Do not send funds directly to the contract.
The minimal frontend supports browser wallets whose accounts have no code; contract wallets and delegated accounts require additional validation. Actions other than job creation use official JSON input fields. Signatures and the tracking secret remain in the browser session; ending the session may lose tracking access. They are not private keys.
See architecture, integration, security, adversarial review, deployment, V2, and the delivery report.
ETH-first checkout presentation
The checkout presents “Pay with ETH” as funding a job. ETH estimates, maximum input and slippage stay visible; IMD balances and settlement details are available in expandable sections. Existing IMD reduces the required purchase, potentially to zero. The purchase still requires wallet confirmation, and a separate payment authorization and submission starts the job. This wording does not introduce native-ETH IdentityMD settlement, gas subsidies or automatic signatures. Safe-mode restrictions remain unchanged.
Confirmed job admission now permits an explicit new order with a fresh request key and cleared payment signatures. Unresolved submitted payments still block reset. A status refresh after admission is supported. These controls do not activate mainnet deployment or spending.
Control-station visual identity
The current landing, checkout and documentation use the dark mint control-station design described in CONTROL_STATION_DESIGN.md. The primary CTA opens safe job preparation at /checkout. Deployment and real payment remain disabled. Generated instrument artwork is illustrative and does not represent live transaction activity.
Mainnet deployment update — 2026-10-05
The user confirmed deployment from 0xFC2D201c44b18E85eD39fbF9FD098B4c3D7BaD14. Router: 0xf476a72f0e4d31f1cbaaa629a550cd982bab8d26, Ethereum chain 1, block 26128548. Transaction: 0x87123526b82e77b89d95850807696df2690b6aa59a7f2aac0108deb571ee97bf. Gas paid: 0.000255657297530583 ETH.
Creation input matches the compiled artifact exactly. Runtime instructions and all immutable values match the compiled router and expected market configuration. Runtime hash: 0x50eb37a199fe108bdd0dc0ae8f37d0cc82ea0a62eb8d84f16d5219a060bc66ee. A fork-only simulated purchase using the deployed address passed at block 26128559. Evidence: deployment/mainnet-verification.json. This supersedes earlier statements that the router is undeployed. It is not explorer source verification or an independent audit.
Production configuration is being connected to this deployment with the runtime hash pinned. Wallet confirmation and separate payment submission remain mandatory. No live swap or paid job acceptance has been verified. The next step is a user-confirmed first transaction and settlement, following docs/LIVE_IMD_TEST.md. Deployment alone does not establish successful end-to-end job payment.
Wallet account-code restriction removed — 2026-10-05
The checkout no longer rejects a payer solely because eth_getCode returns contract code, including EIP-7702 delegation. Ethereum chain and connected-payer checks remain mandatory. Router runtime hash, amount, recipient, expiry, allowance and both signature validations are unchanged. This removes the observed local blocker; it does not certify every smart wallet or guarantee IdentityMD acceptance. Current payment validation still requires recoverable 65-byte signatures for the same payer. Arbitrary ERC-1271-only or counterfactual account signatures are not newly implemented. Reuse existing IMD: obtain a fresh quote and refresh the balance before any additional purchase.
Checkout permission clarity
The checkout shows the live quoted IMD amount, Permit2 approval address and x402 spender. Approval is shown only when the allowance is insufficient; signing is shown when sufficient. Final submission remains a separate explicit action. Provider rejection code 4001 now gives cancellation and order-recovery advice without automatic retries. Wallet risk alerts are not suppressed. The reported Blockaid risky-spender classification remains unresolved; matching a canonical address does not prove an alert incorrect.
Visible job progress
Accepted orders display a JobResult card with manual read-only refresh, objective, execution state and available artifact links. Payment controls are hidden after admission. /jobs/{id} supports bookmarking a public job. The proxy permits only GET jobs/{id} and jobs/{id}/result and does not forward order bearer credentials to those public routes. Result links are restricted to HTTPS api.imd.fun/artifacts/{64-hex-hash}. Completion is distinct from admission and does not imply independent factual verification. Failed reads never trigger payment or job creation.
Reports open inline through a read-only, fixed-origin /api/report/{hash} proxy (64 KiB limit, no redirects or credentials). Markdown is rendered without raw HTML or remote images; citation links permit HTTPS only. This view does not download files or initiate payments.
Job work selection
The checkout supports research-report, build-contract-project and template audit through job.open. Audit requires a public GitHub repository and a pinned 40-character commit. Live check/quote validation remains required. No deployment or GitHub publishing is requested. A strict input union prevents mixing job types or injecting unsupported model fields. Existing research drafts remain compatible. The live documentation does not expose model selection for paid jobs: the AI model control accurately shows Automatic, managed by IdentityMD. Code and audit execution have not been paid or verified end-to-end; free prechecks validated research/code and rejected an invalid audit revision. Code source browsing and the separate audit report endpoint are not yet implemented in the result reader.