amount - fee every time it grants. By the end you will have an adapter the broker can call and, optionally, a discovery listing. Two worked adapters follow: AllocationAdapter (the floor, no external dependency), then TimeboostAdapter (a categorically different scarcity, which is what proves the primitive is general).
The interface
An adapter implementsICapabilityAdapter: two functions.
Brokered event. The full ABI is in the IAdapter reference.
The security model
Three rules every shipped adapter follows. They are what makes a permissionless broker safe.- Gate
grantto the broker. A permissionlessgrantis a free-mint and payment-bypass hole: anyone could obtain the capability without paying. Only the broker, which settles payment before callinggrant, may grant. Store the broker address, restrictgrantwith anonlyBrokermodifier, and exposesetBroker(address)for redeploys. This keeps the primitive permissionless (anyone still relays through the broker) while closing the hole. - Enforce scarcity on-chain, and revert when unavailable. A revert inside
grantunwinds the broker’s settlement atomically, so a consumer is never charged for a capability that did not exist.AllocationAdapterrevertsAllocationSoldOut;TimeboostAdapterrevertsNoLaneControl. - Checks-effects-interactions. Commit your effects before any external interaction, so a reentrant call cannot exceed the cap. The broker’s
nonReentrantis the backstop; the adapter does its own ordering as well.
amount - fee at payee, after the grant, by the broker in the same transaction. The relayer (msg.sender) earns fee = amount * feeBps / 10000.
Worked example A: AllocationAdapter
The guaranteed floor: capped allocation slots in a tokenized-stock vault, with no oracle, no sequencer, and no off-chain service in its path. It isis ICapabilityAdapter, ERC721, Ownable, holds cap / allocated / slotVault / nextSlotId, and is constructed with (payToken, slotPrice, broker, owner).
The grant shows all three rules at once: gated to the broker, scarcity checked then reverted on sold-out, effects committed before the mint.
Deploy.s.sol plus DeployAdapters.s.sol on Arbitrum Sepolia, and DeployRobinhood.s.sol on Robinhood. The decision you make is the constructor, (payToken, slotPrice, broker, owner). Then setCap(vaultId, n) opens real scarcity, and you can mint TUSDC to test consumers. setCap and setBroker are onlyOwner admin on the caps and wiring; they never touch the broker’s settlement logic. The full ABI and addresses are in the IAdapter reference and Deployments.
Worked example B: TimeboostAdapter
A categorically different scarcity: priority inclusion in a single Arbitrum Timeboost express lane, 60-second rounds, use-it-or-lose-it. That is a third shape beside Allocation (a consumable cap) and Gas (execute-my-action). Running two categorically distinct adapters side by side is what proves the primitive is general, not specific to one capability. It isis ICapabilityAdapter, Ownable, holds auction / laneOperator / slotPrice / payToken / broker, and is constructed with (auction, laneOperator, slotPrice, payToken, broker, owner). Its grant guards on live round control, then commits to the consumer’s exact payload.
agents/timeboost): it wraps the consumer’s raw transaction in an express-lane envelope signed by the laneOperator key and submits timeboost_sendExpressLaneTransaction on the sequencer RPC. The contract proves it controls the current round and commits to a payload; it does not perform the inclusion. The el-proxy honors the on-chain InclusionGranted commitment.
Make it discoverable
Discovery is opt-in and entirely off the settlement path. Two steps, both optional for settlement.- Add the capability to
capabilities.jsonso the consumer SDK can target it by name. The entry maps the capability to{ chainId, contract, paramsSchema };resolveCapabilitythen reads the adapter address from that chain’s registry, so the address is never duplicated. - Optionally register on the
CapabilityRegistry, signed from your own wallet, so an indexer (and a marketplace surface) can enumerate your capability.
Registering is discovery, not permission. The broker never gates on it. The capability settles through the broker whether or not it is listed; the registry holds no funds, gates no settlement, and cannot affect a grant. A listing is self-scoped, keyed by
keccak256(abi.encode(chainId, adapter, msg.sender)), so no one can overwrite yours, and a false listing can only mislead a display, never misroute a payment. The consumer signs the real adapter, payee, and params into the order hash, which never touches this contract.Verify
Deploy the adapter, set a cap, and have a consumer broker an order against your address (see the agent-builder lane). The grant mints or commits, theBrokered event carries your adapter and payee, and you receive amount - fee at payee in the same transaction. If you set the cap to zero first, the order reverts AllocationSoldOut and the consumer is not charged: that is the atomic unwind, observable.
Related
- How a brokerage settles: why payout happens after the grant, and why a revert unwinds everything.
- The IAdapter reference and Deployments: the full interface ABI and the live addresses.
- Security and threat model and honest limits: the invariants you rely on, and the two declared landmines.
- Buy a capability from the consumer side, or run a relayer.

