@manifoldxyz/dropkit 0.4.0 → 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.
- package/README.md +95 -1
- package/dist/cart.cjs +39 -35
- package/dist/cart.cjs.map +1 -1
- package/dist/cart.js +13 -9
- package/dist/cart.js.map +1 -1
- package/dist/{chunk-SUJQB3SL.js → chunk-VHAAGH7Z.js} +60 -605
- package/dist/chunk-VHAAGH7Z.js.map +1 -0
- package/dist/chunk-W7OBZPCY.js +619 -0
- package/dist/chunk-W7OBZPCY.js.map +1 -0
- package/dist/{chunk-GO4TR3HB.cjs → chunk-XWONUCGA.cjs} +46 -591
- package/dist/chunk-XWONUCGA.cjs.map +1 -0
- package/dist/chunk-YGBIJOKQ.cjs +619 -0
- package/dist/chunk-YGBIJOKQ.cjs.map +1 -0
- package/dist/index.cjs +184 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +62 -17
- package/dist/index.js.map +1 -1
- package/dist/status.cjs +570 -0
- package/dist/status.cjs.map +1 -0
- package/dist/status.d.cts +259 -0
- package/dist/status.d.ts +259 -0
- package/dist/status.js +570 -0
- package/dist/status.js.map +1 -0
- package/package.json +11 -1
- package/dist/chunk-GO4TR3HB.cjs.map +0 -1
- package/dist/chunk-SUJQB3SL.js.map +0 -1
package/dist/status.cjs
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
var _chunkXWONUCGAcjs = require('./chunk-XWONUCGA.cjs');
|
|
8
|
+
|
|
9
|
+
// src/status/useDropStatus.ts
|
|
10
|
+
var _react = require('react');
|
|
11
|
+
|
|
12
|
+
// src/status/core.ts
|
|
13
|
+
var soldOut = (s) => s.maxSupply !== null && s.maxSupply > 0n && s.totalMinted !== null && s.totalMinted >= s.maxSupply;
|
|
14
|
+
var windowClosed = (s, nowSec) => s.endTime !== null && nowSec >= s.endTime;
|
|
15
|
+
function phaseOf(s, nowSec) {
|
|
16
|
+
if (soldOut(s)) return "sold-out";
|
|
17
|
+
if (s.auction && s.bid !== null && (s.closed || windowClosed(s, nowSec))) return "sold-out";
|
|
18
|
+
if (s.closed) return "ended";
|
|
19
|
+
if (s.startTime !== null && nowSec < s.startTime) return "upcoming";
|
|
20
|
+
if (windowClosed(s, nowSec)) return "ended";
|
|
21
|
+
return "live";
|
|
22
|
+
}
|
|
23
|
+
function unknownStatus(error = null) {
|
|
24
|
+
return {
|
|
25
|
+
phase: "unknown",
|
|
26
|
+
startTime: null,
|
|
27
|
+
endTime: null,
|
|
28
|
+
durationAfterFirstBid: null,
|
|
29
|
+
totalMinted: null,
|
|
30
|
+
maxSupply: null,
|
|
31
|
+
remaining: null,
|
|
32
|
+
price: null,
|
|
33
|
+
paymentToken: null,
|
|
34
|
+
bid: null,
|
|
35
|
+
soldFor: null,
|
|
36
|
+
stale: false,
|
|
37
|
+
updatedAt: null,
|
|
38
|
+
error,
|
|
39
|
+
clockOffsetMs: 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function statusOf(s, nowSec, meta) {
|
|
43
|
+
const phase = phaseOf(s, nowSec);
|
|
44
|
+
const remaining = s.maxSupply !== null && s.totalMinted !== null ? s.maxSupply > s.totalMinted ? s.maxSupply - s.totalMinted : 0n : null;
|
|
45
|
+
const soldFor = phase !== "sold-out" ? null : s.auction ? _nullishCoalesce(_optionalChain([s, 'access', _ => _.bid, 'optionalAccess', _2 => _2.amount]), () => ( null)) : s.price;
|
|
46
|
+
return {
|
|
47
|
+
phase,
|
|
48
|
+
startTime: s.startTime,
|
|
49
|
+
endTime: s.endTime,
|
|
50
|
+
durationAfterFirstBid: s.durationAfterFirstBid,
|
|
51
|
+
totalMinted: s.totalMinted,
|
|
52
|
+
maxSupply: s.maxSupply,
|
|
53
|
+
remaining,
|
|
54
|
+
price: s.price,
|
|
55
|
+
paymentToken: s.paymentToken,
|
|
56
|
+
bid: s.bid,
|
|
57
|
+
soldFor,
|
|
58
|
+
...meta
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
var CLOCK_TOLERANCE_MS = 3e4;
|
|
62
|
+
function clockOffsetOf(serverNowMs, deviceNowMs) {
|
|
63
|
+
const offset = serverNowMs - deviceNowMs;
|
|
64
|
+
return Math.abs(offset) <= CLOCK_TOLERANCE_MS ? 0 : offset;
|
|
65
|
+
}
|
|
66
|
+
var UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400, w: 604800 };
|
|
67
|
+
function parseDuration(value) {
|
|
68
|
+
if (typeof value === "number") {
|
|
69
|
+
if (!Number.isFinite(value) || value < 0) throw new RangeError(`Invalid duration ${String(value)}: use seconds >= 0 or a string like "6h".`);
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
const m = /^(\d+(?:\.\d+)?)\s*([smhdw])?$/i.exec(value.trim());
|
|
73
|
+
if (!m) throw new RangeError(`Invalid duration "${value}": use seconds or a string like "6h", "30m", "2d".`);
|
|
74
|
+
return Number(m[1]) * UNIT_SECONDS[(_nullishCoalesce(m[2], () => ( "s"))).toLowerCase()];
|
|
75
|
+
}
|
|
76
|
+
function countdownParts(totalSeconds, units = "dhms") {
|
|
77
|
+
let left = Math.max(0, Math.floor(totalSeconds));
|
|
78
|
+
const shown = ["d", "h", "m", "s"].filter((u) => units.includes(u));
|
|
79
|
+
const use = shown.length > 0 ? shown : ["d", "h", "m", "s"];
|
|
80
|
+
const out = { d: 0, h: 0, m: 0, s: 0 };
|
|
81
|
+
for (const u of use) {
|
|
82
|
+
const size = UNIT_SECONDS[u];
|
|
83
|
+
out[u] = Math.floor(left / size);
|
|
84
|
+
left -= out[u] * size;
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/status/read.ts
|
|
90
|
+
var _viem = require('viem');
|
|
91
|
+
var MARKETPLACE_API = {
|
|
92
|
+
1: "https://marketplace.api.manifoldxyz.dev",
|
|
93
|
+
11155111: "https://sepolia.marketplace.api.manifoldxyz.dev",
|
|
94
|
+
8453: "https://base.marketplace.api.manifoldxyz.dev",
|
|
95
|
+
10: "https://optimism.marketplace.api.manifoldxyz.dev",
|
|
96
|
+
360: "https://shape.marketplace.api.manifoldxyz.dev"
|
|
97
|
+
};
|
|
98
|
+
var MARKETPLACE_ADDRESSES = {
|
|
99
|
+
1: "0x3a3548e060be10c2614d0a4cb0c03cc9093fd799",
|
|
100
|
+
10: "0x5246807fb65d87b0d0a234e0f3d42374de83b421",
|
|
101
|
+
8453: "0x5246807fb65d87b0d0a234e0f3d42374de83b421",
|
|
102
|
+
360: "0x5246807fb65d87b0d0a234e0f3d42374de83b421",
|
|
103
|
+
11155111: "0x5246807fb65d87b0d0a234e0f3d42374de83b421"
|
|
104
|
+
};
|
|
105
|
+
var SINGLE_CONCURRENCY = 3;
|
|
106
|
+
var DURATION_CUTOFF = 1e11;
|
|
107
|
+
var asError = (e) => e instanceof Error ? e : new Error(String(e));
|
|
108
|
+
var fail = (message, cause) => ({ ok: false, error: new Error(message, cause === void 0 ? void 0 : { cause }) });
|
|
109
|
+
function sourceKey(source) {
|
|
110
|
+
if (source.type === "manifold-claim") {
|
|
111
|
+
return JSON.stringify(["claim", source.chainId, source.creatorContract.toLowerCase(), source.instanceId, _nullishCoalesce(_optionalChain([source, 'access', _3 => _3.extensionAddress, 'optionalAccess', _4 => _4.toLowerCase, 'call', _5 => _5()]), () => ( null))]);
|
|
112
|
+
}
|
|
113
|
+
return JSON.stringify(["listing", source.chainId, source.listingId, _nullishCoalesce(_optionalChain([source, 'access', _6 => _6.marketplace, 'optionalAccess', _7 => _7.toLowerCase, 'call', _8 => _8()]), () => ( null))]);
|
|
114
|
+
}
|
|
115
|
+
var TIMESTAMP_ABI = _viem.parseAbi.call(void 0, ["function getCurrentBlockTimestamp() view returns (uint256 timestamp)"]);
|
|
116
|
+
function seconds(v, field) {
|
|
117
|
+
if (v === 0n) return null;
|
|
118
|
+
if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error(`Claim ${field} does not fit a safe integer`);
|
|
119
|
+
return Number(v);
|
|
120
|
+
}
|
|
121
|
+
function snapshotFromClaim(claim) {
|
|
122
|
+
return {
|
|
123
|
+
kind: "claim",
|
|
124
|
+
startTime: seconds(BigInt(claim.startDate), "startDate"),
|
|
125
|
+
endTime: seconds(BigInt(claim.endDate), "endDate"),
|
|
126
|
+
durationAfterFirstBid: null,
|
|
127
|
+
totalMinted: claim.total,
|
|
128
|
+
maxSupply: claim.totalMax === 0n ? null : claim.totalMax,
|
|
129
|
+
price: claim.cost,
|
|
130
|
+
paymentToken: claim.erc20 === _viem.zeroAddress ? null : _viem.getAddress.call(void 0, claim.erc20),
|
|
131
|
+
bid: null,
|
|
132
|
+
auction: false,
|
|
133
|
+
closed: false
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function validateClaim(source) {
|
|
137
|
+
if (!_viem.isAddress.call(void 0, source.creatorContract, { strict: false })) return new Error("creatorContract is not a valid address.");
|
|
138
|
+
if (typeof source.instanceId !== "string" || !/^\d+$/.test(source.instanceId)) return new Error("instanceId must be a non-negative integer string.");
|
|
139
|
+
let pinned = null;
|
|
140
|
+
if (source.extensionAddress !== void 0) {
|
|
141
|
+
if (!_viem.isAddress.call(void 0, source.extensionAddress, { strict: false })) return new Error("extensionAddress is not a valid address.");
|
|
142
|
+
pinned = _nullishCoalesce(_chunkXWONUCGAcjs.findExtension.call(void 0, source.chainId, source.extensionAddress.toLowerCase()), () => ( null));
|
|
143
|
+
if (!pinned) return new Error(`Extension ${source.extensionAddress} is not a supported Manifold claim extension on chain ${String(source.chainId)}.`);
|
|
144
|
+
}
|
|
145
|
+
return { creator: _viem.getAddress.call(void 0, source.creatorContract), instanceId: BigInt(source.instanceId), pinned };
|
|
146
|
+
}
|
|
147
|
+
async function multicall(client, calls) {
|
|
148
|
+
const multicall3 = _optionalChain([client, 'access', _9 => _9.chain, 'optionalAccess', _10 => _10.contracts, 'optionalAccess', _11 => _11.multicall3, 'optionalAccess', _12 => _12.address]);
|
|
149
|
+
const contracts = multicall3 ? [...calls, { address: multicall3, abi: TIMESTAMP_ABI, functionName: "getCurrentBlockTimestamp" }] : calls;
|
|
150
|
+
const raw = await client.multicall({ contracts, allowFailure: true, batchSize: 0 });
|
|
151
|
+
const results = raw.slice(0, calls.length).map((r) => r.status === "success" ? r : { status: "failure", error: asError(r.error) });
|
|
152
|
+
const last = multicall3 ? raw[calls.length] : void 0;
|
|
153
|
+
return { results, blockTimestamp: _optionalChain([last, 'optionalAccess', _13 => _13.status]) === "success" && typeof last.result === "bigint" ? last.result : null };
|
|
154
|
+
}
|
|
155
|
+
async function readClaimChain(client, chainId, work, deps) {
|
|
156
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
157
|
+
const cache = deps.extensionCache;
|
|
158
|
+
const cacheKey = (w) => `${String(chainId)}:${w.creator.toLowerCase()}:${w.instanceId.toString()}`;
|
|
159
|
+
const onChain = _chunkXWONUCGAcjs.EXTENSIONS.filter((e) => e.chainId === chainId);
|
|
160
|
+
const probes = [];
|
|
161
|
+
for (const w of work) {
|
|
162
|
+
const known = _nullishCoalesce(_nullishCoalesce(w.pinned, () => ( _optionalChain([cache, 'optionalAccess', _14 => _14.get, 'call', _15 => _15(cacheKey(w))]))), () => ( null));
|
|
163
|
+
const candidates = known ? [known] : onChain;
|
|
164
|
+
if (candidates.length === 0) outcomes.set(w.index, fail(`Manifold claims are not available on chain ${String(chainId)}.`));
|
|
165
|
+
for (const entry of candidates) probes.push({ w, entry });
|
|
166
|
+
}
|
|
167
|
+
if (probes.length === 0) return { outcomes, blockTimestamp: null };
|
|
168
|
+
const { results, blockTimestamp } = await multicall(
|
|
169
|
+
client,
|
|
170
|
+
probes.map(({ w, entry }) => ({ address: _viem.getAddress.call(void 0, entry.address), abi: _chunkXWONUCGAcjs.CLAIM_ABIS[entry.abi], functionName: "getClaim", args: [w.creator, w.instanceId] }))
|
|
171
|
+
);
|
|
172
|
+
const byWork = /* @__PURE__ */ new Map();
|
|
173
|
+
probes.forEach((p, i) => byWork.set(p.w, [..._nullishCoalesce(byWork.get(p.w), () => ( [])), { entry: p.entry, result: results[i] }]));
|
|
174
|
+
for (const [w, list] of byWork) {
|
|
175
|
+
const hits = list.filter((x) => x.result.status === "success");
|
|
176
|
+
const narrowed = list.length === 1;
|
|
177
|
+
if (hits.length === 0) {
|
|
178
|
+
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}.`));
|
|
179
|
+
} else if (hits.length > 1) {
|
|
180
|
+
outcomes.set(w.index, fail(`Claim ${w.source.instanceId} exists on ${String(hits.length)} extensions of ${w.source.creatorContract}. Set extensionAddress.`));
|
|
181
|
+
} else {
|
|
182
|
+
const hit = hits[0];
|
|
183
|
+
try {
|
|
184
|
+
const snapshot = snapshotFromClaim(_chunkXWONUCGAcjs.decodeClaim.call(void 0, hit.entry.abi, hit.result.result));
|
|
185
|
+
if (!w.pinned) _optionalChain([cache, 'optionalAccess', _16 => _16.set, 'call', _17 => _17(cacheKey(w), hit.entry)]);
|
|
186
|
+
outcomes.set(w.index, { ok: true, snapshot });
|
|
187
|
+
} catch (e) {
|
|
188
|
+
outcomes.set(w.index, fail(asError(e).message, e));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { outcomes, blockTimestamp };
|
|
193
|
+
}
|
|
194
|
+
function bigOf(v) {
|
|
195
|
+
if (v === null || v === void 0) return null;
|
|
196
|
+
if (typeof v === "number") return Number.isInteger(v) ? BigInt(v) : null;
|
|
197
|
+
if (typeof v === "string") return /^(0x[0-9a-f]+|\d+)$/i.test(v) ? BigInt(v) : null;
|
|
198
|
+
const hex = typeof v.hex === "string" ? v.hex : typeof v._hex === "string" ? v._hex : null;
|
|
199
|
+
return hex !== null && /^0x[0-9a-f]*$/i.test(hex) ? BigInt(hex === "0x" ? "0x0" : hex) : null;
|
|
200
|
+
}
|
|
201
|
+
function listingIdOf(raw) {
|
|
202
|
+
const id = bigOf(raw.id);
|
|
203
|
+
return id === null ? null : id.toString();
|
|
204
|
+
}
|
|
205
|
+
function timeOf(v) {
|
|
206
|
+
if (typeof v !== "number" || !Number.isSafeInteger(v) || v < 0) throw new Error("Listing has an invalid start or end time.");
|
|
207
|
+
return v;
|
|
208
|
+
}
|
|
209
|
+
function snapshotFromListing(batch, single) {
|
|
210
|
+
const src = _nullishCoalesce(single, () => ( batch));
|
|
211
|
+
const d = _nullishCoalesce(src.details, () => ( batch.details));
|
|
212
|
+
if (!d) throw new Error("Listing has no details.");
|
|
213
|
+
const start = timeOf(d.startTime);
|
|
214
|
+
const end = timeOf(d.endTime);
|
|
215
|
+
const duration = start === 0 && end > 0 && end < DURATION_CUTOFF;
|
|
216
|
+
const auction = d.type_ === 1;
|
|
217
|
+
const amount = _nullishCoalesce(bigOf(_optionalChain([src, 'access', _18 => _18.bid, 'optionalAccess', _19 => _19.amount])), () => ( 0n));
|
|
218
|
+
const erc20 = typeof d.erc20 === "string" && _viem.isAddress.call(void 0, d.erc20, { strict: false }) && d.erc20 !== _viem.zeroAddress ? _viem.getAddress.call(void 0, d.erc20) : null;
|
|
219
|
+
return {
|
|
220
|
+
kind: "listing",
|
|
221
|
+
startTime: start === 0 ? null : start,
|
|
222
|
+
endTime: duration || end === 0 ? null : end,
|
|
223
|
+
durationAfterFirstBid: duration ? end : null,
|
|
224
|
+
// totalSold is left out when it is 0 on some routes.
|
|
225
|
+
totalMinted: single ? _nullishCoalesce(bigOf(single.totalSold), () => ( 0n)) : null,
|
|
226
|
+
maxSupply: bigOf(d.totalAvailable),
|
|
227
|
+
price: bigOf(d.initialAmount),
|
|
228
|
+
paymentToken: erc20,
|
|
229
|
+
bid: auction && amount > 0n ? { amount } : null,
|
|
230
|
+
auction,
|
|
231
|
+
closed: src.finalized === true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function mapLimit(items, limit, fn) {
|
|
235
|
+
const out = new Array(items.length);
|
|
236
|
+
let next = 0;
|
|
237
|
+
const worker = async () => {
|
|
238
|
+
while (next < items.length) {
|
|
239
|
+
const i = next++;
|
|
240
|
+
out[i] = await fn(items[i]);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
async function getJson(f, url, init, signal) {
|
|
247
|
+
const res = await f(url, { ...init, ...signal ? { signal } : {} });
|
|
248
|
+
if (!res.ok) throw new Error(`Marketplace API answered ${String(res.status)} for ${url.replace(/^https?:\/\/[^/]+/, "")}`);
|
|
249
|
+
const header = _nullishCoalesce(_optionalChain([res, 'access', _20 => _20.headers, 'optionalAccess', _21 => _21.get, 'call', _22 => _22("date")]), () => ( null));
|
|
250
|
+
const date = header ? Date.parse(header) : NaN;
|
|
251
|
+
return { json: await res.json(), date: Number.isNaN(date) ? null : date };
|
|
252
|
+
}
|
|
253
|
+
async function readListings(work, deps, outcomes) {
|
|
254
|
+
const f = _nullishCoalesce(deps.fetch, () => ( globalThis.fetch));
|
|
255
|
+
const nowMs = _nullishCoalesce(deps.nowMs, () => ( Date.now));
|
|
256
|
+
let clock = null;
|
|
257
|
+
const groups = /* @__PURE__ */ new Map();
|
|
258
|
+
for (const w of work) {
|
|
259
|
+
const done = _optionalChain([deps, 'access', _23 => _23.finalCache, 'optionalAccess', _24 => _24.get, 'call', _25 => _25(w.key)]);
|
|
260
|
+
if (done) {
|
|
261
|
+
outcomes.set(w.index, { ok: true, snapshot: done });
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const gk = `${w.api}|${w.marketplace.toLowerCase()}`;
|
|
265
|
+
groups.set(gk, [..._nullishCoalesce(groups.get(gk), () => ( [])), w]);
|
|
266
|
+
}
|
|
267
|
+
for (const items of groups.values()) {
|
|
268
|
+
const { api, marketplace } = items[0];
|
|
269
|
+
const ids = [...new Set(items.map((w) => w.source.listingId))];
|
|
270
|
+
let rows;
|
|
271
|
+
try {
|
|
272
|
+
const { json, date } = await getJson(f, `${api}/listing/${marketplace}/batch`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ listingIds: ids }) }, deps.signal);
|
|
273
|
+
if (!Array.isArray(json)) throw new Error("Marketplace API answered with something other than a list.");
|
|
274
|
+
rows = json;
|
|
275
|
+
if (date !== null) clock = { serverNowMs: date, measuredAtMs: nowMs() };
|
|
276
|
+
} catch (e) {
|
|
277
|
+
for (const w of items) outcomes.set(w.index, fail(asError(e).message, e));
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const byId = /* @__PURE__ */ new Map();
|
|
281
|
+
for (const r of rows) {
|
|
282
|
+
const id = listingIdOf(r);
|
|
283
|
+
if (id !== null) byId.set(id, r);
|
|
284
|
+
}
|
|
285
|
+
const nowSec = Math.floor((clock ? clock.serverNowMs : nowMs()) / 1e3);
|
|
286
|
+
const pending = [];
|
|
287
|
+
for (const w of items) {
|
|
288
|
+
const row = byId.get(w.source.listingId);
|
|
289
|
+
if (!row) {
|
|
290
|
+
outcomes.set(w.index, fail(`Listing ${w.source.listingId} was not found on chain ${String(w.source.chainId)}.`));
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const s = snapshotFromListing(row);
|
|
295
|
+
const upcoming = s.startTime !== null && s.startTime > nowSec && row.finalized !== true;
|
|
296
|
+
if (upcoming) outcomes.set(w.index, { ok: true, snapshot: s });
|
|
297
|
+
else pending.push({ w, row });
|
|
298
|
+
} catch (e) {
|
|
299
|
+
outcomes.set(w.index, fail(asError(e).message, e));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const singles = await mapLimit(pending, SINGLE_CONCURRENCY, async ({ w, row }) => {
|
|
303
|
+
try {
|
|
304
|
+
const { json } = await getJson(f, `${api}/listing/${marketplace}/${w.source.listingId}`, void 0, deps.signal);
|
|
305
|
+
if (typeof json !== "object" || json === null || Array.isArray(json)) throw new Error("Marketplace API answered with something other than a listing.");
|
|
306
|
+
const answer = json;
|
|
307
|
+
const id = listingIdOf(answer);
|
|
308
|
+
if (id !== null && id !== w.source.listingId) throw new Error(`Marketplace API answered listing ${id} for listing ${w.source.listingId}.`);
|
|
309
|
+
const snapshot = snapshotFromListing(row, answer);
|
|
310
|
+
if (snapshot.closed) _optionalChain([deps, 'access', _26 => _26.finalCache, 'optionalAccess', _27 => _27.set, 'call', _28 => _28(w.key, snapshot)]);
|
|
311
|
+
return { ok: true, snapshot };
|
|
312
|
+
} catch (e) {
|
|
313
|
+
return fail(asError(e).message, e);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
pending.forEach((p, i) => outcomes.set(p.w.index, singles[i]));
|
|
317
|
+
}
|
|
318
|
+
return { serverNowMs: _nullishCoalesce(_optionalChain([clock, 'optionalAccess', _29 => _29.serverNowMs]), () => ( null)), measuredAtMs: _nullishCoalesce(_optionalChain([clock, 'optionalAccess', _30 => _30.measuredAtMs]), () => ( null)) };
|
|
319
|
+
}
|
|
320
|
+
function resolveMarketplace(source) {
|
|
321
|
+
const api = MARKETPLACE_API[source.chainId];
|
|
322
|
+
if (!api) return new Error(`The marketplace API does not serve chain ${String(source.chainId)}.`);
|
|
323
|
+
const marketplace = _nullishCoalesce(source.marketplace, () => ( MARKETPLACE_ADDRESSES[source.chainId]));
|
|
324
|
+
if (!marketplace) return new Error(`Pass marketplace for chain ${String(source.chainId)}: there is no default marketplace address.`);
|
|
325
|
+
if (!_viem.isAddress.call(void 0, marketplace, { strict: false })) return new Error("marketplace is not a valid address.");
|
|
326
|
+
if (typeof source.listingId !== "string" || !/^\d+$/.test(source.listingId)) return new Error("listingId must be a non-negative integer string.");
|
|
327
|
+
return { marketplace: marketplace.toLowerCase(), api };
|
|
328
|
+
}
|
|
329
|
+
async function readSources(sources, deps = {}) {
|
|
330
|
+
const nowMs = _nullishCoalesce(deps.nowMs, () => ( Date.now));
|
|
331
|
+
const outcomes = new Array(sources.length);
|
|
332
|
+
const out = /* @__PURE__ */ new Map();
|
|
333
|
+
const claimsByChain = /* @__PURE__ */ new Map();
|
|
334
|
+
const listings = [];
|
|
335
|
+
sources.forEach((source, index) => {
|
|
336
|
+
if (source.type === "manifold-claim") {
|
|
337
|
+
const v = validateClaim(source);
|
|
338
|
+
if (v instanceof Error) out.set(index, { ok: false, error: v });
|
|
339
|
+
else claimsByChain.set(source.chainId, [..._nullishCoalesce(claimsByChain.get(source.chainId), () => ( [])), { index, source, ...v }]);
|
|
340
|
+
} else if (source.type === "marketplace-listing") {
|
|
341
|
+
const v = resolveMarketplace(source);
|
|
342
|
+
if (v instanceof Error) out.set(index, { ok: false, error: v });
|
|
343
|
+
else listings.push({ index, source, ...v, key: sourceKey(source) });
|
|
344
|
+
} else {
|
|
345
|
+
out.set(index, fail("Unknown source type."));
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
let blockClock = null;
|
|
349
|
+
const clients = /* @__PURE__ */ new Map();
|
|
350
|
+
const clientFor = (chainId) => {
|
|
351
|
+
let c = clients.get(chainId);
|
|
352
|
+
if (!c) {
|
|
353
|
+
c = deps.getClient ? deps.getClient(chainId) : _chunkXWONUCGAcjs.createReadClient.call(void 0, { chainId, ...deps.rpcUrls ? { rpcUrls: deps.rpcUrls } : {}, useBridge: false });
|
|
354
|
+
clients.set(chainId, c);
|
|
355
|
+
}
|
|
356
|
+
return c;
|
|
357
|
+
};
|
|
358
|
+
const claimJobs = [...claimsByChain].map(async ([chainId, work]) => {
|
|
359
|
+
try {
|
|
360
|
+
const r = await readClaimChain(clientFor(chainId), chainId, work, deps);
|
|
361
|
+
for (const [i, o] of r.outcomes) out.set(i, o);
|
|
362
|
+
if (r.blockTimestamp !== null) blockClock = { serverNowMs: Number(r.blockTimestamp) * 1e3, measuredAtMs: nowMs() };
|
|
363
|
+
} catch (e) {
|
|
364
|
+
for (const w of work) out.set(w.index, fail(asError(e).message, e));
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
const listingJob = readListings(listings, deps, out).catch((e) => {
|
|
368
|
+
for (const w of listings) if (!out.has(w.index)) out.set(w.index, fail(asError(e).message, e));
|
|
369
|
+
return { serverNowMs: null, measuredAtMs: null };
|
|
370
|
+
});
|
|
371
|
+
const [, listingClock] = await Promise.all([Promise.all(claimJobs), listingJob]);
|
|
372
|
+
for (let i = 0; i < sources.length; i++) outcomes[i] = _nullishCoalesce(out.get(i), () => ( fail("Source was not read.")));
|
|
373
|
+
const clock = listingClock.serverNowMs !== null ? { serverNowMs: listingClock.serverNowMs, measuredAtMs: listingClock.measuredAtMs } : blockClock;
|
|
374
|
+
return { outcomes, serverNowMs: _nullishCoalesce(_optionalChain([clock, 'optionalAccess', _31 => _31.serverNowMs]), () => ( null)), measuredAtMs: _nullishCoalesce(_optionalChain([clock, 'optionalAccess', _32 => _32.measuredAtMs]), () => ( null)) };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/status/useDropStatus.ts
|
|
378
|
+
var DEFAULT_REFRESH_MS = 3e4;
|
|
379
|
+
var BOUNDARY_DELAY_MS = 1e3;
|
|
380
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
381
|
+
var hidden = () => typeof document !== "undefined" && document.hidden;
|
|
382
|
+
function nextBoundary(entries, nowSec) {
|
|
383
|
+
let next = null;
|
|
384
|
+
for (const { snapshot } of entries) {
|
|
385
|
+
if (!snapshot) continue;
|
|
386
|
+
for (const t of [snapshot.startTime, snapshot.endTime]) if (t !== null && t > nowSec && (next === null || t < next)) next = t;
|
|
387
|
+
}
|
|
388
|
+
return next;
|
|
389
|
+
}
|
|
390
|
+
function useDropStatus(sources, options = {}) {
|
|
391
|
+
const { refreshMs = DEFAULT_REFRESH_MS, enabled = true } = options;
|
|
392
|
+
const keys = sources.map(sourceKey);
|
|
393
|
+
const key = JSON.stringify(keys);
|
|
394
|
+
const rpcKey = JSON.stringify(_nullishCoalesce(options.rpcUrls, () => ( null)));
|
|
395
|
+
const sourcesRef = _react.useRef.call(void 0, sources);
|
|
396
|
+
const optionsRef = _react.useRef.call(void 0, options);
|
|
397
|
+
_react.useEffect.call(void 0, () => {
|
|
398
|
+
sourcesRef.current = sources;
|
|
399
|
+
optionsRef.current = options;
|
|
400
|
+
});
|
|
401
|
+
const [entries, setEntries] = _react.useState.call(void 0, () => /* @__PURE__ */ new Map());
|
|
402
|
+
const [clockOffsetMs, setClockOffsetMs] = _react.useState.call(void 0, 0);
|
|
403
|
+
const [nowSec, setNowSec] = _react.useState.call(void 0, null);
|
|
404
|
+
const caches = _react.useRef.call(void 0, { extensionCache: /* @__PURE__ */ new Map(), finalCache: /* @__PURE__ */ new Map() });
|
|
405
|
+
const refreshRef = _react.useRef.call(void 0, () => Promise.resolve());
|
|
406
|
+
const offsetRef = _react.useRef.call(void 0, 0);
|
|
407
|
+
_react.useEffect.call(void 0, () => {
|
|
408
|
+
offsetRef.current = clockOffsetMs;
|
|
409
|
+
}, [clockOffsetMs]);
|
|
410
|
+
_react.useEffect.call(void 0, () => {
|
|
411
|
+
if (!enabled) return;
|
|
412
|
+
let active = true;
|
|
413
|
+
let inFlight = null;
|
|
414
|
+
const controller = new AbortController();
|
|
415
|
+
const run = async () => {
|
|
416
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
417
|
+
sourcesRef.current.forEach((s) => wanted.set(sourceKey(s), s));
|
|
418
|
+
const list = [...wanted.values()];
|
|
419
|
+
const o = optionsRef.current;
|
|
420
|
+
const deps = {
|
|
421
|
+
...caches.current,
|
|
422
|
+
signal: controller.signal,
|
|
423
|
+
...o.rpcUrls ? { rpcUrls: o.rpcUrls } : {},
|
|
424
|
+
...o.getClient ? { getClient: o.getClient } : {},
|
|
425
|
+
...o.fetch ? { fetch: o.fetch } : {}
|
|
426
|
+
};
|
|
427
|
+
const result = await readSources(list, deps);
|
|
428
|
+
if (!active) return;
|
|
429
|
+
const finishedAt = Date.now();
|
|
430
|
+
setEntries((prev) => {
|
|
431
|
+
const next = /* @__PURE__ */ new Map();
|
|
432
|
+
[...wanted.keys()].forEach((k, i) => {
|
|
433
|
+
const outcome = result.outcomes[i];
|
|
434
|
+
const before = prev.get(k);
|
|
435
|
+
next.set(
|
|
436
|
+
k,
|
|
437
|
+
outcome.ok ? { snapshot: outcome.snapshot, updatedAt: finishedAt, error: null } : { snapshot: _nullishCoalesce(_optionalChain([before, 'optionalAccess', _33 => _33.snapshot]), () => ( null)), updatedAt: _nullishCoalesce(_optionalChain([before, 'optionalAccess', _34 => _34.updatedAt]), () => ( null)), error: outcome.error }
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
return next;
|
|
441
|
+
});
|
|
442
|
+
const offset = result.serverNowMs !== null && result.measuredAtMs !== null ? clockOffsetOf(result.serverNowMs, result.measuredAtMs) : null;
|
|
443
|
+
if (offset !== null) setClockOffsetMs(offset);
|
|
444
|
+
setNowSec(Math.floor((finishedAt + (_nullishCoalesce(offset, () => ( offsetRef.current)))) / 1e3));
|
|
445
|
+
};
|
|
446
|
+
const refresh = () => {
|
|
447
|
+
inFlight ??= run().finally(() => {
|
|
448
|
+
inFlight = null;
|
|
449
|
+
});
|
|
450
|
+
return inFlight;
|
|
451
|
+
};
|
|
452
|
+
refreshRef.current = refresh;
|
|
453
|
+
void refresh();
|
|
454
|
+
const timer = refreshMs > 0 ? setInterval(() => {
|
|
455
|
+
if (!hidden()) void refresh();
|
|
456
|
+
}, refreshMs) : null;
|
|
457
|
+
const onVisible = () => {
|
|
458
|
+
if (!hidden()) void refresh();
|
|
459
|
+
};
|
|
460
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
461
|
+
return () => {
|
|
462
|
+
active = false;
|
|
463
|
+
controller.abort();
|
|
464
|
+
if (timer !== null) clearInterval(timer);
|
|
465
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
466
|
+
refreshRef.current = () => Promise.resolve();
|
|
467
|
+
};
|
|
468
|
+
}, [key, enabled, refreshMs, rpcKey]);
|
|
469
|
+
_react.useEffect.call(void 0, () => {
|
|
470
|
+
if (!enabled) return;
|
|
471
|
+
const serverNow = Date.now() + clockOffsetMs;
|
|
472
|
+
const next = nextBoundary(entries.values(), Math.floor(serverNow / 1e3));
|
|
473
|
+
if (next === null) return;
|
|
474
|
+
const delay = Math.min(Math.max(next * 1e3 - serverNow + BOUNDARY_DELAY_MS, 0), MAX_TIMEOUT_MS);
|
|
475
|
+
const timer = setTimeout(() => {
|
|
476
|
+
setNowSec(Math.floor((Date.now() + clockOffsetMs) / 1e3));
|
|
477
|
+
if (!hidden()) void refreshRef.current();
|
|
478
|
+
}, delay);
|
|
479
|
+
return () => clearTimeout(timer);
|
|
480
|
+
}, [entries, clockOffsetMs, enabled, nowSec]);
|
|
481
|
+
return _react.useMemo.call(void 0, () => {
|
|
482
|
+
return keys.map((k) => {
|
|
483
|
+
const e = entries.get(k);
|
|
484
|
+
if (!_optionalChain([e, 'optionalAccess', _35 => _35.snapshot])) return { ...unknownStatus(_nullishCoalesce(_optionalChain([e, 'optionalAccess', _36 => _36.error]), () => ( null))), clockOffsetMs };
|
|
485
|
+
return statusOf(e.snapshot, _nullishCoalesce(nowSec, () => ( Math.floor(e.updatedAt / 1e3))), { stale: e.error !== null, updatedAt: e.updatedAt, error: e.error, clockOffsetMs });
|
|
486
|
+
});
|
|
487
|
+
}, [key, entries, clockOffsetMs, nowSec]);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/status/DropCountdown.ts
|
|
491
|
+
|
|
492
|
+
var MAX_TIMEOUT_MS2 = 2147483647;
|
|
493
|
+
var pad = (n) => String(n).padStart(2, "0");
|
|
494
|
+
function formatCountdown(parts, units = "dhms") {
|
|
495
|
+
const shown = ["d", "h", "m", "s"].filter((u) => units.includes(u));
|
|
496
|
+
const use = shown.length > 0 ? shown : ["d", "h", "m", "s"];
|
|
497
|
+
let first = use.findIndex((u) => parts[u] > 0);
|
|
498
|
+
if (first === -1) first = use.length - 1;
|
|
499
|
+
return use.slice(first).map((u, i) => `${i === 0 ? String(parts[u]) : pad(parts[u])}${u}`).join(" ");
|
|
500
|
+
}
|
|
501
|
+
var hidden2 = () => typeof document !== "undefined" && document.hidden;
|
|
502
|
+
function DropCountdown(props) {
|
|
503
|
+
const { to, showWithin, hideAfter, now, format = "dhms", className, children } = props;
|
|
504
|
+
const within = showWithin === void 0 ? Infinity : parseDuration(showWithin);
|
|
505
|
+
const keep = hideAfter === void 0 ? 0 : parseDuration(hideAfter);
|
|
506
|
+
const [nowMs, setNowMs] = _react.useState.call(void 0, null);
|
|
507
|
+
_react.useEffect.call(void 0, () => {
|
|
508
|
+
if (to === null) return;
|
|
509
|
+
const clock = _nullishCoalesce(now, () => ( Date.now));
|
|
510
|
+
let timer = null;
|
|
511
|
+
const stop = () => {
|
|
512
|
+
if (timer !== null) clearTimeout(timer);
|
|
513
|
+
timer = null;
|
|
514
|
+
};
|
|
515
|
+
const step = () => {
|
|
516
|
+
stop();
|
|
517
|
+
const t = clock();
|
|
518
|
+
setNowMs(t);
|
|
519
|
+
if (hidden2()) return;
|
|
520
|
+
const untilTo = to * 1e3 - t;
|
|
521
|
+
let delay;
|
|
522
|
+
if (untilTo > 0) {
|
|
523
|
+
delay = untilTo > within * 1e3 ? untilTo - within * 1e3 : Math.min(1e3, untilTo);
|
|
524
|
+
} else if (keep > 0 && -untilTo < keep * 1e3) {
|
|
525
|
+
delay = keep * 1e3 + untilTo;
|
|
526
|
+
} else {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
timer = setTimeout(step, Math.min(Math.max(delay, 0), MAX_TIMEOUT_MS2));
|
|
530
|
+
};
|
|
531
|
+
const onVisibility = () => {
|
|
532
|
+
if (hidden2()) stop();
|
|
533
|
+
else step();
|
|
534
|
+
};
|
|
535
|
+
step();
|
|
536
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
537
|
+
return () => {
|
|
538
|
+
stop();
|
|
539
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
540
|
+
};
|
|
541
|
+
}, [to, within, keep, now]);
|
|
542
|
+
if (to === null || nowMs === null) return null;
|
|
543
|
+
const left = to - nowMs / 1e3;
|
|
544
|
+
const counting = left > 0;
|
|
545
|
+
const secondsLeft = counting ? Math.ceil(left) : 0;
|
|
546
|
+
const visible = counting ? left <= within : -left < keep;
|
|
547
|
+
const parts = countdownParts(secondsLeft, format);
|
|
548
|
+
const text = formatCountdown(parts, format);
|
|
549
|
+
const state = { state: counting ? "counting" : "done", visible, secondsLeft, parts, text };
|
|
550
|
+
if (typeof children === "function") return children(state);
|
|
551
|
+
if (!visible) return null;
|
|
552
|
+
return _react.createElement.call(void 0, "span", { className, role: "timer", "data-state": state.state }, _nullishCoalesce(children, () => ( text)));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
exports.DropCountdown = DropCountdown; exports.MARKETPLACE_ADDRESSES = MARKETPLACE_ADDRESSES; exports.MARKETPLACE_API = MARKETPLACE_API; exports.clockOffsetOf = clockOffsetOf; exports.countdownParts = countdownParts; exports.formatCountdown = formatCountdown; exports.parseDuration = parseDuration; exports.phaseOf = phaseOf; exports.readSources = readSources; exports.snapshotFromListing = snapshotFromListing; exports.sourceKey = sourceKey; exports.statusOf = statusOf; exports.unknownStatus = unknownStatus; exports.useDropStatus = useDropStatus;
|
|
570
|
+
//# sourceMappingURL=status.cjs.map
|