Skip to content

Instantly share code, notes, and snippets.

@patcito
Last active February 2, 2026 14:05
Show Gist options
  • Select an option

  • Save patcito/1fc9005c191ab13d60ee85a9c79f8e68 to your computer and use it in GitHub Desktop.

Select an option

Save patcito/1fc9005c191ab13d60ee85a9c79f8e68 to your computer and use it in GitHub Desktop.
FTUSD Dashboard API Documentation - User Balances, Preview Endpoints, USD Values

FTUSD Dashboard API Documentation

This document covers the API endpoints for the FTUSD dashboard, including user balances, staking info, and reward previews.


Endpoints Overview

Endpoint Method Description
/ftusd/user/balances GET Get user's ftUSD balances and USD values
/ftusd/preview/claim GET Preview claimable FT rewards with USD value
/ftusd/preview/mint GET Preview ftUSD output for collateral input
/ftusd/preview/redeem GET Preview collateral output for ftUSD redemption
/ftusd/preview/stake GET Preview sftUSD shares for ftUSD deposit
/ftusd/preview/unstake GET Preview ftUSD output for sftUSD redemption

Standardized Preview Response Format

All preview endpoints (except /preview/claim) now return a consistent response format with both raw and formatted values:

{
  "success": true,
  "chain_id": 146,
  "output": "1000000000",
  "output_formatted": "1000",
  "output_decimals": 6,
  "input": "1000000000",
  "input_formatted": "1000",
  "input_decimals": 6
}
Field Type Description
output string Raw output amount in wei
output_formatted string Human-readable output (e.g., "1000.50")
output_decimals int Decimals for output token (6 for ftUSD/sftUSD)
input string Raw input amount in wei
input_formatted string Human-readable input (e.g., "1000.00")
input_decimals int Decimals for input token

1. User Balances

Get a user's ftUSD-related balances including USD values calculated using on-chain oracle prices.

Request

GET /ftusd/user/balances?address={wallet_address}&chainId={chain_id}
Parameter Required Description
address Yes User's wallet address (0x...)
chainId No Filter by chain ID. If omitted, returns data for all chains

Response

{
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "chain_id": 1,
  "chains": [
    {
      "chain_id": 1,
      "ftusd_balance": "1000000000",
      "ftusd_decimals": 6,
      "sftusd_balance": "500000000",
      "sftusd_decimals": 6,
      "ft_earnings": "25000000000000000000",
      "ft_earnings_decimals": 18,
      "ft_earnings_usd": "12.5",
      "investable_collaterals": [
        {
          "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "token_symbol": "USDC",
          "balance": "5000000000",
          "decimals": 6,
          "value_usd": "5000"
        },
        {
          "token_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
          "token_symbol": "USDT",
          "balance": "2500000000",
          "decimals": 6,
          "value_usd": "2499.75"
        }
      ]
    }
  ],
  "totals": {
    "total_ftusd_balance": "1000000000",
    "total_ftusd_decimals": 6,
    "total_sftusd_balance": "500000000",
    "total_sftusd_decimals": 6,
    "total_ft_earnings": "25000000000000000000",
    "total_ft_earnings_decimals": 18,
    "total_ft_earnings_usd": "12.5",
    "total_investable_usd": "7499.75"
  }
}

Field Descriptions

Field Type Description
ftusd_balance string Raw ftUSD balance (divide by 10^ftusd_decimals for display)
ftusd_decimals int Decimals for ftUSD (always 6)
sftusd_balance string Raw sftUSD (staked) balance
sftusd_decimals int Decimals for sftUSD (always 6)
ft_earnings string Raw accrued FT rewards from staking
ft_earnings_decimals int Decimals for FT token (always 18)
ft_earnings_usd string USD value of FT earnings (decimal string, e.g., "12.5")
investable_collaterals array Tokens user can use to mint ftUSD
value_usd string USD value of collateral (decimal string)
total_investable_usd string Sum of all investable collateral USD values

USD Value Format

All USD values are returned as decimal strings without currency symbols:

  • "12.5" means $12.50
  • "1234.567890" means $1,234.567890
  • "0.000001" means $0.000001
  • "0" means $0.00

USD prices are fetched from the Aave oracle on-chain.

Example Usage (JavaScript)

const response = await fetch(
  `https://api.flyingtulip.com/ftusd/user/balances?address=${walletAddress}`
);
const data = await response.json();

// Display ftUSD balance
const ftUsdBalance = Number(data.totals.total_ftusd_balance) / 1e6;
console.log(`ftUSD Balance: ${ftUsdBalance.toFixed(2)}`);

// Display FT earnings with USD value
const ftEarnings = Number(data.totals.total_ft_earnings) / 1e18;
const ftEarningsUsd = parseFloat(data.totals.total_ft_earnings_usd);
console.log(`FT Earnings: ${ftEarnings.toFixed(4)} FT ($${ftEarningsUsd.toFixed(2)})`);

// Display total investable USD
const investableUsd = parseFloat(data.totals.total_investable_usd);
console.log(`Investable: $${investableUsd.toFixed(2)}`);

2. Preview Claimable Rewards

Preview the amount of FT tokens a user can claim from their sftUSD staking, including USD value.

Request

GET /ftusd/preview/claim?address={wallet_address}&chainId={chain_id}
Parameter Required Description
address Yes User's wallet address (0x...)
chainId No Chain ID (default: 1)

Response

{
  "success": true,
  "chain_id": 146,
  "address": "0x540bCa1D332EA9B8cf46473d97AE73DDD3Cc5fE9",
  "claimable_raw": "4237797833730902631295",
  "claimable_ft": "4237.797833730902631295",
  "ft_price": "0.1",
  "claimable_usd": "423.77978337"
}
Field Type Description
address string The wallet address queried
claimable_raw string Raw claimable FT amount in wei (18 decimals)
claimable_ft string Formatted FT amount (e.g., "4237.79")
ft_price string FT price in USD (e.g., "0.1" = $0.10 per FT)
claimable_usd string USD value of claimable FT (e.g., "423.78")

Example Usage (JavaScript)

const response = await fetch(
  `https://api.flyingtulip.com/ftusd/preview/claim?address=${walletAddress}&chainId=146`
);
const data = await response.json();

if (data.success) {
  // Use pre-formatted values directly
  const claimableFT = parseFloat(data.claimable_ft);
  const ftPrice = parseFloat(data.ft_price);
  const claimableUSD = parseFloat(data.claimable_usd);

  console.log(`Claimable: ${claimableFT.toFixed(2)} FT`);
  console.log(`FT Price: $${ftPrice.toFixed(2)}`);
  console.log(`USD Value: $${claimableUSD.toFixed(2)}`);
}

3. Preview Mint

Preview the ftUSD output for a given collateral input.

Request

GET /ftusd/preview/mint?collateralToken={token_address}&amount={amount}&chainId={chain_id}
Parameter Required Description
collateralToken Yes Collateral token address (e.g., USDC)
amount Yes Collateral amount in wei
chainId No Chain ID (default: 1)

Response

{
  "success": true,
  "chain_id": 146,
  "output": "999500000",
  "output_formatted": "999.5",
  "output_decimals": 6,
  "input": "1000000000",
  "input_formatted": "1000",
  "input_decimals": 6
}

Example Usage (JavaScript)

// Preview minting with 1000 USDC
const usdcAddress = "0x29219dd400f2Bf60E5a23d13Be72B486D4038894"; // Sonic USDC
const amount = (1000 * 1e6).toString(); // 1000 USDC in wei

const response = await fetch(
  `https://api.flyingtulip.com/ftusd/preview/mint?collateralToken=${usdcAddress}&amount=${amount}&chainId=146`
);
const data = await response.json();

// Use the formatted output directly
console.log(`You will receive: ${data.output_formatted} ftUSD`);

// Or calculate from raw values
const ftUsdOutput = Number(data.output) / Math.pow(10, data.output_decimals);
console.log(`You will receive: ${ftUsdOutput.toFixed(2)} ftUSD`);

4. Preview Redeem

Preview the collateral output for redeeming ftUSD.

Request

GET /ftusd/preview/redeem?collateralToken={token_address}&amount={amount}&chainId={chain_id}
Parameter Required Description
collateralToken Yes Desired collateral token address
amount Yes ftUSD amount to redeem (in wei, 6 decimals)
chainId No Chain ID (default: 1)

Response

{
  "success": true,
  "chain_id": 146,
  "output": "999000000",
  "output_formatted": "999",
  "output_decimals": 6,
  "input": "1000000000",
  "input_formatted": "1000",
  "input_decimals": 6
}

5. Preview Stake

Preview the sftUSD shares for staking ftUSD.

Request

GET /ftusd/preview/stake?amount={amount}&chainId={chain_id}
Parameter Required Description
amount Yes ftUSD amount to stake (in wei, 6 decimals)
chainId No Chain ID (default: 1)

Response

{
  "success": true,
  "chain_id": 146,
  "output": "1000000000",
  "output_formatted": "1000",
  "output_decimals": 6,
  "input": "1000000000",
  "input_formatted": "1000",
  "input_decimals": 6
}

The output is the sftUSD shares you'll receive. Initially 1:1 with ftUSD.


6. Preview Unstake

Preview the ftUSD output for unstaking sftUSD shares.

Request

GET /ftusd/preview/unstake?shares={shares}&chainId={chain_id}
Parameter Required Description
shares Yes sftUSD shares to unstake (in wei, 6 decimals)
chainId No Chain ID (default: 1)

Response

{
  "success": true,
  "chain_id": 146,
  "output": "1005000000",
  "output_formatted": "1005",
  "output_decimals": 6,
  "input": "1000000000",
  "input_formatted": "1000",
  "input_decimals": 6
}

Dashboard Data Mapping

Here's how to map API responses to common dashboard components:

Personal Stats Card

Display API Field Calculation
ftUSD Balance totals.total_ftusd_balance value / 10^6
ftUSD Staked totals.total_sftusd_balance value / 10^6
Unclaimed FT Use /ftusd/preview/claim claimable_ft field
Unclaimed FT (USD) Use /ftusd/preview/claim claimable_usd field
FT Price Use /ftusd/preview/claim ft_price field
Investable Balance totals.total_investable_usd parseFloat directly

Example React Component

interface DashboardStats {
  ftUsdBalance: number;
  ftUsdStaked: number;
  claimableFT: number;
  claimableFTUsd: number;
  ftPrice: number;
  investableUsd: number;
}

async function fetchDashboardStats(address: string, chainId: number): Promise<DashboardStats> {
  // Fetch user balances
  const balancesRes = await fetch(`/ftusd/user/balances?address=${address}&chainId=${chainId}`);
  const balances = await balancesRes.json();

  // Fetch claimable rewards with USD value
  const claimRes = await fetch(`/ftusd/preview/claim?address=${address}&chainId=${chainId}`);
  const claim = await claimRes.json();

  return {
    ftUsdBalance: Number(balances.totals?.total_ftusd_balance || 0) / 1e6,
    ftUsdStaked: Number(balances.totals?.total_sftusd_balance || 0) / 1e6,
    claimableFT: parseFloat(claim.claimable_ft || "0"),
    claimableFTUsd: parseFloat(claim.claimable_usd || "0"),
    ftPrice: parseFloat(claim.ft_price || "0"),
    investableUsd: parseFloat(balances.totals?.total_investable_usd || "0"),
  };
}

// Display
function StatsCard({ stats }: { stats: DashboardStats }) {
  return (
    <div>
      <div>ftUSD Balance: {stats.ftUsdBalance.toFixed(2)}</div>
      <div>Staked: {stats.ftUsdStaked.toFixed(2)} sftUSD</div>
      <div>
        Claimable: {stats.claimableFT.toFixed(2)} FT
        (${stats.claimableFTUsd.toFixed(2)})
      </div>
      <div>FT Price: ${stats.ftPrice.toFixed(2)}</div>
      <div>Investable: ${stats.investableUsd.toFixed(2)}</div>
    </div>
  );
}

Using Formatted Preview Values

// Preview component using new formatted fields
async function previewStake(amount: string, chainId: number) {
  const response = await fetch(
    `/ftusd/preview/stake?amount=${amount}&chainId=${chainId}`
  );
  const data = await response.json();

  if (data.success) {
    // Use formatted values directly - no manual division needed!
    return {
      inputDisplay: `${data.input_formatted} ftUSD`,
      outputDisplay: `${data.output_formatted} sftUSD`,
    };
  }
}

// Example for mint preview
async function previewMint(collateralToken: string, amount: string, chainId: number) {
  const response = await fetch(
    `/ftusd/preview/mint?collateralToken=${collateralToken}&amount=${amount}&chainId=${chainId}`
  );
  const data = await response.json();

  if (data.success) {
    return {
      inputDisplay: `${data.input_formatted} tokens`,
      outputDisplay: `${data.output_formatted} ftUSD`,
      // decimals available if needed for further calculations
      inputDecimals: data.input_decimals,
      outputDecimals: data.output_decimals,
    };
  }
}

Error Handling

All endpoints return HTTP error codes with plain text messages:

Code Description
400 Bad Request - Invalid parameters
500 Internal Server Error
503 Service Unavailable - Rate limited

Example error response:

HTTP/1.1 400 Bad Request
Content-Type: text/plain

Invalid or missing address

Notes

  1. USD values use on-chain oracle prices from FlyingTulip Oracle (for FT) and Aave Oracle (for collaterals)
  2. All raw balances are strings to avoid JavaScript number precision issues with large values
  3. USD values are decimal strings - parse with parseFloat() for display
  4. Chain ID defaults to 1 (Ethereum mainnet) if not specified. Use chainId=146 for Sonic.
  5. Rate limiting is in place - handle 503 errors with exponential backoff
  6. FT claims are in FT tokens (18 decimals), NOT ftUSD - the API provides formatted values for convenience
  7. All preview endpoints now include formatted values - output_formatted, input_formatted, and decimals fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment