Skip to main content
This flow starts with a funded source asset on Base or Solana and ends with BTC delivered to Spark, Bitcoin L1, or Lightning. If you want a reusable deposit address that runs this flow automatically on each deposit, use Accumulation Addresses. Common uses:
  • Wallet buy flow: users pay USDC and receive BTC to a Spark address, Bitcoin address, or Lightning invoice.
  • Payout rails: convert USDC into BTC without running Bitcoin infrastructure.

Supported Routes

  • USDC (base|solana) -> BTC (spark)
  • USDC (base|solana) -> BTC (bitcoin)
  • USDC (base|solana) -> BTC (lightning)
  • USDC (base|solana) -> USDB (spark)
  • ETH (base) -> BTC (spark)
  • ETH (base) -> BTC (bitcoin)
  • ETH (base) -> BTC (lightning)
  • SOL (solana) -> BTC (spark)
  • SOL (solana) -> BTC (bitcoin)
  • SOL (solana) -> BTC (lightning)
  • ETH (base) -> USDB (spark)
  • SOL (solana) -> USDB (spark)

Token Identifiers

If you need to construct a USDC transfer:
ChainUSDC Identifier
Base0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
SolanaEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Flow

  1. Create a quote: POST /v1/orchestration/quote.
  2. Send the source asset to depositAddress for amountIn.
  3. Submit the deposit transaction: POST /v1/orchestration/submit.
  4. Track status via webhooks or GET /v1/orchestration/status?id=....
Quotes expire 30 minutes after creation.

Affiliate Fee Note

App fees are supported on Stablecoin -> BTC routes when the source is base or solana. Behavior:
  • Fees are computed in USDC units on the source-side USDC leg.
  • For SOL/ETH source routes, the engine first converts to USDC, then applies platform + app fees atomically on that source-side USDC amount.
  • You can pass recipients inline (appFees) or reference a registered profile (affiliateId).
  • Use either appFees or affiliateId, not both.
  • Fee recipient addresses must match the source chain format.
  • The remaining net amount is bridged/swapped into BTC or USDB.
  • Quote response includes totalFeeAmount, and includes appFeeAmount and appFees when requested.

Example: USDC (Base) to BTC (Spark)

const BASE_URL = 'https://orchestration.flashnet.xyz';

const quote = await fetch(`${BASE_URL}/v1/orchestration/quote`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    // Required for authenticated quotes.
    'X-Idempotency-Key': `quote:${Date.now()}`,
  },
  body: JSON.stringify({
    sourceChain: 'base',
    sourceAsset: 'USDC',
    destinationChain: 'spark',
    destinationAsset: 'BTC',
    amount: '100000000',
    recipientAddress: 'spark1...',
    slippageBps: 50,
  }),
}).then((r) => r.json());

// Send USDC to quote.depositAddress for quote.amountIn.
// Capture the deposit transaction hash.
const txHash = await sendUsdcOnBase({
  to: quote.depositAddress,
  amountUsdcSmallest: quote.amountIn,
});

const submit = await fetch(`${BASE_URL}/v1/orchestration/submit`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    'X-Idempotency-Key': `submit:${quote.quoteId}:${txHash}`,
  },
  body: JSON.stringify({
    quoteId: quote.quoteId,
    txHash,
    // Optional but recommended. When present, the deposit verifier checks the sender address.
    sourceAddress: '0xYourSenderAddress',
  }),
}).then((r) => r.json());

const status = await fetch(
  `${BASE_URL}/v1/orchestration/status?id=${encodeURIComponent(submit.orderId)}`,
).then((r) => r.json());

console.log(status.order.status);

Example: SOL (Solana) to BTC (Spark)

{
  "sourceChain": "solana",
  "sourceAsset": "SOL",
  "destinationChain": "spark",
  "destinationAsset": "BTC",
  "amount": "25000000",
  "recipientAddress": "spark1...",
  "slippageBps": 50
}
amount is lamports (1 SOL = 1_000_000_000 lamports).

Example: SOL (Solana) to BTC (Spark) with Affiliate Fee

{
  "sourceChain": "solana",
  "sourceAsset": "SOL",
  "destinationChain": "spark",
  "destinationAsset": "BTC",
  "amount": "100000000",
  "recipientAddress": "spark1...",
  "appFees": [
    {
      "recipient": "Dtkxt55zEUDj6NTXGRtH8uQsACsTjoSVkXfYBEF8xkkT",
      "fee": 100
    }
  ],
  "slippageBps": 50
}

Example: SOL (Solana) to BTC (Spark) with Affiliate Registry

{
  "sourceChain": "solana",
  "sourceAsset": "SOL",
  "destinationChain": "spark",
  "destinationAsset": "BTC",
  "amount": "100000000",
  "recipientAddress": "spark1...",
  "affiliateId": "swapkit",
  "slippageBps": 50
}

Example: USDC to BTC (Bitcoin L1)

Set destinationChain = bitcoin and provide a Bitcoin address in recipientAddress.
{
  "sourceChain": "base",
  "sourceAsset": "USDC",
  "destinationChain": "bitcoin",
  "destinationAsset": "BTC",
  "amount": "100000000",
  "recipientAddress": "bc1...",
  "slippageBps": 50
}
The submit step is the same as any USDC source quote.

Example: USDC to BTC (Lightning)

For Lightning payouts, recipientAddress must be a 0-amount (amountless) BOLT11 invoice.
{
  "sourceChain": "solana",
  "sourceAsset": "USDC",
  "destinationChain": "lightning",
  "destinationAsset": "BTC",
  "amount": "25000000",
  "recipientAddress": "lnbc1...",
  "slippageBps": 50
}
The submit step is the same as any USDC source quote.

Deposit Verification Notes

The engine verifies that the deposit transaction sent USDC to the quoted depositAddress.
  • Base: the verifier checks for a USDC Transfer log to depositAddress and, when sourceAddress is provided, requires the log sender to match.
  • Solana: the verifier checks that depositAddress received at least amountIn of USDC in the transaction and, when sourceAddress is provided, checks that address had a corresponding debit.
Send exactly amountIn. On Base, deposits must match the quoted amount. On Solana, deposits of at least amountIn pass verification, but the engine still processes only the quoted amount and any surplus is not attributed to the order.

Next Steps