Deployments
The protocol is live on Base, Base Sepolia and Ethereum Sepolia, and runs locally on anvil.
The SDK never needs these hardcoded — CHAINS, DEFAULT_CHAIN and
isDeployedOn(chainId) from @0xslots/contracts are all derived from the
address maps below, so the set of usable chains is exactly the set that has a
SlotFactory recorded. Chains sort local, then testnets, then mainnets, off
chain.testnet, so DEFAULT_CHAIN is never a mainnet by accident.
Every address here is written by DeployProtocol into
apps/contracts/deployments/<chainid>/. That directory is the source; this page
is a copy of it, and the copy is the one that goes stale.
Base — 8453
| Contract | Address | Version |
|---|---|---|
| SlotFactory | 0x14df7d78ef556A80F0AD3ede3F10F1e24f92E1cE | 2 |
| Slot (beacon impl) | 0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f530 | 3 |
| OfferBook | 0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C2 | 2 |
| SlotCollectiveFactory | 0x0552807845Ba2090bE65b8823D8A56514d38Ee72 | 2 |
| SlotCollective (beacon impl) | 0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf0 | 2 |
| SlotBoundNFTFactory | 0x34E5dBeca60c26aeF6513f1cade6679cE4DBfDB8 | 1 |
| AdLand (hook) | 0xf93d43532E1cC486479c9cF83CFc6161aAa8F4A9 | 1 |
| MinimumTenureHook | 0xB1e68532Ba467b2310A931abcDD682E718426c9C | 2 |
Base Sepolia — 84532
| Contract | Address | Version |
|---|---|---|
| SlotFactory | 0xc20bD429Acb3f85ae12b46e3980a8a5184936b8C | 2 |
| Slot (beacon impl) | 0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f530 | 3 |
| OfferBook | 0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C2 | 2 |
| SlotCollectiveFactory | 0x0552807845Ba2090bE65b8823D8A56514d38Ee72 | 2 |
| SlotCollective (beacon impl) | 0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf0 | 2 |
| SlotBoundNFTFactory | 0x960438BAEb1586Ac2B3fCfdC3fBa196F1226b45a | 1 |
| AdLand (hook) | 0xf93d43532E1cC486479c9cF83CFc6161aAa8F4A9 | 1 |
| MinimumTenureHook | 0xB1e68532Ba467b2310A931abcDD682E718426c9C | 2 |
Ethereum Sepolia — 11155111
| Contract | Address | Version |
|---|---|---|
| SlotFactory | 0x14df7d78ef556A80F0AD3ede3F10F1e24f92E1cE | 2 |
| Slot (beacon impl) | 0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f530 | 3 |
| OfferBook | 0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C2 | 2 |
| SlotCollectiveFactory | 0x0552807845Ba2090bE65b8823D8A56514d38Ee72 | 2 |
| SlotCollective (beacon impl) | 0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf0 | 2 |
| SlotBoundNFTFactory | 0x34E5dBeca60c26aeF6513f1cade6679cE4DBfDB8 | 1 |
| AdLand (hook) | 0xA8079a3226C29D0D91DaDc698823Fd3cE96D7Ee7 | 1 |
| MinimumTenureHook | 0xB1e68532Ba467b2310A931abcDD682E718426c9C | 2 |
Local anvil — 31337
pnpm dev:local at the repo root starts a chain, deploys the protocol and runs
an indexer against it — the same DeployProtocol script every testnet uses, so a
local address is a real address. CREATE2 with bytecode_hash = "none", so these
survive a rebuild but move when a contract's own code changes; read them back
from apps/contracts/deployments/31337/ rather than trusting this table.
No table here on purpose. Local addresses move whenever a contract's own code changes — that is what CREATE2 over initcode means — so any list printed here is wrong by the next commit and was, repeatedly. Read them back:
cat apps/contracts/deployments/31337/SlotFactory.jsondev-chain.sh re-checks the generated address table against those records on
every boot and refuses to start when they disagree, so a stale local address is
a startup error rather than an empty page.
CHAINS filters anvil out unless NODE_ENV === "development". Bundlers inline
NODE_ENV, so a production build drops the entry at compile time rather than
shipping a chain option pointing at nobody's localhost. The address tables
themselves are unconditional — they are only data, and a consumer that knows it
wants the local factory can still ask for it by id.
Naming a hook
A slot stores its hook as a bare address. To turn that back into something a
person recognises, @0xslots/contracts ships a small catalogue of the hooks it
can name, per chain:
import { knownHooks, findKnownHook } from "@0xslots/contracts";
const known = findKnownHook(84532, slot.hook);
// known?.name — e.g. "AdLand — publish a creative", or undefined for a hook
// this client does not recogniseA hook appears in knownHooks on exactly the chains it is deployed to, because
the entries are derived from the deployment records — there is no per-chain edit
to keep in sync. An address the catalogue does not know reads back as undefined
rather than a guess; that is the honest answer for a hook anyone can deploy.
There is exactly ONE MinimumTenureHook per chain. It used to be one per
duration, minted by a factory at a CREATE2 address; the window a slot enforces is
now the slot's own hookData, so a single deployment serves every duration and
there is nothing to predict or verify. What a client checks instead is what the
hook CLAIMS — its descriptors() family id — so somebody else's implementation
of the same rule reads correctly without the client learning its address.
ABIs
import {
slotAbi,
slotFactoryAbi,
offerBookAbi,
minimumTenureHookAbi,
slotBoundNftFactoryAbi,
slotBoundNftAbi,
adLandAbi,
} from "@0xslots/contracts";Addresses are exported as per-chain maps, so you rarely need to hardcode one:
import { slotFactoryAddress, offerBookAddress } from "@0xslots/contracts";
const factory = slotFactoryAddress[84532];