@one_deploy/sdk 1.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/.turbo/turbo-build.log +0 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/dist/config/index.d.mts +74 -0
- package/dist/config/index.d.ts +74 -0
- package/dist/config/index.js +242 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +224 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/engine-5ndtBaCr.d.ts +1039 -0
- package/dist/engine-CrlhH0nw.d.mts +1039 -0
- package/dist/hooks/index.d.mts +56 -0
- package/dist/hooks/index.d.ts +56 -0
- package/dist/hooks/index.js +1360 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +1356 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +356 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.js +5068 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4949 -0
- package/dist/index.mjs.map +1 -0
- package/dist/price-CgqXPnT3.d.ts +13 -0
- package/dist/price-ClbLHHjv.d.mts +13 -0
- package/dist/providers/index.d.mts +121 -0
- package/dist/providers/index.d.ts +121 -0
- package/dist/providers/index.js +1642 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/index.mjs +1600 -0
- package/dist/providers/index.mjs.map +1 -0
- package/dist/react-native.d.mts +120 -0
- package/dist/react-native.d.ts +120 -0
- package/dist/react-native.js +1792 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1755 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/services/index.d.mts +85 -0
- package/dist/services/index.d.ts +85 -0
- package/dist/services/index.js +1466 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/index.mjs +1458 -0
- package/dist/services/index.mjs.map +1 -0
- package/dist/types/index.d.mts +759 -0
- package/dist/types/index.d.ts +759 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +36 -0
- package/dist/utils/index.d.ts +36 -0
- package/dist/utils/index.js +164 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +142 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +101 -0
- package/src/components/OneConnectButton.tsx +143 -0
- package/src/components/OneNFTGallery.tsx +324 -0
- package/src/components/OneOfframpWidget.tsx +660 -0
- package/src/components/OneOnrampWidget.tsx +596 -0
- package/src/components/OnePayWidget.tsx +160 -0
- package/src/components/OneReceiveWidget.tsx +272 -0
- package/src/components/OneSendWidget.tsx +248 -0
- package/src/components/OneSwapWidget.tsx +715 -0
- package/src/components/OneTransactionButton.tsx +150 -0
- package/src/components/OneWalletBalance.tsx +354 -0
- package/src/components/index.ts +24 -0
- package/src/config/index.ts +299 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTokenPrice.ts +162 -0
- package/src/hooks/useWalletBalance.ts +98 -0
- package/src/index.ts +193 -0
- package/src/providers/OneProvider.tsx +452 -0
- package/src/providers/ThirdwebProvider.tsx +203 -0
- package/src/providers/index.ts +26 -0
- package/src/react-native.ts +378 -0
- package/src/services/engine.ts +1854 -0
- package/src/services/index.ts +30 -0
- package/src/services/price.ts +164 -0
- package/src/services/supabase.ts +180 -0
- package/src/types/index.ts +887 -0
- package/src/utils/index.ts +200 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +25 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE SDK Configuration
|
|
3
|
+
*
|
|
4
|
+
* Chain data is fetched from ONE Engine API - no duplicates needed here.
|
|
5
|
+
* Engine is the single source of truth for 200+ EVM chains.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ChainConfig } from '../types';
|
|
9
|
+
|
|
10
|
+
// ===== SDK Configuration =====
|
|
11
|
+
export interface OneConfig {
|
|
12
|
+
// ONE Engine (required)
|
|
13
|
+
oneEngineUrl: string;
|
|
14
|
+
oneClientId: string;
|
|
15
|
+
oneSecretKey?: string; // Only for backend usage
|
|
16
|
+
|
|
17
|
+
// Optional: Direct Supabase access (for realtime subscriptions)
|
|
18
|
+
supabaseUrl?: string;
|
|
19
|
+
supabaseAnonKey?: string;
|
|
20
|
+
|
|
21
|
+
// Deprecated: Use ONE Engine instead
|
|
22
|
+
thirdwebClientId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let config: OneConfig | null = null;
|
|
26
|
+
|
|
27
|
+
export function initOneSDK(options: OneConfig): void {
|
|
28
|
+
if (!options.oneEngineUrl) {
|
|
29
|
+
throw new Error('oneEngineUrl is required');
|
|
30
|
+
}
|
|
31
|
+
if (!options.oneClientId) {
|
|
32
|
+
throw new Error('oneClientId is required');
|
|
33
|
+
}
|
|
34
|
+
config = options;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getConfig(): OneConfig {
|
|
38
|
+
if (!config) {
|
|
39
|
+
throw new Error('ONE SDK not initialized. Call initOneSDK() first.');
|
|
40
|
+
}
|
|
41
|
+
return config;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isInitialized(): boolean {
|
|
45
|
+
return config !== null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getEngineUrl(): string {
|
|
49
|
+
return config?.oneEngineUrl || process.env.NEXT_PUBLIC_ONE_ENGINE_URL || 'http://localhost:4000/api';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ===== Chain Data (Fetched from Engine) =====
|
|
53
|
+
|
|
54
|
+
// Cache for chain data
|
|
55
|
+
let chainsCache: ChainConfig[] | null = null;
|
|
56
|
+
let chainsCacheTimestamp = 0;
|
|
57
|
+
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Fetch chains from ONE Engine API
|
|
61
|
+
* Engine is the source of truth for 200+ EVM chains
|
|
62
|
+
*/
|
|
63
|
+
export async function fetchChains(options?: {
|
|
64
|
+
category?: 'mainnet' | 'l2' | 'testnet' | 'gaming' | 'recommended';
|
|
65
|
+
smartWallet?: boolean;
|
|
66
|
+
limit?: number;
|
|
67
|
+
}): Promise<ChainConfig[]> {
|
|
68
|
+
const engineUrl = getEngineUrl();
|
|
69
|
+
const params = new URLSearchParams();
|
|
70
|
+
|
|
71
|
+
if (options?.category) params.set('category', options.category);
|
|
72
|
+
if (options?.smartWallet) params.set('smartWallet', 'true');
|
|
73
|
+
if (options?.limit) params.set('limit', options.limit.toString());
|
|
74
|
+
|
|
75
|
+
const response = await fetch(`${engineUrl}/v1/chains?${params}`);
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
|
|
78
|
+
if (!data.success) {
|
|
79
|
+
throw new Error(data.error?.message || 'Failed to fetch chains');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Transform Engine response to SDK ChainConfig format
|
|
83
|
+
return data.data.chains.map((chain: any) => ({
|
|
84
|
+
id: chain.id,
|
|
85
|
+
name: chain.name,
|
|
86
|
+
shortName: chain.shortName || chain.slug || chain.name.toLowerCase(),
|
|
87
|
+
icon: getChainIcon(chain.id),
|
|
88
|
+
nativeCurrency: chain.nativeCurrency,
|
|
89
|
+
rpcUrls: Array.isArray(chain.rpc) ? chain.rpc : [chain.rpc],
|
|
90
|
+
blockExplorerUrls: chain.blockExplorer ? [chain.blockExplorer] : [],
|
|
91
|
+
testnet: chain.testnet,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all supported chains (cached)
|
|
97
|
+
*/
|
|
98
|
+
export async function getChains(): Promise<ChainConfig[]> {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
if (chainsCache && (now - chainsCacheTimestamp) < CACHE_TTL) {
|
|
101
|
+
return chainsCache;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
chainsCache = await fetchChains({ limit: 200 });
|
|
105
|
+
chainsCacheTimestamp = now;
|
|
106
|
+
return chainsCache;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get chain by ID
|
|
111
|
+
*/
|
|
112
|
+
export async function getChainById(chainId: number): Promise<ChainConfig | undefined> {
|
|
113
|
+
const chains = await getChains();
|
|
114
|
+
return chains.find(c => c.id === chainId);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get chain by name/shortName
|
|
119
|
+
*/
|
|
120
|
+
export async function getChainByName(name: string): Promise<ChainConfig | undefined> {
|
|
121
|
+
const chains = await getChains();
|
|
122
|
+
const lowerName = name.toLowerCase();
|
|
123
|
+
return chains.find(
|
|
124
|
+
c => c.name.toLowerCase() === lowerName || c.shortName.toLowerCase() === lowerName
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get recommended chains for UI display
|
|
130
|
+
*/
|
|
131
|
+
export async function getRecommendedChains(): Promise<ChainConfig[]> {
|
|
132
|
+
return fetchChains({ category: 'recommended', limit: 10 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get chains with smart wallet support
|
|
137
|
+
*/
|
|
138
|
+
export async function getSmartWalletChains(): Promise<ChainConfig[]> {
|
|
139
|
+
return fetchChains({ smartWallet: true, limit: 50 });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ===== Default Chain Constants =====
|
|
143
|
+
// These are commonly used chain IDs, but full data should be fetched from Engine
|
|
144
|
+
|
|
145
|
+
export const CHAIN_IDS = {
|
|
146
|
+
ETHEREUM: 1,
|
|
147
|
+
POLYGON: 137,
|
|
148
|
+
BSC: 56,
|
|
149
|
+
ARBITRUM: 42161,
|
|
150
|
+
OPTIMISM: 10,
|
|
151
|
+
BASE: 8453,
|
|
152
|
+
AVALANCHE: 43114,
|
|
153
|
+
ZKSYNC: 324,
|
|
154
|
+
LINEA: 59144,
|
|
155
|
+
SCROLL: 534352,
|
|
156
|
+
BLAST: 81457,
|
|
157
|
+
// Testnets
|
|
158
|
+
SEPOLIA: 11155111,
|
|
159
|
+
BASE_SEPOLIA: 84532,
|
|
160
|
+
ARBITRUM_SEPOLIA: 421614,
|
|
161
|
+
} as const;
|
|
162
|
+
|
|
163
|
+
export const DEFAULT_CHAIN_ID = CHAIN_IDS.BASE;
|
|
164
|
+
|
|
165
|
+
// ===== Chain Icons (Simple emoji fallback) =====
|
|
166
|
+
const CHAIN_ICONS: Record<number, string> = {
|
|
167
|
+
1: '\u229F', // Ethereum
|
|
168
|
+
137: '\uD83D\uDFE3', // Polygon
|
|
169
|
+
56: '\uD83D\uDFE1', // BSC
|
|
170
|
+
42161: '\uD83D\uDD35', // Arbitrum
|
|
171
|
+
10: '\uD83D\uDD34', // Optimism
|
|
172
|
+
8453: '\uD83D\uDD37', // Base
|
|
173
|
+
43114: '\uD83D\uDD3A', // Avalanche
|
|
174
|
+
324: '\u26A1', // zkSync
|
|
175
|
+
59144: '\uD83D\uDD39', // Linea
|
|
176
|
+
534352: '\uD83D\uDCDC', // Scroll
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
function getChainIcon(chainId: number): string {
|
|
180
|
+
return CHAIN_ICONS[chainId] || '\uD83D\uDD17';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ===== Token Names Mapping =====
|
|
184
|
+
// Common token symbols to human-readable names
|
|
185
|
+
export const TOKEN_NAMES: Record<string, string> = {
|
|
186
|
+
ETH: 'Ethereum',
|
|
187
|
+
BTC: 'Bitcoin',
|
|
188
|
+
BNB: 'BNB',
|
|
189
|
+
MATIC: 'Polygon',
|
|
190
|
+
POL: 'Polygon',
|
|
191
|
+
AVAX: 'Avalanche',
|
|
192
|
+
USDT: 'Tether',
|
|
193
|
+
USDC: 'USD Coin',
|
|
194
|
+
DAI: 'Dai',
|
|
195
|
+
WBTC: 'Wrapped Bitcoin',
|
|
196
|
+
WETH: 'Wrapped Ether',
|
|
197
|
+
ARB: 'Arbitrum',
|
|
198
|
+
OP: 'Optimism',
|
|
199
|
+
LINK: 'Chainlink',
|
|
200
|
+
UNI: 'Uniswap',
|
|
201
|
+
AAVE: 'Aave',
|
|
202
|
+
CRV: 'Curve',
|
|
203
|
+
MKR: 'Maker',
|
|
204
|
+
SNX: 'Synthetix',
|
|
205
|
+
COMP: 'Compound',
|
|
206
|
+
SUSHI: 'SushiSwap',
|
|
207
|
+
YFI: 'Yearn Finance',
|
|
208
|
+
SOL: 'Solana',
|
|
209
|
+
DOT: 'Polkadot',
|
|
210
|
+
ATOM: 'Cosmos',
|
|
211
|
+
NEAR: 'Near Protocol',
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// ===== CoinGecko ID Mapping =====
|
|
215
|
+
// For price lookups via CoinGecko API
|
|
216
|
+
export const COINGECKO_IDS: Record<string, string> = {
|
|
217
|
+
ETH: 'ethereum',
|
|
218
|
+
BTC: 'bitcoin',
|
|
219
|
+
BNB: 'binancecoin',
|
|
220
|
+
MATIC: 'matic-network',
|
|
221
|
+
POL: 'matic-network',
|
|
222
|
+
AVAX: 'avalanche-2',
|
|
223
|
+
USDT: 'tether',
|
|
224
|
+
USDC: 'usd-coin',
|
|
225
|
+
DAI: 'dai',
|
|
226
|
+
WBTC: 'wrapped-bitcoin',
|
|
227
|
+
WETH: 'weth',
|
|
228
|
+
ARB: 'arbitrum',
|
|
229
|
+
OP: 'optimism',
|
|
230
|
+
LINK: 'chainlink',
|
|
231
|
+
UNI: 'uniswap',
|
|
232
|
+
AAVE: 'aave',
|
|
233
|
+
SOL: 'solana',
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// ===== Backwards Compatibility =====
|
|
237
|
+
// @deprecated - Use getChains() or fetch from Engine API
|
|
238
|
+
// These static configs are kept for backwards compatibility only
|
|
239
|
+
|
|
240
|
+
export const CHAIN_CONFIGS: Record<string, ChainConfig> = {
|
|
241
|
+
ethereum: {
|
|
242
|
+
id: 1,
|
|
243
|
+
name: 'Ethereum',
|
|
244
|
+
shortName: 'ETH',
|
|
245
|
+
icon: '\u229F',
|
|
246
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
247
|
+
rpcUrls: ['https://ethereum.rpc.thirdweb.com'],
|
|
248
|
+
blockExplorerUrls: ['https://etherscan.io'],
|
|
249
|
+
testnet: false,
|
|
250
|
+
},
|
|
251
|
+
polygon: {
|
|
252
|
+
id: 137,
|
|
253
|
+
name: 'Polygon',
|
|
254
|
+
shortName: 'MATIC',
|
|
255
|
+
icon: '\uD83D\uDFE3',
|
|
256
|
+
nativeCurrency: { name: 'POL', symbol: 'POL', decimals: 18 },
|
|
257
|
+
rpcUrls: ['https://polygon.rpc.thirdweb.com'],
|
|
258
|
+
blockExplorerUrls: ['https://polygonscan.com'],
|
|
259
|
+
testnet: false,
|
|
260
|
+
},
|
|
261
|
+
base: {
|
|
262
|
+
id: 8453,
|
|
263
|
+
name: 'Base',
|
|
264
|
+
shortName: 'BASE',
|
|
265
|
+
icon: '\uD83D\uDD37',
|
|
266
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
267
|
+
rpcUrls: ['https://base.rpc.thirdweb.com'],
|
|
268
|
+
blockExplorerUrls: ['https://basescan.org'],
|
|
269
|
+
testnet: false,
|
|
270
|
+
},
|
|
271
|
+
arbitrum: {
|
|
272
|
+
id: 42161,
|
|
273
|
+
name: 'Arbitrum One',
|
|
274
|
+
shortName: 'ARB',
|
|
275
|
+
icon: '\uD83D\uDD35',
|
|
276
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
277
|
+
rpcUrls: ['https://arbitrum.rpc.thirdweb.com'],
|
|
278
|
+
blockExplorerUrls: ['https://arbiscan.io'],
|
|
279
|
+
testnet: false,
|
|
280
|
+
},
|
|
281
|
+
optimism: {
|
|
282
|
+
id: 10,
|
|
283
|
+
name: 'Optimism',
|
|
284
|
+
shortName: 'OP',
|
|
285
|
+
icon: '\uD83D\uDD34',
|
|
286
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
287
|
+
rpcUrls: ['https://optimism.rpc.thirdweb.com'],
|
|
288
|
+
blockExplorerUrls: ['https://optimistic.etherscan.io'],
|
|
289
|
+
testnet: false,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// @deprecated - Use getChains() to get all supported chains
|
|
294
|
+
export const SUPPORTED_CHAINS = Object.keys(CHAIN_CONFIGS);
|
|
295
|
+
|
|
296
|
+
// @deprecated - Use getChainByName() instead
|
|
297
|
+
export function getChainConfig(chain: string): ChainConfig | undefined {
|
|
298
|
+
return CHAIN_CONFIGS[chain.toLowerCase()];
|
|
299
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { createOneEngineClient } from '../services/engine';
|
|
3
|
+
import type { TokenPrice } from '../types';
|
|
4
|
+
|
|
5
|
+
interface UseTokenPriceOptions {
|
|
6
|
+
autoRefresh?: boolean;
|
|
7
|
+
refreshInterval?: number;
|
|
8
|
+
engineUrl?: string;
|
|
9
|
+
clientId?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface UseTokenPriceReturn {
|
|
13
|
+
price: TokenPrice | null;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
error: string | null;
|
|
16
|
+
refetch: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook for fetching token price via ONE Engine
|
|
21
|
+
*
|
|
22
|
+
* Note: If using OneProvider, prefer useOneEngine().getTokenPrices() instead.
|
|
23
|
+
* This hook is for standalone usage outside of OneProvider.
|
|
24
|
+
*/
|
|
25
|
+
export function useTokenPrice(
|
|
26
|
+
symbol: string,
|
|
27
|
+
options: UseTokenPriceOptions = {}
|
|
28
|
+
): UseTokenPriceReturn {
|
|
29
|
+
const { autoRefresh = false, refreshInterval = 30000, engineUrl, clientId } = options;
|
|
30
|
+
|
|
31
|
+
const [price, setPrice] = useState<TokenPrice | null>(null);
|
|
32
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
33
|
+
const [error, setError] = useState<string | null>(null);
|
|
34
|
+
|
|
35
|
+
const engineRef = useRef(createOneEngineClient({
|
|
36
|
+
baseUrl: engineUrl,
|
|
37
|
+
clientId: clientId,
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const fetchPrice = useCallback(async () => {
|
|
41
|
+
if (!symbol) return;
|
|
42
|
+
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const result = await engineRef.current.getTokenPrices([symbol]);
|
|
48
|
+
if (result.success && result.data && result.data[symbol]) {
|
|
49
|
+
const priceData = result.data[symbol];
|
|
50
|
+
setPrice({
|
|
51
|
+
symbol,
|
|
52
|
+
price: priceData.price,
|
|
53
|
+
change24h: priceData.change24h,
|
|
54
|
+
changePercent24h: priceData.change24h, // Same as change24h for percentage
|
|
55
|
+
marketCap: priceData.marketCap,
|
|
56
|
+
volume24h: 0, // Not provided by engine
|
|
57
|
+
updatedAt: new Date().toISOString(),
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
setError(result.error?.message || 'Failed to fetch price');
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch price');
|
|
64
|
+
} finally {
|
|
65
|
+
setIsLoading(false);
|
|
66
|
+
}
|
|
67
|
+
}, [symbol]);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
fetchPrice();
|
|
71
|
+
}, [fetchPrice]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!autoRefresh) return;
|
|
75
|
+
|
|
76
|
+
const interval = setInterval(fetchPrice, refreshInterval);
|
|
77
|
+
return () => clearInterval(interval);
|
|
78
|
+
}, [autoRefresh, refreshInterval, fetchPrice]);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
price,
|
|
82
|
+
isLoading,
|
|
83
|
+
error,
|
|
84
|
+
refetch: fetchPrice,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Hook for multiple token prices
|
|
89
|
+
interface UseTokenPricesReturn {
|
|
90
|
+
prices: Record<string, TokenPrice>;
|
|
91
|
+
isLoading: boolean;
|
|
92
|
+
error: string | null;
|
|
93
|
+
refetch: () => Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function useTokenPrices(
|
|
97
|
+
symbols: string[],
|
|
98
|
+
options: UseTokenPriceOptions = {}
|
|
99
|
+
): UseTokenPricesReturn {
|
|
100
|
+
const { autoRefresh = false, refreshInterval = 30000, engineUrl, clientId } = options;
|
|
101
|
+
|
|
102
|
+
const [prices, setPrices] = useState<Record<string, TokenPrice>>({});
|
|
103
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
104
|
+
const [error, setError] = useState<string | null>(null);
|
|
105
|
+
|
|
106
|
+
const engineRef = useRef(createOneEngineClient({
|
|
107
|
+
baseUrl: engineUrl,
|
|
108
|
+
clientId: clientId,
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
const fetchPrices = useCallback(async () => {
|
|
112
|
+
if (symbols.length === 0) return;
|
|
113
|
+
|
|
114
|
+
setIsLoading(true);
|
|
115
|
+
setError(null);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const result = await engineRef.current.getTokenPrices(symbols);
|
|
119
|
+
if (result.success && result.data) {
|
|
120
|
+
const priceMap: Record<string, TokenPrice> = {};
|
|
121
|
+
for (const sym of symbols) {
|
|
122
|
+
if (result.data[sym]) {
|
|
123
|
+
priceMap[sym] = {
|
|
124
|
+
symbol: sym,
|
|
125
|
+
price: result.data[sym].price,
|
|
126
|
+
change24h: result.data[sym].change24h,
|
|
127
|
+
changePercent24h: result.data[sym].change24h,
|
|
128
|
+
marketCap: result.data[sym].marketCap,
|
|
129
|
+
volume24h: 0,
|
|
130
|
+
updatedAt: new Date().toISOString(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
setPrices(priceMap);
|
|
135
|
+
} else {
|
|
136
|
+
setError(result.error?.message || 'Failed to fetch prices');
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch prices');
|
|
140
|
+
} finally {
|
|
141
|
+
setIsLoading(false);
|
|
142
|
+
}
|
|
143
|
+
}, [symbols]);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
fetchPrices();
|
|
147
|
+
}, [fetchPrices]);
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (!autoRefresh) return;
|
|
151
|
+
|
|
152
|
+
const interval = setInterval(fetchPrices, refreshInterval);
|
|
153
|
+
return () => clearInterval(interval);
|
|
154
|
+
}, [autoRefresh, refreshInterval, fetchPrices]);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
prices,
|
|
158
|
+
isLoading,
|
|
159
|
+
error,
|
|
160
|
+
refetch: fetchPrices,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { createOneEngineClient, type EngineWalletBalance } from '../services/engine';
|
|
3
|
+
import type { Token } from '../types';
|
|
4
|
+
|
|
5
|
+
interface UseWalletBalanceOptions {
|
|
6
|
+
chains?: number[];
|
|
7
|
+
autoRefresh?: boolean;
|
|
8
|
+
refreshInterval?: number; // in milliseconds
|
|
9
|
+
engineUrl?: string;
|
|
10
|
+
clientId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface UseWalletBalanceReturn {
|
|
14
|
+
balance: EngineWalletBalance | null;
|
|
15
|
+
tokens: Token[];
|
|
16
|
+
totalUsd: number;
|
|
17
|
+
change24h: number;
|
|
18
|
+
changePercent24h: number;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
error: string | null;
|
|
21
|
+
refetch: () => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hook for fetching wallet balance via ONE Engine
|
|
26
|
+
*
|
|
27
|
+
* Note: If using OneProvider, prefer useOneWallet() instead for better integration.
|
|
28
|
+
* This hook is for standalone usage outside of OneProvider.
|
|
29
|
+
*/
|
|
30
|
+
export function useWalletBalance(
|
|
31
|
+
walletAddress: string | null,
|
|
32
|
+
options: UseWalletBalanceOptions = {}
|
|
33
|
+
): UseWalletBalanceReturn {
|
|
34
|
+
const {
|
|
35
|
+
chains,
|
|
36
|
+
autoRefresh = false,
|
|
37
|
+
refreshInterval = 60000, // 1 minute
|
|
38
|
+
engineUrl,
|
|
39
|
+
clientId,
|
|
40
|
+
} = options;
|
|
41
|
+
|
|
42
|
+
const [balance, setBalance] = useState<EngineWalletBalance | null>(null);
|
|
43
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
44
|
+
const [error, setError] = useState<string | null>(null);
|
|
45
|
+
|
|
46
|
+
// Create engine client once
|
|
47
|
+
const engineRef = useRef(createOneEngineClient({
|
|
48
|
+
baseUrl: engineUrl,
|
|
49
|
+
clientId: clientId,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const fetchBalance = useCallback(async () => {
|
|
53
|
+
if (!walletAddress) {
|
|
54
|
+
setBalance(null);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setIsLoading(true);
|
|
59
|
+
setError(null);
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const result = await engineRef.current.getWalletBalance(walletAddress, chains);
|
|
63
|
+
if (result.success && result.data) {
|
|
64
|
+
setBalance(result.data);
|
|
65
|
+
} else {
|
|
66
|
+
setError(result.error?.message || 'Failed to fetch balance');
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch balance');
|
|
70
|
+
} finally {
|
|
71
|
+
setIsLoading(false);
|
|
72
|
+
}
|
|
73
|
+
}, [walletAddress, chains]);
|
|
74
|
+
|
|
75
|
+
// Initial fetch
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
fetchBalance();
|
|
78
|
+
}, [fetchBalance]);
|
|
79
|
+
|
|
80
|
+
// Auto-refresh
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!autoRefresh || !walletAddress) return;
|
|
83
|
+
|
|
84
|
+
const interval = setInterval(fetchBalance, refreshInterval);
|
|
85
|
+
return () => clearInterval(interval);
|
|
86
|
+
}, [autoRefresh, refreshInterval, fetchBalance, walletAddress]);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
balance,
|
|
90
|
+
tokens: balance?.tokens || [],
|
|
91
|
+
totalUsd: balance?.totalUsd || 0,
|
|
92
|
+
change24h: balance?.change24h || 0,
|
|
93
|
+
changePercent24h: balance?.changePercent24h || 0,
|
|
94
|
+
isLoading,
|
|
95
|
+
error,
|
|
96
|
+
refetch: fetchBalance,
|
|
97
|
+
};
|
|
98
|
+
}
|