Skip to content
0xSlots

Deployments

Base Mainnet — 8453

ContractAddress
SlotFactory0xbf2F890E8F5CCCB3A1D7c5030dBC1843B9E36B0e
MinimumTenurePolicyFactory0x6C90Ca1A6ac6bBC0e4B48cc3CF589F6A3c2b30a5
MinimumPricePolicyFactory0xFA64C88960c0aaC55279d42131A5B7fB57e0Ff1A
FeedPostModule0xe92BE44E3D77be84E2aC4D6da9FFDaC0FCa67f72
FeedRouter0xCfFA953EfC77591463a9560211bC783b5aaF3A4a
FeedSocialGroup0x5b524d7A1E7449963c42aEaFfAE751573e22F314

Base Sepolia — 84532

ContractAddress
SlotFactory0x6D87C1647f228Baf8DE0374FCd7FdEBF6900fdFF
SlotCollectiveFactory0x03825eA2529e9eA2d5aDFf9DBc3773cDE61Da43d
MinimumTenurePolicyFactory0x2a399E4D93d9b7Ffa8367894A39859013B214E4a
MinimumPricePolicyFactory0x958088c4Afb2cf3E4c7C23560B57fCb64dfC6551
BatchCollector0xd3c7090C2F89c5132C3f91DD1da4bCffEAe10e13
FeedPostModule0x17b663b7C779B64f339ab916aB734A6a4f0b075E
FeedRouter0x93E67283Cbb4bE7b86FeBbb9620e72777715C710
FeedSocialGroup0xC664a125F58cEc92d041c73c58388e58b7b5fE5D
FeedHub0xE4c0c374E3233b5174a1600AF1321cDa9b6B5cF8
ERC721Slots0x65e88189ac09527c5F7da0296ef33C77E5a6BE27

Collectives are Base Sepolia only for now — base is deliberately absent from slotCollectiveFactoryAddress, and the app derives its "not deployed here" message straight from those keys.

Local anvil — 31337

pnpm dev:local at the repo root starts a chain, deploys the protocol and runs an indexer against it. Both addresses are pinned — they survive edits to the Solidity, which a plain CREATE2 would not, since CREATE2 hashes the init code and so moves whenever the contract changes. See LocalBootstrap.sol.

ContractAddress
SlotFactory0x78F614D6e3489a90BD2584D2ab1D90F5C35722F6
SlotCollectiveFactory0x60E7C43423f7aCD6a70d5a1eFd688558a391Bb6d

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 table itself is unconditional — it is only data, and a consumer that knows it wants the local factory can still ask for it by id.

Resolving a policy

Policy factories are listed in the order a resolver should try them. Ask each one whether an address is theirs until one says yes:

import { POLICY_FACTORIES } from "@0xslots/contracts";
 
for (const factory of POLICY_FACTORIES[chainId] ?? []) {
  if (await verify(factory, policyAddress)) {
    // genuine policy — read its terms and format them
  }
}

POLICY_FACTORIES holds more than the two current factories. A policy is immutable and lives at a CREATE2 address derived from its init code, so every redeploy of a factory strands the policies the previous one minted at addresses only that older factory can claim. Superseded factories therefore stay in the list, ordered current-first, so existing slots keep resolving:

chainsuperseded factorywhy it moved
base0xE322cDADB8fd511788F0fA25BffD794b7A946125Tenure, pre-metadata-rename
base0xe218F2e710D2B686fD4524236F3B79EC06E92091Price, pre-metadata-rename
base0xF1cA0Fe72269AaEf1E5e34bfF484269f18e1b777Price, pre-native-ETH floors
base-sepolia0x51650AB1c3aBc6614A38c622A322535b16cD764eTenure, pre-metadata-rename
base-sepolia0x6a1F9D1F78CD63cd969d500994CB333027A22844Price, pre-metadata-rename
base-sepolia0x83d86EDBC62187180A4f94A3099a98ABaa1dfe0cPrice, pre-native-ETH floors

Factories predating IPolicyFactory are deliberately absent from that list. They have no verify(), so slots still pointing at their policies read as unrecognised — the honest answer rather than a guess.

ABIs

import {
  slotAbi,
  slotFactoryAbi,
  slotCollectiveAbi,
  slotCollectiveFactoryAbi,
  metadataModuleAbi,
  minimumTenurePolicyFactoryAbi,
} from "@0xslots/contracts";

Addresses are exported too, so you rarely need to hardcode one:

import {
  getSlotsHubAddress,
  slotCollectiveFactoryAddress,
  POLICY_FACTORIES,
} from "@0xslots/contracts";
 
const factory = getSlotsHubAddress(8453);