Getting Started
Installation
Create a client
SlotsClient reads and writes slots directly over RPC. Addresses come from
@0xslots/contracts, which only lists chains the protocol is deployed on — see
Deployments.
import { SlotsClient } from "@0xslots/sdk";
import { slotFactoryAddress } from "@0xslots/contracts";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { anvil } from "viem/chains";
const publicClient = createPublicClient({ chain: anvil, transport: http() });
const walletClient = createWalletClient({
account: privateKeyToAccount("0x..."),
chain: anvil,
transport: http(),
});
const client = new SlotsClient({
factoryAddress: slotFactoryAddress[anvil.id], // only createSlot and collectAll need it
publicClient,
walletClient, // omit for read-only use
});Read a slot
const state = await client.slotState("0x...");
// occupant, price, deposit, taxOwed, isInsolvent, secondsUntilLiquidation,
// taxRateBps, recipient, module, settings, scopes, pending terms, …
const cost = await client.quoteBuy("0x...", buyer, depositAmount); // what buy() chargesslotState is one SlotLens.getSlotInfo call, so every figure is from the same block.
Create a slot
import { zeroAddress } from "viem";
await client.createSlot({
currency: zeroAddress, // native ETH; or an ERC-20 address
manager: zeroAddress, // nothing is mutable, so there is no manager
mutableTax: false,
mutableRecipient: false,
mutableModule: false,
taxTerms: {
recipient: "0x...",
rateBps: 100, // 1% of the price per 30 days
minRunwaySeconds: 86_400, // a buyer must fund one day of tax
},
// moduleTerms omitted: no module
});To install a module, pass moduleTerms: { module, settings } — see
Modules.
Write actions
import { parseEther } from "viem";
// `account` is who gets seated — it need not be the signer.
// ERC-20 approval is handled, and the quoted total is sent as `maxPayment`.
await client.buy({
slot: "0x...",
account: "0x...",
selfAssessedPrice: parseEther("10"),
depositAmount: parseEther("1"),
});
await client.selfAssess(slot, parseEther("20"));
await client.topUp(slot, parseEther("0.5"));
await client.withdraw(slot, parseEther("0.1"));
await client.release(slot);
await client.collect(slot); // permissionless
await client.liquidate(slot); // permissionless — no bounty, the slot itself is the rewardReact
import { useQuery } from "@tanstack/react-query";
import { useAccount } from "wagmi";
import { useSlotAction, useSlotsClient } from "@0xslots/sdk/react";
function SlotView({ address }: { address: `0x${string}` }) {
const { address: me } = useAccount();
const client = useSlotsClient();
const { data: slot, isLoading } = useQuery({
queryKey: ["slot", address],
queryFn: () => client.slotState(address),
});
const { buy, busy } = useSlotAction({
onSuccess: (label, hash) => console.log(`${label}: ${hash}`),
});
if (isLoading || !slot || !me) return null;
return (
<button
disabled={busy}
onClick={() =>
buy({
slot: address,
account: me,
selfAssessedPrice: 5_000_000n,
depositAmount: 1_000_000n,
})
}
>
Buy (current price {slot.price.toString()})
</button>
);
}