Skip to content
0xSlots

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

ContractAddressVersion
SlotFactory0x14df7d78ef556A80F0AD3ede3F10F1e24f92E1cE2
Slot (beacon impl)0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f5303
OfferBook0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C22
SlotCollectiveFactory0x0552807845Ba2090bE65b8823D8A56514d38Ee722
SlotCollective (beacon impl)0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf02
SlotBoundNFTFactory0x34E5dBeca60c26aeF6513f1cade6679cE4DBfDB81
AdLand (hook)0xf93d43532E1cC486479c9cF83CFc6161aAa8F4A91
MinimumTenureHook0xB1e68532Ba467b2310A931abcDD682E718426c9C2

Base Sepolia — 84532

ContractAddressVersion
SlotFactory0xc20bD429Acb3f85ae12b46e3980a8a5184936b8C2
Slot (beacon impl)0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f5303
OfferBook0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C22
SlotCollectiveFactory0x0552807845Ba2090bE65b8823D8A56514d38Ee722
SlotCollective (beacon impl)0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf02
SlotBoundNFTFactory0x960438BAEb1586Ac2B3fCfdC3fBa196F1226b45a1
AdLand (hook)0xf93d43532E1cC486479c9cF83CFc6161aAa8F4A91
MinimumTenureHook0xB1e68532Ba467b2310A931abcDD682E718426c9C2

Ethereum Sepolia — 11155111

ContractAddressVersion
SlotFactory0x14df7d78ef556A80F0AD3ede3F10F1e24f92E1cE2
Slot (beacon impl)0xfc2Bf27aD41C9C4B23b675Ef5c270161Ab87f5303
OfferBook0xa8A9D7940ceb6bFD4ffeDfB565358B83A9BCc7C22
SlotCollectiveFactory0x0552807845Ba2090bE65b8823D8A56514d38Ee722
SlotCollective (beacon impl)0x627c716889a7Cba789b787fDCfb37EcC41C1Fdf02
SlotBoundNFTFactory0x34E5dBeca60c26aeF6513f1cade6679cE4DBfDB81
AdLand (hook)0xA8079a3226C29D0D91DaDc698823Fd3cE96D7Ee71
MinimumTenureHook0xB1e68532Ba467b2310A931abcDD682E718426c9C2

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.json

dev-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 recognise

A 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];