@ab-org/predicate-market-sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/modules/api.d.ts +1 -1
- package/dist/modules/deposit.js +2 -1
- package/dist/modules/withdraw.js +2 -1
- package/dist/modules/withdrawExecutor.d.ts +1 -1
- package/dist/modules/withdrawExecutor.js +2 -1
- package/dist/ui/DepositModal.d.ts +1 -1
- package/dist/ui/DepositModal.js +39 -4
- package/dist/ui/components/DepositDetailsPanel.d.ts +1 -1
- package/dist/ui/components/DepositDetailsPanel.js +31 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Prediction-market specific helpers built on top of `@ab-org/sdk-core`.
|
|
|
7
7
|
- Connection state via `createWalletConnectController()` and `createAccountController()` from `@ab-org/sdk-core`
|
|
8
8
|
- Unified smart-wallet session metadata with capability policy support
|
|
9
9
|
- High-level execution helpers via `createWalletExecutionController()`
|
|
10
|
-
- Deposit / withdraw controllers with
|
|
10
|
+
- Deposit / withdraw controllers with USDT-first defaults
|
|
11
11
|
- **Dynamic token & chain lists** via `MarketDataProvider` (backend-driven, mock included)
|
|
12
12
|
- **Quote / slippage API** — fetch real-time pricing before confirming deposit or withdraw
|
|
13
13
|
- Predicate-market policy adapter for deposit / withdraw / trade flows
|
|
@@ -98,7 +98,7 @@ Rules:
|
|
|
98
98
|
### Funding chain & funding token (withdraw / balance)
|
|
99
99
|
|
|
100
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
|
|
101
|
+
- **`getFundingTokenAddress(chainId?)`** — same optional **`chainId`** as **`getChainInfo`**. If **`NEXT_PUBLIC_FUNDING_TOKEN_ADDRESS`** / **`FUNDING_TOKEN_ADDRESS`** or legacy defaultFundingTokenAddress`**.
|
|
102
102
|
- **`DEFAULT_FUNDING_TOKEN_ADDRESS`** — shorthand for **`getChainInfo().defaultFundingTokenAddress`** (default funding chain **`3131`**).
|
|
103
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
104
|
- **`createFundingWithdrawExecutor({ chainId?, rpcUrl?, tokenAddress?, … })`** — uses the same **`chainId`** for **`getChainInfo`**, default token address (**`getFundingTokenAddress(chainId)`**), and order payload (default **`3131`** when **`chainId`** omitted).
|
|
@@ -251,7 +251,7 @@ These policies can be passed into your smart-wallet authorization flow so app ac
|
|
|
251
251
|
<button
|
|
252
252
|
onClick={() =>
|
|
253
253
|
withdraw.open({
|
|
254
|
-
defaultToken: "
|
|
254
|
+
defaultToken: "USDT",
|
|
255
255
|
defaultChain: "AB_CORE",
|
|
256
256
|
targetAddress: "0x...",
|
|
257
257
|
defaultAmount: "100",
|
package/dist/modules/api.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface WithdrawOrderResponseData {
|
|
|
75
75
|
one_time_address?: string;
|
|
76
76
|
chain_id: string;
|
|
77
77
|
dst_token_amount: string;
|
|
78
|
-
/** Fee (e.g. "0.01
|
|
78
|
+
/** Fee (e.g. "0.01 USDT"); only present if backend includes it in GET /api/v1/orders/withdraw/:id. UI falls back to feeDisplay prop or "—". */
|
|
79
79
|
fee?: string;
|
|
80
80
|
target_chain_id: string;
|
|
81
81
|
target_address: string;
|
package/dist/modules/deposit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
+
import { getEnv } from "../utils/env";
|
|
2
3
|
function requireSession() {
|
|
3
4
|
const session = sessionStore.getState().session;
|
|
4
5
|
if (!session)
|
|
@@ -17,7 +18,7 @@ export const createDepositController = (custody, marketData) => {
|
|
|
17
18
|
session.chainContext?.settlementChain ??
|
|
18
19
|
session.chain ??
|
|
19
20
|
"AB_CORE";
|
|
20
|
-
const token = config?.preferredToken
|
|
21
|
+
const token = config?.preferredToken || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
21
22
|
try {
|
|
22
23
|
const { address } = await marketData.getDepositAddress(token, chain);
|
|
23
24
|
const depositId = `${chain}:${token}:${Date.now()}`;
|
package/dist/modules/withdraw.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sessionStore } from "@ab-org/sdk-core";
|
|
2
|
+
import { getEnv } from "../utils/env";
|
|
2
3
|
function requireSession() {
|
|
3
4
|
const session = sessionStore.getState().session;
|
|
4
5
|
if (!session)
|
|
@@ -13,7 +14,7 @@ export const createWithdrawController = (custody, marketData) => {
|
|
|
13
14
|
};
|
|
14
15
|
const open = async (config) => {
|
|
15
16
|
const session = requireSession();
|
|
16
|
-
const token = config?.defaultToken
|
|
17
|
+
const token = config?.defaultToken || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
17
18
|
const chain = config?.defaultChain ??
|
|
18
19
|
session.chainContext?.settlementChain ??
|
|
19
20
|
session.chain ??
|
|
@@ -43,7 +43,7 @@ export interface FundingWithdrawExecutorOptions {
|
|
|
43
43
|
/** 系统配置的单笔限额(wei 字符串),若提供则校验 request.amount 不得超过此值 */
|
|
44
44
|
maxAmountWei?: string;
|
|
45
45
|
/**
|
|
46
|
-
* 与商户订单接口约定的 funding 侧 token symbol(默认 `
|
|
46
|
+
* 与商户订单接口约定的 funding 侧 token symbol(默认 `USDT`,按后端协议)。
|
|
47
47
|
*/
|
|
48
48
|
fundingLegTokenSymbol?: string;
|
|
49
49
|
}
|
|
@@ -3,6 +3,7 @@ import { tryAutoReconnect } from "../auth/autoReconnect.js";
|
|
|
3
3
|
import { getChains, createOrder } from "./api.js";
|
|
4
4
|
import { fromHex, parseGwei, toHex } from "viem";
|
|
5
5
|
import { getFundingTokenAddress, getChainInfo } from "../constants/chains.js";
|
|
6
|
+
import { getEnv } from "../utils/env";
|
|
6
7
|
const MAX_WITHDRAW_GAS_LIMIT = 500000n;
|
|
7
8
|
/* ─── Internal helpers ────────────────────────── */
|
|
8
9
|
const ERC20_TRANSFER_SELECTOR = "0xa9059cbb";
|
|
@@ -106,7 +107,7 @@ export function createFundingWithdrawExecutor(options) {
|
|
|
106
107
|
const tokenAddress = options?.tokenAddress ?? getFundingTokenAddress(options?.chainId);
|
|
107
108
|
const decimals = options?.decimals ?? fundingChain.nativeCurrencyDecimals;
|
|
108
109
|
const maxAmountWei = options?.maxAmountWei;
|
|
109
|
-
const fundingLegTokenSymbol = options?.fundingLegTokenSymbol
|
|
110
|
+
const fundingLegTokenSymbol = options?.fundingLegTokenSymbol || getEnv("FUNDING_TOKEN_SYMBOL") || "USDT";
|
|
110
111
|
return async (request) => {
|
|
111
112
|
const amountWei = parseUnits(request.amount, decimals);
|
|
112
113
|
const amountWeiStr = amountWei.toString();
|
|
@@ -27,7 +27,7 @@ export interface DepositModalProps {
|
|
|
27
27
|
explorerTxUrl?: (chainId: string, txHash: string) => string;
|
|
28
28
|
onTokenSelect?: (id: string) => void;
|
|
29
29
|
onChainSelect?: (id: string) => void;
|
|
30
|
-
onCopyAddress?: () => void;
|
|
30
|
+
onCopyAddress?: (address: string) => void;
|
|
31
31
|
onBuyCrypto?: () => void;
|
|
32
32
|
onSignIn?: () => void;
|
|
33
33
|
onBack?: () => void;
|
package/dist/ui/DepositModal.js
CHANGED
|
@@ -8,7 +8,7 @@ import { DepositDetailsPanel } from "./components/DepositDetailsPanel.js";
|
|
|
8
8
|
import { LoginRequiredOverlay } from "./components/LoginRequiredOverlay.js";
|
|
9
9
|
import { useSession } from "./hooks/useSession.js";
|
|
10
10
|
import { colors, fonts, radii } from "./theme.js";
|
|
11
|
-
import { getChains, quote, } from "../modules/api.js";
|
|
11
|
+
import { getChains, quote, registerPlatform, } from "../modules/api.js";
|
|
12
12
|
import { getEnv } from "../utils/env";
|
|
13
13
|
/** 校验是否为合法的充值地址(传入值):非空且长度满足常见链地址格式 */
|
|
14
14
|
function isValidDepositAddress(v) {
|
|
@@ -97,6 +97,9 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
|
|
|
97
97
|
const [loadingChains, setLoadingChains] = useState(false);
|
|
98
98
|
const [loadingQuote, setLoadingQuote] = useState(false);
|
|
99
99
|
const [quoteRefreshKey, setQuoteRefreshKey] = useState(0);
|
|
100
|
+
const [internalDepositAddress, setInternalDepositAddress] = useState(undefined);
|
|
101
|
+
const lastEmittedAddressRef = useRef(undefined);
|
|
102
|
+
const lastEmittedChainRef = useRef(undefined);
|
|
100
103
|
const tokenOptions = useMemo(() => {
|
|
101
104
|
if (tokenOptionsProp?.length)
|
|
102
105
|
return tokenOptionsProp;
|
|
@@ -128,6 +131,25 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
|
|
|
128
131
|
.then((res) => setApiChains(res?.chains ?? {}))
|
|
129
132
|
.finally(() => setLoadingChains(false));
|
|
130
133
|
}, [view]);
|
|
134
|
+
// Fetch depositAddress internally when user is signed in and a chain has been selected.
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (view !== "transfer")
|
|
137
|
+
return;
|
|
138
|
+
if (!session?.address || !chain)
|
|
139
|
+
return;
|
|
140
|
+
registerPlatform({
|
|
141
|
+
platform_contract_address: session.address,
|
|
142
|
+
chain_id: chain,
|
|
143
|
+
})
|
|
144
|
+
.then((res) => {
|
|
145
|
+
if (res?.deposit_address) {
|
|
146
|
+
setInternalDepositAddress(res.deposit_address);
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
.catch(() => {
|
|
150
|
+
// silent failure; UI will keep placeholder if any
|
|
151
|
+
});
|
|
152
|
+
}, [view, chain, session?.address]);
|
|
131
153
|
useEffect(() => {
|
|
132
154
|
if (!apiChains?.length || !token || !chain) {
|
|
133
155
|
setApiQuote(null);
|
|
@@ -165,6 +187,19 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
|
|
|
165
187
|
})
|
|
166
188
|
.finally(() => setLoadingQuote(false));
|
|
167
189
|
}, [apiChains, token, chain, depositAmount, quoteRefreshKey, onShowToast]);
|
|
190
|
+
// Emit resolved address when external prop is provided/changes
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (!chain)
|
|
193
|
+
return;
|
|
194
|
+
if (!depositAddress)
|
|
195
|
+
return;
|
|
196
|
+
if (lastEmittedAddressRef.current === depositAddress &&
|
|
197
|
+
lastEmittedChainRef.current === chain) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
lastEmittedAddressRef.current = depositAddress;
|
|
201
|
+
lastEmittedChainRef.current = chain;
|
|
202
|
+
}, [depositAddress, chain]);
|
|
168
203
|
const handleQuoteExpired = useCallback(() => {
|
|
169
204
|
setQuoteRefreshKey((k) => k + 1);
|
|
170
205
|
}, []);
|
|
@@ -180,8 +215,8 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
|
|
|
180
215
|
setView("entry");
|
|
181
216
|
onBack?.();
|
|
182
217
|
};
|
|
183
|
-
const handleCopyAddress = useCallback(() => {
|
|
184
|
-
onCopyAddress?.();
|
|
218
|
+
const handleCopyAddress = useCallback((address) => {
|
|
219
|
+
onCopyAddress?.(address);
|
|
185
220
|
onShowToast?.("Address copied");
|
|
186
221
|
setCopySuccessMessage("Address copied");
|
|
187
222
|
}, [onCopyAddress, onShowToast]);
|
|
@@ -204,7 +239,7 @@ export const DepositModal = ({ token, chain, tokenOptions: tokenOptionsProp, cha
|
|
|
204
239
|
fontWeight: 500,
|
|
205
240
|
boxShadow: "0 4px 12px rgba(0,0,0,.25)",
|
|
206
241
|
zIndex: 10,
|
|
207
|
-
}, children: copySuccessMessage })), view === "entry" ? (_jsx(EntryView, { cryptoIcons: cryptoIcons, onTransferCrypto: () => setView("transfer"), onBuyCrypto: onBuyCrypto })) : (_jsx(TransferView, { token: token, chain: chain, tokenOptions: tokenOptions, chainOptions: chainOptions, depositAddress: depositAddress, minimumDeposit: minimumDeposit, qrCenterIcon: qrCenterIcon, quote: apiQuote, quoteLoading: loadingQuote, txHash: txHash, chainIdForExplorer: chain, explorerTxUrl: explorerTxUrl, loadingChains: loadingChains, onTokenSelect: onTokenSelect, onChainSelect: onChainSelect, onCopyAddress: handleCopyAddress, onQuoteExpired: handleQuoteExpired, onRefreshQuote: () => {
|
|
242
|
+
}, children: copySuccessMessage })), view === "entry" ? (_jsx(EntryView, { cryptoIcons: cryptoIcons, onTransferCrypto: () => setView("transfer"), onBuyCrypto: onBuyCrypto })) : (_jsx(TransferView, { token: token, chain: chain, tokenOptions: tokenOptions, chainOptions: chainOptions, depositAddress: depositAddress ?? internalDepositAddress, minimumDeposit: minimumDeposit, qrCenterIcon: qrCenterIcon, quote: apiQuote, quoteLoading: loadingQuote, txHash: txHash, chainIdForExplorer: chain, explorerTxUrl: explorerTxUrl, loadingChains: loadingChains, onTokenSelect: onTokenSelect, onChainSelect: onChainSelect, onCopyAddress: handleCopyAddress, onQuoteExpired: handleQuoteExpired, onRefreshQuote: () => {
|
|
208
243
|
if (!apiChains?.length || !token || !chain)
|
|
209
244
|
return;
|
|
210
245
|
const tokenAddress = getTokenAddressForChain(apiChains, chain, token);
|
|
@@ -3,6 +3,6 @@ export interface DepositDetailsPanelProps {
|
|
|
3
3
|
address: string;
|
|
4
4
|
tokenIcon?: ReactNode;
|
|
5
5
|
minimumDeposit?: string;
|
|
6
|
-
onCopyAddress?: () => void;
|
|
6
|
+
onCopyAddress?: (address: string) => void;
|
|
7
7
|
}
|
|
8
8
|
export declare const DepositDetailsPanel: ({ address, tokenIcon, minimumDeposit, onCopyAddress, }: DepositDetailsPanelProps) => import("react/jsx-runtime.js").JSX.Element;
|
|
@@ -56,9 +56,32 @@ const StyledQR = ({ data }) => {
|
|
|
56
56
|
const CopyIcon = () => (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [_jsx("rect", { x: "4.5", y: "4.5", width: "7", height: "7", rx: "1.5", stroke: colors.textPrimary, strokeWidth: "1.2" }), _jsx("path", { d: "M9.5 4.5V3.5C9.5 2.67 8.83 2 8 2H3.5C2.67 2 2 2.67 2 3.5V8C2 8.83 2.67 9.5 3.5 9.5H4.5", stroke: colors.textPrimary, strokeWidth: "1.2" })] }));
|
|
57
57
|
/* ─── Main ──────────────────────────────────── */
|
|
58
58
|
export const DepositDetailsPanel = ({ address, tokenIcon, minimumDeposit, onCopyAddress, }) => {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const canCopy = typeof address === "string" && address.trim().length > 0;
|
|
60
|
+
const handleCopy = async () => {
|
|
61
|
+
if (!canCopy)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
65
|
+
await navigator.clipboard.writeText(address);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const el = document.createElement("textarea");
|
|
69
|
+
el.value = address;
|
|
70
|
+
el.setAttribute("readonly", "");
|
|
71
|
+
el.style.position = "absolute";
|
|
72
|
+
el.style.left = "-9999px";
|
|
73
|
+
document.body.appendChild(el);
|
|
74
|
+
el.select();
|
|
75
|
+
try {
|
|
76
|
+
document.execCommand("copy");
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
document.body.removeChild(el);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
onCopyAddress?.(address);
|
|
84
|
+
}
|
|
62
85
|
};
|
|
63
86
|
return (_jsxs("div", { style: {
|
|
64
87
|
display: "flex",
|
|
@@ -95,7 +118,7 @@ export const DepositDetailsPanel = ({ address, tokenIcon, minimumDeposit, onCopy
|
|
|
95
118
|
display: "flex",
|
|
96
119
|
justifyContent: "space-between",
|
|
97
120
|
fontSize: 13,
|
|
98
|
-
}, children: [_jsx("span", { style: { color: colors.textSecondary }, children: "Minimum deposit" }), _jsx("span", { style: { color: colors.textPrimary, fontWeight: 500 }, children: minimumDeposit })] })), _jsxs("button", { type: "button", onClick: handleCopy, style: {
|
|
121
|
+
}, children: [_jsx("span", { style: { color: colors.textSecondary }, children: "Minimum deposit" }), _jsx("span", { style: { color: colors.textPrimary, fontWeight: 500 }, children: minimumDeposit })] })), _jsxs("button", { type: "button", onClick: handleCopy, disabled: !canCopy, style: {
|
|
99
122
|
display: "flex",
|
|
100
123
|
alignItems: "center",
|
|
101
124
|
gap: 6,
|
|
@@ -106,10 +129,13 @@ export const DepositDetailsPanel = ({ address, tokenIcon, minimumDeposit, onCopy
|
|
|
106
129
|
color: colors.textPrimary,
|
|
107
130
|
fontSize: 14,
|
|
108
131
|
fontWeight: 500,
|
|
109
|
-
cursor: "pointer",
|
|
132
|
+
cursor: canCopy ? "pointer" : "not-allowed",
|
|
133
|
+
opacity: canCopy ? 1 : 0.6,
|
|
110
134
|
fontFamily: fonts.family,
|
|
111
135
|
transition: "background .15s",
|
|
112
136
|
}, onMouseEnter: (e) => {
|
|
137
|
+
if (!canCopy)
|
|
138
|
+
return;
|
|
113
139
|
e.currentTarget.style.background = "rgba(255,255,255,0.06)";
|
|
114
140
|
}, onMouseLeave: (e) => {
|
|
115
141
|
e.currentTarget.style.background = "transparent";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ab-org/predicate-market-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*",
|
|
@@ -23,8 +23,8 @@
|
|
|
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/
|
|
27
|
-
"@ab-org/
|
|
26
|
+
"@ab-org/sdk-core": "0.0.1",
|
|
27
|
+
"@ab-org/wallet-utils": "0.0.7"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=18",
|