@xswap-link/sdk 0.15.0 → 0.15.2

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.
Files changed (58) hide show
  1. package/.eslintrc.json +1 -0
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +22 -4
  4. package/dist/index.d.mts +47 -1
  5. package/dist/index.d.ts +47 -1
  6. package/dist/index.global.js +94 -88
  7. package/dist/index.js +1378 -985
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +1256 -863
  10. package/dist/index.mjs.map +1 -1
  11. package/example/index.html +12 -0
  12. package/example/package.json +24 -0
  13. package/example/pnpm-lock.yaml +1164 -0
  14. package/example/src/App.tsx +421 -0
  15. package/example/src/main.tsx +8 -0
  16. package/example/vite.config.ts +9 -0
  17. package/package.json +2 -1
  18. package/postcss.config.js +2 -0
  19. package/src/assets/icons/ChevronRightThinIcon.tsx +23 -0
  20. package/src/assets/icons/InfoThinIcon.tsx +33 -0
  21. package/src/assets/icons/RedirectThinIcon.tsx +15 -0
  22. package/src/assets/icons/index.ts +3 -0
  23. package/src/components/Swap/Header/index.tsx +3 -1
  24. package/src/components/Swap/HistoryView/ActivityCard/index.tsx +2 -2
  25. package/src/components/Swap/ReorderButton/ReorderButton.tsx +4 -1
  26. package/src/components/Swap/SettingsView/Delivery/index.tsx +3 -3
  27. package/src/components/Swap/SettingsView/InfiniteApproval/index.tsx +3 -3
  28. package/src/components/Swap/SettingsView/Slippage/index.tsx +5 -3
  29. package/src/components/Swap/SwapView/ConfirmationView/TokenTiles/index.tsx +5 -2
  30. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +1 -1
  31. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +12 -7
  32. package/src/components/Swap/SwapView/ConfirmationView/TxProgress/index.tsx +136 -0
  33. package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +108 -26
  34. package/src/components/Swap/SwapView/ConfirmationView/index.tsx +19 -4
  35. package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +12 -6
  36. package/src/components/Swap/SwapView/FeesPanel/index.tsx +3 -1
  37. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +9 -0
  38. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/ChainPicker/index.tsx +1 -1
  39. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx +52 -42
  40. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +1 -1
  41. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +5 -5
  42. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +24 -2
  43. package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +14 -3
  44. package/src/components/Swap/SwapView/SwapPanel/index.tsx +14 -2
  45. package/src/components/Swap/SwapView/WalletPicker/index.tsx +1 -1
  46. package/src/components/ToggleButton/index.tsx +5 -3
  47. package/src/components/TxModal/index.tsx +2 -6
  48. package/src/components/global.css +85 -0
  49. package/src/config/fonts.ts +15 -2
  50. package/src/context/SwapProvider.tsx +22 -7
  51. package/src/context/TxUIWrapper.tsx +34 -4
  52. package/src/hooks/index.ts +1 -0
  53. package/src/hooks/useCrossChainDelivery.ts +143 -0
  54. package/src/models/Route.ts +13 -0
  55. package/src/models/TransactionDelivery.ts +40 -0
  56. package/src/models/index.ts +1 -0
  57. package/src/services/api.ts +23 -0
  58. package/tailwind.config.js +3 -9
@@ -0,0 +1,421 @@
1
+ import { TxWidget } from "@xswap-link/sdk";
2
+ import { useEffect, useMemo, useState } from "react";
3
+
4
+ type Token = { address: string; symbol: string; quickPick?: boolean };
5
+ type Chain = {
6
+ chainId: string;
7
+ displayName: string;
8
+ ecosystem: string;
9
+ web3Environment: string;
10
+ bridgeSupported: boolean;
11
+ swapSupported: boolean;
12
+ tokens: Token[];
13
+ };
14
+
15
+ type Cfg = Record<string, string | boolean | undefined>;
16
+
17
+ const DEFAULT_API = "https://xswap.link/api";
18
+
19
+ const API_URLS = [
20
+ DEFAULT_API,
21
+ "https://develop-72sp7lmyaa-ew.a.run.app/api/beta",
22
+ "http://localhost:3000/api",
23
+ ];
24
+
25
+ const FONTS = ["Inter", "Roboto", "Poppins", "monospace", "serif"];
26
+
27
+ const BOOL_FIELDS = [
28
+ "lightTheme",
29
+ "defaultWalletPicker",
30
+ "bridge",
31
+ "srcChainLocked",
32
+ "srcTokenLocked",
33
+ "dstChainLocked",
34
+ "dstTokenLocked",
35
+ ];
36
+
37
+ const DEFAULTS: Cfg = {
38
+ integratorId: "d6e438dfa14e80701b9b",
39
+ defaultWalletPicker: true,
40
+ bridge: true,
41
+ };
42
+
43
+ const STORAGE_KEY = "xswap-example-config";
44
+
45
+ const loadCfg = (): Cfg => {
46
+ try {
47
+ return { ...DEFAULTS, ...JSON.parse(localStorage.getItem(STORAGE_KEY)!) };
48
+ } catch {
49
+ return DEFAULTS;
50
+ }
51
+ };
52
+
53
+ const str = (v: Cfg[string]) =>
54
+ typeof v === "string" && v.trim() ? v.trim() : undefined;
55
+
56
+ const log =
57
+ (name: string) =>
58
+ (...args: unknown[]) =>
59
+ console.log(`[${name}]`, ...args);
60
+
61
+ const toProps = (c: Cfg) => ({
62
+ integratorId: String(c.integratorId ?? ""),
63
+ srcChain: str(c.srcChain),
64
+ srcToken: str(c.srcToken),
65
+ dstChain: str(c.dstChain),
66
+ dstToken: str(c.dstToken),
67
+ dstDisplayToken: str(c.dstDisplayToken),
68
+ highlightedDstTokens: str(c.highlightedDstTokens)?.split(","),
69
+ widgetTitle: str(c.widgetTitle),
70
+ desc: str(c.desc),
71
+ integratorFee: str(c.integratorFee) ? Number(c.integratorFee) : undefined,
72
+ integratorFeeReceiverAddress: str(c.integratorFeeReceiverAddress),
73
+ override: str(c.apiUrl) ? { apiUrl: String(c.apiUrl) } : undefined,
74
+ styles: {
75
+ mainAccentLight: str(c.mainAccentLight),
76
+ mainAccentDark: str(c.mainAccentDark),
77
+ textSecondary: str(c.textSecondary),
78
+ fontFamily: str(c.fontFamily),
79
+ width: str(c.width),
80
+ },
81
+ lightTheme: !!c.lightTheme,
82
+ defaultWalletPicker: !!c.defaultWalletPicker,
83
+ bridge: !!c.bridge,
84
+ srcChainLocked: !!c.srcChainLocked,
85
+ srcTokenLocked: !!c.srcTokenLocked,
86
+ dstChainLocked: !!c.dstChainLocked,
87
+ dstTokenLocked: !!c.dstTokenLocked,
88
+ onSrcChainChange: log("onSrcChainChange"),
89
+ onSrcTokenChange: log("onSrcTokenChange"),
90
+ onDstChainChange: log("onDstChainChange"),
91
+ onDstTokenChange: log("onDstTokenChange"),
92
+ onConnectWallet: log("onConnectWallet"),
93
+ onPendingTransactionsChange: log("onPendingTransactionsChange"),
94
+ });
95
+
96
+ const useChains = (apiUrl: string) => {
97
+ const [chains, setChains] = useState<Chain[]>([]);
98
+
99
+ useEffect(() => {
100
+ fetch(`${apiUrl}/chains?data={}`)
101
+ .then((r) => r.json())
102
+ .then((all: Chain[]) =>
103
+ setChains(
104
+ all.filter(
105
+ (c) =>
106
+ c.web3Environment === "mainnet" &&
107
+ (c.bridgeSupported || c.swapSupported),
108
+ ),
109
+ ),
110
+ )
111
+ .catch((e) => console.error("failed to load chains", e));
112
+ }, [apiUrl]);
113
+
114
+ return chains;
115
+ };
116
+
117
+ const sortTokens = (tokens: Token[] = []) =>
118
+ [...tokens].sort((a, b) => Number(!!b.quickPick) - Number(!!a.quickPick));
119
+
120
+ type FieldProps = {
121
+ k: string;
122
+ cfg: Cfg;
123
+ set: (key: string, value: string | boolean) => void;
124
+ };
125
+
126
+ // ponytail: declared outside App, otherwise every render remounts the inputs and typing loses focus
127
+ const Text = ({ k, cfg, set, ...rest }: FieldProps & Record<string, unknown>) => (
128
+ <label>
129
+ <span>{k}</span>
130
+ <input
131
+ value={(cfg[k] as string) ?? ""}
132
+ onChange={(e) => set(k, e.target.value)}
133
+ {...rest}
134
+ />
135
+ </label>
136
+ );
137
+
138
+ const Color = ({ k, cfg, set }: FieldProps) => (
139
+ <label>
140
+ <span>{k}</span>
141
+ <div className="inline">
142
+ <input
143
+ type="color"
144
+ value={(cfg[k] as string) || "#3b82f6"}
145
+ onChange={(e) => set(k, e.target.value)}
146
+ />
147
+ <input
148
+ value={(cfg[k] as string) ?? ""}
149
+ placeholder="unset"
150
+ onChange={(e) => set(k, e.target.value)}
151
+ />
152
+ <button onClick={() => set(k, "")}>×</button>
153
+ </div>
154
+ </label>
155
+ );
156
+
157
+ const ChainSelect = ({
158
+ side,
159
+ cfg,
160
+ chains,
161
+ onChange,
162
+ }: {
163
+ side: "src" | "dst";
164
+ cfg: Cfg;
165
+ chains: Chain[];
166
+ onChange: (chainId: string) => void;
167
+ }) => (
168
+ <label>
169
+ <span>{side}Chain</span>
170
+ <select
171
+ value={(cfg[`${side}Chain`] as string) ?? ""}
172
+ onChange={(e) => onChange(e.target.value)}
173
+ >
174
+ <option value="">— any —</option>
175
+ {chains.map((c) => (
176
+ <option key={c.chainId} value={c.chainId}>
177
+ {c.displayName} ({c.ecosystem} · {c.chainId})
178
+ </option>
179
+ ))}
180
+ </select>
181
+ </label>
182
+ );
183
+
184
+ const TokenSelect = ({
185
+ k,
186
+ cfg,
187
+ set,
188
+ tokens,
189
+ }: FieldProps & { tokens: Token[] }) => (
190
+ <label>
191
+ <span>{k}</span>
192
+ <select
193
+ value={(cfg[k] as string) ?? ""}
194
+ onChange={(e) => set(k, e.target.value)}
195
+ >
196
+ <option value="">— any —</option>
197
+ {tokens.map((t) => (
198
+ <option key={t.address} value={t.address}>
199
+ {t.symbol}
200
+ </option>
201
+ ))}
202
+ </select>
203
+ </label>
204
+ );
205
+
206
+ export const App = () => {
207
+ const [cfg, setCfg] = useState(loadCfg);
208
+ const [mountKey, setMountKey] = useState(0);
209
+
210
+ const chains = useChains(str(cfg.apiUrl) ?? DEFAULT_API);
211
+ const byId = useMemo(
212
+ () => Object.fromEntries(chains.map((c) => [c.chainId, c])),
213
+ [chains],
214
+ );
215
+
216
+ useEffect(() => {
217
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg));
218
+ }, [cfg]);
219
+
220
+ const set = (key: string, value: string | boolean) =>
221
+ setCfg((c) => ({ ...c, [key]: value }));
222
+
223
+ // ponytail: switching a chain invalidates the token picked on the old one
224
+ const setChain = (side: "src" | "dst", chainId: string) =>
225
+ setCfg((c) => ({
226
+ ...c,
227
+ [`${side}Chain`]: chainId,
228
+ [`${side}Token`]: "",
229
+ ...(side === "dst" ? { dstDisplayToken: "", highlightedDstTokens: "" } : {}),
230
+ }));
231
+
232
+ const reset = () => {
233
+ setCfg(DEFAULTS);
234
+ setMountKey((k) => k + 1);
235
+ };
236
+
237
+ const srcTokens = sortTokens(byId[cfg.srcChain as string]?.tokens);
238
+ const dstTokens = sortTokens(byId[cfg.dstChain as string]?.tokens);
239
+ const highlighted = str(cfg.highlightedDstTokens)?.split(",") ?? [];
240
+
241
+ // ponytail: the widget reads chain/token props only on mount, so remount when they change.
242
+ // Text fields (integratorId, apiUrl) are excluded — they'd remount on every keystroke.
243
+ const remountKey = [
244
+ mountKey,
245
+ cfg.srcChain,
246
+ cfg.srcToken,
247
+ cfg.dstChain,
248
+ cfg.dstToken,
249
+ cfg.dstDisplayToken,
250
+ cfg.highlightedDstTokens,
251
+ ].join("|");
252
+
253
+ return (
254
+ <div className="page">
255
+ <style>{CSS}</style>
256
+
257
+ <aside>
258
+ <h2>Widget config</h2>
259
+
260
+ <Text k="integratorId" cfg={cfg} set={set} />
261
+
262
+ <ChainSelect
263
+ side="src"
264
+ cfg={cfg}
265
+ chains={chains}
266
+ onChange={(id) => setChain("src", id)}
267
+ />
268
+ <TokenSelect k="srcToken" cfg={cfg} set={set} tokens={srcTokens} />
269
+ <ChainSelect
270
+ side="dst"
271
+ cfg={cfg}
272
+ chains={chains}
273
+ onChange={(id) => setChain("dst", id)}
274
+ />
275
+ <TokenSelect k="dstToken" cfg={cfg} set={set} tokens={dstTokens} />
276
+ <TokenSelect
277
+ k="dstDisplayToken"
278
+ cfg={cfg}
279
+ set={set}
280
+ tokens={dstTokens}
281
+ />
282
+
283
+ <label>
284
+ <span>highlightedDstTokens (ctrl/cmd + click)</span>
285
+ <select
286
+ multiple
287
+ size={5}
288
+ value={highlighted}
289
+ onChange={(e) =>
290
+ set(
291
+ "highlightedDstTokens",
292
+ [...e.target.selectedOptions].map((o) => o.value).join(","),
293
+ )
294
+ }
295
+ >
296
+ {dstTokens.map((t) => (
297
+ <option key={t.address} value={t.address}>
298
+ {t.symbol}
299
+ </option>
300
+ ))}
301
+ </select>
302
+ </label>
303
+
304
+ <Text k="widgetTitle" cfg={cfg} set={set} placeholder="Bridge" />
305
+ <Text k="desc" cfg={cfg} set={set} />
306
+ <Text
307
+ k="integratorFee"
308
+ cfg={cfg}
309
+ set={set}
310
+ type="number"
311
+ step="0.001"
312
+ placeholder="0.003"
313
+ />
314
+ <Text
315
+ k="integratorFeeReceiverAddress"
316
+ cfg={cfg}
317
+ set={set}
318
+ placeholder="0x…"
319
+ />
320
+
321
+ <label>
322
+ <span>apiUrl (override)</span>
323
+ <input
324
+ list="api-urls"
325
+ value={(cfg.apiUrl as string) ?? ""}
326
+ placeholder={DEFAULT_API}
327
+ onChange={(e) => set("apiUrl", e.target.value)}
328
+ />
329
+ <datalist id="api-urls">
330
+ {API_URLS.map((u) => (
331
+ <option key={u} value={u} />
332
+ ))}
333
+ </datalist>
334
+ </label>
335
+
336
+ <h3>styles</h3>
337
+ <Color k="mainAccentLight" cfg={cfg} set={set} />
338
+ <Color k="mainAccentDark" cfg={cfg} set={set} />
339
+ <Color k="textSecondary" cfg={cfg} set={set} />
340
+
341
+ <label>
342
+ <span>fontFamily</span>
343
+ <input
344
+ list="fonts"
345
+ value={(cfg.fontFamily as string) ?? ""}
346
+ onChange={(e) => set("fontFamily", e.target.value)}
347
+ />
348
+ <datalist id="fonts">
349
+ {FONTS.map((f) => (
350
+ <option key={f} value={f} />
351
+ ))}
352
+ </datalist>
353
+ </label>
354
+
355
+ <label>
356
+ <span>width — {(cfg.width as string) || "default"}</span>
357
+ <div className="inline">
358
+ <input
359
+ type="range"
360
+ min={300}
361
+ max={600}
362
+ step={10}
363
+ value={parseInt((cfg.width as string) || "420", 10)}
364
+ onChange={(e) => set("width", `${e.target.value}px`)}
365
+ />
366
+ <button onClick={() => set("width", "")}>×</button>
367
+ </div>
368
+ </label>
369
+
370
+ <h3>flags</h3>
371
+ <div className="flags">
372
+ {BOOL_FIELDS.map((f) => (
373
+ <label key={f} className="check">
374
+ <input
375
+ type="checkbox"
376
+ checked={!!cfg[f]}
377
+ onChange={(e) => set(f, e.target.checked)}
378
+ />
379
+ {f}
380
+ </label>
381
+ ))}
382
+ </div>
383
+
384
+ <div className="inline">
385
+ <button onClick={() => setMountKey((k) => k + 1)}>Remount</button>
386
+ <button onClick={reset}>Reset</button>
387
+ </div>
388
+
389
+ <pre>{JSON.stringify(toProps(cfg), null, 2)}</pre>
390
+ </aside>
391
+
392
+ <main>
393
+ <TxWidget key={remountKey} {...toProps(cfg)} />
394
+ </main>
395
+ </div>
396
+ );
397
+ };
398
+
399
+ const CSS = `
400
+ body { margin: 0; font: 13px/1.4 system-ui, sans-serif; background: #f6f7f9; }
401
+ .page { display: flex; gap: 32px; align-items: flex-start; padding: 24px; }
402
+ aside { width: 340px; display: flex; flex-direction: column; gap: 10px;
403
+ background: #fff; border: 1px solid #e3e5e8; border-radius: 12px; padding: 16px; }
404
+ main { position: sticky; top: 24px; }
405
+ h2, h3 { margin: 4px 0; }
406
+ h3 { color: #667; text-transform: uppercase; font-size: 11px; letter-spacing: .08em; }
407
+ label { display: flex; flex-direction: column; gap: 3px; }
408
+ label > span { color: #667; font-size: 11px; }
409
+ input, select, button { font: inherit; padding: 5px 6px; border: 1px solid #d3d6da;
410
+ border-radius: 6px; background: #fff; }
411
+ input[type=color] { padding: 0; width: 34px; height: 30px; }
412
+ input[type=range] { padding: 0; flex: 1; }
413
+ button { cursor: pointer; }
414
+ .inline { display: flex; gap: 6px; align-items: center; }
415
+ .inline > input:not([type=color]) { flex: 1; min-width: 0; }
416
+ .flags { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
417
+ .check { flex-direction: row; align-items: center; gap: 5px; font-size: 12px; }
418
+ .check input { margin: 0; }
419
+ pre { font-size: 10px; background: #f2f3f5; border-radius: 8px; padding: 10px;
420
+ max-height: 260px; overflow: auto; margin: 0; }
421
+ `;
@@ -0,0 +1,8 @@
1
+ import { Buffer } from "buffer";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./App";
4
+
5
+ // ponytail: SDK deps (solana/sui) expect a Buffer global in the browser
6
+ globalThis.Buffer ??= Buffer;
7
+
8
+ createRoot(document.getElementById("root")!).render(<App />);
@@ -0,0 +1,9 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ // ponytail: linked package -> force pre-bundling so its deps resolve from ../node_modules
7
+ optimizeDeps: { include: ["@xswap-link/sdk"] },
8
+ define: { global: "globalThis" },
9
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -81,6 +81,7 @@
81
81
  "@types/react-dom": "^18.2.0",
82
82
  "@typescript-eslint/eslint-plugin": "^7.1.0",
83
83
  "@typescript-eslint/parser": "^7.1.0",
84
+ "autoprefixer": "^10.5.4",
84
85
  "babel-jest": "^29.7.0",
85
86
  "cross-fetch": "^4.1.0",
86
87
  "eslint": "^8.57.0",
package/postcss.config.js CHANGED
@@ -4,6 +4,8 @@ module.exports = {
4
4
  require("postcss-import"),
5
5
  // Resolves TailwindCSS
6
6
  require("tailwindcss")(),
7
+ // Adds vendor prefixes (e.g. -webkit-backdrop-filter for Safari < 18)
8
+ require("autoprefixer"),
7
9
  // Removes comments from CSS and redundant whitespaces
8
10
  require("postcss-minify"),
9
11
  ],
@@ -0,0 +1,23 @@
1
+ // Glyph-tight box (no 16/24px padding frame) so flex gaps around it match
2
+ // the design's spacing exactly. Geometry is Figma's "Vector 8262" chevron
3
+ // rotated to point right.
4
+ export const ChevronRightThinIcon = ({
5
+ className,
6
+ }: {
7
+ className?: string;
8
+ }) => {
9
+ return (
10
+ <svg
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ width="4"
13
+ height="7"
14
+ viewBox="0 0 3.58671 7"
15
+ fill="none"
16
+ stroke="currentColor"
17
+ strokeLinejoin="round"
18
+ className={className}
19
+ >
20
+ <path d="M0 6.5H0.500926L2.79382 4.20711C3.18434 3.81658 3.18434 3.18342 2.79382 2.79289L0.500926 0.5H0" />
21
+ </svg>
22
+ );
23
+ };
@@ -0,0 +1,33 @@
1
+ import { forwardRef } from "react";
2
+
3
+ const InfoThinIcon = forwardRef<SVGSVGElement, { className?: string }>(
4
+ ({ className }, ref) => {
5
+ return (
6
+ <svg
7
+ ref={ref}
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ width="16"
10
+ height="16"
11
+ viewBox="0 0 16 16"
12
+ fill="none"
13
+ className={className || ""}
14
+ >
15
+ <circle cx="8" cy="8" r="8" fill="currentColor" fillOpacity="0.05" />
16
+ <circle
17
+ cx="8"
18
+ cy="8"
19
+ r="7.5"
20
+ stroke="currentColor"
21
+ strokeOpacity="0.05"
22
+ />
23
+ <g opacity="0.6">
24
+ <path d="M8 7.5V10.5" stroke="currentColor" />
25
+ <path d="M8 5.5V6.5" stroke="currentColor" />
26
+ </g>
27
+ </svg>
28
+ );
29
+ },
30
+ );
31
+
32
+ InfoThinIcon.displayName = "InfoThinIcon";
33
+ export { InfoThinIcon };
@@ -0,0 +1,15 @@
1
+ export const RedirectThinIcon = ({ className }: { className?: string }) => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="12"
6
+ height="12"
7
+ viewBox="0 0 12 12"
8
+ fill="currentColor"
9
+ className={className}
10
+ >
11
+ <path d="M10.9226 10.9226H1.07744V1.07744H5.45455L4.3771 0H0V12H12V7.6229L10.9226 6.54545V10.9226Z" />
12
+ <path d="M7.07071 0L8.14815 1.07744H10.1549L9.15825 2.07407L9.92593 2.84175L10.9226 1.84512V3.83838L12 4.91582V0H7.07071Z" />
13
+ </svg>
14
+ );
15
+ };
@@ -17,6 +17,7 @@ export * from "./CheckIcon";
17
17
  export * from "./ChevronDownIcon";
18
18
  export * from "./ChevronDownThinIcon";
19
19
  export * from "./ChevronDownWideThinIcon";
20
+ export * from "./ChevronRightThinIcon";
20
21
  export * from "./ChevronsUpDownThinIcon";
21
22
  export * from "./ChevronUpIcon";
22
23
  export * from "./CircularProgressIcon";
@@ -34,12 +35,14 @@ export * from "./HistoryGradientBgIcon";
34
35
  export * from "./HistoryIcon";
35
36
  export * from "./HourGlassIcon";
36
37
  export * from "./InfoIcon";
38
+ export * from "./InfoThinIcon";
37
39
  export * from "./LinkExternalIcon";
38
40
  export * from "./LinkExternalThinIcon";
39
41
  export * from "./LoadingRingIcon";
40
42
  export * from "./PercentageIcon";
41
43
  export * from "./ProgressRingThinIcon";
42
44
  export * from "./RankIcon";
45
+ export * from "./RedirectThinIcon";
43
46
  export * from "./SearchIcon";
44
47
  export * from "./SearchThinIcon";
45
48
  export * from "./SettingsGradientBgIcon";
@@ -16,7 +16,9 @@ export const Header = ({ onClose }: Props) => {
16
16
 
17
17
  return (
18
18
  <div className="flex justify-between items-center">
19
- <div className="text-2xl leading-8 tracking-[0.01em]">{title}</div>
19
+ <div className="text-2xl font-medium leading-8 tracking-[0.01em]">
20
+ {title}
21
+ </div>
20
22
  <Controls onClose={onClose} />
21
23
  </div>
22
24
  );
@@ -1,5 +1,5 @@
1
1
  import {
2
- ChevronDownThinSmallIcon,
2
+ ChevronRightThinIcon,
3
3
  LinkExternalThinIcon,
4
4
  LoadingRingIcon,
5
5
  } from "@src/assets/icons";
@@ -170,7 +170,7 @@ export const ActivityCard = ({ transaction }: Props) => {
170
170
  <span>{srcToken?.symbol || "?"}</span>
171
171
  {dstToken && (
172
172
  <>
173
- <ChevronDownThinSmallIcon className="w-4 h-4 shrink-0 -rotate-90 opacity-50" />
173
+ <ChevronRightThinIcon className="shrink-0 opacity-50" />
174
174
  <span className="truncate">{dstToken.symbol}</span>
175
175
  </>
176
176
  )}
@@ -23,10 +23,13 @@ export const ReorderButton: FC = () => {
23
23
 
24
24
  return (
25
25
  <div className="h-0 w-full relative z-[1]">
26
+ {/* Opaque bg on purpose: Chromium ignores backdrop-filter nested
27
+ inside the widget's own backdrop-blur, so a translucent bg would
28
+ let the panel edges show through the button. */}
26
29
  <button
27
30
  onClick={onReorderClick}
28
31
  aria-label="Switch target token with the source token"
29
- className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-opacity-50 bg-gradient-to-r from-t_bg_tertiary/5 to-t_bg_tertiary/5 hover:from-t_bg_tertiary/10 hover:to-t_bg_tertiary/10 transition-colors"
32
+ className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-gradient-to-r from-t_bg_tertiary/5 to-t_bg_tertiary/5 hover:from-t_bg_tertiary/10 hover:to-t_bg_tertiary/10 transition-colors"
30
33
  >
31
34
  <div className="flex text-t_text_primary opacity-50">
32
35
  <ChevronsUpDownThinIcon />
@@ -1,4 +1,4 @@
1
- import { HelpIcon } from "@src/assets/icons";
1
+ import { InfoThinIcon } from "@src/assets/icons";
2
2
  import { ToggleButton, Tooltip } from "@src/components";
3
3
  import { useSwapContext } from "@src/context";
4
4
  import { useRef } from "react";
@@ -26,9 +26,9 @@ export const Delivery = () => {
26
26
  triggerRef={tooltipTriggerRef}
27
27
  id="express-delivery-tooltip"
28
28
  />
29
- <HelpIcon
29
+ <InfoThinIcon
30
30
  ref={tooltipTriggerRef}
31
- className="cursor-help fill-t_text_primary opacity-30 w-4 h-4 shrink-0"
31
+ className="cursor-help text-t_text_primary w-4 h-4 shrink-0"
32
32
  />
33
33
  </div>
34
34
  </div>
@@ -1,4 +1,4 @@
1
- import { HelpIcon } from "@src/assets/icons";
1
+ import { InfoThinIcon } from "@src/assets/icons";
2
2
  import { ToggleButton } from "@src/components/ToggleButton";
3
3
  import { Tooltip } from "@src/components/Tooltip";
4
4
  import { useSwapContext } from "@src/context";
@@ -27,9 +27,9 @@ export const InfiniteApproval = () => {
27
27
  triggerRef={tooltipTriggerRef}
28
28
  id="infinite-approval-tooltip"
29
29
  />
30
- <HelpIcon
30
+ <InfoThinIcon
31
31
  ref={tooltipTriggerRef}
32
- className="cursor-help fill-t_text_primary opacity-30 w-4 h-4 shrink-0"
32
+ className="cursor-help text-t_text_primary w-4 h-4 shrink-0"
33
33
  />
34
34
  </div>
35
35
  </div>
@@ -31,7 +31,7 @@ export const Slippage = () => {
31
31
  <div className="flex flex-col gap-4">
32
32
  <div className="flex items-center gap-2">
33
33
  <h2 className="text-sm leading-6 tracking-[0.01em] text-t_text_primary">
34
- Price Tolerance
34
+ Slippage
35
35
  </h2>
36
36
  <div className="w-0.5 h-0.5 bg-t_text_primary opacity-25 shrink-0" />
37
37
  <p className="text-sm leading-6 tracking-[0.01em] text-t_text_primary text-opacity-50">
@@ -77,11 +77,13 @@ export const Slippage = () => {
77
77
  </div>
78
78
  </div>
79
79
  <div className="flex flex-col gap-3">
80
- <p className="text-[10px] font-medium leading-4 tracking-[0.015em] text-t_text_primary text-opacity-30">
80
+ <p className="text-[10px] leading-4 tracking-[0.015em] text-t_text_primary text-opacity-30">
81
81
  NOTE.
82
82
  </p>
83
83
  <p className="text-sm leading-[18px] tracking-[0.01em] text-t_text_primary text-opacity-40">
84
- {`Price Tolerance defines how much a price can change before your
84
+ {/* The design sets the leading term in Medium, the rest Regular. */}
85
+ <span className="font-medium">Slippage</span>
86
+ {` defines how much a price can change before your
85
87
  transfer falls back to USDC. If the price moves more than your
86
88
  tolerance allows, you'll receive USDC on your destination chain.`}
87
89
  </p>