Slot
One slot, one contract. Deployed as a BeaconProxy by the SlotFactory.
Occupant operations
function buy(
address account,
uint256 depositAmount,
uint256 selfAssessedPrice
) external;Takes the slot. Pays the current occupant their declared price, plus
depositAmount into escrow. account is who becomes occupant — it need not be
msg.sender, so you can buy on someone's behalf.
Vacant slots cost only the deposit; there is no incumbent to pay.
function selfAssess(uint256 newPrice) external; // occupant or operator
function topUp(uint256 amount) external; // anyone may fund a slot
function withdraw(uint256 amount) external; // occupant only
function release() external; // occupant only, always allowedtopUp is deliberately open: anyone can keep a slot solvent, including the
recipient who benefits from it staying occupied.
withdraw takes back surplus deposit, subject to minDepositSeconds. release
returns the remaining deposit and vacates.
Permissionless operations
function collect() external; // push accrued tax to the recipient
function liquidate() external; // remove an insolvent occupant, earn the bounty
function claim(address account) external;Anyone may call these. liquidate requires the deposit to be fully exhausted;
the bounty is liquidationBountyBps of collected tax, not of anyone's
deposit.
claim is the escape hatch for a refund that could not be pushed — a
blocklisting token, a contract that reverts on receipt. Rather than let that
brick the slot, the amount is credited to withdrawableOf and claimed later.
Delegation
function setOperator(address operator, bool approved) external;
mapping(address => mapping(address => bool)) public isOperator;Lets another address manage your price without holding the slot. Keyed by occupant, so approvals survive leaving and re-entering.
Manager operations
Only available for whatever was declared mutable at creation.
function proposeTaxUpdate(uint256 newPct) external;
function proposeModuleUpdate(address newModule) external;
function proposePolicyUpdate(address newPolicy) external;
function cancelPendingUpdates() external;
function setLiquidationBounty(uint256 newBps) external;Changes are proposed, not applied. They take effect at the next occupancy change, so the terms cannot shift under someone mid-tenancy.
Reading state
function getSlotInfo() external view returns (SlotInfo memory);Everything in one call — identity, live financials, module metadata, pending updates, occupancy policy. Prefer it over assembling individual getters, which costs more round trips and can tear across a state change.
Individual getters exist too:
function occupant() external view returns (address);
function price() external view returns (uint256);
function deposit() external view returns (uint256);
function taxOwed() external view returns (uint256);
function isInsolvent() external view returns (bool);
function isVacant() external view returns (bool);Tax
Tax accrues per second at taxPercentage basis points per 30 days:
owed = price × taxPercentage × elapsed / (30 days × 10_000)
It is charged against the deposit at the start of every mutating call, so state is always settled before anything else happens. Nothing runs on a timer.
A charge is capped by the remaining deposit. An occupant who has run dry pays less than they owe and becomes liquidatable — which is why accounting must use amounts actually paid rather than amounts theoretically owed.
Events
| Event | Fires on |
|---|---|
Bought | occupancy transfer |
Released | occupant leaves |
Liquidated | insolvent occupant removed |
PriceUpdated | self-assessment |
Deposited / Withdrawn | deposit changes |
Settled | tax charged — amounts only |
TaxPaid | tax charged — with the payer |
TaxCollected | tax pushed to the recipient |
TaxPaid is the one to reduce over for per-address accounting. Settled
carries the same amounts but not who paid them, so it cannot be attributed.
TaxPaid always fires, including when a module hook fails.