@jimmygu/sfa-sdk-test 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/README.md ADDED
@@ -0,0 +1,1003 @@
1
+ # StableFlow AI SDK - TypeScript
2
+
3
+ A powerful TypeScript SDK for seamless cross-chain token swaps. Built with type safety in mind, this SDK enables developers to easily integrate cross-chain swapping functionality into their applications with minimal setup.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js >= 16
8
+ - npm / yarn / pnpm
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Using npm
14
+ npm install stableflow-ai-sdk
15
+
16
+ # Using yarn
17
+ yarn add stableflow-ai-sdk
18
+
19
+ # Using pnpm
20
+ pnpm add stableflow-ai-sdk
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { OpenAPI, SFA, tokens, EVMWallet, setRpcUrls } from 'stableflow-ai-sdk';
27
+ import { ethers } from 'ethers';
28
+
29
+ // Initialize the API client
30
+ OpenAPI.BASE = 'https://api.stableflow.ai';
31
+ OpenAPI.TOKEN = "your-JSON-Web-Token";
32
+
33
+ // (Optional) Configure custom RPC endpoints
34
+ setRpcUrls({
35
+ "eth": ["https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"],
36
+ "arb": ["https://arbitrum-one-rpc.publicnode.com"],
37
+ });
38
+
39
+ // Get wallet instance (example with EVM)
40
+ const provider = new ethers.BrowserProvider(window.ethereum);
41
+ const signer = await provider.getSigner();
42
+ const wallet = new EVMWallet(provider, signer);
43
+
44
+ // Get token configurations
45
+ const fromToken = tokens.find(t => t.chainName === 'Ethereum' && t.symbol === 'USDT');
46
+ const toToken = tokens.find(t => t.chainName === 'Arbitrum' && t.symbol === 'USDT');
47
+
48
+ // Get quotes from all bridge services
49
+ const quotes = await SFA.getAllQuote({
50
+ dry: false,
51
+ minInputAmount: "0.1",
52
+ prices: {},
53
+ fromToken: fromToken!,
54
+ toToken: toToken!,
55
+ wallet: wallet,
56
+ recipient: '0x...', // recipient address
57
+ refundTo: '0x...', // refund address
58
+ amountWei: ethers.parseUnits('100', fromToken!.decimals).toString(),
59
+ slippageTolerance: 0.5, // 0.5%
60
+ // Optional
61
+ oneclickParams: {
62
+ appFees: [
63
+ {
64
+ // your fee collection address
65
+ recipient: "stableflow.near",
66
+ // Fee rate, as a percentage of the amount. 100 = 1%, 1 = 0.01%
67
+ fee: 100,
68
+ },
69
+ ],
70
+ },
71
+ });
72
+
73
+ // Select the best quote and send transaction
74
+ const selectedQuote = quotes.find(q => q.quote && !q.error);
75
+ if (selectedQuote && selectedQuote.quote) {
76
+ const txHash = await SFA.send(selectedQuote.serviceType, {
77
+ wallet: wallet,
78
+ quote: selectedQuote.quote,
79
+ });
80
+
81
+ // Check transaction status
82
+ const status = await SFA.getStatus(selectedQuote.serviceType, {
83
+ hash: txHash,
84
+ depositAddress: selectedQuote.quote.depositAddress,
85
+ });
86
+ }
87
+ ```
88
+
89
+ ## Authentication
90
+
91
+ The StableFlow AI API requires JWT authentication for all endpoints.
92
+
93
+ ### Getting Your JWT Token
94
+
95
+ To use the SDK, you need to apply for a JWT token:
96
+
97
+ 1. Visit [https://app.stableflow.ai/apply](https://app.stableflow.ai/apply)
98
+ 2. Submit an application form for API access
99
+ 3. Once approved, you will receive your JWT token
100
+
101
+ ### Using Your Token
102
+
103
+ ```typescript
104
+ // Set your JWT token
105
+ OpenAPI.TOKEN = 'your-JSON-Web-Token-here';
106
+ ```
107
+
108
+ ## Core API Methods
109
+
110
+ ### `getAllQuote` - Get Quotes from All Bridge Services
111
+
112
+ Retrieves quotes from all supported bridge services (OneClick, CCTP, USDT0) in parallel. Returns an array of quotes with their corresponding service types, allowing users to compare and select the best route.
113
+
114
+ ```typescript
115
+ const quotes = await SFA.getAllQuote({
116
+ singleService?: ServiceType, // Optional: query specific service only
117
+ dry?: boolean, // Set to true for testing without deposit address
118
+ minInputAmount?: string, // Minimum input amount (default: "1")
119
+ prices: Record<string, string>, // Token prices
120
+ fromToken: TokenConfig, // Source token configuration
121
+ toToken: TokenConfig, // Destination token configuration
122
+ wallet: WalletConfig, // Wallet instance (EVMWallet, SolanaWallet, etc.)
123
+ recipient: string, // Recipient address on destination chain
124
+ refundTo: string, // Refund address on source chain
125
+ amountWei: string, // Amount in smallest units (wei/satoshi/etc.)
126
+ slippageTolerance: number, // Slippage tolerance percentage (e.g., 0.5 for 0.5%)
127
+ oneclickParams?: {
128
+ // Custom fee rates
129
+ appFees?: { recipient: string; fee: number; }[];
130
+ // default is EXACT_INPUT
131
+ swapType?: "EXACT_INPUT" | "EXACT_OUTPUT";
132
+ };
133
+ });
134
+ ```
135
+
136
+ **Returns**: `Promise<Array<{ serviceType: ServiceType; quote?: any; error?: string }>>`
137
+
138
+ **Example**:
139
+
140
+ ```typescript
141
+ import { SFA, tokens, EVMWallet, GetAllQuoteParams } from 'stableflow-ai-sdk';
142
+ import { ethers } from 'ethers';
143
+
144
+ const provider = new ethers.BrowserProvider(window.ethereum);
145
+ const signer = await provider.getSigner();
146
+ const wallet = new EVMWallet(provider, signer);
147
+
148
+ const fromToken = tokens.find(t => t.contractAddress === '0x...');
149
+ const toToken = tokens.find(t => t.contractAddress === '0x...');
150
+
151
+ const quotes = await SFA.getAllQuote({
152
+ dry: false,
153
+ prices: {},
154
+ fromToken: fromToken!,
155
+ toToken: toToken!,
156
+ wallet: wallet,
157
+ recipient: '0x1234...',
158
+ refundTo: '0x5678...',
159
+ amountWei: ethers.parseUnits('100', fromToken!.decimals).toString(),
160
+ slippageTolerance: 0.5,
161
+ });
162
+
163
+ // Filter valid quotes
164
+ const validQuotes = quotes.filter(q => q.quote && !q.error);
165
+ console.log('Available routes:', validQuotes.map(q => q.serviceType));
166
+ ```
167
+
168
+ **Response Structure**:
169
+
170
+ ```typescript
171
+ [
172
+ {
173
+ serviceType: "oneclick",
174
+ quote: {
175
+ quote: QuoteResponse,
176
+ quoteParam: {...},
177
+ sendParam: {...},
178
+ depositAddress: "0x...",
179
+ needApprove: boolean,
180
+ approveSpender: "0x...",
181
+ fees: {...}
182
+ }
183
+ },
184
+ {
185
+ serviceType: "cctp",
186
+ quote: {...},
187
+ error: undefined
188
+ },
189
+ {
190
+ serviceType: "usdt0",
191
+ error: "Amount exceeds max"
192
+ }
193
+ ]
194
+ ```
195
+
196
+ ### `send` - Execute Transaction
197
+
198
+ Executes the transaction using the specified bridge service based on the service type. This method handles token approval (if needed) and submits the transaction to the blockchain.
199
+
200
+ ```typescript
201
+ const txHash = await SFA.send(
202
+ serviceType: ServiceType, // "oneclick" | "cctp" | "usdt0"
203
+ {
204
+ wallet: WalletConfig, // Wallet instance
205
+ quote: any, // Quote object from getAllQuote response
206
+ }
207
+ );
208
+ ```
209
+
210
+ **Returns**: `Promise<string>` - Transaction hash or signature
211
+
212
+ **Example**:
213
+
214
+ ```typescript
215
+ // After getting quotes and selecting one
216
+ const selectedQuote = quotes.find(q => q.quote && !q.error);
217
+
218
+ if (selectedQuote && selectedQuote.quote) {
219
+ // Check if approval is needed
220
+ if (selectedQuote.quote.needApprove) {
221
+ await wallet.approve({
222
+ contractAddress: selectedQuote.quote.quoteParam.fromToken.contractAddress,
223
+ spender: selectedQuote.quote.approveSpender,
224
+ amountWei: selectedQuote.quote.quoteParam.amountWei,
225
+ });
226
+ }
227
+
228
+ // Send the transaction
229
+ const txHash = await SFA.send(selectedQuote.serviceType, {
230
+ wallet: wallet,
231
+ quote: selectedQuote.quote,
232
+ });
233
+
234
+ console.log('Transaction hash:', txHash);
235
+ }
236
+ ```
237
+
238
+ **Note**: The `send` method automatically submits the transaction hash to the StableFlow service for tracking. You don't need to call `submitDepositTx` separately.
239
+
240
+ ### `getStatus` - Check Transaction Status
241
+
242
+ Queries the transaction status from the specified bridge service. Returns the current status and destination chain transaction hash (if available).
243
+
244
+ ```typescript
245
+ const status = await SFA.getStatus(
246
+ serviceType: ServiceType, // "oneclick" | "cctp" | "usdt0"
247
+ {
248
+ depositAddress?: string, // Deposit address from quote (for OneClick)
249
+ hash?: string, // Transaction hash (for USDT0 and CCTP)
250
+ }
251
+ );
252
+ ```
253
+
254
+ **Returns**: `Promise<{ status: TransactionStatus; toChainTxHash?: string }>`
255
+
256
+ **TransactionStatus**:
257
+ - `TransactionStatus.Pending` - Transaction is pending
258
+ - `TransactionStatus.Success` - Transaction completed successfully
259
+ - `TransactionStatus.Failed` - Transaction failed or was refunded
260
+
261
+ **Example**:
262
+
263
+ ```typescript
264
+ import { SFA, Service, TransactionStatus } from 'stableflow-ai-sdk';
265
+
266
+ // For OneClick service
267
+ const status = await SFA.getStatus(Service.OneClick, {
268
+ depositAddress: '0x...',
269
+ });
270
+
271
+ // For USDT0 or CCTP service
272
+ const status = await SFA.getStatus(Service.Usdt0, {
273
+ hash: '0x...',
274
+ });
275
+
276
+ console.log('Status:', status.status); // "pending" | "success" | "failed"
277
+ if (status.toChainTxHash) {
278
+ console.log('Destination tx hash:', status.toChainTxHash);
279
+ }
280
+ ```
281
+
282
+ **Polling Example**:
283
+
284
+ ```typescript
285
+ async function pollTransactionStatus(
286
+ serviceType: ServiceType,
287
+ params: { depositAddress?: string; hash?: string },
288
+ interval: number = 5000
289
+ ): Promise<{ status: TransactionStatus; toChainTxHash?: string }> {
290
+ return new Promise((resolve) => {
291
+ const checkStatus = async () => {
292
+ try {
293
+ const result = await SFA.getStatus(serviceType, params);
294
+ if (result.status !== TransactionStatus.Pending) {
295
+ resolve(result);
296
+ } else {
297
+ setTimeout(checkStatus, interval);
298
+ }
299
+ } catch (error) {
300
+ console.error('Error checking status:', error);
301
+ setTimeout(checkStatus, interval);
302
+ }
303
+ };
304
+ checkStatus();
305
+ });
306
+ }
307
+
308
+ // Usage
309
+ const finalStatus = await pollTransactionStatus(Service.OneClick, {
310
+ depositAddress: '0x...',
311
+ });
312
+ ```
313
+
314
+ ## Supported Bridge Services
315
+
316
+ The SDK supports three bridge services:
317
+
318
+ - **OneClick** (`Service.OneClick`) - Native StableFlow bridge service
319
+ - **CCTP** (`Service.CCTP`) - Circle's Cross-Chain Transfer Protocol
320
+ - **USDT0** (`Service.Usdt0`) - LayerZero-based USDT bridge
321
+
322
+ Each service has different characteristics:
323
+ - Different fee structures
324
+ - Different supported token pairs
325
+ - Different processing times
326
+ - Different minimum/maximum amounts
327
+
328
+ ### USDT0 Service Features
329
+
330
+ The USDT0 service provides LayerZero-based USDT bridging with the following capabilities:
331
+
332
+ - **Multi-chain Support**: Supports bridging from EVM chains (Ethereum, Arbitrum, Polygon, Optimism, etc.), Solana, and Tron
333
+ - **Multi-hop Routing**: Automatically handles multi-hop transfers when direct routes are not available (e.g., Solana → Arbitrum → Ethereum)
334
+ - **Dynamic Time Estimation**: Calculates estimated completion time based on source and destination chain block times and confirmations
335
+ - **Accurate Fee Estimation**: Improved fee calculation including LayerZero message fees, gas costs, and legacy mesh transfer fees (0.03% for legacy routes)
336
+ - **Legacy and Upgradeable Support**: Seamlessly handles both legacy and upgradeable OFT contracts
337
+
338
+ Use `getAllQuote` to compare all available routes and select the best one for your use case.
339
+
340
+ ## Wallet Integration
341
+
342
+ The SDK supports multiple wallet types:
343
+
344
+ ### EVM Wallets (Ethereum, Arbitrum, Polygon, etc.)
345
+
346
+ **Using ethers.js with BrowserProvider:**
347
+
348
+ ```typescript
349
+ import { EVMWallet } from 'stableflow-ai-sdk';
350
+ import { ethers } from 'ethers';
351
+
352
+ const provider = new ethers.BrowserProvider(window.ethereum);
353
+ const signer = await provider.getSigner();
354
+ const wallet = new EVMWallet(provider, signer);
355
+ ```
356
+
357
+ **Using wagmi/viem (recommended for React apps):**
358
+
359
+ ```typescript
360
+ import { EVMWallet } from 'stableflow-ai-sdk';
361
+ import { ethers } from 'ethers';
362
+ import { usePublicClient, useWalletClient } from 'wagmi';
363
+
364
+ // In your React component
365
+ const publicClient = usePublicClient();
366
+ const { data: walletClient } = useWalletClient();
367
+
368
+ const provider = new ethers.BrowserProvider(publicClient);
369
+ const signer = walletClient
370
+ ? await new ethers.BrowserProvider(walletClient).getSigner()
371
+ : null;
372
+
373
+ const wallet = new EVMWallet(provider, signer);
374
+ ```
375
+
376
+ ### Solana Wallets
377
+
378
+ **Using @solana/wallet-adapter-react (recommended for React apps):**
379
+
380
+ ```typescript
381
+ import { SolanaWallet } from 'stableflow-ai-sdk';
382
+ import { useWallet } from '@solana/wallet-adapter-react';
383
+
384
+ // In your React component
385
+ const { publicKey, signTransaction } = useWallet();
386
+
387
+ const wallet = new SolanaWallet({
388
+ publicKey: publicKey,
389
+ signer: { signTransaction }
390
+ });
391
+ ```
392
+
393
+ **Using wallet adapter directly:**
394
+
395
+ ```typescript
396
+ import { SolanaWallet } from 'stableflow-ai-sdk';
397
+ import { Connection, PublicKey } from '@solana/web3.js';
398
+
399
+ const connection = new Connection('https://api.mainnet-beta.solana.com');
400
+ const publicKey = new PublicKey('YOUR_SOLANA_ADDRESS');
401
+
402
+ const wallet = new SolanaWallet({
403
+ publicKey: publicKey,
404
+ signer: {
405
+ signTransaction: async (transaction) => {
406
+ // Sign transaction using your wallet adapter
407
+ // Example with Phantom:
408
+ // const provider = window.solana;
409
+ // return await provider.signTransaction(transaction);
410
+ return signedTransaction;
411
+ }
412
+ }
413
+ });
414
+ ```
415
+
416
+ **Note**: Solana wallets can be used as the source chain for USDT0 bridging, enabling cross-chain transfers from Solana to EVM chains, Tron, and other supported networks.
417
+
418
+ ### Near Wallets
419
+
420
+ ```typescript
421
+ import { NearWallet } from 'stableflow-ai-sdk';
422
+ import { setupWalletSelector } from '@near-wallet-selector/core';
423
+
424
+ // Setup wallet selector (e.g., using @near-wallet-selector)
425
+ const selector = await setupWalletSelector({
426
+ network: 'mainnet',
427
+ modules: [
428
+ // Add your wallet modules here
429
+ ]
430
+ });
431
+
432
+ const wallet = new NearWallet(selector);
433
+ ```
434
+
435
+ **Note**: NearWallet requires a wallet selector instance from `@near-wallet-selector/core`. The selector handles wallet connection and transaction signing.
436
+
437
+ ### Tron Wallets
438
+
439
+ ```typescript
440
+ import { TronWallet } from 'stableflow-ai-sdk';
441
+
442
+ // Using TronLink or other Tron wallet adapters
443
+ const wallet = new TronWallet({
444
+ signAndSendTransaction: async (transaction: any) => {
445
+ // Sign transaction using TronWeb
446
+ const signedTransaction = await window.tronWeb.trx.sign(transaction);
447
+ // Send signed transaction
448
+ return await window.tronWeb.trx.sendRawTransaction(signedTransaction);
449
+ },
450
+ address: window.tronWeb?.defaultAddress?.base58, // User's Tron address
451
+ });
452
+ ```
453
+
454
+ **With TronLink Wallet:**
455
+
456
+ ```typescript
457
+ import { TronWallet } from 'stableflow-ai-sdk';
458
+
459
+ // Wait for TronLink to be available
460
+ if (window.tronWeb && window.tronWeb.ready) {
461
+ const wallet = new TronWallet({
462
+ signAndSendTransaction: async (transaction: any) => {
463
+ const signedTransaction = await window.tronWeb.trx.sign(transaction);
464
+ const result = await window.tronWeb.trx.sendRawTransaction(signedTransaction);
465
+ // Return transaction ID (txid)
466
+ return typeof result === 'string' ? result : result.txid;
467
+ },
468
+ address: window.tronWeb.defaultAddress.base58,
469
+ });
470
+ }
471
+ ```
472
+
473
+ **Note:** The `signAndSendTransaction` function should:
474
+ - Accept a transaction object as parameter
475
+ - Sign the transaction using the connected wallet
476
+ - Send the signed transaction to the network
477
+ - Return the transaction ID (txid) as a string, or an object with a `txid` property
478
+
479
+ ### Aptos Wallets
480
+
481
+ ```typescript
482
+ import { AptosWallet } from 'stableflow-ai-sdk';
483
+ import { useWallet } from '@aptos-labs/wallet-adapter-react';
484
+
485
+ // Using Aptos wallet adapter
486
+ const { account, signAndSubmitTransaction } = useWallet();
487
+
488
+ const wallet = new AptosWallet({
489
+ account: account,
490
+ signAndSubmitTransaction: signAndSubmitTransaction,
491
+ });
492
+ ```
493
+
494
+ **Note**: AptosWallet requires an account object and a `signAndSubmitTransaction` function from the Aptos wallet adapter (e.g., `@aptos-labs/wallet-adapter-react`).
495
+
496
+ ## Token Configuration
497
+
498
+ The SDK provides pre-configured token information:
499
+
500
+ ```typescript
501
+ import { tokens, usdtTokens, usdcTokens } from 'stableflow-ai-sdk';
502
+
503
+ // Get all supported tokens
504
+ const allTokens = tokens;
505
+
506
+ // Get USDT tokens only
507
+ const usdtOnly = usdtTokens;
508
+
509
+ // Get USDC tokens only
510
+ const usdcOnly = usdcTokens;
511
+
512
+ // Find token by contract address
513
+ const token = tokens.find(t =>
514
+ t.contractAddress.toLowerCase() === '0x...'.toLowerCase()
515
+ );
516
+
517
+ // Find tokens by chain
518
+ const ethereumTokens = tokens.filter(t => t.chainName === 'Ethereum');
519
+ ```
520
+
521
+ Each token configuration includes:
522
+ - `chainName` - Name of the blockchain
523
+ - `chainType` - Type of chain (evm, solana, near, tron, aptos)
524
+ - `symbol` - Token symbol (USDT, USDC, etc.)
525
+ - `decimals` - Token decimals
526
+ - `contractAddress` - Token contract address
527
+ - `assetId` - StableFlow asset identifier
528
+ - `services` - Array of supported bridge services
529
+ - `rpcUrls` - RPC endpoint URLs
530
+
531
+ ## Complete Example
532
+
533
+ ### Example 1: EVM to EVM Bridge (Ethereum → Arbitrum)
534
+
535
+ Here's a complete example of a cross-chain swap:
536
+
537
+ ```typescript
538
+ import { SFA, OpenAPI, tokens, EVMWallet, Service, TransactionStatus, setRpcUrls } from 'stableflow-ai-sdk';
539
+ import { ethers } from 'ethers';
540
+
541
+ // 1. Initialize SDK
542
+ OpenAPI.BASE = 'https://api.stableflow.ai';
543
+ OpenAPI.TOKEN = 'your-jwt-token';
544
+
545
+ // (Optional) Configure custom RPC endpoints
546
+ setRpcUrls({
547
+ "eth": ["https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"],
548
+ "arb": ["https://arbitrum-one-rpc.publicnode.com"],
549
+ });
550
+
551
+ // 2. Setup wallet
552
+ const provider = new ethers.BrowserProvider(window.ethereum);
553
+ await provider.send('eth_requestAccounts', []);
554
+ const signer = await provider.getSigner();
555
+ const wallet = new EVMWallet(provider, signer);
556
+ const userAddress = await signer.getAddress();
557
+
558
+ // 3. Select tokens
559
+ const fromToken = tokens.find(t =>
560
+ t.chainName === 'Ethereum' && t.symbol === 'USDT'
561
+ );
562
+ const toToken = tokens.find(t =>
563
+ t.chainName === 'Arbitrum' && t.symbol === 'USDT'
564
+ );
565
+
566
+ if (!fromToken || !toToken) {
567
+ throw new Error('Token pair not supported');
568
+ }
569
+
570
+ // 4. Get quotes
571
+ const quotes = await SFA.getAllQuote({
572
+ dry: false,
573
+ prices: {},
574
+ fromToken,
575
+ toToken,
576
+ wallet,
577
+ recipient: userAddress, // or another address
578
+ refundTo: userAddress,
579
+ amountWei: ethers.parseUnits('100', fromToken.decimals).toString(),
580
+ slippageTolerance: 0.5,
581
+ });
582
+
583
+ // 5. Select best quote (e.g., first valid one)
584
+ const selectedQuote = quotes.find(q => q.quote && !q.error);
585
+ if (!selectedQuote || !selectedQuote.quote) {
586
+ throw new Error('No valid quotes available');
587
+ }
588
+
589
+ console.log(`Selected route: ${selectedQuote.serviceType}`);
590
+ console.log(`Estimated time: ${selectedQuote.quote.estimateTime}s`);
591
+
592
+ // 6. Handle approval if needed
593
+ if (selectedQuote.quote.needApprove) {
594
+ const allowance = await wallet.allowance({
595
+ contractAddress: selectedQuote.quote.quoteParam.fromToken.contractAddress,
596
+ spender: selectedQuote.quote.approveSpender,
597
+ address: userAddress,
598
+ });
599
+
600
+ if (allowance < BigInt(selectedQuote.quote.quoteParam.amountWei)) {
601
+ await wallet.approve({
602
+ contractAddress: selectedQuote.quote.quoteParam.fromToken.contractAddress,
603
+ spender: selectedQuote.quote.approveSpender,
604
+ amountWei: selectedQuote.quote.quoteParam.amountWei,
605
+ });
606
+ }
607
+ }
608
+
609
+ // 7. Send transaction
610
+ const txHash = await SFA.send(selectedQuote.serviceType, {
611
+ wallet,
612
+ quote: selectedQuote.quote,
613
+ });
614
+
615
+ console.log('Transaction submitted:', txHash);
616
+
617
+ // 8. Poll for status
618
+ const statusParams = selectedQuote.serviceType === Service.OneClick
619
+ ? { depositAddress: selectedQuote.quote.quote?.depositAddress }
620
+ : { hash: txHash };
621
+
622
+ const checkStatus = async () => {
623
+ const status = await SFA.getStatus(selectedQuote.serviceType, statusParams);
624
+ console.log('Current status:', status.status);
625
+
626
+ if (status.status === TransactionStatus.Success) {
627
+ console.log('Swap completed! Destination tx:', status.toChainTxHash);
628
+ } else if (status.status === TransactionStatus.Failed) {
629
+ console.log('Swap failed or refunded');
630
+ } else {
631
+ // Still pending, check again later
632
+ setTimeout(checkStatus, 5000);
633
+ }
634
+ };
635
+
636
+ checkStatus();
637
+ ```
638
+
639
+ ### Example 2: Solana to EVM Bridge (Solana → Ethereum)
640
+
641
+ Bridge USDT from Solana to Ethereum using USDT0:
642
+
643
+ ```typescript
644
+ import { SFA, OpenAPI, tokens, SolanaWallet, Service, TransactionStatus, setRpcUrls } from 'stableflow-ai-sdk';
645
+ import { Connection, PublicKey } from '@solana/web3.js';
646
+
647
+ // 1. Initialize SDK
648
+ OpenAPI.BASE = 'https://api.stableflow.ai';
649
+ OpenAPI.TOKEN = 'your-jwt-token';
650
+
651
+ // (Optional) Configure custom RPC endpoints
652
+ setRpcUrls({
653
+ "sol": ["https://api.mainnet-beta.solana.com"],
654
+ "eth": ["https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"],
655
+ });
656
+
657
+ // 2. Setup Solana wallet
658
+ // Note: In a real application, get these from your wallet adapter
659
+ // Example with @solana/wallet-adapter-react:
660
+ // const { publicKey, signTransaction } = useWallet();
661
+ // const wallet = new SolanaWallet({
662
+ // publicKey: publicKey,
663
+ // signer: { signTransaction }
664
+ // });
665
+
666
+ const connection = new Connection('https://api.mainnet-beta.solana.com');
667
+ const publicKey = new PublicKey('YOUR_SOLANA_ADDRESS');
668
+ const wallet = new SolanaWallet({
669
+ publicKey: publicKey,
670
+ signer: {
671
+ signTransaction: async (tx) => {
672
+ // Sign transaction using your Solana wallet adapter
673
+ // Example with Phantom:
674
+ // const provider = window.solana;
675
+ // return await provider.signTransaction(tx);
676
+ throw new Error('Implement wallet signing');
677
+ }
678
+ }
679
+ });
680
+
681
+ // 3. Select tokens
682
+ const fromToken = tokens.find(t =>
683
+ t.chainName === 'Solana' && t.symbol === 'USDT'
684
+ );
685
+ const toToken = tokens.find(t =>
686
+ t.chainName === 'Ethereum' && t.symbol === 'USDT'
687
+ );
688
+
689
+ if (!fromToken || !toToken) {
690
+ throw new Error('Token pair not supported');
691
+ }
692
+
693
+ // 4. Get quotes (USDT0 will automatically use multi-hop routing if needed)
694
+ const quotes = await SFA.getAllQuote({
695
+ dry: false,
696
+ prices: {},
697
+ fromToken,
698
+ toToken,
699
+ wallet,
700
+ recipient: '0x...', // Ethereum recipient address
701
+ refundTo: publicKey.toString(), // Solana refund address
702
+ amountWei: '1000000', // 1 USDT (6 decimals)
703
+ slippageTolerance: 0.5,
704
+ });
705
+
706
+ // 5. Find USDT0 quote
707
+ const usdt0Quote = quotes.find(q => q.serviceType === Service.Usdt0 && q.quote && !q.error);
708
+ if (usdt0Quote && usdt0Quote.quote) {
709
+ console.log(`USDT0 route available`);
710
+ console.log(`Estimated time: ${usdt0Quote.quote.estimateTime}s`);
711
+ console.log(`Total fees: $${usdt0Quote.quote.totalFeesUsd}`);
712
+
713
+ // 6. Send transaction
714
+ const txHash = await SFA.send(Service.Usdt0, {
715
+ wallet,
716
+ quote: usdt0Quote.quote,
717
+ });
718
+
719
+ console.log('Transaction submitted:', txHash);
720
+
721
+ // 7. Poll for status
722
+ const checkStatus = async () => {
723
+ const status = await SFA.getStatus(Service.Usdt0, { hash: txHash });
724
+ console.log('Current status:', status.status);
725
+
726
+ if (status.status === TransactionStatus.Success) {
727
+ console.log('Bridge completed! Destination tx:', status.toChainTxHash);
728
+ } else if (status.status === TransactionStatus.Failed) {
729
+ console.log('Bridge failed or refunded');
730
+ } else {
731
+ setTimeout(checkStatus, 5000);
732
+ }
733
+ };
734
+
735
+ checkStatus();
736
+ }
737
+ ```
738
+
739
+ ## Error Handling
740
+
741
+ The SDK throws typed errors that you can catch and handle:
742
+
743
+ ```typescript
744
+ import { ApiError } from 'stableflow-ai-sdk';
745
+
746
+ try {
747
+ const quotes = await SFA.getAllQuote(params);
748
+ } catch (error) {
749
+ if (error instanceof ApiError) {
750
+ switch (error.status) {
751
+ case 401:
752
+ console.error('Authentication failed: JWT is missing or invalid');
753
+ break;
754
+ case 400:
755
+ console.error('Invalid request:', error.body);
756
+ break;
757
+ default:
758
+ console.error('API error:', error.message);
759
+ }
760
+ } else {
761
+ console.error('Unexpected error:', error);
762
+ }
763
+ }
764
+ ```
765
+
766
+ Common error scenarios:
767
+
768
+ - **Invalid parameters**: Missing or invalid token configurations, addresses, or amounts
769
+ - **Insufficient balance**: User doesn't have enough tokens
770
+ - **Amount too low**: Amount below minimum threshold for the bridge service
771
+ - **Amount exceeds max**: Amount above maximum limit for the bridge service
772
+ - **No route available**: No bridge service supports the token pair
773
+ - **Network errors**: Connection issues or RPC failures
774
+
775
+ ## Type Definitions
776
+
777
+ The SDK provides full TypeScript type definitions:
778
+
779
+ - `GetAllQuoteParams` - Parameters for `getAllQuote`
780
+ - `ServiceType` - Bridge service type (`"oneclick" | "cctp" | "usdt0"`)
781
+ - `Service` - Service constants
782
+ - `TokenConfig` - Token configuration interface
783
+ - `WalletConfig` - Wallet interface
784
+ - `TransactionStatus` - Transaction status enum
785
+
786
+ ## Custom RPC Configuration
787
+
788
+ The SDK allows you to configure custom RPC endpoints for different blockchains. This is useful when you want to use your own RPC providers, private endpoints, or RPC services with API keys.
789
+
790
+ ### Setting Custom RPC URLs
791
+
792
+ You can set custom RPC URLs using the `setRpcUrls` function. The function accepts a record where keys are blockchain identifiers and values are arrays of RPC URLs.
793
+
794
+ **Supported Blockchain Identifiers:**
795
+ - `"eth"` - Ethereum
796
+ - `"arb"` - Arbitrum
797
+ - `"bsc"` - BNB Smart Chain
798
+ - `"avax"` - Avalanche
799
+ - `"base"` - Base
800
+ - `"pol"` - Polygon
801
+ - `"gnosis"` - Gnosis Chain
802
+ - `"op"` - Optimism
803
+ - `"bera"` - Berachain
804
+ - `"tron"` - Tron
805
+ - `"aptos"` - Aptos
806
+ - `"sol"` - Solana
807
+ - `"near"` - NEAR Protocol
808
+ - `"xlayer"` - X Layer
809
+
810
+ **Basic Usage:**
811
+
812
+ ```typescript
813
+ import { setRpcUrls } from 'stableflow-ai-sdk';
814
+
815
+ // Set custom RPC URLs for specific blockchains
816
+ setRpcUrls({
817
+ "eth": ["https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"],
818
+ "arb": ["https://arbitrum-one-rpc.publicnode.com"],
819
+ "sol": ["https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY"],
820
+ "near": ["https://rpc.mainnet.near.org"],
821
+ });
822
+ ```
823
+
824
+ **Multiple RPC URLs (Fallback Support):**
825
+
826
+ You can provide multiple RPC URLs for the same blockchain. The SDK will use them in order, with the first URL being the primary endpoint:
827
+
828
+ ```typescript
829
+ setRpcUrls({
830
+ "eth": [
831
+ "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY",
832
+ "https://eth.merkle.io",
833
+ "https://cloudflare-eth.com"
834
+ ],
835
+ "arb": [
836
+ "https://arbitrum-one-rpc.publicnode.com",
837
+ "https://arb1.arbitrum.io/rpc"
838
+ ],
839
+ });
840
+ ```
841
+
842
+ **How It Works:**
843
+
844
+ - Custom RPC URLs are prepended to the default RPC URLs for each blockchain
845
+ - If a custom URL already exists in the default list, it won't be duplicated
846
+ - The SDK will prioritize custom URLs over default ones
847
+ - Multiple URLs can be provided for redundancy and fallback support
848
+
849
+ **Getting Current RPC URLs:**
850
+
851
+ You can access the current RPC configuration using `NetworkRpcUrlsMap`:
852
+
853
+ ```typescript
854
+ import { NetworkRpcUrlsMap, getRpcUrls } from 'stableflow-ai-sdk';
855
+
856
+ // Get all RPC URLs for a specific blockchain
857
+ const ethRpcUrls = getRpcUrls("eth");
858
+ console.log(ethRpcUrls); // ["https://custom-rpc.com", "https://eth.merkle.io", ...]
859
+
860
+ // Access the full RPC URLs map
861
+ console.log(NetworkRpcUrlsMap);
862
+ ```
863
+
864
+ **Complete Example:**
865
+
866
+ ```typescript
867
+ import { OpenAPI, SFA, tokens, EVMWallet, setRpcUrls } from 'stableflow-ai-sdk';
868
+ import { ethers } from 'ethers';
869
+
870
+ // Initialize the API client
871
+ OpenAPI.BASE = 'https://api.stableflow.ai';
872
+ OpenAPI.TOKEN = "your-JSON-Web-Token";
873
+
874
+ // Configure custom RPC endpoints
875
+ setRpcUrls({
876
+ "eth": ["https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"],
877
+ "arb": ["https://arbitrum-one-rpc.publicnode.com"],
878
+ "sol": ["https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY"],
879
+ });
880
+
881
+ // Get wallet instance (will use custom RPC if configured)
882
+ const provider = new ethers.BrowserProvider(window.ethereum);
883
+ const signer = await provider.getSigner();
884
+ const wallet = new EVMWallet(provider, signer);
885
+
886
+ // Continue with your swap flow...
887
+ const fromToken = tokens.find(t => t.chainName === 'Ethereum' && t.symbol === 'USDT');
888
+ const toToken = tokens.find(t => t.chainName === 'Arbitrum' && t.symbol === 'USDT');
889
+
890
+ const quotes = await SFA.getAllQuote({
891
+ dry: false,
892
+ minInputAmount: "0.1",
893
+ prices: {},
894
+ fromToken: fromToken!,
895
+ toToken: toToken!,
896
+ wallet: wallet,
897
+ recipient: '0x...',
898
+ refundTo: '0x...',
899
+ amountWei: ethers.parseUnits('100', fromToken!.decimals).toString(),
900
+ slippageTolerance: 0.5,
901
+ });
902
+ ```
903
+
904
+ **Best Practices:**
905
+
906
+ 1. **Set RPC URLs Early**: Configure custom RPC URLs before initializing wallets or making API calls
907
+ 2. **Use Multiple URLs**: Provide fallback RPC URLs for better reliability
908
+ 3. **API Key Security**: Never commit API keys to version control. Use environment variables:
909
+ ```typescript
910
+ setRpcUrls({
911
+ "eth": [process.env.ETH_RPC_URL || "https://eth.merkle.io"],
912
+ "sol": [process.env.SOL_RPC_URL || "https://solana-rpc.publicnode.com"],
913
+ });
914
+ ```
915
+ 4. **Test Your RPCs**: Ensure your custom RPC endpoints are working correctly before deploying
916
+
917
+ ## Examples
918
+
919
+ ### 🌐 Web Application Demo 2.0
920
+
921
+ **Try our interactive web app with real wallet connection:**
922
+
923
+ ```bash
924
+ cd examples/web-demo-2.0
925
+ npm install
926
+ npm run dev
927
+ ```
928
+
929
+ Features:
930
+ - 🔗 **Multi-Wallet Support** - Connect MetaMask, Solana, Near, Tron, Aptos wallets
931
+ - 🎨 **Modern UI** - Beautiful interface with real-time updates
932
+ - 🌐 **Multiple Chains** - Support for all major blockchains
933
+ - 💰 **Multi-Token Support** - USDT and USDC bridging
934
+ - 💵 **Real-Time Quotes** - Compare quotes from all bridge services
935
+ - 🚀 **One-Click Execution** - Execute transactions with automatic approval
936
+ - 📊 **Transaction History** - Track your bridging activity with status polling
937
+
938
+ **Tech Stack**: TypeScript, React, Vite, StableFlow SDK
939
+
940
+ See [examples/web-demo-2.0/README.md](examples/web-demo-2.0/README.md) for full documentation.
941
+
942
+ ## Migration from v1.0
943
+
944
+ If you're using SDK v1.0, see the [v1.0 documentation](README-v1.md) for the old API methods (`getQuote`, `getExecutionStatus`, `submitDepositTx`).
945
+
946
+ Key differences in v2.0:
947
+ - `getAllQuote` replaces `getQuote` and returns quotes from all services
948
+ - `send` replaces `submitDepositTx` and handles transaction submission automatically
949
+ - `getStatus` replaces `getExecutionStatus` with service-specific status checking
950
+ - Wallet instances are now required for quote requests
951
+ - Token configurations are provided via the `tokens` export
952
+
953
+ ## Development
954
+
955
+ Developer commands:
956
+
957
+ ```bash
958
+ # Install dependencies
959
+ npm install
960
+
961
+ # Build the SDK
962
+ npm run build
963
+
964
+ # Clean build artifacts
965
+ npm run clean
966
+
967
+ # Watch mode for development
968
+ npm run dev
969
+ ```
970
+
971
+ ## License
972
+
973
+ MIT
974
+
975
+ ## Support
976
+
977
+ For issues or support:
978
+
979
+ - Open an issue on GitHub
980
+ - Check the documentation
981
+ - Contact our support team
982
+
983
+ ## Changelog
984
+
985
+ ### v2.0.0
986
+ - Added `getAllQuote` method to get quotes from all bridge services
987
+ - Added `send` method for executing transactions
988
+ - Added `getStatus` method for checking transaction status
989
+ - Support for multiple bridge services (OneClick, CCTP, USDT0)
990
+ - Wallet integration for multiple chains
991
+ - Pre-configured token information
992
+ - **USDT0 Improvements**:
993
+ - Support for bridging from Solana as source chain
994
+ - Multi-hop routing support for cross-chain transfers
995
+ - Improved fee estimation accuracy
996
+ - Dynamic time estimation based on chain block times
997
+ - Fixed multi-hop composer issues
998
+
999
+ ### v1.0.0
1000
+ - Initial release
1001
+ - Support for cross-chain token swaps
1002
+ - JWT authentication support
1003
+ - Full TypeScript type definitions