@ab-org/predicate-market-sdk 0.0.2 → 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 CHANGED
@@ -94,6 +94,16 @@ initSDK({
94
94
 
95
95
  Rules:
96
96
  - **`NEXT_PUBLIC_RELAY_ORIGIN`** (or `RELAY_ORIGIN`) must match where you serve **`/relay/google`** and **`/relay/x`**
97
+
98
+ ### Funding chain & funding token (withdraw / balance)
99
+
100
+ - **`getChainInfo(chainId?)`** — resolves RPC, explorer metadata, native currency fields, and **`defaultFundingTokenAddress`** (per-chain default ERC-20 for the funding leg). If **`chainId`** is omitted or empty, defaults to **`3131`** (Tenderly BSC vnet). Built-in ids are **`3131`** and **`56`** (BSC mainnet); unknown ids throw.
101
+ - **`getFundingTokenAddress(chainId?)`** — same optional **`chainId`** as **`getChainInfo`**. If **`NEXT_PUBLIC_FUNDING_TOKEN_ADDRESS`** / **`FUNDING_TOKEN_ADDRESS`** or legacy **`NEXT_PUBLIC_BSC_USD1_ADDRESS`** / **`BSC_USD1_ADDRESS`** is set (checksummed `0x` + 40 hex), that value overrides defaults for **all** chains; otherwise returns **`getChainInfo(chainId).defaultFundingTokenAddress`**.
102
+ - **`DEFAULT_FUNDING_TOKEN_ADDRESS`** — shorthand for **`getChainInfo().defaultFundingTokenAddress`** (default funding chain **`3131`**).
103
+ - **`fetchFundingTokenBalance(address, { chainId, rpcUrl?, tokenAddress?, decimals?, displaySymbol? })`** — uses **`getChainInfo(chainId)`** for RPC when **`rpcUrl`** is omitted, **`getFundingTokenAddress(chainId)`** when **`tokenAddress`** is omitted, and **`decimals`** default **`chain.nativeCurrencyDecimals`** when omitted (override if your funding token uses different decimals).
104
+ - **`createFundingWithdrawExecutor({ chainId?, rpcUrl?, tokenAddress?, … })`** — uses the same **`chainId`** for **`getChainInfo`**, default token address (**`getFundingTokenAddress(chainId)`**), and order payload (default **`3131`** when **`chainId`** omitted).
105
+
106
+ **Type note:** **`EvmChainInfo`** includes **`defaultFundingTokenAddress`**. If you construct chain objects manually in TypeScript, add that field or use **`getChainInfo`** instead of literals.
97
107
  - `socialProviders: undefined` uses built-in defaults (`google`, `x`)
98
108
  - `socialProviders: []` hides all social buttons
99
109
  - known social ids like `google` and `x` automatically reuse built-in icons unless you override `icon`
@@ -1,22 +1,2 @@
1
- export declare const testBsc: {
2
- readonly chainId: "3131";
3
- readonly name: "BSC_TENDERLY";
4
- readonly chainName: "BSC_TENDERLY";
5
- readonly nativeCurrencyName: "BSC";
6
- readonly nativeCurrencySymbol: "BSC";
7
- readonly nativeCurrencyDecimals: 18;
8
- readonly rpcUrls: readonly ["https://virtual.binance.eu.rpc.tenderly.co/e643ea28-32eb-4fb9-8116-90be24f7defa"];
9
- readonly blockExplorerUrl: "https://dashboard.tenderly.co/explorer/vnet/6593bc72-f548-497d-bff9-5be061436a48";
10
- readonly platformType: "EVM";
11
- readonly icon: "https://static.tomo.inc/token/bsc_new.svg";
12
- readonly supportSwap: true;
13
- readonly supportGift: true;
14
- readonly supportHistory: true;
15
- };
16
- export declare const clientIds: {
17
- readonly google: string;
18
- readonly x: string;
19
- readonly sdk: "MfLzAb2ItdDR3RZfE33ueqUhRf3mo1w0tBzLfExvLG72oUzvI6mgACtq19v6Vz7cmHVsfMBpbmr1fFU09sn30wbD";
20
- };
21
- export declare const BSC_USD1_ADDRESS = "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d";
22
- export declare const BSC_USD1_PLATFORM_CONTRACT_ADDRESS = "0x1234567890123456789012345678901234567890";
1
+ export { ClientIds, DEFAULT_FUNDING_CHAIN_ID, DEFAULT_FUNDING_TOKEN_ADDRESS, getFundingTokenAddress, getChainInfo, type EvmChainInfo, } from "@ab-org/wallet-utils";
2
+ /** Demo / docs placeholder platform contract; replace in production. */
@@ -1,23 +1,3 @@
1
- import { getEnv } from "../utils/env.js";
2
- export const testBsc = {
3
- chainId: "3131",
4
- name: "BSC_TENDERLY",
5
- chainName: "BSC_TENDERLY",
6
- nativeCurrencyName: "BSC",
7
- nativeCurrencySymbol: "BSC",
8
- nativeCurrencyDecimals: 18,
9
- rpcUrls: ["https://virtual.binance.eu.rpc.tenderly.co/e643ea28-32eb-4fb9-8116-90be24f7defa"],
10
- blockExplorerUrl: "https://dashboard.tenderly.co/explorer/vnet/6593bc72-f548-497d-bff9-5be061436a48",
11
- platformType: "EVM",
12
- icon: "https://static.tomo.inc/token/bsc_new.svg",
13
- supportSwap: true,
14
- supportGift: true,
15
- supportHistory: true,
16
- };
17
- export const clientIds = {
18
- google: getEnv("GOOGLE_CLIENT_ID"),
19
- x: getEnv("X_CLIENT_ID"),
20
- sdk: "MfLzAb2ItdDR3RZfE33ueqUhRf3mo1w0tBzLfExvLG72oUzvI6mgACtq19v6Vz7cmHVsfMBpbmr1fFU09sn30wbD",
21
- };
22
- export const BSC_USD1_ADDRESS = "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d";
23
- export const BSC_USD1_PLATFORM_CONTRACT_ADDRESS = "0x1234567890123456789012345678901234567890";
1
+ export { ClientIds, DEFAULT_FUNDING_CHAIN_ID, DEFAULT_FUNDING_TOKEN_ADDRESS, getFundingTokenAddress, getChainInfo, } from "@ab-org/wallet-utils";
2
+ /** Demo / docs placeholder platform contract; replace in production. */
3
+ // export const FUNDING_TOKEN_PLATFORM_CONTRACT_ADDRESS ="0x1234567890123456789012345678901234567890";
package/dist/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export * from "./modules/withdraw.js";
8
8
  export * from "./modules/marketData.js";
9
9
  export * from "./modules/balanceQuery.js";
10
10
  export * from "./modules/withdrawExecutor.js";
11
+ export * from "./modules/api.js";
12
+ export * from "./modules/withdrawDirect.js";
11
13
  export * from "./policyAdapter.js";
12
14
  export * from "./ui/SignInModal.js";
13
15
  export * from "./ui/signInTypes.js";
@@ -17,3 +19,4 @@ export * from "./ui/WithdrawModal.js";
17
19
  export * from "./ui/components/DropdownField.js";
18
20
  export * from "./ui/components/DepositDetailsPanel.js";
19
21
  export * from "./walletUtils.js";
22
+ export * from "./constants/chains.js";
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ export * from "./modules/withdraw.js";
8
8
  export * from "./modules/marketData.js";
9
9
  export * from "./modules/balanceQuery.js";
10
10
  export * from "./modules/withdrawExecutor.js";
11
+ export * from "./modules/api.js";
12
+ export * from "./modules/withdrawDirect.js";
11
13
  export * from "./policyAdapter.js";
12
14
  export * from "./ui/SignInModal.js";
13
15
  export * from "./ui/signInTypes.js";
@@ -17,3 +19,5 @@ export * from "./ui/WithdrawModal.js";
17
19
  export * from "./ui/components/DropdownField.js";
18
20
  export * from "./ui/components/DepositDetailsPanel.js";
19
21
  export * from "./walletUtils.js";
22
+ // Re-export funding chain helpers so apps can import from package root
23
+ export * from "./constants/chains.js";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Merchant API client (BaseUrl: https://merchant.tomo.services)
2
+ * Merchant API client
3
3
  * 基于 axios 封装,类型与文档一致。
4
4
  */
5
5
  import { type AxiosInstance } from "axios";
@@ -14,6 +14,10 @@ export interface TokenData {
14
14
  symbol: string;
15
15
  address: string;
16
16
  decimals: number;
17
+ /** 最小充值量(最小单位字符串,如 `"15000000"`) */
18
+ minimum_deposit?: string;
19
+ /** 是否为 USD 类稳定币 */
20
+ is_usd_stable?: boolean;
17
21
  }
18
22
  export interface ChainData {
19
23
  chain_id: string;
@@ -33,12 +37,12 @@ export interface PlatformRegisterResponseData {
33
37
  chain_id: string;
34
38
  }
35
39
  export type QuoteDirection = "deposit" | "withdraw";
36
- export interface QuoteRequest {
40
+ interface QuoteRequest {
37
41
  direction: QuoteDirection;
38
42
  chain_id: string;
39
43
  token_address: string;
40
44
  token_amount?: string;
41
- usd1_amount?: string;
45
+ dst_token_amount?: string;
42
46
  }
43
47
  export interface QuoteResponseData {
44
48
  token_address: string;
@@ -47,7 +51,7 @@ export interface QuoteResponseData {
47
51
  rate: string;
48
52
  chain_id: number;
49
53
  deposit_address?: string;
50
- usd1_amount?: string;
54
+ dst_token_amount?: string;
51
55
  token_amount?: string;
52
56
  expires_at?: string;
53
57
  }
@@ -58,7 +62,7 @@ export interface DepositOrderResponseData {
58
62
  source_chain_id: string;
59
63
  token_address: string;
60
64
  token_amount?: string;
61
- usd1_amount?: string;
65
+ dst_token_amount?: string;
62
66
  deposit_address?: string;
63
67
  source_tx_hash?: string;
64
68
  created_at?: string;
@@ -70,7 +74,7 @@ export interface WithdrawOrderResponseData {
70
74
  status: WithdrawOrderStatus;
71
75
  one_time_address?: string;
72
76
  chain_id: string;
73
- usd1_amount: string;
77
+ dst_token_amount: string;
74
78
  /** Fee (e.g. "0.01 USD1"); only present if backend includes it in GET /api/v1/orders/withdraw/:id. UI falls back to feeDisplay prop or "—". */
75
79
  fee?: string;
76
80
  target_chain_id: string;
@@ -123,7 +127,7 @@ export interface CreateOrderResponseData {
123
127
  payment_sessions: PaymentSessionResponseData[];
124
128
  }
125
129
  /**
126
- * 配置 Merchant API 的 baseURL(可选,默认 https://merchant.tomo.services)
130
+ * 配置 Merchant API 的 baseURL
127
131
  */
128
132
  export declare function configureMerchantApi(): void;
129
133
  /**
@@ -142,3 +146,4 @@ export declare function getDepositOrder(orderId: string): Promise<DepositOrderRe
142
146
  export declare function getWithdrawOrder(orderId: string): Promise<WithdrawOrderResponseData>;
143
147
  /** POST /order - 创建 NATIVE_SWAP 提现订单 */
144
148
  export declare function createOrder(body: CreateOrderRequest): Promise<CreateOrderResponseData>;
149
+ export {};
@@ -1,13 +1,15 @@
1
1
  /**
2
- * Merchant API client (BaseUrl: https://merchant.tomo.services)
2
+ * Merchant API client
3
3
  * 基于 axios 封装,类型与文档一致。
4
4
  */
5
5
  import { getEnv } from "../utils/env.js";
6
6
  import axios from "axios";
7
- const DEFAULT_MERCHANT_BASE_URL = "https://merchant.tomo.services";
8
7
  // ─── Axios 实例与请求封装 ─────────────────────────────────────────────────────
9
8
  function createClient() {
10
- const BASE_URL = getEnv("MERCHANT_BASE_URL") || DEFAULT_MERCHANT_BASE_URL;
9
+ const BASE_URL = getEnv("MERCHANT_BASE_URL");
10
+ if (!BASE_URL) {
11
+ throw new Error('MERCHANT_BASE_URL is not set');
12
+ }
11
13
  return axios.create({
12
14
  baseURL: BASE_URL,
13
15
  timeout: 30000,
@@ -24,7 +26,7 @@ function unwrap(res) {
24
26
  // 单例 client,可通过 configureMerchantApi 修改 baseURL
25
27
  let apiClient = createClient();
26
28
  /**
27
- * 配置 Merchant API 的 baseURL(可选,默认 https://merchant.tomo.services)
29
+ * 配置 Merchant API 的 baseURL
28
30
  */
29
31
  export function configureMerchantApi() {
30
32
  apiClient = createClient();
@@ -8,13 +8,20 @@ export interface Erc20BalanceResult {
8
8
  * No external library dependency – uses the global `fetch`.
9
9
  */
10
10
  export declare function fetchErc20Balance(rpcUrl: string, tokenAddress: string, walletAddress: string): Promise<bigint>;
11
- export interface BscUsd1BalanceOptions {
11
+ export interface FundingTokenBalanceOptions {
12
12
  rpcUrl?: string;
13
13
  tokenAddress?: string;
14
14
  decimals?: number;
15
+ /**
16
+ * Funding chain id (e.g. `"3131"` Tenderly BSC, `"56"` mainnet).
17
+ * When omitted, defaults to `3131` via {@link getChainInfo}.
18
+ */
19
+ chainId?: string | number | null;
20
+ /** Label for {@link Erc20BalanceResult.symbol} (default `"Funding"`). */
21
+ displaySymbol?: string;
15
22
  }
16
23
  /**
17
- * Convenience wrapper fetches the USD1 balance on BSC for a given wallet.
18
- * All parameters have sensible defaults and can be overridden.
24
+ * Fetches the configured funding ERC-20 balance on the selected funding chain.
25
+ * Defaults: chain `3131`, RPC from {@link getChainInfo}, token from {@link getFundingTokenAddress} / env.
19
26
  */
20
- export declare function fetchBscUsd1Balance(walletAddress: string, options?: BscUsd1BalanceOptions): Promise<Erc20BalanceResult>;
27
+ export declare function fetchFundingTokenBalance(walletAddress: string, options?: FundingTokenBalanceOptions): Promise<Erc20BalanceResult>;
@@ -1,7 +1,6 @@
1
- import { testBsc, BSC_USD1_ADDRESS } from "../constants/chains.js";
1
+ import { getEnv } from "../utils/env";
2
+ import { getFundingTokenAddress, getChainInfo } from "../constants/chains.js";
2
3
  const ERC20_BALANCE_OF_SELECTOR = "0x70a08231";
3
- const USD1_DECIMALS = testBsc.nativeCurrencyDecimals;
4
- const BSC_RPC_URL = testBsc.rpcUrls[0];
5
4
  function padAddress(address) {
6
5
  return address.toLowerCase().replace("0x", "").padStart(64, "0");
7
6
  }
@@ -42,17 +41,20 @@ export async function fetchErc20Balance(rpcUrl, tokenAddress, walletAddress) {
42
41
  return BigInt(json.result ?? "0x0");
43
42
  }
44
43
  /**
45
- * Convenience wrapper fetches the USD1 balance on BSC for a given wallet.
46
- * All parameters have sensible defaults and can be overridden.
44
+ * Fetches the configured funding ERC-20 balance on the selected funding chain.
45
+ * Defaults: chain `3131`, RPC from {@link getChainInfo}, token from {@link getFundingTokenAddress} / env.
47
46
  */
48
- export async function fetchBscUsd1Balance(walletAddress, options) {
49
- const rpcUrl = options?.rpcUrl ?? BSC_RPC_URL;
50
- const tokenAddress = options?.tokenAddress ?? BSC_USD1_ADDRESS;
51
- const decimals = options?.decimals ?? USD1_DECIMALS;
47
+ export async function fetchFundingTokenBalance(walletAddress, options) {
48
+ const chain = getChainInfo(options?.chainId);
49
+ const rpcUrl = options?.rpcUrl ?? chain.rpcUrls[0];
50
+ const tokenAddress = options?.tokenAddress ?? getFundingTokenAddress(options?.chainId);
51
+ const decimals = options?.decimals ?? chain.nativeCurrencyDecimals;
52
+ const FUNDING_TOKEN_SYMBOL = getEnv("FUNDING_TOKEN_SYMBOL");
53
+ const displaySymbol = options?.displaySymbol ?? (FUNDING_TOKEN_SYMBOL || "Funding");
52
54
  const raw = await fetchErc20Balance(rpcUrl, tokenAddress, walletAddress);
53
55
  return {
54
56
  raw,
55
57
  formatted: formatBalanceDisplay(raw, decimals),
56
- symbol: "USD1",
58
+ symbol: displaySymbol,
57
59
  };
58
60
  }
@@ -3,6 +3,6 @@ import type { MarketDataProvider } from "../types.js";
3
3
  * Default `MarketDataProvider` backed by the merchant chains API.
4
4
  * - getSupportedTokens / getSupportedChains: from GET `{merchantBase}/chains`
5
5
  * - getQuote: local estimate until a dedicated quote API exists
6
- * - getDepositAddress: session wallet address + fixed minimum (no deposit-address HTTP API here)
6
+ * - getDepositAddress: 当前 session 钱包地址 + `token`/`chain` 匹配的 `GET /chains` `minimum_deposit`(按 decimals 格式化)
7
7
  */
8
8
  export declare function createMarketDataProvider(): MarketDataProvider;
@@ -1,45 +1,20 @@
1
1
  import { sessionStore } from "@ab-org/sdk-core";
2
- import { getEnv } from "../utils/env.js";
3
- const DEFAULT_MERCHANT_BASE_URL = "https://merchant.tomo.services";
2
+ import { formatUnits } from "viem";
3
+ import { getChains } from "./api.js";
4
4
  let cachedChains = null;
5
- /** Fallback when chains API fails (network error, 4xx/5xx, parse error, missing base URL). */
6
- const DEFAULT_CHAINS_FALLBACK = [
7
- {
8
- chain_id: "56",
9
- network: "BSC",
10
- tokens: [
11
- { symbol: "USD1", address: "0x", decimals: 18 },
12
- { symbol: "USDT", address: "0x", decimals: 6 },
13
- { symbol: "USDC", address: "0x", decimals: 6 },
14
- ],
15
- },
16
- {
17
- chain_id: "ETH",
18
- network: "Ethereum",
19
- tokens: [
20
- { symbol: "USDT", address: "0x", decimals: 6 },
21
- { symbol: "USDC", address: "0x", decimals: 6 },
22
- ],
23
- },
24
- ];
25
5
  async function fetchChainsFromApi() {
26
6
  if (cachedChains)
27
7
  return cachedChains;
28
8
  try {
29
- const CHAINS_API_BASE = getEnv("MERCHANT_BASE_URL") || DEFAULT_MERCHANT_BASE_URL;
30
- const res = await fetch(`${CHAINS_API_BASE}/chains`);
31
- if (!res.ok)
32
- throw new Error(`Chains API error: ${res.status} ${res.statusText}`);
33
- const json = (await res.json());
34
- const chains = json.data?.chains ?? [];
9
+ const { chains } = await getChains();
35
10
  if (chains.length > 0) {
36
11
  cachedChains = chains;
37
12
  return chains;
38
13
  }
39
- return DEFAULT_CHAINS_FALLBACK;
14
+ return [];
40
15
  }
41
16
  catch {
42
- return DEFAULT_CHAINS_FALLBACK;
17
+ return [];
43
18
  }
44
19
  }
45
20
  function chainToChainInfo(chain) {
@@ -48,6 +23,22 @@ function chainToChainInfo(chain) {
48
23
  name: chain.network,
49
24
  };
50
25
  }
26
+ function findTokenInChains(chains, chainId, tokenSymbol) {
27
+ const chain = chains.find((c) => c.chain_id === chainId);
28
+ return chain?.tokens.find((t) => t.symbol === tokenSymbol);
29
+ }
30
+ /** `minimum_deposit` 为链上最小单位整数字符串,按 `decimals` 转成可读金额并拼 token 符号 */
31
+ function formatMinimumDepositDisplay(token, tokenSymbol) {
32
+ const raw = token.minimum_deposit?.trim();
33
+ if (raw == null || raw === "")
34
+ return undefined;
35
+ try {
36
+ return `${formatUnits(BigInt(raw), token.decimals)} ${tokenSymbol}`;
37
+ }
38
+ catch {
39
+ return `${raw} ${tokenSymbol}`;
40
+ }
41
+ }
51
42
  function deriveTokensFromChains(chains) {
52
43
  const bySymbol = new Map();
53
44
  for (const chain of chains) {
@@ -84,7 +75,7 @@ function computeDefaultQuote(request) {
84
75
  * Default `MarketDataProvider` backed by the merchant chains API.
85
76
  * - getSupportedTokens / getSupportedChains: from GET `{merchantBase}/chains`
86
77
  * - getQuote: local estimate until a dedicated quote API exists
87
- * - getDepositAddress: session wallet address + fixed minimum (no deposit-address HTTP API here)
78
+ * - getDepositAddress: 当前 session 钱包地址 + `token`/`chain` 匹配的 `GET /chains` `minimum_deposit`(按 decimals 格式化)
88
79
  */
89
80
  export function createMarketDataProvider() {
90
81
  return {
@@ -102,11 +93,14 @@ export function createMarketDataProvider() {
102
93
  async getQuote(request) {
103
94
  return computeDefaultQuote(request);
104
95
  },
105
- async getDepositAddress(_token, _chain) {
96
+ async getDepositAddress(token, chain) {
106
97
  const session = sessionStore.getState().session;
98
+ const chains = await fetchChainsFromApi();
99
+ const meta = findTokenInChains(chains, chain, token);
100
+ const minimumDeposit = meta != null ? formatMinimumDepositDisplay(meta, token) : undefined;
107
101
  return {
108
102
  address: session?.address ?? "",
109
- minimumDeposit: "15.00",
103
+ minimumDeposit,
110
104
  };
111
105
  },
112
106
  };
@@ -0,0 +1,14 @@
1
+ import type { ChainData, TokenData } from "./api.js";
2
+ /**
3
+ * funding 链(与 {@link DEFAULT_FUNDING_CHAIN_ID} 一致)上,且 `tokenAddress` 在 Merchant `GET /chains`
4
+ * 中对应条目的 `is_usd_stable === true` 时,由业务层自行完成提现并构造 `withdrawDirectResult`;
5
+ * 其余情况走默认 funding 提现执行器与订单轮询。
6
+ */
7
+ export declare function isUsdtWithdrawDirect(chainId: string, tokenAddress: string, chains: ChainData[]): boolean;
8
+ /**
9
+ * 从 `getChains()` 返回的 `chains` 中,按当前选择的链 id + token symbol 或合约地址解析 `TokenData`。
10
+ */
11
+ export declare function findTokenDataFromChains(chains: ChainData[], chainId: string, opts: {
12
+ symbol: string;
13
+ tokenAddress?: string;
14
+ }): TokenData | undefined;
@@ -0,0 +1,33 @@
1
+ import { DEFAULT_FUNDING_CHAIN_ID } from "../constants/chains.js";
2
+ /**
3
+ * funding 链(与 {@link DEFAULT_FUNDING_CHAIN_ID} 一致)上,且 `tokenAddress` 在 Merchant `GET /chains`
4
+ * 中对应条目的 `is_usd_stable === true` 时,由业务层自行完成提现并构造 `withdrawDirectResult`;
5
+ * 其余情况走默认 funding 提现执行器与订单轮询。
6
+ */
7
+ export function isUsdtWithdrawDirect(chainId, tokenAddress, chains) {
8
+ if (chainId !== String(DEFAULT_FUNDING_CHAIN_ID))
9
+ return false;
10
+ const addr = tokenAddress.trim();
11
+ if (!addr)
12
+ return false;
13
+ const chain = chains.find((c) => c.chain_id === chainId);
14
+ const token = chain?.tokens.find((t) => t.address.toLowerCase() === addr.toLowerCase());
15
+ return token?.is_usd_stable === true;
16
+ }
17
+ /**
18
+ * 从 `getChains()` 返回的 `chains` 中,按当前选择的链 id + token symbol 或合约地址解析 `TokenData`。
19
+ */
20
+ export function findTokenDataFromChains(chains, chainId, opts) {
21
+ const chain = chains.find((c) => c.chain_id === chainId);
22
+ if (!chain?.tokens.length)
23
+ return undefined;
24
+ const sym = opts.symbol.trim();
25
+ const addr = opts.tokenAddress?.trim().toLowerCase();
26
+ return chain.tokens.find((t) => {
27
+ if (addr && t.address.toLowerCase() === addr)
28
+ return true;
29
+ if (sym.length > 0 && t.symbol === sym)
30
+ return true;
31
+ return false;
32
+ });
33
+ }
@@ -9,7 +9,7 @@ export interface WithdrawRequest {
9
9
  chain: string;
10
10
  }
11
11
  export interface WithdrawResult {
12
- /** BSC 上转入一次性地址的 funding tx hash */
12
+ /** Funding 链上转入一次性地址的 tx hash */
13
13
  txHash: string;
14
14
  /** 提现订单 ID,用于轮询 getWithdrawOrder(orderId) */
15
15
  orderId: string;
@@ -30,18 +30,27 @@ export type WithdrawExecutor = (request: WithdrawRequest) => Promise<WithdrawRes
30
30
  * "100.1" → 100100000000000000000n
31
31
  */
32
32
  export declare function parseUnits(value: string, decimals: number): bigint;
33
- export interface BscUsd1WithdrawExecutorOptions {
34
- /** 源代币 USD1 合约地址(提现场景下为 BSC 上的 USD1) */
33
+ export interface FundingWithdrawExecutorOptions {
34
+ /** 源链 funding ERC-20 合约地址;默认 {@link getFundingTokenAddress} / env */
35
35
  tokenAddress?: string;
36
36
  decimals?: number;
37
- chainId?: number;
37
+ /**
38
+ * Funding EVM chain id(如 `3131` Tenderly、`56` 主网)。未传时默认 `3131`。
39
+ */
40
+ chainId?: number | string;
41
+ /** 覆盖 {@link getChainInfo} 提供的 JSON-RPC URL */
42
+ rpcUrl?: string;
38
43
  /** 系统配置的单笔限额(wei 字符串),若提供则校验 request.amount 不得超过此值 */
39
44
  maxAmountWei?: string;
45
+ /**
46
+ * 与商户订单接口约定的 funding 侧 token symbol(默认 `USD1`,按后端协议)。
47
+ */
48
+ fundingLegTokenSymbol?: string;
40
49
  }
41
50
  /**
42
51
  * Factory that returns a `WithdrawExecutor` implementing the flow in withdraw.md:
43
52
  * 1) Create NATIVE_SWAP order → get one-time wallet address (OTW);
44
- * 2) Sign ERC-20 transfer of USD1 on BSC to the OTW (not to user);
53
+ * 2) Sign ERC-20 transfer of the funding token on the funding chain to the OTW (not to user);
45
54
  * 3) Broadcast tx and return { txHash, orderId } for the client to poll getWithdrawOrder(orderId).
46
55
  */
47
- export declare function createBscUsd1WithdrawExecutor(options?: BscUsd1WithdrawExecutorOptions): WithdrawExecutor;
56
+ export declare function createFundingWithdrawExecutor(options?: FundingWithdrawExecutorOptions): WithdrawExecutor;
@@ -2,12 +2,10 @@ import { sessionStore } from "@ab-org/sdk-core";
2
2
  import { tryAutoReconnect } from "../auth/autoReconnect.js";
3
3
  import { getChains, createOrder } from "./api.js";
4
4
  import { fromHex, parseGwei, toHex } from "viem";
5
- import { testBsc, BSC_USD1_ADDRESS } from "../constants/chains.js";
5
+ import { getFundingTokenAddress, getChainInfo } from "../constants/chains.js";
6
6
  const MAX_WITHDRAW_GAS_LIMIT = 500000n;
7
- const BSC_RPC_URL = testBsc.rpcUrls[0];
8
7
  /* ─── Internal helpers ────────────────────────── */
9
8
  const ERC20_TRANSFER_SELECTOR = "0xa9059cbb";
10
- const USD1_DECIMALS = testBsc.nativeCurrencyDecimals;
11
9
  function padHex256(value) {
12
10
  return value.toString(16).padStart(64, "0");
13
11
  }
@@ -82,13 +80,13 @@ async function requestHexQuantity(provider, rpcUrl, method, params) {
82
80
  return toHexQuantity(await callRpc(rpcUrl, method, params));
83
81
  }
84
82
  }
85
- async function ensureBscChain(provider, chainId) {
83
+ async function ensureFundingEvmChain(provider, chainId) {
86
84
  const hex = `0x${chainId.toString(16)}`;
87
85
  try {
88
86
  await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hex }] });
89
87
  }
90
88
  catch {
91
- // Ignored – the wallet may already be on BSC or may not support switching.
89
+ // Ignored – the wallet may already be on the funding chain or may not support switching.
92
90
  }
93
91
  }
94
92
  function getDstTokenAddress(chains, chainId, tokenSymbol) {
@@ -98,14 +96,17 @@ function getDstTokenAddress(chains, chainId, tokenSymbol) {
98
96
  /**
99
97
  * Factory that returns a `WithdrawExecutor` implementing the flow in withdraw.md:
100
98
  * 1) Create NATIVE_SWAP order → get one-time wallet address (OTW);
101
- * 2) Sign ERC-20 transfer of USD1 on BSC to the OTW (not to user);
99
+ * 2) Sign ERC-20 transfer of the funding token on the funding chain to the OTW (not to user);
102
100
  * 3) Broadcast tx and return { txHash, orderId } for the client to poll getWithdrawOrder(orderId).
103
101
  */
104
- export function createBscUsd1WithdrawExecutor(options) {
105
- const tokenAddress = options?.tokenAddress ?? BSC_USD1_ADDRESS;
106
- const decimals = options?.decimals ?? USD1_DECIMALS;
102
+ export function createFundingWithdrawExecutor(options) {
103
+ const fundingChain = getChainInfo(options?.chainId);
104
+ const chainIdNum = Number(fundingChain.chainId);
105
+ const rpcUrl = options?.rpcUrl ?? fundingChain.rpcUrls[0];
106
+ const tokenAddress = options?.tokenAddress ?? getFundingTokenAddress(options?.chainId);
107
+ const decimals = options?.decimals ?? fundingChain.nativeCurrencyDecimals;
107
108
  const maxAmountWei = options?.maxAmountWei;
108
- const chainId = options?.chainId ?? Number(testBsc.chainId);
109
+ const fundingLegTokenSymbol = options?.fundingLegTokenSymbol ?? "USD1";
109
110
  return async (request) => {
110
111
  const amountWei = parseUnits(request.amount, decimals);
111
112
  const amountWeiStr = amountWei.toString();
@@ -139,13 +140,13 @@ export function createBscUsd1WithdrawExecutor(options) {
139
140
  if (!dstTokenAddress) {
140
141
  throw new Error(`Unsupported token ${request.token} on chain ${request.chain}`);
141
142
  }
142
- // 校验规则:源代币或目标代币中必须有一个是 USD1。提现场景下源代币(token_address)为 USD1;token_amount 已按 maxAmountWei 做单笔限额校验
143
- const sourceTokenSymbol = "USD1";
143
+ // 校验规则:源/目标代币需满足商户约定;提现场景下源链为 funding token;token_amount 已按 maxAmountWei 做单笔限额校验
144
+ const sourceTokenSymbol = fundingLegTokenSymbol;
144
145
  const orderRes = await createOrder({
145
146
  intent_id: `withdraw-${Date.now()}`,
146
147
  order_type: "NATIVE_SWAP",
147
148
  order_payload: {
148
- chain_id: String(chainId),
149
+ chain_id: fundingChain.chainId,
149
150
  token_address: tokenAddress,
150
151
  token_amount: amountWeiStr,
151
152
  dst_chain_id: request.chain,
@@ -158,7 +159,7 @@ export function createBscUsd1WithdrawExecutor(options) {
158
159
  token_amount: amountWeiStr,
159
160
  token_address: tokenAddress,
160
161
  user_address: session.address,
161
- chain_id: String(chainId),
162
+ chain_id: fundingChain.chainId,
162
163
  },
163
164
  ],
164
165
  });
@@ -166,18 +167,18 @@ export function createBscUsd1WithdrawExecutor(options) {
166
167
  if (!oneTimeAddress) {
167
168
  throw new Error("Order created but no one-time wallet address returned");
168
169
  }
169
- await ensureBscChain(provider, chainId);
170
+ await ensureFundingEvmChain(provider, chainIdNum);
170
171
  const data = encodeTransferData(oneTimeAddress, amountWei);
171
- const nonce = await requestHexQuantity(provider, BSC_RPC_URL, "eth_getTransactionCount", [session.address, "latest"]);
172
+ const nonce = await requestHexQuantity(provider, rpcUrl, "eth_getTransactionCount", [session.address, "latest"]);
172
173
  const tx = {
173
174
  from: session.address,
174
175
  to: tokenAddress,
175
176
  value: "0x0",
176
177
  nonce,
177
178
  data,
178
- chainId: toHex(chainId),
179
+ chainId: toHex(chainIdNum),
179
180
  };
180
- const estimatedGasHex = await requestHexQuantity(provider, BSC_RPC_URL, "eth_estimateGas", [tx]);
181
+ const estimatedGasHex = await requestHexQuantity(provider, rpcUrl, "eth_estimateGas", [tx]);
181
182
  const estimatedGas = fromHex(estimatedGasHex, "bigint");
182
183
  const gas = toHex(estimatedGas > MAX_WITHDRAW_GAS_LIMIT ? MAX_WITHDRAW_GAS_LIMIT : estimatedGas);
183
184
  const transaction = {
@@ -201,8 +202,8 @@ export function createBscUsd1WithdrawExecutor(options) {
201
202
  method: "eth_signTransaction",
202
203
  params: [transaction],
203
204
  });
204
- txHash = await callRpc(BSC_RPC_URL, "eth_sendRawTransaction", [signedTx]);
205
+ txHash = await callRpc(rpcUrl, "eth_sendRawTransaction", [signedTx]);
205
206
  }
206
- return { txHash, orderId: orderRes.order_id, fundingChainId: String(chainId) };
207
+ return { txHash, orderId: orderRes.order_id, fundingChainId: fundingChain.chainId };
207
208
  };
208
209
  }
@@ -9,6 +9,7 @@ import { LoginRequiredOverlay } from "./components/LoginRequiredOverlay.js";
9
9
  import { useSession } from "./hooks/useSession.js";
10
10
  import { colors, fonts, radii } from "./theme.js";
11
11
  import { getChains, quote, } from "../modules/api.js";
12
+ import { getEnv } from "../utils/env";
12
13
  /** 校验是否为合法的充值地址(传入值):非空且长度满足常见链地址格式 */
13
14
  function isValidDepositAddress(v) {
14
15
  return typeof v === "string" && v.trim().length >= 20;
@@ -43,8 +44,6 @@ const DefaultCryptoIcons = () => {
43
44
  }, children: t.label }, i))) }));
44
45
  };
45
46
  /* ─── Helpers: derive options from getChains ─── */
46
- /** Deposit 时不可选 USD1(充值是转入 USD1,源代币为 USDT/USDC 等) */
47
- const DEPOSIT_EXCLUDED_TOKEN = "USD1";
48
47
  function chainsToTokenOptions(chains) {
49
48
  const bySymbol = new Map();
50
49
  for (const c of chains) {
@@ -54,7 +53,6 @@ function chainsToTokenOptions(chains) {
54
53
  }
55
54
  }
56
55
  return Array.from(bySymbol.entries())
57
- .filter(([id]) => id !== DEPOSIT_EXCLUDED_TOKEN)
58
56
  .map(([id, { symbol }]) => ({
59
57
  id,
60
58
  label: symbol,
@@ -88,6 +86,8 @@ function getTokenAddressForChain(chains, chainId, tokenSymbol) {
88
86
  const chain = chains.find((c) => c.chain_id === chainId);
89
87
  return chain?.tokens.find((t) => t.symbol === tokenSymbol)?.address;
90
88
  }
89
+ /* ─── Main Component ─────────────────────────── */
90
+ const FUNDING_TOKEN_SYMBOL = getEnv("FUNDING_TOKEN_SYMBOL");
91
91
  export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, chainOptions: chainOptionsProp, depositAddress, minimumDeposit, qrCenterIcon, cryptoIcons, depositAmount, onShowToast, txHash, explorerTxUrl, onTokenSelect, onChainSelect, onCopyAddress, onBuyCrypto, onSignIn, onBack, onClose, }) => {
92
92
  const session = useSession();
93
93
  const [view, setView] = useState("entry");
@@ -98,9 +98,8 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
98
98
  const [loadingQuote, setLoadingQuote] = useState(false);
99
99
  const [quoteRefreshKey, setQuoteRefreshKey] = useState(0);
100
100
  const tokenOptions = useMemo(() => {
101
- const excludeUsd1 = (opts) => opts.filter((o) => o.id !== DEPOSIT_EXCLUDED_TOKEN && o.label !== DEPOSIT_EXCLUDED_TOKEN);
102
101
  if (tokenOptionsProp?.length)
103
- return excludeUsd1(tokenOptionsProp);
102
+ return tokenOptionsProp;
104
103
  if (!apiChains?.length)
105
104
  return undefined;
106
105
  return chainsToTokenOptions(apiChains);
@@ -112,12 +111,6 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
112
111
  return undefined;
113
112
  return chainsToChainOptionsForToken(apiChains, token);
114
113
  }, [chainOptionsProp, apiChains, token]);
115
- // 当前选中的是 USD1 时自动切到第一个可选 token(deposit 不允许选 USD1)
116
- useEffect(() => {
117
- if (token !== DEPOSIT_EXCLUDED_TOKEN || !tokenOptions?.length || !onTokenSelect)
118
- return;
119
- onTokenSelect(tokenOptions[0].id);
120
- }, [token, tokenOptions, onTokenSelect]);
121
114
  // 仅有一个 chain 选项时默认选中
122
115
  useEffect(() => {
123
116
  if (chainOptions?.length !== 1 || !onChainSelect)
@@ -166,7 +159,7 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
166
159
  rate: "1",
167
160
  chain_id: Number(chain) || 56,
168
161
  deposit_address: "0x" + "0".repeat(39) + "1",
169
- usd1_amount: depositAmount ?? "0",
162
+ dst_token_amount: depositAmount ?? "0",
170
163
  expires_at: new Date(Date.now() + 60000).toISOString(),
171
164
  });
172
165
  })
@@ -227,7 +220,7 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
227
220
  rate: "1",
228
221
  chain_id: Number(chain) || 56,
229
222
  deposit_address: "0x" + "0".repeat(39) + "1",
230
- usd1_amount: depositAmount ?? "0",
223
+ dst_token_amount: depositAmount ?? "0",
231
224
  expires_at: new Date(Date.now() + 60000).toISOString(),
232
225
  });
233
226
  })
@@ -302,7 +295,7 @@ const TransferView = ({ token, chain, tokenOptions, chainOptions, depositAddress
302
295
  display: "flex",
303
296
  flexDirection: "column",
304
297
  gap: 8,
305
- }, children: quoteLoading ? (_jsx("span", { style: { fontSize: 13, color: colors.textSecondary }, children: "Loading\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: { fontSize: 13, color: colors.textSecondary }, children: ["1 ", quoteData.token_symbol, " = ", quoteData.rate, " USD1"] }), quoteData.expires_at && (_jsx(Countdown, { expiresAt: quoteData.expires_at, isExpired: quoteExpired, onExpired: onQuoteExpired })), quoteExpired && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: { fontSize: 13, color: "#f59e0b" }, children: "Quote expired, please refresh" }), onRefreshQuote && (_jsx("button", { type: "button", onClick: onRefreshQuote, style: {
298
+ }, children: quoteLoading ? (_jsx("span", { style: { fontSize: 13, color: colors.textSecondary }, children: "Loading\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: { fontSize: 13, color: colors.textSecondary }, children: ["1 ", quoteData.token_symbol, " = ", quoteData.rate, " ", FUNDING_TOKEN_SYMBOL] }), quoteData.expires_at && (_jsx(Countdown, { expiresAt: quoteData.expires_at, isExpired: quoteExpired, onExpired: onQuoteExpired })), quoteExpired && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: { fontSize: 13, color: "#f59e0b" }, children: "Quote expired, please refresh" }), onRefreshQuote && (_jsx("button", { type: "button", onClick: onRefreshQuote, style: {
306
299
  padding: "4px 12px",
307
300
  fontSize: 12,
308
301
  borderRadius: radii.pill,
@@ -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、所选 chain),由调用方执行提现 */
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, explorerTxUrl, onShowToast, onAddressChange, onTokenSelect, onChainSelect, onAmountChange, onMaxClick, onSubmit, onWithdrawCompleted, onStartAnotherWithdrawal, onSignIn, onClose, }: WithdrawModalProps) => import("react/jsx-runtime.js").JSX.Element;
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;
@@ -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 USD1" → 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 USD1" → "123.46 USD1") */
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 = "USDT", chain, amount = "", balance, status = "idle", receiveAmount: receiveAmountProp, txHash, tokenOptions: tokenOptionsProp, chainOptions: chainOptionsProp, useMerchantApi = false, orderId, feeDisplay, fundingChainId, explorerTxUrl, onShowToast, onAddressChange, onTokenSelect, onChainSelect, onAmountChange, onMaxClick, onSubmit, onWithdrawCompleted, onStartAnotherWithdrawal, onSignIn, onClose, }) => {
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.filter((o) => o.id !== "USD1" && o.label !== "USD1");
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
- const trackingWithdraw = useMemo(() => Boolean(orderId), [orderId]);
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 USD1 获取 rate
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
- usd1_amount: amountWei,
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
- usd1_amount: "0",
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
- if (!orderId) {
194
- setWithdrawOrder(null);
195
- return;
196
- }
197
- const t = setInterval(() => {
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((order) => {
200
- setWithdrawOrder(order);
201
- if (order.status === "completed" && onShowToast) {
202
- onShowToast("Withdrawal completed");
203
- }
204
- })
219
+ .then(setWithdrawOrder)
205
220
  .catch(() => setWithdrawOrder(mockWithdrawOrderState));
206
- }, POLL_INTERVAL_MS);
207
- getWithdrawOrder(orderId)
208
- .then(setWithdrawOrder)
209
- .catch(() => setWithdrawOrder(mockWithdrawOrderState));
210
- return () => clearInterval(t);
211
- }, [orderId, onShowToast, mockWithdrawOrderState]);
212
- const completedOrderIdRef = useRef(null);
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 === "completed" && orderId && completedOrderIdRef.current !== orderId) {
215
- completedOrderIdRef.current = orderId;
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 (!orderId)
219
- completedOrderIdRef.current = null;
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
- usd1_amount: amountWei,
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.usd1_amount), " ", tokenSymbol ?? "USD1"] })] }), _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: {
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: () => (onStartAnotherWithdrawal ?? onClose)?.(), style: {
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 USD1 = ", 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: {
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
- } }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: {
467
- fontSize: 14,
468
- lineHeight: 1.4,
469
- color: colors.textSecondary,
470
- fontFamily: fonts.family,
471
- }, children: "USD1" }), _jsx("button", { type: "button", onClick: onMaxClick, style: {
472
- background: colors.textPrimary,
473
- border: "none",
474
- borderRadius: radii.pill,
475
- padding: "2px 8px",
476
- fontSize: 12,
477
- lineHeight: 1.4,
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
  };
@@ -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;
@@ -3,7 +3,7 @@ import { getSDKConfig } from "../auth/config.js";
3
3
  import { createOidcRelayAuth } from "../auth/oidcRelay.js";
4
4
  import WalletAccount from "../auth/walletAccount.js";
5
5
  import { getEnv } from "../utils/env.js";
6
- import { describeSessionCapabilityPolicy, WalletConnector, createDefaultInjectedWalletRegistry, } from "@ab-org/sdk-core";
6
+ import { WalletConnector, createDefaultInjectedWalletRegistry, } from "@ab-org/sdk-core";
7
7
  import { resolveWalletItems } from "./SignInModal.shared.js";
8
8
  function resolveOidcStage() {
9
9
  return getEnv("STAGE").toLowerCase() === "prod" ? "prod" : "dev";
@@ -13,7 +13,6 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
13
13
  const [loadingProvider, setLoadingProvider] = useState(null);
14
14
  const [loadingWalletId, setLoadingWalletId] = useState(null);
15
15
  const defaultWalletRegistry = useMemo(() => createDefaultInjectedWalletRegistry(), []);
16
- const sdkConfig = getSDKConfig();
17
16
  const defaultWalletConnector = useMemo(() => new WalletConnector([
18
17
  ...defaultWalletRegistry.map((item) => item.provider),
19
18
  ]), [defaultWalletRegistry]);
@@ -21,30 +20,6 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
21
20
  const showExpandToggle = resolvedWallets.length > initialVisibleCount;
22
21
  const visibleWallets = expanded ? resolvedWallets : resolvedWallets.slice(0, initialVisibleCount);
23
22
  const isBusy = !!loadingProvider || !!loadingWalletId;
24
- const confirmCapabilitySession = async (providerId, cubeSignerSession) => {
25
- const confirmation = sdkConfig.signIn?.sessionConfirmation;
26
- const policy = cubeSignerSession && sdkConfig.cubeSigner
27
- ? {
28
- id: "preview",
29
- ...(sdkConfig.signIn?.sessionPolicy ?? sdkConfig.cubeSigner.defaultSessionPolicy),
30
- }
31
- : undefined;
32
- if (confirmation?.enabled === false || !policy) {
33
- return true;
34
- }
35
- if (typeof window === "undefined" || typeof window.confirm !== "function") {
36
- return true;
37
- }
38
- const lines = describeSessionCapabilityPolicy(policy);
39
- const message = [
40
- confirmation?.title ?? "Authorize smart-wallet session",
41
- confirmation?.message ?? `Continue with ${providerId} and create a capability session?`,
42
- ...lines,
43
- ]
44
- .filter(Boolean)
45
- .join("\n");
46
- return window.confirm(message);
47
- };
48
23
  const handleSocialClick = async (providerId) => {
49
24
  setLoadingProvider(providerId);
50
25
  try {
@@ -67,11 +42,6 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
67
42
  await WalletAccount.getInstance(oidcToken, authSource, config.cubeSigner);
68
43
  const session = WalletAccount.getWalletSession();
69
44
  const cubeSignerSession = WalletAccount.getCubeSignerSession();
70
- const confirmed = await confirmCapabilitySession(providerId, cubeSignerSession);
71
- if (!confirmed) {
72
- WalletAccount.clearInstance();
73
- return null;
74
- }
75
45
  onGoogleLogin?.({ idToken: oidcToken });
76
46
  if (cubeSignerSession)
77
47
  onCubeSignerSession?.(cubeSignerSession);
@@ -91,11 +61,6 @@ export const useSignInModalController = ({ wallets, initialVisibleCount, onGoogl
91
61
  await WalletAccount.getInstance(oidcToken, authSource, config.cubeSigner);
92
62
  const session = WalletAccount.getWalletSession();
93
63
  const cubeSignerSession = WalletAccount.getCubeSignerSession();
94
- const confirmed = await confirmCapabilitySession(providerId, cubeSignerSession);
95
- if (!confirmed) {
96
- WalletAccount.clearInstance();
97
- return null;
98
- }
99
64
  onTwitterLogin?.({ code: "", codeVerifier: "", state: "" });
100
65
  if (cubeSignerSession)
101
66
  onCubeSignerSession?.(cubeSignerSession);
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
  }
@@ -1,3 +1,3 @@
1
1
  export { getEnv } from "./utils/env.js";
2
2
  export { getExplorerUrl } from "./utils/explorer.js";
3
- export { testBsc, clientIds as ClientIds, BSC_USD1_ADDRESS, BSC_USD1_PLATFORM_CONTRACT_ADDRESS, } from "./constants/chains.js";
3
+ export { ClientIds, DEFAULT_FUNDING_TOKEN_ADDRESS, DEFAULT_FUNDING_CHAIN_ID, getFundingTokenAddress, getChainInfo, type EvmChainInfo, } from "./constants/chains.js";
@@ -1,3 +1,3 @@
1
1
  export { getEnv } from "./utils/env.js";
2
2
  export { getExplorerUrl } from "./utils/explorer.js";
3
- export { testBsc, clientIds as ClientIds, BSC_USD1_ADDRESS, BSC_USD1_PLATFORM_CONTRACT_ADDRESS, } from "./constants/chains.js";
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.2",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*",
@@ -23,6 +23,7 @@
23
23
  "@cubist-labs/cubesigner-sdk": "^0.4.219",
24
24
  "axios": "^1.13.6",
25
25
  "qrcode-generator": "^2.0.4",
26
+ "@ab-org/wallet-utils": "0.0.7",
26
27
  "@ab-org/sdk-core": "0.0.1"
27
28
  },
28
29
  "peerDependencies": {