@orkid-labs/widget 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1022 @@
1
+ // #style-inject:#style-inject
2
+ function styleInject(css, { insertAt } = {}) {
3
+ if (!css || typeof document === "undefined") return;
4
+ const head = document.head || document.getElementsByTagName("head")[0];
5
+ const style = document.createElement("style");
6
+ style.type = "text/css";
7
+ if (insertAt === "top") {
8
+ if (head.firstChild) {
9
+ head.insertBefore(style, head.firstChild);
10
+ } else {
11
+ head.appendChild(style);
12
+ }
13
+ } else {
14
+ head.appendChild(style);
15
+ }
16
+ if (style.styleSheet) {
17
+ style.styleSheet.cssText = css;
18
+ } else {
19
+ style.appendChild(document.createTextNode(css));
20
+ }
21
+ }
22
+
23
+ // src/styles.css
24
+ styleInject(".orkid-widget {\n font-family: var(--orkid-widget-font-family);\n color: var(--orkid-widget-text);\n background: var(--orkid-widget-bg);\n border-radius: var(--orkid-widget-radius);\n border: 1px solid var(--orkid-widget-border);\n padding: 1rem;\n width: 100%;\n max-width: 28rem;\n box-sizing: border-box;\n}\n.orkid-widget *,\n.orkid-widget *::before,\n.orkid-widget *::after {\n box-sizing: border-box;\n}\n.orkid-widget-card {\n background: var(--orkid-widget-card);\n color: var(--orkid-widget-card-foreground);\n border-radius: var(--orkid-widget-radius);\n border: 1px solid var(--orkid-widget-border);\n}\n.orkid-widget-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n margin-bottom: 1rem;\n}\n.orkid-widget-title {\n font-size: 1rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.orkid-widget-label {\n font-size: 0.875rem;\n color: var(--orkid-widget-muted-foreground);\n margin-bottom: 0.25rem;\n}\n.orkid-widget-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.orkid-widget-input,\n.orkid-widget-select-trigger,\n.orkid-widget-button {\n font-family: inherit;\n font-size: 0.875rem;\n border-radius: var(--orkid-widget-radius);\n border: 1px solid var(--orkid-widget-input);\n background: var(--orkid-widget-card);\n color: var(--orkid-widget-card-foreground);\n padding: 0.5rem 0.75rem;\n min-height: 2.5rem;\n}\n.orkid-widget-input {\n flex: 1;\n outline: none;\n}\n.orkid-widget-input:focus,\n.orkid-widget-select-trigger:focus,\n.orkid-widget-button:focus {\n outline: 2px solid var(--orkid-widget-ring);\n outline-offset: -1px;\n}\n.orkid-widget-button {\n cursor: pointer;\n background: var(--orkid-widget-primary);\n color: var(--orkid-widget-primary-foreground);\n border-color: var(--orkid-widget-primary);\n width: 100%;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n}\n.orkid-widget-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.orkid-widget-button-secondary {\n background: var(--orkid-widget-muted);\n color: var(--orkid-widget-muted-foreground);\n border-color: var(--orkid-widget-border);\n}\n.orkid-widget-select-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n cursor: pointer;\n min-width: 7rem;\n}\n.orkid-widget-dropdown {\n position: absolute;\n z-index: 50;\n top: 100%;\n left: 0;\n margin-top: 0.25rem;\n width: 18rem;\n background: var(--orkid-widget-popover);\n color: var(--orkid-widget-popover-foreground);\n border: 1px solid var(--orkid-widget-border);\n border-radius: var(--orkid-widget-radius);\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n}\n.orkid-widget-dropdown-item {\n width: 100%;\n text-align: left;\n padding: 0.5rem 0.75rem;\n background: transparent;\n border: none;\n color: inherit;\n cursor: pointer;\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.orkid-widget-dropdown-item:hover {\n background: var(--orkid-widget-accent);\n color: var(--orkid-widget-accent-foreground);\n}\n.orkid-widget-dropdown-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.orkid-widget-search {\n width: 100%;\n border: none;\n background: transparent;\n color: inherit;\n padding: 0.5rem 0.75rem;\n outline: none;\n}\n.orkid-widget-success {\n color: var(--orkid-widget-success);\n background: color-mix(in srgb, var(--orkid-widget-success) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--orkid-widget-success) 30%, transparent);\n border-radius: var(--orkid-widget-radius);\n padding: 0.75rem;\n font-size: 0.875rem;\n}\n.orkid-widget-error {\n color: var(--orkid-widget-danger);\n background: color-mix(in srgb, var(--orkid-widget-danger) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--orkid-widget-danger) 30%, transparent);\n border-radius: var(--orkid-widget-radius);\n padding: 0.75rem;\n font-size: 0.875rem;\n}\n.orkid-widget-warning {\n color: var(--orkid-widget-warning);\n background: color-mix(in srgb, var(--orkid-widget-warning) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--orkid-widget-warning) 30%, transparent);\n border-radius: var(--orkid-widget-radius);\n padding: 0.75rem;\n font-size: 0.875rem;\n}\n.orkid-widget-info {\n color: var(--orkid-widget-muted-foreground);\n background: color-mix(in srgb, var(--orkid-widget-primary) 5%, transparent);\n border: 1px solid color-mix(in srgb, var(--orkid-widget-primary) 20%, transparent);\n border-radius: var(--orkid-widget-radius);\n padding: 0.75rem;\n font-size: 0.75rem;\n}\n.orkid-widget-footer {\n font-size: 0.75rem;\n color: var(--orkid-widget-muted-foreground);\n text-align: center;\n margin-top: 0.75rem;\n}\n.orkid-widget-spin {\n animation: orkid-spin 1s linear infinite;\n}\n@keyframes orkid-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n");
25
+
26
+ // src/OrkidSwapProvider.tsx
27
+ import { useMemo } from "react";
28
+ import { WagmiProvider, createConfig, http } from "wagmi";
29
+ import { base, mainnet, arbitrum, polygon } from "wagmi/chains";
30
+ import { injected, walletConnect } from "wagmi/connectors";
31
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
32
+ import { OrkidClient } from "@orkid/sdk";
33
+
34
+ // src/context.tsx
35
+ import { createContext, useContext } from "react";
36
+ var OrkidSwapContext = createContext(void 0);
37
+ function useOrkidSwapContext() {
38
+ const ctx = useContext(OrkidSwapContext);
39
+ if (!ctx) throw new Error("useOrkidSwapContext must be used within OrkidSwapProvider");
40
+ return ctx;
41
+ }
42
+
43
+ // src/theme.ts
44
+ var defaultLightTheme = {
45
+ background: "#ffffff",
46
+ card: "#ffffff",
47
+ cardForeground: "#0f172a",
48
+ primary: "#0052ff",
49
+ primaryForeground: "#ffffff",
50
+ text: "#0f172a",
51
+ muted: "#f1f5f9",
52
+ mutedForeground: "#64748b",
53
+ border: "#e2e8f0",
54
+ success: "#22c55e",
55
+ warning: "#f59e0b",
56
+ danger: "#ef4444",
57
+ radius: "0.625rem",
58
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
59
+ accent: "#f1f5f9",
60
+ accentForeground: "#0f172a",
61
+ popover: "#ffffff",
62
+ popoverForeground: "#0f172a",
63
+ input: "#e2e8f0",
64
+ ring: "#0052ff"
65
+ };
66
+ var defaultDarkTheme = {
67
+ background: "#0f172a",
68
+ card: "#1e293b",
69
+ cardForeground: "#f8fafc",
70
+ primary: "#3b82f6",
71
+ primaryForeground: "#ffffff",
72
+ text: "#f8fafc",
73
+ muted: "#334155",
74
+ mutedForeground: "#94a3b8",
75
+ border: "#334155",
76
+ success: "#4ade80",
77
+ warning: "#fbbf24",
78
+ danger: "#f87171",
79
+ radius: "0.625rem",
80
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
81
+ accent: "#334155",
82
+ accentForeground: "#f8fafc",
83
+ popover: "#1e293b",
84
+ popoverForeground: "#f8fafc",
85
+ input: "#334155",
86
+ ring: "#3b82f6"
87
+ };
88
+ function resolveTheme(partial = {}) {
89
+ return { ...defaultLightTheme, ...partial };
90
+ }
91
+ function themeToCssVariables(theme) {
92
+ return {
93
+ "--orkid-widget-bg": theme.background,
94
+ "--orkid-widget-card": theme.card,
95
+ "--orkid-widget-card-foreground": theme.cardForeground,
96
+ "--orkid-widget-primary": theme.primary,
97
+ "--orkid-widget-primary-foreground": theme.primaryForeground,
98
+ "--orkid-widget-text": theme.text,
99
+ "--orkid-widget-muted": theme.muted,
100
+ "--orkid-widget-muted-foreground": theme.mutedForeground,
101
+ "--orkid-widget-border": theme.border,
102
+ "--orkid-widget-success": theme.success,
103
+ "--orkid-widget-warning": theme.warning,
104
+ "--orkid-widget-danger": theme.danger,
105
+ "--orkid-widget-radius": theme.radius,
106
+ "--orkid-widget-font-family": theme.fontFamily,
107
+ "--orkid-widget-accent": theme.accent,
108
+ "--orkid-widget-accent-foreground": theme.accentForeground,
109
+ "--orkid-widget-popover": theme.popover,
110
+ "--orkid-widget-popover-foreground": theme.popoverForeground,
111
+ "--orkid-widget-input": theme.input,
112
+ "--orkid-widget-ring": theme.ring
113
+ };
114
+ }
115
+
116
+ // src/OrkidSwapProvider.tsx
117
+ import { jsx } from "react/jsx-runtime";
118
+ var DEFAULT_API_BASE_URL = "https://orkidlabs.xyz";
119
+ var queryClient = new QueryClient();
120
+ function OrkidSwapProvider({
121
+ apiKey = "",
122
+ apiBaseUrl = DEFAULT_API_BASE_URL,
123
+ partnerId,
124
+ walletMode = "built-in",
125
+ walletConnectProjectId,
126
+ wagmiConfig,
127
+ theme,
128
+ logoUrl,
129
+ brandName,
130
+ showPoweredBy = true,
131
+ supportedChains = [8453, 1, 42161, 137],
132
+ defaultFromToken,
133
+ defaultToToken,
134
+ defaultAmount,
135
+ defaultSlippageBps = 50,
136
+ tokenList,
137
+ gasless = true,
138
+ onQuote,
139
+ onSolve,
140
+ onError,
141
+ children
142
+ }) {
143
+ const client = useMemo(
144
+ () => new OrkidClient({ apiKey, baseUrl: apiBaseUrl }),
145
+ [apiKey, apiBaseUrl]
146
+ );
147
+ const resolvedTheme = useMemo(() => resolveTheme(theme), [theme]);
148
+ const resolvedWagmiConfig = useMemo(() => {
149
+ if (walletMode === "external") return wagmiConfig;
150
+ if (walletMode !== "built-in") return void 0;
151
+ const chainsToUse = [base];
152
+ if (supportedChains.includes(1)) chainsToUse.push(mainnet);
153
+ if (supportedChains.includes(42161)) chainsToUse.push(arbitrum);
154
+ if (supportedChains.includes(137)) chainsToUse.push(polygon);
155
+ const connectors = [injected({ shimDisconnect: true })];
156
+ if (walletConnectProjectId) {
157
+ connectors.push(
158
+ walletConnect({
159
+ projectId: walletConnectProjectId,
160
+ metadata: {
161
+ name: brandName || "Orkid Swap",
162
+ description: "Gasless token swaps \u2014 competitive, no gas",
163
+ url: "https://orkidlabs.xyz",
164
+ icons: logoUrl ? [logoUrl] : []
165
+ }
166
+ })
167
+ );
168
+ }
169
+ return createConfig({
170
+ chains: chainsToUse,
171
+ connectors,
172
+ transports: {
173
+ [base.id]: http("https://rpc.orkidlabs.com/rpc/base"),
174
+ [mainnet.id]: http("https://rpc.orkidlabs.com/rpc/ethereum"),
175
+ [arbitrum.id]: http("https://rpc.orkidlabs.com/rpc/arbitrum"),
176
+ [polygon.id]: http("https://rpc.orkidlabs.com/rpc/polygon")
177
+ }
178
+ });
179
+ }, [walletMode, wagmiConfig, supportedChains, walletConnectProjectId, brandName, logoUrl]);
180
+ const value = {
181
+ client,
182
+ wagmiConfig: resolvedWagmiConfig,
183
+ walletMode,
184
+ theme: resolvedTheme,
185
+ logoUrl,
186
+ brandName,
187
+ showPoweredBy,
188
+ supportedChains,
189
+ defaultFromToken,
190
+ defaultToToken,
191
+ defaultAmount,
192
+ defaultSlippageBps,
193
+ tokenList,
194
+ gasless,
195
+ partnerId,
196
+ onQuote,
197
+ onSolve,
198
+ onError
199
+ };
200
+ const content = /* @__PURE__ */ jsx(OrkidSwapContext.Provider, { value, children });
201
+ if (walletMode === "built-in" && resolvedWagmiConfig) {
202
+ return /* @__PURE__ */ jsx(WagmiProvider, { config: resolvedWagmiConfig, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: content }) });
203
+ }
204
+ return content;
205
+ }
206
+
207
+ // src/OrkidSwapWidget.tsx
208
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState3 } from "react";
209
+ import {
210
+ useAccount,
211
+ useChainId,
212
+ useConnect,
213
+ useDisconnect,
214
+ usePublicClient,
215
+ useReadContract,
216
+ useSendTransaction,
217
+ useSignTypedData,
218
+ useSwitchChain,
219
+ useWaitForTransactionReceipt,
220
+ useWriteContract
221
+ } from "wagmi";
222
+ import { parseAbi, maxUint256 } from "viem";
223
+ import {
224
+ buildPermit2Domain,
225
+ chainConfigFromName,
226
+ PERMIT2
227
+ } from "@orkid/sdk";
228
+
229
+ // src/TokenSelect.tsx
230
+ import { useEffect, useRef, useState } from "react";
231
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
232
+ function TokenSelect({
233
+ value,
234
+ onChange,
235
+ chainId,
236
+ chainName,
237
+ tokenList,
238
+ client,
239
+ label
240
+ }) {
241
+ const [open, setOpen] = useState(false);
242
+ const [search, setSearch] = useState("");
243
+ const [tokens, setTokens] = useState(tokenList || []);
244
+ const [loading, setLoading] = useState(false);
245
+ const ref = useRef(null);
246
+ const searchRef = useRef(null);
247
+ useEffect(() => {
248
+ if (tokenList) {
249
+ setTokens(tokenList.filter((t2) => t2.chain === chainName));
250
+ return;
251
+ }
252
+ if (!open) return;
253
+ setLoading(true);
254
+ const t = setTimeout(async () => {
255
+ try {
256
+ const res = await client.listTokens({ search, limit: 50, chain: chainName });
257
+ if (res.ok && res.tokens) {
258
+ setTokens(res.tokens.filter((t2) => t2.address !== "0x0000000000000000000000000000000000000000"));
259
+ }
260
+ } catch (err) {
261
+ console.error("TokenSelect fetch failed:", err);
262
+ } finally {
263
+ setLoading(false);
264
+ }
265
+ }, 200);
266
+ return () => clearTimeout(t);
267
+ }, [open, search, chainName, tokenList, client]);
268
+ useEffect(() => {
269
+ if (open && searchRef.current) searchRef.current.focus();
270
+ }, [open]);
271
+ useEffect(() => {
272
+ const handler = (e) => {
273
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
274
+ };
275
+ if (open) document.addEventListener("mousedown", handler);
276
+ return () => document.removeEventListener("mousedown", handler);
277
+ }, [open]);
278
+ useEffect(() => {
279
+ setSearch("");
280
+ if (!tokenList) setTokens([]);
281
+ }, [chainName, tokenList]);
282
+ return /* @__PURE__ */ jsxs("div", { ref, style: { position: "relative" }, children: [
283
+ /* @__PURE__ */ jsxs(
284
+ "button",
285
+ {
286
+ type: "button",
287
+ onClick: () => setOpen(!open),
288
+ className: "orkid-widget-select-trigger",
289
+ children: [
290
+ /* @__PURE__ */ jsx2("span", { children: value ? value.symbol : "Select" }),
291
+ /* @__PURE__ */ jsx2("span", { children: "\u25BC" })
292
+ ]
293
+ }
294
+ ),
295
+ open && /* @__PURE__ */ jsxs("div", { className: "orkid-widget-dropdown", children: [
296
+ /* @__PURE__ */ jsx2("div", { style: { borderBottom: "1px solid var(--orkid-widget-border)", padding: "0.5rem" }, children: /* @__PURE__ */ jsx2(
297
+ "input",
298
+ {
299
+ ref: searchRef,
300
+ value: search,
301
+ onChange: (e) => setSearch(e.target.value),
302
+ placeholder: "Search token...",
303
+ className: "orkid-widget-search"
304
+ }
305
+ ) }),
306
+ /* @__PURE__ */ jsx2("div", { style: { maxHeight: "16rem", overflowY: "auto" }, children: loading ? /* @__PURE__ */ jsx2("div", { style: { padding: "0.75rem", textAlign: "center", color: "var(--orkid-widget-muted-foreground)" }, children: "Loading..." }) : tokens.length === 0 ? /* @__PURE__ */ jsx2("div", { style: { padding: "0.75rem", textAlign: "center", color: "var(--orkid-widget-muted-foreground)" }, children: "No tokens found" }) : tokens.map((t) => /* @__PURE__ */ jsxs(
307
+ "button",
308
+ {
309
+ type: "button",
310
+ className: "orkid-widget-dropdown-item",
311
+ onClick: () => {
312
+ onChange(t);
313
+ setOpen(false);
314
+ setSearch("");
315
+ },
316
+ children: [
317
+ /* @__PURE__ */ jsx2("span", { style: { fontWeight: 500 }, children: t.symbol }),
318
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.75rem", fontFamily: "monospace", color: "var(--orkid-widget-muted-foreground)" }, children: [
319
+ t.address.slice(0, 6),
320
+ "...",
321
+ t.address.slice(-4)
322
+ ] })
323
+ ]
324
+ },
325
+ t.address
326
+ )) })
327
+ ] })
328
+ ] });
329
+ }
330
+
331
+ // src/ChainSelect.tsx
332
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
333
+ import { ORKID_CHAIN_CONFIG } from "@orkid/sdk";
334
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
335
+ var CHAIN_NAME = {
336
+ 8453: "base",
337
+ 1: "ethereum",
338
+ 42161: "arbitrum",
339
+ 137: "polygon"
340
+ };
341
+ var CHAIN_LIVE = {
342
+ 8453: true,
343
+ 1: true,
344
+ 42161: true,
345
+ 137: true
346
+ };
347
+ function ChainSelect({
348
+ selected,
349
+ onSelect,
350
+ supported
351
+ }) {
352
+ const [open, setOpen] = useState2(false);
353
+ const ref = useRef2(null);
354
+ useEffect2(() => {
355
+ const handler = (e) => {
356
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
357
+ };
358
+ if (open) document.addEventListener("mousedown", handler);
359
+ return () => document.removeEventListener("mousedown", handler);
360
+ }, [open]);
361
+ const currentConfig = ORKID_CHAIN_CONFIG[selected];
362
+ return /* @__PURE__ */ jsxs2("div", { ref, style: { position: "relative" }, children: [
363
+ /* @__PURE__ */ jsxs2(
364
+ "button",
365
+ {
366
+ type: "button",
367
+ onClick: () => setOpen(!open),
368
+ className: "orkid-widget-select-trigger",
369
+ style: { display: "flex", alignItems: "center", gap: "0.5rem" },
370
+ children: [
371
+ /* @__PURE__ */ jsx3(
372
+ "span",
373
+ {
374
+ style: {
375
+ width: "0.5rem",
376
+ height: "0.5rem",
377
+ borderRadius: "50%",
378
+ background: currentConfig?.color || "var(--orkid-widget-primary)"
379
+ }
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsx3("span", { children: currentConfig?.shortName || "BASE" }),
383
+ /* @__PURE__ */ jsx3("span", { children: "\u25BC" })
384
+ ]
385
+ }
386
+ ),
387
+ open && /* @__PURE__ */ jsx3("div", { className: "orkid-widget-dropdown", style: { right: 0, left: "auto", width: "12rem" }, children: supported.map((id) => {
388
+ const config = ORKID_CHAIN_CONFIG[id];
389
+ const live = CHAIN_LIVE[id];
390
+ return /* @__PURE__ */ jsxs2(
391
+ "button",
392
+ {
393
+ type: "button",
394
+ className: "orkid-widget-dropdown-item",
395
+ onClick: () => {
396
+ if (live) onSelect(id);
397
+ setOpen(false);
398
+ },
399
+ disabled: !live,
400
+ title: live ? void 0 : "Contact Orkid to enable this chain",
401
+ children: [
402
+ /* @__PURE__ */ jsxs2("span", { className: "orkid-widget-row", style: { gap: "0.5rem" }, children: [
403
+ /* @__PURE__ */ jsx3(
404
+ "span",
405
+ {
406
+ style: {
407
+ width: "0.5rem",
408
+ height: "0.5rem",
409
+ borderRadius: "50%",
410
+ background: config?.color || "var(--orkid-widget-primary)"
411
+ }
412
+ }
413
+ ),
414
+ /* @__PURE__ */ jsx3("span", { children: config?.name || CHAIN_NAME[id] })
415
+ ] }),
416
+ id === selected && /* @__PURE__ */ jsx3("span", { style: { color: "var(--orkid-widget-primary)" }, children: "\u2713" }),
417
+ !live && /* @__PURE__ */ jsx3("span", { style: { fontSize: "0.7rem", color: "var(--orkid-widget-muted-foreground)" }, children: "soon" })
418
+ ]
419
+ },
420
+ id
421
+ );
422
+ }) })
423
+ ] });
424
+ }
425
+
426
+ // src/OrkidSwapWidget.tsx
427
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
428
+ var erc20Abi = parseAbi([
429
+ "function allowance(address owner, address spender) view returns (uint256)",
430
+ "function approve(address spender, uint256 amount) returns (bool)",
431
+ "function balanceOf(address account) view returns (uint256)"
432
+ ]);
433
+ var permit2Abi = parseAbi([
434
+ "function nonceBitmap(address owner, uint256 wordPos) view returns (uint256)"
435
+ ]);
436
+ var PERMIT_TRANSFER_FROM_TYPES = {
437
+ PermitTransferFrom: [
438
+ { name: "permitted", type: "TokenPermissions" },
439
+ { name: "spender", type: "address" },
440
+ { name: "nonce", type: "uint256" },
441
+ { name: "deadline", type: "uint256" }
442
+ ],
443
+ TokenPermissions: [
444
+ { name: "token", type: "address" },
445
+ { name: "amount", type: "uint256" }
446
+ ]
447
+ };
448
+ var CHAIN_NAME2 = {
449
+ 8453: "base",
450
+ 1: "ethereum",
451
+ 42161: "arbitrum",
452
+ 137: "polygon"
453
+ };
454
+ var CHAIN_LIVE2 = {
455
+ 8453: true,
456
+ 1: true,
457
+ 42161: true,
458
+ 137: true
459
+ };
460
+ function OrkidSwapWidget({ className }) {
461
+ const ctx = useOrkidSwapContext();
462
+ const client = ctx.client;
463
+ const [selectedChainId, setSelectedChainId] = useState3(ctx.supportedChains[0] ?? 8453);
464
+ const [fromToken, setFromToken] = useState3(ctx.defaultFromToken);
465
+ const [toToken, setToToken] = useState3(ctx.defaultToToken);
466
+ const [amount, setAmount] = useState3(ctx.defaultAmount ?? "");
467
+ const [slippageBps, setSlippageBps] = useState3(ctx.defaultSlippageBps);
468
+ const [showSlippage, setShowSlippage] = useState3(false);
469
+ const [quote, setQuote] = useState3(null);
470
+ const [loading, setLoading] = useState3(null);
471
+ const [error, setError] = useState3(null);
472
+ const [txHash, setTxHash] = useState3(null);
473
+ const [execMode, setExecMode] = useState3(null);
474
+ const { address, isConnected } = useAccount();
475
+ const walletChainId = useChainId();
476
+ const { connect, connectors, isPending: isConnecting } = useConnect();
477
+ const { disconnect } = useDisconnect();
478
+ const { switchChain } = useSwitchChain();
479
+ const publicClient = usePublicClient({ chainId: selectedChainId });
480
+ const { signTypedDataAsync } = useSignTypedData();
481
+ const { writeContract: approveWrite, data: approveTxHash, isPending: isApproving } = useWriteContract();
482
+ const { isSuccess: approvalConfirmed } = useWaitForTransactionReceipt({ hash: approveTxHash });
483
+ const { sendTransactionAsync, data: fallbackTxHash, isPending: isSendingFallback } = useSendTransaction();
484
+ const { isSuccess: fallbackConfirmed } = useWaitForTransactionReceipt({ hash: fallbackTxHash });
485
+ const { isSuccess: swapConfirmed } = useWaitForTransactionReceipt({
486
+ hash: txHash,
487
+ chainId: selectedChainId
488
+ });
489
+ const isOnCorrectChain = walletChainId === selectedChainId;
490
+ const chainName = CHAIN_NAME2[selectedChainId] ?? "base";
491
+ const chainLive = CHAIN_LIVE2[selectedChainId] ?? false;
492
+ const { data: fromTokenBalance } = useReadContract({
493
+ address: fromToken?.address,
494
+ abi: erc20Abi,
495
+ functionName: "balanceOf",
496
+ args: address && fromToken ? [address] : void 0,
497
+ chainId: selectedChainId,
498
+ query: { enabled: !!address && !!fromToken && chainLive }
499
+ });
500
+ const { data: erc20Allowance, refetch: refetchAllowance } = useReadContract({
501
+ address: fromToken?.address,
502
+ abi: erc20Abi,
503
+ functionName: "allowance",
504
+ args: address && fromToken ? [address, PERMIT2] : void 0,
505
+ chainId: selectedChainId,
506
+ query: { enabled: !!address && !!fromToken && chainLive }
507
+ });
508
+ useEffect3(() => {
509
+ if (approvalConfirmed) {
510
+ refetchAllowance();
511
+ setLoading(null);
512
+ }
513
+ }, [approvalConfirmed, refetchAllowance]);
514
+ useEffect3(() => {
515
+ if (fallbackConfirmed && fallbackTxHash) {
516
+ setTxHash(fallbackTxHash);
517
+ setLoading(null);
518
+ setAmount("");
519
+ setQuote(null);
520
+ client.confirmTransaction?.(fallbackTxHash, chainName).catch(() => {
521
+ });
522
+ ctx.onSolve?.({ ok: true, transaction: { txHash: fallbackTxHash, to: "", data: "", value: "0", chain: chainName }, computeMs: 0 });
523
+ }
524
+ }, [fallbackConfirmed, fallbackTxHash, chainName, ctx, client]);
525
+ const chainConfig = useMemo2(() => {
526
+ try {
527
+ return chainConfigFromName(chainName);
528
+ } catch {
529
+ return void 0;
530
+ }
531
+ }, [chainName]);
532
+ const fetchQuote = useCallback(async () => {
533
+ if (!amount || Number.parseFloat(amount) <= 0 || !fromToken || !toToken || !chainLive) {
534
+ setQuote(null);
535
+ return;
536
+ }
537
+ setLoading("quote");
538
+ setError(null);
539
+ try {
540
+ const res = await client.getQuote({
541
+ from: fromToken.symbol,
542
+ to: toToken.symbol,
543
+ amount,
544
+ chain: chainName,
545
+ fromAddress: fromToken.address,
546
+ toAddress: toToken.address,
547
+ fromDecimals: fromToken.decimals,
548
+ toDecimals: toToken.decimals
549
+ });
550
+ setQuote(res);
551
+ ctx.onQuote?.(res);
552
+ if (!res.ok) setError(res.error || "Quote failed");
553
+ } catch (e) {
554
+ setError(e.message || "Quote failed");
555
+ setQuote(null);
556
+ ctx.onError?.(e.message || "Quote failed");
557
+ } finally {
558
+ setLoading(null);
559
+ }
560
+ }, [amount, fromToken, toToken, chainName, chainLive, client, ctx]);
561
+ useEffect3(() => {
562
+ const t = setTimeout(fetchQuote, 400);
563
+ return () => clearTimeout(t);
564
+ }, [fetchQuote]);
565
+ useEffect3(() => {
566
+ setFromToken(ctx.defaultFromToken);
567
+ setToToken(ctx.defaultToToken);
568
+ setAmount(ctx.defaultAmount ?? "");
569
+ setQuote(null);
570
+ setError(null);
571
+ setTxHash(null);
572
+ }, [selectedChainId, ctx.defaultFromToken, ctx.defaultToToken, ctx.defaultAmount]);
573
+ async function findUnusedNonce(owner) {
574
+ if (!publicClient) throw new Error("No public client");
575
+ const arr = new Uint32Array(1);
576
+ crypto.getRandomValues(arr);
577
+ const startWord = BigInt(arr[0] >> 8);
578
+ const MAX_OFFSET = BigInt(1e3);
579
+ const MODULUS = BigInt(1e6);
580
+ const EIGHT = BigInt(8);
581
+ const ONE = BigInt(1);
582
+ const ZERO = BigInt(0);
583
+ for (let offset = BigInt(0); offset < MAX_OFFSET; offset++) {
584
+ const wordPos = (startWord + offset) % MODULUS;
585
+ const bitmap = await publicClient.readContract({
586
+ address: PERMIT2,
587
+ abi: permit2Abi,
588
+ functionName: "nonceBitmap",
589
+ args: [owner, wordPos]
590
+ });
591
+ if (bitmap === maxUint256) continue;
592
+ for (let bitPos = 0; bitPos < 256; bitPos++) {
593
+ const bit = ONE << BigInt(bitPos);
594
+ if ((bitmap & bit) === ZERO) {
595
+ return wordPos << EIGHT | BigInt(bitPos);
596
+ }
597
+ }
598
+ }
599
+ throw new Error("No unused Permit2 nonce found");
600
+ }
601
+ function parseAmount(value, decimals) {
602
+ const normalized = value.trim();
603
+ if (!/^\d+(\.\d+)?$/.test(normalized)) throw new Error("Invalid amount");
604
+ const [whole, fraction = ""] = normalized.split(".");
605
+ if (fraction.length > decimals) throw new Error(`Amount supports at most ${decimals} decimals`);
606
+ const scale = BigInt("1" + "0".repeat(decimals));
607
+ return BigInt(whole) * scale + BigInt(fraction.padEnd(decimals, "0") || "0");
608
+ }
609
+ async function handleSwap() {
610
+ if (!isConnected || !address || !fromToken || !toToken || !quote?.ok) {
611
+ setError("Connect a wallet and get a valid quote");
612
+ return;
613
+ }
614
+ if (!isOnCorrectChain || !chainLive) {
615
+ setError(`Please switch to ${chainConfig?.shortName || "Base"}`);
616
+ return;
617
+ }
618
+ setError(null);
619
+ setTxHash(null);
620
+ try {
621
+ const amountRaw = parseAmount(amount, fromToken.decimals);
622
+ if (fromTokenBalance !== void 0 && fromTokenBalance < amountRaw) {
623
+ const balHuman = Number(fromTokenBalance) / 10 ** fromToken.decimals;
624
+ setError(`Insufficient ${fromToken.symbol} balance. You have ${balHuman.toFixed(6)} ${fromToken.symbol} but tried ${amount}.`);
625
+ return;
626
+ }
627
+ const needsApproval = erc20Allowance === void 0 || erc20Allowance === BigInt(0);
628
+ if (needsApproval) {
629
+ setLoading("approve");
630
+ approveWrite({
631
+ address: fromToken.address,
632
+ abi: erc20Abi,
633
+ functionName: "approve",
634
+ args: [PERMIT2, maxUint256],
635
+ chainId: selectedChainId
636
+ });
637
+ return;
638
+ }
639
+ const nonce = await findUnusedNonce(address);
640
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
641
+ const spender = chainConfig?.tvmExecutor;
642
+ if (!spender) throw new Error("No TVMExecutor configured for this chain");
643
+ setLoading("sign");
644
+ const signature = await signTypedDataAsync({
645
+ domain: buildPermit2Domain(Number(walletChainId)),
646
+ types: PERMIT_TRANSFER_FROM_TYPES,
647
+ primaryType: "PermitTransferFrom",
648
+ message: {
649
+ permitted: { token: fromToken.address, amount: amountRaw },
650
+ spender,
651
+ nonce,
652
+ deadline
653
+ }
654
+ });
655
+ if (!signature || signature.length < 10) {
656
+ setError("Signature not provided");
657
+ setLoading(null);
658
+ return;
659
+ }
660
+ const belowMin = quote?.gaslessEligible === false;
661
+ const wantsGasless = ctx.gasless && !belowMin;
662
+ setLoading("solve");
663
+ setExecMode(wantsGasless ? "gasless" : "user-pays");
664
+ const permit = {
665
+ permitted: { token: fromToken.address, amount: amountRaw.toString() },
666
+ nonce: nonce.toString(),
667
+ deadline: deadline.toString()
668
+ };
669
+ const solveRes = await client.solve({
670
+ from: fromToken.symbol,
671
+ to: toToken.symbol,
672
+ amount,
673
+ chain: chainName,
674
+ user: address,
675
+ fromAddress: fromToken.address,
676
+ toAddress: toToken.address,
677
+ fromDecimals: fromToken.decimals,
678
+ toDecimals: toToken.decimals,
679
+ permit,
680
+ signature,
681
+ slippageBps,
682
+ dryRun: !wantsGasless
683
+ });
684
+ if (solveRes.ok && solveRes.transaction?.txHash) {
685
+ setTxHash(solveRes.transaction.txHash);
686
+ setAmount("");
687
+ setQuote(null);
688
+ ctx.onSolve?.(solveRes);
689
+ return;
690
+ }
691
+ if (solveRes.error?.includes("Gas estimation failed")) {
692
+ setError(`Solver rejected the swap: ${solveRes.error}`);
693
+ setLoading(null);
694
+ return;
695
+ }
696
+ if (!wantsGasless) {
697
+ if (!solveRes.ok || !solveRes.transaction) {
698
+ setError(solveRes.error || "Failed to encode swap");
699
+ setLoading(null);
700
+ return;
701
+ }
702
+ setExecMode("user-pays");
703
+ await sendTransactionAsync({
704
+ to: solveRes.transaction.to,
705
+ data: solveRes.transaction.data,
706
+ value: BigInt(solveRes.transaction.value || "0"),
707
+ chainId: selectedChainId
708
+ });
709
+ return;
710
+ }
711
+ const dryRes = await client.dryRun({
712
+ from: fromToken.symbol,
713
+ to: toToken.symbol,
714
+ amount,
715
+ chain: chainName,
716
+ user: address,
717
+ fromAddress: fromToken.address,
718
+ toAddress: toToken.address,
719
+ fromDecimals: fromToken.decimals,
720
+ toDecimals: toToken.decimals,
721
+ permit,
722
+ signature,
723
+ slippageBps
724
+ });
725
+ if (!dryRes.ok || !dryRes.transaction) {
726
+ setError(dryRes.error || solveRes.error || "Solver failed to encode swap");
727
+ setLoading(null);
728
+ return;
729
+ }
730
+ setExecMode("user-pays");
731
+ await sendTransactionAsync({
732
+ to: dryRes.transaction.to,
733
+ data: dryRes.transaction.data,
734
+ value: BigInt(dryRes.transaction.value || "0"),
735
+ chainId: selectedChainId
736
+ });
737
+ } catch (e) {
738
+ if (e.code === 4001 || e.message?.includes("rejected")) {
739
+ setError("Signature or transaction rejected");
740
+ } else {
741
+ setError(`[${e.code || "?"}] ${e.message || "Swap failed"}`);
742
+ }
743
+ setLoading(null);
744
+ ctx.onError?.(e.message || "Swap failed");
745
+ }
746
+ }
747
+ function handleConnect() {
748
+ const connector = connectors[0];
749
+ if (connector) {
750
+ connect({ connector });
751
+ } else {
752
+ setError("No wallet connector found");
753
+ }
754
+ }
755
+ function handleChainChange(id) {
756
+ if (!CHAIN_LIVE2[id]) return;
757
+ setSelectedChainId(id);
758
+ switchChain({ chainId: id });
759
+ }
760
+ const isBusy = loading !== null || isConnecting || isApproving || isSendingFallback;
761
+ const highImpact = quote?.ok && quote.quote?.priceImpactBps != null && quote.quote.priceImpactBps >= 300;
762
+ const canSwap = isConnected && isOnCorrectChain && chainLive && !!amount && !!fromToken && !!toToken && quote?.ok && !isBusy && !highImpact;
763
+ const buttonLabel = () => {
764
+ if (!isConnected) return "Connect Wallet";
765
+ if (!isOnCorrectChain || !chainLive) return `Switch to ${chainConfig?.shortName || "Base"}`;
766
+ if (loading === "quote") return "Fetching quote...";
767
+ if (loading === "approve" || isApproving) return "Approve Permit2...";
768
+ if (loading === "sign") return "Sign in wallet...";
769
+ if (loading === "solve" && execMode === "gasless") return "Executing gasless swap...";
770
+ if (loading === "solve" && execMode === "user-pays") return "Preparing fallback...";
771
+ if (isSendingFallback) return "Confirm in wallet...";
772
+ return "Swap";
773
+ };
774
+ const handleButtonClick = () => {
775
+ if (!isConnected) return handleConnect();
776
+ if (!isOnCorrectChain || !chainLive) {
777
+ if (chainLive) switchChain({ chainId: selectedChainId });
778
+ return;
779
+ }
780
+ handleSwap();
781
+ };
782
+ const explorer = chainConfig?.explorer || "https://basescan.org";
783
+ const style = useMemo2(() => themeToCssVariables(ctx.theme), [ctx.theme]);
784
+ return /* @__PURE__ */ jsxs3("div", { className: `orkid-widget ${className ?? ""}`, style, children: [
785
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-header", children: [
786
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-title", children: [
787
+ ctx.logoUrl && /* @__PURE__ */ jsx4("img", { src: ctx.logoUrl, alt: "", className: "w-6 h-6 object-contain" }),
788
+ /* @__PURE__ */ jsx4("span", { children: ctx.brandName || "Swap" })
789
+ ] }),
790
+ /* @__PURE__ */ jsx4(ChainSelect, { selected: selectedChainId, onSelect: handleChainChange, supported: ctx.supportedChains })
791
+ ] }),
792
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
793
+ isConnected && address ? /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between" }, children: [
794
+ /* @__PURE__ */ jsxs3("span", { className: "orkid-widget-muted-text", style: { fontFamily: "monospace", fontSize: "0.875rem" }, children: [
795
+ address.slice(0, 6),
796
+ "...",
797
+ address.slice(-4)
798
+ ] }),
799
+ /* @__PURE__ */ jsx4("button", { className: "orkid-widget-button-secondary", style: { width: "auto" }, onClick: () => disconnect(), children: "Disconnect" })
800
+ ] }) : /* @__PURE__ */ jsx4("button", { className: "orkid-widget-button", onClick: handleConnect, disabled: isConnecting, children: isConnecting ? "Connecting..." : "Connect Wallet" }),
801
+ /* @__PURE__ */ jsxs3("div", { children: [
802
+ /* @__PURE__ */ jsx4("div", { className: "orkid-widget-label", children: "From" }),
803
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", children: [
804
+ /* @__PURE__ */ jsx4(TokenSelect, { value: fromToken, onChange: setFromToken, chainId: selectedChainId, chainName, tokenList: ctx.tokenList, client }),
805
+ /* @__PURE__ */ jsx4(
806
+ "input",
807
+ {
808
+ type: "number",
809
+ value: amount,
810
+ onChange: (e) => setAmount(e.target.value),
811
+ placeholder: "0.0",
812
+ className: "orkid-widget-input",
813
+ disabled: !chainLive
814
+ }
815
+ )
816
+ ] })
817
+ ] }),
818
+ /* @__PURE__ */ jsxs3("div", { children: [
819
+ /* @__PURE__ */ jsx4("div", { className: "orkid-widget-label", children: "To" }),
820
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", children: [
821
+ /* @__PURE__ */ jsx4(TokenSelect, { value: toToken, onChange: setToToken, chainId: selectedChainId, chainName, tokenList: ctx.tokenList, client }),
822
+ /* @__PURE__ */ jsx4("div", { className: "orkid-widget-input", style: { display: "flex", alignItems: "center", fontFamily: "monospace", fontSize: "0.875rem" }, children: loading === "quote" ? /* @__PURE__ */ jsx4("span", { className: "orkid-widget-spin", children: "\u21BB" }) : quote?.ok ? quote.quote?.amountOut || "--" : "--" })
823
+ ] })
824
+ ] }),
825
+ quote?.ok && quote.savings && /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-card", style: { padding: "0.75rem" }, children: [
826
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.875rem" }, children: [
827
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-muted-foreground)" }, children: "Orkid Fee" }),
828
+ /* @__PURE__ */ jsxs3("span", { style: { color: "var(--orkid-widget-primary)", fontWeight: 600 }, children: [
829
+ quote.savings.orkidBps,
830
+ " bps"
831
+ ] })
832
+ ] }),
833
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.875rem" }, children: [
834
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-muted-foreground)" }, children: "MetaMask Avg" }),
835
+ /* @__PURE__ */ jsxs3("span", { style: { textDecoration: "line-through" }, children: [
836
+ quote.savings.metamaskBps,
837
+ " bps"
838
+ ] })
839
+ ] }),
840
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.875rem", borderTop: "1px solid var(--orkid-widget-border)", paddingTop: "0.5rem", marginTop: "0.5rem" }, children: [
841
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-primary)" }, children: "You Save" }),
842
+ /* @__PURE__ */ jsxs3("span", { style: { fontWeight: 700, color: "var(--orkid-widget-primary)" }, children: [
843
+ quote.savings.savingsMultiplier,
844
+ " lower fees"
845
+ ] })
846
+ ] }),
847
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.75rem" }, children: [
848
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-muted-foreground)" }, children: "Gas" }),
849
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-success)" }, children: quote?.gaslessEligible === false ? "You pay (below $20 gasless min)" : execMode === "user-pays" ? "User pays" : "Gasless" })
850
+ ] })
851
+ ] }),
852
+ quote?.ok && quote.quote?.amountOut && /* @__PURE__ */ jsxs3("div", { children: [
853
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.875rem" }, children: [
854
+ /* @__PURE__ */ jsx4("span", { style: { color: "var(--orkid-widget-muted-foreground)" }, children: "Minimum receive" }),
855
+ /* @__PURE__ */ jsxs3("span", { style: { fontFamily: "monospace" }, children: [
856
+ (Number(quote.quote.amountOut) * (1 - slippageBps / 1e4)).toFixed(6),
857
+ " ",
858
+ toToken?.symbol
859
+ ] })
860
+ ] }),
861
+ /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", style: { justifyContent: "space-between", fontSize: "0.75rem" }, children: [
862
+ /* @__PURE__ */ jsxs3("button", { onClick: () => setShowSlippage(!showSlippage), style: { color: "var(--orkid-widget-muted-foreground)", background: "none", border: "none", cursor: "pointer" }, children: [
863
+ "Slippage: ",
864
+ (slippageBps / 100).toFixed(1),
865
+ "% ",
866
+ showSlippage ? "\u25B2" : "\u25BC"
867
+ ] }),
868
+ showSlippage && /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-row", children: [
869
+ [10, 50, 100, 200].map((bps) => /* @__PURE__ */ jsxs3(
870
+ "button",
871
+ {
872
+ onClick: () => setSlippageBps(bps),
873
+ className: slippageBps === bps ? "orkid-widget-button" : "orkid-widget-button-secondary",
874
+ style: { width: "auto", padding: "0.25rem 0.5rem", fontSize: "0.75rem" },
875
+ children: [
876
+ bps / 100,
877
+ "%"
878
+ ]
879
+ },
880
+ bps
881
+ )),
882
+ /* @__PURE__ */ jsx4(
883
+ "input",
884
+ {
885
+ type: "number",
886
+ min: 1,
887
+ max: 500,
888
+ step: 1,
889
+ placeholder: "custom bps",
890
+ className: "orkid-widget-input",
891
+ style: { width: "6.5rem", padding: "0.25rem 0.5rem", fontSize: "0.75rem" },
892
+ onChange: (e) => {
893
+ const v = Number(e.target.value);
894
+ if (v > 0 && v <= 500) setSlippageBps(v);
895
+ }
896
+ }
897
+ )
898
+ ] })
899
+ ] })
900
+ ] }),
901
+ quote?.ok && quote.quote?.priceImpactBps != null && quote.quote.priceImpactBps > 100 && (quote.quote.priceImpactBps >= 300 ? /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-warning", children: [
902
+ /* @__PURE__ */ jsxs3("div", { style: { fontWeight: 600 }, children: [
903
+ "High Price Impact: ",
904
+ (quote.quote.priceImpactBps / 100).toFixed(1),
905
+ "%"
906
+ ] }),
907
+ /* @__PURE__ */ jsx4("p", { style: { fontSize: "0.875rem", margin: "0.25rem 0 0" }, children: "This size exceeds available pool liquidity. Contact Orkid for OTC/API access." })
908
+ ] }) : /* @__PURE__ */ jsxs3("div", { style: { color: "var(--orkid-widget-warning)", fontSize: "0.75rem" }, children: [
909
+ "Price impact: ",
910
+ (quote.quote.priceImpactBps / 100).toFixed(2),
911
+ "%"
912
+ ] })),
913
+ txHash && /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-success", children: [
914
+ swapConfirmed ? /* @__PURE__ */ jsxs3(Fragment, { children: [
915
+ "Swap confirmed",
916
+ execMode === "user-pays" ? " (you paid gas)" : " (gasless)"
917
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
918
+ /* @__PURE__ */ jsx4("span", { className: "orkid-widget-spin", style: { marginRight: "0.5rem" }, children: "\u21BB" }),
919
+ "Swap submitted",
920
+ execMode === "user-pays" ? " (you paid gas)" : " (gasless)",
921
+ " \u2014 confirming\u2026"
922
+ ] }),
923
+ " ",
924
+ /* @__PURE__ */ jsx4("a", { href: `${explorer}/tx/${txHash}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "underline" }, children: "View" }),
925
+ swapConfirmed && /* @__PURE__ */ jsx4(
926
+ "button",
927
+ {
928
+ className: "orkid-widget-button-secondary",
929
+ style: { width: "auto", padding: "0.25rem 0.75rem", fontSize: "0.75rem", marginLeft: "0.5rem" },
930
+ onClick: () => {
931
+ setTxHash(null);
932
+ setExecMode(null);
933
+ setQuote(null);
934
+ setAmount("");
935
+ },
936
+ children: "Swap again"
937
+ }
938
+ )
939
+ ] }),
940
+ quote?.ok && quote.gaslessEligible === false && !txHash && !error && /* @__PURE__ */ jsx4("div", { style: { color: "var(--orkid-widget-muted-foreground)", fontSize: "0.75rem" }, children: "Below the gasless minimum \u2014 you will submit this swap and pay gas yourself." }),
941
+ isConnected && canSwap && !txHash && !error && /* @__PURE__ */ jsxs3("div", { className: "orkid-widget-info", children: [
942
+ /* @__PURE__ */ jsx4("p", { style: { fontWeight: 600 }, children: "How this works:" }),
943
+ /* @__PURE__ */ jsx4("p", { children: "1. Sign a permit off-chain (no gas) to authorize the solver." }),
944
+ /* @__PURE__ */ jsx4("p", { children: "2. The solver sends the transaction and pays the gas." })
945
+ ] }),
946
+ /* @__PURE__ */ jsxs3("button", { className: "orkid-widget-button", onClick: handleButtonClick, disabled: isConnected && !canSwap, children: [
947
+ isBusy && /* @__PURE__ */ jsx4("span", { className: "orkid-widget-spin", style: { marginRight: "0.5rem" }, children: "\u21BB" }),
948
+ buttonLabel()
949
+ ] }),
950
+ error && /* @__PURE__ */ jsx4("div", { className: "orkid-widget-error", children: error }),
951
+ ctx.showPoweredBy && /* @__PURE__ */ jsx4("div", { className: "orkid-widget-footer", children: "Powered by Orkid \xB7 Competitive fee \xB7 Gasless" })
952
+ ] })
953
+ ] });
954
+ }
955
+
956
+ // src/IframeWidget.tsx
957
+ import { useMemo as useMemo3 } from "react";
958
+ import { jsx as jsx5 } from "react/jsx-runtime";
959
+ function OrkidSwapIframe({
960
+ src = "https://widget.orkidlabs.xyz",
961
+ partner,
962
+ brandName,
963
+ logoUrl,
964
+ theme,
965
+ fromToken,
966
+ toToken,
967
+ amount,
968
+ showPoweredBy,
969
+ allowFullScreen,
970
+ className,
971
+ style
972
+ }) {
973
+ const url = useMemo3(() => {
974
+ const u = new URL(src);
975
+ if (partner) u.searchParams.set("partner", partner);
976
+ if (brandName) u.searchParams.set("brand", brandName);
977
+ if (logoUrl) u.searchParams.set("logo", logoUrl);
978
+ if (theme) u.searchParams.set("theme", encodeURIComponent(JSON.stringify(theme)));
979
+ if (fromToken) u.searchParams.set("from", fromToken);
980
+ if (toToken) u.searchParams.set("to", toToken);
981
+ if (amount) u.searchParams.set("amount", amount);
982
+ if (showPoweredBy !== void 0) u.searchParams.set("poweredBy", String(showPoweredBy));
983
+ return u.toString();
984
+ }, [src, partner, brandName, logoUrl, theme, fromToken, toToken, amount, showPoweredBy]);
985
+ return /* @__PURE__ */ jsx5(
986
+ "iframe",
987
+ {
988
+ src: url,
989
+ className,
990
+ style: { width: "100%", height: "100%", minHeight: "560px", border: "none", ...style },
991
+ allowFullScreen,
992
+ title: "Orkid Swap"
993
+ }
994
+ );
995
+ }
996
+
997
+ // src/embed.ts
998
+ import { createElement } from "react";
999
+ import { createRoot } from "react-dom/client";
1000
+ function mountOrkidSwap(options) {
1001
+ const { target, ...props } = options;
1002
+ const el = typeof target === "string" ? document.querySelector(target) : target;
1003
+ if (!el) throw new Error(`OrkidSwap: target element not found: ${target}`);
1004
+ const root = createRoot(el);
1005
+ root.render(
1006
+ createElement(OrkidSwapProvider, props, createElement(OrkidSwapWidget))
1007
+ );
1008
+ return () => root.unmount();
1009
+ }
1010
+ export {
1011
+ OrkidSwapContext,
1012
+ OrkidSwapIframe,
1013
+ OrkidSwapProvider,
1014
+ OrkidSwapWidget,
1015
+ defaultDarkTheme,
1016
+ defaultLightTheme,
1017
+ mountOrkidSwap,
1018
+ resolveTheme,
1019
+ themeToCssVariables,
1020
+ useOrkidSwapContext
1021
+ };
1022
+ //# sourceMappingURL=index.mjs.map