@ab-org/predicate-market-sdk 1.0.0 → 1.0.1

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 (91) hide show
  1. package/dist/index.d.ts +672 -22
  2. package/dist/index.js +4700 -23
  3. package/package.json +6 -5
  4. package/dist/auth/autoReconnect.d.ts +0 -11
  5. package/dist/auth/autoReconnect.js +0 -36
  6. package/dist/auth/bundledConfig.d.ts +0 -2
  7. package/dist/auth/bundledConfig.js +0 -19
  8. package/dist/auth/config.d.ts +0 -29
  9. package/dist/auth/config.js +0 -53
  10. package/dist/auth/google.d.ts +0 -43
  11. package/dist/auth/google.js +0 -147
  12. package/dist/auth/oidcRelay.d.ts +0 -11
  13. package/dist/auth/oidcRelay.js +0 -107
  14. package/dist/auth/twitter.d.ts +0 -7
  15. package/dist/auth/twitter.js +0 -94
  16. package/dist/auth/walletAccount.d.ts +0 -20
  17. package/dist/auth/walletAccount.js +0 -267
  18. package/dist/constants/chains.d.ts +0 -2
  19. package/dist/constants/chains.js +0 -3
  20. package/dist/modules/api.d.ts +0 -149
  21. package/dist/modules/api.js +0 -95
  22. package/dist/modules/balanceQuery.d.ts +0 -27
  23. package/dist/modules/balanceQuery.js +0 -60
  24. package/dist/modules/deposit.d.ts +0 -31
  25. package/dist/modules/deposit.js +0 -58
  26. package/dist/modules/marketData.d.ts +0 -8
  27. package/dist/modules/marketData.js +0 -107
  28. package/dist/modules/withdraw.d.ts +0 -31
  29. package/dist/modules/withdraw.js +0 -61
  30. package/dist/modules/withdrawDirect.d.ts +0 -14
  31. package/dist/modules/withdrawDirect.js +0 -33
  32. package/dist/modules/withdrawExecutor.d.ts +0 -56
  33. package/dist/modules/withdrawExecutor.js +0 -210
  34. package/dist/policyAdapter.d.ts +0 -11
  35. package/dist/policyAdapter.js +0 -38
  36. package/dist/types.d.ts +0 -62
  37. package/dist/types.js +0 -1
  38. package/dist/ui/DepositModal.d.ts +0 -36
  39. package/dist/ui/DepositModal.js +0 -354
  40. package/dist/ui/SignInModal.d.ts +0 -22
  41. package/dist/ui/SignInModal.js +0 -77
  42. package/dist/ui/SignInModal.sections.d.ts +0 -33
  43. package/dist/ui/SignInModal.sections.js +0 -45
  44. package/dist/ui/SignInModal.shared.d.ts +0 -15
  45. package/dist/ui/SignInModal.shared.js +0 -126
  46. package/dist/ui/WalletSelectionModal.d.ts +0 -14
  47. package/dist/ui/WalletSelectionModal.js +0 -54
  48. package/dist/ui/WithdrawModal.d.ts +0 -57
  49. package/dist/ui/WithdrawModal.js +0 -574
  50. package/dist/ui/components/CloseButton.d.ts +0 -4
  51. package/dist/ui/components/CloseButton.js +0 -15
  52. package/dist/ui/components/Countdown.d.ts +0 -16
  53. package/dist/ui/components/Countdown.js +0 -42
  54. package/dist/ui/components/DepositDetailsPanel.d.ts +0 -8
  55. package/dist/ui/components/DepositDetailsPanel.js +0 -143
  56. package/dist/ui/components/DropdownField.d.ts +0 -19
  57. package/dist/ui/components/DropdownField.js +0 -81
  58. package/dist/ui/components/Field.d.ts +0 -10
  59. package/dist/ui/components/Field.js +0 -21
  60. package/dist/ui/components/LoginRequiredOverlay.d.ts +0 -6
  61. package/dist/ui/components/LoginRequiredOverlay.js +0 -31
  62. package/dist/ui/components/ModalCard.d.ts +0 -9
  63. package/dist/ui/components/ModalCard.js +0 -14
  64. package/dist/ui/components/ModalFrame.d.ts +0 -9
  65. package/dist/ui/components/ModalFrame.js +0 -18
  66. package/dist/ui/components/PrimaryButton.d.ts +0 -2
  67. package/dist/ui/components/PrimaryButton.js +0 -14
  68. package/dist/ui/components/QRCodePanel.d.ts +0 -4
  69. package/dist/ui/components/QRCodePanel.js +0 -43
  70. package/dist/ui/components/Select.d.ts +0 -12
  71. package/dist/ui/components/Select.js +0 -29
  72. package/dist/ui/components/StepIndicator.d.ts +0 -7
  73. package/dist/ui/components/StepIndicator.js +0 -35
  74. package/dist/ui/components/Success.d.ts +0 -1
  75. package/dist/ui/components/Success.js +0 -4
  76. package/dist/ui/components/Toast.d.ts +0 -8
  77. package/dist/ui/components/Toast.js +0 -51
  78. package/dist/ui/hooks/useSession.d.ts +0 -2
  79. package/dist/ui/hooks/useSession.js +0 -10
  80. package/dist/ui/signInTypes.d.ts +0 -28
  81. package/dist/ui/signInTypes.js +0 -1
  82. package/dist/ui/theme.d.ts +0 -31
  83. package/dist/ui/theme.js +0 -31
  84. package/dist/ui/useSignInModalController.d.ts +0 -25
  85. package/dist/ui/useSignInModalController.js +0 -119
  86. package/dist/utils/env.d.ts +0 -1
  87. package/dist/utils/env.js +0 -63
  88. package/dist/utils/explorer.d.ts +0 -3
  89. package/dist/utils/explorer.js +0 -47
  90. package/dist/walletUtils.d.ts +0 -3
  91. package/dist/walletUtils.js +0 -3
@@ -1,574 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useCallback, useMemo, useRef } from "react";
3
- import { ModalFrame } from "./components/ModalFrame.js";
4
- import { StepIndicator } from "./components/StepIndicator.js";
5
- import { DropdownField } from "./components/DropdownField.js";
6
- import { Countdown } from "./components/Countdown.js";
7
- import { LoginRequiredOverlay } from "./components/LoginRequiredOverlay.js";
8
- import { useSession } from "./hooks/useSession.js";
9
- import { colors, fonts, radii } from "./theme.js";
10
- import { getChains, quote, getWithdrawOrder, } from "../modules/api.js";
11
- import { getEnv } from "../utils/env";
12
- import { getExplorerUrl } from "../utils/explorer.js";
13
- import { SuccessIcon } from "./components/Success";
14
- function CopyIcon() {
15
- return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [_jsx("rect", { x: "5", y: "5", width: "9", height: "9", rx: "1", stroke: "currentColor", strokeWidth: "1.2", fill: "none" }), _jsx("path", { d: "M3 11V3a1 1 0 0 1 1-1h8", stroke: "currentColor", strokeWidth: "1.2", fill: "none" })] }));
16
- }
17
- const FUNDING_TOKEN_SYMBOL = getEnv("FUNDING_TOKEN_SYMBOL");
18
- function chainsToTokenOptions(chains) {
19
- const bySymbol = new Map();
20
- for (const c of chains) {
21
- for (const t of c.tokens) {
22
- if (!bySymbol.has(t.symbol))
23
- bySymbol.set(t.symbol, t.symbol);
24
- }
25
- }
26
- return Array.from(bySymbol.entries()).map(([id, symbol]) => ({
27
- id,
28
- label: symbol,
29
- subtitle: symbol,
30
- }));
31
- }
32
- function chainsToChainOptionsForToken(chains, tokenSymbol) {
33
- return chains
34
- .filter((c) => c.tokens.some((t) => t.symbol === tokenSymbol))
35
- .map((c) => ({ id: c.chain_id, label: c.network, subtitle: c.chain_id }));
36
- }
37
- function getTokenAddressForChain(chains, chainId, tokenSymbol) {
38
- const chain = chains.find((c) => c.chain_id === chainId);
39
- return chain?.tokens.find((t) => t.symbol === tokenSymbol)?.address;
40
- }
41
- /** 从 balance 字符串解析出数值部分(如 "123.46 xxx" → 123.46) */
42
- function parseBalanceNumber(balance) {
43
- const match = balance.trim().match(/^(\d*\.?\d*)/);
44
- if (!match)
45
- return null;
46
- const n = Number(match[1]);
47
- return Number.isNaN(n) ? null : n;
48
- }
49
- /** 将 balance 字符串格式化为小数点后 2 位(如 "123.456789012 xxx" → "123.46 xxx") */
50
- function formatBalanceTo2Decimals(balance) {
51
- const match = balance.trim().match(/^(\d*\.?\d*)(.*)$/);
52
- if (!match)
53
- return balance;
54
- const numPart = match[1];
55
- const suffix = match[2].trim() ? " " + match[2].trim() : "";
56
- const n = Number(numPart);
57
- if (Number.isNaN(n))
58
- return balance;
59
- const formatted = n.toFixed(2);
60
- return formatted + suffix;
61
- }
62
- /** Wei (string) to ether display string; keeps up to 6 decimal places, strips trailing zeros */
63
- function weiToEtherDisplay(wei) {
64
- const s = (wei || "0").trim().replace(/^0+/, "") || "0";
65
- if (s === "0")
66
- return "0";
67
- const padded = s.padStart(19, "0");
68
- const intPart = padded.slice(0, Math.max(0, padded.length - 18));
69
- const decPart = padded.slice(-18).replace(/0+$/, "");
70
- const combined = decPart ? `${intPart}.${decPart}` : intPart;
71
- const num = Number(combined);
72
- if (Number.isNaN(num))
73
- return wei;
74
- const fixed = num.toFixed(6).replace(/\.?0+$/, "");
75
- return fixed;
76
- }
77
- const POLL_INTERVAL_MS = 4000;
78
- export const WithdrawModal = ({ address = "", token, tokenSymbol, chain, amount = "", balance, status = "idle", receiveAmount: receiveAmountProp, txHash, tokenOptions: tokenOptionsProp, chainOptions: chainOptionsProp, useMerchantApi = false, orderId, withdrawMode, withdrawDirectResult, feeDisplay, fundingChainId, onShowToast, onAddressChange, onTokenSelect, onChainSelect, onAmountChange, onMaxClick, onSubmit, onWithdrawCompleted, onStartAnotherWithdrawal, onSignIn, onClose, }) => {
79
- const session = useSession();
80
- const addressInputRef = useRef(null);
81
- const [addressInputFocused, setAddressInputFocused] = useState(false);
82
- const [amountInputFocused, setAmountInputFocused] = useState(false);
83
- const [apiChains, setApiChains] = useState(null);
84
- const [apiQuote, setApiQuote] = useState(null);
85
- const [withdrawOrder, setWithdrawOrder] = useState(null);
86
- const [loadingChains, setLoadingChains] = useState(false);
87
- const [loadingQuote, setLoadingQuote] = useState(false);
88
- // 默认收款地址为当前用户钱包地址
89
- // useEffect(() => {
90
- // if (session?.address && (address === "" || address === undefined) && onAddressChange) {
91
- // onAddressChange({
92
- // target: { value: session.address },
93
- // } as ChangeEvent<HTMLInputElement>);
94
- // }
95
- // }, [session?.address, address, onAddressChange]);
96
- const tokenOptions = useMemo(() => {
97
- const raw = tokenOptionsProp?.length
98
- ? tokenOptionsProp
99
- : apiChains?.length
100
- ? chainsToTokenOptions(apiChains)
101
- : undefined;
102
- if (!raw?.length)
103
- return undefined;
104
- return raw;
105
- }, [tokenOptionsProp, apiChains]);
106
- const chainOptions = useMemo(() => {
107
- if (chainOptionsProp?.length)
108
- return chainOptionsProp;
109
- if (!apiChains?.length || !token)
110
- return undefined;
111
- return chainsToChainOptionsForToken(apiChains, token);
112
- }, [chainOptionsProp, apiChains, token]);
113
- /** 当前选中的 token + chain 在 getChains 数据中的合约地址(与询价 / quote 使用同一解析方式) */
114
- const resolvedTokenAddress = useMemo(() => {
115
- if (!apiChains?.length || !token || !chain)
116
- return undefined;
117
- return getTokenAddressForChain(apiChains, chain, token);
118
- }, [apiChains, token, chain]);
119
- // direct 模式的内部激活标记(便于“Start another withdrawal”时重置为表单态)
120
- const [directActive, setDirectActive] = useState(false);
121
- // 当外部把 withdrawMode 设为 direct 且提供 withdrawDirectResut 时,进入“追踪提现”态
122
- useEffect(() => {
123
- setDirectActive(withdrawMode === "direct" && Boolean(withdrawDirectResult));
124
- }, [withdrawMode, withdrawDirectResult]);
125
- const trackingWithdraw = useMemo(() => Boolean(orderId) || directActive, [orderId, directActive]);
126
- // Default focus on recipient address when form is visible; white border while focused, revert on blur
127
- useEffect(() => {
128
- if (trackingWithdraw)
129
- return;
130
- const t = requestAnimationFrame(() => {
131
- addressInputRef.current?.focus();
132
- });
133
- return () => cancelAnimationFrame(t);
134
- }, [trackingWithdraw]);
135
- // 选中 token+chain 后需要 chains 数据才能询价,因此无论 useMerchantApi 与否都拉取 getChains
136
- const shouldLoadChains = useMerchantApi || Boolean(token && chain);
137
- useEffect(() => {
138
- if (!shouldLoadChains)
139
- return;
140
- setLoadingChains(true);
141
- getChains()
142
- .then((res) => setApiChains(res?.chains ?? []))
143
- .finally(() => setLoadingChains(false));
144
- }, [shouldLoadChains]);
145
- // 选中 token + chain 后即询价;未填 amount 时用 1 xxx 获取 rate
146
- useEffect(() => {
147
- if (!apiChains?.length || !token || !chain) {
148
- setApiQuote(null);
149
- return;
150
- }
151
- const tokenAddress = getTokenAddressForChain(apiChains, chain, token);
152
- if (!tokenAddress) {
153
- setApiQuote(null);
154
- return;
155
- }
156
- const amountNum = amount && Number(amount) > 0 ? Number(amount) : 1;
157
- const amountWei = String(BigInt(Math.floor(amountNum * 1e18)));
158
- setLoadingQuote(true);
159
- quote({
160
- direction: "withdraw",
161
- chain_id: chain,
162
- token_address: tokenAddress,
163
- dst_token_amount: amountWei,
164
- })
165
- .then((q) => setApiQuote(q ?? null))
166
- .catch(() => {
167
- setApiQuote({
168
- token_address: tokenAddress,
169
- token_symbol: token,
170
- token_decimals: 18,
171
- rate: "1",
172
- chain_id: Number(chain) || 56,
173
- token_amount: amountWei,
174
- expires_at: new Date(Date.now() + 60000).toISOString(),
175
- });
176
- })
177
- .finally(() => setLoadingQuote(false));
178
- }, [apiChains, token, chain, amount]);
179
- useEffect(() => {
180
- if (tokenOptions?.length !== 1 || !onTokenSelect)
181
- return;
182
- const onlyId = tokenOptions[0].id;
183
- if (token === onlyId)
184
- return;
185
- onTokenSelect(onlyId);
186
- }, [tokenOptions, token, onTokenSelect]);
187
- useEffect(() => {
188
- if (chainOptions?.length !== 1 || !onChainSelect)
189
- return;
190
- const onlyId = chainOptions[0].id;
191
- if (chain === onlyId)
192
- return;
193
- onChainSelect(onlyId);
194
- }, [chainOptions, chain, onChainSelect]);
195
- const mockWithdrawOrderState = useMemo(() => ({
196
- order_id: orderId ?? "",
197
- status: "pending",
198
- chain_id: "56",
199
- dst_token_amount: "0",
200
- target_chain_id: chain ?? "1",
201
- target_address: address ?? "0x",
202
- created_at: new Date().toISOString(),
203
- updated_at: new Date().toISOString(),
204
- }), [orderId, chain, address]);
205
- useEffect(() => {
206
- // 1) 有 orderId:按原逻辑轮询
207
- if (orderId) {
208
- const t = setInterval(() => {
209
- getWithdrawOrder(orderId)
210
- .then((order) => {
211
- setWithdrawOrder(order);
212
- if (order.status === "completed" && onShowToast) {
213
- onShowToast("Withdrawal completed");
214
- }
215
- })
216
- .catch(() => setWithdrawOrder(mockWithdrawOrderState));
217
- }, POLL_INTERVAL_MS);
218
- getWithdrawOrder(orderId)
219
- .then(setWithdrawOrder)
220
- .catch(() => setWithdrawOrder(mockWithdrawOrderState));
221
- return () => clearInterval(t);
222
- }
223
- // 2) 无 orderId 且 direct 模式:用传入的结果构造一个“伪订单”,以复用已提交/成功页
224
- if (!orderId && directActive) {
225
- const now = new Date().toISOString();
226
- const inferredAmountWei = (() => {
227
- if (withdrawDirectResult?.dst_token_amount)
228
- return withdrawDirectResult.dst_token_amount;
229
- const n = Number(amount || "0");
230
- return String(n > 0 ? BigInt(Math.floor(n * 1e18)) : 0n);
231
- })();
232
- const synthetic = {
233
- order_id: withdrawDirectResult?.order_id ?? `direct-${Date.now()}`,
234
- status: withdrawDirectResult?.status ?? "pending",
235
- chain_id: withdrawDirectResult?.chain_id ?? String(fundingChainId ?? "3131"),
236
- dst_token_amount: inferredAmountWei,
237
- fee: withdrawDirectResult?.fee ?? feeDisplay,
238
- target_chain_id: withdrawDirectResult?.target_chain_id ?? (chain ?? ""),
239
- target_address: withdrawDirectResult?.target_address ?? (address ?? ""),
240
- funding_tx_hash: withdrawDirectResult?.funding_tx_hash ?? txHash,
241
- dst_tx_hash: withdrawDirectResult?.dst_tx_hash,
242
- out_tx_hash: withdrawDirectResult?.out_tx_hash,
243
- created_at: withdrawDirectResult?.created_at ?? now,
244
- updated_at: withdrawDirectResult?.updated_at ?? now,
245
- one_time_address: withdrawDirectResult?.one_time_address,
246
- };
247
- setWithdrawOrder(synthetic);
248
- return;
249
- }
250
- // 3) 默认:清空订单,显示表单
251
- setWithdrawOrder(null);
252
- }, [orderId, directActive, withdrawDirectResult, amount, feeDisplay, chain, address, txHash, fundingChainId, onShowToast, mockWithdrawOrderState]);
253
- // 订单完成回调:支持 orderId 或 direct 模式
254
- const completedKeyRef = useRef(null);
255
- useEffect(() => {
256
- if (withdrawOrder?.status !== "completed")
257
- return;
258
- const key = orderId ?? withdrawOrder.order_id;
259
- if (key && completedKeyRef.current !== key) {
260
- completedKeyRef.current = key;
261
- onWithdrawCompleted?.();
262
- }
263
- if (!trackingWithdraw)
264
- completedKeyRef.current = null;
265
- }, [withdrawOrder?.status, orderId, trackingWithdraw, onWithdrawCompleted]);
266
- const receiveAmount = useMemo(() => {
267
- if (receiveAmountProp) {
268
- const n = Number(receiveAmountProp);
269
- if (!Number.isNaN(n))
270
- return n.toFixed(2);
271
- return receiveAmountProp;
272
- }
273
- if (!apiQuote?.token_amount || !apiQuote?.token_symbol)
274
- return undefined;
275
- const amount = Number(apiQuote.token_amount);
276
- const formatted = Number.isNaN(amount) ? apiQuote.token_amount : amount.toFixed(2);
277
- return `≈ ${formatted} (wei) ${apiQuote.token_symbol}`;
278
- }, [receiveAmountProp, apiQuote]);
279
- const quoteExpired = useMemo(() => {
280
- if (!apiQuote?.expires_at)
281
- return false;
282
- try {
283
- return new Date(apiQuote.expires_at).getTime() < Date.now();
284
- }
285
- catch {
286
- return false;
287
- }
288
- }, [apiQuote?.expires_at]);
289
- const handleRefreshQuote = useCallback(() => {
290
- if (!apiChains?.length || !token || !chain)
291
- return;
292
- const tokenAddress = getTokenAddressForChain(apiChains, chain, token);
293
- if (!tokenAddress)
294
- return;
295
- const amountNum = amount && Number(amount) > 0 ? Number(amount) : 1;
296
- const amountWei = String(BigInt(Math.floor(amountNum * 1e18)));
297
- setLoadingQuote(true);
298
- quote({
299
- direction: "withdraw",
300
- chain_id: chain,
301
- token_address: tokenAddress,
302
- dst_token_amount: amountWei,
303
- })
304
- .then((q) => setApiQuote(q ?? null))
305
- .catch(() => {
306
- setApiQuote({
307
- token_address: tokenAddress,
308
- token_symbol: token,
309
- token_decimals: 18,
310
- rate: "1",
311
- chain_id: Number(chain) || 56,
312
- token_amount: amountWei,
313
- expires_at: new Date(Date.now() + 60000).toISOString(),
314
- });
315
- })
316
- .finally(() => setLoadingQuote(false));
317
- }, [apiChains, token, chain, amount]);
318
- const handleAmountChange = useCallback((e) => {
319
- const raw = e.target.value;
320
- const maxNum = balance ? parseBalanceNumber(balance) : null;
321
- if (maxNum != null && raw !== "" && raw !== ".") {
322
- const num = Number(raw);
323
- if (!Number.isNaN(num) && num > maxNum) {
324
- onAmountChange?.({
325
- ...e,
326
- target: { ...e.target, value: String(maxNum) },
327
- });
328
- return;
329
- }
330
- }
331
- onAmountChange?.(e);
332
- }, [balance, onAmountChange]);
333
- if (!session) {
334
- return _jsx(LoginRequiredOverlay, { title: "Withdraw", onSignIn: onSignIn, onClose: onClose });
335
- }
336
- const activeStep = amount ? 3 : chain ? 2 : token ? 1 : address ? 0 : 0;
337
- const canSubmit = address && token && chain && amount;
338
- const isSubmitting = status !== "idle";
339
- /** 已提交提现(有 orderId):隐藏表单;处理中显示等待,完成后显示成功说明,关闭弹层后由调用方清空 orderId 再开可继续提现 */
340
- const orderInProgress = trackingWithdraw && (!withdrawOrder || withdrawOrder.status !== "completed");
341
- const orderSucceeded = trackingWithdraw && withdrawOrder?.status === "completed";
342
- return (_jsxs(ModalFrame, { onClose: onClose, contentStyle: {
343
- justifyContent: "space-between",
344
- padding: "24px",
345
- }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 32 }, children: [_jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: _jsx("h2", { style: { margin: 0, fontSize: 24, fontWeight: 600, lineHeight: 1.4 }, children: "Withdraw" }) }), _jsxs("div", { style: { display: "flex", gap: 16 }, children: [_jsx("div", { style: {
346
- opacity: trackingWithdraw ? 0 : 1,
347
- maxWidth: trackingWithdraw ? 0 : 48,
348
- overflow: "hidden",
349
- transition: "opacity 0.3s ease, max-width 0.35s ease",
350
- }, children: _jsx(StepIndicator, { steps: 4, stepActive: [!!address, !!token, !!chain, !!apiQuote], activeStep: activeStep }) }), _jsxs("div", { style: { flex: 1, position: "relative", minHeight: 400 }, children: [_jsxs("div", { style: {
351
- position: "absolute",
352
- inset: 0,
353
- display: "flex",
354
- flexDirection: "column",
355
- alignItems: "center",
356
- justifyContent: "center",
357
- gap: 12,
358
- padding: "0 16px",
359
- opacity: trackingWithdraw ? 1 : 0,
360
- maxHeight: trackingWithdraw ? 400 : 0,
361
- overflow: "hidden",
362
- pointerEvents: trackingWithdraw ? "auto" : "none",
363
- transition: "opacity 0.3s ease, max-height 0.35s ease",
364
- }, children: [orderInProgress && (_jsxs("div", { style: {
365
- display: "flex",
366
- flexDirection: "column",
367
- alignItems: "center",
368
- justifyContent: "center",
369
- gap: 32,
370
- }, children: [_jsx("span", { style: {
371
- display: "inline-block",
372
- width: 60,
373
- height: 60,
374
- border: `3px solid ${colors.border}`,
375
- borderTopColor: colors.textPrimary,
376
- borderRadius: "50%",
377
- animation: "withdraw-modal-spin 0.8s linear infinite",
378
- }, "aria-hidden": true }), _jsx("span", { style: {
379
- fontSize: 20,
380
- fontWeight: 600,
381
- color: colors.textPrimary,
382
- textAlign: "center",
383
- fontFamily: fonts.family,
384
- lineHeight: 1.4,
385
- }, children: "Processing withdrawal..." })] })), orderSucceeded && (() => {
386
- const destTxHash = withdrawOrder.dst_tx_hash ?? withdrawOrder.out_tx_hash;
387
- const targetChainName = apiChains?.find((c) => c.chain_id === withdrawOrder.target_chain_id)?.network ?? withdrawOrder.target_chain_id ?? "—";
388
- const explorerUrl = destTxHash && withdrawOrder.target_chain_id
389
- ? getExplorerUrl(withdrawOrder.target_chain_id, { txId: destTxHash })
390
- : null;
391
- const copyHash = () => {
392
- if (destTxHash && typeof navigator?.clipboard?.writeText === "function") {
393
- navigator.clipboard.writeText(destTxHash);
394
- onShowToast?.("Copied");
395
- }
396
- };
397
- return (_jsxs("div", { style: {
398
- display: "flex",
399
- flexDirection: "column",
400
- alignItems: "center",
401
- gap: 20,
402
- width: "100%",
403
- maxWidth: 412,
404
- }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 24 }, children: [_jsx("span", { style: {
405
- display: "flex",
406
- alignItems: "center",
407
- justifyContent: "center",
408
- width: 60,
409
- height: 60,
410
- borderRadius: "50%",
411
- color: "#22c55e",
412
- fontSize: 40,
413
- lineHeight: 1,
414
- fontWeight: 700,
415
- }, "aria-hidden": true, children: _jsx(SuccessIcon, {}) }), _jsx("span", { style: {
416
- fontSize: 20,
417
- fontWeight: 600,
418
- color: colors.textPrimary,
419
- textAlign: "center",
420
- fontFamily: fonts.family,
421
- lineHeight: 1.4,
422
- }, children: "Withdrawal successful" })] }), _jsxs("div", { style: {
423
- display: "flex",
424
- flexDirection: "column",
425
- gap: 4,
426
- width: "100%",
427
- }, children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", height: 36 }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textSecondary }, children: "Amount" }), _jsxs("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: [weiToEtherDisplay(withdrawOrder.dst_token_amount), " ", tokenSymbol] })] }), _jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", height: 36 }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textSecondary }, children: "Fee" }), _jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: withdrawOrder.fee ?? feeDisplay ?? "—" })] }), _jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", height: 36 }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textSecondary }, children: "Network" }), _jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: targetChainName })] }), destTxHash && (_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", minHeight: 38 }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textSecondary }, children: "Transaction Hash" }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [explorerUrl ? (_jsxs("a", { href: explorerUrl, target: "_blank", rel: "noopener noreferrer", style: { fontSize: 14, color: colors.textPrimary, textDecoration: "underline" }, children: [destTxHash.slice(0, 10), "...", destTxHash.slice(-8)] })) : (_jsxs("span", { style: { fontSize: 14, color: colors.textPrimary }, children: [destTxHash.slice(0, 10), "...", destTxHash.slice(-8)] })), _jsx("button", { type: "button", onClick: copyHash, "aria-label": "Copy hash", style: {
428
- padding: 0,
429
- border: "none",
430
- background: "transparent",
431
- color: colors.textPrimary,
432
- cursor: "pointer",
433
- display: "flex",
434
- alignItems: "center",
435
- justifyContent: "center",
436
- width: 16,
437
- height: 16,
438
- }, children: _jsx(CopyIcon, {}) })] })] }))] }), _jsx("div", { style: { paddingTop: 24, width: "100%", display: "flex", justifyContent: "center" }, children: _jsx("button", { type: "button", onClick: () => {
439
- // 重置 direct 模式追踪与本地订单,恢复到表单态
440
- setDirectActive(false);
441
- setWithdrawOrder(null);
442
- (onStartAnotherWithdrawal ?? onClose)?.();
443
- }, style: {
444
- width: 274,
445
- padding: "12px 0",
446
- borderRadius: radii.full,
447
- border: `1px solid ${colors.buttonDisabledBorder}`,
448
- background: colors.textPrimary,
449
- color: "#121214",
450
- fontSize: 16,
451
- fontWeight: 500,
452
- lineHeight: 1.4,
453
- fontFamily: fonts.family,
454
- cursor: "pointer",
455
- textAlign: "center",
456
- }, children: "Start another withdrawal" }) })] }));
457
- })()] }), _jsx("div", { style: {
458
- opacity: trackingWithdraw ? 0 : 1,
459
- maxHeight: trackingWithdraw ? 0 : 1200,
460
- overflow: "hidden",
461
- pointerEvents: trackingWithdraw ? "none" : "auto",
462
- transition: "opacity 0.3s ease, max-height 0.35s ease",
463
- }, children: _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 40 }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: "Recipient address" }), _jsx("div", { style: {
464
- display: "flex",
465
- alignItems: "center",
466
- height: 44,
467
- padding: "0 16px",
468
- borderRadius: radii.input,
469
- border: `1px solid ${addressInputFocused ? colors.borderFocused : colors.border}`,
470
- boxSizing: "border-box",
471
- }, children: _jsx("input", { ref: addressInputRef, value: address, onChange: onAddressChange, onFocus: () => setAddressInputFocused(true), onBlur: () => setAddressInputFocused(false), spellCheck: false, placeholder: "0x", style: {
472
- flex: 1,
473
- border: "none",
474
- outline: "none",
475
- background: "transparent",
476
- color: colors.textPrimary,
477
- fontSize: 14,
478
- fontFamily: fonts.family,
479
- lineHeight: 1.4,
480
- padding: 0,
481
- } }) })] }), _jsx(DropdownField, { label: "Select token", placeholder: loadingChains ? "Loading…" : "Token", value: token, options: tokenOptions, onSelect: onTokenSelect }), _jsx(DropdownField, { label: "Select chain", placeholder: loadingChains ? "Loading…" : "Chain", value: chain, options: chainOptions, onSelect: onChainSelect }), apiQuote && token && chain && (_jsx("div", { style: {
482
- padding: "12px 16px",
483
- borderRadius: radii.card,
484
- border: `1px solid ${colors.border}`,
485
- display: "flex",
486
- flexDirection: "column",
487
- gap: 8,
488
- }, children: loadingQuote ? (_jsx("span", { style: { fontSize: 13, color: colors.textSecondary }, children: "Loading\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: { fontSize: 13, color: colors.textSecondary }, children: ["Rate: 1 ", FUNDING_TOKEN_SYMBOL, " = ", apiQuote.rate, " ", apiQuote.token_symbol] }), apiQuote.expires_at && (_jsx(Countdown, { expiresAt: apiQuote.expires_at, isExpired: quoteExpired, onExpired: handleRefreshQuote })), quoteExpired && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: { fontSize: 13, color: "#f59e0b" }, children: "Quote expired, please refresh" }), _jsx("button", { type: "button", onClick: handleRefreshQuote, style: {
489
- padding: "4px 12px",
490
- fontSize: 12,
491
- borderRadius: radii.pill,
492
- border: `1px solid ${colors.border}`,
493
- background: "transparent",
494
- color: colors.textPrimary,
495
- cursor: "pointer",
496
- fontFamily: fonts.family,
497
- }, children: "Refresh" })] }))] })) })), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: "Amount" }), balance && (_jsxs("span", { style: { fontSize: 13, lineHeight: 1.2, color: colors.textSecondary }, children: ["Balance: \u00A0", formatBalanceTo2Decimals(balance), " ", FUNDING_TOKEN_SYMBOL] }))] }), _jsxs("div", { style: {
498
- display: "flex",
499
- alignItems: "center",
500
- justifyContent: "space-between",
501
- height: 48,
502
- padding: "0 16px",
503
- borderRadius: radii.input,
504
- border: `1px solid ${amountInputFocused ? colors.borderFocused : colors.border}`,
505
- boxSizing: "border-box",
506
- }, children: [_jsx("input", { value: amount, onChange: handleAmountChange, onFocus: () => setAmountInputFocused(true), onBlur: () => setAmountInputFocused(false), placeholder: "0.00", type: "text", inputMode: "decimal", style: {
507
- flex: 1,
508
- border: "none",
509
- outline: "none",
510
- background: "transparent",
511
- color: amount ? colors.textPrimary : colors.textSecondary,
512
- fontSize: 14,
513
- fontFamily: fonts.family,
514
- lineHeight: 1.4,
515
- padding: 0,
516
- } }), _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: _jsx("button", { type: "button", onClick: onMaxClick, style: {
517
- background: colors.textPrimary,
518
- border: "none",
519
- borderRadius: radii.pill,
520
- padding: "2px 8px",
521
- fontSize: 12,
522
- lineHeight: 1.4,
523
- fontWeight: 400,
524
- color: "#15181D",
525
- fontFamily: fonts.family,
526
- cursor: "pointer",
527
- }, children: "Max" }) })] })] }) })] }) })] })] })] }), _jsxs("div", { style: { padding: "20px 20px 0", display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }, id: "WithdrawModalReceiveAmount", children: [!trackingWithdraw && amount && Number(amount) > 0 && (_jsxs("span", { style: { fontSize: 14, lineHeight: 1.4, color: colors.textPrimary }, children: ["You will receive : ", receiveAmount] })), orderInProgress && withdrawOrder && (() => {
528
- const fundingHash = txHash ?? withdrawOrder?.funding_tx_hash;
529
- const showFundingLink = fundingHash && fundingChainId;
530
- try {
531
- return (_jsx("div", { style: { display: "none", flexDirection: "column", gap: 6, alignItems: "center" }, children: showFundingLink && (_jsx("a", { href: getExplorerUrl(fundingChainId, { txId: fundingHash }), target: "_blank", rel: "noopener noreferrer", style: { fontSize: 13, color: colors.textPrimary, textDecoration: "underline" }, children: "Withdraw request transaction on explorer" })) }));
532
- }
533
- catch {
534
- return (_jsx("span", { style: { fontSize: 13, color: colors.textPrimary }, children: "Withdraw in progress." }));
535
- }
536
- })(), orderInProgress && withdrawOrder && (_jsxs("span", { style: { fontSize: 13, color: colors.textSecondary, display: "none", alignItems: "center", gap: 6 }, children: ["Order status: ", withdrawOrder.status, _jsx("span", { style: {
537
- display: "inline-block",
538
- width: 10,
539
- height: 10,
540
- border: `2px solid ${colors.textSecondary}`,
541
- borderTopColor: "transparent",
542
- borderRadius: "50%",
543
- animation: "withdraw-modal-spin 0.7s linear infinite",
544
- }, "aria-hidden": true })] })), _jsx("style", { children: `@keyframes withdraw-modal-spin { to { transform: rotate(360deg); } }` }), orderSucceeded || trackingWithdraw ? null : (_jsx(SubmitButton, { disabled: !canSubmit || isSubmitting, status: status, onClick: () => onSubmit?.({
545
- toAddress: address,
546
- amount: amount ?? "",
547
- token: token ?? "",
548
- tokenAddress: resolvedTokenAddress ?? apiQuote?.token_address ?? "",
549
- chain: chain ?? "",
550
- }) }))] })] }));
551
- };
552
- const SubmitButton = ({ disabled, status, onClick }) => {
553
- const labels = {
554
- idle: disabled ? "Please fill in withdrawal information" : "Submit",
555
- pending: "Processing...",
556
- success: "Withdrawal successful",
557
- manual_review: "Under manual review",
558
- };
559
- const isDisabled = disabled || status !== "idle";
560
- return (_jsx("button", { type: "button", disabled: isDisabled, onClick: onClick, style: {
561
- width: 364,
562
- padding: "12px 0",
563
- borderRadius: radii.full,
564
- border: isDisabled ? `1px solid ${colors.buttonDisabledBorder}` : "none",
565
- background: isDisabled ? colors.buttonDisabledBg : colors.textPrimary,
566
- color: isDisabled ? colors.textSecondary : "#15181D",
567
- fontSize: 16,
568
- fontWeight: 500,
569
- lineHeight: 1.4,
570
- fontFamily: fonts.family,
571
- cursor: isDisabled ? "not-allowed" : "pointer",
572
- textAlign: "center",
573
- }, children: labels[status] }));
574
- };
@@ -1,4 +0,0 @@
1
- export interface CloseButtonProps {
2
- onClick?: () => void;
3
- }
4
- export declare const CloseButton: ({ onClick }: CloseButtonProps) => import("react/jsx-runtime.js").JSX.Element;
@@ -1,15 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { colors } from "../theme.js";
3
- export const CloseButton = ({ onClick }) => (_jsx("button", { type: "button", onClick: onClick, "aria-label": "Close", style: {
4
- width: 30,
5
- height: 30,
6
- borderRadius: "50%",
7
- background: colors.card,
8
- border: "none",
9
- cursor: "pointer",
10
- display: "flex",
11
- alignItems: "center",
12
- justifyContent: "center",
13
- padding: 0,
14
- flexShrink: 0,
15
- }, children: _jsx("svg", { width: "17", height: "17", viewBox: "0 0 17 17", fill: "none", children: _jsx("path", { d: "M4.25 4.25L12.75 12.75M12.75 4.25L4.25 12.75", stroke: colors.textPrimary, strokeWidth: "1.5", strokeLinecap: "round" }) }) }));
@@ -1,16 +0,0 @@
1
- export interface CountdownProps {
2
- /** 过期时间:ISO 字符串或时间戳(毫秒) */
3
- expiresAt: string | number;
4
- /** 过期时显示文案,默认 "Expired" */
5
- expiredLabel?: string;
6
- /** 自定义样式 */
7
- style?: React.CSSProperties;
8
- /** 是否已过期(由外部控制,用于与 quoteExpired 等联动时不再倒计时) */
9
- isExpired?: boolean;
10
- /** 倒计时归零时调用一次(可用于自动重新询价等) */
11
- onExpired?: () => void;
12
- }
13
- /**
14
- * 倒计时组件:每秒更新,显示距 expiresAt 的剩余时间(MM:SS)。
15
- */
16
- export declare function Countdown({ expiresAt, expiredLabel, style, isExpired, onExpired, }: CountdownProps): import("react/jsx-runtime.js").JSX.Element;