@manifoldxyz/dropkit 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/dropkit/dropkit/packages/react/dropkit/dist/status.cjs","../src/status/useDropStatus.ts","../src/status/core.ts","../src/status/read.ts","../src/status/DropCountdown.ts"],"names":[],"mappings":"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","file":"/home/runner/work/dropkit/dropkit/packages/react/dropkit/dist/status.cjs","sourcesContent":[null,"// useDropStatus: status for many drops at once, from Manifold data. Spec: README \"Drop status\".\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { PublicClient } from 'viem';\n\nimport { clockOffsetOf, statusOf, unknownStatus } from './core';\nimport { readSources, sourceKey, type ReadDeps } from './read';\nimport type { ExtensionEntry } from '../core/adapters/manifold-claim';\nimport type { DropStatus, Snapshot, StatusSource, UseDropStatusOptions } from './types';\n\nconst DEFAULT_REFRESH_MS = 30_000;\n/** Read again this long after a start or end time passes: the chain and the API trail the wall clock a little. */\nconst BOUNDARY_DELAY_MS = 1_000;\n/** setTimeout cannot wait longer than 2^31-1 ms. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\ntype Entry = { snapshot: Snapshot | null; updatedAt: number | null; error: Error | null };\n\nconst hidden = (): boolean => typeof document !== 'undefined' && document.hidden;\n\n/** The first time (unix seconds) after `nowSec` at which any entry changes phase, or null. */\nfunction nextBoundary(entries: Iterable<Entry>, nowSec: number): number | null {\n let next: number | null = null;\n for (const { snapshot } of entries) {\n if (!snapshot) continue;\n for (const t of [snapshot.startTime, snapshot.endTime]) if (t !== null && t > nowSec && (next === null || t < next)) next = t;\n }\n return next;\n}\n\n/**\n * Status of every source, aligned with `sources`. Reads once on mount and then every `refreshMs` (30 s) while the tab is visible,\n * on return to the tab, and right after the nearest start or end time passes.\n *\n * A failed read never changes what the page believes about a drop: the last good value stays with `stale: true` and `error` set;\n * with no good value yet the phase is 'unknown', never 'upcoming'.\n *\n * The array you pass may be a new literal on every render. Sources are keyed by their content, so that does not read again.\n */\nexport function useDropStatus(sources: readonly StatusSource[], options: UseDropStatusOptions = {}): DropStatus[] {\n const { refreshMs = DEFAULT_REFRESH_MS, enabled = true } = options;\n const keys = sources.map(sourceKey);\n const key = JSON.stringify(keys);\n // rpcUrls is usually an object literal: compare it by content so a new literal each render does not read again.\n const rpcKey = JSON.stringify(options.rpcUrls ?? null);\n\n const sourcesRef = useRef(sources);\n const optionsRef = useRef(options);\n // Updated after every commit (before the read effect below runs), never during render.\n useEffect(() => {\n sourcesRef.current = sources;\n optionsRef.current = options;\n });\n\n const [entries, setEntries] = useState<ReadonlyMap<string, Entry>>(() => new Map());\n const [clockOffsetMs, setClockOffsetMs] = useState(0);\n // Server-adjusted unix seconds the phases are computed at. Moves on each read and each boundary, so render stays pure.\n const [nowSec, setNowSec] = useState<number | null>(null);\n\n // Kept across reads: which extension holds each creator contract's claims, and which listings are final.\n const caches = useRef({ extensionCache: new Map<string, ExtensionEntry>(), finalCache: new Map<string, Snapshot>() });\n const refreshRef = useRef<() => Promise<void>>(() => Promise.resolve());\n const offsetRef = useRef(0);\n useEffect(() => {\n offsetRef.current = clockOffsetMs;\n }, [clockOffsetMs]);\n\n useEffect(() => {\n if (!enabled) return;\n let active = true;\n let inFlight: Promise<void> | null = null;\n const controller = new AbortController();\n\n const run = async (): Promise<void> => {\n const wanted = new Map<string, StatusSource>();\n sourcesRef.current.forEach((s) => wanted.set(sourceKey(s), s));\n const list = [...wanted.values()];\n const o = optionsRef.current;\n const deps: ReadDeps = {\n ...caches.current,\n signal: controller.signal,\n ...(o.rpcUrls ? { rpcUrls: o.rpcUrls } : {}),\n ...(o.getClient ? { getClient: o.getClient as (chainId: number) => PublicClient } : {}),\n ...(o.fetch ? { fetch: o.fetch } : {}),\n };\n const result = await readSources(list, deps);\n if (!active) return;\n const finishedAt = Date.now();\n setEntries((prev) => {\n const next = new Map<string, Entry>();\n [...wanted.keys()].forEach((k, i) => {\n const outcome = result.outcomes[i]!;\n const before = prev.get(k);\n // The failure rule: a failed read keeps the last good snapshot and records the error.\n next.set(\n k,\n outcome.ok\n ? { snapshot: outcome.snapshot, updatedAt: finishedAt, error: null }\n : { snapshot: before?.snapshot ?? null, updatedAt: before?.updatedAt ?? null, error: outcome.error },\n );\n });\n return next;\n });\n const offset = result.serverNowMs !== null && result.measuredAtMs !== null ? clockOffsetOf(result.serverNowMs, result.measuredAtMs) : null;\n if (offset !== null) setClockOffsetMs(offset);\n setNowSec(Math.floor((finishedAt + (offset ?? offsetRef.current)) / 1000));\n };\n\n const refresh = (): Promise<void> => {\n inFlight ??= run().finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n refreshRef.current = refresh;\n\n void refresh();\n const timer = refreshMs > 0 ? setInterval(() => { if (!hidden()) void refresh(); }, refreshMs) : null;\n const onVisible = (): void => {\n if (!hidden()) void refresh();\n };\n document.addEventListener('visibilitychange', onVisible);\n return () => {\n active = false;\n controller.abort();\n if (timer !== null) clearInterval(timer);\n document.removeEventListener('visibilitychange', onVisible);\n refreshRef.current = () => Promise.resolve();\n };\n }, [key, enabled, refreshMs, rpcKey]);\n\n // Boundary: wake up just after the nearest start or end time, so the phase changes on time and the new state is read.\n useEffect(() => {\n if (!enabled) return;\n const serverNow = Date.now() + clockOffsetMs;\n const next = nextBoundary(entries.values(), Math.floor(serverNow / 1000));\n if (next === null) return;\n const delay = Math.min(Math.max(next * 1000 - serverNow + BOUNDARY_DELAY_MS, 0), MAX_TIMEOUT_MS);\n const timer = setTimeout(() => {\n setNowSec(Math.floor((Date.now() + clockOffsetMs) / 1000));\n if (!hidden()) void refreshRef.current();\n }, delay);\n return () => clearTimeout(timer);\n }, [entries, clockOffsetMs, enabled, nowSec]);\n\n return useMemo(() => {\n return keys.map((k): DropStatus => {\n const e = entries.get(k);\n if (!e?.snapshot) return { ...unknownStatus(e?.error ?? null), clockOffsetMs };\n // Before the first read there is no snapshot, so nowSec is only used once it is set.\n return statusOf(e.snapshot, nowSec ?? Math.floor(e.updatedAt! / 1000), { stale: e.error !== null, updatedAt: e.updatedAt, error: e.error, clockOffsetMs });\n });\n // `key` stands for `keys`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, entries, clockOffsetMs, nowSec]);\n}\n","// Pure status logic: no React, no window, no network. Safe to import on a server.\nimport type { DropPhase, DropStatus, Snapshot } from './types';\n\nexport type PhaseInput = Pick<Snapshot, 'startTime' | 'endTime' | 'totalMinted' | 'maxSupply' | 'auction' | 'closed' | 'bid'>;\n\nconst soldOut = (s: PhaseInput): boolean => s.maxSupply !== null && s.maxSupply > 0n && s.totalMinted !== null && s.totalMinted >= s.maxSupply;\nconst windowClosed = (s: PhaseInput, nowSec: number): boolean => s.endTime !== null && nowSec >= s.endTime;\n\n/**\n * The phase of a read at `nowSec` (unix seconds). A pure function of the snapshot and the clock:\n * the same data gives the same phase on a server and in a browser.\n */\nexport function phaseOf(s: PhaseInput, nowSec: number): Exclude<DropPhase, 'unknown'> {\n if (soldOut(s)) return 'sold-out';\n // An auction that closed with a bid is sold, finalized yet or not.\n if (s.auction && s.bid !== null && (s.closed || windowClosed(s, nowSec))) return 'sold-out';\n if (s.closed) return 'ended';\n if (s.startTime !== null && nowSec < s.startTime) return 'upcoming';\n if (windowClosed(s, nowSec)) return 'ended';\n return 'live';\n}\n\n/** What a failed or missing read looks like: unknown, never \"upcoming\". */\nexport function unknownStatus(error: Error | null = null): DropStatus {\n return {\n phase: 'unknown',\n startTime: null,\n endTime: null,\n durationAfterFirstBid: null,\n totalMinted: null,\n maxSupply: null,\n remaining: null,\n price: null,\n paymentToken: null,\n bid: null,\n soldFor: null,\n stale: false,\n updatedAt: null,\n error,\n clockOffsetMs: 0,\n };\n}\n\nexport type StatusMeta = { stale: boolean; updatedAt: number | null; error: Error | null; clockOffsetMs: number };\n\n/** Snapshot + clock -> the full status. */\nexport function statusOf(s: Snapshot, nowSec: number, meta: StatusMeta): DropStatus {\n const phase = phaseOf(s, nowSec);\n const remaining = s.maxSupply !== null && s.totalMinted !== null ? (s.maxSupply > s.totalMinted ? s.maxSupply - s.totalMinted : 0n) : null;\n const soldFor = phase !== 'sold-out' ? null : s.auction ? (s.bid?.amount ?? null) : s.price;\n return {\n phase,\n startTime: s.startTime,\n endTime: s.endTime,\n durationAfterFirstBid: s.durationAfterFirstBid,\n totalMinted: s.totalMinted,\n maxSupply: s.maxSupply,\n remaining,\n price: s.price,\n paymentToken: s.paymentToken,\n bid: s.bid,\n soldFor,\n ...meta,\n };\n}\n\n/** Device clocks within this of the server are trusted: a block timestamp trails real time by up to one block. */\nexport const CLOCK_TOLERANCE_MS = 30_000;\n\n/** Server time minus device time, or 0 when the device is within CLOCK_TOLERANCE_MS of it. */\nexport function clockOffsetOf(serverNowMs: number, deviceNowMs: number): number {\n const offset = serverNowMs - deviceNowMs;\n return Math.abs(offset) <= CLOCK_TOLERANCE_MS ? 0 : offset;\n}\n\n// ---------- durations and countdown parts ----------\n\nconst UNIT_SECONDS: Readonly<Record<string, number>> = { s: 1, m: 60, h: 3600, d: 86400, w: 604800 };\n\n/** '6h' | '30m' | '2d' | '90s' | '1w' | seconds (number) -> seconds. Throws RangeError on anything else. */\nexport function parseDuration(value: string | number): number {\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0) throw new RangeError(`Invalid duration ${String(value)}: use seconds >= 0 or a string like \"6h\".`);\n return value;\n }\n const m = /^(\\d+(?:\\.\\d+)?)\\s*([smhdw])?$/i.exec(value.trim());\n if (!m) throw new RangeError(`Invalid duration \"${value}\": use seconds or a string like \"6h\", \"30m\", \"2d\".`);\n return Number(m[1]) * UNIT_SECONDS[(m[2] ?? 's').toLowerCase()]!;\n}\n\nexport type CountdownParts = { d: number; h: number; m: number; s: number };\n\n/**\n * Splits whole seconds into d/h/m/s. `units` (a subset of \"dhms\") picks the units to show; the largest one\n * takes the overflow, so \"hms\" turns 2 days into 48 hours and \"ms\" shows only minutes and seconds.\n */\nexport function countdownParts(totalSeconds: number, units: string = 'dhms'): CountdownParts {\n let left = Math.max(0, Math.floor(totalSeconds));\n const shown = (['d', 'h', 'm', 's'] as const).filter((u) => units.includes(u));\n const use = shown.length > 0 ? shown : (['d', 'h', 'm', 's'] as const);\n const out: CountdownParts = { d: 0, h: 0, m: 0, s: 0 };\n for (const u of use) {\n const size = UNIT_SECONDS[u]!;\n out[u] = Math.floor(left / size);\n left -= out[u] * size;\n }\n return out;\n}\n","// Network reads for ./status: claims through one multicall per chain, listings through one batch POST.\n// No React and no browser globals, so a server can call readSources directly.\nimport { getAddress, isAddress, parseAbi, zeroAddress, type Abi, type PublicClient } from 'viem';\nimport { CLAIM_ABIS, EXTENSIONS, decodeClaim, findExtension, type Claim, type ExtensionEntry } from '../core/adapters/manifold-claim';\nimport { createReadClient } from '../core/rpc';\nimport type { Address, RpcUrls } from '../core/types';\nimport type { ClaimSource, ListingSource, Snapshot, StatusSource } from './types';\n\n/** Manifold marketplace API host per chain. */\nexport const MARKETPLACE_API: Readonly<Record<number, string>> = {\n 1: 'https://marketplace.api.manifoldxyz.dev',\n 11155111: 'https://sepolia.marketplace.api.manifoldxyz.dev',\n 8453: 'https://base.marketplace.api.manifoldxyz.dev',\n 10: 'https://optimism.marketplace.api.manifoldxyz.dev',\n 360: 'https://shape.marketplace.api.manifoldxyz.dev',\n};\n\n/**\n * Manifold marketplace contract per chain. Mainnet has its own deployment; Optimism, Base, Shape and Sepolia\n * share one. Each was checked against the API of its chain: POST /listing/<address>/batch answers with\n * listings whose `address` field is that contract. Other chains must pass `marketplace` in the source.\n */\nexport const MARKETPLACE_ADDRESSES: Readonly<Record<number, Address>> = {\n 1: '0x3a3548e060be10c2614d0a4cb0c03cc9093fd799',\n 10: '0x5246807fb65d87b0d0a234e0f3d42374de83b421',\n 8453: '0x5246807fb65d87b0d0a234e0f3d42374de83b421',\n 360: '0x5246807fb65d87b0d0a234e0f3d42374de83b421',\n 11155111: '0x5246807fb65d87b0d0a234e0f3d42374de83b421',\n};\n\n/** At most this many single-listing GETs run at once. */\nconst SINGLE_CONCURRENCY = 3;\n/** A listing's endTime below this with startTime 0 is a duration (seconds), not a timestamp (seconds since 1970). */\nconst DURATION_CUTOFF = 1e11;\n\nexport type ReadOutcome = { ok: true; snapshot: Snapshot } | { ok: false; error: Error };\n\nexport type ReadDeps = {\n rpcUrls?: RpcUrls;\n /** Replaces the viem client per chain. */\n getClient?: (chainId: number) => PublicClient;\n fetch?: typeof fetch;\n /** Extension found for a claim, kept between reads so only the first read tries every extension. */\n extensionCache?: Map<string, ExtensionEntry>;\n /** Listings that can no longer change (finalized), kept between reads so they are not fetched again. */\n finalCache?: Map<string, Snapshot>;\n /** Wall-clock ms, for tests. */\n nowMs?: () => number;\n signal?: AbortSignal;\n};\n\nexport type ReadResult = {\n /** Aligned with the sources passed in. */\n outcomes: ReadOutcome[];\n /** Server time in ms from the freshest evidence (HTTP Date header, else block timestamp), measured at device time `measuredAtMs`. */\n serverNowMs: number | null;\n measuredAtMs: number | null;\n};\n\nconst asError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e)));\nconst fail = (message: string, cause?: unknown): ReadOutcome => ({ ok: false, error: new Error(message, cause === undefined ? undefined : { cause }) });\n\n/** The stable identity of a source: same key = same thing to read. Addresses are case-folded. */\nexport function sourceKey(source: StatusSource): string {\n if (source.type === 'manifold-claim') {\n return JSON.stringify(['claim', source.chainId, source.creatorContract.toLowerCase(), source.instanceId, source.extensionAddress?.toLowerCase() ?? null]);\n }\n return JSON.stringify(['listing', source.chainId, source.listingId, source.marketplace?.toLowerCase() ?? null]);\n}\n\n// ---------- claims ----------\n\nconst TIMESTAMP_ABI = parseAbi(['function getCurrentBlockTimestamp() view returns (uint256 timestamp)']);\n\ntype Call = { address: Address; abi: Abi; functionName: string; args?: readonly unknown[] };\ntype CallResult = { status: 'success'; result: unknown } | { status: 'failure'; error: Error };\n\nfunction seconds(v: bigint, field: string): number | null {\n if (v === 0n) return null;\n if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error(`Claim ${field} does not fit a safe integer`);\n return Number(v);\n}\n\n/** The claim's fields as a Snapshot. Pure. */\nexport function snapshotFromClaim(claim: Claim): Snapshot {\n return {\n kind: 'claim',\n startTime: seconds(BigInt(claim.startDate), 'startDate'),\n endTime: seconds(BigInt(claim.endDate), 'endDate'),\n durationAfterFirstBid: null,\n totalMinted: claim.total,\n maxSupply: claim.totalMax === 0n ? null : claim.totalMax,\n price: claim.cost,\n paymentToken: claim.erc20 === zeroAddress ? null : getAddress(claim.erc20),\n bid: null,\n auction: false,\n closed: false,\n };\n}\n\ntype ClaimWork = { index: number; source: ClaimSource; creator: Address; instanceId: bigint; pinned: ExtensionEntry | null };\n\nfunction validateClaim(source: ClaimSource): { creator: Address; instanceId: bigint; pinned: ExtensionEntry | null } | Error {\n if (!isAddress(source.creatorContract, { strict: false })) return new Error('creatorContract is not a valid address.');\n if (typeof source.instanceId !== 'string' || !/^\\d+$/.test(source.instanceId)) return new Error('instanceId must be a non-negative integer string.');\n let pinned: ExtensionEntry | null = null;\n if (source.extensionAddress !== undefined) {\n if (!isAddress(source.extensionAddress, { strict: false })) return new Error('extensionAddress is not a valid address.');\n pinned = findExtension(source.chainId, source.extensionAddress.toLowerCase() as Address) ?? null;\n if (!pinned) return new Error(`Extension ${source.extensionAddress} is not a supported Manifold claim extension on chain ${String(source.chainId)}.`);\n }\n return { creator: getAddress(source.creatorContract), instanceId: BigInt(source.instanceId), pinned };\n}\n\nasync function multicall(client: PublicClient, calls: Call[]): Promise<{ results: CallResult[]; blockTimestamp: bigint | null }> {\n const multicall3 = client.chain?.contracts?.multicall3?.address;\n const contracts: Call[] = multicall3 ? [...calls, { address: multicall3, abi: TIMESTAMP_ABI, functionName: 'getCurrentBlockTimestamp' }] : calls;\n // batchSize 0: never split into several eth_calls. One refresh is one request per chain.\n const raw = (await client.multicall({ contracts: contracts as never, allowFailure: true, batchSize: 0 })) as ({ status: 'success'; result: unknown } | { status: 'failure'; error: unknown })[];\n const results: CallResult[] = raw.slice(0, calls.length).map((r) => (r.status === 'success' ? r : { status: 'failure', error: asError(r.error) }));\n const last = multicall3 ? raw[calls.length] : undefined;\n return { results, blockTimestamp: last?.status === 'success' && typeof last.result === 'bigint' ? last.result : null };\n}\n\nasync function readClaimChain(client: PublicClient, chainId: number, work: ClaimWork[], deps: ReadDeps): Promise<{ outcomes: Map<number, ReadOutcome>; blockTimestamp: bigint | null }> {\n const outcomes = new Map<number, ReadOutcome>();\n const cache = deps.extensionCache;\n const cacheKey = (w: ClaimWork) => `${String(chainId)}:${w.creator.toLowerCase()}:${w.instanceId.toString()}`;\n const onChain = EXTENSIONS.filter((e) => e.chainId === chainId);\n\n // One getClaim per source and candidate extension, all in one multicall. A pinned or already-found extension is the only\n // candidate; otherwise every extension Manifold deployed on the chain is tried, and only the one that holds the claim answers.\n type Probe = { w: ClaimWork; entry: ExtensionEntry };\n const probes: Probe[] = [];\n for (const w of work) {\n const known = w.pinned ?? cache?.get(cacheKey(w)) ?? null;\n const candidates = known ? [known] : onChain;\n if (candidates.length === 0) outcomes.set(w.index, fail(`Manifold claims are not available on chain ${String(chainId)}.`));\n for (const entry of candidates) probes.push({ w, entry });\n }\n if (probes.length === 0) return { outcomes, blockTimestamp: null };\n\n const { results, blockTimestamp } = await multicall(\n client,\n probes.map(({ w, entry }) => ({ address: getAddress(entry.address), abi: CLAIM_ABIS[entry.abi], functionName: 'getClaim', args: [w.creator, w.instanceId] })),\n );\n\n const byWork = new Map<ClaimWork, { entry: ExtensionEntry; result: CallResult }[]>();\n probes.forEach((p, i) => byWork.set(p.w, [...(byWork.get(p.w) ?? []), { entry: p.entry, result: results[i]! }]));\n for (const [w, list] of byWork) {\n const hits = list.filter((x) => x.result.status === 'success');\n const narrowed = list.length === 1;\n if (hits.length === 0) {\n // Several candidates and none answered: the claim is not there. One candidate failed: that read failed.\n outcomes.set(w.index, fail(narrowed ? `Could not read claim ${w.source.instanceId} on ${w.source.creatorContract}.` : `No Manifold claim ${w.source.instanceId} found on ${w.source.creatorContract}.`));\n } else if (hits.length > 1) {\n outcomes.set(w.index, fail(`Claim ${w.source.instanceId} exists on ${String(hits.length)} extensions of ${w.source.creatorContract}. Set extensionAddress.`));\n } else {\n const hit = hits[0]!;\n try {\n const snapshot = snapshotFromClaim(decodeClaim(hit.entry.abi, (hit.result as { result: unknown }).result));\n if (!w.pinned) cache?.set(cacheKey(w), hit.entry);\n outcomes.set(w.index, { ok: true, snapshot });\n } catch (e) {\n outcomes.set(w.index, fail(asError(e).message, e));\n }\n }\n }\n return { outcomes, blockTimestamp };\n}\n\n// ---------- listings ----------\n\ntype BigNumberJson = { hex?: unknown; _hex?: unknown } | string | number | null | undefined;\n\n/** A number the marketplace API sends as a decimal string, a JSON number or {type:'BigNumber', hex}. */\nfunction bigOf(v: BigNumberJson): bigint | null {\n if (v === null || v === undefined) return null;\n if (typeof v === 'number') return Number.isInteger(v) ? BigInt(v) : null;\n if (typeof v === 'string') return /^(0x[0-9a-f]+|\\d+)$/i.test(v) ? BigInt(v) : null;\n const hex = typeof v.hex === 'string' ? v.hex : typeof v._hex === 'string' ? v._hex : null;\n return hex !== null && /^0x[0-9a-f]*$/i.test(hex) ? BigInt(hex === '0x' ? '0x0' : hex) : null;\n}\n\n/** The id of a listing as a decimal string, from either shape the API uses (\"1538\" or {hex: \"0x0602\"}). */\nexport function listingIdOf(raw: { id?: BigNumberJson }): string | null {\n const id = bigOf(raw.id);\n return id === null ? null : id.toString();\n}\n\ntype ListingJson = {\n id?: BigNumberJson;\n finalized?: boolean;\n totalSold?: BigNumberJson;\n details?: { type_?: number; initialAmount?: BigNumberJson; totalAvailable?: BigNumberJson; erc20?: string | null; startTime?: number; endTime?: number };\n bid?: { amount?: BigNumberJson };\n};\n\nfunction timeOf(v: unknown): number {\n if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0) throw new Error('Listing has an invalid start or end time.');\n return v;\n}\n\n/**\n * A listing as a Snapshot. `batch` is the entry of POST /listing/<marketplace>/batch (details); `single` is the answer of\n * GET /listing/<marketplace>/<id>, which carries the bid and totalSold the batch leaves out or reports as 0.\n * Pass only `batch` for a listing that is not open yet. Pure.\n */\nexport function snapshotFromListing(batch: ListingJson, single?: ListingJson | null): Snapshot {\n const src = single ?? batch;\n const d = src.details ?? batch.details;\n if (!d) throw new Error('Listing has no details.');\n const start = timeOf(d.startTime);\n const end = timeOf(d.endTime);\n // startTime 0 with a small endTime: the timer starts on the first bid or purchase and runs `endTime` seconds.\n const duration = start === 0 && end > 0 && end < DURATION_CUTOFF;\n const auction = d.type_ === 1;\n const amount = bigOf(src.bid?.amount) ?? 0n;\n const erc20 = typeof d.erc20 === 'string' && isAddress(d.erc20, { strict: false }) && d.erc20 !== zeroAddress ? getAddress(d.erc20) : null;\n return {\n kind: 'listing',\n startTime: start === 0 ? null : start,\n endTime: duration || end === 0 ? null : end,\n durationAfterFirstBid: duration ? end : null,\n // totalSold is left out when it is 0 on some routes.\n totalMinted: single ? (bigOf(single.totalSold) ?? 0n) : null,\n maxSupply: bigOf(d.totalAvailable),\n price: bigOf(d.initialAmount),\n paymentToken: erc20,\n bid: auction && amount > 0n ? { amount } : null,\n auction,\n closed: src.finalized === true,\n };\n}\n\nasync function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const out = new Array<R>(items.length);\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const i = next++;\n out[i] = await fn(items[i]!);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return out;\n}\n\ntype ListingWork = { index: number; source: ListingSource; marketplace: Address; api: string; key: string };\n\nasync function getJson(f: typeof fetch, url: string, init: RequestInit | undefined, signal?: AbortSignal): Promise<{ json: unknown; date: number | null }> {\n const res = await f(url, { ...init, ...(signal ? { signal } : {}) });\n if (!res.ok) throw new Error(`Marketplace API answered ${String(res.status)} for ${url.replace(/^https?:\\/\\/[^/]+/, '')}`);\n const header = res.headers?.get('date') ?? null;\n const date = header ? Date.parse(header) : NaN;\n return { json: await res.json(), date: Number.isNaN(date) ? null : date };\n}\n\nasync function readListings(work: ListingWork[], deps: ReadDeps, outcomes: Map<number, ReadOutcome>): Promise<{ serverNowMs: number | null; measuredAtMs: number | null }> {\n const f = deps.fetch ?? globalThis.fetch;\n const nowMs = deps.nowMs ?? Date.now;\n let clock: { serverNowMs: number; measuredAtMs: number } | null = null;\n\n // One batch POST per marketplace, for every listing that is not already final.\n const groups = new Map<string, ListingWork[]>();\n for (const w of work) {\n const done = deps.finalCache?.get(w.key);\n if (done) {\n outcomes.set(w.index, { ok: true, snapshot: done });\n continue;\n }\n const gk = `${w.api}|${w.marketplace.toLowerCase()}`;\n groups.set(gk, [...(groups.get(gk) ?? []), w]);\n }\n\n for (const items of groups.values()) {\n const { api, marketplace } = items[0]!;\n const ids = [...new Set(items.map((w) => w.source.listingId))];\n let rows: ListingJson[];\n try {\n const { json, date } = await getJson(f, `${api}/listing/${marketplace}/batch`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ listingIds: ids }) }, deps.signal);\n if (!Array.isArray(json)) throw new Error('Marketplace API answered with something other than a list.');\n rows = json as ListingJson[];\n if (date !== null) clock = { serverNowMs: date, measuredAtMs: nowMs() };\n } catch (e) {\n for (const w of items) outcomes.set(w.index, fail(asError(e).message, e));\n continue;\n }\n const byId = new Map<string, ListingJson>();\n for (const r of rows) {\n const id = listingIdOf(r);\n if (id !== null) byId.set(id, r);\n }\n\n // Single GETs: the batch leaves out the bid and units sold. Only a listing that is not open yet needs neither.\n // Server time when the API sent a Date header: a wrong device clock must not decide who needs a second read.\n const nowSec = Math.floor((clock ? clock.serverNowMs : nowMs()) / 1000);\n const pending: { w: ListingWork; row: ListingJson }[] = [];\n for (const w of items) {\n const row = byId.get(w.source.listingId);\n if (!row) {\n outcomes.set(w.index, fail(`Listing ${w.source.listingId} was not found on chain ${String(w.source.chainId)}.`));\n continue;\n }\n try {\n const s = snapshotFromListing(row);\n const upcoming = s.startTime !== null && s.startTime > nowSec && row.finalized !== true;\n if (upcoming) outcomes.set(w.index, { ok: true, snapshot: s });\n else pending.push({ w, row });\n } catch (e) {\n outcomes.set(w.index, fail(asError(e).message, e));\n }\n }\n const singles = await mapLimit(pending, SINGLE_CONCURRENCY, async ({ w, row }): Promise<ReadOutcome> => {\n try {\n const { json } = await getJson(f, `${api}/listing/${marketplace}/${w.source.listingId}`, undefined, deps.signal);\n if (typeof json !== 'object' || json === null || Array.isArray(json)) throw new Error('Marketplace API answered with something other than a listing.');\n const answer = json as ListingJson;\n const id = listingIdOf(answer);\n if (id !== null && id !== w.source.listingId) throw new Error(`Marketplace API answered listing ${id} for listing ${w.source.listingId}.`);\n const snapshot = snapshotFromListing(row, answer);\n if (snapshot.closed) deps.finalCache?.set(w.key, snapshot);\n return { ok: true, snapshot };\n } catch (e) {\n return fail(asError(e).message, e);\n }\n });\n pending.forEach((p, i) => outcomes.set(p.w.index, singles[i]!));\n }\n return { serverNowMs: clock?.serverNowMs ?? null, measuredAtMs: clock?.measuredAtMs ?? null };\n}\n\n// ---------- entry point ----------\n\nfunction resolveMarketplace(source: ListingSource): { marketplace: Address; api: string } | Error {\n const api = MARKETPLACE_API[source.chainId];\n if (!api) return new Error(`The marketplace API does not serve chain ${String(source.chainId)}.`);\n const marketplace = source.marketplace ?? MARKETPLACE_ADDRESSES[source.chainId];\n if (!marketplace) return new Error(`Pass marketplace for chain ${String(source.chainId)}: there is no default marketplace address.`);\n if (!isAddress(marketplace, { strict: false })) return new Error('marketplace is not a valid address.');\n if (typeof source.listingId !== 'string' || !/^\\d+$/.test(source.listingId)) return new Error('listingId must be a non-negative integer string.');\n return { marketplace: marketplace.toLowerCase() as Address, api };\n}\n\n/**\n * Reads every source once. All claims of one chain share one multicall; all listings of one marketplace share one batch POST. A source that cannot be read gets an\n * error outcome and never a made-up value. Never throws.\n */\nexport async function readSources(sources: readonly StatusSource[], deps: ReadDeps = {}): Promise<ReadResult> {\n const nowMs = deps.nowMs ?? Date.now;\n const outcomes = new Array<ReadOutcome>(sources.length);\n const out = new Map<number, ReadOutcome>();\n\n const claimsByChain = new Map<number, ClaimWork[]>();\n const listings: ListingWork[] = [];\n sources.forEach((source, index) => {\n if (source.type === 'manifold-claim') {\n const v = validateClaim(source);\n if (v instanceof Error) out.set(index, { ok: false, error: v });\n else claimsByChain.set(source.chainId, [...(claimsByChain.get(source.chainId) ?? []), { index, source, ...v }]);\n } else if (source.type === 'marketplace-listing') {\n const v = resolveMarketplace(source);\n if (v instanceof Error) out.set(index, { ok: false, error: v });\n else listings.push({ index, source, ...v, key: sourceKey(source) });\n } else {\n out.set(index, fail('Unknown source type.'));\n }\n });\n\n let blockClock: { serverNowMs: number; measuredAtMs: number } | null = null;\n const clients = new Map<number, PublicClient>();\n const clientFor = (chainId: number): PublicClient => {\n let c = clients.get(chainId);\n if (!c) {\n // The Manifold read bridge only allows a few contracts and methods; multicall is not one of them.\n c = deps.getClient ? deps.getClient(chainId) : createReadClient({ chainId, ...(deps.rpcUrls ? { rpcUrls: deps.rpcUrls } : {}), useBridge: false });\n clients.set(chainId, c);\n }\n return c;\n };\n\n const claimJobs = [...claimsByChain].map(async ([chainId, work]) => {\n try {\n const r = await readClaimChain(clientFor(chainId), chainId, work, deps);\n for (const [i, o] of r.outcomes) out.set(i, o);\n if (r.blockTimestamp !== null) blockClock = { serverNowMs: Number(r.blockTimestamp) * 1000, measuredAtMs: nowMs() };\n } catch (e) {\n for (const w of work) out.set(w.index, fail(asError(e).message, e));\n }\n });\n const listingJob = readListings(listings, deps, out).catch((e: unknown) => {\n for (const w of listings) if (!out.has(w.index)) out.set(w.index, fail(asError(e).message, e));\n return { serverNowMs: null, measuredAtMs: null };\n });\n const [, listingClock] = await Promise.all([Promise.all(claimJobs), listingJob]);\n\n for (let i = 0; i < sources.length; i++) outcomes[i] = out.get(i) ?? fail('Source was not read.');\n // The HTTP Date header is exact to the second; a block timestamp trails by up to one block.\n const clock = listingClock.serverNowMs !== null ? { serverNowMs: listingClock.serverNowMs, measuredAtMs: listingClock.measuredAtMs! } : (blockClock as { serverNowMs: number; measuredAtMs: number } | null);\n return { outcomes, serverNowMs: clock?.serverNowMs ?? null, measuredAtMs: clock?.measuredAtMs ?? null };\n}\n","// <DropCountdown>: unstyled countdown to a unix-seconds time. No window or document at import; the first render is empty\n// on server and client alike, so hydration always matches.\nimport { createElement, useEffect, useState, type ReactNode } from 'react';\n\nimport { countdownParts, parseDuration, type CountdownParts } from './core';\n\nexport type CountdownState = {\n /** counting: `to` is still ahead. done: it has passed. */\n state: 'counting' | 'done';\n /** false while outside the showWithin window (or past hideAfter). The default markup renders nothing then. */\n visible: boolean;\n /** Whole seconds left, rounded up; 0 once done. */\n secondsLeft: number;\n /** d/h/m/s for the chosen format; the largest unit shown takes the overflow. */\n parts: CountdownParts;\n /** The default text, e.g. \"2d 03h 04m 05s\". */\n text: string;\n};\n\nexport type DropCountdownProps = {\n /** Unix seconds. null renders nothing. */\n to: number | null;\n /** Show only this long before `to`: '6h' | '30m' | '2d' | '90s' | '1w' | seconds. Default: always. */\n showWithin?: string | number;\n /** Keep showing the done state this long after `to`: same units. Default: hide the moment `to` passes. */\n hideAfter?: string | number;\n /** Current time in ms. Default Date.now. Use `() => Date.now() + status.clockOffsetMs` to follow the server clock. */\n now?: () => number;\n /** Units to show, any of \"dhms\" in that order. Default \"dhms\". \"hms\" turns 2 days into 48 hours. */\n format?: string;\n className?: string;\n /** Replaces the default text. A function replaces the whole markup: it is called whenever `to` is set, visible or not. */\n children?: ReactNode | ((c: CountdownState) => ReactNode);\n};\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst pad = (n: number): string => String(n).padStart(2, '0');\n\n/** \"2d 03h 04m 05s\": leading zero units are dropped (never the last), the rest after the first are padded. */\nexport function formatCountdown(parts: CountdownParts, units: string = 'dhms'): string {\n const shown = (['d', 'h', 'm', 's'] as const).filter((u) => units.includes(u));\n const use = shown.length > 0 ? shown : (['d', 'h', 'm', 's'] as const);\n let first = use.findIndex((u) => parts[u] > 0);\n if (first === -1) first = use.length - 1;\n return use\n .slice(first)\n .map((u, i) => `${i === 0 ? String(parts[u]) : pad(parts[u])}${u}`)\n .join(' ');\n}\n\nconst hidden = (): boolean => typeof document !== 'undefined' && document.hidden;\n\nexport function DropCountdown(props: DropCountdownProps): ReactNode {\n const { to, showWithin, hideAfter, now, format = 'dhms', className, children } = props;\n const within = showWithin === undefined ? Infinity : parseDuration(showWithin);\n const keep = hideAfter === undefined ? 0 : parseDuration(hideAfter);\n // null until mounted: nothing differs between the server render and the first client render.\n const [nowMs, setNowMs] = useState<number | null>(null);\n\n useEffect(() => {\n if (to === null) return;\n const clock = now ?? Date.now;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const stop = (): void => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n };\n const step = (): void => {\n stop();\n const t = clock();\n setNowMs(t);\n // A hidden tab does not tick; visibilitychange starts it again.\n if (hidden()) return;\n const untilTo = to * 1000 - t;\n let delay: number;\n if (untilTo > 0) {\n // Wait for the window to open, then tick each second; never sleep past `to`.\n delay = untilTo > within * 1000 ? untilTo - within * 1000 : Math.min(1000, untilTo);\n } else if (keep > 0 && -untilTo < keep * 1000) {\n delay = keep * 1000 + untilTo;\n } else {\n return;\n }\n timer = setTimeout(step, Math.min(Math.max(delay, 0), MAX_TIMEOUT_MS));\n };\n const onVisibility = (): void => {\n if (hidden()) stop();\n else step();\n };\n step();\n document.addEventListener('visibilitychange', onVisibility);\n return () => {\n stop();\n document.removeEventListener('visibilitychange', onVisibility);\n };\n }, [to, within, keep, now]);\n\n if (to === null || nowMs === null) return null;\n\n const left = to - nowMs / 1000;\n const counting = left > 0;\n const secondsLeft = counting ? Math.ceil(left) : 0;\n const visible = counting ? left <= within : -left < keep;\n const parts = countdownParts(secondsLeft, format);\n const text = formatCountdown(parts, format);\n const state: CountdownState = { state: counting ? 'counting' : 'done', visible, secondsLeft, parts, text };\n\n if (typeof children === 'function') return children(state);\n if (!visible) return null;\n return createElement('span', { className, role: 'timer', 'data-state': state.state }, children ?? text);\n}\n"]}
@@ -0,0 +1,259 @@
1
+ import { b as RpcUrls } from './types-BjKGb6c7.cjs';
2
+ import { Address, PublicClient } from 'viem';
3
+ import { ReactNode } from 'react';
4
+
5
+ /** A Manifold Edition claim. Same fields as the manifold-claim mint config; `extensionAddress` pins the extension. */
6
+ type ClaimSource = {
7
+ type: 'manifold-claim';
8
+ chainId: number;
9
+ creatorContract: Address;
10
+ /** Studio instance id == on-chain claim index. Decimal string. */
11
+ instanceId: string;
12
+ /** Optional; looked up on the creator contract when omitted. */
13
+ extensionAddress?: Address;
14
+ };
15
+ /** A Manifold marketplace listing (auction or fixed price). */
16
+ type ListingSource = {
17
+ type: 'marketplace-listing';
18
+ chainId: number;
19
+ /** The marketplace's listing id, as a decimal string. */
20
+ listingId: string;
21
+ /** Marketplace contract. Defaults to Manifold's deployment on the chains in MARKETPLACE_ADDRESSES. */
22
+ marketplace?: Address;
23
+ };
24
+ type StatusSource = ClaimSource | ListingSource;
25
+ /**
26
+ * upcoming not open yet
27
+ * live on sale now
28
+ * ended the window closed (or the listing was finalized) without a sale
29
+ * sold-out every unit is minted or sold; for an auction, it has a winning bid
30
+ * unknown nothing has been read yet, or the first read failed. Never means "not open yet".
31
+ */
32
+ type DropPhase = 'upcoming' | 'live' | 'ended' | 'sold-out' | 'unknown';
33
+ /** What a read returned, with no clock applied. The phase is derived from it with phaseOf. */
34
+ type Snapshot = {
35
+ kind: 'claim' | 'listing';
36
+ /** Unix seconds. null = no start date (on sale as soon as it exists), or a listing waiting for its first bid. */
37
+ startTime: number | null;
38
+ /** Unix seconds. null = open-ended, or a listing whose timer starts on the first bid. */
39
+ endTime: number | null;
40
+ /** Listing whose timer starts on the first bid or purchase: how many seconds it then runs. */
41
+ durationAfterFirstBid: number | null;
42
+ /** Claim: total minted. Listing: units sold. */
43
+ totalMinted: bigint | null;
44
+ /** null = unlimited. */
45
+ maxSupply: bigint | null;
46
+ /** Claim: price per mint. Listing: fixed price, or the starting bid of an auction. Base units. */
47
+ price: bigint | null;
48
+ /** ERC20 the price is paid in; null = native. */
49
+ paymentToken: Address | null;
50
+ /** Auction listings only: the highest bid, or null while there is none. */
51
+ bid: {
52
+ amount: bigint;
53
+ } | null;
54
+ auction: boolean;
55
+ /** Listing was finalized on chain: nothing more can happen to it. */
56
+ closed: boolean;
57
+ };
58
+ type DropStatus = {
59
+ phase: DropPhase;
60
+ /** Unix seconds, null when the drop has no start date (or is waiting for its first bid). */
61
+ startTime: number | null;
62
+ /** Unix seconds, null when open-ended (or waiting for its first bid). */
63
+ endTime: number | null;
64
+ /** Set while a listing's timer is not running: seconds it will run after the first bid or purchase. */
65
+ durationAfterFirstBid: number | null;
66
+ /** Claim: total minted. Listing: units sold. */
67
+ totalMinted: bigint | null;
68
+ /** null = unlimited (or unknown). */
69
+ maxSupply: bigint | null;
70
+ /** maxSupply - totalMinted, never below 0. null when unlimited or unknown. */
71
+ remaining: bigint | null;
72
+ price: bigint | null;
73
+ /** ERC20 the price is paid in; null = native currency of the chain. */
74
+ paymentToken: Address | null;
75
+ /** Auction listings: the highest bid so far. The marketplace API reports no bid count. */
76
+ bid: {
77
+ amount: bigint;
78
+ } | null;
79
+ /** The winning bid (auction) or the price (fixed price) once phase is 'sold-out'. */
80
+ soldFor: bigint | null;
81
+ /** The last read failed; every other field is from the last good read. */
82
+ stale: boolean;
83
+ /** Device time (ms since epoch) of the last good read, null before one. */
84
+ updatedAt: number | null;
85
+ /** The error of the latest failed read, null when the latest read worked. */
86
+ error: Error | null;
87
+ /**
88
+ * Server time minus device time, in ms, when the device clock is off by more than 30 s; else 0.
89
+ * Server time is now() = Date.now() + clockOffsetMs. Pass it to <DropCountdown now>.
90
+ */
91
+ clockOffsetMs: number;
92
+ };
93
+ type UseDropStatusOptions = {
94
+ /** Poll interval, default 30_000. Polling pauses while the tab is hidden. */
95
+ refreshMs?: number;
96
+ /** chainId -> JSON-RPC URL, like the other hooks. Claims are read with one multicall per chain. */
97
+ rpcUrls?: RpcUrls;
98
+ /** Replaces the viem client per chain (tests, custom transports). */
99
+ getClient?: (chainId: number) => PublicClient;
100
+ /** Replaces fetch for marketplace requests (tests). */
101
+ fetch?: typeof fetch;
102
+ /** false = read nothing. Default true. */
103
+ enabled?: boolean;
104
+ };
105
+
106
+ /**
107
+ * Status of every source, aligned with `sources`. Reads once on mount and then every `refreshMs` (30 s) while the tab is visible,
108
+ * on return to the tab, and right after the nearest start or end time passes.
109
+ *
110
+ * A failed read never changes what the page believes about a drop: the last good value stays with `stale: true` and `error` set;
111
+ * with no good value yet the phase is 'unknown', never 'upcoming'.
112
+ *
113
+ * The array you pass may be a new literal on every render. Sources are keyed by their content, so that does not read again.
114
+ */
115
+ declare function useDropStatus(sources: readonly StatusSource[], options?: UseDropStatusOptions): DropStatus[];
116
+
117
+ type PhaseInput = Pick<Snapshot, 'startTime' | 'endTime' | 'totalMinted' | 'maxSupply' | 'auction' | 'closed' | 'bid'>;
118
+ /**
119
+ * The phase of a read at `nowSec` (unix seconds). A pure function of the snapshot and the clock:
120
+ * the same data gives the same phase on a server and in a browser.
121
+ */
122
+ declare function phaseOf(s: PhaseInput, nowSec: number): Exclude<DropPhase, 'unknown'>;
123
+ /** What a failed or missing read looks like: unknown, never "upcoming". */
124
+ declare function unknownStatus(error?: Error | null): DropStatus;
125
+ type StatusMeta = {
126
+ stale: boolean;
127
+ updatedAt: number | null;
128
+ error: Error | null;
129
+ clockOffsetMs: number;
130
+ };
131
+ /** Snapshot + clock -> the full status. */
132
+ declare function statusOf(s: Snapshot, nowSec: number, meta: StatusMeta): DropStatus;
133
+ /** Server time minus device time, or 0 when the device is within CLOCK_TOLERANCE_MS of it. */
134
+ declare function clockOffsetOf(serverNowMs: number, deviceNowMs: number): number;
135
+ /** '6h' | '30m' | '2d' | '90s' | '1w' | seconds (number) -> seconds. Throws RangeError on anything else. */
136
+ declare function parseDuration(value: string | number): number;
137
+ type CountdownParts = {
138
+ d: number;
139
+ h: number;
140
+ m: number;
141
+ s: number;
142
+ };
143
+ /**
144
+ * Splits whole seconds into d/h/m/s. `units` (a subset of "dhms") picks the units to show; the largest one
145
+ * takes the overflow, so "hms" turns 2 days into 48 hours and "ms" shows only minutes and seconds.
146
+ */
147
+ declare function countdownParts(totalSeconds: number, units?: string): CountdownParts;
148
+
149
+ type CountdownState = {
150
+ /** counting: `to` is still ahead. done: it has passed. */
151
+ state: 'counting' | 'done';
152
+ /** false while outside the showWithin window (or past hideAfter). The default markup renders nothing then. */
153
+ visible: boolean;
154
+ /** Whole seconds left, rounded up; 0 once done. */
155
+ secondsLeft: number;
156
+ /** d/h/m/s for the chosen format; the largest unit shown takes the overflow. */
157
+ parts: CountdownParts;
158
+ /** The default text, e.g. "2d 03h 04m 05s". */
159
+ text: string;
160
+ };
161
+ type DropCountdownProps = {
162
+ /** Unix seconds. null renders nothing. */
163
+ to: number | null;
164
+ /** Show only this long before `to`: '6h' | '30m' | '2d' | '90s' | '1w' | seconds. Default: always. */
165
+ showWithin?: string | number;
166
+ /** Keep showing the done state this long after `to`: same units. Default: hide the moment `to` passes. */
167
+ hideAfter?: string | number;
168
+ /** Current time in ms. Default Date.now. Use `() => Date.now() + status.clockOffsetMs` to follow the server clock. */
169
+ now?: () => number;
170
+ /** Units to show, any of "dhms" in that order. Default "dhms". "hms" turns 2 days into 48 hours. */
171
+ format?: string;
172
+ className?: string;
173
+ /** Replaces the default text. A function replaces the whole markup: it is called whenever `to` is set, visible or not. */
174
+ children?: ReactNode | ((c: CountdownState) => ReactNode);
175
+ };
176
+ /** "2d 03h 04m 05s": leading zero units are dropped (never the last), the rest after the first are padded. */
177
+ declare function formatCountdown(parts: CountdownParts, units?: string): string;
178
+ declare function DropCountdown(props: DropCountdownProps): ReactNode;
179
+
180
+ type ExtensionSpec = '721' | '1155';
181
+ type ExtensionAbi = 'Claim721V5Optimization' | 'Claim1155V5Optimization' | 'Claim721V7' | 'Claim1155V7';
182
+ type ExtensionEntry = {
183
+ chainId: number;
184
+ address: Address;
185
+ generation: 'V6' | 'V7' | 'V8';
186
+ spec: ExtensionSpec;
187
+ abi: ExtensionAbi;
188
+ };
189
+
190
+ /** Manifold marketplace API host per chain. */
191
+ declare const MARKETPLACE_API: Readonly<Record<number, string>>;
192
+ /**
193
+ * Manifold marketplace contract per chain. Mainnet has its own deployment; Optimism, Base, Shape and Sepolia
194
+ * share one. Each was checked against the API of its chain: POST /listing/<address>/batch answers with
195
+ * listings whose `address` field is that contract. Other chains must pass `marketplace` in the source.
196
+ */
197
+ declare const MARKETPLACE_ADDRESSES: Readonly<Record<number, Address>>;
198
+ type ReadOutcome = {
199
+ ok: true;
200
+ snapshot: Snapshot;
201
+ } | {
202
+ ok: false;
203
+ error: Error;
204
+ };
205
+ type ReadDeps = {
206
+ rpcUrls?: RpcUrls;
207
+ /** Replaces the viem client per chain. */
208
+ getClient?: (chainId: number) => PublicClient;
209
+ fetch?: typeof fetch;
210
+ /** Extension found for a claim, kept between reads so only the first read tries every extension. */
211
+ extensionCache?: Map<string, ExtensionEntry>;
212
+ /** Listings that can no longer change (finalized), kept between reads so they are not fetched again. */
213
+ finalCache?: Map<string, Snapshot>;
214
+ /** Wall-clock ms, for tests. */
215
+ nowMs?: () => number;
216
+ signal?: AbortSignal;
217
+ };
218
+ type ReadResult = {
219
+ /** Aligned with the sources passed in. */
220
+ outcomes: ReadOutcome[];
221
+ /** Server time in ms from the freshest evidence (HTTP Date header, else block timestamp), measured at device time `measuredAtMs`. */
222
+ serverNowMs: number | null;
223
+ measuredAtMs: number | null;
224
+ };
225
+ /** The stable identity of a source: same key = same thing to read. Addresses are case-folded. */
226
+ declare function sourceKey(source: StatusSource): string;
227
+ type BigNumberJson = {
228
+ hex?: unknown;
229
+ _hex?: unknown;
230
+ } | string | number | null | undefined;
231
+ type ListingJson = {
232
+ id?: BigNumberJson;
233
+ finalized?: boolean;
234
+ totalSold?: BigNumberJson;
235
+ details?: {
236
+ type_?: number;
237
+ initialAmount?: BigNumberJson;
238
+ totalAvailable?: BigNumberJson;
239
+ erc20?: string | null;
240
+ startTime?: number;
241
+ endTime?: number;
242
+ };
243
+ bid?: {
244
+ amount?: BigNumberJson;
245
+ };
246
+ };
247
+ /**
248
+ * A listing as a Snapshot. `batch` is the entry of POST /listing/<marketplace>/batch (details); `single` is the answer of
249
+ * GET /listing/<marketplace>/<id>, which carries the bid and totalSold the batch leaves out or reports as 0.
250
+ * Pass only `batch` for a listing that is not open yet. Pure.
251
+ */
252
+ declare function snapshotFromListing(batch: ListingJson, single?: ListingJson | null): Snapshot;
253
+ /**
254
+ * Reads every source once. All claims of one chain share one multicall; all listings of one marketplace share one batch POST. A source that cannot be read gets an
255
+ * error outcome and never a made-up value. Never throws.
256
+ */
257
+ declare function readSources(sources: readonly StatusSource[], deps?: ReadDeps): Promise<ReadResult>;
258
+
259
+ export { type ClaimSource, type CountdownParts, type CountdownState, DropCountdown, type DropCountdownProps, type DropPhase, type DropStatus, type ListingSource, MARKETPLACE_ADDRESSES, MARKETPLACE_API, type PhaseInput, type ReadDeps, type ReadOutcome, type ReadResult, type Snapshot, type StatusMeta, type StatusSource, type UseDropStatusOptions, clockOffsetOf, countdownParts, formatCountdown, parseDuration, phaseOf, readSources, snapshotFromListing, sourceKey, statusOf, unknownStatus, useDropStatus };
@@ -0,0 +1,259 @@
1
+ import { b as RpcUrls } from './types-BjKGb6c7.js';
2
+ import { Address, PublicClient } from 'viem';
3
+ import { ReactNode } from 'react';
4
+
5
+ /** A Manifold Edition claim. Same fields as the manifold-claim mint config; `extensionAddress` pins the extension. */
6
+ type ClaimSource = {
7
+ type: 'manifold-claim';
8
+ chainId: number;
9
+ creatorContract: Address;
10
+ /** Studio instance id == on-chain claim index. Decimal string. */
11
+ instanceId: string;
12
+ /** Optional; looked up on the creator contract when omitted. */
13
+ extensionAddress?: Address;
14
+ };
15
+ /** A Manifold marketplace listing (auction or fixed price). */
16
+ type ListingSource = {
17
+ type: 'marketplace-listing';
18
+ chainId: number;
19
+ /** The marketplace's listing id, as a decimal string. */
20
+ listingId: string;
21
+ /** Marketplace contract. Defaults to Manifold's deployment on the chains in MARKETPLACE_ADDRESSES. */
22
+ marketplace?: Address;
23
+ };
24
+ type StatusSource = ClaimSource | ListingSource;
25
+ /**
26
+ * upcoming not open yet
27
+ * live on sale now
28
+ * ended the window closed (or the listing was finalized) without a sale
29
+ * sold-out every unit is minted or sold; for an auction, it has a winning bid
30
+ * unknown nothing has been read yet, or the first read failed. Never means "not open yet".
31
+ */
32
+ type DropPhase = 'upcoming' | 'live' | 'ended' | 'sold-out' | 'unknown';
33
+ /** What a read returned, with no clock applied. The phase is derived from it with phaseOf. */
34
+ type Snapshot = {
35
+ kind: 'claim' | 'listing';
36
+ /** Unix seconds. null = no start date (on sale as soon as it exists), or a listing waiting for its first bid. */
37
+ startTime: number | null;
38
+ /** Unix seconds. null = open-ended, or a listing whose timer starts on the first bid. */
39
+ endTime: number | null;
40
+ /** Listing whose timer starts on the first bid or purchase: how many seconds it then runs. */
41
+ durationAfterFirstBid: number | null;
42
+ /** Claim: total minted. Listing: units sold. */
43
+ totalMinted: bigint | null;
44
+ /** null = unlimited. */
45
+ maxSupply: bigint | null;
46
+ /** Claim: price per mint. Listing: fixed price, or the starting bid of an auction. Base units. */
47
+ price: bigint | null;
48
+ /** ERC20 the price is paid in; null = native. */
49
+ paymentToken: Address | null;
50
+ /** Auction listings only: the highest bid, or null while there is none. */
51
+ bid: {
52
+ amount: bigint;
53
+ } | null;
54
+ auction: boolean;
55
+ /** Listing was finalized on chain: nothing more can happen to it. */
56
+ closed: boolean;
57
+ };
58
+ type DropStatus = {
59
+ phase: DropPhase;
60
+ /** Unix seconds, null when the drop has no start date (or is waiting for its first bid). */
61
+ startTime: number | null;
62
+ /** Unix seconds, null when open-ended (or waiting for its first bid). */
63
+ endTime: number | null;
64
+ /** Set while a listing's timer is not running: seconds it will run after the first bid or purchase. */
65
+ durationAfterFirstBid: number | null;
66
+ /** Claim: total minted. Listing: units sold. */
67
+ totalMinted: bigint | null;
68
+ /** null = unlimited (or unknown). */
69
+ maxSupply: bigint | null;
70
+ /** maxSupply - totalMinted, never below 0. null when unlimited or unknown. */
71
+ remaining: bigint | null;
72
+ price: bigint | null;
73
+ /** ERC20 the price is paid in; null = native currency of the chain. */
74
+ paymentToken: Address | null;
75
+ /** Auction listings: the highest bid so far. The marketplace API reports no bid count. */
76
+ bid: {
77
+ amount: bigint;
78
+ } | null;
79
+ /** The winning bid (auction) or the price (fixed price) once phase is 'sold-out'. */
80
+ soldFor: bigint | null;
81
+ /** The last read failed; every other field is from the last good read. */
82
+ stale: boolean;
83
+ /** Device time (ms since epoch) of the last good read, null before one. */
84
+ updatedAt: number | null;
85
+ /** The error of the latest failed read, null when the latest read worked. */
86
+ error: Error | null;
87
+ /**
88
+ * Server time minus device time, in ms, when the device clock is off by more than 30 s; else 0.
89
+ * Server time is now() = Date.now() + clockOffsetMs. Pass it to <DropCountdown now>.
90
+ */
91
+ clockOffsetMs: number;
92
+ };
93
+ type UseDropStatusOptions = {
94
+ /** Poll interval, default 30_000. Polling pauses while the tab is hidden. */
95
+ refreshMs?: number;
96
+ /** chainId -> JSON-RPC URL, like the other hooks. Claims are read with one multicall per chain. */
97
+ rpcUrls?: RpcUrls;
98
+ /** Replaces the viem client per chain (tests, custom transports). */
99
+ getClient?: (chainId: number) => PublicClient;
100
+ /** Replaces fetch for marketplace requests (tests). */
101
+ fetch?: typeof fetch;
102
+ /** false = read nothing. Default true. */
103
+ enabled?: boolean;
104
+ };
105
+
106
+ /**
107
+ * Status of every source, aligned with `sources`. Reads once on mount and then every `refreshMs` (30 s) while the tab is visible,
108
+ * on return to the tab, and right after the nearest start or end time passes.
109
+ *
110
+ * A failed read never changes what the page believes about a drop: the last good value stays with `stale: true` and `error` set;
111
+ * with no good value yet the phase is 'unknown', never 'upcoming'.
112
+ *
113
+ * The array you pass may be a new literal on every render. Sources are keyed by their content, so that does not read again.
114
+ */
115
+ declare function useDropStatus(sources: readonly StatusSource[], options?: UseDropStatusOptions): DropStatus[];
116
+
117
+ type PhaseInput = Pick<Snapshot, 'startTime' | 'endTime' | 'totalMinted' | 'maxSupply' | 'auction' | 'closed' | 'bid'>;
118
+ /**
119
+ * The phase of a read at `nowSec` (unix seconds). A pure function of the snapshot and the clock:
120
+ * the same data gives the same phase on a server and in a browser.
121
+ */
122
+ declare function phaseOf(s: PhaseInput, nowSec: number): Exclude<DropPhase, 'unknown'>;
123
+ /** What a failed or missing read looks like: unknown, never "upcoming". */
124
+ declare function unknownStatus(error?: Error | null): DropStatus;
125
+ type StatusMeta = {
126
+ stale: boolean;
127
+ updatedAt: number | null;
128
+ error: Error | null;
129
+ clockOffsetMs: number;
130
+ };
131
+ /** Snapshot + clock -> the full status. */
132
+ declare function statusOf(s: Snapshot, nowSec: number, meta: StatusMeta): DropStatus;
133
+ /** Server time minus device time, or 0 when the device is within CLOCK_TOLERANCE_MS of it. */
134
+ declare function clockOffsetOf(serverNowMs: number, deviceNowMs: number): number;
135
+ /** '6h' | '30m' | '2d' | '90s' | '1w' | seconds (number) -> seconds. Throws RangeError on anything else. */
136
+ declare function parseDuration(value: string | number): number;
137
+ type CountdownParts = {
138
+ d: number;
139
+ h: number;
140
+ m: number;
141
+ s: number;
142
+ };
143
+ /**
144
+ * Splits whole seconds into d/h/m/s. `units` (a subset of "dhms") picks the units to show; the largest one
145
+ * takes the overflow, so "hms" turns 2 days into 48 hours and "ms" shows only minutes and seconds.
146
+ */
147
+ declare function countdownParts(totalSeconds: number, units?: string): CountdownParts;
148
+
149
+ type CountdownState = {
150
+ /** counting: `to` is still ahead. done: it has passed. */
151
+ state: 'counting' | 'done';
152
+ /** false while outside the showWithin window (or past hideAfter). The default markup renders nothing then. */
153
+ visible: boolean;
154
+ /** Whole seconds left, rounded up; 0 once done. */
155
+ secondsLeft: number;
156
+ /** d/h/m/s for the chosen format; the largest unit shown takes the overflow. */
157
+ parts: CountdownParts;
158
+ /** The default text, e.g. "2d 03h 04m 05s". */
159
+ text: string;
160
+ };
161
+ type DropCountdownProps = {
162
+ /** Unix seconds. null renders nothing. */
163
+ to: number | null;
164
+ /** Show only this long before `to`: '6h' | '30m' | '2d' | '90s' | '1w' | seconds. Default: always. */
165
+ showWithin?: string | number;
166
+ /** Keep showing the done state this long after `to`: same units. Default: hide the moment `to` passes. */
167
+ hideAfter?: string | number;
168
+ /** Current time in ms. Default Date.now. Use `() => Date.now() + status.clockOffsetMs` to follow the server clock. */
169
+ now?: () => number;
170
+ /** Units to show, any of "dhms" in that order. Default "dhms". "hms" turns 2 days into 48 hours. */
171
+ format?: string;
172
+ className?: string;
173
+ /** Replaces the default text. A function replaces the whole markup: it is called whenever `to` is set, visible or not. */
174
+ children?: ReactNode | ((c: CountdownState) => ReactNode);
175
+ };
176
+ /** "2d 03h 04m 05s": leading zero units are dropped (never the last), the rest after the first are padded. */
177
+ declare function formatCountdown(parts: CountdownParts, units?: string): string;
178
+ declare function DropCountdown(props: DropCountdownProps): ReactNode;
179
+
180
+ type ExtensionSpec = '721' | '1155';
181
+ type ExtensionAbi = 'Claim721V5Optimization' | 'Claim1155V5Optimization' | 'Claim721V7' | 'Claim1155V7';
182
+ type ExtensionEntry = {
183
+ chainId: number;
184
+ address: Address;
185
+ generation: 'V6' | 'V7' | 'V8';
186
+ spec: ExtensionSpec;
187
+ abi: ExtensionAbi;
188
+ };
189
+
190
+ /** Manifold marketplace API host per chain. */
191
+ declare const MARKETPLACE_API: Readonly<Record<number, string>>;
192
+ /**
193
+ * Manifold marketplace contract per chain. Mainnet has its own deployment; Optimism, Base, Shape and Sepolia
194
+ * share one. Each was checked against the API of its chain: POST /listing/<address>/batch answers with
195
+ * listings whose `address` field is that contract. Other chains must pass `marketplace` in the source.
196
+ */
197
+ declare const MARKETPLACE_ADDRESSES: Readonly<Record<number, Address>>;
198
+ type ReadOutcome = {
199
+ ok: true;
200
+ snapshot: Snapshot;
201
+ } | {
202
+ ok: false;
203
+ error: Error;
204
+ };
205
+ type ReadDeps = {
206
+ rpcUrls?: RpcUrls;
207
+ /** Replaces the viem client per chain. */
208
+ getClient?: (chainId: number) => PublicClient;
209
+ fetch?: typeof fetch;
210
+ /** Extension found for a claim, kept between reads so only the first read tries every extension. */
211
+ extensionCache?: Map<string, ExtensionEntry>;
212
+ /** Listings that can no longer change (finalized), kept between reads so they are not fetched again. */
213
+ finalCache?: Map<string, Snapshot>;
214
+ /** Wall-clock ms, for tests. */
215
+ nowMs?: () => number;
216
+ signal?: AbortSignal;
217
+ };
218
+ type ReadResult = {
219
+ /** Aligned with the sources passed in. */
220
+ outcomes: ReadOutcome[];
221
+ /** Server time in ms from the freshest evidence (HTTP Date header, else block timestamp), measured at device time `measuredAtMs`. */
222
+ serverNowMs: number | null;
223
+ measuredAtMs: number | null;
224
+ };
225
+ /** The stable identity of a source: same key = same thing to read. Addresses are case-folded. */
226
+ declare function sourceKey(source: StatusSource): string;
227
+ type BigNumberJson = {
228
+ hex?: unknown;
229
+ _hex?: unknown;
230
+ } | string | number | null | undefined;
231
+ type ListingJson = {
232
+ id?: BigNumberJson;
233
+ finalized?: boolean;
234
+ totalSold?: BigNumberJson;
235
+ details?: {
236
+ type_?: number;
237
+ initialAmount?: BigNumberJson;
238
+ totalAvailable?: BigNumberJson;
239
+ erc20?: string | null;
240
+ startTime?: number;
241
+ endTime?: number;
242
+ };
243
+ bid?: {
244
+ amount?: BigNumberJson;
245
+ };
246
+ };
247
+ /**
248
+ * A listing as a Snapshot. `batch` is the entry of POST /listing/<marketplace>/batch (details); `single` is the answer of
249
+ * GET /listing/<marketplace>/<id>, which carries the bid and totalSold the batch leaves out or reports as 0.
250
+ * Pass only `batch` for a listing that is not open yet. Pure.
251
+ */
252
+ declare function snapshotFromListing(batch: ListingJson, single?: ListingJson | null): Snapshot;
253
+ /**
254
+ * Reads every source once. All claims of one chain share one multicall; all listings of one marketplace share one batch POST. A source that cannot be read gets an
255
+ * error outcome and never a made-up value. Never throws.
256
+ */
257
+ declare function readSources(sources: readonly StatusSource[], deps?: ReadDeps): Promise<ReadResult>;
258
+
259
+ export { type ClaimSource, type CountdownParts, type CountdownState, DropCountdown, type DropCountdownProps, type DropPhase, type DropStatus, type ListingSource, MARKETPLACE_ADDRESSES, MARKETPLACE_API, type PhaseInput, type ReadDeps, type ReadOutcome, type ReadResult, type Snapshot, type StatusMeta, type StatusSource, type UseDropStatusOptions, clockOffsetOf, countdownParts, formatCountdown, parseDuration, phaseOf, readSources, snapshotFromListing, sourceKey, statusOf, unknownStatus, useDropStatus };