@manifoldxyz/dropkit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -5
- package/dist/index.cjs +796 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -3
- package/dist/index.d.ts +102 -3
- package/dist/index.js +764 -72
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -25,16 +25,18 @@ __export(index_exports, {
|
|
|
25
25
|
MINT_STATES: () => MINT_STATES,
|
|
26
26
|
MintButton: () => MintButton,
|
|
27
27
|
MintError: () => MintError,
|
|
28
|
+
PayWithPicker: () => PayWithPicker,
|
|
28
29
|
REASON_CODES: () => REASON_CODES,
|
|
29
30
|
VERSION: () => VERSION,
|
|
30
31
|
isMintError: () => isMintError,
|
|
31
|
-
useMint: () => useMint
|
|
32
|
+
useMint: () => useMint,
|
|
33
|
+
usePayOptions: () => usePayOptions
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(index_exports);
|
|
34
36
|
|
|
35
37
|
// src/react/useMint.ts
|
|
36
38
|
var import_react = require("react");
|
|
37
|
-
var
|
|
39
|
+
var import_viem9 = require("viem");
|
|
38
40
|
|
|
39
41
|
// src/core/rpc/index.ts
|
|
40
42
|
var import_viem = require("viem");
|
|
@@ -56,7 +58,12 @@ var ERROR_REASONS = [
|
|
|
56
58
|
"RATE_LIMITED",
|
|
57
59
|
"RPC_ERROR",
|
|
58
60
|
"CHAIN_SWITCH_FAILED",
|
|
59
|
-
"ALLOWLIST_UNAVAILABLE"
|
|
61
|
+
"ALLOWLIST_UNAVAILABLE",
|
|
62
|
+
// Relay cross-chain mint (spec relay-cross-chain-mint #execute-click).
|
|
63
|
+
"BRIDGE_UNAVAILABLE",
|
|
64
|
+
"BRIDGE_REFUNDED",
|
|
65
|
+
"BRIDGE_FAILED",
|
|
66
|
+
"BRIDGE_TIMEOUT"
|
|
60
67
|
];
|
|
61
68
|
var FUNDS_REASONS = ["INSUFFICIENT_FUNDS"];
|
|
62
69
|
var REASON_CODES = [
|
|
@@ -69,11 +76,14 @@ var MintError = class extends Error {
|
|
|
69
76
|
reason;
|
|
70
77
|
/** INVALID_CONFIG only: dotted path of the bad field, e.g. "mint.args[1]" or "price". */
|
|
71
78
|
field;
|
|
79
|
+
/** 'relay': the message is Relay's decoded destination revert, shown as-is. */
|
|
80
|
+
domain;
|
|
72
81
|
constructor(reason, message, opts) {
|
|
73
82
|
super(message, opts?.cause === void 0 ? void 0 : { cause: opts.cause });
|
|
74
83
|
this.name = "MintError";
|
|
75
84
|
this.reason = reason;
|
|
76
85
|
if (opts?.field !== void 0) this.field = opts.field;
|
|
86
|
+
if (opts?.domain !== void 0) this.domain = opts.domain;
|
|
77
87
|
}
|
|
78
88
|
};
|
|
79
89
|
function isMintError(e) {
|
|
@@ -95,8 +105,22 @@ var CHAINS = {
|
|
|
95
105
|
[import_chains.base.id]: import_chains.base,
|
|
96
106
|
[import_chains.optimism.id]: import_chains.optimism,
|
|
97
107
|
[import_chains.shape.id]: import_chains.shape,
|
|
98
|
-
[import_chains.apeChain.id]: import_chains.apeChain
|
|
108
|
+
[import_chains.apeChain.id]: import_chains.apeChain,
|
|
109
|
+
// Relay pay chain for Sepolia drops only: never a drop chain, no Manifold bridge.
|
|
110
|
+
[import_chains.baseSepolia.id]: import_chains.baseSepolia
|
|
111
|
+
};
|
|
112
|
+
var CHAIN_NAMES = {
|
|
113
|
+
1: "Ethereum",
|
|
114
|
+
8453: "Base",
|
|
115
|
+
10: "Optimism",
|
|
116
|
+
360: "Shape",
|
|
117
|
+
33139: "ApeChain",
|
|
118
|
+
11155111: "Sepolia",
|
|
119
|
+
84532: "Base Sepolia"
|
|
99
120
|
};
|
|
121
|
+
function chainName(chainId) {
|
|
122
|
+
return CHAIN_NAMES[chainId] ?? CHAINS[chainId]?.name ?? `chain ${String(chainId)}`;
|
|
123
|
+
}
|
|
100
124
|
var TRANSPORT_TIMEOUT_MS = 8e3;
|
|
101
125
|
function getChain(chainId) {
|
|
102
126
|
const chain = CHAINS[chainId];
|
|
@@ -334,6 +358,37 @@ function getWalletClient(chainId) {
|
|
|
334
358
|
const request = ((args) => provider.request(args));
|
|
335
359
|
return (0, import_viem2.createWalletClient)({ chain: getChain(chainId), transport: (0, import_viem2.custom)({ request }) });
|
|
336
360
|
}
|
|
361
|
+
async function waitForDeposit(chainId, hash, timeoutMs = 12e4) {
|
|
362
|
+
try {
|
|
363
|
+
const receipt = await getWalletClient(chainId).extend(import_viem2.publicActions).waitForTransactionReceipt({ hash, confirmations: 1, timeout: timeoutMs });
|
|
364
|
+
return receipt.status === "reverted" ? "reverted" : "mined";
|
|
365
|
+
} catch {
|
|
366
|
+
return "unknown";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
var WALLET_READ_TIMEOUT = { ms: 1e4 };
|
|
370
|
+
async function getConnectedBalance(address, chainId) {
|
|
371
|
+
const provider = eip1193();
|
|
372
|
+
let timer;
|
|
373
|
+
const timeout = new Promise((_, reject) => {
|
|
374
|
+
timer = setTimeout(() => reject(new MintError("RPC_ERROR", "The wallet did not report a balance in time.")), WALLET_READ_TIMEOUT.ms);
|
|
375
|
+
});
|
|
376
|
+
try {
|
|
377
|
+
return await Promise.race([
|
|
378
|
+
(async () => {
|
|
379
|
+
const current = Number(await provider.request({ method: "eth_chainId" }));
|
|
380
|
+
if (current !== chainId) {
|
|
381
|
+
throw new MintError("CHAIN_SWITCH_FAILED", `The wallet is on chain ${String(current)}, not ${String(chainId)}.`);
|
|
382
|
+
}
|
|
383
|
+
const hex = await provider.request({ method: "eth_getBalance", params: [address, "latest"] });
|
|
384
|
+
return BigInt(hex);
|
|
385
|
+
})(),
|
|
386
|
+
timeout
|
|
387
|
+
]);
|
|
388
|
+
} finally {
|
|
389
|
+
clearTimeout(timer);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
337
392
|
var code = (e) => e?.code;
|
|
338
393
|
async function switchToChain(chainId) {
|
|
339
394
|
const chain = getChain(chainId);
|
|
@@ -365,31 +420,258 @@ async function switchToChain(chainId) {
|
|
|
365
420
|
}
|
|
366
421
|
}
|
|
367
422
|
|
|
423
|
+
// src/core/relay/client.ts
|
|
424
|
+
var import_viem3 = require("viem");
|
|
425
|
+
var RELAY_API = { mainnet: "https://api.relay.link", testnet: "https://api.testnets.relay.link" };
|
|
426
|
+
var MAINNET_PAY_CHAINS = [1, 8453, 10, 360, 33139];
|
|
427
|
+
var MAINNET_DROP_CHAINS = [1, 8453, 10, 360];
|
|
428
|
+
var SEPOLIA = 11155111;
|
|
429
|
+
var BASE_SEPOLIA = 84532;
|
|
430
|
+
var isRelayTestnet = (dropChainId) => dropChainId === SEPOLIA;
|
|
431
|
+
function payChainsFor(dropChainId) {
|
|
432
|
+
if (MAINNET_DROP_CHAINS.includes(dropChainId)) return [...MAINNET_PAY_CHAINS];
|
|
433
|
+
if (dropChainId === SEPOLIA) return [SEPOLIA, BASE_SEPOLIA];
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
var CACHE_MS = 3e4;
|
|
437
|
+
var cache = /* @__PURE__ */ new Map();
|
|
438
|
+
var cacheKey = (a) => [a.testnet, a.originChainId, a.destinationChainId, a.tx.to.toLowerCase(), a.tx.value, (0, import_viem3.keccak256)(a.tx.data), a.payer.toLowerCase(), a.recipient.toLowerCase()].join(":");
|
|
439
|
+
var big = (v) => {
|
|
440
|
+
if (typeof v === "string" && /^\d+$/.test(v)) return BigInt(v);
|
|
441
|
+
if (typeof v === "number" && Number.isSafeInteger(v) && v >= 0) return BigInt(v);
|
|
442
|
+
return 0n;
|
|
443
|
+
};
|
|
444
|
+
var obj = (v) => v !== null && typeof v === "object" ? v : {};
|
|
445
|
+
function moneyOf(amount, currency) {
|
|
446
|
+
const c = obj(currency);
|
|
447
|
+
const decimals = typeof c.decimals === "number" ? c.decimals : 18;
|
|
448
|
+
const symbol = typeof c.symbol === "string" ? c.symbol : "ETH";
|
|
449
|
+
const value = big(amount);
|
|
450
|
+
return { value, decimals, symbol, formatted: (0, import_viem3.formatUnits)(value, decimals) };
|
|
451
|
+
}
|
|
452
|
+
function revertReason(errorData) {
|
|
453
|
+
if (typeof errorData !== "string") return "";
|
|
454
|
+
const text2 = errorData.replace(/^execution reverted:?\s*/i, "").trim();
|
|
455
|
+
return text2 === "execution reverted" ? "" : text2;
|
|
456
|
+
}
|
|
457
|
+
function parseQuote(body, originChainId) {
|
|
458
|
+
const b = obj(body);
|
|
459
|
+
const steps = Array.isArray(b.steps) ? b.steps.map(obj) : [];
|
|
460
|
+
const unknownStep = () => new MintError("BRIDGE_UNAVAILABLE", "Relay returned a route this button cannot execute.");
|
|
461
|
+
if (steps.length !== 1 || steps[0].kind !== "transaction") throw unknownStep();
|
|
462
|
+
const step2 = steps[0];
|
|
463
|
+
const rawItems = Array.isArray(step2.items) ? step2.items.map(obj) : [];
|
|
464
|
+
if (rawItems.length !== 1) throw unknownStep();
|
|
465
|
+
const items = rawItems.map((item) => {
|
|
466
|
+
const d = obj(item.data);
|
|
467
|
+
const chainId = Number(d.chainId);
|
|
468
|
+
if (chainId !== originChainId || typeof d.to !== "string" || !(0, import_viem3.isHex)(d.data)) throw unknownStep();
|
|
469
|
+
return { chainId, to: (0, import_viem3.getAddress)(d.to), data: d.data, value: big(d.value), gas: d.gas === void 0 ? null : big(d.gas) };
|
|
470
|
+
});
|
|
471
|
+
const requestId = (0, import_viem3.isHex)(step2.requestId) ? step2.requestId : (0, import_viem3.isHex)(b.requestId) ? b.requestId : null;
|
|
472
|
+
if (!requestId) throw unknownStep();
|
|
473
|
+
const details = obj(b.details);
|
|
474
|
+
const currencyIn = obj(details.currencyIn);
|
|
475
|
+
const fees = obj(b.fees);
|
|
476
|
+
const amountIn = moneyOf(currencyIn.amount, currencyIn.currency);
|
|
477
|
+
if (obj(currencyIn.currency).address !== void 0 && obj(currencyIn.currency).address !== import_viem3.zeroAddress) throw unknownStep();
|
|
478
|
+
if (items[0].value !== amountIn.value) throw unknownStep();
|
|
479
|
+
return {
|
|
480
|
+
requestId,
|
|
481
|
+
originChainId,
|
|
482
|
+
items,
|
|
483
|
+
amountIn,
|
|
484
|
+
relayFee: moneyOf(obj(fees.relayer).amount, obj(fees.relayer).currency ?? currencyIn.currency),
|
|
485
|
+
originGas: moneyOf(obj(fees.gas).amount, obj(fees.gas).currency ?? currencyIn.currency),
|
|
486
|
+
quotedAt: Math.floor(Date.now() / 1e3)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
var RELAY_TIMEOUTS = { quoteMs: 1e4, statusMs: 1e4 };
|
|
490
|
+
function raceSignal(p, signal) {
|
|
491
|
+
if (!signal) return p;
|
|
492
|
+
if (signal.aborted) return Promise.reject(new DOMException("Relay quote cancelled", "AbortError"));
|
|
493
|
+
return new Promise((resolve, reject) => {
|
|
494
|
+
const onAbort = () => reject(new DOMException("Relay quote cancelled", "AbortError"));
|
|
495
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
496
|
+
p.then(
|
|
497
|
+
(v) => {
|
|
498
|
+
signal.removeEventListener("abort", onAbort);
|
|
499
|
+
resolve(v);
|
|
500
|
+
},
|
|
501
|
+
(e) => {
|
|
502
|
+
signal.removeEventListener("abort", onAbort);
|
|
503
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
async function timedFetch(f, url, init, ms) {
|
|
509
|
+
const controller = new AbortController();
|
|
510
|
+
let t;
|
|
511
|
+
const timeout = new Promise((_, reject) => {
|
|
512
|
+
t = setTimeout(() => {
|
|
513
|
+
controller.abort();
|
|
514
|
+
reject(new DOMException("Relay request timed out", "TimeoutError"));
|
|
515
|
+
}, ms);
|
|
516
|
+
});
|
|
517
|
+
try {
|
|
518
|
+
return await Promise.race([
|
|
519
|
+
(async () => {
|
|
520
|
+
const res = await f(url, { ...init, signal: controller.signal });
|
|
521
|
+
const json = await res.json().catch(() => null);
|
|
522
|
+
return { ok: res.ok, status: res.status, json };
|
|
523
|
+
})(),
|
|
524
|
+
timeout
|
|
525
|
+
]);
|
|
526
|
+
} finally {
|
|
527
|
+
clearTimeout(t);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function requestQuote(a) {
|
|
531
|
+
const f = a.fetch ?? globalThis.fetch;
|
|
532
|
+
const body = {
|
|
533
|
+
user: a.payer,
|
|
534
|
+
recipient: a.recipient,
|
|
535
|
+
refundTo: a.payer,
|
|
536
|
+
// R11: without refundTo, Relay disables automatic refunds.
|
|
537
|
+
originChainId: a.originChainId,
|
|
538
|
+
destinationChainId: a.destinationChainId,
|
|
539
|
+
originCurrency: import_viem3.zeroAddress,
|
|
540
|
+
destinationCurrency: import_viem3.zeroAddress,
|
|
541
|
+
tradeType: "EXACT_OUTPUT",
|
|
542
|
+
amount: a.tx.value.toString(),
|
|
543
|
+
txs: [{ to: a.tx.to, value: a.tx.value.toString(), data: a.tx.data }]
|
|
544
|
+
};
|
|
545
|
+
let res;
|
|
546
|
+
try {
|
|
547
|
+
res = await timedFetch(f, `${a.testnet ? RELAY_API.testnet : RELAY_API.mainnet}/quote`, {
|
|
548
|
+
method: "POST",
|
|
549
|
+
headers: { "Content-Type": "application/json" },
|
|
550
|
+
body: JSON.stringify(body)
|
|
551
|
+
}, RELAY_TIMEOUTS.quoteMs);
|
|
552
|
+
} catch (e) {
|
|
553
|
+
throw new MintError("BRIDGE_UNAVAILABLE", "Could not reach Relay for a cross-chain quote.", { cause: e });
|
|
554
|
+
}
|
|
555
|
+
const json = res.json;
|
|
556
|
+
if (res.ok) return parseQuote(json, a.originChainId);
|
|
557
|
+
const j = obj(json);
|
|
558
|
+
if (j.errorCode === "DESTINATION_TX_FAILED") {
|
|
559
|
+
const reason = revertReason(j.errorData);
|
|
560
|
+
throw new MintError("SIMULATION_REVERTED", reason || "The mint would fail on the drop network.", { cause: json, domain: "relay" });
|
|
561
|
+
}
|
|
562
|
+
if (res.status === 401 || j.errorCode === "UNAUTHORIZED_QUOTE") {
|
|
563
|
+
console.warn("dropkit: Relay rejected the quote (401 UNAUTHORIZED_QUOTE). Relay gates quotes that carry a referrer or need an API key.");
|
|
564
|
+
throw new MintError("BRIDGE_UNAVAILABLE", "Relay refused the cross-chain quote.", { cause: json });
|
|
565
|
+
}
|
|
566
|
+
if (res.status === 429) throw new MintError("RATE_LIMITED", "Relay is rate limiting quotes. Try again in a moment.", { cause: json });
|
|
567
|
+
throw new MintError("BRIDGE_UNAVAILABLE", `Relay could not quote this mint (${String(res.status)}).`, { cause: json });
|
|
568
|
+
}
|
|
569
|
+
function getQuote(a) {
|
|
570
|
+
const k = cacheKey(a);
|
|
571
|
+
const now = Date.now();
|
|
572
|
+
const hit = cache.get(k);
|
|
573
|
+
if (!a.fresh && hit && now - hit.at < CACHE_MS) return raceSignal(hit.quote, a.signal);
|
|
574
|
+
const quote = requestQuote(a);
|
|
575
|
+
cache.set(k, { at: now, quote });
|
|
576
|
+
quote.catch(() => {
|
|
577
|
+
if (cache.get(k)?.quote === quote) cache.delete(k);
|
|
578
|
+
});
|
|
579
|
+
return raceSignal(quote, a.signal);
|
|
580
|
+
}
|
|
581
|
+
var realReason = (v) => typeof v === "string" && v !== "" && v !== "N/A" ? v : null;
|
|
582
|
+
async function getStatus(a) {
|
|
583
|
+
const f = a.fetch ?? globalThis.fetch;
|
|
584
|
+
const res = await raceSignal(timedFetch(f, `${a.testnet ? RELAY_API.testnet : RELAY_API.mainnet}/intents/status/v3?requestId=${a.requestId}`, {}, RELAY_TIMEOUTS.statusMs), a.signal);
|
|
585
|
+
if (!res.ok) throw new Error(`Relay status ${String(res.status)}`);
|
|
586
|
+
const j = obj(res.json);
|
|
587
|
+
return {
|
|
588
|
+
status: typeof j.status === "string" ? j.status : "unknown",
|
|
589
|
+
txHashes: Array.isArray(j.txHashes) ? j.txHashes.filter((h) => (0, import_viem3.isHex)(h)) : [],
|
|
590
|
+
failReason: realReason(j.failReason),
|
|
591
|
+
refundFailReason: realReason(j.refundFailReason)
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
var KEEP_POLLING = ["waiting", "depositing", "pending", "submitted", "delayed"];
|
|
595
|
+
var TERMINAL = ["success", "refund", "failure"];
|
|
596
|
+
|
|
597
|
+
// src/core/relay/status.ts
|
|
598
|
+
var POLL_FAST_MS = 2e3;
|
|
599
|
+
var POLL_SLOW_MS = 5e3;
|
|
600
|
+
var POLL_FAST_FOR_MS = 6e4;
|
|
601
|
+
var POLL_CAP_MS = 10 * 6e4;
|
|
602
|
+
var defaultSleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
603
|
+
const t = setTimeout(resolve, ms);
|
|
604
|
+
signal?.addEventListener("abort", () => {
|
|
605
|
+
clearTimeout(t);
|
|
606
|
+
reject(new DOMException("Relay polling cancelled", "AbortError"));
|
|
607
|
+
}, { once: true });
|
|
608
|
+
});
|
|
609
|
+
async function pollUntilTerminal(a) {
|
|
610
|
+
const sleep = a.sleep ?? defaultSleep;
|
|
611
|
+
const now = a.now ?? Date.now;
|
|
612
|
+
const start = now();
|
|
613
|
+
let warned = false;
|
|
614
|
+
for (; ; ) {
|
|
615
|
+
a.assertActive?.();
|
|
616
|
+
try {
|
|
617
|
+
const status = await getStatus({ testnet: a.testnet, requestId: a.requestId, ...a.signal ? { signal: a.signal } : {}, ...a.fetch ? { fetch: a.fetch } : {} });
|
|
618
|
+
if (TERMINAL.includes(status.status)) return { kind: "terminal", status };
|
|
619
|
+
if (!KEEP_POLLING.includes(status.status) && !warned) {
|
|
620
|
+
warned = true;
|
|
621
|
+
console.warn(`dropkit: Relay status "${status.status}" for ${a.requestId}; still waiting.`);
|
|
622
|
+
}
|
|
623
|
+
} catch (e) {
|
|
624
|
+
if (e?.name === "AbortError") throw e;
|
|
625
|
+
}
|
|
626
|
+
a.assertActive?.();
|
|
627
|
+
const elapsed = now() - start;
|
|
628
|
+
if (elapsed >= POLL_CAP_MS) return { kind: "timeout" };
|
|
629
|
+
await sleep(elapsed < POLL_FAST_FOR_MS ? POLL_FAST_MS : POLL_SLOW_MS, a.signal);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// src/core/relay/outstanding.ts
|
|
634
|
+
var memory = /* @__PURE__ */ new Map();
|
|
635
|
+
var claimed = /* @__PURE__ */ new Set();
|
|
636
|
+
function outstandingKey(config, payer) {
|
|
637
|
+
const target = config.type === "manifold-claim" ? `${config.creatorContract.toLowerCase()}:${config.instanceId}` : config.address.toLowerCase();
|
|
638
|
+
return `${String(config.chainId)}:${target}:${payer.toLowerCase()}`;
|
|
639
|
+
}
|
|
640
|
+
var getOutstanding = (key) => memory.get(key) ?? null;
|
|
641
|
+
var setOutstanding = (key, out) => void memory.set(key, out);
|
|
642
|
+
var clearOutstanding = (key) => void memory.delete(key);
|
|
643
|
+
function claimDeposit(key) {
|
|
644
|
+
if (claimed.has(key) || memory.has(key)) return false;
|
|
645
|
+
claimed.add(key);
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
var releaseDeposit = (key) => void claimed.delete(key);
|
|
649
|
+
|
|
368
650
|
// src/core/adapters/manifold-claim/index.ts
|
|
369
|
-
var
|
|
651
|
+
var import_viem5 = require("viem");
|
|
370
652
|
|
|
371
653
|
// src/core/adapters/manifold-claim/abis.ts
|
|
372
|
-
var
|
|
373
|
-
var CLAIM_721_V5_ABI = (0,
|
|
654
|
+
var import_viem4 = require("viem");
|
|
655
|
+
var CLAIM_721_V5_ABI = (0, import_viem4.parseAbi)([
|
|
374
656
|
"function getClaim(address creatorContractAddress, uint256 instanceId) view returns ((uint32 total, uint32 totalMax, uint32 walletMax, uint48 startDate, uint48 endDate, uint8 storageProtocol, uint8 contractVersion, bool identical, bytes32 merkleRoot, string location, uint256 cost, address paymentReceiver, address erc20) claim)"
|
|
375
657
|
]);
|
|
376
|
-
var CLAIM_1155_V5_ABI = (0,
|
|
658
|
+
var CLAIM_1155_V5_ABI = (0, import_viem4.parseAbi)([
|
|
377
659
|
"function getClaim(address creatorContractAddress, uint256 instanceId) view returns ((uint32 total, uint32 totalMax, uint32 walletMax, uint48 startDate, uint48 endDate, uint8 storageProtocol, bytes32 merkleRoot, string location, uint256 tokenId, uint256 cost, address paymentReceiver, address erc20) claim)"
|
|
378
660
|
]);
|
|
379
|
-
var CLAIM_721_V7_ABI = (0,
|
|
661
|
+
var CLAIM_721_V7_ABI = (0, import_viem4.parseAbi)([
|
|
380
662
|
"function getClaim(address creatorContractAddress, uint256 instanceId) view returns ((uint32 total, uint32 totalMax, uint32 walletMax, uint48 startDate, uint48 endDate, uint8 storageProtocol, uint8 contractVersion, bool identical, bytes32 merkleRoot, string location, uint256 cost, address paymentReceiver, address erc20, address signingAddress) claim)"
|
|
381
663
|
]);
|
|
382
|
-
var CLAIM_1155_V7_ABI = (0,
|
|
664
|
+
var CLAIM_1155_V7_ABI = (0, import_viem4.parseAbi)([
|
|
383
665
|
"function getClaim(address creatorContractAddress, uint256 instanceId) view returns ((uint32 total, uint32 totalMax, uint32 walletMax, uint48 startDate, uint48 endDate, uint8 storageProtocol, bytes32 merkleRoot, string location, uint256 tokenId, uint256 cost, address paymentReceiver, address erc20, address signingAddress) claim)"
|
|
384
666
|
]);
|
|
385
|
-
var EXTENSION_COMMON_ABI = (0,
|
|
667
|
+
var EXTENSION_COMMON_ABI = (0, import_viem4.parseAbi)([
|
|
386
668
|
"function MINT_FEE() view returns (uint256)",
|
|
387
669
|
"function MINT_FEE_MERKLE() view returns (uint256)",
|
|
388
670
|
"function getTotalMints(address minter, address creatorContractAddress, uint256 instanceId) view returns (uint32)",
|
|
389
671
|
"function checkMintIndices(address creatorContractAddress, uint256 instanceId, uint32[] mintIndices) view returns (bool[] minted)",
|
|
390
672
|
"function mintProxy(address creatorContractAddress, uint256 instanceId, uint16 mintCount, uint32[] mintIndices, bytes32[][] merkleProofs, address mintFor) payable"
|
|
391
673
|
]);
|
|
392
|
-
var EXTENSION_ERRORS_ABI = (0,
|
|
674
|
+
var EXTENSION_ERRORS_ABI = (0, import_viem4.parseAbi)([
|
|
393
675
|
"error Error(string message)",
|
|
394
676
|
"error Panic(uint256 code)",
|
|
395
677
|
"error ClaimInactive()",
|
|
@@ -406,8 +688,8 @@ var EXTENSION_ERRORS_ABI = (0, import_viem3.parseAbi)([
|
|
|
406
688
|
"error TokenDNE()",
|
|
407
689
|
"error TooManyRequested()"
|
|
408
690
|
]);
|
|
409
|
-
var CREATOR_ABI = (0,
|
|
410
|
-
var ERC20_ABI = (0,
|
|
691
|
+
var CREATOR_ABI = (0, import_viem4.parseAbi)(["function getExtensions() view returns (address[])"]);
|
|
692
|
+
var ERC20_ABI = (0, import_viem4.parseAbi)([
|
|
411
693
|
"function symbol() view returns (string)",
|
|
412
694
|
"function decimals() view returns (uint8)",
|
|
413
695
|
"function balanceOf(address owner) view returns (uint256)",
|
|
@@ -435,13 +717,13 @@ async function getJson(fetchFn, url, signal, what) {
|
|
|
435
717
|
}
|
|
436
718
|
var isRecord = (v) => typeof v === "object" && v !== null;
|
|
437
719
|
var scopeCaches = /* @__PURE__ */ new WeakMap();
|
|
438
|
-
function memo(
|
|
439
|
-
const hit =
|
|
720
|
+
function memo(cache2, key, signal, load) {
|
|
721
|
+
const hit = cache2.get(key);
|
|
440
722
|
if (hit) return hit;
|
|
441
723
|
const p = withSignal(load(), signal);
|
|
442
|
-
|
|
724
|
+
cache2.set(key, p);
|
|
443
725
|
p.catch(() => {
|
|
444
|
-
if (
|
|
726
|
+
if (cache2.get(key) === p) cache2.delete(key);
|
|
445
727
|
});
|
|
446
728
|
return p;
|
|
447
729
|
}
|
|
@@ -491,8 +773,8 @@ async function fetchMerkleEntries(args) {
|
|
|
491
773
|
scoped = { signal: args.signal, instances: /* @__PURE__ */ new Map(), entries: /* @__PURE__ */ new Map() };
|
|
492
774
|
scopeCaches.set(args.scope, scoped);
|
|
493
775
|
}
|
|
494
|
-
const
|
|
495
|
-
const entries = await memo(
|
|
776
|
+
const cache2 = scoped;
|
|
777
|
+
const entries = await memo(cache2.entries, key, cache2.signal, () => loadEntries(fetchFn, args.api, args.instanceId, args.recipient, cache2));
|
|
496
778
|
return entries.slice();
|
|
497
779
|
}
|
|
498
780
|
|
|
@@ -537,14 +819,14 @@ var GAS_UNITS_APPROVE = 60000n;
|
|
|
537
819
|
var dropResolved = /* @__PURE__ */ new WeakMap();
|
|
538
820
|
var WALLET_DATA_BRAND = /* @__PURE__ */ Symbol("manifold-claim-wallet-data");
|
|
539
821
|
var isWalletData = (d) => typeof d === "object" && d !== null && d.brand === WALLET_DATA_BRAND;
|
|
540
|
-
var isAllowlist = (claim) => claim.merkleRoot !==
|
|
822
|
+
var isAllowlist = (claim) => claim.merkleRoot !== import_viem5.zeroHash;
|
|
541
823
|
function isContractFailure(e) {
|
|
542
|
-
if (!(e instanceof
|
|
543
|
-
return e.walk((x) => x instanceof
|
|
824
|
+
if (!(e instanceof import_viem5.BaseError)) return false;
|
|
825
|
+
return e.walk((x) => x instanceof import_viem5.ContractFunctionRevertedError || x.name === "ContractFunctionZeroDataError") !== null;
|
|
544
826
|
}
|
|
545
827
|
function toRpcError(e) {
|
|
546
828
|
if (e instanceof MintError) return e;
|
|
547
|
-
const msg = e instanceof
|
|
829
|
+
const msg = e instanceof import_viem5.BaseError ? e.shortMessage : e instanceof Error ? e.message : String(e);
|
|
548
830
|
return new MintError("RPC_ERROR", `RPC request failed: ${msg}`, { cause: e });
|
|
549
831
|
}
|
|
550
832
|
async function rpc(fn) {
|
|
@@ -557,19 +839,19 @@ async function rpc(fn) {
|
|
|
557
839
|
function revertData(e) {
|
|
558
840
|
const hit = e.walk((x) => {
|
|
559
841
|
const d2 = x.data;
|
|
560
|
-
return (0,
|
|
842
|
+
return (0, import_viem5.isHex)(d2) && d2.length >= 10 || typeof d2 === "object" && d2 !== null && (0, import_viem5.isHex)(d2.data);
|
|
561
843
|
});
|
|
562
844
|
const d = hit?.data;
|
|
563
|
-
if ((0,
|
|
845
|
+
if ((0, import_viem5.isHex)(d)) return d;
|
|
564
846
|
const inner = d?.data;
|
|
565
|
-
return (0,
|
|
847
|
+
return (0, import_viem5.isHex)(inner) ? inner : void 0;
|
|
566
848
|
}
|
|
567
849
|
function decodeRevert(e) {
|
|
568
|
-
if (!(e instanceof
|
|
850
|
+
if (!(e instanceof import_viem5.BaseError)) return null;
|
|
569
851
|
const data = revertData(e);
|
|
570
852
|
if (data) {
|
|
571
853
|
try {
|
|
572
|
-
const d = (0,
|
|
854
|
+
const d = (0, import_viem5.decodeErrorResult)({ abi: EXTENSION_ERRORS_ABI, data });
|
|
573
855
|
if (d.errorName === "Error" && typeof d.args?.[0] === "string") return d.args[0];
|
|
574
856
|
if (d.errorName === "Panic") return `Panic(${String(d.args?.[0])})`;
|
|
575
857
|
return d.errorName;
|
|
@@ -578,33 +860,33 @@ function decodeRevert(e) {
|
|
|
578
860
|
}
|
|
579
861
|
}
|
|
580
862
|
const named = e.walk((x) => ["ExecutionRevertedError", "InsufficientFundsError"].includes(x.name));
|
|
581
|
-
return named instanceof
|
|
863
|
+
return named instanceof import_viem5.BaseError ? named.shortMessage : null;
|
|
582
864
|
}
|
|
583
|
-
var money = (value, decimals, symbol) => ({ value, decimals, symbol, formatted: (0,
|
|
865
|
+
var money = (value, decimals, symbol) => ({ value, decimals, symbol, formatted: (0, import_viem5.formatUnits)(value, decimals) });
|
|
584
866
|
function timestamp(v, field) {
|
|
585
867
|
if (v === 0n) return null;
|
|
586
868
|
if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new MintError("RPC_ERROR", `Claim ${field} does not fit a safe integer`);
|
|
587
869
|
return Number(v);
|
|
588
870
|
}
|
|
589
871
|
function parseConfig(config) {
|
|
590
|
-
if (!(0,
|
|
872
|
+
if (!(0, import_viem5.isAddress)(config.creatorContract, { strict: false })) {
|
|
591
873
|
throw new MintError("INVALID_CONFIG", "creatorContract is not a valid address.", { field: "creatorContract" });
|
|
592
874
|
}
|
|
593
|
-
if (config.extension !== void 0 && !(0,
|
|
875
|
+
if (config.extension !== void 0 && !(0, import_viem5.isAddress)(config.extension, { strict: false })) {
|
|
594
876
|
throw new MintError("INVALID_CONFIG", "extension is not a valid address.", { field: "extension" });
|
|
595
877
|
}
|
|
596
878
|
if (typeof config.instanceId !== "string" || !/^\d+$/.test(config.instanceId)) {
|
|
597
879
|
throw new MintError("INVALID_CONFIG", "instanceId must be a non-negative integer string.", { field: "instanceId" });
|
|
598
880
|
}
|
|
599
881
|
return {
|
|
600
|
-
creator: (0,
|
|
882
|
+
creator: (0, import_viem5.getAddress)(config.creatorContract),
|
|
601
883
|
instanceId: BigInt(config.instanceId),
|
|
602
|
-
extension: config.extension === void 0 ? void 0 : (0,
|
|
884
|
+
extension: config.extension === void 0 ? void 0 : (0, import_viem5.getAddress)(config.extension)
|
|
603
885
|
};
|
|
604
886
|
}
|
|
605
887
|
var notFound = (config) => new MintError("CLAIM_NOT_FOUND", `No Manifold claim ${config.instanceId} found on ${config.creatorContract}.`);
|
|
606
888
|
async function readClaim(client, entry, creator, instanceId, config) {
|
|
607
|
-
const address = (0,
|
|
889
|
+
const address = (0, import_viem5.getAddress)(entry.address);
|
|
608
890
|
const args = [creator, instanceId];
|
|
609
891
|
try {
|
|
610
892
|
switch (entry.abi) {
|
|
@@ -643,7 +925,7 @@ function base2(c) {
|
|
|
643
925
|
};
|
|
644
926
|
}
|
|
645
927
|
async function hasCode(client, address) {
|
|
646
|
-
const code2 = await rpc(() => client.getCode({ address: (0,
|
|
928
|
+
const code2 = await rpc(() => client.getCode({ address: (0, import_viem5.getAddress)(address) }));
|
|
647
929
|
return code2 !== void 0 && code2 !== "0x";
|
|
648
930
|
}
|
|
649
931
|
var unknownExtension = (address, why) => new MintError("UNKNOWN_EXTENSION", `Extension ${address} ${why}.`);
|
|
@@ -687,21 +969,21 @@ async function resolveExtension(ctx, config) {
|
|
|
687
969
|
}
|
|
688
970
|
async function loadDrop(ctx, config) {
|
|
689
971
|
const { entry, claim } = await resolveExtension(ctx, config);
|
|
690
|
-
if (claim.signingAddress !== null && claim.signingAddress !==
|
|
972
|
+
if (claim.signingAddress !== null && claim.signingAddress !== import_viem5.zeroAddress) {
|
|
691
973
|
throw new MintError("SIGNATURE_CLAIM", "This claim mints with a signature (redemption code), which this button does not support.");
|
|
692
974
|
}
|
|
693
975
|
const { client } = ctx;
|
|
694
|
-
const extension = (0,
|
|
976
|
+
const extension = (0, import_viem5.getAddress)(entry.address);
|
|
695
977
|
const fee = await rpc(
|
|
696
978
|
() => client.readContract({ address: extension, abi: EXTENSION_COMMON_ABI, functionName: isAllowlist(claim) ? "MINT_FEE_MERKLE" : "MINT_FEE" })
|
|
697
979
|
);
|
|
698
980
|
const native = client.chain?.nativeCurrency ?? { symbol: "ETH", decimals: 18 };
|
|
699
981
|
let unitPrice;
|
|
700
982
|
let paymentToken = null;
|
|
701
|
-
if (claim.erc20 ===
|
|
983
|
+
if (claim.erc20 === import_viem5.zeroAddress) {
|
|
702
984
|
unitPrice = money(claim.cost, native.decimals, native.symbol);
|
|
703
985
|
} else {
|
|
704
|
-
paymentToken = (0,
|
|
986
|
+
paymentToken = (0, import_viem5.getAddress)(claim.erc20);
|
|
705
987
|
const token = paymentToken;
|
|
706
988
|
const [symbol, decimals] = await rpc(
|
|
707
989
|
() => Promise.all([
|
|
@@ -732,9 +1014,9 @@ async function loadWallet(ctx, config, args) {
|
|
|
732
1014
|
const { client } = ctx;
|
|
733
1015
|
const resolved = dropResolved.get(args.drop) ?? (await loadDrop(ctx, config)).resolved;
|
|
734
1016
|
const { entry, claim } = resolved;
|
|
735
|
-
const extension = (0,
|
|
736
|
-
const payer = (0,
|
|
737
|
-
const recipient = (0,
|
|
1017
|
+
const extension = (0, import_viem5.getAddress)(entry.address);
|
|
1018
|
+
const payer = (0, import_viem5.getAddress)(args.payer);
|
|
1019
|
+
const recipient = (0, import_viem5.getAddress)(args.recipient);
|
|
738
1020
|
let walletMinted = null;
|
|
739
1021
|
let walletMax = null;
|
|
740
1022
|
let allowlistRemaining = null;
|
|
@@ -770,7 +1052,7 @@ async function loadWallet(ctx, config, args) {
|
|
|
770
1052
|
maxQuantity = Number(left);
|
|
771
1053
|
if (left === 0n) ineligible = { reason: "WALLET_LIMIT", message: `This wallet has already minted its limit of ${walletMax}.` };
|
|
772
1054
|
}
|
|
773
|
-
const token = claim.erc20 ===
|
|
1055
|
+
const token = claim.erc20 === import_viem5.zeroAddress ? null : (0, import_viem5.getAddress)(claim.erc20);
|
|
774
1056
|
const [native, erc20, gasPrice] = await rpc(
|
|
775
1057
|
() => Promise.all([
|
|
776
1058
|
client.getBalance({ address: payer }),
|
|
@@ -798,8 +1080,8 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
798
1080
|
throw new MintError("INVALID_CONFIG", `quantity must be an integer from 1 to ${MAX_MINT_COUNT}.`, { field: "quantity" });
|
|
799
1081
|
}
|
|
800
1082
|
const { creator, instanceId } = parseConfig(config);
|
|
801
|
-
const recipient = (0,
|
|
802
|
-
const payer = (0,
|
|
1083
|
+
const recipient = (0, import_viem5.getAddress)(attempt.recipient);
|
|
1084
|
+
const payer = (0, import_viem5.getAddress)(attempt.payer);
|
|
803
1085
|
let data = wallet.data;
|
|
804
1086
|
if (!isWalletData(data) || !sameClaim(data.config, config) || data.recipient !== recipient) {
|
|
805
1087
|
const drop = (await loadDrop(ctx, config)).drop;
|
|
@@ -807,7 +1089,7 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
807
1089
|
}
|
|
808
1090
|
if (!isWalletData(data)) throw new MintError("RPC_ERROR", "Wallet read produced no plan data.");
|
|
809
1091
|
const { entry, claim, fee } = data.resolved;
|
|
810
|
-
const extension = (0,
|
|
1092
|
+
const extension = (0, import_viem5.getAddress)(entry.address);
|
|
811
1093
|
let mintIndices = [];
|
|
812
1094
|
let merkleProofs = [];
|
|
813
1095
|
if (isAllowlist(claim)) {
|
|
@@ -819,7 +1101,7 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
819
1101
|
merkleProofs = picked.map((p) => p.proof);
|
|
820
1102
|
}
|
|
821
1103
|
const qty = BigInt(quantity);
|
|
822
|
-
const erc20 = claim.erc20 ===
|
|
1104
|
+
const erc20 = claim.erc20 === import_viem5.zeroAddress ? null : (0, import_viem5.getAddress)(claim.erc20);
|
|
823
1105
|
const cost = claim.cost * qty;
|
|
824
1106
|
const value = fee * qty + (erc20 ? 0n : cost);
|
|
825
1107
|
let approve = null;
|
|
@@ -830,7 +1112,7 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
830
1112
|
token: erc20,
|
|
831
1113
|
spender: extension,
|
|
832
1114
|
amount: cost,
|
|
833
|
-
tx: { to: erc20, data: (0,
|
|
1115
|
+
tx: { to: erc20, data: (0, import_viem5.encodeFunctionData)({ abi: ERC20_ABI, functionName: "approve", args: [extension, cost] }), value: 0n }
|
|
834
1116
|
};
|
|
835
1117
|
}
|
|
836
1118
|
}
|
|
@@ -838,7 +1120,7 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
838
1120
|
approve,
|
|
839
1121
|
mint: {
|
|
840
1122
|
to: extension,
|
|
841
|
-
data: (0,
|
|
1123
|
+
data: (0, import_viem5.encodeFunctionData)({
|
|
842
1124
|
abi: EXTENSION_COMMON_ABI,
|
|
843
1125
|
functionName: "mintProxy",
|
|
844
1126
|
args: [creator, instanceId, quantity, mintIndices, merkleProofs.map((p) => [...p]), recipient]
|
|
@@ -848,13 +1130,18 @@ async function buildPlan(ctx, attempt, wallet) {
|
|
|
848
1130
|
tokenContract: creator
|
|
849
1131
|
};
|
|
850
1132
|
}
|
|
1133
|
+
var RELAY_VERIFIED = ["V6", "V7"];
|
|
851
1134
|
var manifoldClaimAdapter = {
|
|
1135
|
+
supportsRelay: (drop) => {
|
|
1136
|
+
const resolved = dropResolved.get(drop);
|
|
1137
|
+
return resolved !== void 0 && RELAY_VERIFIED.includes(resolved.entry.generation) && drop.paymentToken === null;
|
|
1138
|
+
},
|
|
852
1139
|
readDrop: async (ctx, config) => (await loadDrop(ctx, config)).drop,
|
|
853
1140
|
readWallet: loadWallet,
|
|
854
1141
|
buildPlan,
|
|
855
1142
|
simulate: async (ctx, attempt, plan) => {
|
|
856
1143
|
try {
|
|
857
|
-
await ctx.client.call({ account: (0,
|
|
1144
|
+
await ctx.client.call({ account: (0, import_viem5.getAddress)(attempt.payer), to: plan.mint.to, data: plan.mint.data, value: plan.mint.value });
|
|
858
1145
|
} catch (e) {
|
|
859
1146
|
const text2 = decodeRevert(e);
|
|
860
1147
|
if (text2 === null) throw toRpcError(e);
|
|
@@ -864,10 +1151,10 @@ var manifoldClaimAdapter = {
|
|
|
864
1151
|
};
|
|
865
1152
|
|
|
866
1153
|
// src/core/adapters/contract/index.ts
|
|
867
|
-
var
|
|
1154
|
+
var import_viem7 = require("viem");
|
|
868
1155
|
|
|
869
1156
|
// src/core/machine.ts
|
|
870
|
-
var
|
|
1157
|
+
var import_viem6 = require("viem");
|
|
871
1158
|
var MINT_STATES = [
|
|
872
1159
|
"loading",
|
|
873
1160
|
"unsupported",
|
|
@@ -904,9 +1191,15 @@ function initialState(opts) {
|
|
|
904
1191
|
attempt: null,
|
|
905
1192
|
result: null,
|
|
906
1193
|
pendingRefresh: false,
|
|
907
|
-
now: 0
|
|
1194
|
+
now: 0,
|
|
1195
|
+
crossChain: false,
|
|
1196
|
+
routePending: false,
|
|
1197
|
+
route: null,
|
|
1198
|
+
short: null
|
|
908
1199
|
};
|
|
909
1200
|
}
|
|
1201
|
+
var NO_ROUTE = { crossChain: false, routePending: false, route: null, short: null };
|
|
1202
|
+
var PICK_ANOTHER = "Pick another network to pay from.";
|
|
910
1203
|
function toQuantity(q, max) {
|
|
911
1204
|
if (Number.isNaN(q)) return 1;
|
|
912
1205
|
return Math.max(1, Math.min(Math.floor(q), max));
|
|
@@ -931,11 +1224,17 @@ function derive(s, unsupported) {
|
|
|
931
1224
|
if (walletRead.ineligible !== null) {
|
|
932
1225
|
return { name: "ineligible", reason: walletRead.ineligible.reason, message: walletRead.ineligible.message };
|
|
933
1226
|
}
|
|
1227
|
+
if (s.routePending) return named("loading");
|
|
1228
|
+
if (s.route?.kind === "relay") return named("live");
|
|
1229
|
+
if (s.short !== null) {
|
|
1230
|
+
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${s.short.symbol} on ${s.short.name}. ${PICK_ANOTHER}` };
|
|
1231
|
+
}
|
|
934
1232
|
const total = selectTotal(s);
|
|
935
1233
|
if (total !== null) {
|
|
936
1234
|
const { native, erc20 } = total;
|
|
937
1235
|
if (native.value + walletRead.gasReserve > walletRead.balances.native) {
|
|
938
|
-
|
|
1236
|
+
const more = s.crossChain ? ` ${PICK_ANOTHER}` : "";
|
|
1237
|
+
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${native.symbol} to cover the mint.${more}` };
|
|
939
1238
|
}
|
|
940
1239
|
if (erc20 !== null && walletRead.balances.erc20 !== null && erc20.value > walletRead.balances.erc20) {
|
|
941
1240
|
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${erc20.symbol} to cover the mint.` };
|
|
@@ -964,7 +1263,7 @@ function reducer(state, event) {
|
|
|
964
1263
|
const next2 = { ...state, readGen: event.gen };
|
|
965
1264
|
return inFlight ? { ...next2, pendingRefresh: true } : settle(next2);
|
|
966
1265
|
}
|
|
967
|
-
const next = { ...state, readGen: event.gen, drop: null, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
1266
|
+
const next = { ...state, ...NO_ROUTE, readGen: event.gen, drop: null, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
968
1267
|
return inFlight ? { ...next, pendingRefresh: true } : settle(next, false);
|
|
969
1268
|
}
|
|
970
1269
|
case "DROP_LOADED": {
|
|
@@ -982,16 +1281,22 @@ function reducer(state, event) {
|
|
|
982
1281
|
case "READ_FAILED":
|
|
983
1282
|
return event.gen !== state.readGen ? state : failed(event.reason, event.message);
|
|
984
1283
|
case "ACCOUNT_CHANGED": {
|
|
985
|
-
const next = { ...state, account: event.account, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
1284
|
+
const next = { ...state, ...NO_ROUTE, account: event.account, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
986
1285
|
if (inFlight) return { ...next, pendingRefresh: true };
|
|
987
1286
|
if (state.name === "connecting") return event.account === null ? next : settle({ ...next, name: "loading" }, false);
|
|
988
1287
|
return settle(next);
|
|
989
1288
|
}
|
|
990
1289
|
case "WALLET_LOADED": {
|
|
991
1290
|
if (event.gen !== state.walletGen) return state;
|
|
992
|
-
const
|
|
1291
|
+
const route = event.routePending ? { routePending: state.route === null && state.short === null, route: state.route, short: state.short } : { routePending: false, route: null, short: null };
|
|
1292
|
+
const next = { ...state, ...route, crossChain: event.crossChain ?? false, walletRead: event.read, blocked: null };
|
|
993
1293
|
return inFlight ? { ...next, pendingRefresh: true } : settle(clampQuantity(next));
|
|
994
1294
|
}
|
|
1295
|
+
case "ROUTE_LOADED": {
|
|
1296
|
+
if (event.gen !== state.walletGen) return state;
|
|
1297
|
+
const next = { ...state, routePending: false, route: event.route, short: event.short };
|
|
1298
|
+
return inFlight ? { ...next, pendingRefresh: true } : settle(next);
|
|
1299
|
+
}
|
|
995
1300
|
case "WALLET_FAILED":
|
|
996
1301
|
return event.gen !== state.walletGen ? state : failed(event.reason, event.message);
|
|
997
1302
|
case "CONNECT_STARTED":
|
|
@@ -1005,7 +1310,9 @@ function reducer(state, event) {
|
|
|
1005
1310
|
return inFlight ? { ...next, pendingRefresh: true } : settle(next);
|
|
1006
1311
|
}
|
|
1007
1312
|
case "ATTEMPT_STARTED":
|
|
1008
|
-
|
|
1313
|
+
if (state.attempt !== null) return state;
|
|
1314
|
+
if (event.resume === true) return IN_FLIGHT_STATES.includes(state.name) ? state : { ...state, attempt: event.attempt };
|
|
1315
|
+
return state.name === "live" ? { ...state, attempt: event.attempt } : state;
|
|
1009
1316
|
case "SWITCHING_CHAIN":
|
|
1010
1317
|
return step(state, event.attemptId, "switching-chain");
|
|
1011
1318
|
case "APPROVING":
|
|
@@ -1047,6 +1354,7 @@ function reducer(state, event) {
|
|
|
1047
1354
|
const next = {
|
|
1048
1355
|
...state,
|
|
1049
1356
|
readGen: event.gen,
|
|
1357
|
+
...NO_ROUTE,
|
|
1050
1358
|
walletRead: null,
|
|
1051
1359
|
blocked: null,
|
|
1052
1360
|
walletGen: state.walletGen + 1,
|
|
@@ -1092,7 +1400,7 @@ function selectTotal(state) {
|
|
|
1092
1400
|
return { native: money2(priceTotal + fee.value * q, price), erc20: null };
|
|
1093
1401
|
}
|
|
1094
1402
|
function money2(value, like) {
|
|
1095
|
-
return { value, decimals: like.decimals, symbol: like.symbol, formatted: (0,
|
|
1403
|
+
return { value, decimals: like.decimals, symbol: like.symbol, formatted: (0, import_viem6.formatUnits)(value, like.decimals) };
|
|
1096
1404
|
}
|
|
1097
1405
|
function canAct(state) {
|
|
1098
1406
|
return state.attempt === null && CLICKABLE_STATES.includes(state.name);
|
|
@@ -1100,7 +1408,7 @@ function canAct(state) {
|
|
|
1100
1408
|
|
|
1101
1409
|
// src/core/adapters/contract/index.ts
|
|
1102
1410
|
var PLACEHOLDERS = ["{recipient}", "{payer}", "{quantity}", "{unitPrice}", "{totalPrice}"];
|
|
1103
|
-
var ERC20_ABI2 = (0,
|
|
1411
|
+
var ERC20_ABI2 = (0, import_viem7.parseAbi)([
|
|
1104
1412
|
"function symbol() view returns (string)",
|
|
1105
1413
|
"function decimals() view returns (uint8)",
|
|
1106
1414
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -1115,7 +1423,7 @@ var NUMERIC_TYPE = /^u?int\d*$/;
|
|
|
1115
1423
|
function bad(field, message) {
|
|
1116
1424
|
throw new MintError("INVALID_CONFIG", `${field} ${message}`, { field });
|
|
1117
1425
|
}
|
|
1118
|
-
var isHexAddress = (a) => typeof a === "string" && (0,
|
|
1426
|
+
var isHexAddress = (a) => typeof a === "string" && (0, import_viem7.isAddress)(a, { strict: false });
|
|
1119
1427
|
function functionsNamed(abi, name) {
|
|
1120
1428
|
return abi.filter((i) => i.type === "function" && i.name === name);
|
|
1121
1429
|
}
|
|
@@ -1172,7 +1480,7 @@ function typeIssue(t, p, path) {
|
|
|
1172
1480
|
if (Array.isArray(t)) return no(`must not be an array for ${p.type}`);
|
|
1173
1481
|
if (p.type === "address") {
|
|
1174
1482
|
if (isExactPlaceholder(t)) return t === "{recipient}" || t === "{payer}" ? null : no(`${t} is a number, but the abi input is an address`);
|
|
1175
|
-
return typeof t === "string" && !hasPlaceholder(t) && (0,
|
|
1483
|
+
return typeof t === "string" && !hasPlaceholder(t) && (0, import_viem7.isAddress)(t) ? null : no(`${JSON.stringify(String(t))} is not an address`);
|
|
1176
1484
|
}
|
|
1177
1485
|
if (p.type === "bool") return typeof t === "boolean" ? null : no(`${JSON.stringify(String(t))} is not a boolean`);
|
|
1178
1486
|
if (p.type === "string") return typeof t === "string" ? null : no(`${JSON.stringify(String(t))} is not a string`);
|
|
@@ -1329,7 +1637,7 @@ function varFor(token, vars) {
|
|
|
1329
1637
|
const key = token.slice(1, -1);
|
|
1330
1638
|
const v = vars[key];
|
|
1331
1639
|
if (v === void 0) throw new MintError("INVALID_CONFIG", `placeholder ${token} has no value here`);
|
|
1332
|
-
return typeof v === "string" ? (0,
|
|
1640
|
+
return typeof v === "string" ? (0, import_viem7.getAddress)(v) : v;
|
|
1333
1641
|
}
|
|
1334
1642
|
function fill(t, p, vars) {
|
|
1335
1643
|
const arr = ARRAY_TYPE.exec(p.type);
|
|
@@ -1345,7 +1653,7 @@ function fill(t, p, vars) {
|
|
|
1345
1653
|
var fillArgs = (args, inputs, vars) => (args ?? []).map((a, i) => fill(a, inputs[i], vars));
|
|
1346
1654
|
var totalFor = (per, price, quantity) => per === "total" ? price : price * quantity;
|
|
1347
1655
|
function shortMessage(e) {
|
|
1348
|
-
return e instanceof
|
|
1656
|
+
return e instanceof import_viem7.BaseError ? e.shortMessage : e instanceof Error ? e.message : String(e);
|
|
1349
1657
|
}
|
|
1350
1658
|
var abortError = () => new DOMException("The operation was aborted", "AbortError");
|
|
1351
1659
|
var isAbortError = (e) => e?.name === "AbortError";
|
|
@@ -1368,7 +1676,7 @@ function rpcError(e) {
|
|
|
1368
1676
|
}
|
|
1369
1677
|
function classifyReadError(e, field) {
|
|
1370
1678
|
if (isMintError(e) || isAbortError(e)) throw e;
|
|
1371
|
-
if (e instanceof
|
|
1679
|
+
if (e instanceof import_viem7.BaseError && e.walk((x) => x instanceof import_viem7.ContractFunctionRevertedError || x instanceof import_viem7.ContractFunctionZeroDataError)) {
|
|
1372
1680
|
throw new MintError("INVALID_CONFIG", `${field} read reverted or returned no data: ${e.shortMessage}`, { field, cause: e });
|
|
1373
1681
|
}
|
|
1374
1682
|
return rpcError(e);
|
|
@@ -1418,7 +1726,7 @@ function nativeMeta(ctx) {
|
|
|
1418
1726
|
const n = ctx.client.chain?.nativeCurrency;
|
|
1419
1727
|
return { decimals: n?.decimals ?? 18, symbol: n?.symbol ?? "ETH" };
|
|
1420
1728
|
}
|
|
1421
|
-
var erc20Of = (config) => config.price.currency === "native" ? null : (0,
|
|
1729
|
+
var erc20Of = (config) => config.price.currency === "native" ? null : (0, import_viem7.getAddress)(config.price.currency.address);
|
|
1422
1730
|
async function currencyMeta(ctx, config) {
|
|
1423
1731
|
const token = erc20Of(config);
|
|
1424
1732
|
if (!token) return { token, ...nativeMeta(ctx) };
|
|
@@ -1437,7 +1745,7 @@ function parseDisplay(value, decimals) {
|
|
|
1437
1745
|
if (frac.length > decimals) {
|
|
1438
1746
|
throw new MintError("INVALID_CONFIG", `price.value "${value}" has more than ${decimals} decimal places`, { field: "price.value" });
|
|
1439
1747
|
}
|
|
1440
|
-
return (0,
|
|
1748
|
+
return (0, import_viem7.parseUnits)(value, decimals);
|
|
1441
1749
|
}
|
|
1442
1750
|
async function resolvePrice(ctx, config, meta) {
|
|
1443
1751
|
const price = config.price;
|
|
@@ -1450,7 +1758,7 @@ var money3 = (value, decimals, symbol) => ({
|
|
|
1450
1758
|
value,
|
|
1451
1759
|
decimals,
|
|
1452
1760
|
symbol,
|
|
1453
|
-
formatted: (0,
|
|
1761
|
+
formatted: (0, import_viem7.formatUnits)(value, decimals)
|
|
1454
1762
|
});
|
|
1455
1763
|
function revertData2(e) {
|
|
1456
1764
|
for (let cur = e, depth = 0; cur && depth < 12; depth++) {
|
|
@@ -1466,7 +1774,7 @@ function revertData2(e) {
|
|
|
1466
1774
|
}
|
|
1467
1775
|
function describeRevert(abi, data) {
|
|
1468
1776
|
try {
|
|
1469
|
-
const d = (0,
|
|
1777
|
+
const d = (0, import_viem7.decodeErrorResult)({ abi, data });
|
|
1470
1778
|
const args = d.args ?? [];
|
|
1471
1779
|
if (d.errorName === "Error") return String(args[0] ?? "execution reverted");
|
|
1472
1780
|
const inputs = d.abiItem?.inputs ?? [];
|
|
@@ -1479,12 +1787,12 @@ function describeRevert(abi, data) {
|
|
|
1479
1787
|
function toSimulationError(e, abi) {
|
|
1480
1788
|
if (isMintError(e)) return e;
|
|
1481
1789
|
if (isAbortError(e)) throw e;
|
|
1482
|
-
if (e instanceof
|
|
1790
|
+
if (e instanceof import_viem7.BaseError && e.walk((x) => x instanceof import_viem7.InsufficientFundsError)) {
|
|
1483
1791
|
return new MintError("INSUFFICIENT_FUNDS", `Payer cannot cover the mint: ${e.shortMessage}`, { cause: e });
|
|
1484
1792
|
}
|
|
1485
1793
|
const data = revertData2(e);
|
|
1486
1794
|
if (data) return new MintError("SIMULATION_REVERTED", describeRevert(abi, data), { cause: e });
|
|
1487
|
-
const isRevert = e instanceof
|
|
1795
|
+
const isRevert = e instanceof import_viem7.BaseError && e.walk((x) => x.code === 3 || /execution reverted/i.test(x.message));
|
|
1488
1796
|
if (isRevert) return new MintError("SIMULATION_REVERTED", shortMessage(e), { cause: e });
|
|
1489
1797
|
return new MintError("RPC_ERROR", `RPC request failed: ${shortMessage(e)}`, { cause: e });
|
|
1490
1798
|
}
|
|
@@ -1498,7 +1806,7 @@ async function simulateMint(ctx, config, call) {
|
|
|
1498
1806
|
function encodeMint(config, vars) {
|
|
1499
1807
|
const fn = validateCall(config.abi, config.mint, "mint", "mint", PLACEHOLDERS);
|
|
1500
1808
|
try {
|
|
1501
|
-
return (0,
|
|
1809
|
+
return (0, import_viem7.encodeFunctionData)({ abi: [fn], functionName: fn.name, args: fillArgs(config.mint.args, fn.inputs, vars) });
|
|
1502
1810
|
} catch (e) {
|
|
1503
1811
|
if (isMintError(e)) throw e;
|
|
1504
1812
|
throw new MintError("INVALID_CONFIG", `mint.args cannot be encoded for ${signature(fn)}: ${shortMessage(e)}`, { field: "mint.args", cause: e });
|
|
@@ -1563,7 +1871,7 @@ async function readWallet(ctx, config, { payer, recipient, quantity, drop }) {
|
|
|
1563
1871
|
if (remaining === 0n) {
|
|
1564
1872
|
ineligible = { reason: "WALLET_LIMIT", message: `This wallet has already minted its limit of ${walletMax}.` };
|
|
1565
1873
|
} else if (drop.paymentToken === null && native >= totalPrice) {
|
|
1566
|
-
const call = { payer, to: (0,
|
|
1874
|
+
const call = { payer, to: (0, import_viem7.getAddress)(config.address), data: encodeMint(config, vars), value: mintValue(config, null, totalPrice) };
|
|
1567
1875
|
try {
|
|
1568
1876
|
await simulateMint(ctx, config, call);
|
|
1569
1877
|
gas = await io(ctx, () => ctx.client.estimateGas({ account: payer, to: call.to, data: call.data, value: call.value })).catch((e) => {
|
|
@@ -1594,7 +1902,7 @@ async function buildPlan2(ctx, attempt, wallet) {
|
|
|
1594
1902
|
const qty = BigInt(attempt.quantity);
|
|
1595
1903
|
const unitPrice = await resolvePrice(ctx, config);
|
|
1596
1904
|
const totalPrice = totalFor(config.price.per ?? "unit", unitPrice, qty);
|
|
1597
|
-
const to = (0,
|
|
1905
|
+
const to = (0, import_viem7.getAddress)(config.address);
|
|
1598
1906
|
const token = erc20Of(config);
|
|
1599
1907
|
const mint = {
|
|
1600
1908
|
to,
|
|
@@ -1612,11 +1920,11 @@ async function buildPlan2(ctx, attempt, wallet) {
|
|
|
1612
1920
|
token,
|
|
1613
1921
|
spender: to,
|
|
1614
1922
|
amount: totalPrice,
|
|
1615
|
-
tx: { to: token, data: (0,
|
|
1923
|
+
tx: { to: token, data: (0, import_viem7.encodeFunctionData)({ abi: ERC20_ABI2, functionName: "approve", args: [to, totalPrice] }), value: 0n }
|
|
1616
1924
|
};
|
|
1617
1925
|
}
|
|
1618
1926
|
}
|
|
1619
|
-
return { approve, mint, tokenContract: (0,
|
|
1927
|
+
return { approve, mint, tokenContract: (0, import_viem7.getAddress)(attempt.tokenContract) };
|
|
1620
1928
|
}
|
|
1621
1929
|
async function simulate(ctx, attempt, plan) {
|
|
1622
1930
|
throwIfAborted(ctx);
|
|
@@ -1625,29 +1933,29 @@ async function simulate(ctx, attempt, plan) {
|
|
|
1625
1933
|
var contractAdapter = { readDrop, readWallet, buildPlan: buildPlan2, simulate };
|
|
1626
1934
|
|
|
1627
1935
|
// src/core/adapters/receipt.ts
|
|
1628
|
-
var
|
|
1936
|
+
var import_viem8 = require("viem");
|
|
1629
1937
|
var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
1630
1938
|
var TRANSFER_SINGLE_TOPIC = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62";
|
|
1631
1939
|
var TRANSFER_BATCH_TOPIC = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb";
|
|
1632
|
-
var topicAddress = (t) => t && t.length === 66 ? (0,
|
|
1940
|
+
var topicAddress = (t) => t && t.length === 66 ? (0, import_viem8.getAddress)(`0x${t.slice(26)}`) : null;
|
|
1633
1941
|
function parseMintReceipt(logs, { tokenContract, recipient }) {
|
|
1634
|
-
const contract = (0,
|
|
1635
|
-
const to = (0,
|
|
1942
|
+
const contract = (0, import_viem8.getAddress)(tokenContract);
|
|
1943
|
+
const to = (0, import_viem8.getAddress)(recipient);
|
|
1636
1944
|
const out = /* @__PURE__ */ new Map();
|
|
1637
1945
|
const add = (id, amount) => out.set(id, (out.get(id) ?? 0n) + amount);
|
|
1638
1946
|
for (const log of logs) {
|
|
1639
|
-
if ((0,
|
|
1947
|
+
if ((0, import_viem8.getAddress)(log.address) !== contract) continue;
|
|
1640
1948
|
const [t0, t1, t2, t3] = log.topics;
|
|
1641
1949
|
if (t0 === TRANSFER_TOPIC && log.topics.length === 4) {
|
|
1642
|
-
if (topicAddress(t1) !==
|
|
1950
|
+
if (topicAddress(t1) !== import_viem8.zeroAddress || topicAddress(t2) !== to || !t3) continue;
|
|
1643
1951
|
add(BigInt(t3), 1n);
|
|
1644
1952
|
} else if (t0 === TRANSFER_SINGLE_TOPIC && log.topics.length === 4) {
|
|
1645
|
-
if (topicAddress(t2) !==
|
|
1646
|
-
const [id, value] = (0,
|
|
1953
|
+
if (topicAddress(t2) !== import_viem8.zeroAddress || topicAddress(t3) !== to) continue;
|
|
1954
|
+
const [id, value] = (0, import_viem8.decodeAbiParameters)([{ type: "uint256" }, { type: "uint256" }], log.data);
|
|
1647
1955
|
add(id, value);
|
|
1648
1956
|
} else if (t0 === TRANSFER_BATCH_TOPIC && log.topics.length === 4) {
|
|
1649
|
-
if (topicAddress(t2) !==
|
|
1650
|
-
const [ids, values] = (0,
|
|
1957
|
+
if (topicAddress(t2) !== import_viem8.zeroAddress || topicAddress(t3) !== to) continue;
|
|
1958
|
+
const [ids, values] = (0, import_viem8.decodeAbiParameters)([{ type: "uint256[]" }, { type: "uint256[]" }], log.data);
|
|
1651
1959
|
if (ids.length !== values.length) continue;
|
|
1652
1960
|
ids.forEach((id, i) => add(id, values[i]));
|
|
1653
1961
|
}
|
|
@@ -1694,6 +2002,7 @@ function friendlyErrorMessage(error) {
|
|
|
1694
2002
|
if (isMintError(error)) {
|
|
1695
2003
|
if (error.reason === "INSUFFICIENT_FUNDS") return fundsMessage(collect(error.cause)) ?? error.message;
|
|
1696
2004
|
if (error.reason !== "RPC_ERROR" && error.reason !== "SIMULATION_REVERTED") return error.message;
|
|
2005
|
+
if (error.reason === "SIMULATION_REVERTED" && error.domain === "relay") return error.message;
|
|
1697
2006
|
if (error.reason === "SIMULATION_REVERTED" && (error.message === "The token refused the approval." || error.message === "The drop is no longer available for this quantity.")) return error.message;
|
|
1698
2007
|
}
|
|
1699
2008
|
const errors = collect(error);
|
|
@@ -1755,6 +2064,25 @@ function adapterFor(config) {
|
|
|
1755
2064
|
return config.type === "manifold-claim" ? manifoldClaimAdapter : contractAdapter;
|
|
1756
2065
|
}
|
|
1757
2066
|
var bridgeEnabled = (config) => config.type === "manifold-claim";
|
|
2067
|
+
function payChainsOf(config, option) {
|
|
2068
|
+
if (config.type !== "manifold-claim" || option === false) return [];
|
|
2069
|
+
const all = payChainsFor(config.chainId);
|
|
2070
|
+
const narrowed = typeof option === "object" && option.chains ? all.filter((id) => id === config.chainId || option.chains.includes(id)) : all;
|
|
2071
|
+
return narrowed.length > 1 ? narrowed : [];
|
|
2072
|
+
}
|
|
2073
|
+
function routeChainOf(payChains, accountChainId, payChainId) {
|
|
2074
|
+
if (payChainId !== void 0 && payChains.includes(payChainId)) return payChainId;
|
|
2075
|
+
return accountChainId;
|
|
2076
|
+
}
|
|
2077
|
+
var ROUTE_DEBOUNCE_MS = 500;
|
|
2078
|
+
var symbolOf = (chainId) => {
|
|
2079
|
+
try {
|
|
2080
|
+
return getChain(chainId).nativeCurrency.symbol;
|
|
2081
|
+
} catch {
|
|
2082
|
+
return "ETH";
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
var shortOf = (chainId) => ({ chainId, symbol: symbolOf(chainId), name: chainName(chainId) });
|
|
1758
2086
|
function tokenContractOf(config) {
|
|
1759
2087
|
return config.type === "manifold-claim" ? config.creatorContract : config.tokenContract ?? config.address;
|
|
1760
2088
|
}
|
|
@@ -1812,6 +2140,8 @@ function useMint(options) {
|
|
|
1812
2140
|
const widgetFailRef = (0, import_react.useRef)(null);
|
|
1813
2141
|
const widgetLoadRef = (0, import_react.useRef)(null);
|
|
1814
2142
|
const pushAccountRef = (0, import_react.useRef)(null);
|
|
2143
|
+
const autoResumedRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
2144
|
+
const [crossChainInfo, setCrossChainInfo] = (0, import_react.useState)(null);
|
|
1815
2145
|
(0, import_react.useEffect)(() => {
|
|
1816
2146
|
stateRef.current = machine;
|
|
1817
2147
|
propsRef.current = options;
|
|
@@ -1827,6 +2157,7 @@ function useMint(options) {
|
|
|
1827
2157
|
if (!handler || !mounted.current) return;
|
|
1828
2158
|
const cfg = attemptRef.current?.config ?? propsRef.current.config;
|
|
1829
2159
|
const s = stateRef.current;
|
|
2160
|
+
const originChainId = attemptRef.current?.originChainId ?? (s.route?.kind === "relay" ? s.route.originChainId : s.short?.chainId ?? cfg.chainId);
|
|
1830
2161
|
const payload = {
|
|
1831
2162
|
type,
|
|
1832
2163
|
state: s.name,
|
|
@@ -1834,6 +2165,8 @@ function useMint(options) {
|
|
|
1834
2165
|
chainId: cfg.chainId,
|
|
1835
2166
|
quantity: s.quantity,
|
|
1836
2167
|
config: { type: cfg.type, chainId: cfg.chainId, address: eventAddressOf(cfg) },
|
|
2168
|
+
route: originChainId === cfg.chainId ? "direct" : "relay",
|
|
2169
|
+
originChainId,
|
|
1837
2170
|
...over
|
|
1838
2171
|
};
|
|
1839
2172
|
try {
|
|
@@ -1953,7 +2286,7 @@ function useMint(options) {
|
|
|
1953
2286
|
if (cancelled) throw new MintError("USER_REJECTED", "The transaction was cancelled or replaced with a different transaction.");
|
|
1954
2287
|
if (await walletFor().getChainId() !== attempt.chainId) throw e;
|
|
1955
2288
|
assertActive();
|
|
1956
|
-
receipt2 = await walletFor().extend(
|
|
2289
|
+
receipt2 = await walletFor().extend(import_viem9.publicActions).waitForTransactionReceipt(params);
|
|
1957
2290
|
}
|
|
1958
2291
|
assertActive();
|
|
1959
2292
|
if (cancelled) throw new MintError("USER_REJECTED", "The transaction was cancelled or replaced with a different transaction.");
|
|
@@ -1976,8 +2309,8 @@ function useMint(options) {
|
|
|
1976
2309
|
}
|
|
1977
2310
|
} catch (e) {
|
|
1978
2311
|
if (isMintError(e)) throw e;
|
|
1979
|
-
const reverted = e instanceof
|
|
1980
|
-
if (reverted instanceof
|
|
2312
|
+
const reverted = e instanceof import_viem9.BaseError ? e.walk((cause) => cause instanceof import_viem9.ExecutionRevertedError) : null;
|
|
2313
|
+
if (reverted instanceof import_viem9.ExecutionRevertedError) {
|
|
1981
2314
|
throw new MintError("SIMULATION_REVERTED", reverted.shortMessage);
|
|
1982
2315
|
}
|
|
1983
2316
|
throw e;
|
|
@@ -2033,6 +2366,156 @@ function useMint(options) {
|
|
|
2033
2366
|
},
|
|
2034
2367
|
[clientFor, ctxFor, dispatch, emit]
|
|
2035
2368
|
);
|
|
2369
|
+
const finishRelay = (0, import_react.useCallback)(
|
|
2370
|
+
async (attempt, out, life) => {
|
|
2371
|
+
const cfg = attempt.config;
|
|
2372
|
+
const client = clientFor(life, cfg);
|
|
2373
|
+
const assertActive = () => {
|
|
2374
|
+
if (!mounted.current || life.controller.signal.aborted || lifeRef.current !== life || attemptRef.current !== attempt) {
|
|
2375
|
+
throw new DOMException("Mint attempt cancelled", "AbortError");
|
|
2376
|
+
}
|
|
2377
|
+
};
|
|
2378
|
+
const outcome = await pollUntilTerminal({ testnet: out.testnet, requestId: out.requestId, signal: life.controller.signal, assertActive });
|
|
2379
|
+
assertActive();
|
|
2380
|
+
const where = chainName(out.originChainId);
|
|
2381
|
+
if (outcome.kind === "timeout") {
|
|
2382
|
+
throw new MintError("BRIDGE_TIMEOUT", "The bridge is still processing. Your mint may still complete.");
|
|
2383
|
+
}
|
|
2384
|
+
const key = outstandingKey(attempt.config, attempt.payer);
|
|
2385
|
+
const { status } = outcome;
|
|
2386
|
+
if (status.status !== "success") clearOutstanding(key);
|
|
2387
|
+
if (status.status === "refund") {
|
|
2388
|
+
throw new MintError("BRIDGE_REFUNDED", `Relay could not complete the mint and is refunding your payment. Relay request ${out.requestId}.`);
|
|
2389
|
+
}
|
|
2390
|
+
if (status.status === "failure") {
|
|
2391
|
+
const why = status.failReason ? ` (${status.failReason})` : "";
|
|
2392
|
+
const noRefund = status.refundFailReason ? ` No automatic refund was sent. Contact support with Relay request ${out.requestId}.` : "";
|
|
2393
|
+
throw new MintError("BRIDGE_FAILED", `Relay could not complete the mint paid on ${where}${why}.${noRefund}`);
|
|
2394
|
+
}
|
|
2395
|
+
const fillHash = status.txHashes[0] ?? null;
|
|
2396
|
+
let tokens = [];
|
|
2397
|
+
if (fillHash) {
|
|
2398
|
+
try {
|
|
2399
|
+
const receipt = await client.waitForTransactionReceipt({ hash: fillHash, confirmations: 1, timeout: 6e4 });
|
|
2400
|
+
if (receiptOk(receipt.status)) {
|
|
2401
|
+
tokens = parseMintReceipt(receipt.logs ?? [], { tokenContract: attempt.tokenContract, recipient: attempt.recipient });
|
|
2402
|
+
}
|
|
2403
|
+
} catch {
|
|
2404
|
+
}
|
|
2405
|
+
assertActive();
|
|
2406
|
+
}
|
|
2407
|
+
const txHash = fillHash ?? out.originTxHash;
|
|
2408
|
+
const result = {
|
|
2409
|
+
txHash,
|
|
2410
|
+
explorerUrl: explorerUrlFor(fillHash ? cfg.chainId : out.originChainId, txHash),
|
|
2411
|
+
tokens,
|
|
2412
|
+
relay: { requestId: out.requestId, originTxHash: out.originTxHash, originChainId: out.originChainId }
|
|
2413
|
+
};
|
|
2414
|
+
clearOutstanding(key);
|
|
2415
|
+
dispatch({ type: "ATTEMPT_SUCCEEDED", attemptId: attempt.attemptId, result });
|
|
2416
|
+
emit("success", { state: "success", reason: null, txHash, quantity: attempt.quantity, chainId: cfg.chainId });
|
|
2417
|
+
},
|
|
2418
|
+
[clientFor, dispatch, emit]
|
|
2419
|
+
);
|
|
2420
|
+
const executeRelayAttempt = (0, import_react.useCallback)(
|
|
2421
|
+
async (attempt, walletRead, life, account2) => {
|
|
2422
|
+
const cfg = attempt.config;
|
|
2423
|
+
const adapter = adapterFor(cfg);
|
|
2424
|
+
const ctx = ctxFor(life, cfg);
|
|
2425
|
+
const testnet = isRelayTestnet(cfg.chainId);
|
|
2426
|
+
const origin = attempt.originChainId;
|
|
2427
|
+
const assertPayer = () => {
|
|
2428
|
+
if (!mounted.current || life.controller.signal.aborted || lifeRef.current !== life || attemptRef.current !== attempt) {
|
|
2429
|
+
throw new DOMException("Mint attempt cancelled", "AbortError");
|
|
2430
|
+
}
|
|
2431
|
+
const live = getAccount();
|
|
2432
|
+
if (!live || live.address.toLowerCase() !== attempt.payer.toLowerCase()) {
|
|
2433
|
+
throw new MintError("USER_REJECTED", "The connected wallet changed before the transaction was sent.");
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
assertPayer();
|
|
2437
|
+
if (account2.chainId !== origin) {
|
|
2438
|
+
dispatch({ type: "SWITCHING_CHAIN", attemptId: attempt.attemptId });
|
|
2439
|
+
await switchToChain(origin);
|
|
2440
|
+
}
|
|
2441
|
+
assertPayer();
|
|
2442
|
+
const plan = await adapter.buildPlan(ctx, attempt, walletRead);
|
|
2443
|
+
if (plan.approve) throw new MintError("BRIDGE_UNAVAILABLE", "Cross-chain mint supports native-priced drops only.");
|
|
2444
|
+
assertPayer();
|
|
2445
|
+
const quote = await getQuote({
|
|
2446
|
+
testnet,
|
|
2447
|
+
payer: attempt.payer,
|
|
2448
|
+
recipient: attempt.recipient,
|
|
2449
|
+
originChainId: origin,
|
|
2450
|
+
destinationChainId: cfg.chainId,
|
|
2451
|
+
tx: plan.mint,
|
|
2452
|
+
fresh: true,
|
|
2453
|
+
signal: life.controller.signal
|
|
2454
|
+
});
|
|
2455
|
+
assertPayer();
|
|
2456
|
+
const balance = await getConnectedBalance(attempt.payer, origin);
|
|
2457
|
+
if (balance < quote.amountIn.value + quote.originGas.value) {
|
|
2458
|
+
const s = shortOf(origin);
|
|
2459
|
+
throw new MintError("INSUFFICIENT_FUNDS", `Not enough ${s.symbol} on ${s.name}. Pick another network to pay from.`);
|
|
2460
|
+
}
|
|
2461
|
+
assertPayer();
|
|
2462
|
+
const item = quote.items[0];
|
|
2463
|
+
const key = outstandingKey(cfg, attempt.payer);
|
|
2464
|
+
const wallet2 = getWalletClient(origin);
|
|
2465
|
+
assertPayer();
|
|
2466
|
+
if (!claimDeposit(key)) {
|
|
2467
|
+
throw new MintError("BRIDGE_UNAVAILABLE", "A cross-chain payment for this drop is already in progress. Try again once it finishes.");
|
|
2468
|
+
}
|
|
2469
|
+
let out;
|
|
2470
|
+
try {
|
|
2471
|
+
dispatch({ type: "MINT_PROMPTED", attemptId: attempt.attemptId });
|
|
2472
|
+
const send = wallet2;
|
|
2473
|
+
const originTxHash2 = await send.sendTransaction({
|
|
2474
|
+
account: attempt.payer,
|
|
2475
|
+
to: item.to,
|
|
2476
|
+
data: item.data,
|
|
2477
|
+
value: item.value,
|
|
2478
|
+
...item.gas !== null && item.gas > 0n ? { gas: item.gas } : {},
|
|
2479
|
+
chain: getChain(origin)
|
|
2480
|
+
});
|
|
2481
|
+
out = { requestId: quote.requestId, testnet, originChainId: origin, originTxHash: originTxHash2, attempt };
|
|
2482
|
+
setOutstanding(key, out);
|
|
2483
|
+
} finally {
|
|
2484
|
+
releaseDeposit(key);
|
|
2485
|
+
}
|
|
2486
|
+
const { originTxHash } = out;
|
|
2487
|
+
autoResumedRef.current.add(key);
|
|
2488
|
+
dispatch({ type: "TX_SUBMITTED", attemptId: attempt.attemptId });
|
|
2489
|
+
emit("tx-submitted", { state: "confirming", reason: null, txHash: originTxHash, quantity: attempt.quantity, chainId: cfg.chainId });
|
|
2490
|
+
const deposit = await waitForDeposit(origin, originTxHash);
|
|
2491
|
+
if (deposit === "reverted") {
|
|
2492
|
+
clearOutstanding(key);
|
|
2493
|
+
throw new MintError("TX_REVERTED", `The payment transaction reverted (${originTxHash}).`);
|
|
2494
|
+
}
|
|
2495
|
+
await finishRelay(attempt, out, life);
|
|
2496
|
+
},
|
|
2497
|
+
[ctxFor, dispatch, emit, finishRelay]
|
|
2498
|
+
);
|
|
2499
|
+
const resumeOutstanding = (0, import_react.useCallback)(
|
|
2500
|
+
async (out, life) => {
|
|
2501
|
+
const attempt = Object.freeze({ ...out.attempt, attemptId: ++attemptIdRef.current });
|
|
2502
|
+
attemptRef.current = attempt;
|
|
2503
|
+
dispatch({ type: "ATTEMPT_STARTED", attempt, resume: true });
|
|
2504
|
+
dispatch({ type: "TX_SUBMITTED", attemptId: attempt.attemptId });
|
|
2505
|
+
emit("attempt", { state: "confirming", reason: null, quantity: attempt.quantity, chainId: attempt.chainId });
|
|
2506
|
+
try {
|
|
2507
|
+
await finishRelay(attempt, out, life);
|
|
2508
|
+
} catch (e) {
|
|
2509
|
+
if (!mounted.current || life.controller.signal.aborted || lifeRef.current !== life) return;
|
|
2510
|
+
const { reason, message } = toReason(e);
|
|
2511
|
+
dispatch({ type: "ATTEMPT_FAILED", attemptId: attempt.attemptId, reason, message });
|
|
2512
|
+
emit("error", { quantity: attempt.quantity, chainId: attempt.chainId });
|
|
2513
|
+
} finally {
|
|
2514
|
+
attemptRef.current = null;
|
|
2515
|
+
}
|
|
2516
|
+
},
|
|
2517
|
+
[dispatch, emit, finishRelay]
|
|
2518
|
+
);
|
|
2036
2519
|
const runMint = (0, import_react.useCallback)(async () => {
|
|
2037
2520
|
if (!mounted.current) return;
|
|
2038
2521
|
if (attemptRef.current !== null) return;
|
|
@@ -2069,6 +2552,12 @@ function useMint(options) {
|
|
|
2069
2552
|
}
|
|
2070
2553
|
return;
|
|
2071
2554
|
}
|
|
2555
|
+
const payer = s.account?.address;
|
|
2556
|
+
const out = payer ? getOutstanding(outstandingKey(propsRef.current.config, payer)) : null;
|
|
2557
|
+
if (out && lifeRef.current) {
|
|
2558
|
+
await resumeOutstanding(out, lifeRef.current);
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2072
2561
|
if (s.name === "success" || s.name === "error") {
|
|
2073
2562
|
const life2 = lifeRef.current;
|
|
2074
2563
|
const gen = ++genRef.current;
|
|
@@ -2084,6 +2573,7 @@ function useMint(options) {
|
|
|
2084
2573
|
const life = lifeRef.current;
|
|
2085
2574
|
if (!account2 || !walletRead || !life) return;
|
|
2086
2575
|
const cfg = propsRef.current.config;
|
|
2576
|
+
const relayRoute = s.route?.kind === "relay" ? s.route : null;
|
|
2087
2577
|
const attempt = Object.freeze({
|
|
2088
2578
|
attemptId: ++attemptIdRef.current,
|
|
2089
2579
|
config: structuredClone(cfg),
|
|
@@ -2091,13 +2581,15 @@ function useMint(options) {
|
|
|
2091
2581
|
recipient: propsRef.current.recipient ?? account2.address,
|
|
2092
2582
|
quantity: s.quantity,
|
|
2093
2583
|
chainId: cfg.chainId,
|
|
2094
|
-
tokenContract: tokenContractOf(cfg)
|
|
2584
|
+
tokenContract: tokenContractOf(cfg),
|
|
2585
|
+
originChainId: relayRoute ? relayRoute.originChainId : cfg.chainId
|
|
2095
2586
|
});
|
|
2096
2587
|
attemptRef.current = attempt;
|
|
2097
2588
|
dispatch({ type: "ATTEMPT_STARTED", attempt });
|
|
2098
2589
|
emit("attempt", { state: "live", reason: null, quantity: attempt.quantity, chainId: attempt.chainId });
|
|
2099
2590
|
try {
|
|
2100
|
-
await
|
|
2591
|
+
if (relayRoute) await executeRelayAttempt(attempt, walletRead, life, account2);
|
|
2592
|
+
else await executeAttempt(attempt, walletRead, life, account2);
|
|
2101
2593
|
} catch (e) {
|
|
2102
2594
|
if (!mounted.current || life.controller.signal.aborted || lifeRef.current !== life) return;
|
|
2103
2595
|
const { reason, message } = toReason(e);
|
|
@@ -2106,7 +2598,7 @@ function useMint(options) {
|
|
|
2106
2598
|
} finally {
|
|
2107
2599
|
attemptRef.current = null;
|
|
2108
2600
|
}
|
|
2109
|
-
}, [dispatch, emit, readDrop2, executeAttempt, refreshNow]);
|
|
2601
|
+
}, [dispatch, emit, readDrop2, executeAttempt, executeRelayAttempt, resumeOutstanding, refreshNow]);
|
|
2110
2602
|
const mint = (0, import_react.useCallback)(() => {
|
|
2111
2603
|
void runMint();
|
|
2112
2604
|
}, [runMint]);
|
|
@@ -2141,9 +2633,10 @@ function useMint(options) {
|
|
|
2141
2633
|
pushAccountRef.current?.(getAccount());
|
|
2142
2634
|
}, [recipient, machine.attempt, isBusy]);
|
|
2143
2635
|
const projectId = connect?.walletConnectProjectId;
|
|
2636
|
+
const widgetChains = [config.chainId, ...payChainsOf(config, options.crossChain).filter((id) => id !== config.chainId)].join(",");
|
|
2144
2637
|
(0, import_react.useEffect)(() => {
|
|
2145
2638
|
let alive = true;
|
|
2146
|
-
const loading = loadConnectWidget({ chainIds:
|
|
2639
|
+
const loading = loadConnectWidget({ chainIds: widgetChains.split(",").map(Number), ...projectId ? { walletConnectProjectId: projectId } : {} });
|
|
2147
2640
|
widgetLoadRef.current = loading;
|
|
2148
2641
|
loading.then(
|
|
2149
2642
|
() => {
|
|
@@ -2158,7 +2651,7 @@ function useMint(options) {
|
|
|
2158
2651
|
return () => {
|
|
2159
2652
|
alive = false;
|
|
2160
2653
|
};
|
|
2161
|
-
}, [
|
|
2654
|
+
}, [widgetChains, projectId, widgetRetry]);
|
|
2162
2655
|
(0, import_react.useEffect)(() => {
|
|
2163
2656
|
if (!mounted.current) return;
|
|
2164
2657
|
if (isBusy()) return;
|
|
@@ -2175,6 +2668,22 @@ function useMint(options) {
|
|
|
2175
2668
|
void refreshNow();
|
|
2176
2669
|
}
|
|
2177
2670
|
});
|
|
2671
|
+
const resumeKey = machine.account ? outstandingKey(config, machine.account.address) : null;
|
|
2672
|
+
(0, import_react.useEffect)(() => {
|
|
2673
|
+
if (!resumeKey || autoResumedRef.current.has(resumeKey) || attemptRef.current !== null || isBusy()) return;
|
|
2674
|
+
const out = getOutstanding(resumeKey);
|
|
2675
|
+
const life = lifeRef.current;
|
|
2676
|
+
if (!out || !life) return;
|
|
2677
|
+
autoResumedRef.current.add(resumeKey);
|
|
2678
|
+
void resumeOutstanding(out, life);
|
|
2679
|
+
}, [resumeKey, machine.attempt, isBusy, resumeOutstanding]);
|
|
2680
|
+
const routeOptionsKey = `${String(options.payChainId ?? "")}|${JSON.stringify(options.crossChain ?? null)}`;
|
|
2681
|
+
const previousRouteOptions = (0, import_react.useRef)(routeOptionsKey);
|
|
2682
|
+
(0, import_react.useEffect)(() => {
|
|
2683
|
+
if (previousRouteOptions.current === routeOptionsKey || isBusy()) return;
|
|
2684
|
+
previousRouteOptions.current = routeOptionsKey;
|
|
2685
|
+
if (stateRef.current.account) dispatch({ type: "ACCOUNT_CHANGED", account: stateRef.current.account });
|
|
2686
|
+
}, [routeOptionsKey, machine.attempt, dispatch, isBusy]);
|
|
2178
2687
|
const previousQuantity = (0, import_react.useRef)(machine.quantity);
|
|
2179
2688
|
(0, import_react.useEffect)(() => {
|
|
2180
2689
|
if (previousQuantity.current === machine.quantity || isBusy()) return;
|
|
@@ -2207,7 +2716,61 @@ function useMint(options) {
|
|
|
2207
2716
|
});
|
|
2208
2717
|
if (!alive || life.controller.signal.aborted || lifeRef.current !== life || life.gen !== readGen) return;
|
|
2209
2718
|
const ineligible = read.ineligible?.reason === "SIMULATION_REVERTED" ? { ...read.ineligible, message: friendlyErrorMessage(new MintError("SIMULATION_REVERTED", read.ineligible.message)) } : read.ineligible;
|
|
2210
|
-
|
|
2719
|
+
const fullRead = { ...read, ineligible };
|
|
2720
|
+
const payChains = payChainsOf(cfg, propsRef.current.crossChain);
|
|
2721
|
+
const crossChain = payChains.length > 0 && ineligible === null && (adapter.supportsRelay?.(drop) ?? false);
|
|
2722
|
+
const routeChain = routeChainOf(payChains, account.chainId, propsRef.current.payChainId);
|
|
2723
|
+
const routePending = crossChain && routeChain !== cfg.chainId && payChains.includes(routeChain);
|
|
2724
|
+
dispatch({ type: "WALLET_LOADED", gen: walletGen, read: fullRead, crossChain, routePending });
|
|
2725
|
+
if (!crossChain) {
|
|
2726
|
+
setCrossChainInfo(null);
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
const recipientAddr = propsRef.current.recipient ?? account.address;
|
|
2730
|
+
const draft = {
|
|
2731
|
+
attemptId: 0,
|
|
2732
|
+
config: cfg,
|
|
2733
|
+
payer: account.address,
|
|
2734
|
+
recipient: recipientAddr,
|
|
2735
|
+
quantity: stateRef.current.quantity,
|
|
2736
|
+
chainId: cfg.chainId,
|
|
2737
|
+
tokenContract: tokenContractOf(cfg),
|
|
2738
|
+
originChainId: routeChain
|
|
2739
|
+
};
|
|
2740
|
+
const testnet = isRelayTestnet(cfg.chainId);
|
|
2741
|
+
let tx;
|
|
2742
|
+
try {
|
|
2743
|
+
tx = (await adapter.buildPlan(ctxFor(life, cfg), draft, fullRead)).mint;
|
|
2744
|
+
} catch {
|
|
2745
|
+
if (!alive) return;
|
|
2746
|
+
setCrossChainInfo(null);
|
|
2747
|
+
if (routePending) dispatch({ type: "ROUTE_LOADED", gen: walletGen, route: { kind: "direct", chainId: cfg.chainId }, short: null });
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
if (!alive || life.controller.signal.aborted || lifeRef.current !== life) return;
|
|
2751
|
+
setCrossChainInfo((prev) => {
|
|
2752
|
+
const same = prev && prev.info.dropChainId === cfg.chainId && prev.info.testnet === testnet && prev.info.tx.to === tx.to && prev.info.tx.data === tx.data && prev.info.tx.value === tx.value && prev.info.payer === account.address && prev.info.recipient === recipientAddr && prev.info.payChains.join() === payChains.join();
|
|
2753
|
+
return { gen: walletGen, info: same ? prev.info : { dropChainId: cfg.chainId, payChains, tx, payer: account.address, recipient: recipientAddr, testnet } };
|
|
2754
|
+
});
|
|
2755
|
+
if (!routePending) return;
|
|
2756
|
+
await new Promise((r) => setTimeout(r, ROUTE_DEBOUNCE_MS));
|
|
2757
|
+
if (!alive || life.controller.signal.aborted || lifeRef.current !== life) return;
|
|
2758
|
+
let route = { kind: "direct", chainId: cfg.chainId };
|
|
2759
|
+
let short = null;
|
|
2760
|
+
try {
|
|
2761
|
+
const quote = await getQuote({ testnet, payer: account.address, recipient: recipientAddr, originChainId: routeChain, destinationChainId: cfg.chainId, tx, signal: life.controller.signal });
|
|
2762
|
+
const relay = { kind: "relay", originChainId: routeChain, amountIn: quote.amountIn, relayFee: quote.relayFee, originGas: quote.originGas, quotedAt: quote.quotedAt };
|
|
2763
|
+
if (routeChain !== account.chainId) {
|
|
2764
|
+
route = relay;
|
|
2765
|
+
} else {
|
|
2766
|
+
const balance = await getConnectedBalance(account.address, routeChain);
|
|
2767
|
+
if (balance >= quote.amountIn.value + quote.originGas.value) route = relay;
|
|
2768
|
+
else short = shortOf(routeChain);
|
|
2769
|
+
}
|
|
2770
|
+
} catch {
|
|
2771
|
+
}
|
|
2772
|
+
if (!alive || life.controller.signal.aborted || lifeRef.current !== life) return;
|
|
2773
|
+
dispatch({ type: "ROUTE_LOADED", gen: walletGen, route: short ? null : route, short });
|
|
2211
2774
|
} catch (e) {
|
|
2212
2775
|
if (!alive || life.controller.signal.aborted || lifeRef.current !== life || life.gen !== readGen) return;
|
|
2213
2776
|
dispatch({ type: "WALLET_FAILED", gen: walletGen, ...errorReasonOf(e) });
|
|
@@ -2307,12 +2870,137 @@ function useMint(options) {
|
|
|
2307
2870
|
result: machine.result,
|
|
2308
2871
|
refresh,
|
|
2309
2872
|
// An attempt claimed by a click is not clickable, even while the reducer still reads 'live'.
|
|
2310
|
-
canAct: canAct({ ...machine, name: effective.name }) && machine.attempt === null
|
|
2873
|
+
canAct: canAct({ ...machine, name: effective.name }) && machine.attempt === null,
|
|
2874
|
+
// Short on the wallet chain is neither route: nothing can be paid until the collector picks another chain.
|
|
2875
|
+
route: machine.route ?? (machine.walletRead && machine.drop && !machine.routePending && machine.short === null ? { kind: "direct", chainId: machine.drop.chainId } : null),
|
|
2876
|
+
crossChain: machine.crossChain && machine.account && crossChainInfo?.gen === machine.walletGen ? crossChainInfo.info : null
|
|
2311
2877
|
};
|
|
2312
2878
|
}
|
|
2313
2879
|
|
|
2314
|
-
// src/react/
|
|
2880
|
+
// src/react/PayWithPicker.ts
|
|
2315
2881
|
var import_react2 = require("react");
|
|
2882
|
+
var import_viem10 = require("viem");
|
|
2883
|
+
var PICKER_REFRESH_MS = 2e4;
|
|
2884
|
+
var PICKER_DEBOUNCE_MS = 500;
|
|
2885
|
+
var RELAY_REASONS = /* @__PURE__ */ new Set(["BRIDGE_UNAVAILABLE", "BRIDGE_TIMEOUT", "BRIDGE_REFUNDED", "BRIDGE_FAILED"]);
|
|
2886
|
+
var money4 = (value, chainId) => {
|
|
2887
|
+
const c = getChain(chainId).nativeCurrency;
|
|
2888
|
+
return { value, decimals: c.decimals, symbol: c.symbol, formatted: (0, import_viem10.formatUnits)(value, c.decimals) };
|
|
2889
|
+
};
|
|
2890
|
+
function usePayOptions(mint, onSelect) {
|
|
2891
|
+
const info = mint.crossChain;
|
|
2892
|
+
const current = mint.wallet?.chainId ?? null;
|
|
2893
|
+
const [quotes, setQuotes] = (0, import_react2.useState)({});
|
|
2894
|
+
const [balance, setBalance] = (0, import_react2.useState)(null);
|
|
2895
|
+
const [tick, setTick] = (0, import_react2.useState)(0);
|
|
2896
|
+
(0, import_react2.useEffect)(() => {
|
|
2897
|
+
const t = setInterval(() => setTick((n) => n + 1), PICKER_REFRESH_MS);
|
|
2898
|
+
return () => clearInterval(t);
|
|
2899
|
+
}, []);
|
|
2900
|
+
const key = info ? `${info.tx.to}:${info.tx.value}:${info.tx.data}:${info.payer}:${info.recipient}:${info.payChains.join()}` : "";
|
|
2901
|
+
const [quotedKey, setQuotedKey] = (0, import_react2.useState)(key);
|
|
2902
|
+
if (quotedKey !== key) {
|
|
2903
|
+
setQuotedKey(key);
|
|
2904
|
+
setQuotes({});
|
|
2905
|
+
}
|
|
2906
|
+
(0, import_react2.useEffect)(() => {
|
|
2907
|
+
if (!info) return;
|
|
2908
|
+
const controller = new AbortController();
|
|
2909
|
+
const timer = setTimeout(() => {
|
|
2910
|
+
for (const chainId of info.payChains) {
|
|
2911
|
+
if (chainId === info.dropChainId) continue;
|
|
2912
|
+
getQuote({ testnet: info.testnet, payer: info.payer, recipient: info.recipient, originChainId: chainId, destinationChainId: info.dropChainId, tx: info.tx, signal: controller.signal }).then(
|
|
2913
|
+
(q) => {
|
|
2914
|
+
if (!controller.signal.aborted) setQuotes((prev) => ({ ...prev, [chainId]: { relayFee: q.relayFee, amountIn: q.amountIn } }));
|
|
2915
|
+
},
|
|
2916
|
+
(e) => {
|
|
2917
|
+
if (controller.signal.aborted || e?.reason === "RATE_LIMITED") return;
|
|
2918
|
+
setQuotes((prev) => ({ ...prev, [chainId]: "failed" }));
|
|
2919
|
+
}
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
}, PICKER_DEBOUNCE_MS);
|
|
2923
|
+
return () => {
|
|
2924
|
+
clearTimeout(timer);
|
|
2925
|
+
controller.abort();
|
|
2926
|
+
};
|
|
2927
|
+
}, [key, tick]);
|
|
2928
|
+
(0, import_react2.useEffect)(() => {
|
|
2929
|
+
if (!info || current === null || !mint.wallet) return;
|
|
2930
|
+
let alive = true;
|
|
2931
|
+
getConnectedBalance(mint.wallet.address, current).then(
|
|
2932
|
+
(value) => {
|
|
2933
|
+
if (alive) setBalance({ chainId: current, value });
|
|
2934
|
+
},
|
|
2935
|
+
() => {
|
|
2936
|
+
if (alive) setBalance(null);
|
|
2937
|
+
}
|
|
2938
|
+
);
|
|
2939
|
+
return () => {
|
|
2940
|
+
alive = false;
|
|
2941
|
+
};
|
|
2942
|
+
}, [current, mint.wallet?.address, key, tick]);
|
|
2943
|
+
const select = (0, import_react2.useCallback)(
|
|
2944
|
+
(chainId) => {
|
|
2945
|
+
switchToChain(chainId).then(
|
|
2946
|
+
() => onSelect?.(chainId, null),
|
|
2947
|
+
(e) => onSelect?.(chainId, e)
|
|
2948
|
+
);
|
|
2949
|
+
},
|
|
2950
|
+
[onSelect]
|
|
2951
|
+
);
|
|
2952
|
+
if (!info) return [];
|
|
2953
|
+
return info.payChains.filter((chainId) => chainId === info.dropChainId || quotes[chainId] !== "failed").map((chainId) => {
|
|
2954
|
+
const q = quotes[chainId];
|
|
2955
|
+
const isDropChain = chainId === info.dropChainId;
|
|
2956
|
+
const quoted = q && q !== "failed" ? q : null;
|
|
2957
|
+
return {
|
|
2958
|
+
chainId,
|
|
2959
|
+
name: chainName(chainId),
|
|
2960
|
+
selected: chainId === current,
|
|
2961
|
+
isDropChain,
|
|
2962
|
+
relayFee: isDropChain ? null : quoted?.relayFee ?? null,
|
|
2963
|
+
amountIn: isDropChain ? null : quoted?.amountIn ?? null,
|
|
2964
|
+
balance: balance && balance.chainId === chainId && chainId === current ? money4(balance.value, chainId) : null,
|
|
2965
|
+
loading: !isDropChain && q === void 0,
|
|
2966
|
+
select: () => select(chainId)
|
|
2967
|
+
};
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
function defaultRow(row) {
|
|
2971
|
+
const fee = row.isDropChain ? "None" : row.loading ? "\u2026" : row.relayFee ? `${row.relayFee.formatted} ${row.relayFee.symbol}` : "--";
|
|
2972
|
+
const balance = row.balance ? ` \xB7 ${row.balance.formatted} ${row.balance.symbol}` : "";
|
|
2973
|
+
return `${row.name}${balance} \xB7 fee ${fee}`;
|
|
2974
|
+
}
|
|
2975
|
+
function PayWithPicker(props) {
|
|
2976
|
+
const rows = usePayOptions(props.mint, props.onSelect);
|
|
2977
|
+
if (rows.length === 0) return null;
|
|
2978
|
+
const open = props.mint.state === "insufficient-funds" || props.mint.reason !== null && RELAY_REASONS.has(props.mint.reason);
|
|
2979
|
+
return (0, import_react2.createElement)(
|
|
2980
|
+
"div",
|
|
2981
|
+
{ className: props.className, role: "group", "aria-label": "Pay with", "data-open": open ? "true" : "false" },
|
|
2982
|
+
rows.map(
|
|
2983
|
+
(row) => (0, import_react2.createElement)(
|
|
2984
|
+
"button",
|
|
2985
|
+
{
|
|
2986
|
+
key: row.chainId,
|
|
2987
|
+
type: "button",
|
|
2988
|
+
"data-chain": String(row.chainId),
|
|
2989
|
+
"data-selected": row.selected ? "true" : "false",
|
|
2990
|
+
"data-loading": row.loading ? "true" : "false",
|
|
2991
|
+
"aria-pressed": row.selected,
|
|
2992
|
+
// Only an idle button may change chains: a click in progress already chose its pay chain.
|
|
2993
|
+
disabled: row.selected || !(props.mint.canAct || props.mint.state === "insufficient-funds" || props.mint.state === "loading"),
|
|
2994
|
+
onClick: row.select
|
|
2995
|
+
},
|
|
2996
|
+
props.renderRow ? props.renderRow(row) : defaultRow(row)
|
|
2997
|
+
)
|
|
2998
|
+
)
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// src/react/MintButton.ts
|
|
3003
|
+
var import_react3 = require("react");
|
|
2316
3004
|
function defaultLabel(m) {
|
|
2317
3005
|
const symbol = m.total?.erc20?.symbol ?? m.total?.native.symbol ?? m.drop?.unitPrice.symbol ?? "";
|
|
2318
3006
|
switch (m.state) {
|
|
@@ -2333,6 +3021,10 @@ function defaultLabel(m) {
|
|
|
2333
3021
|
case "connecting":
|
|
2334
3022
|
return "Connecting\u2026";
|
|
2335
3023
|
case "live": {
|
|
3024
|
+
const route = m.route;
|
|
3025
|
+
if (route?.kind === "relay") {
|
|
3026
|
+
return `Mint \xB7 ${route.amountIn.formatted} ${route.amountIn.symbol} on ${chainName(route.originChainId)}`;
|
|
3027
|
+
}
|
|
2336
3028
|
const total = m.total;
|
|
2337
3029
|
if (!total) return "Mint";
|
|
2338
3030
|
const legs = total.erc20 ? [total.erc20, ...total.native.value > 0n ? [total.native] : []] : [total.native];
|
|
@@ -2364,7 +3056,7 @@ function MintButton(props) {
|
|
|
2364
3056
|
const m = useMint(options);
|
|
2365
3057
|
if (typeof children === "function") return children(m);
|
|
2366
3058
|
const label = labels?.[m.state] ?? defaultLabel(m);
|
|
2367
|
-
return (0,
|
|
3059
|
+
return (0, import_react3.createElement)(
|
|
2368
3060
|
"button",
|
|
2369
3061
|
{
|
|
2370
3062
|
type: "button",
|
|
@@ -2380,7 +3072,7 @@ function MintButton(props) {
|
|
|
2380
3072
|
}
|
|
2381
3073
|
|
|
2382
3074
|
// src/index.ts
|
|
2383
|
-
var VERSION = "0.
|
|
3075
|
+
var VERSION = "0.2.0";
|
|
2384
3076
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2385
3077
|
0 && (module.exports = {
|
|
2386
3078
|
CLICKABLE_STATES,
|
|
@@ -2388,9 +3080,11 @@ var VERSION = "0.1.0";
|
|
|
2388
3080
|
MINT_STATES,
|
|
2389
3081
|
MintButton,
|
|
2390
3082
|
MintError,
|
|
3083
|
+
PayWithPicker,
|
|
2391
3084
|
REASON_CODES,
|
|
2392
3085
|
VERSION,
|
|
2393
3086
|
isMintError,
|
|
2394
|
-
useMint
|
|
3087
|
+
useMint,
|
|
3088
|
+
usePayOptions
|
|
2395
3089
|
});
|
|
2396
3090
|
//# sourceMappingURL=index.cjs.map
|