Foundry reference implementation for the ERC-XXXX Agent Tool Registry: a minimal onchain registry for AI agent tools with extensible predicate-based access control.
Pairs with @opensea/tool-sdk — the TypeScript SDK and CLI for authoring tool manifests, registering tools onchain, and gating tool endpoints against this registry.
The standard defines how AI agents discover and access tools through a shared onchain registry that anyone may write to and anyone may read from. Each tool optionally points to an access-predicate contract that gates invocation. The standard deliberately excludes payment, cross-chain gating, and subscription logic, keeping them as orthogonal concerns.
- Open access:
accessPredicateisaddress(0)— anyone can invoke - Predicate-gated:
accessPredicatepoints to an external contract implementingIAccessPredicate— any access model (NFT gating, subscriptions, allowlists, DAO votes, reputation scores) is expressible as a predicate contract without modifying the registry
| Contract | Interfaces | Description |
|---|---|---|
ToolRegistry.sol |
IToolRegistry |
Tool registration, metadata updates, access delegation |
cd packages/tool-registry
forge install
forge buildforge testforge test --gas-reportToolRegistry handles tool registration and metadata updates. Access checks are delegated to an external predicate contract via staticcall. If a tool's accessPredicate is address(0), the tool is open-access. Otherwise, the registry calls IAccessPredicate(accessPredicate).hasAccess(toolId, account, data). Creators who want to temporarily disable a tool point accessPredicate at an always-deny predicate rather than carry a dedicated pause flag.
Reference predicates under examples/ (not part of the canonical ERC). All multi-tenant: deploy once per chain and configure independently per tool — the predicate keys its config by toolId and pulls the authoritative creator from the registry on every write, so any tool creator can configure their own slot without an admin role.
| Contract | Gate |
|---|---|
ERC721OwnerPredicate.sol |
Account owns ≥1 token (balanceOf > 0) in any of up to 10 configured ERC-721 collections |
ERC1155OwnerPredicate.sol |
Account owns ≥1 of any configured (collection, tokenId) pair across up to 10 ERC-1155 collections |
SubscriptionPredicate.sol |
NFT-tier-with-expiration subscription model |
CompositePredicate.sol |
Combines up to 3 leaf IAccessPredicate contracts under AND-all / OR-any with optional per-term negation, fail-closed on sub-call failure |
script/Deploy.s.sol deploys ToolRegistry, ERC721OwnerPredicate, and ERC1155OwnerPredicate deterministically via the Arachnid keyless CREATE2 factory (pre-deployed at 0x4e59...956C on every major chain). Re-running with the same salt is a no-op once the address is occupied; swapping in _SALT for a vanity salt later deploys the new address on chains that haven't seen it without disturbing existing chains.
| Contract | Base mainnet |
|---|---|
ToolRegistry (v0.1) |
0x7291BbFbC368C2D478eCe1eA30de31F612a34856 |
ERC721OwnerPredicate (v0.2) |
0xd1F703D0B90BB7106fAebBfbcAdD2B07BDc4c769 |
ERC1155OwnerPredicate (v0.2) |
0xc179b9d4D9B7ffe0CdA608134729f72003380A7e |
Each contract advertises its identity onchain via name() and version() (registry) or name() (predicates). See the EIP draft for the version-string format.
cp .env.example .env # fill in BASE_RPC_URL, ETHERSCAN_API_KEY, and one of DEPLOYER (+ keystore) or DEPLOYER_PRIVATE_KEY
# Dry-run (simulation only)
NETWORKS=base forge script script/Deploy.s.sol --sig "run()" -vvv
# Broadcast + verify (keystore-based — preferred)
cast wallet import beta-deployer --interactive # one-time keystore import
NETWORKS=base forge script script/Deploy.s.sol --sig "run()" -vvv \
--account beta-deployer --sender $DEPLOYER --broadcast --verify
# Broadcast + verify (raw private key — one-shot)
DEPLOYER_PRIVATE_KEY=0x... NETWORKS=base forge script script/Deploy.s.sol \
--sig "run()" -vvv --broadcast --verifyThe deploy script reads NETWORKS (comma-separated keys from [rpc_endpoints] in foundry.toml) and forks each in turn. Verification uses the Etherscan v2 unified API key (ETHERSCAN_API_KEY), which works across all Etherscan-supported chains including Base.
- OpenZeppelin Contracts: ERC-165
- Forge Std: testing utilities
- create2-helpers: CREATE2 deploy script base