@manifoldxyz/dropkit 0.2.0 → 0.3.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/dist/cart.cjs ADDED
@@ -0,0 +1,1832 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await 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; } async function _asyncOptionalChain(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 = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+ var _chunkOO6QQT3Mcjs = require('./chunk-OO6QQT3M.cjs');
22
+
23
+ // src/cart/react.ts
24
+ var _react = require('react');
25
+
26
+ // src/cart/errors.ts
27
+ var CartError = class extends Error {
28
+
29
+ constructor(reason, message, opts) {
30
+ super(message, _optionalChain([opts, 'optionalAccess', _ => _.cause]) === void 0 ? void 0 : { cause: opts.cause });
31
+ this.name = "CartError";
32
+ this.reason = reason;
33
+ }
34
+ };
35
+ function cartReason(e) {
36
+ if (e instanceof CartError) return { reason: e.reason, message: e.message };
37
+ if (_chunkOO6QQT3Mcjs.isMintError.call(void 0, e)) return _chunkOO6QQT3Mcjs.toReason.call(void 0, e);
38
+ return _chunkOO6QQT3Mcjs.toReason.call(void 0, e);
39
+ }
40
+
41
+ // src/cart/line.ts
42
+ var _viem = require('viem');
43
+ var isUnsupportedReason = (r) => _chunkOO6QQT3Mcjs.UNSUPPORTED_REASONS.includes(r);
44
+ function deriveLine(args) {
45
+ const { drop, walletRead, readError, now, pending, walletConnected } = args;
46
+ const out = (state, reason = null, message = null) => ({ state, reason, message });
47
+ if (pending) return out("locked");
48
+ if (drop !== null && drop.paymentToken !== null) {
49
+ return out("unsupported", "CART_ERC20_UNSUPPORTED", "This item is priced in a token. The cart takes only items priced in the chain's native token.");
50
+ }
51
+ if (readError && isUnsupportedReason(readError.reason)) return out("unsupported", readError.reason, readError.message);
52
+ if (readError) return out("error", readError.reason, readError.message);
53
+ if (drop === null) return out("loading");
54
+ if (walletConnected && walletRead === null) return out("loading");
55
+ if (drop.endTime !== null && drop.endTime <= now) return out("ended");
56
+ if (drop.maxSupply !== null && drop.maxSupply > 0n && drop.totalMinted >= drop.maxSupply) return out("sold-out");
57
+ if (drop.startTime !== null && drop.startTime > now) return out("upcoming");
58
+ if (_optionalChain([walletRead, 'optionalAccess', _2 => _2.ineligible])) return out("ineligible", walletRead.ineligible.reason, walletRead.ineligible.message);
59
+ return out("live");
60
+ }
61
+ function lineMaxQuantity(drop, walletRead) {
62
+ if (drop === null) return null;
63
+ let max = null;
64
+ if (drop.maxSupply !== null && drop.maxSupply > 0n) {
65
+ const left = drop.maxSupply - drop.totalMinted;
66
+ max = left > 0n ? left < BigInt(_chunkOO6QQT3Mcjs.MAX_QUANTITY) ? Number(left) : _chunkOO6QQT3Mcjs.MAX_QUANTITY : 0;
67
+ }
68
+ if (walletRead !== null && walletRead.maxQuantity !== null) {
69
+ const cap = Math.max(0, Math.floor(walletRead.maxQuantity));
70
+ max = max === null ? cap : Math.min(max, cap);
71
+ }
72
+ return max;
73
+ }
74
+ var money = (value, like) => ({ value, decimals: like.decimals, symbol: like.symbol, formatted: _viem.formatUnits.call(void 0, value, like.decimals) });
75
+ function lineTotal(drop, quantity) {
76
+ if (drop === null || drop.paymentToken !== null) return null;
77
+ const q = BigInt(quantity);
78
+ return money((drop.unitPrice.value + drop.unitFee.value) * q, drop.unitPrice);
79
+ }
80
+ function sumMoney(parts, like) {
81
+ return money(parts.reduce((a, m) => a + m.value, 0n), like);
82
+ }
83
+
84
+ // src/cart/reads.ts
85
+ function sharedReadClient(raw) {
86
+ let epoch = 0;
87
+ const forever = /* @__PURE__ */ new Map();
88
+ const perEpoch = /* @__PURE__ */ new Map();
89
+ const memo = (cache, key, load) => {
90
+ const hit = cache.get(key);
91
+ if (hit) return hit;
92
+ const p = load();
93
+ cache.set(key, p);
94
+ p.catch(() => {
95
+ if (cache.get(key) === p) cache.delete(key);
96
+ });
97
+ return p;
98
+ };
99
+ const chainId = () => _nullishCoalesce(_optionalChain([raw, 'access', _3 => _3.chain, 'optionalAccess', _4 => _4.id]), () => ( 0));
100
+ const overrides = {
101
+ getCode: (args) => {
102
+ const address = args.address.toLowerCase();
103
+ if (_chunkOO6QQT3Mcjs.findExtension.call(void 0, chainId(), address)) return memo(forever, `code:${address}`, () => raw.getCode(args));
104
+ return raw.getCode(args);
105
+ },
106
+ readContract: (args) => {
107
+ if (args.functionName === "MINT_FEE" || args.functionName === "MINT_FEE_MERKLE") {
108
+ return memo(forever, `fee:${args.address.toLowerCase()}:${args.functionName}`, () => raw.readContract(args));
109
+ }
110
+ return raw.readContract(args);
111
+ },
112
+ getGasPrice: () => memo(perEpoch, `gas:${String(epoch)}`, () => raw.getGasPrice()),
113
+ getBalance: (args) => memo(perEpoch, `bal:${String(epoch)}:${args.address.toLowerCase()}`, () => raw.getBalance(args))
114
+ };
115
+ const client = new Proxy(raw, {
116
+ get(target, prop, receiver) {
117
+ if (typeof prop === "string" && prop in overrides) return overrides[prop];
118
+ const value = Reflect.get(target, prop, receiver);
119
+ return typeof value === "function" ? value.bind(target) : value;
120
+ }
121
+ });
122
+ return {
123
+ client,
124
+ bump: () => {
125
+ epoch += 1;
126
+ perEpoch.clear();
127
+ }
128
+ };
129
+ }
130
+ var Limiter = (_class = class {
131
+ constructor(limit) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);
132
+ this.limit = limit;
133
+ }
134
+
135
+ __init() {this.running = 0}
136
+ __init2() {this.queue = []}
137
+ async run(task) {
138
+ if (this.running >= this.limit()) await new Promise((resolve) => this.queue.push(resolve));
139
+ this.running += 1;
140
+ try {
141
+ return await task();
142
+ } finally {
143
+ this.running -= 1;
144
+ _optionalChain([this, 'access', _5 => _5.queue, 'access', _6 => _6.shift, 'call', _7 => _7(), 'optionalCall', _8 => _8()]);
145
+ }
146
+ }
147
+ }, _class);
148
+ var sleep = (ms, signal) => new Promise((resolve, reject) => {
149
+ if (_optionalChain([signal, 'optionalAccess', _9 => _9.aborted])) return reject(signal.reason);
150
+ const t = setTimeout(() => {
151
+ _optionalChain([signal, 'optionalAccess', _10 => _10.removeEventListener, 'call', _11 => _11("abort", onAbort)]);
152
+ resolve();
153
+ }, ms);
154
+ const onAbort = () => {
155
+ clearTimeout(t);
156
+ reject(_optionalChain([signal, 'optionalAccess', _12 => _12.reason]));
157
+ };
158
+ _optionalChain([signal, 'optionalAccess', _13 => _13.addEventListener, 'call', _14 => _14("abort", onAbort, { once: true })]);
159
+ });
160
+ function rateLimitedFetch(base, perMinute) {
161
+ const starts = [];
162
+ const target = _nullishCoalesce(base, () => ( ((input, init) => globalThis.fetch(input, init))));
163
+ return (async (input, init) => {
164
+ for (; ; ) {
165
+ const now = Date.now();
166
+ while (starts.length && now - starts[0] >= 6e4) starts.shift();
167
+ if (starts.length < perMinute()) break;
168
+ await sleep(6e4 - (now - starts[0]) + 5, _nullishCoalesce(_optionalChain([init, 'optionalAccess', _15 => _15.signal]), () => ( void 0)));
169
+ }
170
+ starts.push(Date.now());
171
+ return target(input, init);
172
+ });
173
+ }
174
+
175
+ // src/cart/run.ts
176
+
177
+
178
+ // src/cart/store.ts
179
+
180
+ var CART_VALUE_VERSION = 6;
181
+ var defaultStorageKey = (chainId) => `dropkit-cart:${String(chainId)}`;
182
+ function itemKey(config) {
183
+ return `${String(config.chainId)}:${_viem.getAddress.call(void 0, config.creatorContract)}:${BigInt(config.instanceId).toString()}:${_viem.getAddress.call(void 0, config.extension)}`;
184
+ }
185
+ function canonicalConfig(config, chainId) {
186
+ const c = _nullishCoalesce(config, () => ( {}));
187
+ if (c.type !== "manifold-claim") {
188
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", 'The cart takes only Manifold claims (type: "manifold-claim").', { field: "type" });
189
+ }
190
+ if (c.chainId !== chainId) {
191
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", `This cart is on chain ${String(chainId)}; the config is on chain ${String(c.chainId)}.`, { field: "chainId" });
192
+ }
193
+ if (typeof c.creatorContract !== "string" || !_viem.isAddress.call(void 0, c.creatorContract, { strict: false })) {
194
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "creatorContract is not a valid address.", { field: "creatorContract" });
195
+ }
196
+ if (typeof c.extension !== "string" || !_viem.isAddress.call(void 0, c.extension, { strict: false })) {
197
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "Cart configs must set extension (the claim extension address).", { field: "extension" });
198
+ }
199
+ if (typeof c.instanceId !== "string" || !/^\d+$/.test(c.instanceId)) {
200
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "instanceId must be a non-negative integer string.", { field: "instanceId" });
201
+ }
202
+ return {
203
+ type: "manifold-claim",
204
+ chainId,
205
+ creatorContract: _viem.getAddress.call(void 0, c.creatorContract),
206
+ instanceId: BigInt(c.instanceId).toString(),
207
+ extension: _viem.getAddress.call(void 0, c.extension)
208
+ };
209
+ }
210
+ var clampQuantity = (q) => Math.max(1, Math.min(Math.floor(Number.isFinite(q) ? q : 1), _chunkOO6QQT3Mcjs.MAX_QUANTITY));
211
+ var EMPTY_VALUE = (chainId) => ({ v: CART_VALUE_VERSION, chainId, items: [], pending: [], results: [] });
212
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
213
+ var isHash = (v) => typeof v === "string" && /^0x[0-9a-fA-F]{64}$/.test(v);
214
+ var isAddr = (v) => typeof v === "string" && _viem.isAddress.call(void 0, v, { strict: false });
215
+ var isIntStr = (v) => typeof v === "string" && /^\d+$/.test(v);
216
+ var isQuantity = (v) => typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= _chunkOO6QQT3Mcjs.MAX_QUANTITY;
217
+ function restoreConfig(v, chainId) {
218
+ try {
219
+ return canonicalConfig(v, chainId);
220
+ } catch (e2) {
221
+ return null;
222
+ }
223
+ }
224
+ function restoreHoldings(v) {
225
+ if (!isRecord(v) || !isIntStr(v.before)) return null;
226
+ if (v.kind === "total-mints" && isAddr(v.extension) && isAddr(v.creatorContract) && isIntStr(v.instanceId)) {
227
+ return { kind: "total-mints", extension: _viem.getAddress.call(void 0, v.extension), creatorContract: _viem.getAddress.call(void 0, v.creatorContract), instanceId: v.instanceId, before: v.before };
228
+ }
229
+ if (v.kind === "balance-1155" && isAddr(v.creatorContract) && isIntStr(v.tokenId)) {
230
+ return { kind: "balance-1155", creatorContract: _viem.getAddress.call(void 0, v.creatorContract), tokenId: v.tokenId, before: v.before };
231
+ }
232
+ if (v.kind === "balance-721" && isAddr(v.creatorContract)) {
233
+ return { kind: "balance-721", creatorContract: _viem.getAddress.call(void 0, v.creatorContract), before: v.before };
234
+ }
235
+ return null;
236
+ }
237
+ function restorePending(v, chainId) {
238
+ if (!isRecord(v)) return null;
239
+ const config = restoreConfig(v.config, chainId);
240
+ const holdings = restoreHoldings(v.holdings);
241
+ if (!config || !holdings) return null;
242
+ if (typeof v.id !== "string" || typeof v.lineKey !== "string" || !isAddr(v.payer) || !isAddr(v.recipient) || !isQuantity(v.quantity)) return null;
243
+ if (v.hash !== null && !isHash(v.hash)) return null;
244
+ if (v.hashAt !== null && typeof v.hashAt !== "number") return null;
245
+ if (typeof v.createdAt !== "number") return null;
246
+ return {
247
+ id: v.id,
248
+ lineKey: v.lineKey,
249
+ config,
250
+ payer: _viem.getAddress.call(void 0, v.payer),
251
+ recipient: _viem.getAddress.call(void 0, v.recipient),
252
+ quantity: v.quantity,
253
+ holdings,
254
+ hash: v.hash,
255
+ hashAt: v.hashAt,
256
+ createdAt: v.createdAt
257
+ };
258
+ }
259
+ function restoreResult(v, chainId) {
260
+ if (!isRecord(v)) return null;
261
+ const config = restoreConfig(v.config, chainId);
262
+ const holdings = restoreHoldings(v.holdings);
263
+ if (!config || !holdings) return null;
264
+ if (typeof v.id !== "string" || typeof v.lineKey !== "string" || !isAddr(v.payer) || !isAddr(v.recipient) || !isQuantity(v.quantity)) return null;
265
+ if (v.status !== "collected" && v.status !== "unconfirmed") return null;
266
+ if (v.txHash !== null && !isHash(v.txHash)) return null;
267
+ if (typeof v.at !== "number") return null;
268
+ if (!Array.isArray(v.tokens)) return null;
269
+ const tokens = [];
270
+ for (const t of v.tokens) {
271
+ if (!isRecord(t) || !isAddr(t.contract) || typeof t.tokenId !== "string" || !/^\d+$/.test(t.tokenId)) return null;
272
+ tokens.push({ contract: _viem.getAddress.call(void 0, t.contract), tokenId: t.tokenId });
273
+ }
274
+ return { id: v.id, status: v.status, lineKey: v.lineKey, config, payer: _viem.getAddress.call(void 0, v.payer), recipient: _viem.getAddress.call(void 0, v.recipient), quantity: v.quantity, holdings, txHash: v.txHash, tokens, at: v.at };
275
+ }
276
+ function restoreValue(raw, chainId) {
277
+ if (raw === null) return { value: EMPTY_VALUE(chainId), lock: null };
278
+ let parsed;
279
+ try {
280
+ parsed = JSON.parse(raw);
281
+ } catch (e3) {
282
+ return { value: EMPTY_VALUE(chainId), lock: null };
283
+ }
284
+ if (!isRecord(parsed) || parsed.v !== CART_VALUE_VERSION) return { value: EMPTY_VALUE(chainId), lock: null };
285
+ if (parsed.chainId !== chainId) return { value: EMPTY_VALUE(chainId), lock: "foreign" };
286
+ const items = [];
287
+ const seen = /* @__PURE__ */ new Set();
288
+ for (const it of Array.isArray(parsed.items) ? parsed.items : []) {
289
+ if (!isRecord(it)) continue;
290
+ const config = restoreConfig(it.config, chainId);
291
+ if (!config || !isQuantity(it.quantity)) continue;
292
+ const key = itemKey(config);
293
+ if (seen.has(key)) continue;
294
+ seen.add(key);
295
+ items.push({ config, quantity: it.quantity });
296
+ }
297
+ let lock = null;
298
+ const pending = [];
299
+ for (const p of Array.isArray(parsed.pending) ? parsed.pending : []) {
300
+ const r = restorePending(p, chainId);
301
+ if (r) pending.push(r);
302
+ else lock = "corrupt";
303
+ }
304
+ const results = [];
305
+ for (const p of Array.isArray(parsed.results) ? parsed.results : []) {
306
+ const r = restoreResult(p, chainId);
307
+ if (r) results.push(r);
308
+ else lock = "corrupt";
309
+ }
310
+ if (!Array.isArray(parsed.pending) || !Array.isArray(parsed.results)) lock = "corrupt";
311
+ return { value: { v: CART_VALUE_VERSION, chainId, items, pending, results }, lock };
312
+ }
313
+ var defaultStorage = () => {
314
+ try {
315
+ return typeof window === "undefined" ? null : window.sessionStorage;
316
+ } catch (e4) {
317
+ return null;
318
+ }
319
+ };
320
+ var newId = () => {
321
+ const c = globalThis.crypto;
322
+ return _optionalChain([c, 'optionalAccess', _16 => _16.randomUUID]) ? c.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
323
+ };
324
+ var CartStore = (_class2 = class {
325
+
326
+
327
+
328
+ __init3() {this.value = null}
329
+ __init4() {this.lock = null}
330
+ /** Item writes that could not reach storage (storage threw): the cart then lives in memory only. */
331
+ __init5() {this.degraded = false}
332
+ __init6() {this.listeners = /* @__PURE__ */ new Set()}
333
+ /** The raw value this store last read or wrote. A different raw value in storage was written by another store. */
334
+ __init7() {this.lastRaw = null}
335
+ /** Goes up on every item change (R19). One live provider per key (R17); a stale one merges (latest()). */
336
+ __init8() {this.revision = 0}
337
+ constructor(opts) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);
338
+ this.storageKey = opts.storageKey;
339
+ this.chainId = opts.chainId;
340
+ this.storage = _nullishCoalesce(opts.storage, () => ( defaultStorage));
341
+ }
342
+ /** Loads on first use (client only); never touches storage during server rendering. */
343
+ __init9() {this.getSnapshot = () => {
344
+ if (this.value === null) {
345
+ let raw = null;
346
+ try {
347
+ raw = _nullishCoalesce(_optionalChain([this, 'access', _21 => _21.storage, 'call', _22 => _22(), 'optionalAccess', _23 => _23.getItem, 'call', _24 => _24(this.storageKey)]), () => ( null));
348
+ } catch (e6) {
349
+ this.degraded = true;
350
+ }
351
+ const restored = restoreValue(raw, this.chainId);
352
+ this.value = restored.value;
353
+ this.lock = restored.lock;
354
+ this.lastRaw = raw;
355
+ }
356
+ return this.value;
357
+ }}
358
+ /**
359
+ * The value every write starts from: storage as it is now. A store left behind by an unmounted provider (a route
360
+ * change mid-checkout) may still write once, when its wallet prompt returns; it must merge into what the newer
361
+ * provider saved, never overwrite it (Codex PR #11 review A-F3). Memory-only stores (locked, no storage) keep
362
+ * their own value.
363
+ */
364
+ latest() {
365
+ const v = this.getSnapshot();
366
+ if (this.lock !== null) return v;
367
+ let raw;
368
+ try {
369
+ const st = this.storage();
370
+ if (!st) return v;
371
+ raw = st.getItem(this.storageKey);
372
+ } catch (e7) {
373
+ return v;
374
+ }
375
+ if (raw === null || raw === this.lastRaw) return v;
376
+ const restored = restoreValue(raw, this.chainId);
377
+ this.lastRaw = raw;
378
+ if (restored.lock !== null) {
379
+ this.lock = restored.lock;
380
+ return v;
381
+ }
382
+ this.value = restored.value;
383
+ this.revision += 1;
384
+ this.notify();
385
+ return restored.value;
386
+ }
387
+ __init10() {this.subscribe = (cb) => {
388
+ this.listeners.add(cb);
389
+ return () => {
390
+ this.listeners.delete(cb);
391
+ };
392
+ }}
393
+ /** True when a value this code could not read protects the storage slot (writes are then memory-only). */
394
+ get locked() {
395
+ this.getSnapshot();
396
+ return this.lock;
397
+ }
398
+ /** A real test write: storage must accept and return a value, and no unreadable value may be in the way. */
399
+ persistenceOk() {
400
+ this.getSnapshot();
401
+ if (this.lock !== null) return false;
402
+ try {
403
+ const s = this.storage();
404
+ if (!s) return false;
405
+ const probe = `${this.storageKey}:probe`;
406
+ s.setItem(probe, "1");
407
+ const ok = s.getItem(probe) === "1";
408
+ s.removeItem(probe);
409
+ return ok;
410
+ } catch (e8) {
411
+ return false;
412
+ }
413
+ }
414
+ notify() {
415
+ for (const cb of [...this.listeners]) cb();
416
+ }
417
+ /** One setItem, read back. Returns false when the value did not reach storage. */
418
+ persist(next) {
419
+ if (this.lock !== null) return false;
420
+ try {
421
+ const s = this.storage();
422
+ if (!s) return false;
423
+ const raw = JSON.stringify(next);
424
+ s.setItem(this.storageKey, raw);
425
+ const ok = s.getItem(this.storageKey) === raw;
426
+ if (ok) this.lastRaw = raw;
427
+ return ok;
428
+ } catch (e9) {
429
+ return false;
430
+ }
431
+ }
432
+ /**
433
+ * strict: the change must reach storage or nothing changes and PERSISTENCE_UNAVAILABLE is thrown (R15).
434
+ * Item-only changes are not strict: a cart on a storage-less browser still works for the page's life.
435
+ */
436
+ commit(next, opts) {
437
+ const ok = this.persist(next);
438
+ if (!ok) {
439
+ if (opts.strict) throw new CartError("PERSISTENCE_UNAVAILABLE", "The cart could not save its state in this tab, so nothing was sent.");
440
+ this.degraded = true;
441
+ }
442
+ this.value = next;
443
+ if (opts.items) this.revision += 1;
444
+ this.notify();
445
+ }
446
+ get isDegraded() {
447
+ return this.degraded;
448
+ }
449
+ hasItem(key) {
450
+ return this.getSnapshot().items.some((i) => itemKey(i.config) === key);
451
+ }
452
+ pendingFor(key) {
453
+ return this.getSnapshot().pending.find((p) => p.lineKey === key);
454
+ }
455
+ // ---------- items ----------
456
+ /** Adds an item, or raises the quantity of an existing key. `drop` deletes those results in the same write. */
457
+ addItem(config, quantity, opts) {
458
+ const v = this.latest();
459
+ const key = itemKey(config);
460
+ const existing = v.items.find((i) => itemKey(i.config) === key);
461
+ const cap = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _25 => _25.cap]), () => ( null));
462
+ const bound = (q) => clampQuantity(cap !== null ? Math.min(q, Math.max(1, cap)) : q);
463
+ const items = existing ? v.items.map((i) => i === existing ? { ...i, quantity: bound(i.quantity + quantity) } : i) : [...v.items, { config, quantity: bound(quantity) }];
464
+ const drop = new Set(_nullishCoalesce(_optionalChain([opts, 'optionalAccess', _26 => _26.dropResults]), () => ( [])));
465
+ const results = drop.size ? v.results.filter((r) => !drop.has(r.id)) : v.results;
466
+ this.commit({ ...v, items, results }, { strict: false, items: true });
467
+ }
468
+ setQuantity(key, quantity) {
469
+ const v = this.latest();
470
+ const item = v.items.find((i) => itemKey(i.config) === key);
471
+ if (!item) return;
472
+ const q = clampQuantity(quantity);
473
+ if (q === item.quantity) return;
474
+ this.commit({ ...v, items: v.items.map((i) => i === item ? { ...i, quantity: q } : i) }, { strict: false, items: true });
475
+ }
476
+ /** A line with a pending mint is locked and stays. */
477
+ removeItem(key) {
478
+ const v = this.latest();
479
+ if (v.pending.some((p) => p.lineKey === key)) return;
480
+ const items = v.items.filter((i) => itemKey(i.config) !== key);
481
+ if (items.length === v.items.length) return;
482
+ this.commit({ ...v, items }, { strict: false, items: true });
483
+ }
484
+ /** Removes items only. Locked lines, pending mints and results stay. */
485
+ clearItems() {
486
+ const v = this.latest();
487
+ const locked = new Set(v.pending.map((p) => p.lineKey));
488
+ const items = v.items.filter((i) => locked.has(itemKey(i.config)));
489
+ if (items.length === v.items.length) return;
490
+ this.commit({ ...v, items }, { strict: false, items: true });
491
+ }
492
+ // ---------- pending mints and results ----------
493
+ /**
494
+ * Synchronous write plus read-back (R15). Throws CartError('PERSISTENCE_UNAVAILABLE'), changing nothing.
495
+ * One pending mint per line: returns false, writing nothing, when the line already has one (a second
496
+ * useCheckout under the same provider confirming the same line). The caller must not send.
497
+ */
498
+ writePending(p) {
499
+ const v = this.latest();
500
+ if (v.pending.some((x) => x.lineKey === p.lineKey)) return false;
501
+ this.commit({ ...v, pending: [...v.pending, p] }, { strict: true, items: false });
502
+ return true;
503
+ }
504
+ setHash(id, hash, hashAt) {
505
+ const v = this.latest();
506
+ if (!v.pending.some((p) => p.id === id)) return;
507
+ this.commit({ ...v, pending: v.pending.map((p) => p.id === id ? { ...p, hash, hashAt } : p) }, { strict: true, items: false });
508
+ }
509
+ /** A failed or not-sent mint: the pending mint goes, the line stays. */
510
+ removePending(id) {
511
+ const v = this.latest();
512
+ if (!v.pending.some((p) => p.id === id)) return;
513
+ this.commit({ ...v, pending: v.pending.filter((p) => p.id !== id) }, { strict: true, items: false });
514
+ }
515
+ /**
516
+ * Settling is one write of the whole value (R16, D24): remove the pending mint, remove its line, add the result.
517
+ */
518
+ settle(id, result) {
519
+ const v = this.latest();
520
+ const p = v.pending.find((x) => x.id === id);
521
+ if (!p) return;
522
+ this.commit(
523
+ {
524
+ ...v,
525
+ pending: v.pending.filter((x) => x.id !== id),
526
+ items: v.items.filter((i) => itemKey(i.config) !== p.lineKey),
527
+ results: [...v.results, result]
528
+ },
529
+ { strict: true, items: true }
530
+ );
531
+ }
532
+ deleteResult(id) {
533
+ const v = this.latest();
534
+ if (!v.results.some((r) => r.id === id)) return;
535
+ this.commit({ ...v, results: v.results.filter((r) => r.id !== id) }, { strict: false, items: false });
536
+ }
537
+ }, _class2);
538
+
539
+ // src/cart/run.ts
540
+ var CART_TIMING = {
541
+ /** A failed read is retried after these delays, then the line shows error (Step 3). */
542
+ retryDelaysMs: [1e3, 2e3, 4e3],
543
+ /** A prepared request older than this goes back to preparing (R19). */
544
+ preparedExpiryMs: 3e4,
545
+ refreshMs: 3e4,
546
+ refreshBackoffMs: 6e4,
547
+ receiptTimeoutMs: 12e4,
548
+ /** An unknown hash this long after hashAt is a lost hash (Step 5b). */
549
+ unknownHashSec: 300,
550
+ holdingsTries: 3,
551
+ maxReadsInFlight: 4,
552
+ merklePerMinute: 10
553
+ };
554
+ var nowSec = () => Math.floor(Date.now() / 1e3);
555
+ var adapter = _chunkOO6QQT3Mcjs.manifoldClaimAdapter;
556
+ var LineSkipped = class extends Error {
557
+
558
+
559
+ constructor(state, reason, message) {
560
+ super(message);
561
+ this.name = "LineSkipped";
562
+ this.state = state;
563
+ this.reason = reason;
564
+ }
565
+ };
566
+ var skipText = {
567
+ "sold-out": "Sold out.",
568
+ ended: "The sale has ended.",
569
+ upcoming: "The sale has not started.",
570
+ locked: "A mint for this item is still being confirmed."
571
+ };
572
+ var attemptIds = 0;
573
+ async function assessLine(env, line, payer) {
574
+ const config = structuredClone(line.config);
575
+ const key = itemKey(config);
576
+ const ctx = env.ctxFor(key);
577
+ const recipient = payer;
578
+ const drop = await adapter.readDrop(ctx, config);
579
+ if (drop.paymentToken !== null) throw new CartError("CART_ERC20_UNSUPPORTED", "This item is priced in a token, which the cart does not support.");
580
+ const walletRead = await adapter.readWallet(ctx, config, { payer, recipient, quantity: line.quantity, drop });
581
+ const read = deriveLine({ drop, walletRead, readError: null, now: nowSec(), pending: false, walletConnected: true });
582
+ if (read.state !== "live") throw new LineSkipped(read.state, read.reason, _nullishCoalesce(_nullishCoalesce(read.message, () => ( skipText[read.state])), () => ( "This item cannot be minted right now.")));
583
+ const max = lineMaxQuantity(drop, walletRead);
584
+ if (max !== null && line.quantity > max) {
585
+ throw new LineSkipped(max === 0 ? "sold-out" : "ineligible", "WALLET_LIMIT", max === 0 ? "Sold out." : `Only ${String(max)} left for this wallet.`);
586
+ }
587
+ const attempt = Object.freeze({
588
+ attemptId: ++attemptIds,
589
+ config,
590
+ payer,
591
+ recipient,
592
+ quantity: line.quantity,
593
+ chainId: config.chainId,
594
+ tokenContract: config.creatorContract,
595
+ originChainId: config.chainId
596
+ });
597
+ const plan = await adapter.buildPlan(ctx, attempt, walletRead);
598
+ if (plan.approve) throw new CartError("CART_ERC20_UNSUPPORTED", "This item needs a token approval, which the cart does not support.");
599
+ return { key, config, quantity: line.quantity, payer, recipient, attempt, drop, walletRead, plan, ctx };
600
+ }
601
+ async function prepareLine(env, line, payer) {
602
+ const a = await assessLine(env, line, payer);
603
+ try {
604
+ await adapter.simulate(a.ctx, a.attempt, a.plan);
605
+ } catch (e) {
606
+ if (e instanceof _chunkOO6QQT3Mcjs.MintError && e.reason === "SIMULATION_REVERTED") throw new LineSkipped("ineligible", "SIMULATION_REVERTED", _chunkOO6QQT3Mcjs.friendlyErrorMessage.call(void 0, e));
607
+ throw e;
608
+ }
609
+ const query = _nullishCoalesce(_optionalChain([adapter, 'access', _27 => _27.holdingsQuery, 'optionalCall', _28 => _28(a.drop)]), () => ( null));
610
+ if (!query || !adapter.readHoldings) throw new (0, _chunkOO6QQT3Mcjs.MintError)("RPC_ERROR", "The cart cannot count holdings for this item.");
611
+ const before = await adapter.readHoldings(a.ctx, query, a.recipient);
612
+ const unit = a.drop.unitPrice;
613
+ return {
614
+ key: a.key,
615
+ config: a.config,
616
+ quantity: a.quantity,
617
+ payer: a.payer,
618
+ recipient: a.recipient,
619
+ plan: a.plan,
620
+ drop: a.drop,
621
+ walletRead: a.walletRead,
622
+ holdings: { ...query, before: before.toString() },
623
+ preparedAt: Date.now(),
624
+ amount: { value: a.plan.mint.value, decimals: unit.decimals, symbol: unit.symbol, formatted: _viem.formatUnits.call(void 0, a.plan.mint.value, unit.decimals) },
625
+ revision: line.revision
626
+ };
627
+ }
628
+ async function withRetries(fn, delaysMs, isFinal, signal) {
629
+ for (let i = 0; ; i++) {
630
+ try {
631
+ return await fn();
632
+ } catch (e) {
633
+ if (isFinal(e) || i >= delaysMs.length || _optionalChain([signal, 'optionalAccess', _29 => _29.aborted])) throw e;
634
+ await sleep(delaysMs[i], signal);
635
+ }
636
+ }
637
+ }
638
+ var sendSwitch = (chainId) => _chunkOO6QQT3Mcjs.requestSwitch.call(void 0, chainId);
639
+ var sendAddChain = (chainId) => _chunkOO6QQT3Mcjs.requestAddChain.call(void 0, chainId);
640
+ var openCalls = /* @__PURE__ */ new Set();
641
+ var notSentCalls = /* @__PURE__ */ new Set();
642
+ var markCallOpen = (id) => void openCalls.add(id);
643
+ var markCallClosed = (id) => void openCalls.delete(id);
644
+ var isCallOpen = (id) => openCalls.has(id);
645
+ var markNotSent = (id) => void notSentCalls.add(id);
646
+ var wasNotSent = (id) => notSentCalls.has(id);
647
+ var SEND_METHODS = /* @__PURE__ */ new Set(["eth_sendTransaction", "wallet_sendTransaction"]);
648
+ async function sendMint(prepared, hooks) {
649
+ const notSent = (message, cause) => new CartError("REQUEST_NOT_SENT", message, { cause });
650
+ const live = _chunkOO6QQT3Mcjs.getAccount.call(void 0, );
651
+ if (!live || live.address.toLowerCase() !== prepared.payer.toLowerCase()) {
652
+ throw notSent("The connected wallet changed before the transaction was sent.");
653
+ }
654
+ let provider;
655
+ let chain;
656
+ try {
657
+ provider = _chunkOO6QQT3Mcjs.getWalletProvider.call(void 0, );
658
+ chain = _chunkOO6QQT3Mcjs.getChain.call(void 0, prepared.config.chainId);
659
+ } catch (e) {
660
+ throw notSent("No wallet is connected.", e);
661
+ }
662
+ const state = { forwarded: false };
663
+ const request = ((args) => {
664
+ if (SEND_METHODS.has(args.method)) state.forwarded = true;
665
+ return provider.request(args);
666
+ });
667
+ const wallet = _viem.createWalletClient.call(void 0, { chain, transport: _viem.custom.call(void 0, { request }, { retryCount: 0 }) });
668
+ let hash;
669
+ try {
670
+ hash = await wallet.sendTransaction({ account: prepared.payer, to: prepared.plan.mint.to, data: prepared.plan.mint.data, value: prepared.plan.mint.value, chain });
671
+ } catch (e) {
672
+ if (!state.forwarded) throw notSent("The transaction request did not reach the wallet.", e);
673
+ throw e;
674
+ }
675
+ hooks.onHash(hash);
676
+ return hash;
677
+ }
678
+ var receiptOk = (status) => status === "success" || status === 1 || status === 1n || status === "0x1";
679
+ function toMintReceipt(receipt, hash, tokenContract, recipient) {
680
+ const ok = receiptOk(receipt.status);
681
+ const parsed = ok ? _chunkOO6QQT3Mcjs.parseMintReceipt.call(void 0, _nullishCoalesce(receipt.logs, () => ( [])), { tokenContract, recipient }) : [];
682
+ const minted = parsed.reduce((n, t) => n + t.amount, 0n);
683
+ const tokens = parsed.map((t) => ({ contract: t.contract, tokenId: t.tokenId.toString() }));
684
+ return { status: ok ? "success" : "reverted", hash: _nullishCoalesce(receipt.transactionHash, () => ( hash)), minted, tokens };
685
+ }
686
+ async function waitMint(client, args) {
687
+ const receipt = await client.waitForTransactionReceipt({
688
+ hash: args.hash,
689
+ confirmations: 1,
690
+ timeout: CART_TIMING.receiptTimeoutMs,
691
+ onReplaced: (r) => _optionalChain([args, 'access', _30 => _30.onReplaced, 'optionalCall', _31 => _31({ reason: r.reason, hash: _nullishCoalesce(_optionalChain([r, 'access', _32 => _32.transaction, 'optionalAccess', _33 => _33.hash]), () => ( null)) })])
692
+ });
693
+ return toMintReceipt(receipt, args.hash, args.tokenContract, args.recipient);
694
+ }
695
+ var shortAddress = (a) => `${a.slice(0, 6)}\u2026${a.slice(-4)}`;
696
+ var lostHashWarning = (wallet) => `If wallet ${shortAddress(wallet)} still shows a request for this item, reject it.`;
697
+ var unconfirmedMessage = (wallet) => `We couldn't confirm this. Check wallet ${shortAddress(wallet)}. If it didn't go through, add it again.`;
698
+ var isNotFound = (e) => {
699
+ for (let x = e, depth = 0; x && depth < 6; depth++) {
700
+ if (x.name === "TransactionReceiptNotFoundError" || x.name === "TransactionNotFoundError") return true;
701
+ x = x.cause;
702
+ }
703
+ return false;
704
+ };
705
+ async function readHoldingsUp(env, p) {
706
+ const read = adapter.readHoldings;
707
+ if (!read) return null;
708
+ for (let i = 0; i < CART_TIMING.holdingsTries; i++) {
709
+ try {
710
+ const after = await read(env.ctx, p.holdings, p.recipient);
711
+ return after - BigInt(p.holdings.before) >= BigInt(p.quantity);
712
+ } catch (e10) {
713
+ }
714
+ }
715
+ return null;
716
+ }
717
+ var tokensFromQuery = (p) => p.holdings.kind === "balance-1155" ? [{ contract: p.holdings.creatorContract, tokenId: p.holdings.tokenId }] : [];
718
+ function resultFor(p, status, hash, tokens = []) {
719
+ return { id: newId(), status, lineKey: p.lineKey, config: p.config, payer: p.payer, recipient: p.recipient, quantity: p.quantity, holdings: p.holdings, txHash: hash, tokens, at: nowSec() };
720
+ }
721
+ function commit(env, p, result) {
722
+ try {
723
+ env.store.settle(p.id, result);
724
+ return null;
725
+ } catch (e) {
726
+ const { reason, message } = cartReason(e);
727
+ return { kind: "unavailable", reason, message };
728
+ }
729
+ }
730
+ async function settle(env, pending) {
731
+ const p = pending;
732
+ const hash = _nullishCoalesce(_nullishCoalesce(p.hash, () => ( env.memHash)), () => ( null));
733
+ if (!hash) {
734
+ if (isCallOpen(p.id)) return { kind: "waiting", message: "Waiting for the wallet." };
735
+ if (wasNotSent(p.id)) {
736
+ try {
737
+ env.store.removePending(p.id);
738
+ } catch (e) {
739
+ const { reason, message } = cartReason(e);
740
+ return { kind: "unavailable", reason, message };
741
+ }
742
+ notSentCalls.delete(p.id);
743
+ return { kind: "not-sent" };
744
+ }
745
+ return lostHash(env, p);
746
+ }
747
+ let receipt = _nullishCoalesce(env.receipt, () => ( null));
748
+ if (!receipt) {
749
+ try {
750
+ const r = await env.client.getTransactionReceipt({ hash });
751
+ receipt = toMintReceipt(r, hash, p.config.creatorContract, p.recipient);
752
+ } catch (e) {
753
+ if (!isNotFound(e)) {
754
+ const { reason, message } = cartReason(e);
755
+ return { kind: "unavailable", reason, message };
756
+ }
757
+ }
758
+ }
759
+ if (receipt) {
760
+ if (receipt.status === "reverted") {
761
+ try {
762
+ env.store.removePending(p.id);
763
+ } catch (e) {
764
+ const { reason, message } = cartReason(e);
765
+ return { kind: "unavailable", reason, message };
766
+ }
767
+ return { kind: "failed", hash: receipt.hash, message: "The mint transaction reverted." };
768
+ }
769
+ if (receipt.minted >= BigInt(p.quantity)) {
770
+ const result2 = resultFor(p, "collected", receipt.hash, receipt.tokens);
771
+ return _nullishCoalesce(commit(env, p, result2), () => ( { kind: "collected", result: result2, warning: null }));
772
+ }
773
+ const result = resultFor(p, "unconfirmed", receipt.hash);
774
+ return _nullishCoalesce(commit(env, p, result), () => ( { kind: "unconfirmed", result }));
775
+ }
776
+ let known = false;
777
+ try {
778
+ await env.client.getTransaction({ hash });
779
+ known = true;
780
+ } catch (e) {
781
+ if (!isNotFound(e)) {
782
+ const { reason, message } = cartReason(e);
783
+ return { kind: "unavailable", reason, message };
784
+ }
785
+ }
786
+ const age = p.hashAt === null ? Infinity : nowSec() - p.hashAt;
787
+ if (known || age < CART_TIMING.unknownHashSec) return { kind: "waiting", message: "Still confirming." };
788
+ return lostHash(env, p);
789
+ }
790
+ async function lostHash(env, p) {
791
+ const up = await readHoldingsUp(env, p);
792
+ if (up) {
793
+ const result2 = resultFor(p, "collected", _nullishCoalesce(_nullishCoalesce(p.hash, () => ( env.memHash)), () => ( null)), tokensFromQuery(p));
794
+ return _nullishCoalesce(commit(env, p, result2), () => ( { kind: "collected", result: result2, warning: lostHashWarning(p.payer) }));
795
+ }
796
+ const result = resultFor(p, "unconfirmed", _nullishCoalesce(_nullishCoalesce(p.hash, () => ( env.memHash)), () => ( null)));
797
+ return _nullishCoalesce(commit(env, p, result), () => ( { kind: "unconfirmed", result }));
798
+ }
799
+
800
+ // src/cart/links.ts
801
+
802
+ var OPENSEA_CHAINS = {
803
+ 1: "ethereum",
804
+ 8453: "base",
805
+ 10: "optimism",
806
+ 360: "shape",
807
+ 33139: "ape_chain"
808
+ };
809
+ var openseaChain = (chainId) => _nullishCoalesce(OPENSEA_CHAINS[chainId], () => ( null));
810
+ function explorerTx(chainId, hash) {
811
+ if (!hash) return null;
812
+ let base;
813
+ try {
814
+ base = _optionalChain([_chunkOO6QQT3Mcjs.getChain.call(void 0, chainId), 'access', _34 => _34.blockExplorers, 'optionalAccess', _35 => _35.default, 'access', _36 => _36.url]);
815
+ } catch (e11) {
816
+ return null;
817
+ }
818
+ return base ? `${base.replace(/\/$/, "")}/tx/${hash}` : null;
819
+ }
820
+ function slugFor(collections, contract) {
821
+ if (!collections) return null;
822
+ const want = _viem.getAddress.call(void 0, contract);
823
+ for (const [k, v] of Object.entries(collections)) {
824
+ try {
825
+ if (_viem.getAddress.call(void 0, k) === want && v) return v;
826
+ } catch (e12) {
827
+ }
828
+ }
829
+ return null;
830
+ }
831
+ function collectedLinks(args) {
832
+ const tx = explorerTx(args.chainId, args.txHash);
833
+ const chain = openseaChain(args.chainId);
834
+ if (!chain) return { tx, profile: null, tokens: [] };
835
+ const wallet = args.wallet.toLowerCase();
836
+ const tokens = args.tokens.map((t) => ({ tokenId: t.tokenId, url: `https://opensea.io/item/${chain}/${t.contract.toLowerCase()}/${t.tokenId}` }));
837
+ const slug = slugFor(args.collections, args.contract);
838
+ const profile = slug ? `https://opensea.io/${wallet}/holdings?collectionSlugs=${encodeURIComponent(slug)}` : tokens.length === 0 ? `https://opensea.io/${wallet}` : null;
839
+ return { tx, profile, tokens };
840
+ }
841
+
842
+ // src/cart/react.ts
843
+ var activeKeys = /* @__PURE__ */ new Set();
844
+ var StateContext = _react.createContext.call(void 0, null);
845
+ var InternalsContext = _react.createContext.call(void 0, null);
846
+ function useCartInternals() {
847
+ const v = _react.useContext.call(void 0, InternalsContext);
848
+ if (!v) throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "useCheckout must be used inside <CartProvider>.");
849
+ return v;
850
+ }
851
+ function useCart() {
852
+ const v = _react.useContext.call(void 0, StateContext);
853
+ if (!v) throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "useCart must be used inside <CartProvider>.");
854
+ return v;
855
+ }
856
+ var isFinalRead = (e) => {
857
+ const r = cartReason(e).reason;
858
+ return _chunkOO6QQT3Mcjs.UNSUPPORTED_REASONS.includes(r) || r === "INVALID_CONFIG";
859
+ };
860
+ var emptyValue = (chainId) => ({ v: 6, chainId, items: [], pending: [], results: [] });
861
+ var LineBook = (_class3 = class {constructor() { _class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this); }
862
+ __init11() {this.map = /* @__PURE__ */ new Map()}
863
+ __init12() {this.version = 0}
864
+ __init13() {this.listeners = /* @__PURE__ */ new Set()}
865
+ __init14() {this.subscribe = (cb) => {
866
+ this.listeners.add(cb);
867
+ return () => void this.listeners.delete(cb);
868
+ }}
869
+ __init15() {this.getVersion = () => this.version}
870
+ changed() {
871
+ this.version += 1;
872
+ for (const cb of [...this.listeners]) cb();
873
+ }
874
+ }, _class3);
875
+ var Core = (_class4 = class {constructor() { _class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);_class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this);_class4.prototype.__init24.call(this);_class4.prototype.__init25.call(this);_class4.prototype.__init26.call(this); }
876
+ __init16() {this.lifetime = new AbortController()}
877
+ __init17() {this.scopes = /* @__PURE__ */ new Map()}
878
+ __init18() {this.settling = /* @__PURE__ */ new Map()}
879
+ __init19() {this.settledAtMount = /* @__PURE__ */ new Set()}
880
+ __init20() {this.busy = false}
881
+ __init21() {this.backoffUntil = 0}
882
+ __init22() {this.warned = false}
883
+ __init23() {this.registeredKey = null}
884
+ __init24() {this.widgetLoad = null}
885
+ __init25() {this.items = []}
886
+ __init26() {this.payer = null}
887
+ sync(items, payer) {
888
+ this.items = items;
889
+ this.payer = payer;
890
+ }
891
+ setBusy(b) {
892
+ this.busy = b;
893
+ }
894
+ setWidgetLoad(p) {
895
+ this.widgetLoad = p;
896
+ }
897
+ /** True the first time it is called. */
898
+ warnOnce() {
899
+ const first = !this.warned;
900
+ this.warned = true;
901
+ return first;
902
+ }
903
+ setRegistered(key) {
904
+ this.registeredKey = key;
905
+ }
906
+ renewLifetime() {
907
+ if (this.lifetime.signal.aborted) this.lifetime = new AbortController();
908
+ return this.lifetime;
909
+ }
910
+ }, _class4);
911
+ var subscribeAccountChanges = (notify) => _chunkOO6QQT3Mcjs.subscribeAccount.call(void 0, () => notify());
912
+ var accountSnapshot = () => {
913
+ const a = _chunkOO6QQT3Mcjs.getAccount.call(void 0, );
914
+ return a ? `${a.address}:${String(a.chainId)}` : "";
915
+ };
916
+ function buildLines(value, book, payer, now, version) {
917
+ if (version < 0) return [];
918
+ return value.items.map((item) => {
919
+ const key = itemKey(item.config);
920
+ const data = book.map.get(key);
921
+ const drop = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _37 => _37.drop]), () => ( null));
922
+ const walletRead = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _38 => _38.walletRead]), () => ( null));
923
+ const d = deriveLine({ drop, walletRead, readError: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _39 => _39.readError]), () => ( null)), now, pending: value.pending.some((p) => p.lineKey === key), walletConnected: payer !== null });
924
+ return { key, config: item.config, quantity: item.quantity, state: d.state, reason: d.reason, drop, maxQuantity: lineMaxQuantity(drop, walletRead), total: lineTotal(drop, item.quantity) };
925
+ });
926
+ }
927
+ var currentSeconds = () => Math.floor(Date.now() / 1e3);
928
+ function CartProvider(props) {
929
+ const { chainId, rpcUrls, children } = props;
930
+ const storageKey = _nullishCoalesce(props.storageKey, () => ( defaultStorageKey(chainId)));
931
+ const projectId = _optionalChain([props, 'access', _40 => _40.connect, 'optionalAccess', _41 => _41.walletConnectProjectId]);
932
+ const [core] = _react.useState.call(void 0, () => new Core());
933
+ const [book] = _react.useState.call(void 0, () => new LineBook());
934
+ const store = _react.useMemo.call(void 0, () => new CartStore({ storageKey, chainId }), [storageKey, chainId]);
935
+ const serverValue = _react.useMemo.call(void 0, () => emptyValue(chainId), [chainId]);
936
+ const value = _react.useSyncExternalStore.call(void 0, store.subscribe, store.getSnapshot, () => serverValue);
937
+ const bookVersion = _react.useSyncExternalStore.call(void 0, book.subscribe, book.getVersion, () => 0);
938
+ const accountKey = _react.useSyncExternalStore.call(void 0, subscribeAccountChanges, accountSnapshot, () => "");
939
+ const account = _react.useMemo.call(void 0, () => {
940
+ const [address, chain] = accountKey.split(":");
941
+ return address ? { address, chainId: Number(chain) } : null;
942
+ }, [accountKey]);
943
+ const payer = _nullishCoalesce(_optionalChain([account, 'optionalAccess', _42 => _42.address]), () => ( null));
944
+ _react.useEffect.call(void 0, () => core.sync(value.items, payer), [core, value.items, payer]);
945
+ _react.useEffect.call(void 0, () => {
946
+ if (activeKeys.has(storageKey)) {
947
+ throw new (0, _chunkOO6QQT3Mcjs.MintError)("INVALID_CONFIG", "Only one CartProvider per storageKey. Wrap your app once.", { field: "storageKey" });
948
+ }
949
+ activeKeys.add(storageKey);
950
+ core.setRegistered(storageKey);
951
+ const life = core.renewLifetime();
952
+ return () => {
953
+ if (core.registeredKey === storageKey) activeKeys.delete(storageKey);
954
+ core.setRegistered(null);
955
+ life.abort();
956
+ };
957
+ }, [storageKey, core]);
958
+ _react.useEffect.call(void 0, () => {
959
+ if (!core.warnOnce()) return;
960
+ if (!_optionalChain([rpcUrls, 'optionalAccess', _43 => _43[chainId]])) {
961
+ console.warn(`dropkit cart: no rpcUrls for chain ${String(chainId)}. Receipts, holdings and balance checks fall back to a public RPC and can be rate-limited during a sale.`);
962
+ }
963
+ }, []);
964
+ const rpcKey = _nullishCoalesce(_optionalChain([rpcUrls, 'optionalAccess', _44 => _44[chainId]]), () => ( ""));
965
+ const readClient = _react.useMemo.call(void 0,
966
+ () => _chunkOO6QQT3Mcjs.createReadClient.call(void 0, { chainId, ...rpcKey ? { rpcUrls: { [chainId]: rpcKey } } : {}, useBridge: true }),
967
+ [chainId, rpcKey]
968
+ );
969
+ const shared = _react.useMemo.call(void 0, () => sharedReadClient(readClient), [readClient]);
970
+ const limiter = _react.useMemo.call(void 0, () => new Limiter(() => CART_TIMING.maxReadsInFlight), []);
971
+ const merkleFetch = _react.useMemo.call(void 0, () => rateLimitedFetch(void 0, () => CART_TIMING.merklePerMinute), []);
972
+ const env = _react.useMemo.call(void 0,
973
+ () => ({
974
+ client: shared.client,
975
+ chainId,
976
+ fetch: merkleFetch,
977
+ ctxFor: (key) => {
978
+ let scope = core.scopes.get(key);
979
+ if (!scope) {
980
+ scope = {};
981
+ core.scopes.set(key, scope);
982
+ }
983
+ return { client: shared.client, fetch: merkleFetch, cacheScope: scope, signal: core.lifetime.signal };
984
+ }
985
+ }),
986
+ [shared, chainId, merkleFetch, core]
987
+ );
988
+ _react.useEffect.call(void 0, () => {
989
+ const loading = _chunkOO6QQT3Mcjs.loadConnectWidget.call(void 0, { chainIds: [chainId], ...projectId ? { walletConnectProjectId: projectId } : {} });
990
+ core.setWidgetLoad(loading);
991
+ loading.catch(() => void 0);
992
+ }, [chainId, projectId, core]);
993
+ const connect = _react.useCallback.call(void 0, async () => {
994
+ try {
995
+ await core.widgetLoad;
996
+ return await _chunkOO6QQT3Mcjs.openConnectModal.call(void 0, );
997
+ } catch (e13) {
998
+ return "dismissed";
999
+ }
1000
+ }, [core]);
1001
+ const setBusy = _react.useCallback.call(void 0, (b) => core.setBusy(b), [core]);
1002
+ const readLine = _react.useCallback.call(void 0,
1003
+ (key) => {
1004
+ const item = core.items.find((i) => itemKey(i.config) === key);
1005
+ if (!item || core.lifetime.signal.aborted) return;
1006
+ const data = _nullishCoalesce(book.map.get(key), () => ( { gen: 0, controller: null, drop: null, walletRead: null, readError: null, readFor: null }));
1007
+ book.map.set(key, data);
1008
+ _optionalChain([data, 'access', _45 => _45.controller, 'optionalAccess', _46 => _46.abort, 'call', _47 => _47()]);
1009
+ const controller = new AbortController();
1010
+ data.controller = controller;
1011
+ const gen = ++data.gen;
1012
+ const wantPayer = core.payer;
1013
+ const quantity = item.quantity;
1014
+ const ctx = { ...env.ctxFor(key), signal: controller.signal };
1015
+ void limiter.run(
1016
+ () => withRetries(
1017
+ async () => {
1018
+ const drop = await adapter.readDrop(ctx, item.config);
1019
+ let walletRead = null;
1020
+ if (wantPayer && drop.paymentToken === null) {
1021
+ walletRead = await adapter.readWallet(ctx, item.config, { payer: wantPayer, recipient: wantPayer, quantity, drop });
1022
+ }
1023
+ return { drop, walletRead };
1024
+ },
1025
+ CART_TIMING.retryDelaysMs,
1026
+ (e) => isFinalRead(e) || controller.signal.aborted,
1027
+ controller.signal
1028
+ )
1029
+ ).then(
1030
+ ({ drop, walletRead }) => {
1031
+ if (data.gen !== gen || controller.signal.aborted) return;
1032
+ data.drop = drop;
1033
+ data.walletRead = walletRead;
1034
+ data.readError = null;
1035
+ data.readFor = { quantity, payer: wantPayer };
1036
+ book.changed();
1037
+ const cap = lineMaxQuantity(drop, walletRead);
1038
+ const now = store.getSnapshot().items.find((i) => itemKey(i.config) === key);
1039
+ if (cap !== null && cap > 0 && now && now.quantity > cap && !store.pendingFor(key)) store.setQuantity(key, cap);
1040
+ },
1041
+ (e) => {
1042
+ if (data.gen !== gen || controller.signal.aborted) return;
1043
+ const r = cartReason(e);
1044
+ if (r.reason === "RATE_LIMITED") core.backoffUntil = Date.now() + CART_TIMING.refreshBackoffMs;
1045
+ data.readError = r;
1046
+ data.walletRead = null;
1047
+ data.readFor = { quantity, payer: wantPayer };
1048
+ book.changed();
1049
+ }
1050
+ );
1051
+ },
1052
+ [env, limiter, book, core, store]
1053
+ );
1054
+ const itemsSig = value.items.map((i) => `${itemKey(i.config)}#${String(i.quantity)}`).join("|");
1055
+ const pendingSig = value.pending.map((p) => p.lineKey).join("|");
1056
+ _react.useEffect.call(void 0, () => {
1057
+ const keys = /* @__PURE__ */ new Set();
1058
+ for (const item of core.items) {
1059
+ const key = itemKey(item.config);
1060
+ keys.add(key);
1061
+ if (store.pendingFor(key)) continue;
1062
+ const data = book.map.get(key);
1063
+ if (!data || data.readFor === null || data.readFor.quantity !== item.quantity || data.readFor.payer !== core.payer) {
1064
+ if (!data || data.controller === null || _optionalChain([data, 'access', _48 => _48.readFor, 'optionalAccess', _49 => _49.quantity]) !== item.quantity || _optionalChain([data, 'access', _50 => _50.readFor, 'optionalAccess', _51 => _51.payer]) !== core.payer) readLine(key);
1065
+ }
1066
+ }
1067
+ for (const [key, data] of book.map) {
1068
+ if (!keys.has(key)) {
1069
+ _optionalChain([data, 'access', _52 => _52.controller, 'optionalAccess', _53 => _53.abort, 'call', _54 => _54()]);
1070
+ book.map.delete(key);
1071
+ core.scopes.delete(key);
1072
+ }
1073
+ }
1074
+ }, [itemsSig, payer, pendingSig, readLine, store, book, core]);
1075
+ _react.useEffect.call(void 0, () => {
1076
+ let timer;
1077
+ const tick = () => {
1078
+ const wait = Date.now() < core.backoffUntil ? CART_TIMING.refreshBackoffMs : CART_TIMING.refreshMs;
1079
+ timer = setTimeout(() => {
1080
+ const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
1081
+ if (!hidden && !core.busy) {
1082
+ shared.bump();
1083
+ for (const item of core.items) if (!store.pendingFor(itemKey(item.config))) readLine(itemKey(item.config));
1084
+ }
1085
+ tick();
1086
+ }, wait);
1087
+ };
1088
+ tick();
1089
+ return () => clearTimeout(timer);
1090
+ }, [shared, store, readLine, core]);
1091
+ const settleOne = _react.useCallback.call(void 0,
1092
+ (id, extra) => {
1093
+ const hit = core.settling.get(id);
1094
+ if (hit) return hit;
1095
+ const p = store.getSnapshot().pending.find((x) => x.id === id);
1096
+ if (!p) return Promise.resolve(null);
1097
+ const run = settle({ client: readClient, ctx: env.ctxFor(p.lineKey), store, receipt: _nullishCoalesce(_optionalChain([extra, 'optionalAccess', _55 => _55.receipt]), () => ( null)), memHash: _nullishCoalesce(_optionalChain([extra, 'optionalAccess', _56 => _56.memHash]), () => ( null)) }, p).catch((e) => ({ kind: "unavailable", ...cartReason(e) })).finally(() => core.settling.delete(id));
1098
+ core.settling.set(id, run);
1099
+ return run;
1100
+ },
1101
+ [store, env, readClient, core]
1102
+ );
1103
+ _react.useEffect.call(void 0, () => {
1104
+ for (const p of store.getSnapshot().pending) {
1105
+ if (core.settledAtMount.has(p.id)) continue;
1106
+ core.settledAtMount.add(p.id);
1107
+ void settleOne(p.id).then((out) => {
1108
+ if (out && (out.kind === "collected" || out.kind === "unconfirmed")) shared.bump();
1109
+ });
1110
+ }
1111
+ }, [store, settleOne, shared, core]);
1112
+ const cartLines = _react.useMemo.call(void 0,
1113
+ () => buildLines(value, book, payer, currentSeconds(), bookVersion),
1114
+ [value, book, payer, bookVersion]
1115
+ );
1116
+ const total = _react.useMemo.call(void 0, () => {
1117
+ const counted = cartLines.filter((l) => l.state !== "unsupported" && l.state !== "locked" && l.total !== null);
1118
+ if (counted.length === 0) return null;
1119
+ return sumMoney(counted.map((l) => l.total), counted[0].total);
1120
+ }, [cartLines]);
1121
+ const unconfirmed = _react.useMemo.call(void 0,
1122
+ () => value.results.filter((r) => r.status === "unconfirmed").map((r) => ({ id: r.id, key: r.lineKey, config: r.config, quantity: r.quantity, at: r.at, wallet: r.payer, message: unconfirmedMessage(r.payer) })),
1123
+ [value.results]
1124
+ );
1125
+ const openseaCollections = props.openseaCollections;
1126
+ const collected = _react.useMemo.call(void 0,
1127
+ () => value.results.filter((r) => r.status === "collected").map((r) => ({
1128
+ id: r.id,
1129
+ key: r.lineKey,
1130
+ quantity: r.quantity,
1131
+ at: r.at,
1132
+ txHash: r.txHash,
1133
+ wallet: r.payer,
1134
+ tokens: r.tokens,
1135
+ links: collectedLinks({ chainId, wallet: r.recipient, contract: r.config.creatorContract, txHash: r.txHash, tokens: r.tokens, collections: openseaCollections })
1136
+ })),
1137
+ [value.results, chainId, openseaCollections]
1138
+ );
1139
+ const add = _react.useCallback.call(void 0,
1140
+ async (config, quantity = 1, opts) => {
1141
+ const canon = canonicalConfig(config, chainId);
1142
+ const key = itemKey(canon);
1143
+ const snap = store.getSnapshot();
1144
+ if (snap.pending.some((p) => p.lineKey === key)) return "added";
1145
+ const priors = snap.results.filter((r) => r.status === "unconfirmed" && r.lineKey === key);
1146
+ if (priors.length > 0 && !_optionalChain([opts, 'optionalAccess', _57 => _57.force])) {
1147
+ if (!adapter.readHoldings) return "check-unavailable";
1148
+ for (const r of priors) {
1149
+ try {
1150
+ const now = await adapter.readHoldings(env.ctxFor(key), r.holdings, r.recipient);
1151
+ if (now - BigInt(r.holdings.before) >= BigInt(r.quantity)) return "looks-collected";
1152
+ } catch (e14) {
1153
+ return "check-unavailable";
1154
+ }
1155
+ }
1156
+ }
1157
+ const data = book.map.get(key);
1158
+ const cap = _optionalChain([data, 'optionalAccess', _58 => _58.drop]) ? lineMaxQuantity(data.drop, data.walletRead) : null;
1159
+ store.addItem(canon, quantity, { cap, dropResults: priors.map((r) => r.id) });
1160
+ return "added";
1161
+ },
1162
+ [chainId, store, env, book]
1163
+ );
1164
+ const state = _react.useMemo.call(void 0,
1165
+ () => ({
1166
+ lines: cartLines,
1167
+ unconfirmed,
1168
+ collected,
1169
+ total,
1170
+ count: cartLines.length,
1171
+ add,
1172
+ setQuantity: (key, quantity) => {
1173
+ const line = cartLines.find((l) => l.key === key);
1174
+ const cap = _optionalChain([line, 'optionalAccess', _59 => _59.maxQuantity]);
1175
+ store.setQuantity(key, cap !== null && cap !== void 0 ? Math.min(quantity, Math.max(1, cap)) : quantity);
1176
+ },
1177
+ remove: (key) => store.removeItem(key),
1178
+ clear: () => store.clearItems(),
1179
+ has: (config) => {
1180
+ try {
1181
+ return store.hasItem(itemKey(canonicalConfig(config, chainId)));
1182
+ } catch (e15) {
1183
+ return false;
1184
+ }
1185
+ },
1186
+ dismissUnconfirmed: (id) => store.deleteResult(id),
1187
+ checkPending: async () => {
1188
+ await Promise.all(store.getSnapshot().pending.map((p) => settleOne(p.id)));
1189
+ shared.bump();
1190
+ }
1191
+ }),
1192
+ [cartLines, unconfirmed, collected, total, add, store, chainId, settleOne, shared]
1193
+ );
1194
+ const internals = _react.useMemo.call(void 0,
1195
+ () => ({
1196
+ chainId,
1197
+ store,
1198
+ env,
1199
+ shared,
1200
+ limiter,
1201
+ account,
1202
+ getSignal: () => core.lifetime.signal,
1203
+ connect,
1204
+ setBusy,
1205
+ settle: settleOne,
1206
+ refreshLine: readLine,
1207
+ lineData: (key) => _nullishCoalesce(book.map.get(key), () => ( null))
1208
+ }),
1209
+ [chainId, store, env, shared, limiter, account, connect, setBusy, settleOne, readLine, book, core]
1210
+ );
1211
+ return _react.createElement.call(void 0, InternalsContext.Provider, { value: internals }, _react.createElement.call(void 0, StateContext.Provider, { value: state }, children));
1212
+ }
1213
+
1214
+ // src/cart/useCheckout.ts
1215
+
1216
+
1217
+ var nowSec2 = () => Math.floor(Date.now() / 1e3);
1218
+ var BUSY = /* @__PURE__ */ new Set(["preparing", "ready", "sending", "checking", "paused"]);
1219
+ var fresh = () => ({
1220
+ state: "idle",
1221
+ action: null,
1222
+ reason: null,
1223
+ message: null,
1224
+ steps: [],
1225
+ currentKey: null,
1226
+ run: 0,
1227
+ needsAddChain: false,
1228
+ initialDone: false,
1229
+ assessedQty: {},
1230
+ prepared: null,
1231
+ payer: null,
1232
+ waiting: null,
1233
+ claimed: false,
1234
+ expiry: null
1235
+ });
1236
+ var step = (key, patch = {}) => ({ key, status: "pending", reason: null, message: null, txHash: null, ...patch });
1237
+ function useCheckout() {
1238
+ const cart = useCartInternals();
1239
+ const { store } = cart;
1240
+ const value = _react.useSyncExternalStore.call(void 0, store.subscribe, store.getSnapshot, store.getSnapshot);
1241
+ const [, publish] = _react.useReducer.call(void 0, (n) => n + 1, 0);
1242
+ const cRef = _react.useRef.call(void 0, fresh());
1243
+ const c = cRef.current;
1244
+ const cartRef = _react.useRef.call(void 0, cart);
1245
+ cartRef.current = cart;
1246
+ const alive = _react.useRef.call(void 0, true);
1247
+ _react.useEffect.call(void 0, () => {
1248
+ alive.current = true;
1249
+ const ctl = cRef.current;
1250
+ const k = cartRef;
1251
+ return () => {
1252
+ alive.current = false;
1253
+ ctl.run += 1;
1254
+ if (ctl.expiry) clearTimeout(ctl.expiry);
1255
+ k.current.setBusy(false);
1256
+ };
1257
+ }, []);
1258
+ const set = _react.useCallback.call(void 0, (patch) => {
1259
+ const ctl = cRef.current;
1260
+ Object.assign(ctl, patch);
1261
+ if (ctl.expiry && ctl.state !== "ready") {
1262
+ clearTimeout(ctl.expiry);
1263
+ ctl.expiry = null;
1264
+ }
1265
+ cartRef.current.setBusy(BUSY.has(ctl.state));
1266
+ if (alive.current) publish();
1267
+ }, []);
1268
+ const setStep = _react.useCallback.call(void 0,
1269
+ (key, patch) => {
1270
+ const ctl = cRef.current;
1271
+ ctl.steps = ctl.steps.map((s) => s.key === key ? { ...s, ...patch } : s);
1272
+ },
1273
+ []
1274
+ );
1275
+ const startPreparing = _react.useCallback.call(void 0, (patch = {}) => {
1276
+ const ctl = cRef.current;
1277
+ const run = ctl.run + 1;
1278
+ set({ state: "preparing", action: null, reason: null, message: null, prepared: null, claimed: false, run, ...patch });
1279
+ void prepare(run);
1280
+ }, []);
1281
+ const prepare = _react.useCallback.call(void 0, async (run) => {
1282
+ const ctl = cRef.current;
1283
+ const live = () => alive.current && ctl.run === run;
1284
+ const k = cartRef.current;
1285
+ const blocked = (reason, message) => {
1286
+ if (live()) set({ state: "blocked", action: null, reason, message, prepared: null, currentKey: null, claimed: false });
1287
+ };
1288
+ const idle = () => {
1289
+ if (live()) set({ ...fresh(), run: ctl.run + 1 });
1290
+ };
1291
+ try {
1292
+ if (!k.store.persistenceOk()) return blocked("PERSISTENCE_UNAVAILABLE", "The cart could not save its state in this tab, so nothing was sent.");
1293
+ let account = _chunkOO6QQT3Mcjs.getAccount.call(void 0, );
1294
+ if (!account) {
1295
+ const outcome = await k.connect();
1296
+ if (!live()) return;
1297
+ account = _chunkOO6QQT3Mcjs.getAccount.call(void 0, );
1298
+ if (outcome === "dismissed" || !account) return idle();
1299
+ }
1300
+ const payer = account.address;
1301
+ if (ctl.payer !== null && ctl.payer.toLowerCase() !== payer.toLowerCase()) {
1302
+ ctl.initialDone = false;
1303
+ }
1304
+ ctl.payer = payer;
1305
+ if (account.chainId !== k.chainId) {
1306
+ const action = ctl.needsAddChain ? { kind: "add-chain", chainId: k.chainId } : { kind: "switch-chain", chainId: k.chainId };
1307
+ return set({ state: "ready", action, reason: null, message: null, claimed: false });
1308
+ }
1309
+ const qtyNow = (key) => _nullishCoalesce(_optionalChain([k, 'access', _60 => _60.store, 'access', _61 => _61.getSnapshot, 'call', _62 => _62(), 'access', _63 => _63.items, 'access', _64 => _64.find, 'call', _65 => _65((i) => itemKey(i.config) === key), 'optionalAccess', _66 => _66.quantity]), () => ( 0));
1310
+ const grew = () => ctl.steps.some((s) => (s.status === "pending" || s.status === "active") && qtyNow(s.key) > (_nullishCoalesce(ctl.assessedQty[s.key], () => ( 0))));
1311
+ while (!ctl.initialDone || grew()) {
1312
+ const ok = await assessAll(run, payer);
1313
+ if (!ok || !live()) return;
1314
+ ctl.initialDone = true;
1315
+ }
1316
+ for (; ; ) {
1317
+ const next = ctl.steps.find((s) => s.status === "pending" || s.status === "active");
1318
+ if (!next) break;
1319
+ const item = k.store.getSnapshot().items.find((i) => itemKey(i.config) === next.key);
1320
+ if (!item) {
1321
+ setStep(next.key, { status: "skipped", reason: null, message: "This item left the cart." });
1322
+ continue;
1323
+ }
1324
+ if (k.store.pendingFor(next.key)) {
1325
+ setStep(next.key, { status: "skipped", reason: null, message: "A mint for this item is still being confirmed." });
1326
+ continue;
1327
+ }
1328
+ try {
1329
+ const prepared = await withRetries(
1330
+ () => k.limiter.run(() => prepareLine(k.env, { config: item.config, quantity: item.quantity, revision: k.store.revision }, payer)),
1331
+ CART_TIMING.retryDelaysMs,
1332
+ (e) => e instanceof LineSkipped || e instanceof CartError,
1333
+ k.getSignal()
1334
+ );
1335
+ if (!live()) return;
1336
+ setStep(next.key, { status: "active", reason: null, message: null });
1337
+ const action = { kind: "mint", key: prepared.key, amount: prepared.amount };
1338
+ ctl.expiry = setTimeout(() => {
1339
+ if (alive.current && ctl.run === run && ctl.state === "ready") startPreparing();
1340
+ }, CART_TIMING.preparedExpiryMs);
1341
+ set({ state: "ready", action, reason: null, message: null, prepared, currentKey: prepared.key, claimed: false });
1342
+ if (!ctl.expiry) {
1343
+ ctl.expiry = setTimeout(() => {
1344
+ if (alive.current && ctl.run === run && ctl.state === "ready") startPreparing();
1345
+ }, CART_TIMING.preparedExpiryMs);
1346
+ }
1347
+ return;
1348
+ } catch (e) {
1349
+ if (!live()) return;
1350
+ if (e instanceof LineSkipped) setStep(next.key, { status: "skipped", reason: e.reason, message: e.message });
1351
+ else {
1352
+ const r = cartReason(e);
1353
+ setStep(next.key, { status: "skipped", reason: r.reason, message: r.message });
1354
+ }
1355
+ publish();
1356
+ }
1357
+ }
1358
+ const settled = ctl.steps.some((s) => s.status === "success" || s.status === "unconfirmed");
1359
+ const first = ctl.steps.find((s) => s.status === "skipped");
1360
+ if (!settled && first) return blocked(_nullishCoalesce(first.reason, () => ( "RPC_ERROR")), _nullishCoalesce(first.message, () => ( "No item in the cart can be collected right now.")));
1361
+ if (live()) set({ state: "done", action: null, reason: null, message: null, prepared: null, currentKey: null, claimed: false });
1362
+ } catch (e) {
1363
+ if (!live()) return;
1364
+ const r = cartReason(e);
1365
+ blocked(r.reason, r.message);
1366
+ }
1367
+ }, []);
1368
+ const assessAll = _react.useCallback.call(void 0, async (run, payer) => {
1369
+ const ctl = cRef.current;
1370
+ const k = cartRef.current;
1371
+ const live = () => alive.current && ctl.run === run;
1372
+ k.shared.bump();
1373
+ const plans = [];
1374
+ const assessedQty = {};
1375
+ for (const s of ctl.steps.filter((x) => x.status === "pending" || x.status === "active")) {
1376
+ const item = k.store.getSnapshot().items.find((i) => itemKey(i.config) === s.key);
1377
+ if (!item) {
1378
+ setStep(s.key, { status: "skipped", message: "This item left the cart." });
1379
+ continue;
1380
+ }
1381
+ if (k.store.pendingFor(s.key)) {
1382
+ setStep(s.key, { status: "skipped", message: "A mint for this item is still being confirmed." });
1383
+ continue;
1384
+ }
1385
+ try {
1386
+ const a = await withRetries(
1387
+ () => k.limiter.run(() => assessLine(k.env, { config: item.config, quantity: item.quantity }, payer)),
1388
+ CART_TIMING.retryDelaysMs,
1389
+ (e) => e instanceof LineSkipped || e instanceof CartError,
1390
+ k.getSignal()
1391
+ );
1392
+ if (!live()) return false;
1393
+ assessedQty[s.key] = item.quantity;
1394
+ plans.push({ value: a.plan.mint.value, gas: a.walletRead.gasReserve, symbol: a.drop.unitPrice.symbol, decimals: a.drop.unitPrice.decimals });
1395
+ } catch (e) {
1396
+ if (!live()) return false;
1397
+ if (e instanceof LineSkipped) setStep(s.key, { status: "skipped", reason: e.reason, message: e.message });
1398
+ else {
1399
+ const r = cartReason(e);
1400
+ setStep(s.key, { status: "skipped", reason: r.reason, message: r.message });
1401
+ }
1402
+ }
1403
+ }
1404
+ if (plans.length > 0) {
1405
+ const balance = await withRetries(() => k.limiter.run(() => k.shared.client.getBalance({ address: payer })), CART_TIMING.retryDelaysMs, () => false, k.getSignal());
1406
+ if (!live()) return false;
1407
+ const need = plans.reduce((n, p) => n + p.value + p.gas, 0n);
1408
+ if (balance < need) {
1409
+ const p = plans[0];
1410
+ const short = need - balance;
1411
+ set({
1412
+ state: "blocked",
1413
+ action: null,
1414
+ reason: "INSUFFICIENT_FUNDS",
1415
+ message: `Your balance is ${_viem.formatUnits.call(void 0, short, p.decimals)} ${p.symbol} short of this cart (price, fees and gas).`,
1416
+ prepared: null,
1417
+ currentKey: null,
1418
+ claimed: false
1419
+ });
1420
+ return false;
1421
+ }
1422
+ }
1423
+ ctl.assessedQty = assessedQty;
1424
+ return true;
1425
+ }, []);
1426
+ const applyOutcome = _react.useCallback.call(void 0,
1427
+ (key, id, out, memHash) => {
1428
+ const ctl = cRef.current;
1429
+ if (!alive.current) return;
1430
+ if (!out) return startPreparing();
1431
+ switch (out.kind) {
1432
+ case "collected":
1433
+ setStep(key, { status: "success", reason: null, message: out.warning, txHash: out.result.txHash });
1434
+ cartRef.current.shared.bump();
1435
+ return startPreparing();
1436
+ case "unconfirmed":
1437
+ setStep(key, { status: "unconfirmed", reason: null, message: unconfirmedMessage(out.result.payer), txHash: out.result.txHash });
1438
+ cartRef.current.shared.bump();
1439
+ return startPreparing();
1440
+ case "not-sent":
1441
+ setStep(key, { status: "pending", reason: null, message: null });
1442
+ return startPreparing();
1443
+ case "failed":
1444
+ setStep(key, { status: "failed", reason: "SIMULATION_REVERTED", message: out.message, txHash: out.hash });
1445
+ return set({ state: "paused", action: null, reason: "SIMULATION_REVERTED", message: out.message, prepared: null, waiting: null, claimed: false });
1446
+ case "waiting":
1447
+ setStep(key, { status: "unknown", reason: null, message: out.message });
1448
+ return set({ state: "checking", action: null, reason: null, message: out.message, prepared: null, waiting: { id, key, memHash }, claimed: false });
1449
+ case "unavailable":
1450
+ setStep(key, { status: "unknown", reason: out.reason, message: out.message });
1451
+ return set({ state: "checking", action: null, reason: out.reason, message: out.message, prepared: null, waiting: { id, key, memHash }, claimed: false });
1452
+ }
1453
+ void ctl;
1454
+ },
1455
+ [set, setStep, startPreparing]
1456
+ );
1457
+ const checkout = _react.useCallback.call(void 0, () => {
1458
+ const ctl = cRef.current;
1459
+ if (ctl.state !== "idle") return;
1460
+ const k = cartRef.current;
1461
+ const items = k.store.getSnapshot().items;
1462
+ const steps = items.map((i) => step(itemKey(i.config)));
1463
+ k.shared.bump();
1464
+ startPreparing({ steps, currentKey: null, needsAddChain: false, initialDone: false, payer: null, waiting: null });
1465
+ void steps;
1466
+ }, [startPreparing]);
1467
+ const confirm = _react.useCallback.call(void 0, () => {
1468
+ const ctl = cRef.current;
1469
+ if (ctl.claimed || ctl.state !== "ready" || !ctl.action) return;
1470
+ ctl.claimed = true;
1471
+ const action = ctl.action;
1472
+ const k = cartRef.current;
1473
+ const run = ctl.run;
1474
+ if (action.kind === "switch-chain" || action.kind === "add-chain") {
1475
+ set({ state: "sending", claimed: true });
1476
+ const request = action.kind === "switch-chain" ? sendSwitch(action.chainId) : sendAddChain(action.chainId).then(() => ({ needsAdd: false }));
1477
+ request.then(
1478
+ (r) => {
1479
+ if (!alive.current || ctl.run !== run) return;
1480
+ if (r.needsAdd) return set({ state: "ready", action: { kind: "add-chain", chainId: k.chainId }, needsAddChain: true, claimed: false });
1481
+ startPreparing({ needsAddChain: false });
1482
+ },
1483
+ (e) => {
1484
+ if (!alive.current || ctl.run !== run) return;
1485
+ const m = cartReason(e);
1486
+ set({ ...fresh(), run: ctl.run + 1, steps: ctl.steps, state: "idle", reason: "CHAIN_SWITCH_FAILED", message: m.reason === "USER_REJECTED" ? "You rejected the network request." : m.message });
1487
+ }
1488
+ );
1489
+ return;
1490
+ }
1491
+ const prepared = ctl.prepared;
1492
+ const item = k.store.getSnapshot().items.find((i) => itemKey(i.config) === action.key);
1493
+ if (!prepared || prepared.key !== action.key || !item || item.quantity !== prepared.quantity || k.store.revision !== prepared.revision) {
1494
+ startPreparing();
1495
+ return;
1496
+ }
1497
+ const pending = {
1498
+ id: newId(),
1499
+ lineKey: prepared.key,
1500
+ config: prepared.config,
1501
+ payer: prepared.payer,
1502
+ recipient: prepared.recipient,
1503
+ quantity: prepared.quantity,
1504
+ holdings: prepared.holdings,
1505
+ hash: null,
1506
+ hashAt: null,
1507
+ createdAt: nowSec2()
1508
+ };
1509
+ try {
1510
+ if (!k.store.writePending(pending)) {
1511
+ startPreparing();
1512
+ return;
1513
+ }
1514
+ } catch (e) {
1515
+ const m = cartReason(e);
1516
+ set({ state: "blocked", action: null, reason: m.reason, message: m.message, prepared: null, claimed: false });
1517
+ return;
1518
+ }
1519
+ markCallOpen(pending.id);
1520
+ set({ state: "sending", claimed: true, waiting: { id: pending.id, key: pending.lineKey, memHash: null } });
1521
+ let memHash = null;
1522
+ let hashWriteFailed = false;
1523
+ const sent = sendMint(prepared, {
1524
+ onHash: (hash) => {
1525
+ memHash = hash;
1526
+ try {
1527
+ k.store.setHash(pending.id, hash, nowSec2());
1528
+ } catch (e16) {
1529
+ hashWriteFailed = true;
1530
+ }
1531
+ }
1532
+ });
1533
+ void sent.then(
1534
+ async (hash) => {
1535
+ markCallClosed(pending.id);
1536
+ if (!alive.current || ctl.run !== run) return;
1537
+ setStep(pending.lineKey, { txHash: hash });
1538
+ if (hashWriteFailed) {
1539
+ setStep(pending.lineKey, { status: "unknown", message: "Still confirming" });
1540
+ return set({ state: "checking", action: null, message: "Still confirming", prepared: null, waiting: { id: pending.id, key: pending.lineKey, memHash }, claimed: false });
1541
+ }
1542
+ let receipt;
1543
+ try {
1544
+ receipt = await waitMint(k.env.client, { hash, tokenContract: prepared.config.creatorContract, recipient: prepared.recipient });
1545
+ } catch (e17) {
1546
+ receipt = null;
1547
+ }
1548
+ if (!alive.current || ctl.run !== run) return;
1549
+ if (!receipt) {
1550
+ setStep(pending.lineKey, { status: "unknown", message: "Still confirming" });
1551
+ return set({ state: "checking", action: null, message: "Still confirming", prepared: null, waiting: { id: pending.id, key: pending.lineKey, memHash }, claimed: false });
1552
+ }
1553
+ const out = await k.settle(pending.id, { receipt, memHash });
1554
+ applyOutcome(pending.lineKey, pending.id, out, memHash);
1555
+ },
1556
+ async (e) => {
1557
+ markCallClosed(pending.id);
1558
+ if (!alive.current || ctl.run !== run) return;
1559
+ if (e instanceof CartError && e.reason === "REQUEST_NOT_SENT") {
1560
+ markNotSent(pending.id);
1561
+ const out2 = await k.settle(pending.id);
1562
+ return applyOutcome(pending.lineKey, pending.id, out2, null);
1563
+ }
1564
+ if (_chunkOO6QQT3Mcjs.isRejection.call(void 0, e)) {
1565
+ try {
1566
+ k.store.removePending(pending.id);
1567
+ } catch (e18) {
1568
+ }
1569
+ setStep(pending.lineKey, { status: "failed", reason: "USER_REJECTED", message: "You rejected the request." });
1570
+ return set({ state: "paused", action: null, reason: "USER_REJECTED", message: "You rejected the request.", prepared: null, waiting: null, claimed: false });
1571
+ }
1572
+ const out = await k.settle(pending.id, { memHash });
1573
+ applyOutcome(pending.lineKey, pending.id, out, memHash);
1574
+ }
1575
+ );
1576
+ }, [applyOutcome, set, setStep, startPreparing]);
1577
+ const check = _react.useCallback.call(void 0, () => {
1578
+ const ctl = cRef.current;
1579
+ if (ctl.state !== "checking" || !ctl.waiting || ctl.claimed) return;
1580
+ ctl.claimed = true;
1581
+ const w = ctl.waiting;
1582
+ const k = cartRef.current;
1583
+ const run = ctl.run;
1584
+ void (async () => {
1585
+ const p = k.store.getSnapshot().pending.find((x) => x.id === w.id);
1586
+ if (p && p.hash === null && w.memHash) {
1587
+ try {
1588
+ k.store.setHash(w.id, w.memHash, nowSec2());
1589
+ } catch (e19) {
1590
+ }
1591
+ }
1592
+ const out = await k.settle(w.id, { memHash: w.memHash });
1593
+ if (!alive.current || ctl.run !== run) return;
1594
+ ctl.claimed = false;
1595
+ if (out && (out.kind === "waiting" || out.kind === "unavailable")) {
1596
+ setStep(w.key, { status: "unknown", reason: out.kind === "unavailable" ? out.reason : null, message: out.kind === "waiting" ? "Still confirming" : out.message });
1597
+ return set({ state: "checking", reason: out.kind === "unavailable" ? out.reason : null, message: out.kind === "waiting" ? "Still confirming" : out.message });
1598
+ }
1599
+ applyOutcome(w.key, w.id, out, w.memHash);
1600
+ })();
1601
+ }, [applyOutcome, set, setStep]);
1602
+ const retry = _react.useCallback.call(void 0, () => {
1603
+ const ctl = cRef.current;
1604
+ if (ctl.state !== "paused" || !ctl.currentKey) return;
1605
+ setStep(ctl.currentKey, { status: "pending", reason: null, message: null, txHash: null });
1606
+ startPreparing();
1607
+ }, [setStep, startPreparing]);
1608
+ const skip = _react.useCallback.call(void 0, () => {
1609
+ const ctl = cRef.current;
1610
+ if (ctl.state !== "paused" || !ctl.currentKey) return;
1611
+ const s = ctl.steps.find((x) => x.key === ctl.currentKey);
1612
+ setStep(ctl.currentKey, { status: "skipped", reason: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _67 => _67.reason]), () => ( null)), message: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _68 => _68.message]), () => ( null)) });
1613
+ startPreparing();
1614
+ }, [setStep, startPreparing]);
1615
+ const stop = _react.useCallback.call(void 0, () => {
1616
+ const ctl = cRef.current;
1617
+ if (ctl.state !== "ready" && ctl.state !== "checking" && ctl.state !== "paused") return;
1618
+ set({ state: "done", action: null, prepared: null, claimed: false, run: ctl.run + 1 });
1619
+ }, [set]);
1620
+ const reset = _react.useCallback.call(void 0, () => {
1621
+ const ctl = cRef.current;
1622
+ if (ctl.state !== "blocked" && ctl.state !== "done") return;
1623
+ set({ ...fresh(), run: ctl.run + 1 });
1624
+ }, [set]);
1625
+ const accountAddress = _nullishCoalesce(_optionalChain([cart, 'access', _69 => _69.account, 'optionalAccess', _70 => _70.address]), () => ( null));
1626
+ const accountChain = _nullishCoalesce(_optionalChain([cart, 'access', _71 => _71.account, 'optionalAccess', _72 => _72.chainId]), () => ( null));
1627
+ const lastAccount = _react.useRef.call(void 0, { address: accountAddress, chain: accountChain });
1628
+ _react.useEffect.call(void 0, () => {
1629
+ const prev = lastAccount.current;
1630
+ lastAccount.current = { address: accountAddress, chain: accountChain };
1631
+ const ctl = cRef.current;
1632
+ if (prev.address === accountAddress && prev.chain === accountChain) return;
1633
+ if (ctl.state !== "preparing" && ctl.state !== "ready" && ctl.state !== "paused") return;
1634
+ if (accountAddress === null) {
1635
+ set({ ...fresh(), run: ctl.run + 1 });
1636
+ return;
1637
+ }
1638
+ if (prev.address !== accountAddress) {
1639
+ startPreparing({ initialDone: false, payer: null });
1640
+ return;
1641
+ }
1642
+ if (ctl.state === "ready") startPreparing();
1643
+ }, [accountAddress, accountChain, set, startPreparing]);
1644
+ _react.useEffect.call(void 0, () => {
1645
+ const ctl = cRef.current;
1646
+ if (ctl.state !== "ready" || !ctl.prepared) return;
1647
+ const p = ctl.prepared;
1648
+ const item = value.items.find((i) => itemKey(i.config) === p.key);
1649
+ if (!item || item.quantity !== p.quantity || store.revision !== p.revision) startPreparing();
1650
+ }, [value, store, startPreparing]);
1651
+ const published = cRef.current;
1652
+ return _react.useMemo.call(void 0, () => {
1653
+ const live = published.steps.filter((s) => s.status !== "skipped");
1654
+ const idx = published.currentKey === null ? -1 : live.findIndex((s) => s.key === published.currentKey);
1655
+ return {
1656
+ state: published.state,
1657
+ action: published.action,
1658
+ reason: published.reason,
1659
+ message: published.message,
1660
+ steps: published.steps.map((s) => ({ ...s })),
1661
+ current: idx >= 0 ? idx + 1 : null,
1662
+ checkout,
1663
+ confirm,
1664
+ check,
1665
+ retry,
1666
+ skip,
1667
+ stop,
1668
+ reset
1669
+ };
1670
+ }, [c.state, c.action, c.reason, c.message, c.steps, c.currentKey, checkout, confirm, check, retry, skip, stop, reset, renderKey(published)]);
1671
+ }
1672
+ function renderKey(ctl) {
1673
+ return `${ctl.run}|${ctl.state}|${ctl.steps.map((s) => s.status).join(",")}|${_nullishCoalesce(ctl.message, () => ( ""))}|${_nullishCoalesce(ctl.currentKey, () => ( ""))}`;
1674
+ }
1675
+
1676
+ // src/cart/useHoldings.ts
1677
+
1678
+
1679
+ function useHoldings(configs, opts) {
1680
+ const k = useCartInternals();
1681
+ const { collected } = useCart();
1682
+ const wallet = _react.useMemo.call(void 0, () => {
1683
+ const w = _nullishCoalesce(_nullishCoalesce(_optionalChain([opts, 'optionalAccess', _73 => _73.wallet]), () => ( _optionalChain([k, 'access', _74 => _74.account, 'optionalAccess', _75 => _75.address]))), () => ( null));
1684
+ return w ? _viem.getAddress.call(void 0, w) : null;
1685
+ }, [_optionalChain([opts, 'optionalAccess', _76 => _76.wallet]), _optionalChain([k, 'access', _77 => _77.account, 'optionalAccess', _78 => _78.address])]);
1686
+ const keyed = _react.useMemo.call(void 0, () => {
1687
+ const out = [];
1688
+ for (const c of configs) {
1689
+ try {
1690
+ const config = canonicalConfig(c, k.chainId);
1691
+ out.push({ key: itemKey(config), config });
1692
+ } catch (e20) {
1693
+ }
1694
+ }
1695
+ return out;
1696
+ }, [configs, k.chainId]);
1697
+ const listKey = keyed.map((x) => x.key).join("|");
1698
+ const [read, setRead] = _react.useState.call(void 0, { wallet: null, byKey: {} });
1699
+ const [tick, setTick] = _react.useState.call(void 0, 0);
1700
+ const run = _react.useRef.call(void 0, 0);
1701
+ _react.useEffect.call(void 0, () => {
1702
+ const id = ++run.current;
1703
+ if (!wallet || keyed.length === 0) return;
1704
+ const round = new AbortController();
1705
+ const providerSignal = k.getSignal();
1706
+ const onProviderAbort = () => round.abort();
1707
+ providerSignal.addEventListener("abort", onProviderAbort);
1708
+ const signal = round.signal;
1709
+ const live = () => run.current === id && !signal.aborted;
1710
+ void Promise.all(
1711
+ keyed.map(async ({ key, config }) => {
1712
+ let holding = null;
1713
+ try {
1714
+ holding = await withRetries(
1715
+ () => k.limiter.run(async () => {
1716
+ if (signal.aborted) throw signal.reason;
1717
+ const ctx = { ...k.env.ctxFor(key), signal };
1718
+ const drop = await _asyncNullishCoalesce(await _asyncOptionalChain([k, 'access', async _79 => _79.lineData, 'call', async _80 => _80(key), 'optionalAccess', async _81 => _81.drop]), async () => ( await adapter.readDrop(ctx, config)));
1719
+ if (signal.aborted) throw signal.reason;
1720
+ const query = _nullishCoalesce(_optionalChain([adapter, 'access', _82 => _82.holdingsQuery, 'optionalCall', _83 => _83(drop, "display")]), () => ( null));
1721
+ if (!query || !adapter.readHoldings) return null;
1722
+ const n = await adapter.readHoldings(ctx, query, wallet);
1723
+ const kind = query.kind === "balance-1155" ? "owned" : query.kind === "total-mints" ? "minted" : "owned-in-contract";
1724
+ return { kind, count: Number(n) };
1725
+ }),
1726
+ CART_TIMING.retryDelaysMs,
1727
+ () => signal.aborted,
1728
+ signal
1729
+ );
1730
+ } catch (e21) {
1731
+ holding = null;
1732
+ }
1733
+ if (live()) setRead((prev) => prev.wallet === wallet ? { wallet, byKey: { ...prev.byKey, [key]: holding } } : { wallet, byKey: { [key]: holding } });
1734
+ })
1735
+ );
1736
+ return () => {
1737
+ round.abort();
1738
+ providerSignal.removeEventListener("abort", onProviderAbort);
1739
+ };
1740
+ }, [wallet, listKey, collected.length, tick, k]);
1741
+ const byKey = _react.useMemo.call(void 0, () => wallet && read.wallet === wallet ? read.byKey : {}, [wallet, read]);
1742
+ const loading = wallet !== null && keyed.some((x) => !(x.key in byKey));
1743
+ const get = _react.useCallback.call(void 0,
1744
+ (config) => {
1745
+ try {
1746
+ return byKey[itemKey(canonicalConfig(config, k.chainId))];
1747
+ } catch (e22) {
1748
+ return void 0;
1749
+ }
1750
+ },
1751
+ [byKey, k.chainId]
1752
+ );
1753
+ const refresh = _react.useCallback.call(void 0, () => setTick((t) => t + 1), []);
1754
+ return _react.useMemo.call(void 0, () => ({ wallet, byKey, get, loading, refresh }), [wallet, byKey, get, loading, refresh]);
1755
+ }
1756
+
1757
+ // src/cart/AddToCartButton.ts
1758
+
1759
+ var LABELS = {
1760
+ add: "Add to cart",
1761
+ adding: "Adding\u2026",
1762
+ "in-cart": "In cart",
1763
+ locked: "Confirming\u2026",
1764
+ unconfirmed: "Check your wallet",
1765
+ "sold-out": "Sold out",
1766
+ upcoming: "Not started",
1767
+ unsupported: "Unavailable"
1768
+ };
1769
+ function AddToCartButton(props) {
1770
+ const { config, quantity = 1, className, children } = props;
1771
+ const cart = useCart();
1772
+ const [adding, setAdding] = _react.useState.call(void 0, false);
1773
+ const [result, setResult] = _react.useState.call(void 0, null);
1774
+ const inCart = cart.has(config);
1775
+ const same = (c) => {
1776
+ try {
1777
+ return c.creatorContract.toLowerCase() === config.creatorContract.toLowerCase() && BigInt(c.instanceId) === BigInt(config.instanceId) && _optionalChain([c, 'access', _84 => _84.extension, 'optionalAccess', _85 => _85.toLowerCase, 'call', _86 => _86()]) === _optionalChain([config, 'access', _87 => _87.extension, 'optionalAccess', _88 => _88.toLowerCase, 'call', _89 => _89()]);
1778
+ } catch (e23) {
1779
+ return false;
1780
+ }
1781
+ };
1782
+ const line = inCart ? _nullishCoalesce(cart.lines.find((l) => same(l.config)), () => ( null)) : null;
1783
+ const prior = cart.unconfirmed.some((u) => same(u.config));
1784
+ let state = "add";
1785
+ if (adding) state = "adding";
1786
+ else if (_optionalChain([line, 'optionalAccess', _90 => _90.state]) === "locked") state = "locked";
1787
+ else if (_optionalChain([line, 'optionalAccess', _91 => _91.state]) === "unsupported") state = "unsupported";
1788
+ else if (inCart) state = _optionalChain([line, 'optionalAccess', _92 => _92.state]) === "sold-out" ? "sold-out" : _optionalChain([line, 'optionalAccess', _93 => _93.state]) === "upcoming" ? "upcoming" : "in-cart";
1789
+ else if (prior) state = "unconfirmed";
1790
+ const add = _react.useCallback.call(void 0,
1791
+ (opts) => {
1792
+ if (adding) return;
1793
+ setAdding(true);
1794
+ cart.add(config, quantity, opts).then(
1795
+ (r) => {
1796
+ setResult(r);
1797
+ setAdding(false);
1798
+ },
1799
+ () => {
1800
+ setResult(null);
1801
+ setAdding(false);
1802
+ }
1803
+ );
1804
+ },
1805
+ [adding, cart, config, quantity]
1806
+ );
1807
+ const render = { state, result, line, add };
1808
+ if (typeof children === "function") return children(render);
1809
+ const disabled = state === "adding" || state === "in-cart" || state === "locked" || state === "sold-out" || state === "upcoming" || state === "unsupported";
1810
+ return _react.createElement.call(void 0,
1811
+ "button",
1812
+ {
1813
+ type: "button",
1814
+ className,
1815
+ "data-state": state,
1816
+ disabled,
1817
+ "aria-busy": state === "adding" ? true : void 0,
1818
+ onClick: () => add()
1819
+ },
1820
+ _nullishCoalesce(children, () => ( LABELS[state]))
1821
+ );
1822
+ }
1823
+
1824
+
1825
+
1826
+
1827
+
1828
+
1829
+
1830
+
1831
+ exports.AddToCartButton = AddToCartButton; exports.CartError = CartError; exports.CartProvider = CartProvider; exports.openseaChain = openseaChain; exports.useCart = useCart; exports.useCheckout = useCheckout; exports.useHoldings = useHoldings;
1832
+ //# sourceMappingURL=cart.cjs.map