Skip to content
0xSlots

Deployments

Where addresses come from

DeployProtocol writes one record per contract to apps/contracts/deployments/<chainId>/<Contract>.json, with an address, the startBlock the indexer begins from, and a version. Only records carrying a version belong to this protocol: the retired protocol's scripts wrote the same filenames, so @0xslots/contracts, the indexer and the deploy script all ignore a record without one.

@0xslots/contracts generates its address maps from those records, so a contract appears on exactly the chains it has been deployed to. CHAINS, DEFAULT_CHAIN and isDeployedOn(chainId) are derived from slotFactoryAddress, so the set of usable chains is exactly the set that has a SlotFactory recorded. Chains sort local, then testnets, then mainnets, so DEFAULT_CHAIN is never a mainnet by accident.

Addresses are CREATE2 through the canonical deployer, salted by contract name and version, so the same code with the same admin and constructor arguments lands at the same address on every chain.

Deploying to a chain

Each chain the deploy tooling knows has a config file in apps/contracts/deployments/config/ — today anvil, Base, Base Sepolia and Ethereum Sepolia. From the repo root:

pnpm protocol deploy --dry --chain base-sepolia   # simulate, send nothing
pnpm protocol deploy --chain base-sepolia

Local anvil — 31337

pnpm dev:local at the repo root starts a chain, deploys the protocol with the same DeployProtocol script and runs an indexer against it. Local addresses move whenever a contract's code changes, so read them back rather than copying them:

cat apps/contracts/deployments/31337/SlotFactory.json

CHAINS includes anvil only when NODE_ENV === "development". Bundlers inline NODE_ENV, so a production build drops it at compile time. The address maps themselves are unconditional.

Naming a module

A slot stores its module as a bare address. @0xslots/contracts ships a small catalogue of the modules a client can name, per chain:

import { findKnownModule, knownModules } from "@0xslots/contracts";
 
const known = findKnownModule(chainId, slot.module);
// known?.name — "AdLand", "Minimum tenure", or undefined for a module this
// client does not recognise

A module appears in knownModules on exactly the chains it is deployed to, because the entries are derived from the deployment records. An unknown address reads back as undefined rather than a guess — the honest answer for a module anyone can deploy.

There is one MinimumTenureModule per chain. The window a slot enforces is its own settings, so a single deployment serves every duration.

ABIs and addresses

import {
  slotAbi,
  slotFactoryAbi,
  offerBookAbi,
  minimumTenureModuleAbi,
  adLandAbi,
  slotCollectiveAbi,
  slotCollectiveFactoryAbi,
  slotBoundNftAbi,
  slotBoundNftFactoryAbi,
  slotBoundNftWrapperAbi,
} from "@0xslots/contracts";
 
import {
  slotFactoryAddress,
  offerBookAddress,
  minimumTenureModuleAddress,
  adLandAddress,
  slotCollectiveFactoryAddress,
  slotImplementationAddress, // the beacon implementation, not a slot
  deployBlockOf,             // the lower bound for historical log queries
} from "@0xslots/contracts";
 
const factory = slotFactoryAddress[chainId]; // Address | undefined