Developer Docs
Developer Guide: SDK Integration
A comprehensive guide for developers to integrate StableFlow’s cross-chain transfers into your applications.
Table of Contents
Getting Started
Installation
npm install stableflow-ai-sdkPrerequisites
Node.js: Version 16 or higher
TypeScript (recommended): For full type safety
API Configuration
Basic Setup
import { OpenAPI, SFA } from 'stableflow-ai-sdk';
// Configure API endpoint
OpenAPI.BASE = 'https://api.stableflow.ai';
// Set your JWT token
OpenAPI.TOKEN = 'your-jwt-token-here';Configuration Options
OpenAPI.BASE
string
API endpoint URL
Yes
OpenAPI.TOKEN
string
JWT authentication token
Yes
OpenAPI.WITH_CREDENTIALS
boolean
Include credentials in requests
No
OpenAPI.HEADERS
object
Additional request headers
No
Core Functions
1. getTokens()
getTokens()Retrieves the list of all supported tokens across different blockchains.
Signature
SFA.getTokens(): Promise<TokenResponse[]>Returns
Array of TokenResponse objects containing:
interface TokenResponse {
assetId: string; // Unique asset identifier
blockchain: string; // Network ID (e.g., 'eth', 'arb', 'pol')
symbol: string; // Token symbol (e.g., 'USDT', 'USDC')
decimals: number; // Token decimals (e.g., 6, 18)
address?: string; // Contract address
price?: string; // Current price in USD
}Example Usage
// Fetch all supported tokens
const tokens = await SFA.getTokens();
// Filter USDT tokens
const usdtTokens = tokens.filter(t => t.symbol === 'USDT');
// Get tokens for specific network
const ethTokens = tokens.filter(t => t.blockchain === 'eth');
// Find specific token
const arbUsdc = tokens.find(
t => t.blockchain === 'arb' && t.symbol === 'USDC'
);Use Cases
Building token selection UI
Validating supported tokens
Getting current token prices
Discovering available networks
2. getQuote()
getQuote()Requests a quote for cross-chain token swap, including fees, estimated time, and deposit address.
Signature
SFA.getQuote(request: QuoteRequest): Promise<QuoteResponse>Request Parameters
interface QuoteRequest {
// Testing mode (true = no real deposit address)
dry: boolean;
// Swap calculation type
swapType: 'EXACT_INPUT' | 'EXACT_OUTPUT';
// Slippage tolerance in basis points (100 = 1%)
slippageTolerance: number;
// Source token asset ID
originAsset: string;
// Where user deposits funds
depositType: 'ORIGIN_CHAIN' | 'NEAR';
// Destination token asset ID
destinationAsset: string;
// Amount in token's smallest unit
amount: string;
// Refund address if swap fails
refundTo: string;
// Refund location
refundType: 'ORIGIN_CHAIN' | 'NEAR';
// Recipient address for destination tokens
recipient: string;
// Recipient location
recipientType: 'DESTINATION_CHAIN' | 'NEAR';
// Quote expiration time (ISO 8601)
deadline: string;
// Optional: max wait time for quote in milliseconds
quoteWaitingTimeMs?: number;
}Response
interface QuoteResponse {
quote: {
depositAddress: string; // Address to send tokens to
amountIn: string; // Input amount (smallest unit)
amountInFormatted: string; // Input amount (human-readable)
amountOut: string; // Output amount (smallest unit)
amountOutFormatted: string; // Output amount (human-readable)
minAmountOut: string; // Minimum output after slippage
amountInUsd: string; // Input value in USD
amountOutUsd: string; // Output value in USD
timeEstimate: number; // Estimated completion time (seconds)
appFee?: AppFee; // Fee breakdown
};
swapDetails: SwapDetails; // Detailed swap information
transactionDetails: TransactionDetails; // Transaction parameters
}Example Usage
import { QuoteRequest } from 'stableflow-ai-sdk';
const quoteRequest: QuoteRequest = {
dry: false, // Get real deposit address
swapType: QuoteRequest.swapType.EXACT_INPUT,
slippageTolerance: 100, // 1%
// Swap from Arbitrum USDT to Ethereum USDT
originAsset: 'nep141:arb-0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9.omft.near',
destinationAsset: 'nep141:eth-0xdac17f958d2ee523a2206206994597c13d831ec7.omft.near',
amount: '1000000', // 1 USDT (6 decimals)
depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
refundTo: '0xYourArbitrumAddress',
refundType: QuoteRequest.refundType.ORIGIN_CHAIN,
recipient: '0xYourEthereumAddress',
recipientType: QuoteRequest.recipientType.DESTINATION_CHAIN,
deadline: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
};
const quote = await SFA.getQuote(quoteRequest);
console.log('Deposit to:', quote.quote.depositAddress);
console.log('You will receive:', quote.quote.amountOutFormatted);
console.log('Estimated time:', quote.quote.timeEstimate, 'seconds');Important Notes
dry: true: Testing mode, no real deposit addressdry: false: Production mode, returns real deposit addressAmount Format: Always use smallest token unit (e.g., 1 USDT = 1000000 for 6 decimals)
Deadline: Must be a future timestamp in ISO 8601 format
Address Validation: Ensure addresses match the respective network format
3. submitDepositTx()
submitDepositTx()Notifies StableFlow that you've sent tokens to the deposit address.
Signature
SFA.submitDepositTx(request: SubmitDepositTxRequest): Promise<SubmitDepositTxResponse>Request Parameters
interface SubmitDepositTxRequest {
txHash: string; // Transaction hash from blockchain
depositAddress: string; // Deposit address from quote
}Response
interface SubmitDepositTxResponse {
success: boolean;
message?: string;
}Example Usage
// After sending tokens to deposit address
const txHash = '0x123...abc'; // From blockchain transaction
const depositAddress = quote.quote.depositAddress;
const result = await SFA.submitDepositTx({
txHash,
depositAddress,
});
if (result.success) {
console.log('Transaction submitted successfully!');
}When to Call
After sending tokens to
depositAddressAfter transaction is confirmed on blockchain
Before checking execution status
4. getExecutionStatus()
getExecutionStatus()Checks the current status of a cross-chain swap.
Signature
SFA.getExecutionStatus(depositAddress: string): Promise<GetExecutionStatusResponse>Parameters
depositAddress
string
Deposit address from quote
Response
interface GetExecutionStatusResponse {
status: 'pending' | 'processing' | 'completed' | 'failed';
depositTxHash?: string;
destinationTxHash?: string;
message?: string;
updatedAt: string;
}Example Usage
const status = await SFA.getExecutionStatus(depositAddress);
switch (status.status) {
case 'pending':
console.log('Waiting for deposit confirmation...');
break;
case 'processing':
console.log('Swap in progress...');
break;
case 'completed':
console.log('Swap completed!');
console.log('Destination tx:', status.destinationTxHash);
break;
case 'failed':
console.log('Swap failed:', status.message);
break;
}Polling Example
async function waitForCompletion(depositAddress: string) {
const maxAttempts = 60; // 5 minutes with 5s interval
for (let i = 0; i < maxAttempts; i++) {
const status = await SFA.getExecutionStatus(depositAddress);
if (status.status === 'completed') {
return status;
}
if (status.status === 'failed') {
throw new Error(status.message);
}
// Wait 5 seconds before next check
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error('Timeout waiting for swap completion');
}Working Examples
The SDK includes a complete web application example demonstrating real-world usage.
Web Demo Application
Location: examples/web-demo/
Features
â Real wallet connection (MetaMask)
â Network switching
â Token balance checking
â Quote generation
â Transaction execution
â Status tracking
Running the Example
cd examples/web-demo
npm install
npm run devKey Files to Study
app.ts- Main application logicWallet connection:
connectWallet()Network switching:
switchNetwork()Getting quotes:
handleGetQuote()Executing swaps:
executeBridge()
Network Configuration
const SUPPORTED_NETWORKS = [ { id: 'eth', name: 'Ethereum', chainId: 1, usdtContract: '0xdac17f958d2ee523a2206206994597c13d831ec7', usdtAssetId: 'nep141:eth-0xdac17f958d2ee523a2206206994597c13d831ec7.omft.near', decimals: 6 }, // ... more networks ];ERC20 Token Transfer
// Create contract instance const erc20Abi = ['function transfer(address to, uint256 amount) returns (bool)']; const usdtContract = new ethers.Contract( fromNetwork.usdtContract, erc20Abi, signer ); // Execute transfer const tx = await usdtContract.transfer(depositAddress, amount); await tx.wait();
Learning Path
Start with: Understanding the flow in
app.tsStudy: Network configuration and asset IDs
Review: Error handling patterns
Examine: UI/UX best practices
Customize: Adapt for your use case
Best Practices
1. Error Handling
Always wrap SDK calls in try-catch blocks:
import { ApiError } from 'stableflow-ai-sdk';
try {
const quote = await SFA.getQuote(request);
} catch (error) {
if (error instanceof ApiError) {
switch (error.status) {
case 400:
// Invalid request parameters
console.error('Bad request:', error.body);
break;
case 401:
// Authentication failed
console.error('Invalid JWT token');
break;
case 404:
// Resource not found
console.error('Endpoint not found');
break;
default:
console.error('API error:', error.message);
}
} else {
// Network or other errors
console.error('Unexpected error:', error);
}
}2. Token Amount Conversion
Always convert human-readable amounts to smallest units:
import { ethers } from 'ethers';
// Human-readable to smallest unit
const decimals = 6; // USDT decimals
const humanAmount = '100.5'; // 100.5 USDT
const smallestUnit = ethers.parseUnits(humanAmount, decimals);
// Result: 100500000n
// Smallest unit to human-readable
const formatted = ethers.formatUnits(smallestUnit, decimals);
// Result: '100.5'3. Deadline Management
Set reasonable deadlines:
// Good: 24 hours from now
const deadline = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
// Bad: Past timestamp
const badDeadline = new Date('2020-01-01').toISOString(); // â
// Bad: Too short
const tooShort = new Date(Date.now() + 60 * 1000).toISOString(); // â4. Network Validation
Validate network compatibility:
async function validateNetworks(fromNetwork: string, toNetwork: string) {
if (fromNetwork === toNetwork) {
throw new Error('Source and destination networks must be different');
}
const tokens = await SFA.getTokens();
const fromTokens = tokens.filter(t => t.blockchain === fromNetwork);
const toTokens = tokens.filter(t => t.blockchain === toNetwork);
if (fromTokens.length === 0) {
throw new Error(`Network ${fromNetwork} not supported`);
}
if (toTokens.length === 0) {
throw new Error(`Network ${toNetwork} not supported`);
}
}5. Status Polling
Implement exponential backoff:
async function pollStatus(depositAddress: string) {
let delay = 2000; // Start with 2 seconds
const maxDelay = 30000; // Max 30 seconds
const maxAttempts = 100;
for (let i = 0; i < maxAttempts; i++) {
const status = await SFA.getExecutionStatus(depositAddress);
if (status.status === 'completed' || status.status === 'failed') {
return status;
}
await new Promise(resolve => setTimeout(resolve, delay));
// Exponential backoff
delay = Math.min(delay * 1.5, maxDelay);
}
throw new Error('Polling timeout');
}Common Use Cases
Use Case 1: Simple USDT Bridge
Bridge USDT from Ethereum to Arbitrum:
// 1. Get tokens to find asset IDs
const tokens = await SFA.getTokens();
const ethUsdt = tokens.find(t => t.blockchain === 'eth' && t.symbol === 'USDT');
const arbUsdt = tokens.find(t => t.blockchain === 'arb' && t.symbol === 'USDT');
// 2. Create quote request
const quoteRequest: QuoteRequest = {
dry: false,
swapType: QuoteRequest.swapType.EXACT_INPUT,
slippageTolerance: 100,
originAsset: ethUsdt.assetId,
destinationAsset: arbUsdt.assetId,
amount: ethers.parseUnits('100', 6).toString(), // 100 USDT
depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
refundTo: userEthAddress,
refundType: QuoteRequest.refundType.ORIGIN_CHAIN,
recipient: userArbAddress,
recipientType: QuoteRequest.recipientType.DESTINATION_CHAIN,
deadline: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
};
// 3. Get quote
const quote = await SFA.getQuote(quoteRequest);
// 4. Transfer USDT to deposit address
const usdtContract = new ethers.Contract(
'0xdac17f958d2ee523a2206206994597c13d831ec7',
['function transfer(address to, uint256 amount) returns (bool)'],
signer
);
const tx = await usdtContract.transfer(
quote.quote.depositAddress,
ethers.parseUnits('100', 6)
);
await tx.wait();
// 5. Submit transaction
await SFA.submitDepositTx({
txHash: tx.hash,
depositAddress: quote.quote.depositAddress,
});
// 6. Monitor status
const finalStatus = await pollStatus(quote.quote.depositAddress);
console.log('Bridge completed!', finalStatus.destinationTxHash);Use Case 2: Dynamic Token Selection
Let users choose any supported token pair:
async function buildTokenPairSelector() {
const tokens = await SFA.getTokens();
// Group by network
const byNetwork = tokens.reduce((acc, token) => {
if (!acc[token.blockchain]) {
acc[token.blockchain] = [];
}
acc[token.blockchain].push(token);
return acc;
}, {} as Record<string, TokenResponse[]>);
// Build UI options
const networks = Object.keys(byNetwork);
return {
networks,
getTokensForNetwork: (network: string) => byNetwork[network],
findToken: (network: string, symbol: string) =>
byNetwork[network]?.find(t => t.symbol === symbol),
};
}Use Case 3: Fee Calculator
Calculate bridge fees before execution:
async function calculateFees(
fromToken: TokenResponse,
toToken: TokenResponse,
amount: string
) {
const quoteRequest: QuoteRequest = {
dry: true, // Testing mode
swapType: QuoteRequest.swapType.EXACT_INPUT,
slippageTolerance: 100,
originAsset: fromToken.assetId,
destinationAsset: toToken.assetId,
amount,
// ... other required fields
};
const quote = await SFA.getQuote(quoteRequest);
const feeUsd = parseFloat(quote.quote.amountInUsd) -
parseFloat(quote.quote.amountOutUsd);
const feePercent = (feeUsd / parseFloat(quote.quote.amountInUsd)) * 100;
return {
feeUsd: feeUsd.toFixed(4),
feePercent: feePercent.toFixed(2),
estimatedTime: quote.quote.timeEstimate,
minimumReceived: quote.quote.amountOutFormatted,
};
}Troubleshooting
Common Issues
1. "Invalid token" Error
Cause: Using incorrect assetId format
Solution: Always get asset IDs from getTokens():
const tokens = await SFA.getTokens();
const token = tokens.find(t =>
t.blockchain === 'eth' && t.symbol === 'USDT'
);
// Use: token.assetId2. "Deadline is not valid" Error
Cause: Deadline is in the past or incorrect format
Solution: Use ISO 8601 format with future timestamp:
const deadline = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();3. Authentication Errors
Cause: Missing or invalid JWT token
Solution:
After receiving your token, set it before any API calls:
OpenAPI.TOKEN = 'your-valid-token';4. Network Mismatch
Cause: Wallet on wrong network when sending transaction
Solution: Check and switch network before transaction:
const currentChainId = await window.ethereum.request({
method: 'eth_chainId'
});
if (currentChainId !== expectedChainId) {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: expectedChainId }],
});
}5. Transaction Stuck
Cause: Transaction not confirmed on blockchain
Solution: Wait for confirmation before submitting:
const tx = await usdtContract.transfer(depositAddress, amount);
const receipt = await tx.wait(); // Wait for confirmation
console.log('Confirmed in block:', receipt.blockNumber);
// Now submit to StableFlow
await SFA.submitDepositTx({ txHash: tx.hash, depositAddress });Additional Resources
SDK Repository
GitHub: stableflow-ai/stableflow-ai-sdk
Examples:
examples/directoryIssues: Report bugs via GitHub Issues
StableFlow Platform
Website: https://app.stableflow.ai/
API Access: Apply for JWT token
Documentation: Latest API specs
Community
Discord: Join our community
Twitter: @StableFlowAI
Summary
Quick Reference
getTokens()
List supported tokens
JWT Token
getQuote()
Get swap quote
JWT Token, QuoteRequest
submitDepositTx()
Notify deposit
JWT Token, txHash, depositAddress
getExecutionStatus()
Check swap status
JWT Token, depositAddress
Integration Checklist
Happy Building!
For questions or support, visit our GitHub repository or join our Discord community.
Last updated