- f324c6d: Normalize x402 registry references to the public usage-reporting source tokens.
- a541527: Reject negative and malformed x402 payment amounts. A negative
maxAmountRequiredpreviously slipped under the signed-BigIntmaxAmountcap check and, when signed, wrapped into a huge positiveuint256EIP-3009 authorization — bypassing the caller's spending limit. Amounts are now validated as canonical non-negative integers invalidatePaymentRequirements(unconditionally, even without a cap) and again in the EIP-3009 signer as defense-in-depth for directsignX402Paymentcallers. - Updated dependencies [569fd4f]
- Updated dependencies [a541527]
- @opensea/wallet-adapters@0.3.3
- Scope predicate gate reads to the configured registry chain.
predicateGate/paidPredicateGateno longer force reads to the Base RPC whenrpcUrlis unset, so a non-Base registry chain is actually queried, and the x402 identity/payment chain is decoupled from the registry chain so non-Base gating no longer fails closed.
-
1c2df8f: Fix four lower-severity tool-sdk audit findings:
- Pin EIP-712 domain name/version to canonical USDC for canonical assets.
- Document and surface the default 10,000-block registry lookup window.
- Cache resolved manifests in
createToolHandlerafter the first success. - Decouple shared caller API-key provisioning from each request's abort signal.
-
8e1b0dc: Validate
metadataURIbefore fetching ininspect(SSRF): require http(s) and reject private/internal hosts. Harden the sharedisPrivateHostnameguard to numerically parse IP literals, closing alternate-encoding bypasses (decimal/octal/hex/partial dotted IPv4 and IPv4-mapped/compatible IPv6), and broaden the rejected ranges (0.0.0.0/8, 169.254.0.0/16 link-local, CGNAT 100.64.0.0/10, IPv6 unique-local fc00::/7). Thanks to @Nexory for reporting this SSRF (tool-sdk#12). -
83b406a: Security fix:
paidAuthenticatedFetchnow enforcesmaxAmountas a per-invocation spending cap. At most one nonzero payment authorization may be signed per call — a second 402 after a paid authorization is rejected instead of signed — and cumulative authorized value may never exceedmaxAmount. Previously a malicious tool could return two paid x402 challenges, each individually undermaxAmount, and obtain two independently-settleable EIP-3009 authorizations (up to 2x the configured cap). The legacy zero-value predicate-gate → paid challenge flow keeps working. -
c7907d4: Security fix: harden
verifyXPaymentAuth(used bypredicateGateandpaidPredicateGate) against replay/misuse of the EIP-3009 X-Payment identity proof:- The free identity-only
predicateGatenow rejects (401) any X-Payment authorization with a non-zerovalue— a non-zeroTransferWithAuthorizationis a live, executable USDC pull the operator should never receive on a free gate.paidPredicateGatestill accepts non-zero values (the facilitator settles them). validBeforeis now capped to at most 1 hour (3600s) in the future (401 otherwise), so a captured X-Payment header can't act as a long-lived bearer token. Well-behaved clients sign now+300s/now+600s, so this is non-breaking.- The authorization's resolved chainId is now pinned to the gate's configured chain (401 on mismatch), so e.g. a base-sepolia-signed authorization can't authenticate to a mainnet gate.
- The
to(operator recipient) check is now unconditional and fails closed (500) when no operator address is configured, closing an unauthenticated-recipient hole.
- The free identity-only
- c93fece:
eip3009AuthenticatedFetchnow validates the 402 challenge'sassetandnetworkbefore signing: the network must be supported and the asset must be the canonical USDC contract for that network. Previously a zero-value challenge could name an arbitrary verifying contract/EIP-712 domain and the SDK would still sign it, creating a signature-phishing surface for contracts that assign different semantics to a zero-valueTransferWithAuthorization. Thanks to @Nexory for raising this x402-signing hardening (tool-sdk#11). - 6d0baa7:
eip3009AuthenticatedFetchnow enforces the documented zero-value guarantee: it only signsX-Paymentauthorizations withvalue: 0, and returns non-zero (or unparseable-amount) 402 challenges as-is instead of signing an authorization that could move USDC to the server's advertisedpayTo. - adc561d: Deduplicate CLI command boilerplate: extract a shared
WALLET_PROVIDER_OPTION_DESCRIPTIONconstant and aparseToolId()helper (both incli/commands/shared.ts), replacing the repeatedWallet provider: …--wallet-providerhelp text (10 commands) and the copy-pastedtry/catchtool-id parsing blocks (10 commands). No behavior change — same option text, same error messages, same exit codes. - 6096c0d: Deduplicate predicate-client boilerplate in
lib/onchain/predicate-clients.ts: extract a sharedopenSeaAssetLinks(collection)helper onBasePredicateClient(replacing the copy-pasted segment/normalized/linksconstruction in 4toManifestAccess()methods), and add typedread()/write()helpers on the base class (holding the ABI as a genericconst TAbi extends Abi) so subclasses no longer repeat thepublicClient.readContract/requireWalletClient()+walletClient.writeContractwiring. viem's per-function argument/return typing is preserved. No behavior change.
- f1db520: Add Robinhood Chain (4663) support:
--network robinhoodon all CLI commands (custom viem chain definition until viem ships one), 4663 added to every canonicalDeployment.chainsarray inchains.ts, and skill/docs chain enumerations updated.
- ac4ac6f: tool-registry: add an explicit Etherscan v2
urlto themonadentry infoundry.toml. Forge's built-in chain list doesn't know chain 143, so config resolution failed duringforge script --verifyand aborted verification for every chain in the run (including Robinhood). - 10dcac1: Fix x402 CAIP-2 network handling asymmetry. The server-side predicate gates now accept CAIP-2 (
eip155:8453) and numeric (8453) network identifiers in theX-Paymentpayload — previously they only accepted the short names (base,base-sepolia) and rejected anything else as an "unsupported network", even though the client already tolerated CAIP-2 forms. Additionally, the client now normalizes thenetworkfield in v1 payment payloads to the canonical short name so that v1 gates (which key off short names) don't reject a verbatim CAIP-2 echo. - 6649847: Fix x402 usage reporting when the facilitator returns a CAIP-2/numeric network. The predicate gate's
settle()now resolves the settlement network viaresolveNetwork, sosettlementChainId(used by the usage-reporting REST endpoint) is populated foreip155:8453/8453responses, not just the short namebase.
- 05b370a: feat: add Monad (chain 143) support. The
--networkflag now acceptsmonadin addition tobase,mainnet,shape, andabstract— the chain is defined locally (mirroring viem'smonaddefinition) because the pinned viem version predates Monad mainnet. Added 143 to everyDeployment.chainsarray inchains.tsand updated the deployed-address docs; the canonical v0.2 addresses are identical on Monad (verified via CREATE2 dry-run).
- 158f38f: Fix
createBankrAccountreading the wrong shape from Bankr's/wallet/me. The endpoint returns{ wallets: [{ chain, address }, ...] }(no top-leveladdress), so the account was created with an undefined address. Now resolve the EVM entry fromwallets[]. - Updated dependencies [158f38f]
- @opensea/wallet-adapters@0.3.2
-
1699f42: BREAKING: Unify
predicateGateto use the standard 402 + X-Payment (EIP-3009) auth flow only.PredicateGateConfig.operatorAddressis now required (was optional)Authorization: EIP-3009 <token>header is no longer accepted: use the X-Payment header via the 402 challenge flowAuthorization: SIWE <msg>.<sig>header is no longer accepted (was already deprecated)- CLI
dry-run-predicate-gatenow requires--operator-addressand asserts a 402 response
- 5deb6c1: The
registercommand now validates that the manifest URL and declared endpoint share the same origin before sending the onchain transaction. Previously onlyverifyanddeploychecked this, so a developer could burn gas registering a tool the indexer would immediately reject. All three commands (register,verify,deploy) now show a consistent, actionable error message that explicitly calls out subdomains as different origins and explains how to fix it.
- c23aa96: Add support for the
uptox402 payment scheme, which allows variable pricing (charge up toamountPerCall, with the actual charge potentially less).createX402Clientnow accepts a signer and registers bothExactEip3009SchemeandUptoEip3009Schemeinternally, letting@x402/coreroute to the correct scheme based on the challenge'sschemefield. ThepayCLI,paidFetch, andpaidAuthenticatedFetchroute to the appropriate scheme automatically, so callers no longer need to know which scheme to instantiate.
- cbc654a:
--tool-refand the usage reporters now support non-address registries such asx402:bazaarandx402:bankrfor tools that are not registered onchain. The--tool-reffield delimiter is a comma (chainId,registryAddress,onchainId) so a registry identifier that itself contains a colon stays unambiguous;parseToolRefrequires exactly three comma-separated fields.toolRegistryAddressis widened from0x${string}tostring, andtoolOnchainIdis kept as a string to preserve precision for IDs exceedingNumber.MAX_SAFE_INTEGER.
- a39766d: Caller-side usage reporting can now identify a tool by its ERC-8257 composite key instead of only its endpoint URL.
reportCallerX402Usageaccepts optionaltoolChainId,toolRegistryAddress, andtoolOnchainIdfields, and thepayCLI gains a--tool-ref <chainId:registryAddress:onchainId>flag (e.g.--tool-ref 8453:0x265b...2cf1:65). When supplied, these are sent in place oftool_endpoint, matching the server-side reporter's payload. This fixes the400 Multiple tools registered for endpointerror that occurred when reporting usage for a tool whose endpoint maps to more than one registry entry.
- c80dc52: Migrate
paidFetch,paidAuthenticatedFetch, and thepayCLI to use@x402/coreclient infrastructure (x402Client,x402HTTPClient,SchemeNetworkClient) for payment payload creation and header encoding. AddsExactEip3009Schemeadapter wrapping tool-sdk's existing EIP-3009 signing into the standard x402 scheme interface. No public API changes.
-
6c1be9c:
createToolHandlernow fires the usage report through a platformwaitUntil(keep-alive-after-response) when one is available, instead of awaiting it inline. This removes reporting latency from every successful call and closes a billing edge case: because x402 settlement runs before the report, awaiting the report meant a function freeze in that window could charge a paid caller without returning a result. WithwaitUntilthe response flushes first and the report runs after.It's automatic for most tools: the Vercel request-context
waitUntilis auto-detected (no dependency on@vercel/functions), andtoCloudflareHandlernow wires the per-requestctx.waitUntil(itsfetchsignature gains the optionalctxargument). A new optionalwaitUntiloption onToolHandlerConfiglets you override detection or support another runtime. When nowaitUntilis available (long-running servers, or serverless lacking it), the report is awaited as before so it still fires before any freeze.
- 09f20a7: The
initVercel template now setsexport const maxDuration = 60on the tool entrypoint. Tools that call an LLM or other slow upstream routinely exceed Vercel's 10s Hobby default, which returns a 502 to the caller; because x402 settlement runs after the handler succeeds, a timeout in the settle/report window can also charge the caller without returning a result. 60s is the Hobby maximum and valid on Pro/Enterprise.
-
16c3380: Refine caller-side usage reporting:
- The
payCLI now sends a usage report by default; pass--no-report-usageto opt out. --api-keyfalls back to theOPENSEA_API_KEYenv var before auto-provisioning an instant key (avoids the rate-limited provisioning endpoint when a key is already available).- A "duplicate" rejection (the tool's own server-side reporter already recorded the settlement) is treated as success rather than logged as an error.
reportCallerX402Usage/reportCallerEip3009Usagenow return aCallerUsageReportResult(reported|already-reported|skipped|failed) so callers can surface an accurate status. ThepayCLI uses it to print the real outcome.
- The
-
paidFetchandpaidAuthenticatedFetchnow throwX402PaymentErrorwhen the server responds with a 5xx status after a payment is sent (e.g. 502, 504), instead of silently returning the error response. The error exposesresponseandsettled(whetherPAYMENT-RESPONSEheaders were present), so callers can detect a payment that may already have settled onchain and avoid unintended retries or double charges. (#441)
- e2d9789: Add caller-side usage reporting:
reportCallerX402Usage,reportCallerEip3009Usage, andextractSettlementTxHash. Tool callers can now send usage reports by endpoint URL with auto-provisioned API keys. Integrated intopaidFetch,paidAuthenticatedFetch(viareportCallerUsageoption), and thepayCLI (--report-usage). - e2d9789:
createToolHandlernow echoes the onchain settlement tx hash back to the caller in the x402 settlement-response header (PAYMENT-RESPONSEfor v2,X-PAYMENT-RESPONSEfor v1) after a paid call settles. This lets caller-side usage reporting (and any x402 client) read the tx hash viaextractSettlementTxHash; without it,--report-usagesilently has nothing to report. Adds thebuildSettlementResponseHeaderhelper.
-
b4f5114: Support x402 v2 and GET endpoints in
pay. Bazaar-discovered x402 tools are frequently GET endpoints that speak x402 v2, where the SDK previously could not transact at all:payonly issued POST, only read the v1 bodyacceptschallenge, and signed the v1X-PAYMENTenvelope.What changed:
- New
x402-challenge.ts:resolveNetwork()acceptsbase,eip155:8453, and8453;parseX402Challenge()reads thePAYMENT-REQUIREDheader or the body and normalizes the v2amountfield tomaxAmountRequired. signX402Paymentemits the version-correct envelope (v1{scheme,network,payload}vs v2{payload,resource,accepted}) and echoes the challenge'sx402Version. Newx402PaymentHeaderName()(X-PAYMENTfor v1,PAYMENT-SIGNATUREfor v2) andX402_SETTLEMENT_HEADERSexports.paidFetchroutes through the shared parser and the version-correct header.paycommand:--methodflag (defaults to POST) with auto-fallback to GET on 404/405, query-string encoding for bodyless verbs, version-correct payment header, on-chain settlement readout,RPC_URLdefaulting to a public Base endpoint, and clearer wallet-config errors.smokeandpaid-authenticated-fetchresolve networks so CAIP-2 networks still get USDC validation.
- New
-
ef12e56: Hash the manifest as served, per ERC-8257 §2. The manifest hash is now computed over the full JSON document (including any namespaced extension fields) with no schema stripping and no injected defaults, so it agrees with any RFC 8785 implementation and with the OpenSea backend. Previously the CLI validated first, which stripped unknown fields and injected a default
type, producing a hash that could differ from the served bytes.What changed:
computeManifestHashnow accepts the raw served or authored object and hashes it as-is. Callers must pass the full document, not a schema-stripped copy.- The manifest schema is now open: namespaced extension fields (reverse-DNS, e.g.
io.opensea.paymentHint, or the legacyx-prefix) are preserved through validation. Thetypefield is no longer defaulted, so it is hashed only when present. validate,hash, andregisterwarn about bare un-namespaced extension fields, which risk colliding with future normative fields.- New
findBareExtensionKeys(data)export reports top-level fields that are neither defined by the schema nor namespaced. This replaces the unreleasedfindUnknownManifestKeys.
Tools whose served manifest contains extra fields or omits
typewill now produce a different (correct) hash than priortool-sdkversions. Re-register or update such tools so their onchain hash matches the served document.
- Thread
--rpc-urlthrough to registry reads in CLI commands. Previously the flag only applied to writes; read paths (status, get, list) fell back to the default RPC endpoint.
- Sync the manifest schema with the canonical ERC-8257 spec:
featuredImageis now an optional field in the manifest Zod schema, so manifests declaring it pass validation.
- Remove the broken
--auth eip3009path from thepayCLI command (along with its--manifestand--chainoptions). The flag bypassed the unified x402 flow (probe → 402 → sign X-Payment → retry) and caused 500 errors.paynow always uses the probe-then-sign path. Example READMEs and skill docs updated to match.
- 8812960: Require
validBeforein predicate-gate authorizations.verifyXPaymentAuthpreviously leftvalidBeforeoptional: a caller could sign aTransferWithAuthorizationwithvalidBefore=0and omit the field, so the expiry check was skipped and the gate accepted an unbounded, non-expiring proof. The field is now required in both the X-Payment and EIP-3009 auth paths, with the now-deadvalidBefore !== undefinedguards and the?? "0"recovery fallback removed. Thanks @Nexory (#10).
-
Add
paidPredicateGate, a combined gate that resolves identity verification (predicate) and x402 payment in a single 402 round trip.Tools using
paidPredicateGateneed only 2 requests (a 402 advertising the real payment amount, then a 200) instead of 3 (predicate 402, then x402 402, then 200). The caller'sX-Paymentsignature for the payment amount simultaneously proves identity, via the recoveredfromaddress, and authorizes the transfer. The onchain predicate is checked before the facilitator settles payment: if access is denied, the gate returns a 403 and no funds move.New exports:
paidPredicateGateandPaidPredicateGateConfig.
-
Unified 402 flow for free and paid predicate-gated tools.
predicateGatenow speaks the x402-style 402 challenge for both free and paid tools. When no auth is provided andoperatorAddressis configured, it returns a 402 withPaymentRequirements(payTo=operator,maxAmountRequired=0) instead of a 401. The gate accepts anX-Paymentheader as the auth source (taking precedence overAuthorization), recovers the signer from the EIP-712TransferWithAuthorizationpayload, and extracts caller identity from thefromfield.PaymentRequirementsare chain-aware: the network name and USDC asset address are derived from the configured chain.x402Versionand theexactscheme are validated.Breaking changes:
eip3009AuthenticatedFetchandpaidAuthenticatedFetchno longer takechainIdortoparams. On a 402 they signX-Paymentwith the advertisedpayToand retry; the redundantAuthorization: EIP-3009header has been removed.EIP3009_CHAIN_MAPandZERO_ADDRESSexports were removed.
New features:
eip3009AuthenticatedFetchgains anallowedRecipientsguard that prevents signingX-Paymentfor arbitrarypayToaddresses returned by a malicious 402 response, mirroringpaidAuthenticatedFetch'svalidatePaymentSafety.
-
d7c1191: Await the
usageReportingreporter before returning the tool response.Previously the handler fired the reporter fire-and-forget after the response was built. On serverless runtimes (Vercel, AWS Lambda) the function is frozen the moment the response flushes, which killed the in-flight request so the usage report silently never arrived. The handler now awaits the report (bounded by the reporter's
timeoutMs, default 5s) so it reliably completes; failures are still caught and logged and never fail the tool call.
-
776788d: Fix usage reporting to attribute the real caller, and make reporting a service-side-only responsibility.
- x402 usage reports now use the on-chain payer as
caller_address(after any gate-verified caller), instead of a placeholder address. - EIP-3009 usage reports now forward the caller's original signed authorization, stashed by
predicateGateonctx.callerAuthorizationand surfaced viaInvocationEvent.callerAuthorization. The server no longer re-signs as itself. - Removed
walletClient,operatorAddress, andtokenAddressfromEip3009UsageReporterConfig. Reporting is authenticated byapiKey, and there is no caller self-reporting or signing path.signZeroValueAuthorizationremains exported for buildingAuthorization: EIP-3009request headers.
- x402 usage reports now use the on-chain payer as
-
fc83682: Add usage reporting integration to
createToolHandlerand update reporters to match os2-corePOST /api/v2/tools/usageendpoint.Breaking changes:
Eip3009UsageReporterConfignow requiresapiKey,operatorAddress, and ERC-8257 composite key fields (toolChainId,toolRegistryAddress,toolOnchainId). Previouslytool_slugwas used; it has been removed.- Default aggregator URL changed from
https://api.opensea.io/api/v2/agent-tools/usagetohttps://api.opensea.io/api/v2/tools/usage. - Base Sepolia (84532) removed from
NETWORK_USDCmap.
New features:
ToolHandlerConfig.usageReporting— pass anEip3009UsageReporterConfigand the handler auto-fires the reporter on every successful invocation (both free EIP-3009 and paid x402 paths). Works alongsideonInvocation.createX402UsageReporter— standalone reporter for paid x402 tools (no wallet signing, just settlement tx hash).- Per-event
txHashhex format validation in x402 reporter.
-
8809203: Add Shape and Abstract chain support to all CLI commands. The
--networkflag now acceptsshapeandabstractin addition tobaseandmainnet. -
bddd1e1: Switch predicate gate and CLI auth from SIWE to EIP-3009.
predicateGatenow accepts bothAuthorization: EIP-3009 <token>(preferred) andAuthorization: SIWE <token>(deprecated). EIP-3009 verification uses pureecrecoveron EIP-712 typed data — no RPC call needed.- New exports:
createEip3009AuthHeader,eip3009AuthenticatedFetch,Eip3009AuthenticatedFetchOptions. - Deprecated exports:
createSiweMessage,createSiweAuthHeader,authenticatedFetch. - CLI
authcommand now uses EIP-3009 by default.pay --auth siwe(deprecated) is still accepted and silently maps to EIP-3009 with a deprecation warning. - Zero-value authorizations use 5-minute expiry (
validBefore) instead ofMAX_UINT256.
-
dc56d72: Add EIP-3009 zero-value authorization for usage tracking (
signZeroValueAuthorization,createEip3009UsageReporter,onInvocationcallback oncreateToolHandler).
-
ad8cf93: feat: deploy canonical ERC20BalancePredicate and default its CLIs to it
- Set the canonical ERC20BalancePredicate address (
0x1a834FC48B5f6e119c62C12a98b32137bCFA77cD) on Ethereum mainnet and Base inchains.ts, replacing the prior zero-address stub. configure-erc20-gateandget-erc20-confignow default to the canonical deployment;--predicate-addressis an optional override instead of a required flag (mirrors the trait-gating CLIs from #353).
- Set the canonical ERC20BalancePredicate address (
-
1ce2300: feat: add ERC20BalancePredicate support — new predicate client, ABI, CLI commands (configure-erc20-gate, get-erc20-config), and requirement decoding for ERC-20 token balance gating
-
38212f7: Add
--erc20-gateand--erc20-min-balanceflags to theregistercommand for one-shot ERC-20 balance-gated tool registration. Also adds--predicate-configsupport forERC20BalancePredicateand makes theERC20BalancePredicateClientconstructor use the canonical deployment address by default. -
f1636af: feat: deploy ToolRegistry + canonical predicates to Shape and Abstract
Deterministically deployed the v0.2
ToolRegistryand all five canonical predicates (ERC721OwnerPredicate,ERC1155OwnerPredicate,SubscriptionPredicate,TraitGatedPredicate,ERC20BalancePredicate) via CREATE2 (saltbytes32(uint256(1))) to Shape (chain 360) and Abstract (chain 2741). All addresses are identical to the existing Ethereum mainnet and Base deployments.Abstract is a ZK Stack chain but runs EVM-equivalent execution, so the standard CREATE2 factory resolves the canonical addresses — no per-chain
overrideswere needed. Updated thechainsarrays inchains.tsand the live-addresses table in the tool-registry README.
- 04b589b: docs: add configure-trait-gating and get-trait-config CLI reference to README
-
308e5f8: Add
configure-subscriptionCLI command and wire SubscriptionPredicate support intoregister --predicate-config -
a5992e3:
configure-trait-gatingandget-trait-configCLI commands now default to the canonical TraitGatedPredicate deployment. The<predicateAddress>positional argument is replaced with an optional--predicate-address <address>flag for overriding the canonical address. -
ca8bd98: Add TraitGatedPredicate for ERC-7496 dynamic trait gating. New exports:
TraitGatedPredicateClient,TraitGatedPredicateABI,ERC7496_TRAIT_KIND,DecodedERC7496TraitRequirement,TRAIT_GATED_PREDICATE.Canonical deployment at
0x10abF07CfA34Bf22372C57f27e8bd9C2DCF93fA1on Ethereum mainnet and Base —TraitGatedPredicateClientnow defaults to this address whenpredicateAddressis omitted.
-
27a89da: Canonicalize
SubscriptionPredicatev0.2 on Ethereum mainnet and Base. The predicate now ships inscript/Deploy.s.soland the SDK at the same deterministic CREATE2 address on chain 1 and 8453:0xCBe0cd9B1d99d95Baa9c58f2767246C52e461f25.SubscriptionPredicateClientno longer requirespredicateAddress— it defaults to the canonical deployment for the configured chain, matchingERC721OwnerPredicateClientandERC1155OwnerPredicateClient. PasspredicateAddressonly to target a non-canonical instance.New SDK export:
SUBSCRIPTION_PREDICATE(theDeploymentrecord), alongside the existingERC721_OWNER_PREDICATE/ERC1155_OWNER_PREDICATE/TOOL_REGISTRY.
-
427e093: Redeploy
ToolRegistry+ canonical predicates as v0.2 on Ethereum mainnet and Base. The v0.2 registry returnsversion() == "0.2"and accepts predicates that advertise IAccessPredicate interfaceId0xbdf9dc18(hasAccess + name + getRequirements).New canonical addresses (identical on chain 1 and 8453):
ToolRegistryv0.2:0x265BB2DBFC0A8165C9A1941Eb1372F349baD2cf1ERC721OwnerPredicatev0.2:0xc8721c9A776958FfFfEb602DA1b708bf1D318379ERC1155OwnerPredicatev0.2:0x77373Dc3c1AE9A1e937eF3e5E08F4807D47c7c11
Pre-beta: the previous v0.1 Base deployment is dropped from the SDK entirely. Removed from
@opensea/tool-sdk:ERC721_OWNER_PREDICATE_V1(value export)getPredicateForRegistryVersion(function export)PredicateKind(type export)PredicateClientConfig.registryVersion(option)
The CLI commands (
register,set-collections,set-collection-tokens,get-collections) no longer proberegistry.version()to pick a predicate — they always use the canonical v0.2 predicate.
-
8bac936: Enforce ERC-8257 §Predicate Introspection Hardening ceilings in
describeToolAccessdescribeToolAccessnow defensively bounds the values it reads from an arbitrary access predicate so a malicious predicate cannot grief discovery surfaces with megabyte-scale returns:getRequirements()array with more than 256 entries → empty array (fail closed)- Individual requirement with
dataover 4096 bytes orlabelover 256 UTF-8 bytes → substituted with the kind sentinel0x00000000 name()over 256 UTF-8 bytes → treated as if the predicate did not implementname()(returnsnull)
The caps mirror the existing manifest-side bounds in
schema.tsso onchain and offchain views of the same data have the same upper bounds. -
f539375: Fix
inspectCLI to dispatch wallet-state-attestation rendering on requirementkind, not predicatename()The previous dispatch keyed on
predicateName === "WalletStateAttestationPredicate", which never matches a real deployment — third-party attestation issuers pick their ownname()(e.g., the reference implementation returns"InsumerAccessPredicate"). The decoder is already keyed onkind(0x7a111640); the inspect renderer now matches.
-
6eacfbe: Add
IWalletStateAttestationrequirement type support (kind0x7a111640)- New
WALLET_STATE_ATTESTATION_KINDconstant andDecodedWalletStateAttestationRequirementtype decodeRequirement()now decodes wallet-state attestation data (issuerJwksUri,conditionHash)- CLI
inspectcommand displays decoded attestation fields forWalletStateAttestationPredicate - SKILLS.md updated with WalletStateAttestation predicate documentation
- New
-
0bb20ac: docs: update README and SKILLS.md for F4a–F4g changes
- README: document
--nft-gate,--predicate-config,--wallet-provider,--rpc-urlflags onregister - README: document
--auth siwe,--manifest,--chainflags onpay - README: add CLI reference sections for
smoke,set-collections,get-collections,set-collection-tokens - SKILLS.md: replace
cast sendwithset-collectionsCLI in Example C
- README: document
-
2391e1e: Add
--predicate-configflag toregistercommand (F4d)When
--access-predicate <addr>is supplied, the CLI now:- Calls
name()on the predicate to identify its type - Displays the predicate name in the registration summary
- Accepts
--predicate-config <json>to bundle predicate setup with registration- ERC721OwnerPredicate:
--predicate-config '{"collections":["0x..."]}' - ERC1155OwnerPredicate:
--predicate-config '{"collection":"0x...","tokenIds":["1","2"]}'
- ERC721OwnerPredicate:
- Prints a warning if
--access-predicateis used without--predicate-config, explaining that the tool will accept any caller until configured - Validates
--access-predicateaddresses
- Calls
-
1be6808: Add
set-collections,get-collections, andset-collection-tokensCLI commands (F4c)set-collections <toolId> <addr...>— set the ERC-721 collection gate list for an already-registered toolget-collections <toolId>— read the current ERC-721 collection gate listset-collection-tokens <toolId> <addr> <tokenId...>— set the ERC-1155 collection + token ID gate- All commands auto-detect registry version and select the matching predicate deployment
- Supports
--dry-run,--wallet-provider,--rpc-url, and--networkoptions
-
69b30ff: Fix
--nft-gatebroken end-to-end on Base mainnet (F4a)The SDK hardcoded the v0.2
ERC721OwnerPredicateaddress, which is rejected by the live v0.1 registry on Base. Theregistercommand now queries the registry'sversion()and selects the matching predicate deployment automatically.- Re-added
--nft-gate <collection>toregisterwith registry-version-aware predicate selection - Added
ERC721_OWNER_PREDICATE_V1deployment constant - Added
getPredicateForRegistryVersion()resolver - Added
registryVersionoption toPredicateClientConfig - Updated
tool-registry/README.mdandSKILLS.mdwith v0.1 predicate addresses
- Re-added
-
2c1e552: Add
--auth siweflag to thepayCLI command. When set, usespaidAuthenticatedFetch(SIWE + x402 payment) instead of payment-only flow. Also auto-enables SIWE auth when--manifestpoints to a manifest with anaccessblock. -
53a49cf: Remove deprecated
nftGatemiddleware and--nft-gateCLI flag behavior- Removed
nftGatemiddleware (src/lib/middleware/nft-gate.ts) and itsNFTGateConfigtype - Removed
nftfield fromToolContext.gatesandBypassGates - Re-added
--nft-gateoption toregisterwith registry-version-aware predicate selection - Use
predicateGatewith--access-predicateinstead for all access gating middleware
BREAKING:
nftGate,NFTGateConfig, andToolContext.gates.nftare no longer exported. Migrate topredicateGate({ toolId }). - Removed
-
91d95ba: Add
SubscriptionPredicateClientandCompositePredicateClienttyped clients matchingtool-registryexample predicates. Add missingCollectionsSetevent toERC721OwnerPredicateABI. Fix stale v0.1 predicate addresses in SKILLS.md examples.
-
b02f9b0: Treat 4xx probe responses (e.g. 400 from Zod validation) as "endpoint reachable" instead of printing a misleading WARN. Only 5xx responses are flagged as failures.
-
cab7a72: Treat 402 with valid
acceptsarray as auth-OK insmokecommand- When a paywalled tool returns 402 with payment requirements after SIWE auth,
smokenow exits 0 and prints: "Auth OK — paywall fired (expected for paywalled tools)." - The
--expectflag no longer defaults to 200; when omitted, 402-with-accepts is auto-success. When explicitly set, the exact status code is asserted as before.
- When a paywalled tool returns 402 with payment requirements after SIWE auth,
-
303247f: Add
defineVerifiability()typed builder for verifiability blocksThree tier-narrowed factory methods (
selfAttested,hardwareAttested,verifiable) produce correct verifiability objects fordefineManifest(). Invalid tier/field combos are rejected at the TypeScript type level rather than only at validation time. -
2095a84: Replace ERC-Draft placeholder references with officially assigned ERC-8257 number across CLI help text, documentation, and test vectors. Manifest schema
typeURL switched to the canonical ERCs site (https://ercs.ethereum.org/ERCS/erc-8257#tool-manifest-v1); test vectors and pinned hashes updated accordingly.
-
58a2b4b: Add
toManifestAccess()toERC721OwnerPredicateClientandERC1155OwnerPredicateClientfor programmatic manifest access generation with deterministic OpenSea collection links. Export shared requirement-kind constants (ERC721_KIND,ERC1155_KIND,SUBSCRIPTION_KIND). -
480f67c: Suggest manifest
accessblock when registering with--nft-gateThe
registerCLI command now detects when a manifest is missing theaccessfield and the user is registering with--nft-gate. It generates the correct access object usingERC721OwnerPredicateClient.toManifestAccess()and prints it as a suggestion. The preview appears in both--dry-runand normal mode. In normal mode the user is prompted to view copy-paste instructions for updating their manifest. -
c34fb8d: Enforce bidirectional tier consistency checks in manifest validator
The verifiability schema now rejects all 4 invalid tier/field combinations:
hardware-attestedrequiresteeore2eeexecutionhardware-attestedrequires anattestationfieldself-attestedcannot useteeore2eeexecutionself-attestedcannot include anattestationfield
- 4fbcb98: access.links values now require valid HTTPS URLs; non-HTTPS values (onchain addresses, http://, ipfs://) are rejected by schema validation.
- 5e3c7fb: Enforce ERC-spec parser caps on
access.requirements: max 256 entries, max 4,096 decoded bytes peraccess[].datafield. - 0032316: Enforce access label limit as 256 bytes (UTF-8) instead of 256 characters, aligning with the updated ERC spec.
- e7286be: Enforce lowercase-only hex in manifest schema fields (
access[].kind,access[].data,attestation.enclaveHash,reproducibleBuild.buildHash,creatorAddress) to match the tightened ERC spec. Normalize wallet and onchain addresses to lowercase before comparison inregisterandupdate-metadatacommands.
- Updated dependencies [9ecf704]
- @opensea/wallet-adapters@0.3.0
- 5ea8a05: Replace ERC-XXXX placeholder references with ERC-Draft throughout CLI help text, schema defaults, and documentation.
-
5838915: DX improvements from canary builder feedback:
createWellKnownHandlernow acceptsManifestDefinition(withEnvResolverlambdas) and resolves internally on first request — eliminates the silent-failure footgun where lambda fields would serialize asundefined. Accepts an optionalenvparameter for non-Node runtimes (Cloudflare Workers, Bun).- Well-known handler no longer 404s on pathname mismatch — relies on framework router. If you mounted
createWellKnownHandleras a fall-through, scope it to the well-known path explicitly. defineToolPaywallrecipient acceptsEnvResolver<Address>so the payout address can be read from env vars at request time instead of module-load timedefineToolPaywallreturnsonSettlecallback for post-payment telemetry/logging- Template
package.jsonfiles inject the current SDK version atinittime instead of pinning stale^0.1.0 - Vercel adapter uses
x-forwarded-protoheader (consistent with Express adapter) describeToolAccesshelper reads a tool's predicate name and requirements from the registry;decodeRequirementdecodes known kinds (ERC-721, ERC-1155, Subscription) into typed objectsonSettlepayer is now sourced from the facilitator's/verifyresponse (reliable for pure-x402 tools) with fallback toctx.callerAddressregisterToolandupdateToolMetadatavalidate URI length client-side before sending the transaction- Templates include
EnvResolverlambda examples in comments initcommand prints workspace warning for pnpm users
-
ba59886: Add
@opensea/tool-sdk/testingsubpath export with test utilities for tool builders:createMockManifest,createMockToolContext,mockFetch, andcreateTestHandler.
- 16f4b7e: Re-export
BankrAdapterandBankrConfigfrom@opensea/wallet-adapters.createWalletFromEnv()already auto-detects Bankr whenBANKR_API_KEYis set; this makes the named adapter directly importable from@opensea/tool-sdkfor callers that need to construct it explicitly. - Updated dependencies [a81071b]
- @opensea/wallet-adapters@0.2.0
-
ef922d8: Migrate
authandsmokeCLI commands fromTOOL_SDK_PRIVATE_KEY/privateKeyToAccounttocreateWalletFromEnv()from@opensea/wallet-adapters. Both commands now usePRIVATE_KEY(via wallet-adapters) instead of the non-standardTOOL_SDK_PRIVATE_KEYenv var, and accept the--wallet-providerflag for explicit provider selection. This makes wallet configuration consistent across all CLI commands (auth,pay,smoke,register,update-metadata).Breaking:
--key(auth),--as(smoke), andTOOL_SDK_PRIVATE_KEYenv var have been removed. UsePRIVATE_KEY+RPC_URLenv vars or--wallet-providerinstead. -
f6ef66e: feat: add delegated agent auth via delegate.xyz for predicate-gated tools
-
80bfd16: feat(manifest): validate inputs/outputs as well-formed JSON Schema
-
fde8ef0: feat: add ERC721OwnerPredicateClient and ERC1155OwnerPredicateClient for managing predicate collections
-
6b31470: feat(predicate-gate): accept registryAddress override for local development
PredicateGateConfigandToolRegistryClientnow accept an optionalregistryAddressfield. When provided, the middleware and client use the given address instead of looking up the canonicalTOOL_REGISTRYdeployment. This enables local development against a forked Anvil node or a custom registry deploy without monkey-patching the SDK. -
1368f61: feat: support runtime env resolution in defineManifest for Cloudflare Workers
-
1b3a388: feat(init): update Vercel template with agent-friendly discovery page and llms.txt
The
tool-sdk inittemplate now scaffolds an index.html that serves as an llms.txt-style discovery page — showing agents the manifest location, endpoint, auth requirements (SIWE), input/output schemas, and SDK usage examples. Also adds a/llms.txtplaintext file following the llms.txt spec for direct LLM consumption.Updated to use
createWalletFromEnv/walletAdapterToClientfrom@opensea/wallet-adaptersinstead of rawprivateKeyToAccount. Added coverage for x402 payment flows (paycommand,paidAuthenticatedFetch), smoke-testing, and multi-provider wallet configuration (Privy, Turnkey, Fireblocks). -
b5307e4: fix(deploy): skip blank-valued env vars in .env.local.example during deploy wizard
-
7da1fae: fix(deploy): skip env var prompts for vars already set in Vercel
- 997510c: Add endpoint probe to verify, inspect, and smoke commands to catch routing defects (405, 404) before signing or declaring success
- dca9933: Add paidAuthenticatedFetch for predicate+paywall composite gates; add --paid flag to smoke command; add inspect warning for composite-gated tools
- 181f647: Point ERC721_OWNER_PREDICATE / ERC1155_OWNER_PREDICATE at v0.2 deployments on Base. The new predicates implement
getRequirements()(F18 access-requirement introspection); existing v0.1 addresses are no longer canonical. Tools registered against the v0.1 ToolRegistry are unaffected — only the predicate addresses moved. Tools that delegated access to the v0.1 predicates will needsetAccessPredicateto repoint at the new addresses.
- Add
repositoryfield topackage.jsonso npm provenance can validate the bundle against the public mirror athttps://github.com/ProjectOpenSea/tool-sdk. The 0.1.1 tag/release exists but never reached npm because of this missing field; 0.1.2 is the first version actually on npm.
-
905ed4a: Initial release of
@opensea/tool-sdk— SDK and CLI for building ERC-Draft compliant AI agent tools.createToolHandlerfor building tools with manifest validation, JCS keccak256 hashing, well-known endpoint middleware, and aGateMiddlewarechain.- Framework adapters for Vercel, Cloudflare, and Express. Includes
VercelRequest/VercelResponse/ExpressRequest/ExpressResponsetype exports. ToolHandlerErrorfor typed HTTP status codes; unhandled errors are logged automatically.ToolManifestSchemawith optionalverifiabilityblock (tier, execution, dataRetention, sourceVisibility, attestation, reproducibleBuild).
ToolRegistryClientwithregisterTool,setAccessPredicate,tryHasAccess, andlistToolsByCreator.- Canonical CREATE2 deployments on Base mainnet:
TOOL_REGISTRY(v0.1),ERC721_OWNER_PREDICATE,ERC1155_OWNER_PREDICATE. Each is aDeploymentobject ({ address, chains, overrides? }); resolve per-chain withdeploymentAddress(deployment, chainId). registerCLI enforcescreatorAddressmatches the signing wallet per ERC-Draft spec.IAccessPredicate(interface ID0xbdf9dc18) andIToolRegistryABIs exported, includingname()/version()view methods. Predicates expose machine-readable access requirements viagetRequirements(toolId)returningAccessRequirement[]withRequirementLogic(AND/OR). Marker interfacesIERC721Holding,IERC1155Holding,ISubscriptionshipped viaIRequirementTypes.sol.
predicateGate({ toolId })middleware delegates the access decision to the onchainToolRegistryso the registered access predicate is the single source of truth. One middleware works for every predicate type (single-collection, multi-collection, ERC-1155, subscriptions, composites, future predicates) without per-predicate SDK changes; on-chainsetAccessPredicateupdates are picked up automatically. Returns 403 with the predicate address on(true, false), 502 on predicate misbehavior.checkToolAccessis the client-side preview ofpredicateGate(no SIWE) for frontends and CLIs that want to gate UI before invocation.nftGateis@deprecatedin favor ofpredicateGatefor any registered tool. It remains exported for local development against unregistered tools.- SIWE auth helpers:
createSiweMessage,authenticatedFetch. Now also accepts external signers (Bankr, MPC, HSM) for agent wallets.
x402Gate— lower-level gate for self-hosted facilitators.payaiX402Gate— uses the PayAI community facilitator (https://facilitator.payai.network, no auth, free); recommended for prototyping.cdpX402Gate— uses Coinbase Developer Platform (https://api.cdp.coinbase.com/platform/v2/x402); accepts acreateAuthHeaderscallback for the CDP JWT auth.- All hosted gates settle on chain after the handler succeeds — they invoke
/settleonce the response validates against the schema, moving USDC from payer to recipient and recording the settled tx hash onctx.gates.x402.settlementTxHash. Settlement is synchronous (up to 10s timeout); failures log[tool-sdk] gate.settle failed:but the response still returns 200. Operators running their own facilitator viax402Gateare responsible for settlement. defineToolPaywallhelper prevents pricing/gate config drift between the manifest and the gate.- Recipient address validation rejects zero and burn addresses.
- Client helpers:
signX402Payment(signs an EIP-3009TransferWithAuthorizationfor USDC, returns base64-encodedX-Paymentheader value) andpaidFetch(drop-in fetch wrapper that handles the 402 challenge automatically). GateMiddlewaregains an optionalsettle?(ctx)hook called bycreateToolHandlerafter a successful run.
- Wallet provider auto-detection across Privy, Turnkey, Fireblocks, and PrivateKey via the shared
@opensea/wallet-adapterspackage. CLI accepts--wallet-providerto override. walletAdapterToClientdelegates to@opensea/wallet-adapters/viem(walletAdapterToViemClient).- Replaces the old
TOOL_SDK_PRIVATE_KEYenv var with theWalletAdapterabstraction.
init— project scaffolding (Vercel/Express/Cloudflare templates) with TS-manifest support.validate,hash,export— also accept TypeScript manifest files.verify,register,update-metadata— manifest lifecycle.inspect— shows predicate type, ERC-721/1155 collections, and accepts--check-access.pay— pay a paywalled tool from the CLI.auth— SIWE-authenticated calls to predicate-gated tools.dry-run-gate,dry-run-predicate-gate— test gate setup locally before deploy.deploy --host vercel— one-command deploy: login check, project link (auto-configured with inferred project and scope), env var setup from.env.local.example(with sensitive-input masking), first deploy,TOOL_ENDPOINTconfiguration, force redeploy, and manifest verification. Recovers from non-zero vercel exit when a URL was issued.smoke— production endpoint verification.
- README with full CLI reference (export, update-metadata, inspect, deploy, pay, auth, dry-run-gate, dry-run-predicate-gate).
- Predicate gating + SIWE authentication guides.
- Hosting comparison, migration guide, and FAQ.
- MIT LICENSE.
The
IAccessPredicateERC-165 interface ID changed from0xa11ea958to0xbdf9dc18due to the addition ofgetRequirements()andname(). Third-party predicate implementations must add both functions and report the new interface ID; existing predicates that declared support for the old ID will fail registration via_validatePredicateuntil updated. Permissive predicates that do not declare ERC-165 support are unaffected.TOOL_REGISTRY_ADDRESS(chain-keyed map) →TOOL_REGISTRY(Deploymentobject).ERC721_OWNER_PREDICATEandERC1155_OWNER_PREDICATEshape changed fromRecord<number, address | undefined>toDeployment. UsedeploymentAddress(deployment, chainId)to resolve an address for a specific chain.PaidFetchOptions.accountrenamed tosigner(type widened toWalletAdapter | Account).signX402Payment({ account })renamed tosignX402Payment({ signer }).createWalletFromEnv(provider?)no longer accepts an optional provider argument — usecreateWalletForProvider(provider)instead.createWalletFromEnv()no longer emitsconsole.warnon multi-provider configs; it silently picks the highest-priority provider.The
registerCLI's--nft-gate <address>flag now configures the canonical multi-tenantERC721OwnerPredicateviasetCollections(toolId, [collection])after registration, instead of deploying a per-collection predicate.setCollectionsis gated to the tool's creator and the predicate reads the authoritative creator from the registry on every write. IfregisterToolsucceeds butsetCollectionsfails, the tool is registered with an unconfigured gate andhasAccessreturns false for everyone — the CLI surfaces transaction details so the creator can re-runsetCollectionsmanually. -
959d87f: Sync tool-sdk with recent tool-registry updates:
- ABI completeness: Add missing
ToolMetadataUpdatedandAccessPredicateUpdatedevents toIToolRegistryABI; addhasAccesstoIAccessPredicateABIto match the full Solidity interface - New predicate ABIs: Add
SubscriptionPredicateABI(configureToolGating, getToolGatingConfig, getSubscriptionStatus) andCompositePredicateABI(setComposition, getOp, getTerms) with events - ToolRegistryClient: Add
name()andversion()methods for onchain identity introspection - inspect CLI: Display access requirements via
getRequirementsfor any predicate; show SubscriptionPredicate config (collection, minTier) and CompositePredicate terms (op, leaf predicates, negation)
- ABI completeness: Add missing