v1 → v2
v1 exported 93 symbols. v2 exports 131. Nothing was removed. 16 of the originals changed shape, 77 are byte-identical, and 38 are new.
This page is computed. One side is a frozen snapshot of v1’s public surface, taken from git history before the port and never hand-edited; the other is read from the SDK’s live types at build time. The explanations are written — and each one is checked against the computed diff, so a note that stops being true fails the build.
This is the SDK surface. For what changed in the contracts — settlement in ERC-20, tenancy that survives displacement, the tiered mutability rules — the protocol spec opens with that table.
What breaks
These 16 exist in both versions with a different type. A v1 call site that touches one of them will not compile against v2 — which is the intended outcome: every change below is one where silently continuing to work would have been worse.
buildBuyFromfunctionsettlementTokenis now a REQUIRED argument. There is no default on purpose: a native-shaped call against a token-settled site revertsNativeNotAccepted, and the identical call is correct on a native site — so a default would be silently right half the time and would fail first on a real stablecoin deployment.v1(site: Address, context: { slot: { key: string; charged: bigint; }; expectedTerms: Hex; now: bigint; }, options?: Pick<BuildBuyOptions, "maxPrice" | "slippageBps" | "deadline" | "deadlineSecs" | "recipient">) => CallRequest<"buy", [`0x${string}`, bigint, `0x${string}`, bigint]> | CallRequest<"buyFor", [`0x${string}`, `0x${string}`, bigint, `0x${string}`, bigint]>v2(site: Address, context: { slot: { key: string; charged: bigint; }; expectedTerms: Hex; now: bigint; }, settlementToken: Address, options?: Pick<BuildBuyOptions, "maxPrice" | "slippageBps" | "deadline" | "deadlineSecs" | "recipient">) => CallRequest<"buy", [`0x${string}`, bigint, `0x${string}`, bigint]> | CallRequest<"buyFor", [`0x${string}`, `0x${string}`, bigint, `0x${string}`, bigint]>BuildBuyOptionsinterfacechargedis the quoted price, explicitly NOTnetCost— netting the inherited rent stream would underpay the call and revert.v1interface BuildBuyOptions { site: Address; key: string; /** * What the caller was quoted — `SlotState.charged`. Used to derive `maxPrice` and `value` when * neither is given explicitly. */ charged: bigint; /** A freshly-read `encumbranceHash(key)` for the state the buyer was shown. Required. */ expectedTerms: Hex; /** Overrides the derived `charged + slippage`. */ maxPrice?: bigint; /** Basis points of headroom over `charged`. Ignored when `maxPrice` is given. */ slippageBps?: bigint; /** Absolute unix seconds. Overrides `deadlineSecs`. */ deadline?: bigint; /** Seconds from `now`. */ deadlineSecs?: bigint; /** * The clock the deadline is measured from. * * **This should be the CHAIN's clock, not the client's.** `buy` compares its deadline against * `block.timestamp`; those are close on most chains and are not the same number. A user whose * system clock is ten minutes slow, or a chain whose timestamps lag, gets `DeadlineExpired` on * every purchase with nothing in the UI able to explain why. `buildBuyFrom` supplies it from a * pinned block; this defaults to `Date.now()` only so the builder stays usable without a client. */ now?: bigint; /** * Who ends up OWNING the slot. Omit and the payer owns it (`buy`); supply it and the payer is * merely paying (`buyFor`). */ recipient?: Address; }v2interface BuildBuyOptions { site: Address; key: string; /** * What the caller was quoted — `SlotState.charged`. Used to derive `maxPrice` and `value` when * neither is given explicitly. * * **Not `netCost`.** The contract charges the gross price; the inherited rent arrives separately * as it accrues. Quoting the net figure here underfunds the call and reverts. */ charged: bigint; /** `0x0` for native, the ERC-20 address otherwise. See the module note — there is no default. */ settlementToken: Address; /** A freshly-read `encumbranceHash(key)` for the state the buyer was shown. Required. */ expectedTerms: Hex; /** Overrides the derived `charged + slippage`. */ maxPrice?: bigint; /** Basis points of headroom over `charged`. Ignored when `maxPrice` is given. */ slippageBps?: bigint; /** Absolute unix seconds. Overrides `deadlineSecs`. */ deadline?: bigint; /** Seconds from `now`. */ deadlineSecs?: bigint; /** * The clock the deadline is measured from. * * **This should be the CHAIN's clock, not the client's.** `buy` compares its deadline against * `block.timestamp`; those are close on most chains and are not the same number. A user whose * system clock is ten minutes slow, or a chain whose timestamps lag, gets `DeadlineExpired` on * every purchase with nothing in the UI able to explain why. `buildBuyFrom` supplies it from a * pinned block; this defaults to `Date.now()` only so the builder stays usable without a client. */ now?: bigint; /** * Who ends up OWNING the slot. Omit and the payer owns it (`buy`); supply it and the payer is * merely paying (`buyFor`). */ recipient?: Address; }BuildCreateSiteOptionsinterfaceSite creation now takes the settlement token, the rental terms and the floor policy. All frozen or ratcheted at issue.
v1interface BuildCreateSiteOptions { factory: Address; name: string; symbol: string; baseTokenURI: string; treasury: Address; economics: SiteEconomicsConfig; /** Registered in the same transaction as the deploy, so one tx produces a working board. */ slots?: Readonly<Record<string, bigint>>; royaltyBps?: bigint; /** §7.5. Leave off unless the site genuinely wants a free-for-all key namespace. */ openRegistration?: boolean; /** Floor an auto-registered key gets. Required when `openRegistration` is on. */ defaultFloor?: bigint; /** * Who owns the SITE. Omit and the sender owns it (`createSite`); supply it and the sender is * merely paying (`createSiteFor`) — §10.8, the same trap as `buyFor` one level up. */ owner?: Address; }v2interface BuildCreateSiteOptions { factory: Address; name: string; symbol: string; baseTokenURI: string; treasury: Address; /** * `0x0` for native, or the ERC-20 to settle in. **Frozen at deploy with no setter, ever** — * changing it would orphan every balance on the ledger. It also fixes `minFloor`, which is derived * from the token's decimals. */ settlementToken: Address; economics: SiteEconomicsConfig; rentals: SiteRentalConfig; floorPolicy: SiteFloorPolicyConfig; /** Registered in the same transaction as the deploy, so one tx produces a working board. */ slots?: Readonly<Record<string, bigint>>; royaltyBps?: bigint; /** §7.5. Leave off unless the site genuinely wants a free-for-all key namespace. */ openRegistration?: boolean; /** Floor an auto-registered key gets. Required when `openRegistration` is on. */ defaultFloor?: bigint; /** * Who owns the SITE. Omit and the sender owns it (`createSite`); supply it and the sender is * merely paying (`createSiteFor`) — §10.8, the same trap as `buyFor` one level up. */ owner?: Address; }DefineSiteInputinterfacereaderis required. It is not resolved from the chain id, because the reader is meant to be replaceable and a config that silently picks one for you is a config that breaks when it is replaced.v1interface DefineSiteInput { /** The clone's address, returned by `createSite()`. */ address: Address; chain: Chain; slots: Record<string, SlotDefinitionInput>; /** * Where verified content bytes are fetched from, given a CID. * * Defaults to a public IPFS gateway, which is fine for text and is the scaffold's zero-credential * default. A site with its own storage passes its own resolver — see §3's three tiers. Whatever * this returns, the bytes are hash-checked before they render, so a hostile gateway can cause a * blank slot but never a substituted one. */ contentUrl?: (cid: string) => string; }v2interface DefineSiteInput { /** The clone's address, returned by `createSite()`. */ address: Address; /** * The `SlotReader` every read goes through (§11.4). * * Required rather than resolved from a chain table, because the reader is deliberately * REPLACEABLE — pinning one in this package would mean a version bump to adopt a new view, and * there is no v2 deployment to resolve one from yet in any case. */ reader: Address; chain: Chain; slots: Record<string, SlotDefinitionInput>; /** * Decimals of the site's settlement token, from `readSiteTerms().settlementToken`. * * **Required for any site that does not settle natively.** Defaults to 18, which is right for * native and wrong by 1e12 for 6-decimal USDG — see `DEFAULT_SETTLEMENT_DECIMALS`. */ decimals?: number; /** * Where verified content bytes are fetched from, given a CID. * * Defaults to a public IPFS gateway, which is fine for text and is the scaffold's zero-credential * default. A site with its own storage passes its own resolver — see §3's three tiers. Whatever * this returns, the bytes are hash-checked before they render, so a hostile gateway can cause a * blank slot but never a substituted one. */ contentUrl?: (cid: string) => string; }DeploymentinterfaceCarries
version: 1 | 2, so an address ledger can no longer be read as the wrong generation.v1interface Deployment { chainId: number; /** The audited `SlotSite` every site on this chain is a clone of. */ implementation: Address; /** `createSite` / `createSiteFor` live here. Not frozen — a new factory can point at the same * implementation without stranding anyone (§10.8). */ factory: Address; /** Basis points of every buy taken by the protocol. An `immutable` in the implementation, so no * clone can strip it. */ protocolBps: bigint; explorer: string; }v2interface Deployment { chainId: number; /** Which contract generation this deployment is. v2 adds rentals, the ask and token settlement. */ version: 1 | 2; /** The audited `SlotSite` every site on this chain is a clone of. */ implementation: Address; /** `createSite` / `createSiteFor` live here. Not frozen — a new factory can point at the same * implementation without stranding anyone (§10.8). */ factory: Address; /** * The convenience-view periphery every read goes through. **v2 only**, and deliberately * replaceable: a new reader can be deployed and adopted without touching a single site (§11.4). * Absent on a v1 deployment, which carried its batch view inside the site itself. */ reader?: Address; /** * `RentalsLib`, recorded for auditability. v2 only. The implementation delegatecalls an address * baked into its own bytecode, so a wrong link is arbitrary behaviour with no revert — a deploy * script must assert this matches the library it just deployed. */ rentalsLib?: Address; /** Basis points of every buy taken by the protocol. An `immutable` in the implementation, so no * clone can strip it. */ protocolBps: bigint; explorer: string; }EXAMPLE_SITESconstThe four reference boards were redeployed as v2 clones. The v1 addresses survive as
EXAMPLE_SITES_V1, unreadable by this SDK.v1{ readonly dispatch: "0x895Fb4Ba710b0f495983A582b5c9013ccC33736c"; readonly devconf: "0xA7f8Dba26F82cc1deD9a63F28932eC87128834F0"; readonly remoteroles: "0x8c0d776ece615Ba01bE5038b95aA9Df5F3411f99"; readonly vaultline: "0xE41addf32313915F98b6cE5c63B6db8d0D6B092e"; }v2{ readonly dispatch: "0xE0d1cF918a53eB92Ec672fa93530601ef4758Aa7"; readonly devconf: "0xB1A7262F3eD2e54F4d950c5Ae76A24D726156932"; readonly remoteroles: "0xa7aea56116E6d478B501E2d75828A286e9E7C489"; readonly vaultline: "0x67E2A12B023c7715Ae98ea30563Bb86BEE57D89a"; }readBuyContextfunctionSame
SiteRefchange.v1(client: PublicClient, site: Address, key: string) => Promise<BuyContext>v2(client: PublicClient, ref: SiteRef, key: string) => Promise<BuyContext>readSiteTermsfunctionSame
SiteRefchange, and the terms it returns now carry rent economics and the availability flag.v1(client: PublicClient, site: Address, blockNumber?: bigint) => Promise<SiteTerms>v2(client: PublicClient, ref: SiteRef, blockNumber?: bigint) => Promise<SiteTerms>readSlotfunctionSame
SiteRefchange asreadSlots.v1(client: PublicClient, site: Address, key: string) => Promise<SlotState>v2(client: PublicClient, ref: SiteRef, key: string) => Promise<SlotState>readSlotsfunctionReads take a
SiteRef—{ site, reader }— instead of a bare site address. v1 read the board out of the site contract itself; v2 routes every read throughSlotReader, a separate and deliberately REPLACEABLE deployment, because view logic is where a frozen contract otherwise strands you. Also gained an optionalblockNumberfor pinned reads.v1(client: PublicClient, site: Address, keys: readonly string[]) => Promise<SlotState[]>v2(client: PublicClient, ref: SiteRef, keys: readonly string[], blockNumber?: bigint) => Promise<SlotState[]>SiteConfiginterfaceCarries
reader, a ready-maderef, and the settlement decimals the floors were parsed against.v1interface SiteConfig { address: Address; chain: Chain; /** Insertion-ordered, matching the config file, so a board reads in the order it was written. */ slots: SlotDefinition[]; slotsByKey: Record<string, SlotDefinition>; keys: string[]; contentUrl: (cid: string) => string; }v2interface SiteConfig { address: Address; reader: Address; /** `{ site, reader }`, ready to hand to any read. */ ref: SiteRef; chain: Chain; /** What the floors above were parsed against. Carried so a consumer can format them back. */ decimals: number; /** Insertion-ordered, matching the config file, so a board reads in the order it was written. */ slots: SlotDefinition[]; slotsByKey: Record<string, SlotDefinition>; keys: string[]; contentUrl: (cid: string) => string; }SiteEconomicsinterfacedecayBpsandmaxDecayWeeksare nowreversionBpsandmaxReversionWeeks. Not cosmetic: the price is bounded below by the publisher's floor, so it is mean reversion of the takeover price, not loss of value, and "decay" told holders the wrong story about what they own.v1interface SiteEconomics { takeBps: bigint; payoutBps: bigint; decayBps: bigint; maxDecayWeeks: bigint; protocolBps: bigint; }v2interface SiteEconomics { takeBps: bigint; payoutBps: bigint; reversionBps: bigint; maxReversionWeeks: bigint; protocolBps: bigint; }SiteEconomicsConfiginterfaceSame reversion rename as
SiteEconomics, on the input side.v1interface SiteEconomicsConfig { /** 1.4x is `14_000`. Must exceed `payoutBps + protocolBps`, and is capped at `30_000`. */ takeBps: bigint; /** 1.15x is `11_500`. Must be at least `10_000` — a site can only monetize the spread. */ payoutBps: bigint; /** Per-week decay. `9_000` is 0.9; `10_000` is no decay at all. */ decayBps: bigint; /** Capped at 52. §7.7 recommends websitekit sites default SHORTER than v1's 52. */ maxDecayWeeks: bigint; /** Minimum seconds between takes on one slot. Capped at 7 days. */ cooldownSecs: bigint; }v2interface SiteEconomicsConfig { /** 1.4x is `14_000`. Must exceed `payoutBps + protocolBps`, and is capped at `30_000`. */ takeBps: bigint; /** 1.15x is `11_500`. Must be at least `10_000` — a site can only monetize the spread. */ payoutBps: bigint; /** Per-week reversion. `9_700` is 0.97 (the Standard profile); `10_000` is none at all. */ reversionBps: bigint; /** Capped at 52. */ maxReversionWeeks: bigint; /** Minimum seconds between takes on one slot. Capped at 7 days. */ cooldownSecs: bigint; }SiteTermsinterfaceThe same rename, plus rent terms, the settlement token, and per-slot availability.
v1interface SiteTerms { implementationVersion: bigint; takeBps: bigint; payoutBps: bigint; decayBps: bigint; maxDecayWeeks: bigint; cooldownSecs: bigint; protocolBps: bigint; paused: boolean; openRegistration: boolean; defaultFloor: bigint; treasury: Address; royaltyBps: bigint; }v2interface SiteTerms { implementationVersion: bigint; takeBps: bigint; payoutBps: bigint; /** Per-week reversion. `10_000` is none at all. v1 called this `decayBps`. */ reversionBps: bigint; maxReversionWeeks: bigint; cooldownSecs: bigint; protocolBps: bigint; protocolRentBps: bigint; /** The site's rent cut. Freely mutable — but a listing SNAPSHOTS it, so never price an existing * listing against this (§2.5.1). */ siteRentBps: bigint; minRentBps: bigint; maxAskBps: bigint; floorDeltaBps: bigint; floorChangeCooldown: bigint; /** The smallest legal floor, derived from the settlement token's decimals (§11.2). */ minFloor: bigint; maxRentalTerm: bigint; /** `0x0` means native settlement. Frozen — there is no setter, ever. */ settlementToken: Address; paused: boolean; openRegistration: boolean; /** Set by the first claim in a site's life, and never unset. Gates the ratchet rules. */ termsLocked: boolean; defaultFloor: bigint; treasury: Address; royaltyBps: bigint; }SlotDefinitionInputinterfaceA floor is a decimal string in the SITE'S SETTLEMENT CURRENCY, not in ether.
minFloorderives from the token's decimals — 1e14 native, 100 units on a 6-decimal stablecoin — soparseEtheron a floor is wrong by 1e12 against USDG and silently correct on a native site. UseparseFloor.v1interface SlotDefinitionInput { kind: SlotKindName; /** * Ether as a decimal string — `'0.002'`, not `2000000000000000n`. * * A string because this is the one number a builder types by hand, and `0.002` in a config file * read as a JS float is exactly the class of money bug the rest of this codebase refuses to have. * `parseEther` converts once, here. */ floor: string; }v2interface SlotDefinitionInput { kind: SlotKindName; /** * A decimal string in the site's SETTLEMENT currency — `'0.002'`, not `2000000000000000n`. * * A string because this is the one number a builder types by hand, and `0.002` in a config file * read as a JS float is exactly the class of money bug the rest of this codebase refuses to have. * It is converted once, here, against the site's `decimals`. */ floor: string; }SlotStateinterfaceGained the rental state, the availability flag, and the net cost of an encumbered position.
v1interface SlotState { /** The dotted key from the site's config, e.g. `hero.headline`. */ key: string; /** `keccak256(utf8(key))` — what the contract stores. */ keyHash: Hex; /** `null` when unclaimed, rather than the zero address. */ owner: Address | null; /** * `null` when the slot has never been edited. The zero hash is the contract's "unset" value and * would otherwise be handed to a fetcher that would 404 on it. */ contentHash: Hex | null; /** The site owner's configured floor. */ floor: bigint; /** What a first claim costs right now — `max(decayed, floor)`. */ effectiveFloor: bigint; /** What displacing the current owner costs right now. */ price: bigint; /** What the current owner actually paid. */ lastPrice: bigint; lastPurchaseTs: bigint; /** Bumped on every edit; what a client caches content against. */ version: number; takes: number; /** False for a key the site owner has not registered. Such a slot cannot be bought (§7.5). */ registered: boolean; /** Convenience for the overwhelmingly common branch. */ isUnclaimed: boolean; /** What THIS buyer would be charged — `effectiveFloor` on a claim, `price` on a take. */ charged: bigint; }v2interface SlotState { /** The dotted key from the site's config, e.g. `hero.headline`. */ key: string; /** `keccak256(utf8(key))` — what the contract stores. */ keyHash: Hex; /** `null` when unclaimed, rather than the zero address. */ owner: Address | null; /** * `null` when the slot has never been edited. The zero hash is the contract's "unset" value and * would otherwise be handed to a fetcher that would 404 on it. */ contentHash: Hex | null; /** The site owner's configured floor. */ floor: bigint; /** What a first claim costs right now — `max(reverted, floor)`. */ effectiveFloor: bigint; /** What the current owner actually paid. */ lastPrice: bigint; /** * The owner's posted ask, or `0n`. §3: this is the **reversion base** — the price the slot * reverts *from* — not a list price and not what a buyer pays. Cleared on every sale. */ askFloor: bigint; lastPurchaseTs: bigint; /** Bumped on every edit; what a client caches content against. */ version: number; takes: number; /** False for a key the site owner has not registered. Such a slot cannot be bought (§7.5). */ registered: boolean; /** * The publisher's listing toggle (§10.4). Only meaningful while unclaimed: `false` means the * claim path reverts `SlotUnavailable`, so a board should render the slot as "not currently * offered" rather than claimable. Takes and rentals on an OWNED position ignore it — the flag is * not a lever over positions people already paid for. */ isAvailable: boolean; /** Convenience for the overwhelmingly common branch. */ isUnclaimed: boolean; /** What THIS buyer would be charged — `effectiveFloor` on a claim, the take price on a take. */ charged: bigint; // --- tenancy (§2) --- /** The LIVE tenant, or `null`. An expired-but-uncleared term reads as `null`, not as a tenancy. */ tenant: Address | null; /** Raw expiry. Non-zero on a lapsed-but-uncleared term, so it is not a liveness signal — use * `tenant` or `isRented` for that. */ rentalExpiry: bigint; /** * Escrowed rent not yet earned — **what a buyer inherits** (§2.4.2). * * This is the number that makes an encumbered position legible. Without it a rented slot reads as * "buy something you cannot use"; with it, the buyer sees they are paying `charged` for an asset * that hands them `unaccruedRent` back over the remaining term. */ unaccruedRent: bigint; /** * `charged - unaccruedRent`. The number a confirm dialog should lead with, and the reason §2.4.2 * calls surfacing it the highest-value client change in the release. * * Clamped at zero: a position can carry more unaccrued rent than it costs to take, and a negative * "cost" is a presentation problem rather than a refund. `isFreeCarry` flags that case rather than * hiding it. */ netCost: bigint; /** True when the inherited rent stream exceeds the purchase price outright. */ isFreeCarry: boolean; /** Whether a live tenancy is in force right now. */ isRented: boolean; /** Listed rent, per DAY, or `0n` when not listed. Durations elsewhere are in SECONDS (§2.5.3). */ ratePerDay: bigint; /** The longest term the owner will accept, in seconds. `0n` when not listed. */ maxDurationSecs: bigint; /** Whether the position can be rented right now. */ isListed: boolean; }
New in v2
38 additions, grouped the way the SDK’s entry point groups them. Full signatures are on the API reference.
Writes 20
buildApproveSettlementfunction(settlementToken: Address, site: Address, amount: bigint) => CallRequest<"approve", [Address, bigint]>buildClaimRentfunction(site: Address, key: string) => CallRequest<"claimRent", [Hex]>buildDelistfunction(site: Address, key: string) => CallRequest<"listForRent", [Hex, bigint, bigint]>buildEndRentalfunction(site: Address, key: string) => CallRequest<"endRental", [Hex]>buildExtendRentalfunction(options: BuildRentOptions) => CallRequest<"extendRental", [Hex, bigint, bigint]>buildListForRentfunction(site: Address, key: string, ratePerDay: bigint, maxDurationSecs: bigint) => CallRequest<"listForRent", [Hex, bigint, bigint]>buildRentfunction(options: BuildRentOptions) => CallRequest<"rent", [Hex, bigint, bigint]>BuildRentOptionsinterfaceinterface BuildRentOptions { site: Address; key: string; /** Seconds. At least one hour, at most the listing's `maxDurationSecs`. */ durationSecs: bigint; /** * The rate the tenant was shown, from `readListing().ratePerDay`. * * Required and exact — the contract reverts `RateChanged` on any mismatch. This is the rental * path's `maxPrice`: without it an owner front-runs a rental by raising the rate between the quote * and the transaction. */ expectedRatePerDay: bigint; settlementToken: Address; /** What the term costs. Compute with `quoteRent`/`rentCost` from `./rentals`. */ cost: bigint; }buildSetAskfunction(site: Address, key: string, askFloor: bigint) => CallRequest<"setAsk", [Hex, bigint]>buildSetAvailabilityfunction(site: Address, keys: readonly string[], available: boolean) => CallRequest<"setAvailability", [Hex[], boolean]>buildSetBaseTokenURIfunction(site: Address, uri: string) => CallRequest<"setBaseTokenURI", [string]>buildSetEconomicsfunction(site: Address, economics: SiteEconomicsConfig) => CallRequest<"setEconomics", [bigint, bigint, bigint, bigint, bigint]>buildSetFloorPolicyfunction(site: Address, policy: { floorDeltaBps: bigint; floorChangeCooldown: bigint; maxAskBps: bigint; }) => CallRequest<"setFloorPolicy", [bigint, bigint, bigint]>buildSetRentalTermsfunction(site: Address, terms: { siteRentBps: bigint; maxRentalTerm: bigint; minRentBps: bigint; }) => CallRequest<"setRentalTerms", [bigint, bigint, bigint]>buildSweepTreasuryfunction(site: Address) => CallRequest<"sweepTreasury", []>buildWithdrawTreasuryfunction(site: Address, amount: bigint) => CallRequest<"withdrawTreasury", [bigint]>ERC20_ABIconstreadonly [{ readonly type: "function"; readonly name: "approve"; readonly stateMutability: "nonpayable"; readonly inputs: readonly [{ readonly name: "spender"; readonly type: "address"; }, { readonly name: "amount"; readonly type: "uint256"; }]; readonly outputs: readonly [{ readonly name: ""; readonly type: "bool"; }]; }, { readonly type: "function"; readonly name: "allowance"; readonly stateMutability: "view"; readonly inputs: readonly [{ readonly name: "owner"; readonly type: "address"; }, { readonly name: "spender"; readonly type: "address"; }]; readonly outputs: readonly [{ readonly name: ""; readonly type: "uint256"; }]; }]isNativeSettlementfunction(settlementToken: Address) => booleanSiteFloorPolicyConfiginterfaceinterface SiteFloorPolicyConfig { /** How far one `setFloor` may move a floor. `1..2_000`. */ floorDeltaBps: bigint; /** Seconds between floor changes. At least 24 hours. */ floorChangeCooldown: bigint; /** Ceiling on an owner's ask, in bps of `max(lastPrice, floor)`. At least `10_000`. */ maxAskBps: bigint; }SiteRentalConfiginterfaceinterface SiteRentalConfig { /** The site's cut of rent. `siteRentBps + protocolRentBps` must land in `[1_000, 4_000]`. */ siteRentBps: bigint; /** The longest term the site will allow, in seconds. One hour to 365 days. */ maxRentalTerm: bigint; /** Anti-poisoning rate floor, in bps of a position's effective floor, per day. `1..10_000`. */ minRentBps: bigint; }
Site config 2
DEFAULT_SETTLEMENT_DECIMALSconst18parseFloorfunction(amount: string, decimals?: number) => bigint
Deployed addresses (§7.9 3
DEMO_SITE_V1const`0x${string}`EXAMPLE_SITES_V1const{ readonly dispatch: "0x895Fb4Ba710b0f495983A582b5c9013ccC33736c"; readonly devconf: "0xA7f8Dba26F82cc1deD9a63F28932eC87128834F0"; readonly remoteroles: "0x8c0d776ece615Ba01bE5038b95aA9Df5F3411f99"; readonly vaultline: "0xE41addf32313915F98b6cE5c63B6db8d0D6B092e"; }SMOKE_TEST_SITEconst`0x${string}`
Reads 13
economicsFromTermsfunction(terms: SiteTerms) => SiteEconomicsisTokenSettledfunction(terms: Pick<SiteTerms, "settlementToken">) => booleanListinginterfaceinterface Listing { ratePerDay: bigint; maxDurationSecs: bigint; /** * The site's cut, SNAPSHOTTED when the listing was written. **Price an existing listing against * this, never against the site's current `siteRentBps`** — the snapshot is what lets that * parameter be freely mutable without letting a publisher advertise 0% and raise it before anyone * rents (§2.5.1). */ feeBps: bigint; isListed: boolean; }readAccruedRentfunction(client: PublicClient, site: Address, key: string) => Promise<bigint>readListingfunction(client: PublicClient, site: Address, key: string) => Promise<Listing>readRentalfunction(client: PublicClient, site: Address, key: string, nowSecs?: bigint) => Promise<Rental>readSlotsMultifunction(client: PublicClient, reader: Address, boards: ReadonlyArray<{ site: Address; keys: readonly string[]; }>, blockNumber?: bigint) => Promise<SlotState[][]>readUnaccruedRentfunction(client: PublicClient, site: Address, key: string) => Promise<bigint>Rentalinterfaceinterface Rental { tenant: Address | null; start: bigint; expiry: bigint; /** Net of the fee split — what actually streams to the owner over the term. */ prepaid: bigint; /** How much of `prepaid` has already been swept into the pull ledger. */ claimed: bigint; /** The owner's content hash, restored by `endRental` when the term lapses. */ ownerHashSnapshot: Hex; /** Whether the term is live right now. */ isActive: boolean; /** Whether the term has lapsed but not yet been cleared — see `buildEndRental`. */ isLapsed: boolean; }RENTALS_LIB_ABIconstAbiSITE_EVENTS_ABIconstAbiSiteRefinterfaceinterface SiteRef { site: Address; reader: Address; }SLOT_READER_ABIconstAbi
What stayed
Every v1 export still exists in v2, and 77 of them are unchanged down to the byte. The keyed slot model, the pricing math, the pull ledger, the recipient-taking builders and clone-per-site fund isolation were not up for renegotiation — v2 is additive at the surface and breaking only where a type had to admit something new.
The v1 contracts are a different story: they are deleted, and the boards cloned from them are unreadable by this SDK. If you have a deployed v1 board, its ABIs are in git history — nothing in @websitekit/sdk can talk to it.