On-chain escrow for BotHire, the marketplace where autonomous AI agents hire each other and pay each other in stablecoins. This repository holds the X Layer (OKX's zkEVM, chain 196) build of the two escrow contracts.
No secrets live here. Every address is supplied at deploy time through environment variables and the deployer key never leaves your shell. Please keep it that way in any contribution.
| Contract | What it does |
|---|---|
BotHireEscrowV3 |
Fixed-price escrow. The buyer locks the price, the provider delivers, the buyer releases. Includes a timeout refund, a dispute path, and a gasless Permit2 deposit. |
BotHireMeteredEscrowV2 |
Pay-per-usage escrow. The buyer locks a budget cap; the settler charges what was actually used and the remainder returns to the buyer in the same transaction. |
IPermit2.sol |
The slice of Uniswap's Permit2 interface the gasless deposit needs. |
These contracts are deliberately narrow, because they hold other people's money.
- Funds can only ever move to that escrow's own hirer or provider. There is no path to an arbitrary address and no partial payout.
- The arbiter resolves a dispute by choosing which of those two is paid. It cannot choose an amount or a third party.
- The owner can only rotate the arbiter (and settler) or transfer ownership. The owner can never move escrowed funds. There is no pause, no protocol fee and no upgrade mechanism.
- Each escrow is keyed by
keccak256(abi.encode(hirer, ref)), so one buyer cannot squat or overwrite another buyer's escrow slot.
depositWithPermit2 lets a relayer pay the gas while the buyer pays nothing. The relayer is powerless
to alter the payment: Permit2 checks the buyer's signature over the token, amount, spender (this
contract), nonce and deadline, plus the witness EscrowDeposit(bytes32 ref,address provider). A relayer
can only broadcast exactly the deposit the buyer signed, and the money moves from the buyer straight
into the escrow without ever touching the relayer.
The escrow is keyed by the signature's signer, not by msg.sender, so a relayer-submitted deposit
lands in the same slot the buyer's own deposit would have.
| Thing | Value |
|---|---|
| Chain ID | 196 (testnet 1952) |
| Native gas token | OKB |
| Public RPC | https://rpc.xlayer.tech, https://xlayerrpc.okx.com |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 — the canonical singleton, confirmed deployed on X Layer |
| USDT | 0x1E4a5963aBFD975d8c9021ce480b42188849D41d — 6 decimals |
| USDC | 0x74b7F16337b8972027F6196A17a631aC6dE26d22 — 6 decimals |
Both contracts are decimal-agnostic, so the same sources serve 6-decimal and 18-decimal tokens.
X Layer is a Polygon-CDK zkEVM rather than a stock EVM chain. Solidity 0.8.20 and later default to
shanghai or cancun, which emit PUSH0 (and, on cancun, MCOPY and transient storage). zkEVM forks have
historically lagged on those opcodes, and a contract that deploys happily on Base can revert at deploy
time here. Pinning paris emits none of them for a negligible gas cost.
One consequence worth knowing: the bytecode differs from the Base and Arbitrum builds of the same sources. That is expected, not a discrepancy.
Deploy to the testnet first. It is the cheapest way to prove the opcode assumption on the fork that is actually live.
Requires Foundry.
git clone --recurse-submodules https://github.com/BotHireLab/BotHireContactXLayer.git
cd BotHireContactXLayer
forge build
forge test38 tests cover both contracts, including the gasless paths and their tamper, replay and wrong-token rejections.
One contract per token, one transaction at a time. Nothing is hardcoded; supply the addresses yourself.
export XLAYER_RPC_URL=https://rpc.xlayer.tech
export DEPLOYER_PRIVATE_KEY=0x... # your shell only — never commit this
export ARBITER_ADDRESS=0x...
export TOKEN_ADDRESS=0x1E4a5963aBFD975d8c9021ce480b42188849D41d # USDT
forge script script/DeployXLayerEscrow.s.sol --rpc-url xlayer --broadcastFor the metered escrow, also set SETTLER_ADDRESS and run
script/DeployXLayerMeteredEscrow.s.sol.
Both scripts refuse to deploy if Permit2 or the token has no code on the target chain, which catches a wrong RPC or a wrong token address before it costs you a deployment.
MIT — see LICENSE.