@ab-org/predicate-market-sdk 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +706 -22
- package/dist/index.js +43009 -23
- package/package.json +11 -5
- package/dist/auth/autoReconnect.d.ts +0 -11
- package/dist/auth/autoReconnect.js +0 -36
- package/dist/auth/bundledConfig.d.ts +0 -2
- package/dist/auth/bundledConfig.js +0 -19
- package/dist/auth/config.d.ts +0 -29
- package/dist/auth/config.js +0 -53
- package/dist/auth/google.d.ts +0 -43
- package/dist/auth/google.js +0 -147
- package/dist/auth/oidcRelay.d.ts +0 -11
- package/dist/auth/oidcRelay.js +0 -107
- package/dist/auth/twitter.d.ts +0 -7
- package/dist/auth/twitter.js +0 -94
- package/dist/auth/walletAccount.d.ts +0 -20
- package/dist/auth/walletAccount.js +0 -267
- package/dist/constants/chains.d.ts +0 -2
- package/dist/constants/chains.js +0 -3
- package/dist/modules/api.d.ts +0 -149
- package/dist/modules/api.js +0 -95
- package/dist/modules/balanceQuery.d.ts +0 -27
- package/dist/modules/balanceQuery.js +0 -60
- package/dist/modules/deposit.d.ts +0 -31
- package/dist/modules/deposit.js +0 -58
- package/dist/modules/marketData.d.ts +0 -8
- package/dist/modules/marketData.js +0 -107
- package/dist/modules/withdraw.d.ts +0 -31
- package/dist/modules/withdraw.js +0 -61
- package/dist/modules/withdrawDirect.d.ts +0 -14
- package/dist/modules/withdrawDirect.js +0 -33
- package/dist/modules/withdrawExecutor.d.ts +0 -56
- package/dist/modules/withdrawExecutor.js +0 -210
- package/dist/policyAdapter.d.ts +0 -11
- package/dist/policyAdapter.js +0 -38
- package/dist/types.d.ts +0 -62
- package/dist/types.js +0 -1
- package/dist/ui/DepositModal.d.ts +0 -36
- package/dist/ui/DepositModal.js +0 -354
- package/dist/ui/SignInModal.d.ts +0 -22
- package/dist/ui/SignInModal.js +0 -77
- package/dist/ui/SignInModal.sections.d.ts +0 -33
- package/dist/ui/SignInModal.sections.js +0 -45
- package/dist/ui/SignInModal.shared.d.ts +0 -15
- package/dist/ui/SignInModal.shared.js +0 -126
- package/dist/ui/WalletSelectionModal.d.ts +0 -14
- package/dist/ui/WalletSelectionModal.js +0 -54
- package/dist/ui/WithdrawModal.d.ts +0 -57
- package/dist/ui/WithdrawModal.js +0 -574
- package/dist/ui/components/CloseButton.d.ts +0 -4
- package/dist/ui/components/CloseButton.js +0 -15
- package/dist/ui/components/Countdown.d.ts +0 -16
- package/dist/ui/components/Countdown.js +0 -42
- package/dist/ui/components/DepositDetailsPanel.d.ts +0 -8
- package/dist/ui/components/DepositDetailsPanel.js +0 -143
- package/dist/ui/components/DropdownField.d.ts +0 -19
- package/dist/ui/components/DropdownField.js +0 -81
- package/dist/ui/components/Field.d.ts +0 -10
- package/dist/ui/components/Field.js +0 -21
- package/dist/ui/components/LoginRequiredOverlay.d.ts +0 -6
- package/dist/ui/components/LoginRequiredOverlay.js +0 -31
- package/dist/ui/components/ModalCard.d.ts +0 -9
- package/dist/ui/components/ModalCard.js +0 -14
- package/dist/ui/components/ModalFrame.d.ts +0 -9
- package/dist/ui/components/ModalFrame.js +0 -18
- package/dist/ui/components/PrimaryButton.d.ts +0 -2
- package/dist/ui/components/PrimaryButton.js +0 -14
- package/dist/ui/components/QRCodePanel.d.ts +0 -4
- package/dist/ui/components/QRCodePanel.js +0 -43
- package/dist/ui/components/Select.d.ts +0 -12
- package/dist/ui/components/Select.js +0 -29
- package/dist/ui/components/StepIndicator.d.ts +0 -7
- package/dist/ui/components/StepIndicator.js +0 -35
- package/dist/ui/components/Success.d.ts +0 -1
- package/dist/ui/components/Success.js +0 -4
- package/dist/ui/components/Toast.d.ts +0 -8
- package/dist/ui/components/Toast.js +0 -51
- package/dist/ui/hooks/useSession.d.ts +0 -2
- package/dist/ui/hooks/useSession.js +0 -10
- package/dist/ui/signInTypes.d.ts +0 -28
- package/dist/ui/signInTypes.js +0 -1
- package/dist/ui/theme.d.ts +0 -31
- package/dist/ui/theme.js +0 -31
- package/dist/ui/useSignInModalController.d.ts +0 -25
- package/dist/ui/useSignInModalController.js +0 -119
- package/dist/utils/env.d.ts +0 -1
- package/dist/utils/env.js +0 -63
- package/dist/utils/explorer.d.ts +0 -3
- package/dist/utils/explorer.js +0 -47
- package/dist/walletUtils.d.ts +0 -3
- package/dist/walletUtils.js +0 -3
package/dist/modules/deposit.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
-
import { getEnv } from "../utils/env";
|
|
3
|
-
function requireSession() {
|
|
4
|
-
const session = sessionStore.getState().session;
|
|
5
|
-
if (!session)
|
|
6
|
-
throw new Error("Login required");
|
|
7
|
-
return session;
|
|
8
|
-
}
|
|
9
|
-
export const createDepositController = (custody, marketData) => {
|
|
10
|
-
let status = { phase: "idle" };
|
|
11
|
-
const notify = (next, cb) => {
|
|
12
|
-
status = next;
|
|
13
|
-
cb?.(status);
|
|
14
|
-
};
|
|
15
|
-
const open = async (config) => {
|
|
16
|
-
const session = requireSession();
|
|
17
|
-
const chain = config?.preferredChain ??
|
|
18
|
-
session.chainContext?.settlementChain ??
|
|
19
|
-
session.chain ??
|
|
20
|
-
"AB_CORE";
|
|
21
|
-
const token = config?.preferredToken || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
22
|
-
try {
|
|
23
|
-
const { address } = await marketData.getDepositAddress(token, chain);
|
|
24
|
-
const depositId = `${chain}:${token}:${Date.now()}`;
|
|
25
|
-
notify({ phase: "address-issued", depositId, address }, config?.onStatusChange);
|
|
26
|
-
notify({ phase: "confirming", depositId }, config?.onStatusChange);
|
|
27
|
-
const { status: finalStatus, txHash } = await custody.getDepositStatus(depositId);
|
|
28
|
-
notify(finalStatus === "SETTLED"
|
|
29
|
-
? { phase: "settled", depositId, txHash }
|
|
30
|
-
: { phase: "failed", reason: finalStatus }, config?.onStatusChange);
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
notify({ phase: "failed", reason: error.message }, config?.onStatusChange);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
return {
|
|
37
|
-
get status() {
|
|
38
|
-
return status;
|
|
39
|
-
},
|
|
40
|
-
open,
|
|
41
|
-
fetchTokens() {
|
|
42
|
-
requireSession();
|
|
43
|
-
return marketData.getSupportedTokens("deposit");
|
|
44
|
-
},
|
|
45
|
-
fetchChains(token) {
|
|
46
|
-
requireSession();
|
|
47
|
-
return marketData.getSupportedChains(token, "deposit");
|
|
48
|
-
},
|
|
49
|
-
fetchQuote(token, chain, amount) {
|
|
50
|
-
requireSession();
|
|
51
|
-
return marketData.getQuote({ token, chain, amount, direction: "deposit" });
|
|
52
|
-
},
|
|
53
|
-
fetchDepositAddress(token, chain) {
|
|
54
|
-
requireSession();
|
|
55
|
-
return marketData.getDepositAddress(token, chain);
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { MarketDataProvider } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Default `MarketDataProvider` backed by the merchant chains API.
|
|
4
|
-
* - getSupportedTokens / getSupportedChains: from GET `{merchantBase}/chains`
|
|
5
|
-
* - getQuote: local estimate until a dedicated quote API exists
|
|
6
|
-
* - getDepositAddress: 当前 session 钱包地址 + 与 `token`/`chain` 匹配的 `GET /chains` 中 `minimum_deposit`(按 decimals 格式化)
|
|
7
|
-
*/
|
|
8
|
-
export declare function createMarketDataProvider(): MarketDataProvider;
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
-
import { formatUnits } from "viem";
|
|
3
|
-
import { getChains } from "./api.js";
|
|
4
|
-
let cachedChains = null;
|
|
5
|
-
async function fetchChainsFromApi() {
|
|
6
|
-
if (cachedChains)
|
|
7
|
-
return cachedChains;
|
|
8
|
-
try {
|
|
9
|
-
const { chains } = await getChains();
|
|
10
|
-
if (chains.length > 0) {
|
|
11
|
-
cachedChains = chains;
|
|
12
|
-
return chains;
|
|
13
|
-
}
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return [];
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function chainToChainInfo(chain) {
|
|
21
|
-
return {
|
|
22
|
-
id: chain.chain_id,
|
|
23
|
-
name: chain.network,
|
|
24
|
-
};
|
|
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
|
-
}
|
|
42
|
-
function deriveTokensFromChains(chains) {
|
|
43
|
-
const bySymbol = new Map();
|
|
44
|
-
for (const chain of chains) {
|
|
45
|
-
for (const t of chain.tokens) {
|
|
46
|
-
if (!bySymbol.has(t.symbol)) {
|
|
47
|
-
bySymbol.set(t.symbol, {
|
|
48
|
-
symbol: t.symbol,
|
|
49
|
-
name: t.symbol,
|
|
50
|
-
decimals: t.decimals,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return Array.from(bySymbol.values());
|
|
56
|
-
}
|
|
57
|
-
function computeDefaultQuote(request) {
|
|
58
|
-
const isStable = ["USDT", "USDC", "USD1"].includes(request.token);
|
|
59
|
-
const slippage = isStable ? "0.3" : "1.0";
|
|
60
|
-
const feeRate = request.direction === "withdraw" ? 0.001 : 0;
|
|
61
|
-
const amount = Number(request.amount) || 0;
|
|
62
|
-
const fee = (amount * feeRate).toFixed(2);
|
|
63
|
-
const estimatedAmount = (amount - Number(fee)).toFixed(6);
|
|
64
|
-
return {
|
|
65
|
-
quoteId: `quote-${Date.now()}`,
|
|
66
|
-
estimatedAmount: amount > 0 ? estimatedAmount : "0",
|
|
67
|
-
slippage,
|
|
68
|
-
fee,
|
|
69
|
-
feeToken: request.token,
|
|
70
|
-
exchangeRate: "1.0",
|
|
71
|
-
expiresAt: Date.now() + 30000,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Default `MarketDataProvider` backed by the merchant chains API.
|
|
76
|
-
* - getSupportedTokens / getSupportedChains: from GET `{merchantBase}/chains`
|
|
77
|
-
* - getQuote: local estimate until a dedicated quote API exists
|
|
78
|
-
* - getDepositAddress: 当前 session 钱包地址 + 与 `token`/`chain` 匹配的 `GET /chains` 中 `minimum_deposit`(按 decimals 格式化)
|
|
79
|
-
*/
|
|
80
|
-
export function createMarketDataProvider() {
|
|
81
|
-
return {
|
|
82
|
-
async getSupportedTokens(_direction) {
|
|
83
|
-
const chains = await fetchChainsFromApi();
|
|
84
|
-
const tokens = deriveTokensFromChains(chains);
|
|
85
|
-
return tokens.length > 0 ? tokens : [];
|
|
86
|
-
},
|
|
87
|
-
async getSupportedChains(token, _direction) {
|
|
88
|
-
const chains = await fetchChainsFromApi();
|
|
89
|
-
const forToken = chains.filter((c) => c.tokens.some((t) => t.symbol === token));
|
|
90
|
-
const list = forToken.length > 0 ? forToken : chains;
|
|
91
|
-
return list.map(chainToChainInfo);
|
|
92
|
-
},
|
|
93
|
-
async getQuote(request) {
|
|
94
|
-
return computeDefaultQuote(request);
|
|
95
|
-
},
|
|
96
|
-
async getDepositAddress(token, chain) {
|
|
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;
|
|
101
|
-
return {
|
|
102
|
-
address: session?.address ?? "",
|
|
103
|
-
minimumDeposit,
|
|
104
|
-
};
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ChainInfo, CustodyAdapter, MarketDataProvider, ModalController, QuoteResult, TokenInfo } from "../types.js";
|
|
2
|
-
export interface WithdrawModalConfig {
|
|
3
|
-
defaultAmount?: string;
|
|
4
|
-
defaultToken?: string;
|
|
5
|
-
defaultChain?: string;
|
|
6
|
-
targetAddress?: string;
|
|
7
|
-
onStatusChange?: (status: WithdrawStatus) => void;
|
|
8
|
-
}
|
|
9
|
-
export type WithdrawStatus = {
|
|
10
|
-
phase: "idle";
|
|
11
|
-
} | {
|
|
12
|
-
phase: "requested";
|
|
13
|
-
requestId: string;
|
|
14
|
-
} | {
|
|
15
|
-
phase: "processing";
|
|
16
|
-
requestId: string;
|
|
17
|
-
} | {
|
|
18
|
-
phase: "settled";
|
|
19
|
-
requestId: string;
|
|
20
|
-
txHash?: string;
|
|
21
|
-
} | {
|
|
22
|
-
phase: "failed";
|
|
23
|
-
reason: string;
|
|
24
|
-
};
|
|
25
|
-
export interface WithdrawController extends ModalController<WithdrawModalConfig> {
|
|
26
|
-
readonly status: WithdrawStatus;
|
|
27
|
-
fetchTokens(): Promise<TokenInfo[]>;
|
|
28
|
-
fetchChains(token: string): Promise<ChainInfo[]>;
|
|
29
|
-
fetchQuote(token: string, chain: string, amount: string): Promise<QuoteResult>;
|
|
30
|
-
}
|
|
31
|
-
export declare const createWithdrawController: (custody: CustodyAdapter, marketData: MarketDataProvider) => WithdrawController;
|
package/dist/modules/withdraw.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
-
import { getEnv } from "../utils/env";
|
|
3
|
-
function requireSession() {
|
|
4
|
-
const session = sessionStore.getState().session;
|
|
5
|
-
if (!session)
|
|
6
|
-
throw new Error("Login required");
|
|
7
|
-
return session;
|
|
8
|
-
}
|
|
9
|
-
export const createWithdrawController = (custody, marketData) => {
|
|
10
|
-
let status = { phase: "idle" };
|
|
11
|
-
const update = (next, cb) => {
|
|
12
|
-
status = next;
|
|
13
|
-
cb?.(status);
|
|
14
|
-
};
|
|
15
|
-
const open = async (config) => {
|
|
16
|
-
const session = requireSession();
|
|
17
|
-
const token = config?.defaultToken || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
18
|
-
const chain = config?.defaultChain ??
|
|
19
|
-
session.chainContext?.settlementChain ??
|
|
20
|
-
session.chain ??
|
|
21
|
-
"AB_CORE";
|
|
22
|
-
const targetAddress = config?.targetAddress;
|
|
23
|
-
if (!targetAddress)
|
|
24
|
-
throw new Error("targetAddress required for withdraw");
|
|
25
|
-
try {
|
|
26
|
-
const { requestId } = await custody.requestWithdraw({
|
|
27
|
-
amount: config?.defaultAmount ?? "0",
|
|
28
|
-
token,
|
|
29
|
-
chain,
|
|
30
|
-
targetAddress,
|
|
31
|
-
});
|
|
32
|
-
update({ phase: "requested", requestId }, config?.onStatusChange);
|
|
33
|
-
update({ phase: "processing", requestId }, config?.onStatusChange);
|
|
34
|
-
const { status: finalStatus, txHash } = await custody.getWithdrawStatus(requestId);
|
|
35
|
-
update(finalStatus === "SETTLED"
|
|
36
|
-
? { phase: "settled", requestId, txHash }
|
|
37
|
-
: { phase: "failed", reason: finalStatus }, config?.onStatusChange);
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
update({ phase: "failed", reason: error.message }, config?.onStatusChange);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
return {
|
|
44
|
-
get status() {
|
|
45
|
-
return status;
|
|
46
|
-
},
|
|
47
|
-
open,
|
|
48
|
-
fetchTokens() {
|
|
49
|
-
requireSession();
|
|
50
|
-
return marketData.getSupportedTokens("withdraw");
|
|
51
|
-
},
|
|
52
|
-
fetchChains(token) {
|
|
53
|
-
requireSession();
|
|
54
|
-
return marketData.getSupportedChains(token, "withdraw");
|
|
55
|
-
},
|
|
56
|
-
fetchQuote(token, chain, amount) {
|
|
57
|
-
requireSession();
|
|
58
|
-
return marketData.getQuote({ token, chain, amount, direction: "withdraw" });
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
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;
|
|
@@ -1,33 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export interface WithdrawRequest {
|
|
2
|
-
/** 用户收款地址(目标链) */
|
|
3
|
-
toAddress: string;
|
|
4
|
-
/** Human-readable amount, e.g. "100.5" */
|
|
5
|
-
amount: string;
|
|
6
|
-
/** 目标链代币 symbol,如 "USDT" */
|
|
7
|
-
token: string;
|
|
8
|
-
/** 目标链 chain_id */
|
|
9
|
-
chain: string;
|
|
10
|
-
}
|
|
11
|
-
export interface WithdrawResult {
|
|
12
|
-
/** Funding 链上转入一次性地址的 tx hash */
|
|
13
|
-
txHash: string;
|
|
14
|
-
/** 提现订单 ID,用于轮询 getWithdrawOrder(orderId) */
|
|
15
|
-
orderId: string;
|
|
16
|
-
/** 广播 funding tx 的链 id,用于构建 explorer 链接 */
|
|
17
|
-
fundingChainId?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* A function that executes a withdraw operation.
|
|
21
|
-
* Callers can supply their own implementation to override the default behaviour.
|
|
22
|
-
*/
|
|
23
|
-
export type WithdrawExecutor = (request: WithdrawRequest) => Promise<WithdrawResult>;
|
|
24
|
-
/**
|
|
25
|
-
* Convert a human-readable decimal string to the smallest unit (wei-equivalent).
|
|
26
|
-
*
|
|
27
|
-
* Examples with decimals=18:
|
|
28
|
-
* "1" → 1000000000000000000n
|
|
29
|
-
* "0.5" → 500000000000000000n
|
|
30
|
-
* "100.1" → 100100000000000000000n
|
|
31
|
-
*/
|
|
32
|
-
export declare function parseUnits(value: string, decimals: number): bigint;
|
|
33
|
-
export interface FundingWithdrawExecutorOptions {
|
|
34
|
-
/** 源链 funding ERC-20 合约地址;默认 {@link getFundingTokenAddress} / env */
|
|
35
|
-
tokenAddress?: string;
|
|
36
|
-
decimals?: 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;
|
|
43
|
-
/** 系统配置的单笔限额(wei 字符串),若提供则校验 request.amount 不得超过此值 */
|
|
44
|
-
maxAmountWei?: string;
|
|
45
|
-
/**
|
|
46
|
-
* 与商户订单接口约定的 funding 侧 token symbol(默认 `USDT`,按后端协议)。
|
|
47
|
-
*/
|
|
48
|
-
fundingLegTokenSymbol?: string;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Factory that returns a `WithdrawExecutor` implementing the flow in withdraw.md:
|
|
52
|
-
* 1) Create NATIVE_SWAP order → get one-time wallet address (OTW);
|
|
53
|
-
* 2) Sign ERC-20 transfer of the funding token on the funding chain to the OTW (not to user);
|
|
54
|
-
* 3) Broadcast tx and return { txHash, orderId } for the client to poll getWithdrawOrder(orderId).
|
|
55
|
-
*/
|
|
56
|
-
export declare function createFundingWithdrawExecutor(options?: FundingWithdrawExecutorOptions): WithdrawExecutor;
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
-
import { tryAutoReconnect } from "../auth/autoReconnect.js";
|
|
3
|
-
import { getChains, createOrder } from "./api.js";
|
|
4
|
-
import { fromHex, parseGwei, toHex } from "viem";
|
|
5
|
-
import { getFundingTokenAddress, getChainInfo } from "../constants/chains.js";
|
|
6
|
-
import { getEnv } from "../utils/env";
|
|
7
|
-
const MAX_WITHDRAW_GAS_LIMIT = 500000n;
|
|
8
|
-
/* ─── Internal helpers ────────────────────────── */
|
|
9
|
-
const ERC20_TRANSFER_SELECTOR = "0xa9059cbb";
|
|
10
|
-
function padHex256(value) {
|
|
11
|
-
return value.toString(16).padStart(64, "0");
|
|
12
|
-
}
|
|
13
|
-
function padAddress(address) {
|
|
14
|
-
return address.toLowerCase().replace("0x", "").padStart(64, "0");
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Convert a human-readable decimal string to the smallest unit (wei-equivalent).
|
|
18
|
-
*
|
|
19
|
-
* Examples with decimals=18:
|
|
20
|
-
* "1" → 1000000000000000000n
|
|
21
|
-
* "0.5" → 500000000000000000n
|
|
22
|
-
* "100.1" → 100100000000000000000n
|
|
23
|
-
*/
|
|
24
|
-
export function parseUnits(value, decimals) {
|
|
25
|
-
if (!value || value === "0")
|
|
26
|
-
return 0n;
|
|
27
|
-
const [intPart = "0", fracPart = ""] = value.split(".");
|
|
28
|
-
const padded = fracPart.padEnd(decimals, "0").slice(0, decimals);
|
|
29
|
-
return BigInt(intPart) * 10n ** BigInt(decimals) + BigInt(padded);
|
|
30
|
-
}
|
|
31
|
-
function encodeTransferData(to, amountWei) {
|
|
32
|
-
return `${ERC20_TRANSFER_SELECTOR}${padAddress(to)}${padHex256(amountWei)}`;
|
|
33
|
-
}
|
|
34
|
-
function isUnsupportedMethodError(error) {
|
|
35
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
-
return /unsupported rpc method|unsupported method|method not found|does not support/i.test(message);
|
|
37
|
-
}
|
|
38
|
-
function toHexQuantity(value) {
|
|
39
|
-
if (typeof value === "string") {
|
|
40
|
-
if (/^0x[0-9a-fA-F]+$/.test(value)) {
|
|
41
|
-
return value;
|
|
42
|
-
}
|
|
43
|
-
if (/^\d+$/.test(value)) {
|
|
44
|
-
return toHex(BigInt(value));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (typeof value === "number") {
|
|
48
|
-
return toHex(BigInt(value));
|
|
49
|
-
}
|
|
50
|
-
if (typeof value === "bigint") {
|
|
51
|
-
return toHex(value);
|
|
52
|
-
}
|
|
53
|
-
throw new Error(`Invalid EVM quantity: ${String(value)}`);
|
|
54
|
-
}
|
|
55
|
-
async function callRpc(rpcUrl, method, params) {
|
|
56
|
-
const response = await fetch(rpcUrl, {
|
|
57
|
-
method: "POST",
|
|
58
|
-
headers: { "Content-Type": "application/json" },
|
|
59
|
-
body: JSON.stringify({
|
|
60
|
-
jsonrpc: "2.0",
|
|
61
|
-
id: Date.now(),
|
|
62
|
-
method,
|
|
63
|
-
params,
|
|
64
|
-
}),
|
|
65
|
-
});
|
|
66
|
-
const json = (await response.json());
|
|
67
|
-
if (!response.ok || json.error) {
|
|
68
|
-
throw new Error(json.error?.message ?? `${method} failed`);
|
|
69
|
-
}
|
|
70
|
-
return json.result;
|
|
71
|
-
}
|
|
72
|
-
async function requestHexQuantity(provider, rpcUrl, method, params) {
|
|
73
|
-
try {
|
|
74
|
-
const result = await provider.request({ method, params });
|
|
75
|
-
return toHexQuantity(result);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
if (!isUnsupportedMethodError(error)) {
|
|
79
|
-
throw error;
|
|
80
|
-
}
|
|
81
|
-
return toHexQuantity(await callRpc(rpcUrl, method, params));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async function ensureFundingEvmChain(provider, chainId) {
|
|
85
|
-
const hex = `0x${chainId.toString(16)}`;
|
|
86
|
-
try {
|
|
87
|
-
await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hex }] });
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
// Ignored – the wallet may already be on the funding chain or may not support switching.
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function getDstTokenAddress(chains, chainId, tokenSymbol) {
|
|
94
|
-
const chain = chains.find((c) => c.chain_id === chainId);
|
|
95
|
-
return chain?.tokens.find((t) => t.symbol === tokenSymbol)?.address;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Factory that returns a `WithdrawExecutor` implementing the flow in withdraw.md:
|
|
99
|
-
* 1) Create NATIVE_SWAP order → get one-time wallet address (OTW);
|
|
100
|
-
* 2) Sign ERC-20 transfer of the funding token on the funding chain to the OTW (not to user);
|
|
101
|
-
* 3) Broadcast tx and return { txHash, orderId } for the client to poll getWithdrawOrder(orderId).
|
|
102
|
-
*/
|
|
103
|
-
export function createFundingWithdrawExecutor(options) {
|
|
104
|
-
const fundingChain = getChainInfo(options?.chainId);
|
|
105
|
-
const chainIdNum = Number(fundingChain.chainId);
|
|
106
|
-
const rpcUrl = options?.rpcUrl ?? fundingChain.rpcUrls[0];
|
|
107
|
-
const tokenAddress = options?.tokenAddress ?? getFundingTokenAddress(options?.chainId);
|
|
108
|
-
const decimals = options?.decimals ?? fundingChain.nativeCurrencyDecimals;
|
|
109
|
-
const maxAmountWei = options?.maxAmountWei;
|
|
110
|
-
const fundingLegTokenSymbol = options?.fundingLegTokenSymbol || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
111
|
-
return async (request) => {
|
|
112
|
-
const amountWei = parseUnits(request.amount, decimals);
|
|
113
|
-
const amountWeiStr = amountWei.toString();
|
|
114
|
-
if (maxAmountWei != null && amountWei > BigInt(maxAmountWei)) {
|
|
115
|
-
throw new Error("Withdraw amount exceeds the single-transaction limit");
|
|
116
|
-
}
|
|
117
|
-
let session = sessionStore.getState().session;
|
|
118
|
-
if (!session)
|
|
119
|
-
throw new Error("Login required");
|
|
120
|
-
let { provider } = session;
|
|
121
|
-
try {
|
|
122
|
-
await provider.request({ method: "eth_chainId", params: [] });
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
const msg = err.message ?? "";
|
|
126
|
-
if (msg.includes("restored from cache") || msg.includes("Reconnect your wallet")) {
|
|
127
|
-
const reconnected = await tryAutoReconnect();
|
|
128
|
-
if (reconnected) {
|
|
129
|
-
session = reconnected;
|
|
130
|
-
provider = reconnected.provider;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
sessionStore.clearSession();
|
|
134
|
-
throw new Error("Session expired. Please sign in again.");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
const chainsRes = await getChains();
|
|
139
|
-
const chains = chainsRes?.chains ?? [];
|
|
140
|
-
const dstTokenAddress = getDstTokenAddress(chains, request.chain, request.token);
|
|
141
|
-
if (!dstTokenAddress) {
|
|
142
|
-
throw new Error(`Unsupported token ${request.token} on chain ${request.chain}`);
|
|
143
|
-
}
|
|
144
|
-
// 校验规则:源/目标代币需满足商户约定;提现场景下源链为 funding token;token_amount 已按 maxAmountWei 做单笔限额校验
|
|
145
|
-
const sourceTokenSymbol = fundingLegTokenSymbol;
|
|
146
|
-
const orderRes = await createOrder({
|
|
147
|
-
intent_id: `withdraw-${Date.now()}`,
|
|
148
|
-
order_type: "NATIVE_SWAP",
|
|
149
|
-
order_payload: {
|
|
150
|
-
chain_id: fundingChain.chainId,
|
|
151
|
-
token_address: tokenAddress,
|
|
152
|
-
token_amount: amountWeiStr,
|
|
153
|
-
dst_chain_id: request.chain,
|
|
154
|
-
dst_token_address: dstTokenAddress,
|
|
155
|
-
recipient: request.toAddress,
|
|
156
|
-
},
|
|
157
|
-
payment_pairs: [
|
|
158
|
-
{
|
|
159
|
-
token_symbol: sourceTokenSymbol,
|
|
160
|
-
token_amount: amountWeiStr,
|
|
161
|
-
token_address: tokenAddress,
|
|
162
|
-
user_address: session.address,
|
|
163
|
-
chain_id: fundingChain.chainId,
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
});
|
|
167
|
-
const oneTimeAddress = orderRes.payment_sessions?.[0]?.one_time_wallet_address;
|
|
168
|
-
if (!oneTimeAddress) {
|
|
169
|
-
throw new Error("Order created but no one-time wallet address returned");
|
|
170
|
-
}
|
|
171
|
-
await ensureFundingEvmChain(provider, chainIdNum);
|
|
172
|
-
const data = encodeTransferData(oneTimeAddress, amountWei);
|
|
173
|
-
const nonce = await requestHexQuantity(provider, rpcUrl, "eth_getTransactionCount", [session.address, "latest"]);
|
|
174
|
-
const tx = {
|
|
175
|
-
from: session.address,
|
|
176
|
-
to: tokenAddress,
|
|
177
|
-
value: "0x0",
|
|
178
|
-
nonce,
|
|
179
|
-
data,
|
|
180
|
-
chainId: toHex(chainIdNum),
|
|
181
|
-
};
|
|
182
|
-
const estimatedGasHex = await requestHexQuantity(provider, rpcUrl, "eth_estimateGas", [tx]);
|
|
183
|
-
const estimatedGas = fromHex(estimatedGasHex, "bigint");
|
|
184
|
-
const gas = toHex(estimatedGas > MAX_WITHDRAW_GAS_LIMIT ? MAX_WITHDRAW_GAS_LIMIT : estimatedGas);
|
|
185
|
-
const transaction = {
|
|
186
|
-
...tx,
|
|
187
|
-
gas,
|
|
188
|
-
maxFeePerGas: toHex(parseGwei("5")),
|
|
189
|
-
maxPriorityFeePerGas: toHex(parseGwei("1")),
|
|
190
|
-
};
|
|
191
|
-
let txHash;
|
|
192
|
-
try {
|
|
193
|
-
txHash = await provider.request({
|
|
194
|
-
method: "eth_sendTransaction",
|
|
195
|
-
params: [transaction],
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
if (!isUnsupportedMethodError(error)) {
|
|
200
|
-
throw error;
|
|
201
|
-
}
|
|
202
|
-
const signedTx = await provider.request({
|
|
203
|
-
method: "eth_signTransaction",
|
|
204
|
-
params: [transaction],
|
|
205
|
-
});
|
|
206
|
-
txHash = await callRpc(rpcUrl, "eth_sendRawTransaction", [signedTx]);
|
|
207
|
-
}
|
|
208
|
-
return { txHash, orderId: orderRes.order_id, fundingChainId: fundingChain.chainId };
|
|
209
|
-
};
|
|
210
|
-
}
|
package/dist/policyAdapter.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type SessionCapabilityPolicy, type SupportedChain, type SupportedToken, type WalletCapability } from "@ab-org/sdk-core";
|
|
2
|
-
export interface PredicateMarketPolicyAdapterOptions {
|
|
3
|
-
appId?: string;
|
|
4
|
-
origin?: string;
|
|
5
|
-
expiresAt?: number;
|
|
6
|
-
}
|
|
7
|
-
export declare const createPredicateMarketPolicyAdapter: (options?: PredicateMarketPolicyAdapterOptions) => {
|
|
8
|
-
deposit(token: SupportedToken, chain: SupportedChain, maxAmount?: string): SessionCapabilityPolicy;
|
|
9
|
-
withdraw(token: SupportedToken, chain: SupportedChain, maxAmount?: string): SessionCapabilityPolicy;
|
|
10
|
-
trade(chain: SupportedChain, capabilities?: WalletCapability[]): SessionCapabilityPolicy;
|
|
11
|
-
};
|
package/dist/policyAdapter.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { createSessionCapabilityPolicy, } from "@ab-org/sdk-core";
|
|
2
|
-
const createPolicy = (overrides, options) => createSessionCapabilityPolicy({
|
|
3
|
-
appId: options?.appId,
|
|
4
|
-
origin: options?.origin,
|
|
5
|
-
expiresAt: options?.expiresAt,
|
|
6
|
-
...overrides,
|
|
7
|
-
});
|
|
8
|
-
const withActionMetadata = (action, policy) => ({
|
|
9
|
-
...policy,
|
|
10
|
-
metadata: {
|
|
11
|
-
...(policy.metadata ?? {}),
|
|
12
|
-
action,
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
export const createPredicateMarketPolicyAdapter = (options) => ({
|
|
16
|
-
deposit(token, chain, maxAmount) {
|
|
17
|
-
return withActionMetadata("deposit", createPolicy({
|
|
18
|
-
methods: ["eth_sendTransaction"],
|
|
19
|
-
chains: [chain],
|
|
20
|
-
tokens: [token],
|
|
21
|
-
maxAmount,
|
|
22
|
-
}, options));
|
|
23
|
-
},
|
|
24
|
-
withdraw(token, chain, maxAmount) {
|
|
25
|
-
return withActionMetadata("withdraw", createPolicy({
|
|
26
|
-
methods: ["eth_sendTransaction"],
|
|
27
|
-
chains: [chain],
|
|
28
|
-
tokens: [token],
|
|
29
|
-
maxAmount,
|
|
30
|
-
}, options));
|
|
31
|
-
},
|
|
32
|
-
trade(chain, capabilities = ["eth_sendTransaction"]) {
|
|
33
|
-
return withActionMetadata("trade", createPolicy({
|
|
34
|
-
methods: capabilities,
|
|
35
|
-
chains: [chain],
|
|
36
|
-
}, options));
|
|
37
|
-
},
|
|
38
|
-
});
|