APEX (Agent Payment Exchange) is an ERC-8183 Agentic Commerce Protocol deployment coupled with a pluggable, UMA-style Optimistic evaluation policy.
This repository implements a lightweight three-layer architecture:
┌────────────────────┐ job.evaluator, ┌───────────────────────┐
│ │ job.hook │ │
│ AgenticCommerce │ ──────────────────▶ │ EvaluatorRouter │
│ Upgradeable (UUPS) │ │ Upgradeable (UUPS) │
│ ERC-8183 kernel │ ◀──────────────────── │ IACPHook + admin │
└────────────────────┘ hook callbacks └──────────┬────────────┘
│
▼
┌───────────────────────┐
│ OptimisticPolicy │
│ (immutable) │
│ default-approve + │
│ client dispute + │
│ voter quorum reject │
└───────────────────────┘
AgenticCommerceUpgradeable— ERC-8183 job-escrow kernel. Holds funds, runs the state machine, calls hooks.EvaluatorRouterUpgradeable— acts as bothjob.evaluatorandjob.hook. Whitelists policies, binds each registered job to a policy, and settles jobs by pulling a verdict from the bound policy.OptimisticPolicy— immutable policy contract. Silence within a dispute window auto-approves. A client dispute forces a whitelisted-voter quorum to reject; otherwise the job stays pending until the kernel'sclaimRefundescape hatch kicks in at expiry.
See docs/design.md for the full design document
(architecture, user flows, risks, verification matrix).
contracts/ Kernel, Router, Policy, interfaces, test-only mocks/
scripts/ deploy.ts · verify.ts · fund-local.ts · export-abi.ts · addresses.ts · lib/
test/ unit/ (bun test) + e2e/ (5-flow ERC-8183 runner)
docs/ design.md · erc-8183-compliance.md · custom-policy.md · deployment.md
abis/ Slim ABI JSON for the 3 public contracts (run `bun run abis` to regenerate)
Source of truth for every deployed address:
scripts/addresses.ts. The table below mirrors
that file — if the two ever disagree, scripts/addresses.ts wins.
Integrators only need the three proxies + the payment token. Implementation
addresses and the treasury are operational details — look them up in
scripts/addresses.ts if you need them.
| Contract | Address |
|---|---|
AgenticCommerceUpgradeable |
0xa206c0517B6371C6638CD9e4a42Cc9f02A33B0DE |
EvaluatorRouterUpgradeable |
0xd7d36d66d2f1b608a0f943f722d27e3744f66f25 |
OptimisticPolicy |
0x4f4678d4439fec812ac7674bb3efb4c8f5fb78a6 |
| Payment token (USDC on testnet) | 0xc70B8741B8B07A6d61E54fd4B20f22Fa648E5565 |
Testnet OptimisticPolicy runs with a short dispute window for faster
iteration — do not assume these parameters match what a production
mainnet deployment would use.
Not yet officially deployed. There is no canonical mainnet address set
maintained by this repository today. Integrators who need a mainnet
deployment today must roll their own — see
docs/deployment.md for the deploy + verify +
multisig-handoff runbook.
cp .env.example .env # fill in BSC_* keys if deploying
bun install
bun run compile
bun test # unit tests, ~1.5sThree terminals, one fresh node per session:
# terminal 1 — local chain (http://127.0.0.1:8545)
bun run node
# terminal 2 — deploy the stack onto that node
bun run deploy:local
# copy the ready-to-run fund:local command printed at the bottom
# terminal 2 — top up a test EOA with native coin + ERC20MinimalMock
# (fixed defaults: 10 native coin + 10000 test tokens).
# Env vars are one-shot CLI prefixes; do NOT add them to .env.
FUND_RECIPIENT=0x... FUND_TOKEN_ADDRESS=0x... bun run fund:localfund:local refuses to run on bsc or bscTestnet. ERC20MinimalMock
is a permissionless-mint test token; never use it as a real payment asset.
A separate runner in test/e2e/ drives all 5 ERC-8183 user flows
(silence-approve, dispute-reject, stalemate-expire, open-cancel, never-submit)
against a real chain:
# Local (requires `bun run node` in another terminal) — completes in ~15s
bun run e2e:local
# BSC Testnet — reuses live proxies + short-window Policy — completes in ~12min
bun run e2e:testnetTestnet requires 3 pre-funded wallets (owner, client, provider) configured via
.env. See test/e2e/README.md for the full
balance + key matrix.
Before running deploy:*, verify each of the following — these are
deployer responsibilities and the contracts assume them silently
(audit I01 / I08):
-
paymentTokenis a standard ERC-20. The kernel does not reconcile pre/postbalanceOfinfund; it trusts thatsafeTransferFrom(client, this, budget)deposits exactlybudgetinto the proxy and that the held balance does not drift between calls. The following classes will cause silent escrow accounting drift and revert at settlement:- fee-on-transfer / reflection / deflationary tokens,
- rebasing / elastic-supply tokens,
- tokens with mid-lifecycle blocklists or fee toggles,
- any token whose
balanceOf(address)can decrease without an outgoing transfer fromaddress.
Audited stablecoins on BNB Chain (USDT, USDC) are safe choices. Confirm against the token's source before calling
initialize. -
Owner is a multisig fronted by a
TimelockController. Required delays: 48h forAgenticCommerce, 24h forEvaluatorRouter. Single- key ownership is a production mis-configuration; seedocs/design.md§6 R1. -
Voter set on
OptimisticPolicyhas≥ 3 × voteQuorummembers under independent operational control, with 24/7 monitoring. The policy is non-upgradeable; rotating voters means deploying a fresh policy and re-whitelisting it on the Router.
cp .env.example .env # fill BSC_TESTNET_PRIVATE_KEY + ETHERSCAN_API_KEY
bun run deploy:testnet # first deploy, impl upgrade, OR full-stack rotation
bun run verify:testnet # zero-arg Etherscan verifyBefore transferring ownership to a production multisig, read the post-deploy
checklist in docs/deployment.md. That file is
also the source of truth for the reuse-vs-deploy cascade rules and the
paymentToken rotation procedure.
See docs/erc-8183-compliance.md for the
compliance matrix and each deliberate deviation (upgradeable hook,
evaluator-only reject) with its mitigation.
ReentrancyGuardTransienton every mutating state transition in the kernel and onsettlein the router.- Hook calls are ERC-165 gated at
createJoband gas-limited to 1 000 000 gas at call time (kernel bails out of its own state on hook failure). claimRefundis never pausable and never invokes hooks — it is the universal escape hatch for clients afterexpiredAt.OptimisticPolicyenforcesvoteQuorum ≤ activeVoterCountbidirectionally.
Full threat model: docs/design.md §6 Risks.
Please report vulnerabilities through the BNB Chain Bug Bounty Program.