RathRath Finance
XPath

Fspot Flash APIs

Integrate the Flash API backend for cross-chain swap applications: authentication, quotes, swap execution, and status tracking.

Complete documentation for integrating with the Flash API backend for cross-chain swap applications.

Table of Contents


Overview

The Flash API provides a comprehensive backend service for cross-chain USDC swaps using the Gateway protocol. It handles:

  • Chain configuration management
  • Token listings and prices
  • Quote generation for swaps
  • Swap execution and tracking
  • User authentication and referrals
  • Transaction history

Base URL

Production: https://flash.rath.fi

Authentication

Flash API uses wallet signature-based authentication.

Flow:

  1. Request a message to sign
  2. Sign the message with user's wallet
  3. Verify signature to receive JWT token
  4. Include token in subsequent requests

Headers:

Authorization: Bearer <jwt_token>
Content-Type: application/json

Chain Configuration

Dynamic Chain Support

All chain configurations are fetched from the backend, making it easy to add/remove chains without frontend changes.

Key Features:

  • Gateway wallet addresses
  • Gateway minter addresses
  • USDC contract addresses
  • Chain metadata (logo, explorer, name)
  • Domain numbers for Gateway protocol

API Endpoints

1. Supported Chains

Get all supported chains with their configuration.

Endpoint: GET /supported-chains

Response:

[
  {
    "id": 8453,                    // Chain ID
    "name": "Base",                // Chain name
    "displayName": "Base",         // Display name
    "logoUrl": "https://...",      // Chain logo URL
    "explorerUrl": "https://...",  // Block explorer URL
    "domain": 6,                   // Gateway protocol domain
    "routerAddress": "0x...",      // Swap router address
    "gatewayWallet": "0x...",      // Gateway wallet contract
    "gatewayMinter": "0x...",      // Gateway minter contract
    "nativeToken": {
      "address": "0x0000000000000000000000000000000000000000",
      "name": "Ethereum",
      "symbol": "ETH",
      "decimals": 18,
      "logoUrl": "https://..."
    },
    "usdc": {
      "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "name": "USD Coin",
      "symbol": "USDC",
      "decimals": 6,
      "logoUrl": "https://..."
    }
  }
]

Example:

const chains = await fetch('https://flash.rath.fi/supported-chains')
  .then(res => res.json());

2. Authentication

2.1 Request Sign Message

Endpoint: POST /auth/message

Request:

{
  "userAddress": "0x1234567890abcdef1234567890abcdef12345678"
}

Response:

{
  "message": "Sign this message to authenticate with Flash Computer...",
  "expiry": 1705449600,
  "messageId": "550e8400-e29b-41d4-a716-446655440000"
}

2.2 Verify Signature

Endpoint: POST /auth/verify

Request:

{
  "signature": "0xabcdef...",
  "messageId": "550e8400-e29b-41d4-a716-446655440000",
  "referredByCode": "REFERRAL123"  // Optional
}

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user_address": "0x1234567890abcdef1234567890abcdef12345678",
  "referralCode": "ABC123XYZ",
  "is_new_user": true
}

Complete Flow Example:

// 1. Request message
const { message, messageId } = await fetch('https://flash.rath.fi/auth/message', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ userAddress: address })
}).then(res => res.json());

// 2. Sign message with wallet
const signature = await walletClient.signMessage({ message });

// 3. Verify and get token
const { token } = await fetch('https://flash.rath.fi/auth/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ signature, messageId })
}).then(res => res.json());

// 4. Use token for authenticated requests
const authenticatedHeaders = {
  'Authorization': `Bearer ${token}`,
  'Content-Type': 'application/json'
};

3. Token List

Get available tokens for swapping.

Endpoint: GET /token-list?chain_id={chainId}

Query Parameters:

  • chain_id (optional): Filter tokens by chain ID. Omit for all chains.

Response:

{
  "tokens": [
    {
      "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "symbol": "USDC",
      "decimals": 6,
      "logo_url": "https://...",
      "name": "USD Coin",
      "chain_id": 8453,
      "in_rath_token_list": true
    }
  ]
}

Examples:

// Get all tokens
const { tokens } = await fetch('https://flash.rath.fi/token-list')
  .then(res => res.json());

// Get tokens for Base (chain ID 8453)
const { tokens } = await fetch('https://flash.rath.fi/token-list?chain_id=8453')
  .then(res => res.json());

4. Quote

Get a quote for a swap.

Endpoint: GET /quote

Query Parameters:

  • inputAmount: Amount in USDC smallest units (1 USDC = 1000000)
  • outputToken: Output token address
  • chainId: Destination chain ID
  • from: User wallet address
  • slippage: Slippage tolerance (1.5 = 1.5%)
  • recipient: Recipient address (optional if not passed, defaults to from)

Example Request:

GET /quote?inputAmount=1000000&outputToken=0x...&chainId=8453&from=0x...&slippage=1.5&recipient=0x...

Response:

{
  "token_in": {
    "address": "0x...",
    "amount": "1000000",
    "decimals": 6,
    "logo_url": "https://...",
    "price_usd": "1.00",
    "usd_amount": "1.00"
  },
  "token_out": {
    "address": "0x...",
    "amount": "998500",
    "decimals": 6,
    "logo_url": "https://...",
    "price_usd": "1.00",
    "usd_amount": "0.9985"
  },
  "fees": {
    "total_fee": "1500",
    "gateway_fee": "1000",
    "execution_fee": "500"
  },
  "single_swap_router": "0x...",
  "hook_data": "0x...",
  "swap_router_address": "0x...",
  "swap_calldata": "0x...",
  "expires_at": 1705449600
}

Example:

const params = new URLSearchParams({
  inputAmount: '1000000',  // 1 USDC
  outputToken: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
  chainId: '8453',
  from: userAddress,
  slippage: '1.5'
});

const quote = await fetch(`https://flash.rath.fi/quote?${params}`)
  .then(res => res.json());

5. Gateway Balance

Get user's balance across all chains in the Gateway protocol.

Endpoint: GET /balance/{address}

Response:

{
  "balances": [
    {
      "domain": 6,
      "depositor": "0x...",
      "balance": "5000000",     // Balance in USDC smallest units
      "chainId": 8453,
      "chainName": "Base"
    }
  ],
  "totalBalance": "5000000"     // Total across all chains
}

Example:

const { balances, totalBalance } = await fetch(
  `https://flash.rath.fi/balance/${userAddress}`
).then(res => res.json());

6. Swap Execution

Execute a swap transaction.

Endpoint: POST /swap

Authentication: Required (Bearer token)

Request:

{
  "signedOrder": [...],           // Array of signed burn intents
  "userAddress": "0x...",
  "chainId": 8453,
  "swapRouterAddress": "0x...",
  "swapCalldata": "0x...",
  "slippageBps": 150              // 150 basis points = 1.5%
}

Response:

{
  "transactionHash": "0xabcdef...",
  "status": "pending",
  "message": "Swap initiated successfully"
}

Example:

// After creating and signing burn intents
const result = await fetch('https://flash.rath.fi/swap', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    signedOrder: signedBurnIntents,
    userAddress: address,
    chainId: destinationChainId,
    swapRouterAddress: quote.swap_router_address,
    swapCalldata: quote.swap_calldata,
    slippageBps: 150
  })
}).then(res => res.json());

7. Search Token

Search for a token by address.

Endpoint: POST /search-token

Request:

{
  "chainId": 8453,
  "address": "0x..."
}

Response:

{
  "address": "0x...",
  "symbol": "UNI",
  "decimals": 18,
  "name": "Uniswap",
  "chainId": 8453,
  "logoUrl": "https://...",
  "inRathTokenList": false
}

Example:

const token = await fetch('https://flash.rath.fi/search-token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    chainId: 8453,
    address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'
  })
}).then(res => res.json());

8. Swap History

Get user's swap transaction history.

Endpoint: GET /history?page={page}&page_size={size}&status={status}

Authentication: Required (Bearer token)

Query Parameters:

  • page (optional): Page number (default: 1)
  • page_size (optional): Items per page (default: 20)
  • status (optional): Filter by status (pending, completed, failed)

Response:

{
  "swaps": [
    {
      "id": "uuid",
      "sourceDomain": 6,
      "destinationDomain": 3,
      "sourceToken": "0x...",
      "destinationToken": "0x...",
      "tokenIn": {
        "address": "0x...",
        "symbol": "USDC",
        "decimals": 6,
        "logo_url": "https://...",
        "name": "USD Coin",
        "chain_id": 8453,
        "in_rath_token_list": true
      },
      "tokenOut": {
        "address": "0x...",
        "symbol": "UNI",
        "decimals": 18,
        "logo_url": "https://...",
        "name": "Uniswap",
        "chain_id": 42161,
        "in_rath_token_list": true
      },
      "amountUsdc": "1.00",
      "amountUsdcRaw": "1000000",
      "chainId": 42161,
      "minAmountOut": "0.9985",
      "rathFee": "0.0015",
      "rathFeeRaw": "1500",
      "transactionHash": "0x...",
      "status": "completed",
      "txId": "gateway-tx-id",
      "createdAt": "2024-01-17T10:00:00Z",
      "updatedAt": "2024-01-17T10:05:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "page_size": 20,
    "total_entries": 45,
    "total_pages": 3,
    "has_next": true,
    "has_previous": false
  },
  "summary": {
    "totalSwaps": 45,
    "totalVolume": "12500.50",
    "totalVolumeRaw": "12500500000",
    "totalFees": "18.75",
    "totalFeesRaw": "18750000"
  }
}

Example:

const history = await fetch(
  'https://flash.rath.fi/history?page=1&page_size=20',
  {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  }
).then(res => res.json());

9. Leaderboard

Get trading leaderboard rankings.

Endpoint: GET /leaderboard?page={page}&page_size={size}&time_range={range}

Query Parameters:

  • page (optional): Page number (default: 1)
  • page_size (optional): Items per page (default: 50)
  • time_range (optional): Time filter (all, 30d, 7d, 24h)

Response:

{
  "entries": [
    {
      "rank": 1,
      "walletAddress": "0x...",
      "totalVolume": "50000.00",
      "totalVolumeRaw": "50000000000",
      "swapCount": 150,
      "referralCount": 25,
      "referralVolume": "5000.00",
      "referralVolumeRaw": "5000000000",
      "combined_volume": "55000.00",
      "referralCode": "ABC123",
      "lastSwapAt": "2024-01-17T10:00:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "page_size": 50,
    "total_entries": 250,
    "total_pages": 5,
    "has_next": true,
    "has_previous": false
  }
}

Example:

const leaderboard = await fetch(
  'https://flash.rath.fi/leaderboard?time_range=7d'
).then(res => res.json());

10. User Referrals

Get user's referral information.

Endpoint: GET /referrals

Authentication: Required (Bearer token)

Response:

{
  "referrals": [
    {
      "user_address": "0x...",
      "total_volume_usdc": 1500.25
    }
  ],
  "total_referral_volume": 5250.75,
  "total_referrals_count": 3,
  "remaining_referral_limit": 7
}

Example:

const referrals = await fetch('https://flash.rath.fi/referrals', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
}).then(res => res.json());

TypeScript Integration

Gateway Protocol Integration

Creating Burn Intents (Signed Orders)

Burn intents are EIP-712 signed messages that authorize cross-chain USDC transfers via the Gateway protocol.

Step 1: Define EIP-712 Types

const EIP712Domain = [
  { name: "name", type: "string" },
  { name: "version", type: "string" },
];

const TransferSpec = [
  { name: "version", type: "uint32" },
  { name: "sourceDomain", type: "uint32" },
  { name: "destinationDomain", type: "uint32" },
  { name: "sourceContract", type: "bytes32" },
  { name: "destinationContract", type: "bytes32" },
  { name: "sourceToken", type: "bytes32" },
  { name: "destinationToken", type: "bytes32" },
  { name: "sourceDepositor", type: "bytes32" },
  { name: "destinationRecipient", type: "bytes32" },
  { name: "sourceSigner", type: "bytes32" },
  { name: "destinationCaller", type: "bytes32" },
  { name: "value", type: "uint256" },
  { name: "salt", type: "bytes32" },
  { name: "hookData", type: "bytes" },
];

const BurnIntent = [
  { name: "maxBlockHeight", type: "uint256" },
  { name: "maxFee", type: "uint256" },
  { name: "spec", type: "TransferSpec" },
];

const BurnIntentSet = [
  { name: "intents", type: "BurnIntent[]" }
];

const domain = { name: "GatewayWallet", version: "1" };

Step 2: Create Burn Intent

import { pad, maxUint256 } from 'viem';

// Helper to convert address to bytes32
function addressToBytes32(address: string): string {
  return pad(address.toLowerCase() as `0x${string}`, { size: 32 });
}

// Generate random salt
function generateRandomSalt(): string {
  const bytes = new Uint8Array(32);
  crypto.getRandomValues(bytes);
  return '0x' + Array.from(bytes)
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

// Create a burn intent
function createBurnIntent({
  userAddress,
  sourceChain,
  destinationChain,
  amount,
  recipient,
  hookData = '0x',
  maxFee = '2010000'  // Gateway fee in smallest units
}: {
  userAddress: string;
  sourceChain: ChainConfig;
  destinationChain: ChainConfig;
  amount: string;  // Amount in smallest units (e.g., 1000000 for 1 USDC)
  recipient: string;
  hookData?: string;
  maxFee?: string;
}) {
  return {
    maxBlockHeight: maxUint256,  // Far future block
    maxFee: BigInt(maxFee),
    spec: {
      version: 1,
      sourceDomain: sourceChain.domain,
      destinationDomain: destinationChain.domain,
      sourceContract: sourceChain.gatewayWallet,
      destinationContract: destinationChain.gatewayMinter,
      sourceToken: sourceChain.usdc.address,
      destinationToken: destinationChain.usdc.address,
      sourceDepositor: userAddress,
      destinationRecipient: recipient,
      sourceSigner: userAddress,
      destinationCaller: recipient,
      value: BigInt(amount),
      salt: generateRandomSalt(),
      hookData: hookData,
    },
  };
}

Step 3: Create Typed Data for Signing

function createBurnIntentTypedData(burnIntent: any) {
  return {
    types: { EIP712Domain, TransferSpec, BurnIntent },
    domain,
    primaryType: "BurnIntent",
    message: {
      maxBlockHeight: burnIntent.maxBlockHeight,
      maxFee: burnIntent.maxFee,
      spec: {
        ...burnIntent.spec,
        sourceContract: addressToBytes32(burnIntent.spec.sourceContract),
        destinationContract: addressToBytes32(burnIntent.spec.destinationContract),
        sourceToken: addressToBytes32(burnIntent.spec.sourceToken),
        destinationToken: addressToBytes32(burnIntent.spec.destinationToken),
        sourceDepositor: addressToBytes32(burnIntent.spec.sourceDepositor),
        destinationRecipient: addressToBytes32(burnIntent.spec.destinationRecipient),
        sourceSigner: addressToBytes32(burnIntent.spec.sourceSigner),
        destinationCaller: addressToBytes32(burnIntent.spec.destinationCaller),
      },
    },
  };
}

// For multiple intents (burn intent set)
function createBurnIntentSetTypedData(burnIntents: any[]) {
  return {
    types: { EIP712Domain, TransferSpec, BurnIntent, BurnIntentSet },
    domain,
    primaryType: "BurnIntentSet",
    message: {
      intents: burnIntents.map(intent => 
        createBurnIntentTypedData(intent).message
      ),
    },
  };
}

Step 4: Sign Burn Intent(s)

// Using ethers.js
import { ethers } from 'ethers';

async function signBurnIntent(
  wallet: ethers.Wallet,
  burnIntent: any
): Promise<string> {
  const typedData = createBurnIntentTypedData(burnIntent);
  const signature = await wallet._signTypedData(
    typedData.domain,
    { TransferSpec: typedData.types.TransferSpec, BurnIntent: typedData.types.BurnIntent },
    typedData.message
  );
  return signature;
}

// Using viem
import { signTypedData } from 'viem/accounts';

async function signBurnIntentViem(
  walletClient: any,
  burnIntent: any
): Promise<string> {
  const typedData = createBurnIntentTypedData(burnIntent);
  const signature = await walletClient.signTypedData({
    domain: typedData.domain,
    types: {
      TransferSpec: typedData.types.TransferSpec,
      BurnIntent: typedData.types.BurnIntent
    },
    primaryType: 'BurnIntent',
    message: typedData.message
  });
  return signature;
}

Complete Example: Creating Signed Order for Swap

async function createSignedSwapOrder({
  walletClient,
  userAddress,
  sourceChains,      // Array of chains to pull USDC from
  destinationChain,  // Chain to swap on
  allocations,       // Amount to pull from each source chain
  recipient,         // Swap router address (from quote)
  hookData,          // Hook data from quote
  gatewayFee         // Gateway fee from quote
}: {
  walletClient: any;
  userAddress: string;
  sourceChains: ChainConfig[];
  destinationChain: ChainConfig;
  allocations: { chainId: number; amount: string }[];
  recipient: string;
  hookData: string;
  gatewayFee: string;
}) {
  // Create burn intents for each source chain
  const burnIntents = allocations.map((allocation, index) => {
    const sourceChain = sourceChains[index];
    return createBurnIntent({
      userAddress,
      sourceChain,
      destinationChain,
      amount: allocation.amount,
      recipient,
      hookData,
      maxFee: gatewayFee
    });
  });

  // Create typed data for signing
  const typedData = createBurnIntentSetTypedData(burnIntents);

  // Sign the burn intent set
  const signature = await walletClient.signTypedData({
    domain: typedData.domain,
    types: {
      TransferSpec: typedData.types.TransferSpec,
      BurnIntent: typedData.types.BurnIntent,
      BurnIntentSet: typedData.types.BurnIntentSet
    },
    primaryType: 'BurnIntentSet',
    message: typedData.message
  });

  // Format for API
  const signedOrder = burnIntents.map((intent, index) => ({
    intent,
    signature: index === 0 ? signature : undefined // Only first intent needs signature
  }));

  return signedOrder;
}

Depositing USDC to Gateway Wallet

To deposit USDC into the Gateway wallet, you can use the depositWithPermit function which combines USDC approval and deposit in a single transaction using EIP-2612 permits.

Gateway Wallet ABI

const gatewayWalletAbi = [
  {
    inputs: [
      { internalType: "address", name: "token", type: "address" },
      { internalType: "address", name: "owner", type: "address" },
      { internalType: "uint256", name: "value", type: "uint256" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
      { internalType: "uint8", name: "v", type: "uint8" },
      { internalType: "bytes32", name: "r", type: "bytes32" },
      { internalType: "bytes32", name: "s", type: "bytes32" }
    ],
    name: "depositWithPermit",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function"
  }
];

Step 1: Generate USDC Permit Signature

import { parseUnits } from 'viem';

async function generateUSDCPermit({
  walletClient,
  publicClient,
  chainId,
  usdcAddress,
  usdcName,
  usdcVersion,
  ownerAddress,
  spenderAddress,  // Gateway wallet address
  amount
}: {
  walletClient: any;
  publicClient: any;
  chainId: number;
  usdcAddress: string;
  usdcName: string;
  usdcVersion: string;
  ownerAddress: string;
  spenderAddress: string;
  amount: bigint;
}) {
  // Get current nonce
  const nonce = await publicClient.readContract({
    address: usdcAddress,
    abi: [{
      inputs: [{ name: 'owner', type: 'address' }],
      name: 'nonces',
      outputs: [{ name: '', type: 'uint256' }],
      stateMutability: 'view',
      type: 'function',
    }],
    functionName: 'nonces',
    args: [ownerAddress],
  });

  // Set deadline (e.g., 1 hour from now)
  const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);

  // EIP-2612 Permit types
  const types = {
    Permit: [
      { name: "owner", type: "address" },
      { name: "spender", type: "address" },
      { name: "value", type: "uint256" },
      { name: "nonce", type: "uint256" },
      { name: "deadline", type: "uint256" }
    ]
  };

  // Domain
  const domain = {
    name: usdcName,
    version: usdcVersion,
    chainId,
    verifyingContract: usdcAddress
  };

  // Sign permit
  const signature = await walletClient.signTypedData({
    domain,
    types,
    primaryType: 'Permit',
    message: {
      owner: ownerAddress,
      spender: spenderAddress,
      value: amount,
      nonce,
      deadline
    }
  });

  // Parse signature
  const { r, s, v } = parseSignature(signature);

  return { deadline, v, r, s };
}

function parseSignature(signature: string) {
  const r = `0x${signature.slice(2, 66)}`;
  const s = `0x${signature.slice(66, 130)}`;
  const v = parseInt(signature.slice(130, 132), 16);
  return { r, s, v };
}

Step 2: Execute depositWithPermit

import { writeContract, waitForTransactionReceipt } from 'viem';

async function depositWithPermit({
  walletClient,
  publicClient,
  chainConfig,
  amount,  // Amount in USDC (e.g., "1.5" for 1.5 USDC)
  ownerAddress
}: {
  walletClient: any;
  publicClient: any;
  chainConfig: ChainConfig;
  amount: string;
  ownerAddress: string;
}) {
  // Convert amount to smallest units (USDC has 6 decimals)
  const amountInWei = parseUnits(amount, 6);

  // Generate permit signature
  const { deadline, v, r, s } = await generateUSDCPermit({
    walletClient,
    publicClient,
    chainId: chainConfig.id,
    usdcAddress: chainConfig.usdc.address,
    usdcName: chainConfig.usdc.name,
    usdcVersion: chainConfig.usdc.version.toString(),
    ownerAddress,
    spenderAddress: chainConfig.gatewayWallet,
    amount: amountInWei
  });

  // Execute depositWithPermit
  const hash = await walletClient.writeContract({
    address: chainConfig.gatewayWallet,
    abi: gatewayWalletAbi,
    functionName: 'depositWithPermit',
    args: [
      chainConfig.usdc.address,
      ownerAddress,
      amountInWei,
      deadline,
      v,
      r,
      s
    ]
  });

  console.log('Transaction hash:', hash);

  // Wait for confirmation
  const receipt = await waitForTransactionReceipt(publicClient, { hash });
  
  console.log('Deposit confirmed:', receipt);
  return receipt;
}

Complete Deposit Example

// Example: Deposit 10 USDC on Base chain
async function depositExample() {
  // Get chain config from API
  const chains = await flashApi.getSupportedChains();
  const baseChain = chains.find(c => c.id === 8453);
  
  if (!baseChain) {
    throw new Error('Base chain not found');
  }

  // Deposit 10 USDC
  const receipt = await depositWithPermit({
    walletClient,
    publicClient,
    chainConfig: baseChain,
    amount: '10',
    ownerAddress: userAddress
  });

  console.log('✅ Deposited 10 USDC to Gateway wallet');
  console.log('Transaction:', receipt.transactionHash);
  
  // Check new balance
  const balance = await flashApi.getGatewayBalance(userAddress);
  console.log('New Gateway balance:', balance.totalBalance);
}

On this page