@ab-org/predicate-market-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +246 -0
  2. package/dist/auth/autoReconnect.d.ts +11 -0
  3. package/dist/auth/autoReconnect.js +36 -0
  4. package/dist/auth/bundledConfig.d.ts +2 -0
  5. package/dist/auth/bundledConfig.js +19 -0
  6. package/dist/auth/config.d.ts +29 -0
  7. package/dist/auth/config.js +53 -0
  8. package/dist/auth/google.d.ts +43 -0
  9. package/dist/auth/google.js +147 -0
  10. package/dist/auth/twitter.d.ts +7 -0
  11. package/dist/auth/twitter.js +94 -0
  12. package/dist/constants/chains.d.ts +22 -0
  13. package/dist/constants/chains.js +23 -0
  14. package/dist/index.d.ts +19 -0
  15. package/dist/index.js +19 -0
  16. package/dist/modules/api.d.ts +144 -0
  17. package/dist/modules/api.js +93 -0
  18. package/dist/modules/balanceQuery.d.ts +20 -0
  19. package/dist/modules/balanceQuery.js +58 -0
  20. package/dist/modules/deposit.d.ts +31 -0
  21. package/dist/modules/deposit.js +57 -0
  22. package/dist/modules/marketData.d.ts +8 -0
  23. package/dist/modules/marketData.js +113 -0
  24. package/dist/modules/withdraw.d.ts +31 -0
  25. package/dist/modules/withdraw.js +60 -0
  26. package/dist/modules/withdrawExecutor.d.ts +47 -0
  27. package/dist/modules/withdrawExecutor.js +208 -0
  28. package/dist/policyAdapter.d.ts +11 -0
  29. package/dist/policyAdapter.js +38 -0
  30. package/dist/types.d.ts +62 -0
  31. package/dist/types.js +1 -0
  32. package/dist/ui/DepositModal.d.ts +36 -0
  33. package/dist/ui/DepositModal.js +326 -0
  34. package/dist/ui/SignInModal.d.ts +22 -0
  35. package/dist/ui/SignInModal.js +74 -0
  36. package/dist/ui/SignInModal.sections.d.ts +33 -0
  37. package/dist/ui/SignInModal.sections.js +45 -0
  38. package/dist/ui/SignInModal.shared.d.ts +15 -0
  39. package/dist/ui/SignInModal.shared.js +87 -0
  40. package/dist/ui/WalletSelectionModal.d.ts +14 -0
  41. package/dist/ui/WalletSelectionModal.js +54 -0
  42. package/dist/ui/WithdrawModal.d.ts +47 -0
  43. package/dist/ui/WithdrawModal.js +528 -0
  44. package/dist/ui/components/CloseButton.d.ts +4 -0
  45. package/dist/ui/components/CloseButton.js +15 -0
  46. package/dist/ui/components/Countdown.d.ts +16 -0
  47. package/dist/ui/components/Countdown.js +42 -0
  48. package/dist/ui/components/DepositDetailsPanel.d.ts +8 -0
  49. package/dist/ui/components/DepositDetailsPanel.js +117 -0
  50. package/dist/ui/components/DropdownField.d.ts +19 -0
  51. package/dist/ui/components/DropdownField.js +81 -0
  52. package/dist/ui/components/Field.d.ts +10 -0
  53. package/dist/ui/components/Field.js +21 -0
  54. package/dist/ui/components/LoginRequiredOverlay.d.ts +6 -0
  55. package/dist/ui/components/LoginRequiredOverlay.js +31 -0
  56. package/dist/ui/components/ModalCard.d.ts +9 -0
  57. package/dist/ui/components/ModalCard.js +14 -0
  58. package/dist/ui/components/ModalFrame.d.ts +9 -0
  59. package/dist/ui/components/ModalFrame.js +18 -0
  60. package/dist/ui/components/PrimaryButton.d.ts +2 -0
  61. package/dist/ui/components/PrimaryButton.js +14 -0
  62. package/dist/ui/components/QRCodePanel.d.ts +4 -0
  63. package/dist/ui/components/QRCodePanel.js +43 -0
  64. package/dist/ui/components/Select.d.ts +12 -0
  65. package/dist/ui/components/Select.js +29 -0
  66. package/dist/ui/components/StepIndicator.d.ts +7 -0
  67. package/dist/ui/components/StepIndicator.js +35 -0
  68. package/dist/ui/components/Success.d.ts +1 -0
  69. package/dist/ui/components/Success.js +4 -0
  70. package/dist/ui/components/Toast.d.ts +8 -0
  71. package/dist/ui/components/Toast.js +51 -0
  72. package/dist/ui/hooks/useSession.d.ts +2 -0
  73. package/dist/ui/hooks/useSession.js +10 -0
  74. package/dist/ui/signInTypes.d.ts +25 -0
  75. package/dist/ui/signInTypes.js +1 -0
  76. package/dist/ui/theme.d.ts +31 -0
  77. package/dist/ui/theme.js +31 -0
  78. package/dist/ui/useSignInModalController.d.ts +25 -0
  79. package/dist/ui/useSignInModalController.js +173 -0
  80. package/dist/utils/env.d.ts +1 -0
  81. package/dist/utils/env.js +61 -0
  82. package/dist/utils/explorer.d.ts +3 -0
  83. package/dist/utils/explorer.js +47 -0
  84. package/dist/walletUtils.d.ts +3 -0
  85. package/dist/walletUtils.js +3 -0
  86. package/package.json +41 -0
@@ -0,0 +1,94 @@
1
+ function generateRandom(length) {
2
+ const array = new Uint8Array(length);
3
+ crypto.getRandomValues(array);
4
+ return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
5
+ }
6
+ function base64url(buffer) {
7
+ const bytes = new Uint8Array(buffer);
8
+ let binary = "";
9
+ for (const b of bytes)
10
+ binary += String.fromCharCode(b);
11
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
12
+ }
13
+ async function createCodeChallenge(verifier) {
14
+ const encoder = new TextEncoder();
15
+ const data = encoder.encode(verifier);
16
+ const digest = await crypto.subtle.digest("SHA-256", data);
17
+ return base64url(digest);
18
+ }
19
+ const POPUP_WIDTH = 600;
20
+ const POPUP_HEIGHT = 700;
21
+ const MESSAGE_TYPE = "twitter-oauth-callback";
22
+ const TWITTER_AUTH_URL = "https://twitter.com/i/oauth2/authorize";
23
+ export function notifyTwitterCallback() {
24
+ const params = new URLSearchParams(window.location.search);
25
+ const code = params.get("code");
26
+ const state = params.get("state");
27
+ const error = params.get("error");
28
+ if (window.opener) {
29
+ window.opener.postMessage({ type: MESSAGE_TYPE, code, state, error }, window.location.origin);
30
+ window.close();
31
+ }
32
+ }
33
+ export async function signInWithTwitter(clientId, redirectUri) {
34
+ const codeVerifier = generateRandom(64);
35
+ const codeChallenge = await createCodeChallenge(codeVerifier);
36
+ const state = generateRandom(16);
37
+ const authUrl = new URL(TWITTER_AUTH_URL);
38
+ authUrl.searchParams.set("response_type", "code");
39
+ authUrl.searchParams.set("client_id", clientId);
40
+ authUrl.searchParams.set("redirect_uri", redirectUri);
41
+ authUrl.searchParams.set("scope", "tweet.read users.read offline.access");
42
+ authUrl.searchParams.set("state", state);
43
+ authUrl.searchParams.set("code_challenge", codeChallenge);
44
+ authUrl.searchParams.set("code_challenge_method", "S256");
45
+ const left = Math.round(screen.width / 2 - POPUP_WIDTH / 2);
46
+ const top = Math.round(screen.height / 2 - POPUP_HEIGHT / 2);
47
+ const popup = window.open(authUrl.toString(), "twitter-auth", `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},toolbar=no,menubar=no`);
48
+ if (!popup) {
49
+ throw new Error("Failed to open Twitter login popup. Check your popup blocker.");
50
+ }
51
+ return new Promise((resolve, reject) => {
52
+ let settled = false;
53
+ const cleanup = () => {
54
+ window.removeEventListener("message", onMessage);
55
+ clearInterval(pollTimer);
56
+ };
57
+ const onMessage = (event) => {
58
+ if (event.origin !== window.location.origin)
59
+ return;
60
+ const data = event.data;
61
+ if (!data || data.type !== MESSAGE_TYPE)
62
+ return;
63
+ if (settled)
64
+ return;
65
+ settled = true;
66
+ cleanup();
67
+ if (data.error) {
68
+ reject(new Error(`Twitter auth error: ${data.error}`));
69
+ return;
70
+ }
71
+ if (!data.code) {
72
+ reject(new Error("No authorization code received from Twitter"));
73
+ return;
74
+ }
75
+ if (data.state !== state) {
76
+ reject(new Error("State mismatch - possible CSRF attack"));
77
+ return;
78
+ }
79
+ resolve({
80
+ code: data.code,
81
+ codeVerifier,
82
+ state,
83
+ });
84
+ };
85
+ const pollTimer = setInterval(() => {
86
+ if (popup.closed && !settled) {
87
+ settled = true;
88
+ cleanup();
89
+ reject(new Error("Twitter login popup was closed"));
90
+ }
91
+ }, 500);
92
+ window.addEventListener("message", onMessage);
93
+ });
94
+ }
@@ -0,0 +1,22 @@
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";
@@ -0,0 +1,23 @@
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";
@@ -0,0 +1,19 @@
1
+ export * from "./types.js";
2
+ export * from "./auth/config.js";
3
+ export * from "./auth/autoReconnect.js";
4
+ export * from "./auth/google.js";
5
+ export * from "./auth/twitter.js";
6
+ export * from "./modules/deposit.js";
7
+ export * from "./modules/withdraw.js";
8
+ export * from "./modules/marketData.js";
9
+ export * from "./modules/balanceQuery.js";
10
+ export * from "./modules/withdrawExecutor.js";
11
+ export * from "./policyAdapter.js";
12
+ export * from "./ui/SignInModal.js";
13
+ export * from "./ui/signInTypes.js";
14
+ export * from "./ui/WalletSelectionModal.js";
15
+ export * from "./ui/DepositModal.js";
16
+ export * from "./ui/WithdrawModal.js";
17
+ export * from "./ui/components/DropdownField.js";
18
+ export * from "./ui/components/DepositDetailsPanel.js";
19
+ export * from "./walletUtils.js";
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ export * from "./types.js";
2
+ export * from "./auth/config.js";
3
+ export * from "./auth/autoReconnect.js";
4
+ export * from "./auth/google.js";
5
+ export * from "./auth/twitter.js";
6
+ export * from "./modules/deposit.js";
7
+ export * from "./modules/withdraw.js";
8
+ export * from "./modules/marketData.js";
9
+ export * from "./modules/balanceQuery.js";
10
+ export * from "./modules/withdrawExecutor.js";
11
+ export * from "./policyAdapter.js";
12
+ export * from "./ui/SignInModal.js";
13
+ export * from "./ui/signInTypes.js";
14
+ export * from "./ui/WalletSelectionModal.js";
15
+ export * from "./ui/DepositModal.js";
16
+ export * from "./ui/WithdrawModal.js";
17
+ export * from "./ui/components/DropdownField.js";
18
+ export * from "./ui/components/DepositDetailsPanel.js";
19
+ export * from "./walletUtils.js";
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Merchant API client (BaseUrl: https://merchant.tomo.services)
3
+ * 基于 axios 封装,类型与文档一致。
4
+ */
5
+ import { type AxiosInstance } from "axios";
6
+ /** 通用 API 响应:所有接口统一格式 */
7
+ export interface ApiResponse<T> {
8
+ data: T | null;
9
+ code: number;
10
+ msg: string;
11
+ timestamp: number;
12
+ }
13
+ export interface TokenData {
14
+ symbol: string;
15
+ address: string;
16
+ decimals: number;
17
+ }
18
+ export interface ChainData {
19
+ chain_id: string;
20
+ network: string;
21
+ tokens: TokenData[];
22
+ }
23
+ export interface ChainsResponseData {
24
+ chains: ChainData[];
25
+ is_testnet: boolean;
26
+ }
27
+ export interface PlatformRegisterRequest {
28
+ platform_contract_address: string;
29
+ chain_id: string;
30
+ }
31
+ export interface PlatformRegisterResponseData {
32
+ deposit_address: string;
33
+ chain_id: string;
34
+ }
35
+ export type QuoteDirection = "deposit" | "withdraw";
36
+ export interface QuoteRequest {
37
+ direction: QuoteDirection;
38
+ chain_id: string;
39
+ token_address: string;
40
+ token_amount?: string;
41
+ usd1_amount?: string;
42
+ }
43
+ export interface QuoteResponseData {
44
+ token_address: string;
45
+ token_symbol: string;
46
+ token_decimals: number;
47
+ rate: string;
48
+ chain_id: number;
49
+ deposit_address?: string;
50
+ usd1_amount?: string;
51
+ token_amount?: string;
52
+ expires_at?: string;
53
+ }
54
+ export type DepositOrderStatus = "received" | "processing" | "completed" | "failed" | "funded";
55
+ export interface DepositOrderResponseData {
56
+ order_id: string;
57
+ status: DepositOrderStatus;
58
+ source_chain_id: string;
59
+ token_address: string;
60
+ token_amount?: string;
61
+ usd1_amount?: string;
62
+ deposit_address?: string;
63
+ source_tx_hash?: string;
64
+ created_at?: string;
65
+ updated_at?: string;
66
+ }
67
+ export type WithdrawOrderStatus = "pending" | "funded" | "processing" | "completed" | "failed" | "expired";
68
+ export interface WithdrawOrderResponseData {
69
+ order_id: string;
70
+ status: WithdrawOrderStatus;
71
+ one_time_address?: string;
72
+ chain_id: string;
73
+ usd1_amount: string;
74
+ /** 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
+ fee?: string;
76
+ target_chain_id: string;
77
+ target_address: string;
78
+ funding_tx_hash?: string;
79
+ dst_tx_hash?: string;
80
+ out_tx_hash?: string;
81
+ created_at?: string;
82
+ updated_at?: string;
83
+ }
84
+ export interface NativeSwapPayload {
85
+ chain_id: string;
86
+ token_address: string;
87
+ token_amount: string;
88
+ dst_chain_id: string;
89
+ dst_token_address: string;
90
+ recipient: string;
91
+ }
92
+ export interface PaymentPairData {
93
+ token_symbol: string;
94
+ token_amount: string;
95
+ token_address: string;
96
+ user_address: string;
97
+ chain_id: string;
98
+ }
99
+ export interface CreateOrderRequest {
100
+ intent_id: string;
101
+ order_type: "NATIVE_SWAP";
102
+ order_payload: NativeSwapPayload;
103
+ payment_pairs: PaymentPairData[];
104
+ }
105
+ export type PaymentSessionStatus = "CREATED" | "PENDING" | "FUNDED" | "CANCELED" | "CAPTURING" | "SUCCEED";
106
+ export interface PaymentSessionResponseData {
107
+ payment_session_id: string;
108
+ created_at?: string;
109
+ status: PaymentSessionStatus;
110
+ one_time_wallet_address: string;
111
+ amount: string;
112
+ currency: string;
113
+ user_address?: string;
114
+ network?: string;
115
+ token_symbol?: string;
116
+ chain_id?: string;
117
+ funding_transaction_hash?: string | null;
118
+ capturable_amount?: string;
119
+ captured_amount?: string;
120
+ }
121
+ export interface CreateOrderResponseData {
122
+ order_id: string;
123
+ payment_sessions: PaymentSessionResponseData[];
124
+ }
125
+ /**
126
+ * 配置 Merchant API 的 baseURL(可选,默认 https://merchant.tomo.services)
127
+ */
128
+ export declare function configureMerchantApi(): void;
129
+ /**
130
+ * 获取当前 axios 实例(用于自定义请求)
131
+ */
132
+ export declare function getMerchantApiClient(): AxiosInstance;
133
+ /** GET /chains - 查询支持的链和代币列表 */
134
+ export declare function getChains(): Promise<ChainsResponseData>;
135
+ /** POST /api/v1/platform - 注册平台合约并获取充值地址 */
136
+ export declare function registerPlatform(body: PlatformRegisterRequest): Promise<PlatformRegisterResponseData>;
137
+ /** POST /api/v1/quote - 统一报价(充值/提现) */
138
+ export declare function quote(body: QuoteRequest): Promise<QuoteResponseData>;
139
+ /** GET /api/v1/orders/deposit/{order_id} - 查询充值订单状态 */
140
+ export declare function getDepositOrder(orderId: string): Promise<DepositOrderResponseData>;
141
+ /** GET /api/v1/orders/withdraw/{order_id} - 查询提现订单状态 */
142
+ export declare function getWithdrawOrder(orderId: string): Promise<WithdrawOrderResponseData>;
143
+ /** POST /order - 创建 NATIVE_SWAP 提现订单 */
144
+ export declare function createOrder(body: CreateOrderRequest): Promise<CreateOrderResponseData>;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Merchant API client (BaseUrl: https://merchant.tomo.services)
3
+ * 基于 axios 封装,类型与文档一致。
4
+ */
5
+ import { getEnv } from "../utils/env.js";
6
+ import axios from "axios";
7
+ const DEFAULT_MERCHANT_BASE_URL = "https://merchant.tomo.services";
8
+ // ─── Axios 实例与请求封装 ─────────────────────────────────────────────────────
9
+ function createClient() {
10
+ const BASE_URL = getEnv("MERCHANT_BASE_URL") || DEFAULT_MERCHANT_BASE_URL;
11
+ return axios.create({
12
+ baseURL: BASE_URL,
13
+ timeout: 30000,
14
+ headers: { "Content-Type": "application/json" },
15
+ });
16
+ }
17
+ /** 解析通用响应并抛出业务错误 */
18
+ function unwrap(res) {
19
+ if (res.code !== 0 || res.data === null) {
20
+ throw new Error(res.msg || "API error");
21
+ }
22
+ return res.data;
23
+ }
24
+ // 单例 client,可通过 configureMerchantApi 修改 baseURL
25
+ let apiClient = createClient();
26
+ /**
27
+ * 配置 Merchant API 的 baseURL(可选,默认 https://merchant.tomo.services)
28
+ */
29
+ export function configureMerchantApi() {
30
+ apiClient = createClient();
31
+ }
32
+ /**
33
+ * 获取当前 axios 实例(用于自定义请求)
34
+ */
35
+ export function getMerchantApiClient() {
36
+ return apiClient;
37
+ }
38
+ // ─── 接口方法(均 try-catch,失败时返回 mock)──────────────────────────────────
39
+ /** GET /chains - 查询支持的链和代币列表 */
40
+ export async function getChains() {
41
+ try {
42
+ const { data } = await apiClient.get("/chains");
43
+ return unwrap(data);
44
+ }
45
+ catch {
46
+ throw new Error('Failed to get chains');
47
+ }
48
+ }
49
+ /** POST /api/v1/platform - 注册平台合约并获取充值地址 */
50
+ export async function registerPlatform(body) {
51
+ try {
52
+ const { data } = await apiClient.post("/api/v1/platform", body);
53
+ return unwrap(data);
54
+ }
55
+ catch {
56
+ throw new Error('Failed to register platform');
57
+ }
58
+ }
59
+ /** POST /api/v1/quote - 统一报价(充值/提现) */
60
+ export async function quote(body) {
61
+ const { data } = await apiClient.post("/api/v1/quote", body);
62
+ return unwrap(data);
63
+ }
64
+ /** GET /api/v1/orders/deposit/{order_id} - 查询充值订单状态 */
65
+ export async function getDepositOrder(orderId) {
66
+ try {
67
+ const { data } = await apiClient.get(`/api/v1/orders/deposit/${encodeURIComponent(orderId)}`);
68
+ return unwrap(data);
69
+ }
70
+ catch {
71
+ throw new Error('Failed to get deposit order');
72
+ }
73
+ }
74
+ /** GET /api/v1/orders/withdraw/{order_id} - 查询提现订单状态 */
75
+ export async function getWithdrawOrder(orderId) {
76
+ try {
77
+ const { data } = await apiClient.get(`/api/v1/orders/withdraw/${encodeURIComponent(orderId)}`);
78
+ return unwrap(data);
79
+ }
80
+ catch {
81
+ throw new Error('Failed to get withdraw order');
82
+ }
83
+ }
84
+ /** POST /order - 创建 NATIVE_SWAP 提现订单 */
85
+ export async function createOrder(body) {
86
+ try {
87
+ const { data } = await apiClient.post("/order", body);
88
+ return unwrap(data);
89
+ }
90
+ catch {
91
+ throw new Error('Failed to create order');
92
+ }
93
+ }
@@ -0,0 +1,20 @@
1
+ export interface Erc20BalanceResult {
2
+ raw: bigint;
3
+ formatted: string;
4
+ symbol: string;
5
+ }
6
+ /**
7
+ * Query any ERC-20 token balance via a raw JSON-RPC `eth_call`.
8
+ * No external library dependency – uses the global `fetch`.
9
+ */
10
+ export declare function fetchErc20Balance(rpcUrl: string, tokenAddress: string, walletAddress: string): Promise<bigint>;
11
+ export interface BscUsd1BalanceOptions {
12
+ rpcUrl?: string;
13
+ tokenAddress?: string;
14
+ decimals?: number;
15
+ }
16
+ /**
17
+ * Convenience wrapper – fetches the USD1 balance on BSC for a given wallet.
18
+ * All parameters have sensible defaults and can be overridden.
19
+ */
20
+ export declare function fetchBscUsd1Balance(walletAddress: string, options?: BscUsd1BalanceOptions): Promise<Erc20BalanceResult>;
@@ -0,0 +1,58 @@
1
+ import { testBsc, BSC_USD1_ADDRESS } from "../constants/chains.js";
2
+ const ERC20_BALANCE_OF_SELECTOR = "0x70a08231";
3
+ const USD1_DECIMALS = testBsc.nativeCurrencyDecimals;
4
+ const BSC_RPC_URL = testBsc.rpcUrls[0];
5
+ function padAddress(address) {
6
+ return address.toLowerCase().replace("0x", "").padStart(64, "0");
7
+ }
8
+ function formatUnits(value, decimals) {
9
+ const divisor = 10n ** BigInt(decimals);
10
+ const intPart = value / divisor;
11
+ const fracPart = value % divisor;
12
+ if (fracPart === 0n)
13
+ return intPart.toString();
14
+ const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
15
+ return `${intPart}.${fracStr}`;
16
+ }
17
+ /** 格式化为显示用 balance,保留小数点后 2 位 */
18
+ function formatBalanceDisplay(value, decimals) {
19
+ const full = formatUnits(value, decimals);
20
+ const n = Number(full);
21
+ return Number.isNaN(n) ? full : n.toFixed(2);
22
+ }
23
+ /**
24
+ * Query any ERC-20 token balance via a raw JSON-RPC `eth_call`.
25
+ * No external library dependency – uses the global `fetch`.
26
+ */
27
+ export async function fetchErc20Balance(rpcUrl, tokenAddress, walletAddress) {
28
+ const data = `${ERC20_BALANCE_OF_SELECTOR}${padAddress(walletAddress)}`;
29
+ const response = await fetch(rpcUrl, {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify({
33
+ jsonrpc: "2.0",
34
+ id: 1,
35
+ method: "eth_call",
36
+ params: [{ to: tokenAddress, data }, "latest"],
37
+ }),
38
+ });
39
+ const json = (await response.json());
40
+ if (json.error)
41
+ throw new Error(json.error.message ?? "RPC error");
42
+ return BigInt(json.result ?? "0x0");
43
+ }
44
+ /**
45
+ * Convenience wrapper – fetches the USD1 balance on BSC for a given wallet.
46
+ * All parameters have sensible defaults and can be overridden.
47
+ */
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;
52
+ const raw = await fetchErc20Balance(rpcUrl, tokenAddress, walletAddress);
53
+ return {
54
+ raw,
55
+ formatted: formatBalanceDisplay(raw, decimals),
56
+ symbol: "USD1",
57
+ };
58
+ }
@@ -0,0 +1,31 @@
1
+ import type { ChainInfo, CustodyAdapter, DepositAddressResult, MarketDataProvider, ModalController, QuoteResult, TokenInfo } from "../types.js";
2
+ export interface DepositModalConfig {
3
+ preferredToken?: string;
4
+ preferredChain?: string;
5
+ onStatusChange?: (status: DepositStatus) => void;
6
+ }
7
+ export type DepositStatus = {
8
+ phase: "idle";
9
+ } | {
10
+ phase: "address-issued";
11
+ depositId: string;
12
+ address: string;
13
+ } | {
14
+ phase: "confirming";
15
+ depositId: string;
16
+ } | {
17
+ phase: "settled";
18
+ depositId: string;
19
+ txHash?: string;
20
+ } | {
21
+ phase: "failed";
22
+ reason: string;
23
+ };
24
+ export interface DepositController extends ModalController<DepositModalConfig> {
25
+ readonly status: DepositStatus;
26
+ fetchTokens(): Promise<TokenInfo[]>;
27
+ fetchChains(token: string): Promise<ChainInfo[]>;
28
+ fetchQuote(token: string, chain: string, amount: string): Promise<QuoteResult>;
29
+ fetchDepositAddress(token: string, chain: string): Promise<DepositAddressResult>;
30
+ }
31
+ export declare const createDepositController: (custody: CustodyAdapter, marketData: MarketDataProvider) => DepositController;
@@ -0,0 +1,57 @@
1
+ import { sessionStore } from "@ab-org/sdk-core";
2
+ function requireSession() {
3
+ const session = sessionStore.getState().session;
4
+ if (!session)
5
+ throw new Error("Login required");
6
+ return session;
7
+ }
8
+ export const createDepositController = (custody, marketData) => {
9
+ let status = { phase: "idle" };
10
+ const notify = (next, cb) => {
11
+ status = next;
12
+ cb?.(status);
13
+ };
14
+ const open = async (config) => {
15
+ const session = requireSession();
16
+ const chain = config?.preferredChain ??
17
+ session.chainContext?.settlementChain ??
18
+ session.chain ??
19
+ "AB_CORE";
20
+ const token = config?.preferredToken ?? "USD1";
21
+ try {
22
+ const { address } = await marketData.getDepositAddress(token, chain);
23
+ const depositId = `${chain}:${token}:${Date.now()}`;
24
+ notify({ phase: "address-issued", depositId, address }, config?.onStatusChange);
25
+ notify({ phase: "confirming", depositId }, config?.onStatusChange);
26
+ const { status: finalStatus, txHash } = await custody.getDepositStatus(depositId);
27
+ notify(finalStatus === "SETTLED"
28
+ ? { phase: "settled", depositId, txHash }
29
+ : { phase: "failed", reason: finalStatus }, config?.onStatusChange);
30
+ }
31
+ catch (error) {
32
+ notify({ phase: "failed", reason: error.message }, config?.onStatusChange);
33
+ }
34
+ };
35
+ return {
36
+ get status() {
37
+ return status;
38
+ },
39
+ open,
40
+ fetchTokens() {
41
+ requireSession();
42
+ return marketData.getSupportedTokens("deposit");
43
+ },
44
+ fetchChains(token) {
45
+ requireSession();
46
+ return marketData.getSupportedChains(token, "deposit");
47
+ },
48
+ fetchQuote(token, chain, amount) {
49
+ requireSession();
50
+ return marketData.getQuote({ token, chain, amount, direction: "deposit" });
51
+ },
52
+ fetchDepositAddress(token, chain) {
53
+ requireSession();
54
+ return marketData.getDepositAddress(token, chain);
55
+ },
56
+ };
57
+ };
@@ -0,0 +1,8 @@
1
+ import type { MarketDataProvider } from "../types.js";
2
+ /**
3
+ * Market data provider backed by merchant API.
4
+ * - getSupportedTokens / getSupportedChains: from GET https://merchant.tomo.services/chains
5
+ * - getQuote: mock (no quote API yet)
6
+ * - getDepositAddress: from session (no deposit-address API in this service)
7
+ */
8
+ export declare function createMockMarketDataProvider(): MarketDataProvider;