@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.
@@ -0,0 +1,619 @@
1
+ import {
2
+ INELIGIBLE_REASONS,
3
+ MintError,
4
+ getChain
5
+ } from "./chunk-VHAAGH7Z.js";
6
+
7
+ // src/core/machine.ts
8
+ import { formatUnits } from "viem";
9
+ var MINT_STATES = [
10
+ "loading",
11
+ "unsupported",
12
+ "upcoming",
13
+ "ended",
14
+ "sold-out",
15
+ "disconnected",
16
+ "connecting",
17
+ "live",
18
+ "ineligible",
19
+ "insufficient-funds",
20
+ "switching-chain",
21
+ "approving",
22
+ "minting",
23
+ "confirming",
24
+ "success",
25
+ "error"
26
+ ];
27
+ var CLICKABLE_STATES = ["disconnected", "live", "success", "error"];
28
+ var IN_FLIGHT_STATES = ["switching-chain", "approving", "minting", "confirming"];
29
+ var MAX_QUANTITY = 65535;
30
+ function initialState(opts) {
31
+ return {
32
+ name: "loading",
33
+ reason: null,
34
+ message: null,
35
+ readGen: 0,
36
+ walletGen: 0,
37
+ drop: null,
38
+ account: null,
39
+ walletRead: null,
40
+ blocked: null,
41
+ quantity: toQuantity(opts?.quantity ?? 1, MAX_QUANTITY),
42
+ attempt: null,
43
+ result: null,
44
+ pendingRefresh: false,
45
+ now: 0,
46
+ crossChain: false,
47
+ routePending: false,
48
+ route: null,
49
+ short: null
50
+ };
51
+ }
52
+ var NO_ROUTE = { crossChain: false, routePending: false, route: null, short: null };
53
+ var PICK_ANOTHER = "Pick another network to pay from.";
54
+ function toQuantity(q, max) {
55
+ if (Number.isNaN(q)) return 1;
56
+ return Math.max(1, Math.min(Math.floor(q), max));
57
+ }
58
+ function isInFlight(s) {
59
+ return s.attempt !== null || IN_FLIGHT_STATES.includes(s.name);
60
+ }
61
+ function isIdle(s) {
62
+ return !isInFlight(s) && s.name !== "connecting" && s.name !== "success" && s.name !== "error";
63
+ }
64
+ function derive(s, unsupported) {
65
+ const named = (name) => ({ name, reason: null, message: null });
66
+ const { drop, walletRead } = s;
67
+ if (unsupported) return { name: "unsupported", reason: s.reason, message: s.message };
68
+ if (drop === null) return named("loading");
69
+ if (drop.endTime !== null && drop.endTime <= s.now) return named("ended");
70
+ if (drop.maxSupply !== null && drop.maxSupply > 0n && drop.totalMinted >= drop.maxSupply) return named("sold-out");
71
+ if (drop.startTime !== null && drop.startTime > s.now) return named("upcoming");
72
+ if (s.account === null) return named("disconnected");
73
+ if (walletRead === null) return named("loading");
74
+ if (s.blocked !== null) return s.blocked;
75
+ if (walletRead.ineligible !== null) {
76
+ return { name: "ineligible", reason: walletRead.ineligible.reason, message: walletRead.ineligible.message };
77
+ }
78
+ if (s.routePending) return named("loading");
79
+ if (s.route?.kind === "relay") return named("live");
80
+ if (s.short !== null) {
81
+ return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${s.short.symbol} on ${s.short.name}. ${PICK_ANOTHER}` };
82
+ }
83
+ const total = selectTotal(s);
84
+ if (total !== null) {
85
+ const { native, erc20 } = total;
86
+ if (native.value + walletRead.gasReserve > walletRead.balances.native) {
87
+ const more = s.crossChain ? ` ${PICK_ANOTHER}` : "";
88
+ return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${native.symbol} to cover the mint.${more}` };
89
+ }
90
+ if (erc20 !== null && walletRead.balances.erc20 !== null && erc20.value > walletRead.balances.erc20) {
91
+ return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${erc20.symbol} to cover the mint.` };
92
+ }
93
+ }
94
+ return named("live");
95
+ }
96
+ function settle(s, unsupported = s.name === "unsupported") {
97
+ if (!isIdle(s)) return s;
98
+ return { ...s, ...derive(s, unsupported) };
99
+ }
100
+ function clampQuantity(s) {
101
+ const quantity = toQuantity(s.quantity, selectMaxQuantity(s) ?? MAX_QUANTITY);
102
+ return quantity === s.quantity ? s : { ...s, quantity };
103
+ }
104
+ function reducer(state, event) {
105
+ const inFlight = isInFlight(state);
106
+ const failed = (reason, message) => {
107
+ if (inFlight) return { ...state, pendingRefresh: true };
108
+ if (state.name === "success" || state.name === "error") return state;
109
+ return { ...state, name: "error", reason, message };
110
+ };
111
+ switch (event.type) {
112
+ case "LOAD": {
113
+ if (event.kind === "refresh") {
114
+ const next2 = { ...state, readGen: event.gen };
115
+ return inFlight ? { ...next2, pendingRefresh: true } : settle(next2);
116
+ }
117
+ const next = { ...state, ...NO_ROUTE, readGen: event.gen, drop: null, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
118
+ return inFlight ? { ...next, pendingRefresh: true } : settle(next, false);
119
+ }
120
+ case "DROP_LOADED": {
121
+ if (event.gen !== state.readGen) return state;
122
+ let next = { ...state, drop: event.drop, now: Math.max(state.now, event.now) };
123
+ const oldKey = state.drop?.walletKey;
124
+ const newKey = event.drop.walletKey;
125
+ if (oldKey !== void 0 && newKey !== void 0 && oldKey !== newKey) {
126
+ next = { ...next, walletRead: null, walletGen: state.walletGen + 1 };
127
+ }
128
+ if (inFlight) return { ...next, pendingRefresh: true };
129
+ return isIdle(next) ? settle(clampQuantity(next), false) : clampQuantity(next);
130
+ }
131
+ case "DROP_UNSUPPORTED": {
132
+ if (event.gen !== state.readGen) return state;
133
+ if (inFlight) return { ...state, pendingRefresh: true };
134
+ if (state.name === "success" || state.name === "error") return state;
135
+ return { ...state, name: "unsupported", reason: event.reason, message: event.message };
136
+ }
137
+ case "READ_FAILED":
138
+ return event.gen !== state.readGen ? state : failed(event.reason, event.message);
139
+ case "ACCOUNT_CHANGED": {
140
+ const next = { ...state, ...NO_ROUTE, account: event.account, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
141
+ if (inFlight) return { ...next, pendingRefresh: true };
142
+ if (state.name === "connecting") return event.account === null ? next : settle({ ...next, name: "loading" }, false);
143
+ return settle(next);
144
+ }
145
+ case "WALLET_LOADED": {
146
+ if (event.gen !== state.walletGen) return state;
147
+ const route = event.routePending ? { routePending: state.route === null && state.short === null, route: state.route, short: state.short } : { routePending: false, route: null, short: null };
148
+ const next = { ...state, ...route, crossChain: event.crossChain ?? false, walletRead: event.read, blocked: null };
149
+ return inFlight ? { ...next, pendingRefresh: true } : settle(clampQuantity(next));
150
+ }
151
+ case "ROUTE_LOADED": {
152
+ if (event.gen !== state.walletGen) return state;
153
+ const next = { ...state, routePending: false, route: event.route, short: event.short };
154
+ return inFlight ? { ...next, pendingRefresh: true } : settle(next);
155
+ }
156
+ case "WALLET_FAILED":
157
+ return event.gen !== state.walletGen ? state : failed(event.reason, event.message);
158
+ case "CONNECT_STARTED":
159
+ return state.name === "disconnected" && state.attempt === null ? { ...state, name: "connecting", reason: null, message: null } : state;
160
+ case "CONNECT_DISMISSED":
161
+ return state.name === "connecting" ? settle({ ...state, name: "disconnected" }, false) : state;
162
+ case "TICK":
163
+ return settle({ ...state, now: Math.max(state.now, event.now) });
164
+ case "SET_QUANTITY": {
165
+ const next = { ...state, quantity: toQuantity(event.quantity, selectMaxQuantity(state) ?? MAX_QUANTITY) };
166
+ return inFlight ? { ...next, pendingRefresh: true } : settle(next);
167
+ }
168
+ case "ATTEMPT_STARTED":
169
+ if (state.attempt !== null) return state;
170
+ if (event.resume === true) return IN_FLIGHT_STATES.includes(state.name) ? state : { ...state, attempt: event.attempt };
171
+ return state.name === "live" ? { ...state, attempt: event.attempt } : state;
172
+ case "SWITCHING_CHAIN":
173
+ return step(state, event.attemptId, "switching-chain");
174
+ case "APPROVING":
175
+ return step(state, event.attemptId, "approving");
176
+ case "MINT_PROMPTED":
177
+ return step(state, event.attemptId, "minting");
178
+ case "TX_SUBMITTED":
179
+ return step(state, event.attemptId, "confirming");
180
+ case "ATTEMPT_SUCCEEDED":
181
+ return state.attempt?.attemptId === event.attemptId ? {
182
+ ...state,
183
+ name: "success",
184
+ reason: null,
185
+ message: null,
186
+ attempt: null,
187
+ result: event.result,
188
+ drop: null,
189
+ readGen: state.readGen + 1
190
+ } : state;
191
+ case "ATTEMPT_FAILED": {
192
+ if (state.attempt?.attemptId !== event.attemptId) return state;
193
+ const { reason, message } = event;
194
+ const done = { ...state, reason, message, attempt: null, result: null };
195
+ if (INELIGIBLE_REASONS.includes(reason)) {
196
+ return { ...done, name: "ineligible", blocked: { name: "ineligible", reason, message } };
197
+ }
198
+ if (reason === "INSUFFICIENT_FUNDS") {
199
+ return { ...done, name: "insufficient-funds", blocked: { name: "insufficient-funds", reason, message } };
200
+ }
201
+ return {
202
+ ...done,
203
+ name: "error",
204
+ drop: state.name === "confirming" ? null : state.drop,
205
+ readGen: state.name === "confirming" ? state.readGen + 1 : state.readGen
206
+ };
207
+ }
208
+ case "RESET": {
209
+ if (state.name !== "success" && state.name !== "error") return state;
210
+ const next = {
211
+ ...state,
212
+ readGen: event.gen,
213
+ ...NO_ROUTE,
214
+ walletRead: null,
215
+ blocked: null,
216
+ walletGen: state.walletGen + 1,
217
+ result: null,
218
+ pendingRefresh: false
219
+ };
220
+ return settle(clampQuantity({ ...next, name: "loading" }), false);
221
+ }
222
+ }
223
+ }
224
+ var STEP_ORDER = ["switching-chain", "approving", "minting", "confirming"];
225
+ function step(state, attemptId, name) {
226
+ if (state.attempt?.attemptId !== attemptId) return state;
227
+ if (STEP_ORDER.indexOf(name) <= STEP_ORDER.indexOf(state.name)) return state;
228
+ return { ...state, name, reason: null, message: null };
229
+ }
230
+ function selectMaxQuantity(state) {
231
+ const { drop, walletRead } = state;
232
+ if (drop === null) return null;
233
+ let max = MAX_QUANTITY;
234
+ if (drop.maxSupply !== null && drop.maxSupply > 0n) {
235
+ const remaining = drop.maxSupply - drop.totalMinted;
236
+ if (remaining < BigInt(max)) max = remaining > 0n ? Number(remaining) : 0;
237
+ }
238
+ if (walletRead !== null && walletRead.maxQuantity !== null) {
239
+ max = Math.min(max, Math.max(0, Math.floor(walletRead.maxQuantity)));
240
+ }
241
+ return max;
242
+ }
243
+ function selectTotal(state) {
244
+ const { drop } = state;
245
+ if (drop === null) return null;
246
+ const q = BigInt(state.quantity);
247
+ const price = drop.unitPrice;
248
+ const fee = drop.unitFee;
249
+ const priceTotal = drop.pricePer === "total" ? price.value : price.value * q;
250
+ if (drop.paymentToken !== null) {
251
+ return {
252
+ native: money(fee.value * q, fee),
253
+ erc20: money(priceTotal, price)
254
+ };
255
+ }
256
+ return { native: money(priceTotal + fee.value * q, price), erc20: null };
257
+ }
258
+ function money(value, like) {
259
+ return { value, decimals: like.decimals, symbol: like.symbol, formatted: formatUnits(value, like.decimals) };
260
+ }
261
+ function canAct(state) {
262
+ return state.attempt === null && CLICKABLE_STATES.includes(state.name);
263
+ }
264
+
265
+ // src/core/wallet/index.ts
266
+ import { createWalletClient, custom, getAddress, publicActions } from "viem";
267
+ var CONNECT_WIDGET_VERSION = "6.5.0";
268
+ var CONNECT_BASE_URL = `https://connect.manifoldxyz.dev/${CONNECT_WIDGET_VERSION}`;
269
+ var CONNECT_ID = "mint-button";
270
+ var ADDRESS_CHANGED = "ethereum-address-changed";
271
+ var CHAIN_CHANGED = "frontend-provider-chain-changed";
272
+ var LOADER_KEY = "__manifoldMintConnectLoader";
273
+ var DEFAULT_WALLETCONNECT_PROJECT_ID = "a03ac1695f309dc8d756e7b4006e5e7d";
274
+ var STATE_KEY = "__manifoldMintConnectStateV1";
275
+ var win = () => window;
276
+ var pageState = () => win()[STATE_KEY] ??= {};
277
+ var TOO_OLD = "A Manifold connect widget older than 6.4.0 is already on this page; update it or remove it so dropkit can load " + CONNECT_WIDGET_VERSION + ".";
278
+ var MANIFOLD_HOST = /(^|\.)(manifoldxyz\.dev|manifold\.xyz)$/;
279
+ function isManifoldConnectScript(src) {
280
+ try {
281
+ const url = new URL(src, document.baseURI);
282
+ return url.hostname === "connect.manifoldxyz.dev" || MANIFOLD_HOST.test(url.hostname) && /\/connect\.umd[^/]*\.js$/.test(url.pathname);
283
+ } catch {
284
+ return false;
285
+ }
286
+ }
287
+ function applyState(state) {
288
+ if (!state.div) return;
289
+ const network = state.networks.join(",");
290
+ if (state.div.dataset.network !== network) state.div.dataset.network = network;
291
+ const projectId = state.projectId ?? state.div.dataset.walletConnectProjectId ?? DEFAULT_WALLETCONNECT_PROJECT_ID;
292
+ if (state.div.dataset.walletConnectProjectId !== projectId) state.div.dataset.walletConnectProjectId = projectId;
293
+ }
294
+ function register(state, opts) {
295
+ for (const id2 of opts.chainIds) if (!state.networks.includes(id2)) state.networks.push(id2);
296
+ const id = opts.walletConnectProjectId;
297
+ if (id) {
298
+ if (!state.projectId) state.projectId = id;
299
+ else if (state.projectId !== id) {
300
+ console.warn(`dropkit: walletConnectProjectId "${id}" ignored; this page already uses "${state.projectId}" for the shared connect widget.`);
301
+ }
302
+ }
303
+ applyState(state);
304
+ }
305
+ function startLoad(state) {
306
+ const connect = win().ManifoldConnect;
307
+ if (connect?.capabilities?.includes("open")) return Promise.resolve(connect.ready).then(() => void 0);
308
+ const hasScript = Array.from(document.querySelectorAll("script[src]")).some((s) => isManifoldConnectScript(s.src));
309
+ if (hasScript || document.querySelector('[data-widget="m-connect"]')) {
310
+ return Promise.reject(new MintError("CONNECT_WIDGET_TOO_OLD", TOO_OLD));
311
+ }
312
+ return new Promise((resolve, reject) => {
313
+ const css = document.createElement("link");
314
+ css.rel = "stylesheet";
315
+ css.href = `${CONNECT_BASE_URL}/connect.css`;
316
+ document.head.appendChild(css);
317
+ state.nodes.push(css);
318
+ const div = document.createElement("div");
319
+ div.hidden = true;
320
+ div.style.display = "none";
321
+ div.dataset.widget = "m-connect";
322
+ div.dataset.connectId = CONNECT_ID;
323
+ div.dataset.autoReconnect = "true";
324
+ div.dataset.enableNetworkSwitching = "true";
325
+ state.div = div;
326
+ applyState(state);
327
+ document.body.appendChild(div);
328
+ state.nodes.push(div);
329
+ const script = document.createElement("script");
330
+ script.src = `${CONNECT_BASE_URL}/connect.umd.min.js`;
331
+ script.async = true;
332
+ script.addEventListener("load", () => {
333
+ const ready = win().ManifoldConnect?.ready;
334
+ if (!ready) return reject(new MintError("CONNECT_WIDGET_TOO_OLD", TOO_OLD));
335
+ Promise.resolve(ready).then(() => resolve(), reject);
336
+ });
337
+ script.addEventListener("error", () => reject(new MintError("RPC_ERROR", `Could not load the Manifold connect widget from ${CONNECT_BASE_URL}`)));
338
+ document.head.appendChild(script);
339
+ state.nodes.push(script);
340
+ });
341
+ }
342
+ function loadConnectWidget(opts) {
343
+ const w = win();
344
+ const page = pageState();
345
+ const existing = w[LOADER_KEY];
346
+ if (existing) {
347
+ if (page.load?.promise !== existing) {
348
+ const div = document.querySelector(`[data-widget="m-connect"][data-connect-id="${CONNECT_ID}"]`) ?? void 0;
349
+ page.load = {
350
+ promise: existing,
351
+ networks: div?.dataset.network?.split(",").filter(Boolean).map(Number) ?? [],
352
+ // The default id is a placeholder, not a choice: a caller's real id may still replace it.
353
+ projectId: div?.dataset.walletConnectProjectId === DEFAULT_WALLETCONNECT_PROJECT_ID ? void 0 : div?.dataset.walletConnectProjectId,
354
+ div,
355
+ nodes: []
356
+ };
357
+ }
358
+ register(page.load, opts);
359
+ return existing;
360
+ }
361
+ let resolve;
362
+ let reject;
363
+ const p = new Promise((res, rej) => {
364
+ resolve = res;
365
+ reject = rej;
366
+ });
367
+ const state = { promise: p, networks: [], nodes: [] };
368
+ page.load = state;
369
+ w[LOADER_KEY] = p;
370
+ p.catch(() => {
371
+ if (w[LOADER_KEY] === p) delete w[LOADER_KEY];
372
+ if (page.load === state) page.load = void 0;
373
+ for (const node of state.nodes) node.parentNode?.removeChild(node);
374
+ state.nodes.length = 0;
375
+ state.div = void 0;
376
+ });
377
+ register(state, opts);
378
+ try {
379
+ startLoad(state).then(resolve, reject);
380
+ } catch (error) {
381
+ reject(error);
382
+ }
383
+ return p;
384
+ }
385
+ function openConnectModal() {
386
+ const page = pageState();
387
+ if (page.pendingModal) return page.pendingModal;
388
+ const p = new Promise((resolve) => {
389
+ const onClosed = (e) => {
390
+ const detail = e.detail ?? {};
391
+ if (detail.connectId !== void 0 && detail.connectId !== CONNECT_ID) return;
392
+ window.removeEventListener("m-connect:closed", onClosed);
393
+ page.pendingModal = void 0;
394
+ resolve(detail.reason === "connected" ? "connected" : "dismissed");
395
+ };
396
+ window.addEventListener("m-connect:closed", onClosed);
397
+ });
398
+ page.pendingModal = p;
399
+ window.dispatchEvent(new CustomEvent("m-connect:open", { detail: { connectId: CONNECT_ID } }));
400
+ return p;
401
+ }
402
+ function getAccount() {
403
+ const p = win().ManifoldEthereumProvider;
404
+ const address = p?.selectedAddress?.();
405
+ let chainId = p?.chainId?.();
406
+ if (chainId === void 0 || chainId === null) {
407
+ try {
408
+ chainId = p?.browserChainId?.();
409
+ } catch {
410
+ chainId = void 0;
411
+ }
412
+ }
413
+ if (!address || chainId === void 0 || chainId === null) return null;
414
+ return { address: getAddress(address), chainId: Number(chainId) };
415
+ }
416
+ function subscribeAccount(cb) {
417
+ const handler = () => cb(getAccount());
418
+ window.addEventListener(ADDRESS_CHANGED, handler);
419
+ window.addEventListener(CHAIN_CHANGED, handler);
420
+ return () => {
421
+ window.removeEventListener(ADDRESS_CHANGED, handler);
422
+ window.removeEventListener(CHAIN_CHANGED, handler);
423
+ };
424
+ }
425
+ function eip1193() {
426
+ const p = win().ManifoldEthereumProvider;
427
+ const provider = (p?.provider?.(true) ?? p?.browserProvider?.())?.provider;
428
+ if (!provider) throw new MintError("CHAIN_SWITCH_FAILED", "No wallet is connected.");
429
+ return provider;
430
+ }
431
+ function getWalletClient(chainId) {
432
+ const provider = eip1193();
433
+ const request = ((args) => provider.request(args));
434
+ return createWalletClient({ chain: getChain(chainId), transport: custom({ request }) });
435
+ }
436
+ async function waitForDeposit(chainId, hash, timeoutMs = 12e4) {
437
+ try {
438
+ const receipt = await getWalletClient(chainId).extend(publicActions).waitForTransactionReceipt({ hash, confirmations: 1, timeout: timeoutMs });
439
+ return receipt.status === "reverted" ? "reverted" : "mined";
440
+ } catch {
441
+ return "unknown";
442
+ }
443
+ }
444
+ var WALLET_READ_TIMEOUT = { ms: 1e4 };
445
+ async function getConnectedBalance(address, chainId) {
446
+ const provider = eip1193();
447
+ let timer;
448
+ const timeout = new Promise((_, reject) => {
449
+ timer = setTimeout(() => reject(new MintError("RPC_ERROR", "The wallet did not report a balance in time.")), WALLET_READ_TIMEOUT.ms);
450
+ });
451
+ try {
452
+ return await Promise.race([
453
+ (async () => {
454
+ const current = Number(await provider.request({ method: "eth_chainId" }));
455
+ if (current !== chainId) {
456
+ throw new MintError("CHAIN_SWITCH_FAILED", `The wallet is on chain ${String(current)}, not ${String(chainId)}.`);
457
+ }
458
+ const hex = await provider.request({ method: "eth_getBalance", params: [address, "latest"] });
459
+ return BigInt(hex);
460
+ })(),
461
+ timeout
462
+ ]);
463
+ } finally {
464
+ clearTimeout(timer);
465
+ }
466
+ }
467
+ var code = (e) => e?.code;
468
+ var hexChainId = (chainId) => `0x${chainId.toString(16)}`;
469
+ var sendSwitchRequest = (provider, chainId) => provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId(chainId) }] });
470
+ var sendAddRequest = (provider, chainId) => {
471
+ const chain = getChain(chainId);
472
+ return provider.request({
473
+ method: "wallet_addEthereumChain",
474
+ params: [
475
+ {
476
+ chainId: hexChainId(chainId),
477
+ chainName: chain.name,
478
+ nativeCurrency: chain.nativeCurrency,
479
+ rpcUrls: [...chain.rpcUrls.default.http],
480
+ ...chain.blockExplorers ? { blockExplorerUrls: [chain.blockExplorers.default.url] } : {}
481
+ }
482
+ ]
483
+ });
484
+ };
485
+ function chainRequestError(e, chainId) {
486
+ if (code(e) === 4001) return new MintError("USER_REJECTED", "You rejected the network switch.", { cause: e });
487
+ return new MintError("CHAIN_SWITCH_FAILED", `Could not switch the wallet to chain ${String(chainId)}.`, { cause: e });
488
+ }
489
+ async function switchToChain(chainId) {
490
+ getChain(chainId);
491
+ const provider = eip1193();
492
+ try {
493
+ try {
494
+ await sendSwitchRequest(provider, chainId);
495
+ } catch (e) {
496
+ if (code(e) !== 4902) throw e;
497
+ await sendAddRequest(provider, chainId);
498
+ await sendSwitchRequest(provider, chainId);
499
+ }
500
+ } catch (e) {
501
+ throw chainRequestError(e, chainId);
502
+ }
503
+ }
504
+ async function requestSwitch(chainId) {
505
+ getChain(chainId);
506
+ const provider = eip1193();
507
+ try {
508
+ await sendSwitchRequest(provider, chainId);
509
+ return { needsAdd: false };
510
+ } catch (e) {
511
+ if (code(e) === 4902) return { needsAdd: true };
512
+ throw chainRequestError(e, chainId);
513
+ }
514
+ }
515
+ async function requestAddChain(chainId) {
516
+ getChain(chainId);
517
+ const provider = eip1193();
518
+ try {
519
+ await sendAddRequest(provider, chainId);
520
+ } catch (e) {
521
+ throw chainRequestError(e, chainId);
522
+ }
523
+ }
524
+ function getWalletProvider() {
525
+ return eip1193();
526
+ }
527
+
528
+ // src/core/adapters/receipt.ts
529
+ import { decodeAbiParameters, getAddress as getAddress2, zeroAddress } from "viem";
530
+ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
531
+ var TRANSFER_SINGLE_TOPIC = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62";
532
+ var TRANSFER_BATCH_TOPIC = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb";
533
+ var topicAddress = (t) => t && t.length === 66 ? getAddress2(`0x${t.slice(26)}`) : null;
534
+ function parseMintReceipt(logs, { tokenContract, recipient }) {
535
+ const contract = getAddress2(tokenContract);
536
+ const to = getAddress2(recipient);
537
+ const out = /* @__PURE__ */ new Map();
538
+ const add = (id, amount) => out.set(id, (out.get(id) ?? 0n) + amount);
539
+ for (const log of logs) {
540
+ if (getAddress2(log.address) !== contract) continue;
541
+ const [t0, t1, t2, t3] = log.topics;
542
+ if (t0 === TRANSFER_TOPIC && log.topics.length === 4) {
543
+ if (topicAddress(t1) !== zeroAddress || topicAddress(t2) !== to || !t3) continue;
544
+ add(BigInt(t3), 1n);
545
+ } else if (t0 === TRANSFER_SINGLE_TOPIC && log.topics.length === 4) {
546
+ if (topicAddress(t2) !== zeroAddress || topicAddress(t3) !== to) continue;
547
+ const [id, value] = decodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], log.data);
548
+ add(id, value);
549
+ } else if (t0 === TRANSFER_BATCH_TOPIC && log.topics.length === 4) {
550
+ if (topicAddress(t2) !== zeroAddress || topicAddress(t3) !== to) continue;
551
+ const [ids, values] = decodeAbiParameters([{ type: "uint256[]" }, { type: "uint256[]" }], log.data);
552
+ if (ids.length !== values.length) continue;
553
+ ids.forEach((id, i) => add(id, values[i]));
554
+ }
555
+ }
556
+ return [...out].map(([tokenId, amount]) => ({ contract, tokenId, amount }));
557
+ }
558
+
559
+ // src/core/mint-record.ts
560
+ var APPS_API = "https://apps.api.manifoldxyz.dev";
561
+ var isRecord = (v) => typeof v === "object" && v !== null;
562
+ var appIds = /* @__PURE__ */ new Map();
563
+ async function loadAppId(fetchFn, instanceId) {
564
+ const res = await fetchFn(`${APPS_API}/public/instance/data?id=${encodeURIComponent(instanceId)}`, { keepalive: true });
565
+ if (!res.ok) throw new Error(`instance data HTTP ${res.status}`);
566
+ const body = await res.json();
567
+ const appId = isRecord(body) ? body.appId : void 0;
568
+ if (typeof appId !== "number" && typeof appId !== "string") throw new Error("instance data has no appId");
569
+ return String(appId);
570
+ }
571
+ function appIdFor(fetchFn, instanceId) {
572
+ const hit = appIds.get(instanceId);
573
+ if (hit) return hit;
574
+ const p = loadAppId(fetchFn, instanceId);
575
+ appIds.set(instanceId, p);
576
+ p.catch(() => {
577
+ if (appIds.get(instanceId) === p) appIds.delete(instanceId);
578
+ });
579
+ return p;
580
+ }
581
+ function recordClaimMint(args) {
582
+ const fetchFn = args.fetch ?? ((input, init) => globalThis.fetch(input, init));
583
+ const send = async () => {
584
+ let appId = "";
585
+ try {
586
+ appId = await appIdFor(fetchFn, args.instanceId);
587
+ } catch {
588
+ }
589
+ const query = new URLSearchParams({ appId, instanceId: args.instanceId, hash: args.hash });
590
+ await fetchFn(`${APPS_API}/public/instance/tx?${query.toString()}`, { method: "POST", keepalive: true });
591
+ };
592
+ return send().catch(() => void 0);
593
+ }
594
+
595
+ export {
596
+ loadConnectWidget,
597
+ openConnectModal,
598
+ getAccount,
599
+ subscribeAccount,
600
+ getWalletClient,
601
+ waitForDeposit,
602
+ getConnectedBalance,
603
+ switchToChain,
604
+ requestSwitch,
605
+ requestAddChain,
606
+ getWalletProvider,
607
+ MINT_STATES,
608
+ CLICKABLE_STATES,
609
+ IN_FLIGHT_STATES,
610
+ MAX_QUANTITY,
611
+ initialState,
612
+ reducer,
613
+ selectMaxQuantity,
614
+ selectTotal,
615
+ canAct,
616
+ parseMintReceipt,
617
+ recordClaimMint
618
+ };
619
+ //# sourceMappingURL=chunk-W7OBZPCY.js.map