@manifoldxyz/dropkit 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/cart.cjs +1832 -0
- package/dist/cart.cjs.map +1 -0
- package/dist/cart.d.cts +216 -0
- package/dist/cart.d.ts +216 -0
- package/dist/cart.js +1832 -0
- package/dist/cart.js.map +1 -0
- package/dist/chunk-3PA74M2L.js +1400 -0
- package/dist/chunk-3PA74M2L.js.map +1 -0
- package/dist/chunk-OO6QQT3M.cjs +1400 -0
- package/dist/chunk-OO6QQT3M.cjs.map +1 -0
- package/dist/index.cjs +351 -1621
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -194
- package/dist/index.d.ts +5 -194
- package/dist/index.js +78 -1334
- package/dist/index.js.map +1 -1
- package/dist/types-CFLoS6p4.d.cts +194 -0
- package/dist/types-CFLoS6p4.d.ts +194 -0
- package/package.json +11 -1
package/dist/index.js
CHANGED
|
@@ -1,394 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLICKABLE_STATES,
|
|
3
|
+
IN_FLIGHT_STATES,
|
|
4
|
+
MAX_QUANTITY,
|
|
5
|
+
MINT_STATES,
|
|
6
|
+
MintError,
|
|
7
|
+
REASON_CODES,
|
|
8
|
+
UNSUPPORTED_REASONS,
|
|
9
|
+
canAct,
|
|
10
|
+
chainName,
|
|
11
|
+
createReadClient,
|
|
12
|
+
friendlyErrorMessage,
|
|
13
|
+
getAccount,
|
|
14
|
+
getChain,
|
|
15
|
+
getConnectedBalance,
|
|
16
|
+
getWalletClient,
|
|
17
|
+
initialState,
|
|
18
|
+
isMintError,
|
|
19
|
+
loadConnectWidget,
|
|
20
|
+
manifoldClaimAdapter,
|
|
21
|
+
openConnectModal,
|
|
22
|
+
parseMintReceipt,
|
|
23
|
+
reducer,
|
|
24
|
+
selectMaxQuantity,
|
|
25
|
+
selectTotal,
|
|
26
|
+
subscribeAccount,
|
|
27
|
+
switchToChain,
|
|
28
|
+
toReason,
|
|
29
|
+
waitForDeposit
|
|
30
|
+
} from "./chunk-3PA74M2L.js";
|
|
31
|
+
|
|
1
32
|
// src/react/useMint.ts
|
|
2
33
|
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
|
|
3
|
-
import { BaseError as
|
|
4
|
-
|
|
5
|
-
// src/core/rpc/index.ts
|
|
6
|
-
import { createPublicClient, http } from "viem";
|
|
7
|
-
import { apeChain, base, baseSepolia, mainnet, optimism, sepolia, shape } from "viem/chains";
|
|
8
|
-
|
|
9
|
-
// src/core/errors.ts
|
|
10
|
-
var UNSUPPORTED_REASONS = [
|
|
11
|
-
"UNKNOWN_EXTENSION",
|
|
12
|
-
"AMBIGUOUS_EXTENSION",
|
|
13
|
-
"CLAIM_NOT_FOUND",
|
|
14
|
-
"SIGNATURE_CLAIM",
|
|
15
|
-
"INVALID_CONFIG",
|
|
16
|
-
"CONNECT_WIDGET_TOO_OLD"
|
|
17
|
-
];
|
|
18
|
-
var INELIGIBLE_REASONS = ["NOT_ON_ALLOWLIST", "ALLOWLIST_USED", "WALLET_LIMIT", "SIMULATION_REVERTED"];
|
|
19
|
-
var ERROR_REASONS = [
|
|
20
|
-
"USER_REJECTED",
|
|
21
|
-
"TX_REVERTED",
|
|
22
|
-
"RATE_LIMITED",
|
|
23
|
-
"RPC_ERROR",
|
|
24
|
-
"CHAIN_SWITCH_FAILED",
|
|
25
|
-
"ALLOWLIST_UNAVAILABLE",
|
|
26
|
-
// Relay cross-chain mint (spec relay-cross-chain-mint #execute-click).
|
|
27
|
-
"BRIDGE_UNAVAILABLE",
|
|
28
|
-
"BRIDGE_REFUNDED",
|
|
29
|
-
"BRIDGE_FAILED",
|
|
30
|
-
"BRIDGE_TIMEOUT"
|
|
31
|
-
];
|
|
32
|
-
var FUNDS_REASONS = ["INSUFFICIENT_FUNDS"];
|
|
33
|
-
var REASON_CODES = [
|
|
34
|
-
...UNSUPPORTED_REASONS,
|
|
35
|
-
...INELIGIBLE_REASONS,
|
|
36
|
-
...ERROR_REASONS,
|
|
37
|
-
...FUNDS_REASONS
|
|
38
|
-
];
|
|
39
|
-
var MintError = class extends Error {
|
|
40
|
-
reason;
|
|
41
|
-
/** INVALID_CONFIG only: dotted path of the bad field, e.g. "mint.args[1]" or "price". */
|
|
42
|
-
field;
|
|
43
|
-
/** 'relay': the message is Relay's decoded destination revert, shown as-is. */
|
|
44
|
-
domain;
|
|
45
|
-
constructor(reason, message, opts) {
|
|
46
|
-
super(message, opts?.cause === void 0 ? void 0 : { cause: opts.cause });
|
|
47
|
-
this.name = "MintError";
|
|
48
|
-
this.reason = reason;
|
|
49
|
-
if (opts?.field !== void 0) this.field = opts.field;
|
|
50
|
-
if (opts?.domain !== void 0) this.domain = opts.domain;
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
function isMintError(e) {
|
|
54
|
-
return e instanceof MintError;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/core/rpc/index.ts
|
|
58
|
-
var BRIDGE_URLS = {
|
|
59
|
-
1: "https://bridge.manifoldxyz.dev/rpc",
|
|
60
|
-
11155111: "https://sepolia.bridge.manifoldxyz.dev/rpc",
|
|
61
|
-
8453: "https://base.bridge.manifoldxyz.dev/rpc",
|
|
62
|
-
10: "https://optimism.bridge.manifoldxyz.dev/rpc",
|
|
63
|
-
360: "https://shape.bridge.manifoldxyz.dev/rpc",
|
|
64
|
-
33139: "https://apechain.bridge.manifoldxyz.dev/rpc"
|
|
65
|
-
};
|
|
66
|
-
var CHAINS = {
|
|
67
|
-
[mainnet.id]: mainnet,
|
|
68
|
-
[sepolia.id]: sepolia,
|
|
69
|
-
[base.id]: base,
|
|
70
|
-
[optimism.id]: optimism,
|
|
71
|
-
[shape.id]: shape,
|
|
72
|
-
[apeChain.id]: apeChain,
|
|
73
|
-
// Relay pay chain for Sepolia drops only: never a drop chain, no Manifold bridge.
|
|
74
|
-
[baseSepolia.id]: baseSepolia
|
|
75
|
-
};
|
|
76
|
-
var CHAIN_NAMES = {
|
|
77
|
-
1: "Ethereum",
|
|
78
|
-
8453: "Base",
|
|
79
|
-
10: "Optimism",
|
|
80
|
-
360: "Shape",
|
|
81
|
-
33139: "ApeChain",
|
|
82
|
-
11155111: "Sepolia",
|
|
83
|
-
84532: "Base Sepolia"
|
|
84
|
-
};
|
|
85
|
-
function chainName(chainId) {
|
|
86
|
-
return CHAIN_NAMES[chainId] ?? CHAINS[chainId]?.name ?? `chain ${String(chainId)}`;
|
|
87
|
-
}
|
|
88
|
-
var TRANSPORT_TIMEOUT_MS = 8e3;
|
|
89
|
-
function getChain(chainId) {
|
|
90
|
-
const chain = CHAINS[chainId];
|
|
91
|
-
if (!chain) {
|
|
92
|
-
throw new MintError("INVALID_CONFIG", `chainId ${String(chainId)} is not supported`, { field: "chainId" });
|
|
93
|
-
}
|
|
94
|
-
return chain;
|
|
95
|
-
}
|
|
96
|
-
var TERMINAL_CODES = /* @__PURE__ */ new Set([3, -32602, -32003, 4001, 4100, 4200, 4900, 4901, 4902]);
|
|
97
|
-
var FALLBACK_CODES = /* @__PURE__ */ new Set([-32700, -32600, -32601, -32001, -32002, -32004, -32005, 429]);
|
|
98
|
-
var EXECUTION_WORDING = /execution reverted|execution error|\breverted?\b|out of gas|invalid opcode|insufficient funds|nonce too low|gas required exceeds/i;
|
|
99
|
-
var UPSTREAM_WORDING = /\btimeout\b|\btimed out\b|\btoo many requests\b|\brate limit\b|\brequest limit reached\b|\b(?:exceeded.*capacity|capacity.*exceeded)\b|\bheader not found\b|\binternal (?:json-rpc )?(?:server )?error\b|\bnon-200 status code:\s*['"]?503\b/i;
|
|
100
|
-
var TRANSPORT_ERROR_NAMES = /* @__PURE__ */ new Set(["HttpRequestError", "TimeoutError", "SocketClosedError", "WebSocketRequestError"]);
|
|
101
|
-
function carriesExecutionEvidence(value, seen = /* @__PURE__ */ new Set()) {
|
|
102
|
-
if (typeof value === "string") return /^(?:Reverted\s+)?0x(?:[a-f\d]{2})*$/i.test(value) || EXECUTION_WORDING.test(value);
|
|
103
|
-
if (!value || typeof value !== "object" || seen.has(value)) return false;
|
|
104
|
-
seen.add(value);
|
|
105
|
-
const e = value;
|
|
106
|
-
if (typeof e.code === "number" && TERMINAL_CODES.has(e.code)) return true;
|
|
107
|
-
if (typeof e.message === "string" && EXECUTION_WORDING.test(e.message)) return true;
|
|
108
|
-
return ["data", "error", "originalError", "cause", "result"].some((key) => carriesExecutionEvidence(e[key], seen));
|
|
109
|
-
}
|
|
110
|
-
function shouldFallback(err) {
|
|
111
|
-
let retry = false;
|
|
112
|
-
const seen = /* @__PURE__ */ new Set();
|
|
113
|
-
for (let e = err; e && typeof e === "object" && !seen.has(e); ) {
|
|
114
|
-
seen.add(e);
|
|
115
|
-
const error = e;
|
|
116
|
-
const message = typeof error.details === "string" ? error.details : typeof error.shortMessage !== "string" ? error.message : "";
|
|
117
|
-
if (typeof message === "string" && EXECUTION_WORDING.test(message)) return false;
|
|
118
|
-
if (carriesExecutionEvidence(error.data)) return false;
|
|
119
|
-
if (typeof error.code === "number" && TERMINAL_CODES.has(error.code)) return false;
|
|
120
|
-
if (error.name === "ContractFunctionRevertedError" || error.name === "ExecutionRevertedError") return false;
|
|
121
|
-
if (typeof error.name === "string" && TRANSPORT_ERROR_NAMES.has(error.name)) retry = true;
|
|
122
|
-
if (typeof error.code === "number" && FALLBACK_CODES.has(error.code)) retry = true;
|
|
123
|
-
if (typeof message === "string" && UPSTREAM_WORDING.test(message)) retry = true;
|
|
124
|
-
e = error.cause;
|
|
125
|
-
}
|
|
126
|
-
return retry;
|
|
127
|
-
}
|
|
128
|
-
function createReadClient(opts) {
|
|
129
|
-
const chain = getChain(opts.chainId);
|
|
130
|
-
const urls = [opts.rpcUrls?.[opts.chainId], opts.useBridge ? BRIDGE_URLS[opts.chainId] : void 0, chain.rpcUrls.default.http[0]].filter(
|
|
131
|
-
(u, i, all) => !!u && all.indexOf(u) === i
|
|
132
|
-
);
|
|
133
|
-
const transport = ({ chain: c, pollingInterval }) => {
|
|
134
|
-
const transports = urls.map((url) => http(url, { timeout: TRANSPORT_TIMEOUT_MS, retryCount: 0 })({ chain: c, pollingInterval, retryCount: 0, timeout: TRANSPORT_TIMEOUT_MS }));
|
|
135
|
-
const request = (async (args) => {
|
|
136
|
-
let last;
|
|
137
|
-
for (const t of transports) {
|
|
138
|
-
try {
|
|
139
|
-
return await t.request(args);
|
|
140
|
-
} catch (err) {
|
|
141
|
-
if (!shouldFallback(err)) throw err;
|
|
142
|
-
last = err;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
console.warn(`dropkit: all RPC endpoints failed for chain ${String(opts.chainId)}; pass rpcUrls={{ ${String(opts.chainId)}: '<your RPC url>' }} to <MintButton>.`);
|
|
146
|
-
throw new MintError("RPC_ERROR", `All RPC endpoints failed for chain ${String(opts.chainId)}`, { cause: last });
|
|
147
|
-
});
|
|
148
|
-
return {
|
|
149
|
-
config: { key: "dropkit-fallback", name: "Dropkit fallback", request, retryCount: 0, retryDelay: 0, timeout: TRANSPORT_TIMEOUT_MS, type: "fallback" },
|
|
150
|
-
request,
|
|
151
|
-
value: {}
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
|
-
return createPublicClient({ chain, transport });
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/core/wallet/index.ts
|
|
158
|
-
import { createWalletClient, custom, getAddress, publicActions } from "viem";
|
|
159
|
-
var CONNECT_WIDGET_VERSION = "6.5.0";
|
|
160
|
-
var CONNECT_BASE_URL = `https://connect.manifoldxyz.dev/${CONNECT_WIDGET_VERSION}`;
|
|
161
|
-
var CONNECT_ID = "mint-button";
|
|
162
|
-
var ADDRESS_CHANGED = "ethereum-address-changed";
|
|
163
|
-
var CHAIN_CHANGED = "frontend-provider-chain-changed";
|
|
164
|
-
var LOADER_KEY = "__manifoldMintConnectLoader";
|
|
165
|
-
var DEFAULT_WALLETCONNECT_PROJECT_ID = "a03ac1695f309dc8d756e7b4006e5e7d";
|
|
166
|
-
var STATE_KEY = "__manifoldMintConnectStateV1";
|
|
167
|
-
var win = () => window;
|
|
168
|
-
var pageState = () => win()[STATE_KEY] ??= {};
|
|
169
|
-
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 + ".";
|
|
170
|
-
var MANIFOLD_HOST = /(^|\.)(manifoldxyz\.dev|manifold\.xyz)$/;
|
|
171
|
-
function isManifoldConnectScript(src) {
|
|
172
|
-
try {
|
|
173
|
-
const url = new URL(src, document.baseURI);
|
|
174
|
-
return url.hostname === "connect.manifoldxyz.dev" || MANIFOLD_HOST.test(url.hostname) && /\/connect\.umd[^/]*\.js$/.test(url.pathname);
|
|
175
|
-
} catch {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function applyState(state) {
|
|
180
|
-
if (!state.div) return;
|
|
181
|
-
const network = state.networks.join(",");
|
|
182
|
-
if (state.div.dataset.network !== network) state.div.dataset.network = network;
|
|
183
|
-
const projectId = state.projectId ?? state.div.dataset.walletConnectProjectId ?? DEFAULT_WALLETCONNECT_PROJECT_ID;
|
|
184
|
-
if (state.div.dataset.walletConnectProjectId !== projectId) state.div.dataset.walletConnectProjectId = projectId;
|
|
185
|
-
}
|
|
186
|
-
function register(state, opts) {
|
|
187
|
-
for (const id2 of opts.chainIds) if (!state.networks.includes(id2)) state.networks.push(id2);
|
|
188
|
-
const id = opts.walletConnectProjectId;
|
|
189
|
-
if (id) {
|
|
190
|
-
if (!state.projectId) state.projectId = id;
|
|
191
|
-
else if (state.projectId !== id) {
|
|
192
|
-
console.warn(`dropkit: walletConnectProjectId "${id}" ignored; this page already uses "${state.projectId}" for the shared connect widget.`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
applyState(state);
|
|
196
|
-
}
|
|
197
|
-
function startLoad(state) {
|
|
198
|
-
const connect = win().ManifoldConnect;
|
|
199
|
-
if (connect?.capabilities?.includes("open")) return Promise.resolve(connect.ready).then(() => void 0);
|
|
200
|
-
const hasScript = Array.from(document.querySelectorAll("script[src]")).some((s) => isManifoldConnectScript(s.src));
|
|
201
|
-
if (hasScript || document.querySelector('[data-widget="m-connect"]')) {
|
|
202
|
-
return Promise.reject(new MintError("CONNECT_WIDGET_TOO_OLD", TOO_OLD));
|
|
203
|
-
}
|
|
204
|
-
return new Promise((resolve, reject) => {
|
|
205
|
-
const css = document.createElement("link");
|
|
206
|
-
css.rel = "stylesheet";
|
|
207
|
-
css.href = `${CONNECT_BASE_URL}/connect.css`;
|
|
208
|
-
document.head.appendChild(css);
|
|
209
|
-
state.nodes.push(css);
|
|
210
|
-
const div = document.createElement("div");
|
|
211
|
-
div.hidden = true;
|
|
212
|
-
div.style.display = "none";
|
|
213
|
-
div.dataset.widget = "m-connect";
|
|
214
|
-
div.dataset.connectId = CONNECT_ID;
|
|
215
|
-
div.dataset.autoReconnect = "true";
|
|
216
|
-
div.dataset.enableNetworkSwitching = "true";
|
|
217
|
-
state.div = div;
|
|
218
|
-
applyState(state);
|
|
219
|
-
document.body.appendChild(div);
|
|
220
|
-
state.nodes.push(div);
|
|
221
|
-
const script = document.createElement("script");
|
|
222
|
-
script.src = `${CONNECT_BASE_URL}/connect.umd.min.js`;
|
|
223
|
-
script.async = true;
|
|
224
|
-
script.addEventListener("load", () => {
|
|
225
|
-
const ready = win().ManifoldConnect?.ready;
|
|
226
|
-
if (!ready) return reject(new MintError("CONNECT_WIDGET_TOO_OLD", TOO_OLD));
|
|
227
|
-
Promise.resolve(ready).then(() => resolve(), reject);
|
|
228
|
-
});
|
|
229
|
-
script.addEventListener("error", () => reject(new MintError("RPC_ERROR", `Could not load the Manifold connect widget from ${CONNECT_BASE_URL}`)));
|
|
230
|
-
document.head.appendChild(script);
|
|
231
|
-
state.nodes.push(script);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
function loadConnectWidget(opts) {
|
|
235
|
-
const w = win();
|
|
236
|
-
const page = pageState();
|
|
237
|
-
const existing = w[LOADER_KEY];
|
|
238
|
-
if (existing) {
|
|
239
|
-
if (page.load?.promise !== existing) {
|
|
240
|
-
const div = document.querySelector(`[data-widget="m-connect"][data-connect-id="${CONNECT_ID}"]`) ?? void 0;
|
|
241
|
-
page.load = {
|
|
242
|
-
promise: existing,
|
|
243
|
-
networks: div?.dataset.network?.split(",").filter(Boolean).map(Number) ?? [],
|
|
244
|
-
// The default id is a placeholder, not a choice: a caller's real id may still replace it.
|
|
245
|
-
projectId: div?.dataset.walletConnectProjectId === DEFAULT_WALLETCONNECT_PROJECT_ID ? void 0 : div?.dataset.walletConnectProjectId,
|
|
246
|
-
div,
|
|
247
|
-
nodes: []
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
register(page.load, opts);
|
|
251
|
-
return existing;
|
|
252
|
-
}
|
|
253
|
-
let resolve;
|
|
254
|
-
let reject;
|
|
255
|
-
const p = new Promise((res, rej) => {
|
|
256
|
-
resolve = res;
|
|
257
|
-
reject = rej;
|
|
258
|
-
});
|
|
259
|
-
const state = { promise: p, networks: [], nodes: [] };
|
|
260
|
-
page.load = state;
|
|
261
|
-
w[LOADER_KEY] = p;
|
|
262
|
-
p.catch(() => {
|
|
263
|
-
if (w[LOADER_KEY] === p) delete w[LOADER_KEY];
|
|
264
|
-
if (page.load === state) page.load = void 0;
|
|
265
|
-
for (const node of state.nodes) node.parentNode?.removeChild(node);
|
|
266
|
-
state.nodes.length = 0;
|
|
267
|
-
state.div = void 0;
|
|
268
|
-
});
|
|
269
|
-
register(state, opts);
|
|
270
|
-
try {
|
|
271
|
-
startLoad(state).then(resolve, reject);
|
|
272
|
-
} catch (error) {
|
|
273
|
-
reject(error);
|
|
274
|
-
}
|
|
275
|
-
return p;
|
|
276
|
-
}
|
|
277
|
-
function openConnectModal() {
|
|
278
|
-
const page = pageState();
|
|
279
|
-
if (page.pendingModal) return page.pendingModal;
|
|
280
|
-
const p = new Promise((resolve) => {
|
|
281
|
-
const onClosed = (e) => {
|
|
282
|
-
const detail = e.detail ?? {};
|
|
283
|
-
if (detail.connectId !== void 0 && detail.connectId !== CONNECT_ID) return;
|
|
284
|
-
window.removeEventListener("m-connect:closed", onClosed);
|
|
285
|
-
page.pendingModal = void 0;
|
|
286
|
-
resolve(detail.reason === "connected" ? "connected" : "dismissed");
|
|
287
|
-
};
|
|
288
|
-
window.addEventListener("m-connect:closed", onClosed);
|
|
289
|
-
});
|
|
290
|
-
page.pendingModal = p;
|
|
291
|
-
window.dispatchEvent(new CustomEvent("m-connect:open", { detail: { connectId: CONNECT_ID } }));
|
|
292
|
-
return p;
|
|
293
|
-
}
|
|
294
|
-
function getAccount() {
|
|
295
|
-
const p = win().ManifoldEthereumProvider;
|
|
296
|
-
const address = p?.selectedAddress?.();
|
|
297
|
-
let chainId = p?.chainId?.();
|
|
298
|
-
if (chainId === void 0 || chainId === null) {
|
|
299
|
-
try {
|
|
300
|
-
chainId = p?.browserChainId?.();
|
|
301
|
-
} catch {
|
|
302
|
-
chainId = void 0;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (!address || chainId === void 0 || chainId === null) return null;
|
|
306
|
-
return { address: getAddress(address), chainId: Number(chainId) };
|
|
307
|
-
}
|
|
308
|
-
function subscribeAccount(cb) {
|
|
309
|
-
const handler = () => cb(getAccount());
|
|
310
|
-
window.addEventListener(ADDRESS_CHANGED, handler);
|
|
311
|
-
window.addEventListener(CHAIN_CHANGED, handler);
|
|
312
|
-
return () => {
|
|
313
|
-
window.removeEventListener(ADDRESS_CHANGED, handler);
|
|
314
|
-
window.removeEventListener(CHAIN_CHANGED, handler);
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function eip1193() {
|
|
318
|
-
const p = win().ManifoldEthereumProvider;
|
|
319
|
-
const provider = (p?.provider?.(true) ?? p?.browserProvider?.())?.provider;
|
|
320
|
-
if (!provider) throw new MintError("CHAIN_SWITCH_FAILED", "No wallet is connected.");
|
|
321
|
-
return provider;
|
|
322
|
-
}
|
|
323
|
-
function getWalletClient(chainId) {
|
|
324
|
-
const provider = eip1193();
|
|
325
|
-
const request = ((args) => provider.request(args));
|
|
326
|
-
return createWalletClient({ chain: getChain(chainId), transport: custom({ request }) });
|
|
327
|
-
}
|
|
328
|
-
async function waitForDeposit(chainId, hash, timeoutMs = 12e4) {
|
|
329
|
-
try {
|
|
330
|
-
const receipt = await getWalletClient(chainId).extend(publicActions).waitForTransactionReceipt({ hash, confirmations: 1, timeout: timeoutMs });
|
|
331
|
-
return receipt.status === "reverted" ? "reverted" : "mined";
|
|
332
|
-
} catch {
|
|
333
|
-
return "unknown";
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
var WALLET_READ_TIMEOUT = { ms: 1e4 };
|
|
337
|
-
async function getConnectedBalance(address, chainId) {
|
|
338
|
-
const provider = eip1193();
|
|
339
|
-
let timer;
|
|
340
|
-
const timeout = new Promise((_, reject) => {
|
|
341
|
-
timer = setTimeout(() => reject(new MintError("RPC_ERROR", "The wallet did not report a balance in time.")), WALLET_READ_TIMEOUT.ms);
|
|
342
|
-
});
|
|
343
|
-
try {
|
|
344
|
-
return await Promise.race([
|
|
345
|
-
(async () => {
|
|
346
|
-
const current = Number(await provider.request({ method: "eth_chainId" }));
|
|
347
|
-
if (current !== chainId) {
|
|
348
|
-
throw new MintError("CHAIN_SWITCH_FAILED", `The wallet is on chain ${String(current)}, not ${String(chainId)}.`);
|
|
349
|
-
}
|
|
350
|
-
const hex = await provider.request({ method: "eth_getBalance", params: [address, "latest"] });
|
|
351
|
-
return BigInt(hex);
|
|
352
|
-
})(),
|
|
353
|
-
timeout
|
|
354
|
-
]);
|
|
355
|
-
} finally {
|
|
356
|
-
clearTimeout(timer);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
var code = (e) => e?.code;
|
|
360
|
-
async function switchToChain(chainId) {
|
|
361
|
-
const chain = getChain(chainId);
|
|
362
|
-
const provider = eip1193();
|
|
363
|
-
const hexId = `0x${chainId.toString(16)}`;
|
|
364
|
-
const switchChain = () => provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexId }] });
|
|
365
|
-
try {
|
|
366
|
-
try {
|
|
367
|
-
await switchChain();
|
|
368
|
-
} catch (e) {
|
|
369
|
-
if (code(e) !== 4902) throw e;
|
|
370
|
-
await provider.request({
|
|
371
|
-
method: "wallet_addEthereumChain",
|
|
372
|
-
params: [
|
|
373
|
-
{
|
|
374
|
-
chainId: hexId,
|
|
375
|
-
chainName: chain.name,
|
|
376
|
-
nativeCurrency: chain.nativeCurrency,
|
|
377
|
-
rpcUrls: [...chain.rpcUrls.default.http],
|
|
378
|
-
...chain.blockExplorers ? { blockExplorerUrls: [chain.blockExplorers.default.url] } : {}
|
|
379
|
-
}
|
|
380
|
-
]
|
|
381
|
-
});
|
|
382
|
-
await switchChain();
|
|
383
|
-
}
|
|
384
|
-
} catch (e) {
|
|
385
|
-
if (code(e) === 4001) throw new MintError("USER_REJECTED", "You rejected the network switch.", { cause: e });
|
|
386
|
-
throw new MintError("CHAIN_SWITCH_FAILED", `Could not switch the wallet to chain ${String(chainId)}.`, { cause: e });
|
|
387
|
-
}
|
|
388
|
-
}
|
|
34
|
+
import { BaseError as BaseError2, ExecutionRevertedError, publicActions } from "viem";
|
|
389
35
|
|
|
390
36
|
// src/core/relay/client.ts
|
|
391
|
-
import { formatUnits, getAddress
|
|
37
|
+
import { formatUnits, getAddress, isHex, keccak256, zeroAddress } from "viem";
|
|
392
38
|
var RELAY_API = { mainnet: "https://api.relay.link", testnet: "https://api.testnets.relay.link" };
|
|
393
39
|
var MAINNET_PAY_CHAINS = [1, 8453, 10, 360, 33139];
|
|
394
40
|
var MAINNET_DROP_CHAINS = [1, 8453, 10, 360];
|
|
@@ -418,24 +64,24 @@ function moneyOf(amount, currency) {
|
|
|
418
64
|
}
|
|
419
65
|
function revertReason(errorData) {
|
|
420
66
|
if (typeof errorData !== "string") return "";
|
|
421
|
-
const
|
|
422
|
-
return
|
|
67
|
+
const text = errorData.replace(/^execution reverted:?\s*/i, "").trim();
|
|
68
|
+
return text === "execution reverted" ? "" : text;
|
|
423
69
|
}
|
|
424
70
|
function parseQuote(body, originChainId) {
|
|
425
71
|
const b = obj(body);
|
|
426
72
|
const steps = Array.isArray(b.steps) ? b.steps.map(obj) : [];
|
|
427
73
|
const unknownStep = () => new MintError("BRIDGE_UNAVAILABLE", "Relay returned a route this button cannot execute.");
|
|
428
74
|
if (steps.length !== 1 || steps[0].kind !== "transaction") throw unknownStep();
|
|
429
|
-
const
|
|
430
|
-
const rawItems = Array.isArray(
|
|
75
|
+
const step = steps[0];
|
|
76
|
+
const rawItems = Array.isArray(step.items) ? step.items.map(obj) : [];
|
|
431
77
|
if (rawItems.length !== 1) throw unknownStep();
|
|
432
78
|
const items = rawItems.map((item) => {
|
|
433
79
|
const d = obj(item.data);
|
|
434
80
|
const chainId = Number(d.chainId);
|
|
435
81
|
if (chainId !== originChainId || typeof d.to !== "string" || !isHex(d.data)) throw unknownStep();
|
|
436
|
-
return { chainId, to:
|
|
82
|
+
return { chainId, to: getAddress(d.to), data: d.data, value: big(d.value), gas: d.gas === void 0 ? null : big(d.gas) };
|
|
437
83
|
});
|
|
438
|
-
const requestId = isHex(
|
|
84
|
+
const requestId = isHex(step.requestId) ? step.requestId : isHex(b.requestId) ? b.requestId : null;
|
|
439
85
|
if (!requestId) throw unknownStep();
|
|
440
86
|
const details = obj(b.details);
|
|
441
87
|
const currencyIn = obj(details.currencyIn);
|
|
@@ -614,791 +260,22 @@ function claimDeposit(key) {
|
|
|
614
260
|
}
|
|
615
261
|
var releaseDeposit = (key) => void claimed.delete(key);
|
|
616
262
|
|
|
617
|
-
// src/core/adapters/
|
|
263
|
+
// src/core/adapters/contract/index.ts
|
|
618
264
|
import {
|
|
619
265
|
BaseError,
|
|
620
266
|
ContractFunctionRevertedError,
|
|
267
|
+
ContractFunctionZeroDataError,
|
|
268
|
+
InsufficientFundsError,
|
|
621
269
|
decodeErrorResult,
|
|
622
270
|
encodeFunctionData,
|
|
623
271
|
formatUnits as formatUnits2,
|
|
624
|
-
getAddress as
|
|
272
|
+
getAddress as getAddress2,
|
|
625
273
|
isAddress,
|
|
626
|
-
|
|
627
|
-
zeroAddress as zeroAddress2,
|
|
628
|
-
zeroHash
|
|
629
|
-
} from "viem";
|
|
630
|
-
|
|
631
|
-
// src/core/adapters/manifold-claim/abis.ts
|
|
632
|
-
import { parseAbi } from "viem";
|
|
633
|
-
var CLAIM_721_V5_ABI = parseAbi([
|
|
634
|
-
"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)"
|
|
635
|
-
]);
|
|
636
|
-
var CLAIM_1155_V5_ABI = parseAbi([
|
|
637
|
-
"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)"
|
|
638
|
-
]);
|
|
639
|
-
var CLAIM_721_V7_ABI = parseAbi([
|
|
640
|
-
"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)"
|
|
641
|
-
]);
|
|
642
|
-
var CLAIM_1155_V7_ABI = parseAbi([
|
|
643
|
-
"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)"
|
|
644
|
-
]);
|
|
645
|
-
var EXTENSION_COMMON_ABI = parseAbi([
|
|
646
|
-
"function MINT_FEE() view returns (uint256)",
|
|
647
|
-
"function MINT_FEE_MERKLE() view returns (uint256)",
|
|
648
|
-
"function getTotalMints(address minter, address creatorContractAddress, uint256 instanceId) view returns (uint32)",
|
|
649
|
-
"function checkMintIndices(address creatorContractAddress, uint256 instanceId, uint32[] mintIndices) view returns (bool[] minted)",
|
|
650
|
-
"function mintProxy(address creatorContractAddress, uint256 instanceId, uint16 mintCount, uint32[] mintIndices, bytes32[][] merkleProofs, address mintFor) payable"
|
|
651
|
-
]);
|
|
652
|
-
var EXTENSION_ERRORS_ABI = parseAbi([
|
|
653
|
-
"error Error(string message)",
|
|
654
|
-
"error Panic(uint256 code)",
|
|
655
|
-
"error ClaimInactive()",
|
|
656
|
-
"error ClaimNotInitialized()",
|
|
657
|
-
"error ExpiredSignature()",
|
|
658
|
-
"error FailedToTransfer()",
|
|
659
|
-
"error InvalidAirdrop()",
|
|
660
|
-
"error InvalidInput()",
|
|
661
|
-
"error InvalidInstance()",
|
|
662
|
-
"error InvalidSignature()",
|
|
663
|
-
"error InvalidStartDate()",
|
|
664
|
-
"error InvalidStorageProtocol()",
|
|
665
|
-
"error MustUseSignatureMinting()",
|
|
666
|
-
"error TokenDNE()",
|
|
667
|
-
"error TooManyRequested()"
|
|
668
|
-
]);
|
|
669
|
-
var CREATOR_ABI = parseAbi(["function getExtensions() view returns (address[])"]);
|
|
670
|
-
var ERC20_ABI = parseAbi([
|
|
671
|
-
"function symbol() view returns (string)",
|
|
672
|
-
"function decimals() view returns (uint8)",
|
|
673
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
674
|
-
"function allowance(address owner, address spender) view returns (uint256)",
|
|
675
|
-
"function approve(address spender, uint256 amount) returns (bool)"
|
|
676
|
-
]);
|
|
677
|
-
|
|
678
|
-
// src/core/adapters/manifold-claim/merkle.ts
|
|
679
|
-
var UINT32_MAX = 4294967295;
|
|
680
|
-
async function getJson(fetchFn, url, signal, what) {
|
|
681
|
-
let res;
|
|
682
|
-
try {
|
|
683
|
-
signal?.throwIfAborted();
|
|
684
|
-
res = await fetchFn(url, signal ? { signal } : void 0);
|
|
685
|
-
} catch (cause) {
|
|
686
|
-
throw new MintError("ALLOWLIST_UNAVAILABLE", `Could not reach the allowlist service (${what}).`, { cause });
|
|
687
|
-
}
|
|
688
|
-
if (res.status === 429) throw new MintError("RATE_LIMITED", "The allowlist service is rate limiting requests. Try again shortly.");
|
|
689
|
-
if (!res.ok) throw new MintError("ALLOWLIST_UNAVAILABLE", `The allowlist service has no data for this claim (${what}: HTTP ${res.status}).`);
|
|
690
|
-
try {
|
|
691
|
-
return await res.json();
|
|
692
|
-
} catch (cause) {
|
|
693
|
-
throw new MintError("ALLOWLIST_UNAVAILABLE", `The allowlist service returned an unreadable response (${what}).`, { cause });
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
var isRecord = (v) => typeof v === "object" && v !== null;
|
|
697
|
-
var scopeCaches = /* @__PURE__ */ new WeakMap();
|
|
698
|
-
function memo(cache2, key, signal, load) {
|
|
699
|
-
const hit = cache2.get(key);
|
|
700
|
-
if (hit) return hit;
|
|
701
|
-
const p = withSignal(load(), signal);
|
|
702
|
-
cache2.set(key, p);
|
|
703
|
-
p.catch(() => {
|
|
704
|
-
if (cache2.get(key) === p) cache2.delete(key);
|
|
705
|
-
});
|
|
706
|
-
return p;
|
|
707
|
-
}
|
|
708
|
-
function withSignal(p, signal) {
|
|
709
|
-
if (!signal) return p;
|
|
710
|
-
return new Promise((resolve, reject) => {
|
|
711
|
-
const onAbort = () => reject(new MintError("ALLOWLIST_UNAVAILABLE", "The allowlist request was cancelled.", { cause: signal.reason }));
|
|
712
|
-
p.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
713
|
-
if (signal.aborted) return onAbort();
|
|
714
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
async function loadInstance(fetchFn, api, instanceId, signal) {
|
|
718
|
-
const instance = await getJson(fetchFn, `${api}/public/instance/data?id=${instanceId}`, signal, "instance data");
|
|
719
|
-
const publicData = isRecord(instance) ? instance.publicData : void 0;
|
|
720
|
-
const allowlist = isRecord(publicData) ? publicData.instanceAllowlist : void 0;
|
|
721
|
-
const merkleTreeId = isRecord(allowlist) ? allowlist.merkleTreeId : void 0;
|
|
722
|
-
const appId = isRecord(instance) ? instance.appId : void 0;
|
|
723
|
-
if (typeof merkleTreeId !== "number" && typeof merkleTreeId !== "string" || typeof appId !== "number" && typeof appId !== "string") {
|
|
724
|
-
throw new MintError("ALLOWLIST_UNAVAILABLE", "The allowlist for this claim is not published yet.");
|
|
725
|
-
}
|
|
726
|
-
return { merkleTreeId, appId };
|
|
727
|
-
}
|
|
728
|
-
async function loadEntries(fetchFn, api, instanceId, recipient, scoped) {
|
|
729
|
-
const { signal } = scoped;
|
|
730
|
-
const { merkleTreeId, appId } = await memo(scoped.instances, `${api}|${instanceId}`, signal, () => loadInstance(fetchFn, api, instanceId, signal));
|
|
731
|
-
const url = `${api}/public/merkleTree/${encodeURIComponent(String(merkleTreeId))}/merkleInfo?address=${recipient}&appId=${encodeURIComponent(String(appId))}`;
|
|
732
|
-
const body = await getJson(fetchFn, url, signal, "merkle info");
|
|
733
|
-
if (!Array.isArray(body)) throw new MintError("ALLOWLIST_UNAVAILABLE", "The allowlist service returned an unexpected response.");
|
|
734
|
-
return body.map((row) => {
|
|
735
|
-
const proof = isRecord(row) ? row.merkleProof : void 0;
|
|
736
|
-
const value = isRecord(row) ? row.value : void 0;
|
|
737
|
-
if (!Array.isArray(proof) || !proof.every((p) => typeof p === "string" && /^0x[0-9a-fA-F]{64}$/.test(p)) || typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > UINT32_MAX) {
|
|
738
|
-
throw new MintError("ALLOWLIST_UNAVAILABLE", "The allowlist service returned an unexpected response.");
|
|
739
|
-
}
|
|
740
|
-
return { mintIndex: value, proof };
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
async function fetchMerkleEntries(args) {
|
|
744
|
-
if (args.signal?.aborted) {
|
|
745
|
-
throw new MintError("ALLOWLIST_UNAVAILABLE", "The allowlist request was cancelled.", { cause: args.signal.reason });
|
|
746
|
-
}
|
|
747
|
-
const fetchFn = args.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
748
|
-
const key = `${args.api}|${args.instanceId}|${args.recipient.toLowerCase()}`;
|
|
749
|
-
let scoped = scopeCaches.get(args.scope);
|
|
750
|
-
if (!scoped || scoped.signal?.aborted) {
|
|
751
|
-
scoped = { signal: args.signal, instances: /* @__PURE__ */ new Map(), entries: /* @__PURE__ */ new Map() };
|
|
752
|
-
scopeCaches.set(args.scope, scoped);
|
|
753
|
-
}
|
|
754
|
-
const cache2 = scoped;
|
|
755
|
-
const entries = await memo(cache2.entries, key, cache2.signal, () => loadEntries(fetchFn, args.api, args.instanceId, args.recipient, cache2));
|
|
756
|
-
return entries.slice();
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// src/core/adapters/manifold-claim/index.ts
|
|
760
|
-
var V6_721 = "0x1eb73fee2090fb1c20105d5ba887e3c3ba14a17e";
|
|
761
|
-
var V6_1155 = "0x04ba6cf3c5aa6d4946f5b7f7adf111012a9fac65";
|
|
762
|
-
var V7_721 = "0x23aa05a271debffaa3d75739af5581f744b326e4";
|
|
763
|
-
var V7_1155 = "0x26bbea7803dcac346d5f5f135b57cf2c752a02be";
|
|
764
|
-
var V8_721 = "0x5889e848b2423344f7969bc158100c07595c0137";
|
|
765
|
-
var V8_1155 = "0x88438f4d4b8516e758a319ad16924da60e1e56cc";
|
|
766
|
-
var v6 = (chainId) => [
|
|
767
|
-
{ chainId, address: V6_721, generation: "V6", spec: "721", abi: "Claim721V5Optimization" },
|
|
768
|
-
{ chainId, address: V6_1155, generation: "V6", spec: "1155", abi: "Claim1155V5Optimization" }
|
|
769
|
-
];
|
|
770
|
-
var v7 = (chainId) => [
|
|
771
|
-
{ chainId, address: V7_721, generation: "V7", spec: "721", abi: "Claim721V7" },
|
|
772
|
-
{ chainId, address: V7_1155, generation: "V7", spec: "1155", abi: "Claim1155V7" }
|
|
773
|
-
];
|
|
774
|
-
var EXTENSIONS = [
|
|
775
|
-
...v6(1),
|
|
776
|
-
...v7(1),
|
|
777
|
-
...v6(8453),
|
|
778
|
-
...v7(8453),
|
|
779
|
-
...v6(10),
|
|
780
|
-
...v7(10),
|
|
781
|
-
...v7(360),
|
|
782
|
-
{ chainId: 360, address: V8_1155, generation: "V8", spec: "1155", abi: "Claim1155V7" },
|
|
783
|
-
...v6(11155111),
|
|
784
|
-
...v7(11155111),
|
|
785
|
-
{ chainId: 11155111, address: V8_721, generation: "V8", spec: "721", abi: "Claim721V7" },
|
|
786
|
-
{ chainId: 11155111, address: V8_1155, generation: "V8", spec: "1155", abi: "Claim1155V7" }
|
|
787
|
-
];
|
|
788
|
-
function findExtension(chainId, address) {
|
|
789
|
-
const a = address.toLowerCase();
|
|
790
|
-
return EXTENSIONS.find((e) => e.chainId === chainId && e.address === a);
|
|
791
|
-
}
|
|
792
|
-
var MERKLE_API = "https://apps.api.manifoldxyz.dev";
|
|
793
|
-
var MAX_MINT_COUNT = 65535;
|
|
794
|
-
var GAS_UNITS_BASE = 100000n;
|
|
795
|
-
var GAS_UNITS_PER_TOKEN = 150000n;
|
|
796
|
-
var GAS_UNITS_APPROVE = 60000n;
|
|
797
|
-
var dropResolved = /* @__PURE__ */ new WeakMap();
|
|
798
|
-
var WALLET_DATA_BRAND = /* @__PURE__ */ Symbol("manifold-claim-wallet-data");
|
|
799
|
-
var isWalletData = (d) => typeof d === "object" && d !== null && d.brand === WALLET_DATA_BRAND;
|
|
800
|
-
var isAllowlist = (claim) => claim.merkleRoot !== zeroHash;
|
|
801
|
-
function isContractFailure(e) {
|
|
802
|
-
if (!(e instanceof BaseError)) return false;
|
|
803
|
-
return e.walk((x) => x instanceof ContractFunctionRevertedError || x.name === "ContractFunctionZeroDataError") !== null;
|
|
804
|
-
}
|
|
805
|
-
function toRpcError(e) {
|
|
806
|
-
if (e instanceof MintError) return e;
|
|
807
|
-
const msg = e instanceof BaseError ? e.shortMessage : e instanceof Error ? e.message : String(e);
|
|
808
|
-
return new MintError("RPC_ERROR", `RPC request failed: ${msg}`, { cause: e });
|
|
809
|
-
}
|
|
810
|
-
async function rpc(fn) {
|
|
811
|
-
try {
|
|
812
|
-
return await fn();
|
|
813
|
-
} catch (e) {
|
|
814
|
-
throw toRpcError(e);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
function revertData(e) {
|
|
818
|
-
const hit = e.walk((x) => {
|
|
819
|
-
const d2 = x.data;
|
|
820
|
-
return isHex2(d2) && d2.length >= 10 || typeof d2 === "object" && d2 !== null && isHex2(d2.data);
|
|
821
|
-
});
|
|
822
|
-
const d = hit?.data;
|
|
823
|
-
if (isHex2(d)) return d;
|
|
824
|
-
const inner = d?.data;
|
|
825
|
-
return isHex2(inner) ? inner : void 0;
|
|
826
|
-
}
|
|
827
|
-
function decodeRevert(e) {
|
|
828
|
-
if (!(e instanceof BaseError)) return null;
|
|
829
|
-
const data = revertData(e);
|
|
830
|
-
if (data) {
|
|
831
|
-
try {
|
|
832
|
-
const d = decodeErrorResult({ abi: EXTENSION_ERRORS_ABI, data });
|
|
833
|
-
if (d.errorName === "Error" && typeof d.args?.[0] === "string") return d.args[0];
|
|
834
|
-
if (d.errorName === "Panic") return `Panic(${String(d.args?.[0])})`;
|
|
835
|
-
return d.errorName;
|
|
836
|
-
} catch {
|
|
837
|
-
return `reverted with unknown error ${data.slice(0, 10)}`;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
const named = e.walk((x) => ["ExecutionRevertedError", "InsufficientFundsError"].includes(x.name));
|
|
841
|
-
return named instanceof BaseError ? named.shortMessage : null;
|
|
842
|
-
}
|
|
843
|
-
var money = (value, decimals, symbol) => ({ value, decimals, symbol, formatted: formatUnits2(value, decimals) });
|
|
844
|
-
function timestamp(v, field) {
|
|
845
|
-
if (v === 0n) return null;
|
|
846
|
-
if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new MintError("RPC_ERROR", `Claim ${field} does not fit a safe integer`);
|
|
847
|
-
return Number(v);
|
|
848
|
-
}
|
|
849
|
-
function parseConfig(config) {
|
|
850
|
-
if (!isAddress(config.creatorContract, { strict: false })) {
|
|
851
|
-
throw new MintError("INVALID_CONFIG", "creatorContract is not a valid address.", { field: "creatorContract" });
|
|
852
|
-
}
|
|
853
|
-
if (config.extension !== void 0 && !isAddress(config.extension, { strict: false })) {
|
|
854
|
-
throw new MintError("INVALID_CONFIG", "extension is not a valid address.", { field: "extension" });
|
|
855
|
-
}
|
|
856
|
-
if (typeof config.instanceId !== "string" || !/^\d+$/.test(config.instanceId)) {
|
|
857
|
-
throw new MintError("INVALID_CONFIG", "instanceId must be a non-negative integer string.", { field: "instanceId" });
|
|
858
|
-
}
|
|
859
|
-
return {
|
|
860
|
-
creator: getAddress3(config.creatorContract),
|
|
861
|
-
instanceId: BigInt(config.instanceId),
|
|
862
|
-
extension: config.extension === void 0 ? void 0 : getAddress3(config.extension)
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
var notFound = (config) => new MintError("CLAIM_NOT_FOUND", `No Manifold claim ${config.instanceId} found on ${config.creatorContract}.`);
|
|
866
|
-
async function readClaim(client, entry, creator, instanceId, config) {
|
|
867
|
-
const address = getAddress3(entry.address);
|
|
868
|
-
const args = [creator, instanceId];
|
|
869
|
-
try {
|
|
870
|
-
switch (entry.abi) {
|
|
871
|
-
case "Claim721V5Optimization": {
|
|
872
|
-
const c = await client.readContract({ address, abi: CLAIM_721_V5_ABI, functionName: "getClaim", args });
|
|
873
|
-
return { ...base2(c), signingAddress: null };
|
|
874
|
-
}
|
|
875
|
-
case "Claim1155V5Optimization": {
|
|
876
|
-
const c = await client.readContract({ address, abi: CLAIM_1155_V5_ABI, functionName: "getClaim", args });
|
|
877
|
-
return { ...base2(c), signingAddress: null };
|
|
878
|
-
}
|
|
879
|
-
case "Claim721V7": {
|
|
880
|
-
const c = await client.readContract({ address, abi: CLAIM_721_V7_ABI, functionName: "getClaim", args });
|
|
881
|
-
return { ...base2(c), signingAddress: c.signingAddress };
|
|
882
|
-
}
|
|
883
|
-
case "Claim1155V7": {
|
|
884
|
-
const c = await client.readContract({ address, abi: CLAIM_1155_V7_ABI, functionName: "getClaim", args });
|
|
885
|
-
return { ...base2(c), signingAddress: c.signingAddress };
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
} catch (e) {
|
|
889
|
-
if (isContractFailure(e)) throw notFound(config);
|
|
890
|
-
throw toRpcError(e);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
function base2(c) {
|
|
894
|
-
return {
|
|
895
|
-
total: BigInt(c.total),
|
|
896
|
-
totalMax: BigInt(c.totalMax),
|
|
897
|
-
walletMax: BigInt(c.walletMax),
|
|
898
|
-
startDate: BigInt(c.startDate),
|
|
899
|
-
endDate: BigInt(c.endDate),
|
|
900
|
-
merkleRoot: c.merkleRoot,
|
|
901
|
-
cost: c.cost,
|
|
902
|
-
erc20: c.erc20
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
async function hasCode(client, address) {
|
|
906
|
-
const code2 = await rpc(() => client.getCode({ address: getAddress3(address) }));
|
|
907
|
-
return code2 !== void 0 && code2 !== "0x";
|
|
908
|
-
}
|
|
909
|
-
var unknownExtension = (address, why) => new MintError("UNKNOWN_EXTENSION", `Extension ${address} ${why}.`);
|
|
910
|
-
async function resolveExtension(ctx, config) {
|
|
911
|
-
const { creator, instanceId, extension } = parseConfig(config);
|
|
912
|
-
const { client } = ctx;
|
|
913
|
-
if (extension) {
|
|
914
|
-
const entry = findExtension(config.chainId, extension);
|
|
915
|
-
if (!entry) throw unknownExtension(extension, `is not a supported Manifold claim extension on chain ${config.chainId}`);
|
|
916
|
-
if (!await hasCode(client, extension)) throw unknownExtension(extension, "has no contract code on this chain");
|
|
917
|
-
return { entry, claim: await readClaim(client, entry, creator, instanceId, config) };
|
|
918
|
-
}
|
|
919
|
-
let registered;
|
|
920
|
-
try {
|
|
921
|
-
registered = await client.readContract({ address: creator, abi: CREATOR_ABI, functionName: "getExtensions" });
|
|
922
|
-
} catch (e) {
|
|
923
|
-
if (isContractFailure(e)) throw notFound(config);
|
|
924
|
-
throw toRpcError(e);
|
|
925
|
-
}
|
|
926
|
-
const candidates = [...new Set(registered.map((a) => a.toLowerCase()))].map((a) => findExtension(config.chainId, a)).filter((e) => e !== void 0);
|
|
927
|
-
const found = await Promise.all(
|
|
928
|
-
candidates.map(async (entry) => {
|
|
929
|
-
if (!await hasCode(client, entry.address)) return null;
|
|
930
|
-
try {
|
|
931
|
-
return { entry, claim: await readClaim(client, entry, creator, instanceId, config) };
|
|
932
|
-
} catch (e) {
|
|
933
|
-
if (e instanceof MintError && e.reason === "CLAIM_NOT_FOUND") return null;
|
|
934
|
-
throw e;
|
|
935
|
-
}
|
|
936
|
-
})
|
|
937
|
-
);
|
|
938
|
-
const hits = found.filter((f) => f !== null);
|
|
939
|
-
if (hits.length === 0) throw notFound(config);
|
|
940
|
-
if (hits.length > 1) {
|
|
941
|
-
throw new MintError(
|
|
942
|
-
"AMBIGUOUS_EXTENSION",
|
|
943
|
-
`Claim ${config.instanceId} exists on ${hits.length} extensions (${hits.map((h) => h.entry.address).join(", ")}). Set config.extension.`
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
return hits[0];
|
|
947
|
-
}
|
|
948
|
-
async function loadDrop(ctx, config) {
|
|
949
|
-
const { entry, claim } = await resolveExtension(ctx, config);
|
|
950
|
-
if (claim.signingAddress !== null && claim.signingAddress !== zeroAddress2) {
|
|
951
|
-
throw new MintError("SIGNATURE_CLAIM", "This claim mints with a signature (redemption code), which this button does not support.");
|
|
952
|
-
}
|
|
953
|
-
const { client } = ctx;
|
|
954
|
-
const extension = getAddress3(entry.address);
|
|
955
|
-
const fee = await rpc(
|
|
956
|
-
() => client.readContract({ address: extension, abi: EXTENSION_COMMON_ABI, functionName: isAllowlist(claim) ? "MINT_FEE_MERKLE" : "MINT_FEE" })
|
|
957
|
-
);
|
|
958
|
-
const native = client.chain?.nativeCurrency ?? { symbol: "ETH", decimals: 18 };
|
|
959
|
-
let unitPrice;
|
|
960
|
-
let paymentToken = null;
|
|
961
|
-
if (claim.erc20 === zeroAddress2) {
|
|
962
|
-
unitPrice = money(claim.cost, native.decimals, native.symbol);
|
|
963
|
-
} else {
|
|
964
|
-
paymentToken = getAddress3(claim.erc20);
|
|
965
|
-
const token = paymentToken;
|
|
966
|
-
const [symbol, decimals] = await rpc(
|
|
967
|
-
() => Promise.all([
|
|
968
|
-
client.readContract({ address: token, abi: ERC20_ABI, functionName: "symbol" }),
|
|
969
|
-
client.readContract({ address: token, abi: ERC20_ABI, functionName: "decimals" })
|
|
970
|
-
])
|
|
971
|
-
);
|
|
972
|
-
unitPrice = money(claim.cost, decimals, symbol);
|
|
973
|
-
}
|
|
974
|
-
const drop = {
|
|
975
|
-
chainId: config.chainId,
|
|
976
|
-
unitPrice,
|
|
977
|
-
unitFee: money(fee, native.decimals, native.symbol),
|
|
978
|
-
totalMinted: claim.total,
|
|
979
|
-
maxSupply: claim.totalMax === 0n ? null : claim.totalMax,
|
|
980
|
-
startTime: timestamp(claim.startDate, "startDate"),
|
|
981
|
-
endTime: timestamp(claim.endDate, "endDate"),
|
|
982
|
-
isAllowlist: isAllowlist(claim),
|
|
983
|
-
paymentToken,
|
|
984
|
-
pricePer: "unit"
|
|
985
|
-
};
|
|
986
|
-
const resolved = { entry, claim, fee };
|
|
987
|
-
dropResolved.set(drop, resolved);
|
|
988
|
-
return { drop, resolved };
|
|
989
|
-
}
|
|
990
|
-
async function loadWallet(ctx, config, args) {
|
|
991
|
-
const { creator, instanceId } = parseConfig(config);
|
|
992
|
-
const { client } = ctx;
|
|
993
|
-
const resolved = dropResolved.get(args.drop) ?? (await loadDrop(ctx, config)).resolved;
|
|
994
|
-
const { entry, claim } = resolved;
|
|
995
|
-
const extension = getAddress3(entry.address);
|
|
996
|
-
const payer = getAddress3(args.payer);
|
|
997
|
-
const recipient = getAddress3(args.recipient);
|
|
998
|
-
let walletMinted = null;
|
|
999
|
-
let walletMax = null;
|
|
1000
|
-
let allowlistRemaining = null;
|
|
1001
|
-
let maxQuantity = null;
|
|
1002
|
-
let ineligible = null;
|
|
1003
|
-
let unused = [];
|
|
1004
|
-
if (isAllowlist(claim)) {
|
|
1005
|
-
const entries = await fetchMerkleEntries({ scope: ctx.cacheScope ?? args.drop, api: MERKLE_API, instanceId: config.instanceId, recipient, fetch: ctx.fetch, signal: ctx.signal });
|
|
1006
|
-
if (entries.length > 0) {
|
|
1007
|
-
const minted = await rpc(
|
|
1008
|
-
() => client.readContract({
|
|
1009
|
-
address: extension,
|
|
1010
|
-
abi: EXTENSION_COMMON_ABI,
|
|
1011
|
-
functionName: "checkMintIndices",
|
|
1012
|
-
args: [creator, instanceId, entries.map((e) => e.mintIndex)]
|
|
1013
|
-
})
|
|
1014
|
-
);
|
|
1015
|
-
unused = entries.filter((_, i) => minted[i] === false);
|
|
1016
|
-
}
|
|
1017
|
-
allowlistRemaining = unused.length;
|
|
1018
|
-
maxQuantity = unused.length;
|
|
1019
|
-
if (entries.length === 0) {
|
|
1020
|
-
ineligible = { reason: "NOT_ON_ALLOWLIST", message: "This wallet is not on the allowlist." };
|
|
1021
|
-
} else if (unused.length === 0) {
|
|
1022
|
-
ineligible = { reason: "ALLOWLIST_USED", message: "This wallet has already used all its allowlist spots." };
|
|
1023
|
-
}
|
|
1024
|
-
} else if (claim.walletMax > 0n) {
|
|
1025
|
-
walletMax = claim.walletMax;
|
|
1026
|
-
walletMinted = BigInt(
|
|
1027
|
-
await rpc(() => client.readContract({ address: extension, abi: EXTENSION_COMMON_ABI, functionName: "getTotalMints", args: [recipient, creator, instanceId] }))
|
|
1028
|
-
);
|
|
1029
|
-
const left = walletMax > walletMinted ? walletMax - walletMinted : 0n;
|
|
1030
|
-
maxQuantity = Number(left);
|
|
1031
|
-
if (left === 0n) ineligible = { reason: "WALLET_LIMIT", message: `This wallet has already minted its limit of ${walletMax}.` };
|
|
1032
|
-
}
|
|
1033
|
-
const token = claim.erc20 === zeroAddress2 ? null : getAddress3(claim.erc20);
|
|
1034
|
-
const [native, erc20, gasPrice] = await rpc(
|
|
1035
|
-
() => Promise.all([
|
|
1036
|
-
client.getBalance({ address: payer }),
|
|
1037
|
-
token ? client.readContract({ address: token, abi: ERC20_ABI, functionName: "balanceOf", args: [payer] }) : Promise.resolve(null),
|
|
1038
|
-
client.getGasPrice()
|
|
1039
|
-
])
|
|
1040
|
-
);
|
|
1041
|
-
const gasUnits = GAS_UNITS_BASE + GAS_UNITS_PER_TOKEN * BigInt(Math.max(1, args.quantity)) + (token ? GAS_UNITS_APPROVE : 0n);
|
|
1042
|
-
const data = { brand: WALLET_DATA_BRAND, resolved, config, recipient, unused };
|
|
1043
|
-
return {
|
|
1044
|
-
walletMinted,
|
|
1045
|
-
walletMax,
|
|
1046
|
-
allowlistRemaining,
|
|
1047
|
-
maxQuantity,
|
|
1048
|
-
ineligible,
|
|
1049
|
-
balances: { native, erc20 },
|
|
1050
|
-
gasReserve: gasPrice * gasUnits,
|
|
1051
|
-
data
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
var sameClaim = (a, b) => a.chainId === b.chainId && a.creatorContract.toLowerCase() === b.creatorContract.toLowerCase() && a.instanceId === b.instanceId && a.extension?.toLowerCase() === b.extension?.toLowerCase();
|
|
1055
|
-
async function buildPlan(ctx, attempt, wallet) {
|
|
1056
|
-
const { config, quantity } = attempt;
|
|
1057
|
-
if (!Number.isInteger(quantity) || quantity < 1 || quantity > MAX_MINT_COUNT) {
|
|
1058
|
-
throw new MintError("INVALID_CONFIG", `quantity must be an integer from 1 to ${MAX_MINT_COUNT}.`, { field: "quantity" });
|
|
1059
|
-
}
|
|
1060
|
-
const { creator, instanceId } = parseConfig(config);
|
|
1061
|
-
const recipient = getAddress3(attempt.recipient);
|
|
1062
|
-
const payer = getAddress3(attempt.payer);
|
|
1063
|
-
let data = wallet.data;
|
|
1064
|
-
if (!isWalletData(data) || !sameClaim(data.config, config) || data.recipient !== recipient) {
|
|
1065
|
-
const drop = (await loadDrop(ctx, config)).drop;
|
|
1066
|
-
data = (await loadWallet(ctx, config, { payer, recipient, quantity, drop })).data;
|
|
1067
|
-
}
|
|
1068
|
-
if (!isWalletData(data)) throw new MintError("RPC_ERROR", "Wallet read produced no plan data.");
|
|
1069
|
-
const { entry, claim, fee } = data.resolved;
|
|
1070
|
-
const extension = getAddress3(entry.address);
|
|
1071
|
-
let mintIndices = [];
|
|
1072
|
-
let merkleProofs = [];
|
|
1073
|
-
if (isAllowlist(claim)) {
|
|
1074
|
-
if (quantity > data.unused.length) {
|
|
1075
|
-
throw new MintError("WALLET_LIMIT", `Only ${data.unused.length} allowlist spot(s) left for this wallet.`);
|
|
1076
|
-
}
|
|
1077
|
-
const picked = data.unused.slice(0, quantity);
|
|
1078
|
-
mintIndices = picked.map((p) => p.mintIndex);
|
|
1079
|
-
merkleProofs = picked.map((p) => p.proof);
|
|
1080
|
-
}
|
|
1081
|
-
const qty = BigInt(quantity);
|
|
1082
|
-
const erc20 = claim.erc20 === zeroAddress2 ? null : getAddress3(claim.erc20);
|
|
1083
|
-
const cost = claim.cost * qty;
|
|
1084
|
-
const value = fee * qty + (erc20 ? 0n : cost);
|
|
1085
|
-
let approve = null;
|
|
1086
|
-
if (erc20 && cost > 0n) {
|
|
1087
|
-
const allowance = await rpc(() => ctx.client.readContract({ address: erc20, abi: ERC20_ABI, functionName: "allowance", args: [payer, extension] }));
|
|
1088
|
-
if (allowance < cost) {
|
|
1089
|
-
approve = {
|
|
1090
|
-
token: erc20,
|
|
1091
|
-
spender: extension,
|
|
1092
|
-
amount: cost,
|
|
1093
|
-
tx: { to: erc20, data: encodeFunctionData({ abi: ERC20_ABI, functionName: "approve", args: [extension, cost] }), value: 0n }
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
return {
|
|
1098
|
-
approve,
|
|
1099
|
-
mint: {
|
|
1100
|
-
to: extension,
|
|
1101
|
-
data: encodeFunctionData({
|
|
1102
|
-
abi: EXTENSION_COMMON_ABI,
|
|
1103
|
-
functionName: "mintProxy",
|
|
1104
|
-
args: [creator, instanceId, quantity, mintIndices, merkleProofs.map((p) => [...p]), recipient]
|
|
1105
|
-
}),
|
|
1106
|
-
value
|
|
1107
|
-
},
|
|
1108
|
-
tokenContract: creator
|
|
1109
|
-
};
|
|
1110
|
-
}
|
|
1111
|
-
var RELAY_VERIFIED = ["V6", "V7"];
|
|
1112
|
-
var manifoldClaimAdapter = {
|
|
1113
|
-
supportsRelay: (drop) => {
|
|
1114
|
-
const resolved = dropResolved.get(drop);
|
|
1115
|
-
return resolved !== void 0 && RELAY_VERIFIED.includes(resolved.entry.generation) && drop.paymentToken === null;
|
|
1116
|
-
},
|
|
1117
|
-
readDrop: async (ctx, config) => (await loadDrop(ctx, config)).drop,
|
|
1118
|
-
readWallet: loadWallet,
|
|
1119
|
-
buildPlan,
|
|
1120
|
-
simulate: async (ctx, attempt, plan) => {
|
|
1121
|
-
try {
|
|
1122
|
-
await ctx.client.call({ account: getAddress3(attempt.payer), to: plan.mint.to, data: plan.mint.data, value: plan.mint.value });
|
|
1123
|
-
} catch (e) {
|
|
1124
|
-
const text2 = decodeRevert(e);
|
|
1125
|
-
if (text2 === null) throw toRpcError(e);
|
|
1126
|
-
throw new MintError("SIMULATION_REVERTED", text2, { cause: e });
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
|
|
1131
|
-
// src/core/adapters/contract/index.ts
|
|
1132
|
-
import {
|
|
1133
|
-
BaseError as BaseError2,
|
|
1134
|
-
ContractFunctionRevertedError as ContractFunctionRevertedError2,
|
|
1135
|
-
ContractFunctionZeroDataError,
|
|
1136
|
-
InsufficientFundsError,
|
|
1137
|
-
decodeErrorResult as decodeErrorResult2,
|
|
1138
|
-
encodeFunctionData as encodeFunctionData2,
|
|
1139
|
-
formatUnits as formatUnits4,
|
|
1140
|
-
getAddress as getAddress4,
|
|
1141
|
-
isAddress as isAddress2,
|
|
1142
|
-
parseAbi as parseAbi2,
|
|
274
|
+
parseAbi,
|
|
1143
275
|
parseUnits
|
|
1144
276
|
} from "viem";
|
|
1145
|
-
|
|
1146
|
-
// src/core/machine.ts
|
|
1147
|
-
import { formatUnits as formatUnits3 } from "viem";
|
|
1148
|
-
var MINT_STATES = [
|
|
1149
|
-
"loading",
|
|
1150
|
-
"unsupported",
|
|
1151
|
-
"upcoming",
|
|
1152
|
-
"ended",
|
|
1153
|
-
"sold-out",
|
|
1154
|
-
"disconnected",
|
|
1155
|
-
"connecting",
|
|
1156
|
-
"live",
|
|
1157
|
-
"ineligible",
|
|
1158
|
-
"insufficient-funds",
|
|
1159
|
-
"switching-chain",
|
|
1160
|
-
"approving",
|
|
1161
|
-
"minting",
|
|
1162
|
-
"confirming",
|
|
1163
|
-
"success",
|
|
1164
|
-
"error"
|
|
1165
|
-
];
|
|
1166
|
-
var CLICKABLE_STATES = ["disconnected", "live", "success", "error"];
|
|
1167
|
-
var IN_FLIGHT_STATES = ["switching-chain", "approving", "minting", "confirming"];
|
|
1168
|
-
var MAX_QUANTITY = 65535;
|
|
1169
|
-
function initialState(opts) {
|
|
1170
|
-
return {
|
|
1171
|
-
name: "loading",
|
|
1172
|
-
reason: null,
|
|
1173
|
-
message: null,
|
|
1174
|
-
readGen: 0,
|
|
1175
|
-
walletGen: 0,
|
|
1176
|
-
drop: null,
|
|
1177
|
-
account: null,
|
|
1178
|
-
walletRead: null,
|
|
1179
|
-
blocked: null,
|
|
1180
|
-
quantity: toQuantity(opts?.quantity ?? 1, MAX_QUANTITY),
|
|
1181
|
-
attempt: null,
|
|
1182
|
-
result: null,
|
|
1183
|
-
pendingRefresh: false,
|
|
1184
|
-
now: 0,
|
|
1185
|
-
crossChain: false,
|
|
1186
|
-
routePending: false,
|
|
1187
|
-
route: null,
|
|
1188
|
-
short: null
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
var NO_ROUTE = { crossChain: false, routePending: false, route: null, short: null };
|
|
1192
|
-
var PICK_ANOTHER = "Pick another network to pay from.";
|
|
1193
|
-
function toQuantity(q, max) {
|
|
1194
|
-
if (Number.isNaN(q)) return 1;
|
|
1195
|
-
return Math.max(1, Math.min(Math.floor(q), max));
|
|
1196
|
-
}
|
|
1197
|
-
function isInFlight(s) {
|
|
1198
|
-
return s.attempt !== null || IN_FLIGHT_STATES.includes(s.name);
|
|
1199
|
-
}
|
|
1200
|
-
function isIdle(s) {
|
|
1201
|
-
return !isInFlight(s) && s.name !== "connecting" && s.name !== "success" && s.name !== "error";
|
|
1202
|
-
}
|
|
1203
|
-
function derive(s, unsupported) {
|
|
1204
|
-
const named = (name) => ({ name, reason: null, message: null });
|
|
1205
|
-
const { drop, walletRead } = s;
|
|
1206
|
-
if (unsupported) return { name: "unsupported", reason: s.reason, message: s.message };
|
|
1207
|
-
if (drop === null) return named("loading");
|
|
1208
|
-
if (drop.endTime !== null && drop.endTime <= s.now) return named("ended");
|
|
1209
|
-
if (drop.maxSupply !== null && drop.maxSupply > 0n && drop.totalMinted >= drop.maxSupply) return named("sold-out");
|
|
1210
|
-
if (drop.startTime !== null && drop.startTime > s.now) return named("upcoming");
|
|
1211
|
-
if (s.account === null) return named("disconnected");
|
|
1212
|
-
if (walletRead === null) return named("loading");
|
|
1213
|
-
if (s.blocked !== null) return s.blocked;
|
|
1214
|
-
if (walletRead.ineligible !== null) {
|
|
1215
|
-
return { name: "ineligible", reason: walletRead.ineligible.reason, message: walletRead.ineligible.message };
|
|
1216
|
-
}
|
|
1217
|
-
if (s.routePending) return named("loading");
|
|
1218
|
-
if (s.route?.kind === "relay") return named("live");
|
|
1219
|
-
if (s.short !== null) {
|
|
1220
|
-
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${s.short.symbol} on ${s.short.name}. ${PICK_ANOTHER}` };
|
|
1221
|
-
}
|
|
1222
|
-
const total = selectTotal(s);
|
|
1223
|
-
if (total !== null) {
|
|
1224
|
-
const { native, erc20 } = total;
|
|
1225
|
-
if (native.value + walletRead.gasReserve > walletRead.balances.native) {
|
|
1226
|
-
const more = s.crossChain ? ` ${PICK_ANOTHER}` : "";
|
|
1227
|
-
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${native.symbol} to cover the mint.${more}` };
|
|
1228
|
-
}
|
|
1229
|
-
if (erc20 !== null && walletRead.balances.erc20 !== null && erc20.value > walletRead.balances.erc20) {
|
|
1230
|
-
return { name: "insufficient-funds", reason: "INSUFFICIENT_FUNDS", message: `Not enough ${erc20.symbol} to cover the mint.` };
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return named("live");
|
|
1234
|
-
}
|
|
1235
|
-
function settle(s, unsupported = s.name === "unsupported") {
|
|
1236
|
-
if (!isIdle(s)) return s;
|
|
1237
|
-
return { ...s, ...derive(s, unsupported) };
|
|
1238
|
-
}
|
|
1239
|
-
function clampQuantity(s) {
|
|
1240
|
-
const quantity = toQuantity(s.quantity, selectMaxQuantity(s) ?? MAX_QUANTITY);
|
|
1241
|
-
return quantity === s.quantity ? s : { ...s, quantity };
|
|
1242
|
-
}
|
|
1243
|
-
function reducer(state, event) {
|
|
1244
|
-
const inFlight = isInFlight(state);
|
|
1245
|
-
const failed = (reason, message) => {
|
|
1246
|
-
if (inFlight) return { ...state, pendingRefresh: true };
|
|
1247
|
-
if (state.name === "success" || state.name === "error") return state;
|
|
1248
|
-
return { ...state, name: "error", reason, message };
|
|
1249
|
-
};
|
|
1250
|
-
switch (event.type) {
|
|
1251
|
-
case "LOAD": {
|
|
1252
|
-
if (event.kind === "refresh") {
|
|
1253
|
-
const next2 = { ...state, readGen: event.gen };
|
|
1254
|
-
return inFlight ? { ...next2, pendingRefresh: true } : settle(next2);
|
|
1255
|
-
}
|
|
1256
|
-
const next = { ...state, ...NO_ROUTE, readGen: event.gen, drop: null, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
1257
|
-
return inFlight ? { ...next, pendingRefresh: true } : settle(next, false);
|
|
1258
|
-
}
|
|
1259
|
-
case "DROP_LOADED": {
|
|
1260
|
-
if (event.gen !== state.readGen) return state;
|
|
1261
|
-
const next = { ...state, drop: event.drop, now: Math.max(state.now, event.now) };
|
|
1262
|
-
if (inFlight) return { ...next, pendingRefresh: true };
|
|
1263
|
-
return isIdle(next) ? settle(clampQuantity(next), false) : clampQuantity(next);
|
|
1264
|
-
}
|
|
1265
|
-
case "DROP_UNSUPPORTED": {
|
|
1266
|
-
if (event.gen !== state.readGen) return state;
|
|
1267
|
-
if (inFlight) return { ...state, pendingRefresh: true };
|
|
1268
|
-
if (state.name === "success" || state.name === "error") return state;
|
|
1269
|
-
return { ...state, name: "unsupported", reason: event.reason, message: event.message };
|
|
1270
|
-
}
|
|
1271
|
-
case "READ_FAILED":
|
|
1272
|
-
return event.gen !== state.readGen ? state : failed(event.reason, event.message);
|
|
1273
|
-
case "ACCOUNT_CHANGED": {
|
|
1274
|
-
const next = { ...state, ...NO_ROUTE, account: event.account, walletRead: null, blocked: null, walletGen: state.walletGen + 1 };
|
|
1275
|
-
if (inFlight) return { ...next, pendingRefresh: true };
|
|
1276
|
-
if (state.name === "connecting") return event.account === null ? next : settle({ ...next, name: "loading" }, false);
|
|
1277
|
-
return settle(next);
|
|
1278
|
-
}
|
|
1279
|
-
case "WALLET_LOADED": {
|
|
1280
|
-
if (event.gen !== state.walletGen) return state;
|
|
1281
|
-
const route = event.routePending ? { routePending: state.route === null && state.short === null, route: state.route, short: state.short } : { routePending: false, route: null, short: null };
|
|
1282
|
-
const next = { ...state, ...route, crossChain: event.crossChain ?? false, walletRead: event.read, blocked: null };
|
|
1283
|
-
return inFlight ? { ...next, pendingRefresh: true } : settle(clampQuantity(next));
|
|
1284
|
-
}
|
|
1285
|
-
case "ROUTE_LOADED": {
|
|
1286
|
-
if (event.gen !== state.walletGen) return state;
|
|
1287
|
-
const next = { ...state, routePending: false, route: event.route, short: event.short };
|
|
1288
|
-
return inFlight ? { ...next, pendingRefresh: true } : settle(next);
|
|
1289
|
-
}
|
|
1290
|
-
case "WALLET_FAILED":
|
|
1291
|
-
return event.gen !== state.walletGen ? state : failed(event.reason, event.message);
|
|
1292
|
-
case "CONNECT_STARTED":
|
|
1293
|
-
return state.name === "disconnected" && state.attempt === null ? { ...state, name: "connecting", reason: null, message: null } : state;
|
|
1294
|
-
case "CONNECT_DISMISSED":
|
|
1295
|
-
return state.name === "connecting" ? settle({ ...state, name: "disconnected" }, false) : state;
|
|
1296
|
-
case "TICK":
|
|
1297
|
-
return settle({ ...state, now: Math.max(state.now, event.now) });
|
|
1298
|
-
case "SET_QUANTITY": {
|
|
1299
|
-
const next = { ...state, quantity: toQuantity(event.quantity, selectMaxQuantity(state) ?? MAX_QUANTITY) };
|
|
1300
|
-
return inFlight ? { ...next, pendingRefresh: true } : settle(next);
|
|
1301
|
-
}
|
|
1302
|
-
case "ATTEMPT_STARTED":
|
|
1303
|
-
if (state.attempt !== null) return state;
|
|
1304
|
-
if (event.resume === true) return IN_FLIGHT_STATES.includes(state.name) ? state : { ...state, attempt: event.attempt };
|
|
1305
|
-
return state.name === "live" ? { ...state, attempt: event.attempt } : state;
|
|
1306
|
-
case "SWITCHING_CHAIN":
|
|
1307
|
-
return step(state, event.attemptId, "switching-chain");
|
|
1308
|
-
case "APPROVING":
|
|
1309
|
-
return step(state, event.attemptId, "approving");
|
|
1310
|
-
case "MINT_PROMPTED":
|
|
1311
|
-
return step(state, event.attemptId, "minting");
|
|
1312
|
-
case "TX_SUBMITTED":
|
|
1313
|
-
return step(state, event.attemptId, "confirming");
|
|
1314
|
-
case "ATTEMPT_SUCCEEDED":
|
|
1315
|
-
return state.attempt?.attemptId === event.attemptId ? {
|
|
1316
|
-
...state,
|
|
1317
|
-
name: "success",
|
|
1318
|
-
reason: null,
|
|
1319
|
-
message: null,
|
|
1320
|
-
attempt: null,
|
|
1321
|
-
result: event.result,
|
|
1322
|
-
drop: null,
|
|
1323
|
-
readGen: state.readGen + 1
|
|
1324
|
-
} : state;
|
|
1325
|
-
case "ATTEMPT_FAILED": {
|
|
1326
|
-
if (state.attempt?.attemptId !== event.attemptId) return state;
|
|
1327
|
-
const { reason, message } = event;
|
|
1328
|
-
const done = { ...state, reason, message, attempt: null, result: null };
|
|
1329
|
-
if (INELIGIBLE_REASONS.includes(reason)) {
|
|
1330
|
-
return { ...done, name: "ineligible", blocked: { name: "ineligible", reason, message } };
|
|
1331
|
-
}
|
|
1332
|
-
if (reason === "INSUFFICIENT_FUNDS") {
|
|
1333
|
-
return { ...done, name: "insufficient-funds", blocked: { name: "insufficient-funds", reason, message } };
|
|
1334
|
-
}
|
|
1335
|
-
return {
|
|
1336
|
-
...done,
|
|
1337
|
-
name: "error",
|
|
1338
|
-
drop: state.name === "confirming" ? null : state.drop,
|
|
1339
|
-
readGen: state.name === "confirming" ? state.readGen + 1 : state.readGen
|
|
1340
|
-
};
|
|
1341
|
-
}
|
|
1342
|
-
case "RESET": {
|
|
1343
|
-
if (state.name !== "success" && state.name !== "error") return state;
|
|
1344
|
-
const next = {
|
|
1345
|
-
...state,
|
|
1346
|
-
readGen: event.gen,
|
|
1347
|
-
...NO_ROUTE,
|
|
1348
|
-
walletRead: null,
|
|
1349
|
-
blocked: null,
|
|
1350
|
-
walletGen: state.walletGen + 1,
|
|
1351
|
-
result: null,
|
|
1352
|
-
pendingRefresh: false
|
|
1353
|
-
};
|
|
1354
|
-
return settle(clampQuantity({ ...next, name: "loading" }), false);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
var STEP_ORDER = ["switching-chain", "approving", "minting", "confirming"];
|
|
1359
|
-
function step(state, attemptId, name) {
|
|
1360
|
-
if (state.attempt?.attemptId !== attemptId) return state;
|
|
1361
|
-
if (STEP_ORDER.indexOf(name) <= STEP_ORDER.indexOf(state.name)) return state;
|
|
1362
|
-
return { ...state, name, reason: null, message: null };
|
|
1363
|
-
}
|
|
1364
|
-
function selectMaxQuantity(state) {
|
|
1365
|
-
const { drop, walletRead } = state;
|
|
1366
|
-
if (drop === null) return null;
|
|
1367
|
-
let max = MAX_QUANTITY;
|
|
1368
|
-
if (drop.maxSupply !== null && drop.maxSupply > 0n) {
|
|
1369
|
-
const remaining = drop.maxSupply - drop.totalMinted;
|
|
1370
|
-
if (remaining < BigInt(max)) max = remaining > 0n ? Number(remaining) : 0;
|
|
1371
|
-
}
|
|
1372
|
-
if (walletRead !== null && walletRead.maxQuantity !== null) {
|
|
1373
|
-
max = Math.min(max, Math.max(0, Math.floor(walletRead.maxQuantity)));
|
|
1374
|
-
}
|
|
1375
|
-
return max;
|
|
1376
|
-
}
|
|
1377
|
-
function selectTotal(state) {
|
|
1378
|
-
const { drop } = state;
|
|
1379
|
-
if (drop === null) return null;
|
|
1380
|
-
const q = BigInt(state.quantity);
|
|
1381
|
-
const price = drop.unitPrice;
|
|
1382
|
-
const fee = drop.unitFee;
|
|
1383
|
-
const priceTotal = drop.pricePer === "total" ? price.value : price.value * q;
|
|
1384
|
-
if (drop.paymentToken !== null) {
|
|
1385
|
-
return {
|
|
1386
|
-
native: money2(fee.value * q, fee),
|
|
1387
|
-
erc20: money2(priceTotal, price)
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
return { native: money2(priceTotal + fee.value * q, price), erc20: null };
|
|
1391
|
-
}
|
|
1392
|
-
function money2(value, like) {
|
|
1393
|
-
return { value, decimals: like.decimals, symbol: like.symbol, formatted: formatUnits3(value, like.decimals) };
|
|
1394
|
-
}
|
|
1395
|
-
function canAct(state) {
|
|
1396
|
-
return state.attempt === null && CLICKABLE_STATES.includes(state.name);
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// src/core/adapters/contract/index.ts
|
|
1400
277
|
var PLACEHOLDERS = ["{recipient}", "{payer}", "{quantity}", "{unitPrice}", "{totalPrice}"];
|
|
1401
|
-
var
|
|
278
|
+
var ERC20_ABI = parseAbi([
|
|
1402
279
|
"function symbol() view returns (string)",
|
|
1403
280
|
"function decimals() view returns (uint8)",
|
|
1404
281
|
"function balanceOf(address) view returns (uint256)",
|
|
@@ -1413,7 +290,7 @@ var NUMERIC_TYPE = /^u?int\d*$/;
|
|
|
1413
290
|
function bad(field, message) {
|
|
1414
291
|
throw new MintError("INVALID_CONFIG", `${field} ${message}`, { field });
|
|
1415
292
|
}
|
|
1416
|
-
var isHexAddress = (a) => typeof a === "string" &&
|
|
293
|
+
var isHexAddress = (a) => typeof a === "string" && isAddress(a, { strict: false });
|
|
1417
294
|
function functionsNamed(abi, name) {
|
|
1418
295
|
return abi.filter((i) => i.type === "function" && i.name === name);
|
|
1419
296
|
}
|
|
@@ -1470,7 +347,7 @@ function typeIssue(t, p, path) {
|
|
|
1470
347
|
if (Array.isArray(t)) return no(`must not be an array for ${p.type}`);
|
|
1471
348
|
if (p.type === "address") {
|
|
1472
349
|
if (isExactPlaceholder(t)) return t === "{recipient}" || t === "{payer}" ? null : no(`${t} is a number, but the abi input is an address`);
|
|
1473
|
-
return typeof t === "string" && !hasPlaceholder(t) &&
|
|
350
|
+
return typeof t === "string" && !hasPlaceholder(t) && isAddress(t) ? null : no(`${JSON.stringify(String(t))} is not an address`);
|
|
1474
351
|
}
|
|
1475
352
|
if (p.type === "bool") return typeof t === "boolean" ? null : no(`${JSON.stringify(String(t))} is not a boolean`);
|
|
1476
353
|
if (p.type === "string") return typeof t === "string" ? null : no(`${JSON.stringify(String(t))} is not a string`);
|
|
@@ -1627,7 +504,7 @@ function varFor(token, vars) {
|
|
|
1627
504
|
const key = token.slice(1, -1);
|
|
1628
505
|
const v = vars[key];
|
|
1629
506
|
if (v === void 0) throw new MintError("INVALID_CONFIG", `placeholder ${token} has no value here`);
|
|
1630
|
-
return typeof v === "string" ?
|
|
507
|
+
return typeof v === "string" ? getAddress2(v) : v;
|
|
1631
508
|
}
|
|
1632
509
|
function fill(t, p, vars) {
|
|
1633
510
|
const arr = ARRAY_TYPE.exec(p.type);
|
|
@@ -1643,7 +520,7 @@ function fill(t, p, vars) {
|
|
|
1643
520
|
var fillArgs = (args, inputs, vars) => (args ?? []).map((a, i) => fill(a, inputs[i], vars));
|
|
1644
521
|
var totalFor = (per, price, quantity) => per === "total" ? price : price * quantity;
|
|
1645
522
|
function shortMessage(e) {
|
|
1646
|
-
return e instanceof
|
|
523
|
+
return e instanceof BaseError ? e.shortMessage : e instanceof Error ? e.message : String(e);
|
|
1647
524
|
}
|
|
1648
525
|
var abortError = () => new DOMException("The operation was aborted", "AbortError");
|
|
1649
526
|
var isAbortError = (e) => e?.name === "AbortError";
|
|
@@ -1666,7 +543,7 @@ function rpcError(e) {
|
|
|
1666
543
|
}
|
|
1667
544
|
function classifyReadError(e, field) {
|
|
1668
545
|
if (isMintError(e) || isAbortError(e)) throw e;
|
|
1669
|
-
if (e instanceof
|
|
546
|
+
if (e instanceof BaseError && e.walk((x) => x instanceof ContractFunctionRevertedError || x instanceof ContractFunctionZeroDataError)) {
|
|
1670
547
|
throw new MintError("INVALID_CONFIG", `${field} read reverted or returned no data: ${e.shortMessage}`, { field, cause: e });
|
|
1671
548
|
}
|
|
1672
549
|
return rpcError(e);
|
|
@@ -1716,14 +593,14 @@ function nativeMeta(ctx) {
|
|
|
1716
593
|
const n = ctx.client.chain?.nativeCurrency;
|
|
1717
594
|
return { decimals: n?.decimals ?? 18, symbol: n?.symbol ?? "ETH" };
|
|
1718
595
|
}
|
|
1719
|
-
var erc20Of = (config) => config.price.currency === "native" ? null :
|
|
596
|
+
var erc20Of = (config) => config.price.currency === "native" ? null : getAddress2(config.price.currency.address);
|
|
1720
597
|
async function currencyMeta(ctx, config) {
|
|
1721
598
|
const token = erc20Of(config);
|
|
1722
599
|
if (!token) return { token, ...nativeMeta(ctx) };
|
|
1723
600
|
const field = "price.currency.address";
|
|
1724
601
|
const [symbol, decimals] = await Promise.all([
|
|
1725
|
-
readCall(ctx, { address: token, abi:
|
|
1726
|
-
readCall(ctx, { address: token, abi:
|
|
602
|
+
readCall(ctx, { address: token, abi: ERC20_ABI, functionName: "symbol" }, field),
|
|
603
|
+
readCall(ctx, { address: token, abi: ERC20_ABI, functionName: "decimals" }, field)
|
|
1727
604
|
]);
|
|
1728
605
|
if (typeof symbol !== "string" || typeof decimals !== "number") {
|
|
1729
606
|
throw new MintError("INVALID_CONFIG", `${field} ${token} is not an ERC20 (symbol/decimals unreadable)`, { field });
|
|
@@ -1744,13 +621,13 @@ async function resolvePrice(ctx, config, meta) {
|
|
|
1744
621
|
if (typeof value === "bigint") return value;
|
|
1745
622
|
return parseDisplay(value, (meta ?? await currencyMeta(ctx, config)).decimals);
|
|
1746
623
|
}
|
|
1747
|
-
var
|
|
624
|
+
var money = (value, decimals, symbol) => ({
|
|
1748
625
|
value,
|
|
1749
626
|
decimals,
|
|
1750
627
|
symbol,
|
|
1751
|
-
formatted:
|
|
628
|
+
formatted: formatUnits2(value, decimals)
|
|
1752
629
|
});
|
|
1753
|
-
function
|
|
630
|
+
function revertData(e) {
|
|
1754
631
|
for (let cur = e, depth = 0; cur && depth < 12; depth++) {
|
|
1755
632
|
const d = cur.data;
|
|
1756
633
|
if (typeof d === "string" && d.startsWith("0x") && d.length >= 10) return d;
|
|
@@ -1764,7 +641,7 @@ function revertData2(e) {
|
|
|
1764
641
|
}
|
|
1765
642
|
function describeRevert(abi, data) {
|
|
1766
643
|
try {
|
|
1767
|
-
const d =
|
|
644
|
+
const d = decodeErrorResult({ abi, data });
|
|
1768
645
|
const args = d.args ?? [];
|
|
1769
646
|
if (d.errorName === "Error") return String(args[0] ?? "execution reverted");
|
|
1770
647
|
const inputs = d.abiItem?.inputs ?? [];
|
|
@@ -1777,12 +654,12 @@ function describeRevert(abi, data) {
|
|
|
1777
654
|
function toSimulationError(e, abi) {
|
|
1778
655
|
if (isMintError(e)) return e;
|
|
1779
656
|
if (isAbortError(e)) throw e;
|
|
1780
|
-
if (e instanceof
|
|
657
|
+
if (e instanceof BaseError && e.walk((x) => x instanceof InsufficientFundsError)) {
|
|
1781
658
|
return new MintError("INSUFFICIENT_FUNDS", `Payer cannot cover the mint: ${e.shortMessage}`, { cause: e });
|
|
1782
659
|
}
|
|
1783
|
-
const data =
|
|
660
|
+
const data = revertData(e);
|
|
1784
661
|
if (data) return new MintError("SIMULATION_REVERTED", describeRevert(abi, data), { cause: e });
|
|
1785
|
-
const isRevert = e instanceof
|
|
662
|
+
const isRevert = e instanceof BaseError && e.walk((x) => x.code === 3 || /execution reverted/i.test(x.message));
|
|
1786
663
|
if (isRevert) return new MintError("SIMULATION_REVERTED", shortMessage(e), { cause: e });
|
|
1787
664
|
return new MintError("RPC_ERROR", `RPC request failed: ${shortMessage(e)}`, { cause: e });
|
|
1788
665
|
}
|
|
@@ -1796,7 +673,7 @@ async function simulateMint(ctx, config, call) {
|
|
|
1796
673
|
function encodeMint(config, vars) {
|
|
1797
674
|
const fn = validateCall(config.abi, config.mint, "mint", "mint", PLACEHOLDERS);
|
|
1798
675
|
try {
|
|
1799
|
-
return
|
|
676
|
+
return encodeFunctionData({ abi: [fn], functionName: fn.name, args: fillArgs(config.mint.args, fn.inputs, vars) });
|
|
1800
677
|
} catch (e) {
|
|
1801
678
|
if (isMintError(e)) throw e;
|
|
1802
679
|
throw new MintError("INVALID_CONFIG", `mint.args cannot be encoded for ${signature(fn)}: ${shortMessage(e)}`, { field: "mint.args", cause: e });
|
|
@@ -1827,8 +704,8 @@ async function readDrop(ctx, config) {
|
|
|
1827
704
|
const native = nativeMeta(ctx);
|
|
1828
705
|
return {
|
|
1829
706
|
chainId: config.chainId,
|
|
1830
|
-
unitPrice:
|
|
1831
|
-
unitFee:
|
|
707
|
+
unitPrice: money(price, meta.decimals, meta.symbol),
|
|
708
|
+
unitFee: money(0n, native.decimals, native.symbol),
|
|
1832
709
|
totalMinted: totalMinted ?? 0n,
|
|
1833
710
|
maxSupply: maxSupply === null || maxSupply === 0n ? null : maxSupply,
|
|
1834
711
|
startTime: toSeconds(start, "startTime"),
|
|
@@ -1849,7 +726,7 @@ async function readWallet(ctx, config, { payer, recipient, quantity, drop }) {
|
|
|
1849
726
|
optField(ctx, config, "walletMinted", config.walletMinted, vars),
|
|
1850
727
|
optField(ctx, config, "walletMax", config.walletMax, vars),
|
|
1851
728
|
io(ctx, () => ctx.client.getBalance({ address: payer })).catch(rpcError),
|
|
1852
|
-
drop.paymentToken ? readCall(ctx, { address: drop.paymentToken, abi:
|
|
729
|
+
drop.paymentToken ? readCall(ctx, { address: drop.paymentToken, abi: ERC20_ABI, functionName: "balanceOf", args: [payer] }, "price.currency.address").then(
|
|
1853
730
|
(v) => toBigInt(v, "price.currency.address")
|
|
1854
731
|
) : Promise.resolve(null)
|
|
1855
732
|
]);
|
|
@@ -1861,7 +738,7 @@ async function readWallet(ctx, config, { payer, recipient, quantity, drop }) {
|
|
|
1861
738
|
if (remaining === 0n) {
|
|
1862
739
|
ineligible = { reason: "WALLET_LIMIT", message: `This wallet has already minted its limit of ${walletMax}.` };
|
|
1863
740
|
} else if (drop.paymentToken === null && native >= totalPrice) {
|
|
1864
|
-
const call = { payer, to:
|
|
741
|
+
const call = { payer, to: getAddress2(config.address), data: encodeMint(config, vars), value: mintValue(config, null, totalPrice) };
|
|
1865
742
|
try {
|
|
1866
743
|
await simulateMint(ctx, config, call);
|
|
1867
744
|
gas = await io(ctx, () => ctx.client.estimateGas({ account: payer, to: call.to, data: call.data, value: call.value })).catch((e) => {
|
|
@@ -1884,7 +761,7 @@ async function readWallet(ctx, config, { payer, recipient, quantity, drop }) {
|
|
|
1884
761
|
gasReserve: gas * gasPrice
|
|
1885
762
|
};
|
|
1886
763
|
}
|
|
1887
|
-
async function
|
|
764
|
+
async function buildPlan(ctx, attempt, wallet) {
|
|
1888
765
|
void wallet;
|
|
1889
766
|
const { config, payer, recipient } = attempt;
|
|
1890
767
|
validateContractConfig(config);
|
|
@@ -1892,7 +769,7 @@ async function buildPlan2(ctx, attempt, wallet) {
|
|
|
1892
769
|
const qty = BigInt(attempt.quantity);
|
|
1893
770
|
const unitPrice = await resolvePrice(ctx, config);
|
|
1894
771
|
const totalPrice = totalFor(config.price.per ?? "unit", unitPrice, qty);
|
|
1895
|
-
const to =
|
|
772
|
+
const to = getAddress2(config.address);
|
|
1896
773
|
const token = erc20Of(config);
|
|
1897
774
|
const mint = {
|
|
1898
775
|
to,
|
|
@@ -1902,7 +779,7 @@ async function buildPlan2(ctx, attempt, wallet) {
|
|
|
1902
779
|
let approve = null;
|
|
1903
780
|
if (token) {
|
|
1904
781
|
const allowance = toBigInt(
|
|
1905
|
-
await readCall(ctx, { address: token, abi:
|
|
782
|
+
await readCall(ctx, { address: token, abi: ERC20_ABI, functionName: "allowance", args: [payer, to] }, "price.currency.address"),
|
|
1906
783
|
"price.currency.address"
|
|
1907
784
|
);
|
|
1908
785
|
if (allowance < totalPrice) {
|
|
@@ -1910,134 +787,17 @@ async function buildPlan2(ctx, attempt, wallet) {
|
|
|
1910
787
|
token,
|
|
1911
788
|
spender: to,
|
|
1912
789
|
amount: totalPrice,
|
|
1913
|
-
tx: { to: token, data:
|
|
790
|
+
tx: { to: token, data: encodeFunctionData({ abi: ERC20_ABI, functionName: "approve", args: [to, totalPrice] }), value: 0n }
|
|
1914
791
|
};
|
|
1915
792
|
}
|
|
1916
793
|
}
|
|
1917
|
-
return { approve, mint, tokenContract:
|
|
794
|
+
return { approve, mint, tokenContract: getAddress2(attempt.tokenContract) };
|
|
1918
795
|
}
|
|
1919
796
|
async function simulate(ctx, attempt, plan) {
|
|
1920
797
|
throwIfAborted(ctx);
|
|
1921
798
|
await simulateMint(ctx, attempt.config, { payer: attempt.payer, to: plan.mint.to, data: plan.mint.data, value: plan.mint.value });
|
|
1922
799
|
}
|
|
1923
|
-
var contractAdapter = { readDrop, readWallet, buildPlan
|
|
1924
|
-
|
|
1925
|
-
// src/core/adapters/receipt.ts
|
|
1926
|
-
import { decodeAbiParameters, getAddress as getAddress5, zeroAddress as zeroAddress3 } from "viem";
|
|
1927
|
-
var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
1928
|
-
var TRANSFER_SINGLE_TOPIC = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62";
|
|
1929
|
-
var TRANSFER_BATCH_TOPIC = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb";
|
|
1930
|
-
var topicAddress = (t) => t && t.length === 66 ? getAddress5(`0x${t.slice(26)}`) : null;
|
|
1931
|
-
function parseMintReceipt(logs, { tokenContract, recipient }) {
|
|
1932
|
-
const contract = getAddress5(tokenContract);
|
|
1933
|
-
const to = getAddress5(recipient);
|
|
1934
|
-
const out = /* @__PURE__ */ new Map();
|
|
1935
|
-
const add = (id, amount) => out.set(id, (out.get(id) ?? 0n) + amount);
|
|
1936
|
-
for (const log of logs) {
|
|
1937
|
-
if (getAddress5(log.address) !== contract) continue;
|
|
1938
|
-
const [t0, t1, t2, t3] = log.topics;
|
|
1939
|
-
if (t0 === TRANSFER_TOPIC && log.topics.length === 4) {
|
|
1940
|
-
if (topicAddress(t1) !== zeroAddress3 || topicAddress(t2) !== to || !t3) continue;
|
|
1941
|
-
add(BigInt(t3), 1n);
|
|
1942
|
-
} else if (t0 === TRANSFER_SINGLE_TOPIC && log.topics.length === 4) {
|
|
1943
|
-
if (topicAddress(t2) !== zeroAddress3 || topicAddress(t3) !== to) continue;
|
|
1944
|
-
const [id, value] = decodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], log.data);
|
|
1945
|
-
add(id, value);
|
|
1946
|
-
} else if (t0 === TRANSFER_BATCH_TOPIC && log.topics.length === 4) {
|
|
1947
|
-
if (topicAddress(t2) !== zeroAddress3 || topicAddress(t3) !== to) continue;
|
|
1948
|
-
const [ids, values] = decodeAbiParameters([{ type: "uint256[]" }, { type: "uint256[]" }], log.data);
|
|
1949
|
-
if (ids.length !== values.length) continue;
|
|
1950
|
-
ids.forEach((id, i) => add(id, values[i]));
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
return [...out].map(([tokenId, amount]) => ({ contract, tokenId, amount }));
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
// src/core/friendly-errors.ts
|
|
1957
|
-
var GENERIC = "Transaction failed. Please try again. If it fails again, please contact support.";
|
|
1958
|
-
var REVERTED = "Transaction reverted";
|
|
1959
|
-
var record = (value) => value !== null && typeof value === "object" ? value : void 0;
|
|
1960
|
-
var text = (value) => typeof value === "string" ? value.toLowerCase() : "";
|
|
1961
|
-
function collect(error) {
|
|
1962
|
-
const pending = [error];
|
|
1963
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1964
|
-
const errors = [];
|
|
1965
|
-
while (pending.length) {
|
|
1966
|
-
const e = record(pending.pop());
|
|
1967
|
-
if (!e || seen.has(e)) continue;
|
|
1968
|
-
seen.add(e);
|
|
1969
|
-
const data = record(e.data);
|
|
1970
|
-
errors.push({
|
|
1971
|
-
name: e.name,
|
|
1972
|
-
code: e.code,
|
|
1973
|
-
cancelled: e.cancelled,
|
|
1974
|
-
replacement: e.replacement,
|
|
1975
|
-
message: [e.message, e.shortMessage, isMintError(e) ? void 0 : e.reason, data?.message].map(text).join("\n")
|
|
1976
|
-
});
|
|
1977
|
-
pending.push(e.cause, data?.originalError);
|
|
1978
|
-
}
|
|
1979
|
-
return errors;
|
|
1980
|
-
}
|
|
1981
|
-
function fundsMessage(errors) {
|
|
1982
|
-
const hasText = (phrase) => errors.some((e) => e.message.includes(phrase));
|
|
1983
|
-
if (hasText("insufficient funds for gas")) {
|
|
1984
|
-
return "Your wallet does not have enough funds to pay for gas fees. Please try again with a different wallet or add more funds to your wallet.";
|
|
1985
|
-
}
|
|
1986
|
-
if (errors.some((e) => e.name === "InsufficientFundsError" || e.code === "INSUFFICIENT_FUNDS") || hasText("insufficient funds") || hasText("balance too low to proceed")) {
|
|
1987
|
-
return "Your wallet does not have enough funds to complete this transaction. Please try again with a different wallet or add more funds to your wallet.";
|
|
1988
|
-
}
|
|
1989
|
-
return null;
|
|
1990
|
-
}
|
|
1991
|
-
function friendlyErrorMessage(error) {
|
|
1992
|
-
if (isMintError(error)) {
|
|
1993
|
-
if (error.reason === "INSUFFICIENT_FUNDS") return fundsMessage(collect(error.cause)) ?? error.message;
|
|
1994
|
-
if (error.reason !== "RPC_ERROR" && error.reason !== "SIMULATION_REVERTED") return error.message;
|
|
1995
|
-
if (error.reason === "SIMULATION_REVERTED" && error.domain === "relay") return error.message;
|
|
1996
|
-
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;
|
|
1997
|
-
}
|
|
1998
|
-
const errors = collect(error);
|
|
1999
|
-
const hasText = (phrase) => errors.some((e) => e.message.includes(phrase));
|
|
2000
|
-
const hasName = (name) => errors.some((e) => e.name === name);
|
|
2001
|
-
const hasCode2 = (code2) => errors.some((e) => e.code === code2);
|
|
2002
|
-
if (hasName("UserRejectedRequestError") || hasCode2("ACTION_REJECTED") || hasCode2(4001) || hasText("user denied transaction signature") || hasText("userrefusedondevice") || hasText("rejected transaction") || hasText("user rejected") || errors.some((e) => e.message.includes("cancelled") && !e.cancelled)) {
|
|
2003
|
-
return "Transaction Rejected";
|
|
2004
|
-
}
|
|
2005
|
-
if (hasCode2(4902)) return "Network not available in wallet";
|
|
2006
|
-
const replaced = errors.find((e) => e.name === "TransactionReplacedError" || e.code === "TRANSACTION_REPLACED");
|
|
2007
|
-
if (replaced?.cancelled) return "Transaction was cancelled";
|
|
2008
|
-
if (replaced?.replacement) return "Transaction was replaced with a new transaction (usually due to speed up)";
|
|
2009
|
-
if (hasText("ledger")) return "Error with Ledger device. Please ensure device is connected and unlocked";
|
|
2010
|
-
if (errors.some((e) => e.code === -32002 && e.message.includes("pending"))) {
|
|
2011
|
-
return "Transaction already pending in wallet. Please check your wallet";
|
|
2012
|
-
}
|
|
2013
|
-
if (hasText("invalid amount")) return "Price calculation is incorrect, contact support!";
|
|
2014
|
-
const funds = fundsMessage(errors);
|
|
2015
|
-
if (funds) return funds;
|
|
2016
|
-
if (hasName("NonceTooLowError") || hasName("NonceExhaustedError") || hasText("nonce too low")) {
|
|
2017
|
-
return "Transaction nonce is too low. Please try again";
|
|
2018
|
-
}
|
|
2019
|
-
if (hasName("FeeCapTooLowError") || hasText("max fee per gas less than block base fee")) {
|
|
2020
|
-
return "Gas price too low for current network conditions. Please try again";
|
|
2021
|
-
}
|
|
2022
|
-
if (hasName("TimeoutError") || hasCode2("TIMEOUT") || hasText("timeout")) {
|
|
2023
|
-
return "Transaction timed out. Your wallet connection is having issues. Disconnect and reconnect your wallet, then please try again";
|
|
2024
|
-
}
|
|
2025
|
-
if (hasText("network disconnected")) return "Network connection lost. Please check your internet connection and try again";
|
|
2026
|
-
if (hasName("ChainMismatchError") || hasText("wrong network")) return "Wrong network selected. Please switch to the correct network";
|
|
2027
|
-
if (hasText("erc20: transfer amount exceeds balance")) {
|
|
2028
|
-
return "You do not have the required amount of ERC20 tokens to complete this transaction.";
|
|
2029
|
-
}
|
|
2030
|
-
if (hasName("EstimateGasExecutionError") || hasCode2("UNPREDICTABLE_GAS_LIMIT") || hasText("cannot estimate gas")) {
|
|
2031
|
-
return "Unable to estimate gas for transaction. The transaction may fail. Please try again.";
|
|
2032
|
-
}
|
|
2033
|
-
if (hasName("ContractFunctionExecutionError") || hasCode2("CALL_EXCEPTION")) {
|
|
2034
|
-
return "Transaction failed due to contract execution error";
|
|
2035
|
-
}
|
|
2036
|
-
if (hasName("ContractFunctionRevertedError") || hasName("ExecutionRevertedError") || hasText("revert")) return REVERTED;
|
|
2037
|
-
if (hasName("TransactionExecutionError")) return "Transaction execution failed";
|
|
2038
|
-
if (hasCode2("NETWORK_ERROR") || hasName("HttpRequestError") || hasText("network")) return "Network error occurred";
|
|
2039
|
-
return isMintError(error) && error.reason === "SIMULATION_REVERTED" ? REVERTED : GENERIC;
|
|
2040
|
-
}
|
|
800
|
+
var contractAdapter = { readDrop, readWallet, buildPlan, simulate };
|
|
2041
801
|
|
|
2042
802
|
// src/react/useMint.ts
|
|
2043
803
|
var REFRESH_MS = 3e4;
|
|
@@ -2080,22 +840,6 @@ function eventAddressOf(config) {
|
|
|
2080
840
|
return config.type === "manifold-claim" ? config.creatorContract : config.address;
|
|
2081
841
|
}
|
|
2082
842
|
var isUnsupported = (reason) => UNSUPPORTED_REASONS.includes(reason);
|
|
2083
|
-
function isRejection(error) {
|
|
2084
|
-
for (let e = error, depth = 0; e && depth < 8; depth++) {
|
|
2085
|
-
if (e.code === 4001) return true;
|
|
2086
|
-
e = e.cause;
|
|
2087
|
-
}
|
|
2088
|
-
return false;
|
|
2089
|
-
}
|
|
2090
|
-
function toReason(e) {
|
|
2091
|
-
if (isMintError(e)) return { reason: e.reason, message: friendlyErrorMessage(e) };
|
|
2092
|
-
if (isRejection(e)) return { reason: "USER_REJECTED", message: "You rejected the request." };
|
|
2093
|
-
for (let cause = e, depth = 0; cause && depth < 8; depth++) {
|
|
2094
|
-
if (cause.name === "ChainMismatchError") return { reason: "CHAIN_SWITCH_FAILED", message: "The wallet network changed before the transaction was sent." };
|
|
2095
|
-
cause = cause.cause;
|
|
2096
|
-
}
|
|
2097
|
-
return { reason: "RPC_ERROR", message: friendlyErrorMessage(e) };
|
|
2098
|
-
}
|
|
2099
843
|
function errorReasonOf(e) {
|
|
2100
844
|
const { reason, message } = toReason(e);
|
|
2101
845
|
const ok = ["USER_REJECTED", "TX_REVERTED", "RATE_LIMITED", "RPC_ERROR", "CHAIN_SWITCH_FAILED", "ALLOWLIST_UNAVAILABLE"];
|
|
@@ -2276,7 +1020,7 @@ function useMint(options) {
|
|
|
2276
1020
|
if (cancelled) throw new MintError("USER_REJECTED", "The transaction was cancelled or replaced with a different transaction.");
|
|
2277
1021
|
if (await walletFor().getChainId() !== attempt.chainId) throw e;
|
|
2278
1022
|
assertActive();
|
|
2279
|
-
receipt2 = await walletFor().extend(
|
|
1023
|
+
receipt2 = await walletFor().extend(publicActions).waitForTransactionReceipt(params);
|
|
2280
1024
|
}
|
|
2281
1025
|
assertActive();
|
|
2282
1026
|
if (cancelled) throw new MintError("USER_REJECTED", "The transaction was cancelled or replaced with a different transaction.");
|
|
@@ -2299,7 +1043,7 @@ function useMint(options) {
|
|
|
2299
1043
|
}
|
|
2300
1044
|
} catch (e) {
|
|
2301
1045
|
if (isMintError(e)) throw e;
|
|
2302
|
-
const reverted = e instanceof
|
|
1046
|
+
const reverted = e instanceof BaseError2 ? e.walk((cause) => cause instanceof ExecutionRevertedError) : null;
|
|
2303
1047
|
if (reverted instanceof ExecutionRevertedError) {
|
|
2304
1048
|
throw new MintError("SIMULATION_REVERTED", reverted.shortMessage);
|
|
2305
1049
|
}
|
|
@@ -2869,13 +1613,13 @@ function useMint(options) {
|
|
|
2869
1613
|
|
|
2870
1614
|
// src/react/PayWithPicker.ts
|
|
2871
1615
|
import { createElement, useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
2872
|
-
import { formatUnits as
|
|
1616
|
+
import { formatUnits as formatUnits3 } from "viem";
|
|
2873
1617
|
var PICKER_REFRESH_MS = 2e4;
|
|
2874
1618
|
var PICKER_DEBOUNCE_MS = 500;
|
|
2875
1619
|
var RELAY_REASONS = /* @__PURE__ */ new Set(["BRIDGE_UNAVAILABLE", "BRIDGE_TIMEOUT", "BRIDGE_REFUNDED", "BRIDGE_FAILED"]);
|
|
2876
|
-
var
|
|
1620
|
+
var money2 = (value, chainId) => {
|
|
2877
1621
|
const c = getChain(chainId).nativeCurrency;
|
|
2878
|
-
return { value, decimals: c.decimals, symbol: c.symbol, formatted:
|
|
1622
|
+
return { value, decimals: c.decimals, symbol: c.symbol, formatted: formatUnits3(value, c.decimals) };
|
|
2879
1623
|
};
|
|
2880
1624
|
function usePayOptions(mint, onSelect) {
|
|
2881
1625
|
const info = mint.crossChain;
|
|
@@ -2951,7 +1695,7 @@ function usePayOptions(mint, onSelect) {
|
|
|
2951
1695
|
isDropChain,
|
|
2952
1696
|
relayFee: isDropChain ? null : quoted?.relayFee ?? null,
|
|
2953
1697
|
amountIn: isDropChain ? null : quoted?.amountIn ?? null,
|
|
2954
|
-
balance: balance && balance.chainId === chainId && chainId === current ?
|
|
1698
|
+
balance: balance && balance.chainId === chainId && chainId === current ? money2(balance.value, chainId) : null,
|
|
2955
1699
|
loading: !isDropChain && q === void 0,
|
|
2956
1700
|
select: () => select(chainId)
|
|
2957
1701
|
};
|
|
@@ -3062,7 +1806,7 @@ function MintButton(props) {
|
|
|
3062
1806
|
}
|
|
3063
1807
|
|
|
3064
1808
|
// src/index.ts
|
|
3065
|
-
var VERSION = "0.
|
|
1809
|
+
var VERSION = "0.3.0";
|
|
3066
1810
|
export {
|
|
3067
1811
|
CLICKABLE_STATES,
|
|
3068
1812
|
MAX_QUANTITY,
|