@ab-org/predicate-market-sdk 0.0.1 → 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/README.md +25 -7
- package/dist/auth/oidcRelay.d.ts +11 -0
- package/dist/auth/oidcRelay.js +107 -0
- package/dist/auth/walletAccount.d.ts +20 -0
- package/dist/auth/walletAccount.js +267 -0
- package/dist/constants/chains.d.ts +2 -22
- package/dist/constants/chains.js +3 -23
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/modules/api.d.ts +12 -7
- package/dist/modules/api.js +6 -4
- package/dist/modules/balanceQuery.d.ts +11 -4
- package/dist/modules/balanceQuery.js +12 -10
- package/dist/modules/marketData.d.ts +5 -5
- package/dist/modules/marketData.js +33 -39
- package/dist/modules/withdrawDirect.d.ts +14 -0
- package/dist/modules/withdrawDirect.js +33 -0
- package/dist/modules/withdrawExecutor.d.ts +15 -6
- package/dist/modules/withdrawExecutor.js +21 -20
- package/dist/ui/DepositModal.js +7 -14
- package/dist/ui/SignInModal.js +3 -0
- package/dist/ui/SignInModal.shared.js +40 -1
- package/dist/ui/WithdrawModal.d.ts +12 -2
- package/dist/ui/WithdrawModal.js +101 -55
- package/dist/ui/signInTypes.d.ts +3 -0
- package/dist/ui/useSignInModalController.d.ts +2 -2
- package/dist/ui/useSignInModalController.js +34 -88
- package/dist/utils/env.js +2 -0
- package/dist/walletUtils.d.ts +1 -1
- package/dist/walletUtils.js +1 -1
- package/package.json +3 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChangeEventHandler } from "react";
|
|
2
2
|
import { type SelectOption } from "./components/DropdownField.js";
|
|
3
|
+
import { type WithdrawOrderResponseData } from "../modules/api.js";
|
|
3
4
|
export type WithdrawUiStatus = "idle" | "pending" | "success" | "manual_review";
|
|
4
5
|
export interface WithdrawModalProps {
|
|
5
6
|
address?: string;
|
|
@@ -18,6 +19,14 @@ export interface WithdrawModalProps {
|
|
|
18
19
|
useMerchantApi?: boolean;
|
|
19
20
|
/** 创建订单后传入,用于轮询提现订单状态 */
|
|
20
21
|
orderId?: string;
|
|
22
|
+
/** 提现模式:direct 直接提现,cross_chain 跨链提现 */
|
|
23
|
+
withdrawMode?: "direct" | "cross_chain";
|
|
24
|
+
/**
|
|
25
|
+
* 直接提现模式(无 orderId)下的结果数据;
|
|
26
|
+
* 将用来构造一个与 getWithdrawOrder 返回值兼容的对象,从而复用“有 orderId”时的全部交互与展示。
|
|
27
|
+
* 最少建议提供:status、dst_token_amount、target_chain_id、target_address(必要时包含 dst_tx_hash / out_tx_hash、fee)。
|
|
28
|
+
*/
|
|
29
|
+
withdrawDirectResult?: Partial<WithdrawOrderResponseData>;
|
|
21
30
|
/** 成功页 Fee 展示:优先用订单接口返回的 fee,若后端未返回则用此值(如询价时的 fee),均无则显示 "—" */
|
|
22
31
|
feeDisplay?: string;
|
|
23
32
|
/** 广播后的 funding tx 所在链 id,与 txHash 一起用于展示「查看交易」链接 */
|
|
@@ -30,11 +39,12 @@ export interface WithdrawModalProps {
|
|
|
30
39
|
onChainSelect?: (id: string) => void;
|
|
31
40
|
onAmountChange?: ChangeEventHandler<HTMLInputElement>;
|
|
32
41
|
onMaxClick?: () => void;
|
|
33
|
-
/** 提交时传入当前表单值(收款地址、金额、所选 token
|
|
42
|
+
/** 提交时传入当前表单值(收款地址、金额、所选 token、链上合约地址、所选 chain),由调用方执行提现 */
|
|
34
43
|
onSubmit?: (payload: {
|
|
35
44
|
toAddress: string;
|
|
36
45
|
amount: string;
|
|
37
46
|
token: string;
|
|
47
|
+
tokenAddress: string;
|
|
38
48
|
chain: string;
|
|
39
49
|
}) => void;
|
|
40
50
|
/** 提现订单状态变为 completed 时调用,用于调用方刷新余额等 */
|
|
@@ -44,4 +54,4 @@ export interface WithdrawModalProps {
|
|
|
44
54
|
onSignIn?: () => void;
|
|
45
55
|
onClose?: () => void;
|
|
46
56
|
}
|
|
47
|
-
export declare const WithdrawModal: ({ address, token, tokenSymbol, chain, amount, balance, status, receiveAmount: receiveAmountProp, txHash, tokenOptions: tokenOptionsProp, chainOptions: chainOptionsProp, useMerchantApi, orderId, feeDisplay, fundingChainId,
|
|
57
|
+
export declare const WithdrawModal: ({ address, token, tokenSymbol, chain, amount, balance, status, receiveAmount: receiveAmountProp, txHash, tokenOptions: tokenOptionsProp, chainOptions: chainOptionsProp, useMerchantApi, orderId, withdrawMode, withdrawDirectResult, feeDisplay, fundingChainId, onShowToast, onAddressChange, onTokenSelect, onChainSelect, onAmountChange, onMaxClick, onSubmit, onWithdrawCompleted, onStartAnotherWithdrawal, onSignIn, onClose, }: WithdrawModalProps) => import("react/jsx-runtime.js").JSX.Element;
|
package/dist/ui/WithdrawModal.js
CHANGED
|
@@ -8,11 +8,13 @@ import { LoginRequiredOverlay } from "./components/LoginRequiredOverlay.js";
|
|
|
8
8
|
import { useSession } from "./hooks/useSession.js";
|
|
9
9
|
import { colors, fonts, radii } from "./theme.js";
|
|
10
10
|
import { getChains, quote, getWithdrawOrder, } from "../modules/api.js";
|
|
11
|
+
import { getEnv } from "../utils/env";
|
|
11
12
|
import { getExplorerUrl } from "../utils/explorer.js";
|
|
12
13
|
import { SuccessIcon } from "./components/Success";
|
|
13
14
|
function CopyIcon() {
|
|
14
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" })] }));
|
|
15
16
|
}
|
|
17
|
+
const FUNDING_TOKEN_SYMBOL = getEnv("FUNDING_TOKEN_SYMBOL");
|
|
16
18
|
function chainsToTokenOptions(chains) {
|
|
17
19
|
const bySymbol = new Map();
|
|
18
20
|
for (const c of chains) {
|
|
@@ -36,7 +38,7 @@ function getTokenAddressForChain(chains, chainId, tokenSymbol) {
|
|
|
36
38
|
const chain = chains.find((c) => c.chain_id === chainId);
|
|
37
39
|
return chain?.tokens.find((t) => t.symbol === tokenSymbol)?.address;
|
|
38
40
|
}
|
|
39
|
-
/** 从 balance 字符串解析出数值部分(如 "123.46
|
|
41
|
+
/** 从 balance 字符串解析出数值部分(如 "123.46 xxx" → 123.46) */
|
|
40
42
|
function parseBalanceNumber(balance) {
|
|
41
43
|
const match = balance.trim().match(/^(\d*\.?\d*)/);
|
|
42
44
|
if (!match)
|
|
@@ -44,7 +46,7 @@ function parseBalanceNumber(balance) {
|
|
|
44
46
|
const n = Number(match[1]);
|
|
45
47
|
return Number.isNaN(n) ? null : n;
|
|
46
48
|
}
|
|
47
|
-
/** 将 balance 字符串格式化为小数点后 2 位(如 "123.456789012
|
|
49
|
+
/** 将 balance 字符串格式化为小数点后 2 位(如 "123.456789012 xxx" → "123.46 xxx") */
|
|
48
50
|
function formatBalanceTo2Decimals(balance) {
|
|
49
51
|
const match = balance.trim().match(/^(\d*\.?\d*)(.*)$/);
|
|
50
52
|
if (!match)
|
|
@@ -57,7 +59,6 @@ function formatBalanceTo2Decimals(balance) {
|
|
|
57
59
|
const formatted = n.toFixed(2);
|
|
58
60
|
return formatted + suffix;
|
|
59
61
|
}
|
|
60
|
-
const WEI_PER_ETHER = 1e18;
|
|
61
62
|
/** Wei (string) to ether display string; keeps up to 6 decimal places, strips trailing zeros */
|
|
62
63
|
function weiToEtherDisplay(wei) {
|
|
63
64
|
const s = (wei || "0").trim().replace(/^0+/, "") || "0";
|
|
@@ -74,7 +75,7 @@ function weiToEtherDisplay(wei) {
|
|
|
74
75
|
return fixed;
|
|
75
76
|
}
|
|
76
77
|
const POLL_INTERVAL_MS = 4000;
|
|
77
|
-
export const WithdrawModal = ({ address = "", token, tokenSymbol
|
|
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, }) => {
|
|
78
79
|
const session = useSession();
|
|
79
80
|
const addressInputRef = useRef(null);
|
|
80
81
|
const [addressInputFocused, setAddressInputFocused] = useState(false);
|
|
@@ -100,7 +101,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
100
101
|
: undefined;
|
|
101
102
|
if (!raw?.length)
|
|
102
103
|
return undefined;
|
|
103
|
-
return raw
|
|
104
|
+
return raw;
|
|
104
105
|
}, [tokenOptionsProp, apiChains]);
|
|
105
106
|
const chainOptions = useMemo(() => {
|
|
106
107
|
if (chainOptionsProp?.length)
|
|
@@ -109,7 +110,19 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
109
110
|
return undefined;
|
|
110
111
|
return chainsToChainOptionsForToken(apiChains, token);
|
|
111
112
|
}, [chainOptionsProp, apiChains, token]);
|
|
112
|
-
|
|
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]);
|
|
113
126
|
// Default focus on recipient address when form is visible; white border while focused, revert on blur
|
|
114
127
|
useEffect(() => {
|
|
115
128
|
if (trackingWithdraw)
|
|
@@ -126,10 +139,10 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
126
139
|
return;
|
|
127
140
|
setLoadingChains(true);
|
|
128
141
|
getChains()
|
|
129
|
-
.then((res) => setApiChains(res?.chains ??
|
|
142
|
+
.then((res) => setApiChains(res?.chains ?? []))
|
|
130
143
|
.finally(() => setLoadingChains(false));
|
|
131
144
|
}, [shouldLoadChains]);
|
|
132
|
-
// 选中 token + chain 后即询价;未填 amount 时用 1
|
|
145
|
+
// 选中 token + chain 后即询价;未填 amount 时用 1 xxx 获取 rate
|
|
133
146
|
useEffect(() => {
|
|
134
147
|
if (!apiChains?.length || !token || !chain) {
|
|
135
148
|
setApiQuote(null);
|
|
@@ -147,7 +160,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
147
160
|
direction: "withdraw",
|
|
148
161
|
chain_id: chain,
|
|
149
162
|
token_address: tokenAddress,
|
|
150
|
-
|
|
163
|
+
dst_token_amount: amountWei,
|
|
151
164
|
})
|
|
152
165
|
.then((q) => setApiQuote(q ?? null))
|
|
153
166
|
.catch(() => {
|
|
@@ -183,41 +196,73 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
183
196
|
order_id: orderId ?? "",
|
|
184
197
|
status: "pending",
|
|
185
198
|
chain_id: "56",
|
|
186
|
-
|
|
199
|
+
dst_token_amount: "0",
|
|
187
200
|
target_chain_id: chain ?? "1",
|
|
188
201
|
target_address: address ?? "0x",
|
|
189
202
|
created_at: new Date().toISOString(),
|
|
190
203
|
updated_at: new Date().toISOString(),
|
|
191
204
|
}), [orderId, chain, address]);
|
|
192
205
|
useEffect(() => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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);
|
|
198
218
|
getWithdrawOrder(orderId)
|
|
199
|
-
.then(
|
|
200
|
-
setWithdrawOrder(order);
|
|
201
|
-
if (order.status === "completed" && onShowToast) {
|
|
202
|
-
onShowToast("Withdrawal completed");
|
|
203
|
-
}
|
|
204
|
-
})
|
|
219
|
+
.then(setWithdrawOrder)
|
|
205
220
|
.catch(() => setWithdrawOrder(mockWithdrawOrderState));
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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);
|
|
213
255
|
useEffect(() => {
|
|
214
|
-
if (withdrawOrder?.status
|
|
215
|
-
|
|
256
|
+
if (withdrawOrder?.status !== "completed")
|
|
257
|
+
return;
|
|
258
|
+
const key = orderId ?? withdrawOrder.order_id;
|
|
259
|
+
if (key && completedKeyRef.current !== key) {
|
|
260
|
+
completedKeyRef.current = key;
|
|
216
261
|
onWithdrawCompleted?.();
|
|
217
262
|
}
|
|
218
|
-
if (!
|
|
219
|
-
|
|
220
|
-
}, [withdrawOrder?.status, orderId, onWithdrawCompleted]);
|
|
263
|
+
if (!trackingWithdraw)
|
|
264
|
+
completedKeyRef.current = null;
|
|
265
|
+
}, [withdrawOrder?.status, orderId, trackingWithdraw, onWithdrawCompleted]);
|
|
221
266
|
const receiveAmount = useMemo(() => {
|
|
222
267
|
if (receiveAmountProp) {
|
|
223
268
|
const n = Number(receiveAmountProp);
|
|
@@ -254,7 +299,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
254
299
|
direction: "withdraw",
|
|
255
300
|
chain_id: chain,
|
|
256
301
|
token_address: tokenAddress,
|
|
257
|
-
|
|
302
|
+
dst_token_amount: amountWei,
|
|
258
303
|
})
|
|
259
304
|
.then((q) => setApiQuote(q ?? null))
|
|
260
305
|
.catch(() => {
|
|
@@ -379,7 +424,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
379
424
|
flexDirection: "column",
|
|
380
425
|
gap: 4,
|
|
381
426
|
width: "100%",
|
|
382
|
-
}, 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.
|
|
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: {
|
|
383
428
|
padding: 0,
|
|
384
429
|
border: "none",
|
|
385
430
|
background: "transparent",
|
|
@@ -390,7 +435,12 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
390
435
|
justifyContent: "center",
|
|
391
436
|
width: 16,
|
|
392
437
|
height: 16,
|
|
393
|
-
}, children: _jsx(CopyIcon, {}) })] })] }))] }), _jsx("div", { style: { paddingTop: 24, width: "100%", display: "flex", justifyContent: "center" }, children: _jsx("button", { type: "button", onClick: () =>
|
|
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: {
|
|
394
444
|
width: 274,
|
|
395
445
|
padding: "12px 0",
|
|
396
446
|
borderRadius: radii.full,
|
|
@@ -435,7 +485,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
435
485
|
display: "flex",
|
|
436
486
|
flexDirection: "column",
|
|
437
487
|
gap: 8,
|
|
438
|
-
}, 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
|
|
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: {
|
|
439
489
|
padding: "4px 12px",
|
|
440
490
|
fontSize: 12,
|
|
441
491
|
borderRadius: radii.pill,
|
|
@@ -444,7 +494,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
444
494
|
color: colors.textPrimary,
|
|
445
495
|
cursor: "pointer",
|
|
446
496
|
fontFamily: fonts.family,
|
|
447
|
-
}, 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)] }))] }), _jsxs("div", { style: {
|
|
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: {
|
|
448
498
|
display: "flex",
|
|
449
499
|
alignItems: "center",
|
|
450
500
|
justifyContent: "space-between",
|
|
@@ -463,23 +513,18 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
463
513
|
fontFamily: fonts.family,
|
|
464
514
|
lineHeight: 1.4,
|
|
465
515
|
padding: 0,
|
|
466
|
-
} }),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
fontWeight: 400,
|
|
479
|
-
color: "#15181D",
|
|
480
|
-
fontFamily: fonts.family,
|
|
481
|
-
cursor: "pointer",
|
|
482
|
-
}, 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 && (() => {
|
|
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 && (() => {
|
|
483
528
|
const fundingHash = txHash ?? withdrawOrder?.funding_tx_hash;
|
|
484
529
|
const showFundingLink = fundingHash && fundingChainId;
|
|
485
530
|
try {
|
|
@@ -500,6 +545,7 @@ export const WithdrawModal = ({ address = "", token, tokenSymbol = "USDT", chain
|
|
|
500
545
|
toAddress: address,
|
|
501
546
|
amount: amount ?? "",
|
|
502
547
|
token: token ?? "",
|
|
548
|
+
tokenAddress: resolvedTokenAddress ?? apiQuote?.token_address ?? "",
|
|
503
549
|
chain: chain ?? "",
|
|
504
550
|
}) }))] })] }));
|
|
505
551
|
};
|
package/dist/ui/signInTypes.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ export interface SignInUiConfig {
|
|
|
16
16
|
socialProviders?: SocialProvider[];
|
|
17
17
|
wallets?: WalletItem[];
|
|
18
18
|
initialVisibleCount?: number;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Ignored by `SignInModal` / `useSignInModalController`: social login creates the session without `window.confirm`.
|
|
21
|
+
*/
|
|
19
22
|
sessionConfirmation?: {
|
|
20
23
|
enabled?: boolean;
|
|
21
24
|
title?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { GoogleCredential } from "../auth/google.js";
|
|
2
|
+
import type { TwitterAuthResult } from "../auth/twitter.js";
|
|
3
3
|
import { type CubeSignerSession, type WalletSession } from "@ab-org/sdk-core";
|
|
4
4
|
import type { WalletItem } from "./signInTypes.js";
|
|
5
5
|
export interface SignInModalControllerOptions {
|
|
@@ -1,71 +1,25 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
2
|
import { getSDKConfig } from "../auth/config.js";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
import { createOidcRelayAuth } from "../auth/oidcRelay.js";
|
|
4
|
+
import WalletAccount from "../auth/walletAccount.js";
|
|
5
|
+
import { getEnv } from "../utils/env.js";
|
|
6
|
+
import { WalletConnector, createDefaultInjectedWalletRegistry, } from "@ab-org/sdk-core";
|
|
6
7
|
import { resolveWalletItems } from "./SignInModal.shared.js";
|
|
8
|
+
function resolveOidcStage() {
|
|
9
|
+
return getEnv("STAGE").toLowerCase() === "prod" ? "prod" : "dev";
|
|
10
|
+
}
|
|
7
11
|
export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogleLogin, onTwitterLogin, onCubeSignerSession, onWalletConnected, onSocialLogin, onWalletSelect, }) => {
|
|
8
12
|
const [expanded, setExpanded] = useState(false);
|
|
9
13
|
const [loadingProvider, setLoadingProvider] = useState(null);
|
|
10
14
|
const [loadingWalletId, setLoadingWalletId] = useState(null);
|
|
11
15
|
const defaultWalletRegistry = useMemo(() => createDefaultInjectedWalletRegistry(), []);
|
|
12
|
-
const sdkConfig = getSDKConfig();
|
|
13
|
-
const cubistProvider = useMemo(() => {
|
|
14
|
-
if (!sdkConfig.cubeSigner)
|
|
15
|
-
return null;
|
|
16
|
-
return new CubistSocialProvider({
|
|
17
|
-
...sdkConfig.cubeSigner,
|
|
18
|
-
defaultSessionPolicy: sdkConfig.signIn?.sessionPolicy ?? sdkConfig.cubeSigner.defaultSessionPolicy,
|
|
19
|
-
});
|
|
20
|
-
}, [sdkConfig.cubeSigner, sdkConfig.signIn?.sessionPolicy]);
|
|
21
16
|
const defaultWalletConnector = useMemo(() => new WalletConnector([
|
|
22
17
|
...defaultWalletRegistry.map((item) => item.provider),
|
|
23
|
-
|
|
24
|
-
]), [cubistProvider, defaultWalletRegistry]);
|
|
18
|
+
]), [defaultWalletRegistry]);
|
|
25
19
|
const resolvedWallets = useMemo(() => resolveWalletItems(wallets, defaultWalletRegistry), [defaultWalletRegistry, wallets]);
|
|
26
20
|
const showExpandToggle = resolvedWallets.length > initialVisibleCount;
|
|
27
21
|
const visibleWallets = expanded ? resolvedWallets : resolvedWallets.slice(0, initialVisibleCount);
|
|
28
22
|
const isBusy = !!loadingProvider || !!loadingWalletId;
|
|
29
|
-
const confirmCapabilitySession = async (providerId, cubistForPolicy) => {
|
|
30
|
-
const cubist = cubistForPolicy ?? cubistProvider;
|
|
31
|
-
const confirmation = sdkConfig.signIn?.sessionConfirmation;
|
|
32
|
-
const policy = cubist?.cubeSignerAuth.defaultSessionPolicy;
|
|
33
|
-
if (confirmation?.enabled === false || !policy) {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
if (typeof window === "undefined" || typeof window.confirm !== "function") {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
const lines = describeSessionCapabilityPolicy(cubist?.cubeSignerAuth.defaultSessionPolicy
|
|
40
|
-
? {
|
|
41
|
-
id: "preview",
|
|
42
|
-
...cubist.cubeSignerAuth.defaultSessionPolicy,
|
|
43
|
-
}
|
|
44
|
-
: undefined);
|
|
45
|
-
const message = [
|
|
46
|
-
confirmation?.title ?? "Authorize smart-wallet session",
|
|
47
|
-
confirmation?.message ?? `Continue with ${providerId} and create a capability session?`,
|
|
48
|
-
...lines,
|
|
49
|
-
]
|
|
50
|
-
.filter(Boolean)
|
|
51
|
-
.join("\n");
|
|
52
|
-
return window.confirm(message);
|
|
53
|
-
};
|
|
54
|
-
const getCubistAndConnector = (config) => {
|
|
55
|
-
if (!config.cubeSigner)
|
|
56
|
-
return { cubist: null, connector: defaultWalletConnector };
|
|
57
|
-
if (cubistProvider)
|
|
58
|
-
return { cubist: cubistProvider, connector: defaultWalletConnector };
|
|
59
|
-
const cubist = new CubistSocialProvider({
|
|
60
|
-
...config.cubeSigner,
|
|
61
|
-
defaultSessionPolicy: config.signIn?.sessionPolicy ?? config.cubeSigner.defaultSessionPolicy,
|
|
62
|
-
});
|
|
63
|
-
const connector = new WalletConnector([
|
|
64
|
-
...defaultWalletRegistry.map((item) => item.provider),
|
|
65
|
-
cubist,
|
|
66
|
-
]);
|
|
67
|
-
return { cubist, connector };
|
|
68
|
-
};
|
|
69
23
|
const handleSocialClick = async (providerId) => {
|
|
70
24
|
setLoadingProvider(providerId);
|
|
71
25
|
try {
|
|
@@ -73,55 +27,47 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
|
|
|
73
27
|
if (!config.cubeSigner) {
|
|
74
28
|
throw new Error("cubeSigner config is required for social login");
|
|
75
29
|
}
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!confirmed)
|
|
82
|
-
return null;
|
|
30
|
+
const auth = createOidcRelayAuth({
|
|
31
|
+
stage: resolveOidcStage(),
|
|
32
|
+
googleClientId: config.googleClientId ?? "",
|
|
33
|
+
xClientId: config.twitterClientId ?? "",
|
|
34
|
+
});
|
|
83
35
|
if (providerId === "google") {
|
|
84
36
|
if (!config.googleClientId) {
|
|
85
37
|
throw new Error("googleClientId is required for Google sign-in");
|
|
86
38
|
}
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const cubeSignerSession =
|
|
39
|
+
const oidcToken = await auth.loginByGoogle();
|
|
40
|
+
const authSource = "google";
|
|
41
|
+
WalletAccount.clearInstance();
|
|
42
|
+
await WalletAccount.getInstance(oidcToken, authSource, config.cubeSigner);
|
|
43
|
+
const session = WalletAccount.getWalletSession();
|
|
44
|
+
const cubeSignerSession = WalletAccount.getCubeSignerSession();
|
|
45
|
+
onGoogleLogin?.({ idToken: oidcToken });
|
|
93
46
|
if (cubeSignerSession)
|
|
94
47
|
onCubeSignerSession?.(cubeSignerSession);
|
|
95
48
|
onSocialLogin?.(providerId);
|
|
49
|
+
if (!session) {
|
|
50
|
+
throw new Error("Failed to build wallet session from Google login");
|
|
51
|
+
}
|
|
96
52
|
return session;
|
|
97
53
|
}
|
|
98
54
|
if (providerId === "x") {
|
|
99
55
|
if (!config.twitterClientId) {
|
|
100
56
|
throw new Error("twitterClientId is required for X sign-in");
|
|
101
57
|
}
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
const result = await signInWithTwitter(config.twitterClientId, redirectUri);
|
|
110
|
-
onTwitterLogin?.(result);
|
|
111
|
-
const session = await connector.connect(cubist.id, {
|
|
112
|
-
payload: {
|
|
113
|
-
type: "twitter",
|
|
114
|
-
params: {
|
|
115
|
-
code: result.code,
|
|
116
|
-
codeVerifier: result.codeVerifier,
|
|
117
|
-
redirectUri,
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
const cubeSignerSession = cubist.cubeSignerSession;
|
|
58
|
+
const oidcToken = await auth.loginByX();
|
|
59
|
+
const authSource = "twitter";
|
|
60
|
+
WalletAccount.clearInstance();
|
|
61
|
+
await WalletAccount.getInstance(oidcToken, authSource, config.cubeSigner);
|
|
62
|
+
const session = WalletAccount.getWalletSession();
|
|
63
|
+
const cubeSignerSession = WalletAccount.getCubeSignerSession();
|
|
64
|
+
onTwitterLogin?.({ code: "", codeVerifier: "", state: "" });
|
|
122
65
|
if (cubeSignerSession)
|
|
123
66
|
onCubeSignerSession?.(cubeSignerSession);
|
|
124
67
|
onSocialLogin?.(providerId);
|
|
68
|
+
if (!session) {
|
|
69
|
+
throw new Error("Failed to build wallet session from X login");
|
|
70
|
+
}
|
|
125
71
|
return session;
|
|
126
72
|
}
|
|
127
73
|
throw new Error(`Unsupported social provider: ${providerId}`);
|
|
@@ -152,7 +98,7 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
|
|
|
152
98
|
return session;
|
|
153
99
|
}
|
|
154
100
|
catch (error) {
|
|
155
|
-
console.error(`[
|
|
101
|
+
console.error(`[predicate-market-sdk] Wallet connect failed for ${walletId}:`, error);
|
|
156
102
|
throw error;
|
|
157
103
|
}
|
|
158
104
|
finally {
|
package/dist/utils/env.js
CHANGED
|
@@ -28,6 +28,8 @@ function readProcessEnvStatic(key) {
|
|
|
28
28
|
return pick(process.env.NEXT_PUBLIC_CUBE_REG, process.env.CUBE_REG);
|
|
29
29
|
case "RELAY_ORIGIN":
|
|
30
30
|
return pick(process.env.NEXT_PUBLIC_RELAY_ORIGIN, process.env.RELAY_ORIGIN);
|
|
31
|
+
case "FUNDING_TOKEN_SYMBOL":
|
|
32
|
+
return pick(process.env.NEXT_PUBLIC_FUNDING_TOKEN_SYMBOL, process.env.FUNDING_TOKEN_SYMBOL) || "USDT";
|
|
31
33
|
default:
|
|
32
34
|
return undefined;
|
|
33
35
|
}
|
package/dist/walletUtils.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { getEnv } from "./utils/env.js";
|
|
2
2
|
export { getExplorerUrl } from "./utils/explorer.js";
|
|
3
|
-
export {
|
|
3
|
+
export { ClientIds, DEFAULT_FUNDING_TOKEN_ADDRESS, DEFAULT_FUNDING_CHAIN_ID, getFundingTokenAddress, getChainInfo, type EvmChainInfo, } from "./constants/chains.js";
|
package/dist/walletUtils.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { getEnv } from "./utils/env.js";
|
|
2
2
|
export { getExplorerUrl } from "./utils/explorer.js";
|
|
3
|
-
export {
|
|
3
|
+
export { ClientIds, DEFAULT_FUNDING_TOKEN_ADDRESS, DEFAULT_FUNDING_CHAIN_ID, getFundingTokenAddress, getChainInfo, } from "./constants/chains.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ab-org/predicate-market-sdk",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*",
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
"registry": "https://registry.npmjs.org/"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@cubist-labs/cubesigner-sdk": "^0.4.219",
|
|
23
24
|
"axios": "^1.13.6",
|
|
24
25
|
"qrcode-generator": "^2.0.4",
|
|
26
|
+
"@ab-org/wallet-utils": "0.0.7",
|
|
25
27
|
"@ab-org/sdk-core": "0.0.1"
|
|
26
28
|
},
|
|
27
29
|
"peerDependencies": {
|