Skip to main content
Accumulation and liquidation addresses are the same primitive with the input asset swapped. Both are persistent deposit addresses. Both run every inbound deposit through a fee plan that is resolved and frozen at creation time. The two address types differ only in direction. Accumulation takes inbound stablecoin, ETH, SOL, BNB, or other supported assets and delivers BTC or USDB on Spark. Liquidation takes inbound BTC on Bitcoin L1 and delivers the output on any supported destination chain.

What are accumulation addresses?

An accumulation address is a persistent deposit address on Solana or any supported EVM chain (base, ethereum, arbitrum, optimism, polygon, bsc, tempo, tron, plasma). Each inbound deposit automatically creates an order that converts to USDC if needed, bridges to Spark, and then either delivers USDB to a fixed Spark address or swaps into BTC and delivers the BTC to a fixed Spark address. Hand out one address per user and skip calling /quote and /submit per deposit.
  • USDC (solana|base|ethereum|arbitrum|optimism|polygon|tempo) -> USDB (spark)
  • USDC (solana|base|ethereum|arbitrum|optimism|polygon|tempo) -> BTC (spark)
  • USDC.e (polygon) -> USDB (spark)
  • USDC.e (polygon) -> BTC (spark)
  • USDT (ethereum|arbitrum|optimism|tron|plasma|bsc) -> USDB (spark)
  • USDT (ethereum|arbitrum|optimism|tron|plasma|bsc) -> BTC (spark)
  • ETH (ethereum|base|arbitrum|optimism) -> USDB (spark)
  • ETH (ethereum|base|arbitrum|optimism) -> BTC (spark)
  • SOL (solana) -> USDB (spark)
  • SOL (solana) -> BTC (spark)
  • BNB (bsc) -> USDB (spark)
  • BNB (bsc) -> BTC (spark)
  • DAI (ethereum) -> USDB (spark)
  • DAI (ethereum) -> BTC (spark)
  • WBTC (ethereum) -> USDB (spark)
  • WBTC (ethereum) -> BTC (spark)
  • cbBTC (base|solana) -> USDB (spark)
  • cbBTC (base|solana) -> BTC (spark)
Tempo PathUSD is not supported as an accumulation source asset.Deposits on chains other than Solana are detected automatically. Solana-sourced addresses use direct webhook detection.

Create an accumulation address

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

const res = await fetch(`${BASE_URL}/v1/accumulation-addresses`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    'X-Idempotency-Key': `acu:create:${Date.now()}`,
  },
  body: JSON.stringify({
    label: 'User 123',
    sourceChain: 'solana',
    sourceAsset: 'USDC',
    destinationAsset: 'BTC',
    recipientSparkAddress: 'spark1UserRecipient...',
    feeBps: 5,
    slippageBps: 50,
    appFees: [
      { recipient: 'So1FeeRecipient...', fee: 50 },
    ],
  }),
}).then((r) => r.json());

console.log(res.depositAddress);
Response fields:
  • depositAddress: the address to receive deposits (a Solana address for Solana sources, an on-chain deposit address for other chain sources).
  • appFees: array of configured fee recipients, present when a fee plan was configured at creation.
  • subscriptions: status of server-side deposit detection subscriptions (Solana-sourced only).
Response example:
{
  "accumulationAddressId": "acu_...",
  "sourceChain": "solana",
  "sourceAsset": "USDC",
  "destinationAsset": "BTC",
  "recipientSparkAddress": "spark1...",
  "feeBps": 5,
  "slippageBps": 50,
  "enabled": true,
  "depositAddress": "So1...",
  "createdAt": "2026-02-04T01:00:00.000Z",
  "appFees": [
    { "recipient": "So1FeeRecipient...", "feeBps": 50 }
  ],
  "subscriptions": []
}

What are the deposit requirements?

On non-Solana chains, deposits must be plain token transfers to the deposit address. The deposit transaction should contain no calldata beyond a standard ERC-20 transfer(). Deposits sent through smart-contract interactions (multisig executions, payment routers, other contract-mediated transfers) are not detected automatically. The funds arrive on-chain, but order creation does not trigger.
If a contract-mediated deposit lands by mistake, a small direct transfer to the same deposit address will cause the system to sweep the full balance, including the stuck funds. This is a recovery workaround, not a supported flow.
Solana-sourced deposits do not have this limitation.

How does a deposit move through the system?

  1. Your user sends a supported deposit asset to the returned deposit address.
  2. Flashnet detects the deposit and creates an order.
  3. The engine processes the order asynchronously.
  4. You observe progress via partner webhooks.

Manage accumulation addresses

  • GET /v1/accumulation-addresses lists accumulation addresses.
  • GET /v1/accumulation-addresses/:id fetches a single address.
  • DELETE /v1/accumulation-addresses/:id disables an address (requires X-Idempotency-Key).
  • POST /v1/accumulation-addresses/sync re-registers all enabled Solana-sourced addresses with the configured deposit detection webhooks. Chain-sourced addresses do not use webhook subscriptions. Intended for operational recovery and self-hosted deployments.

What are liquidation addresses?

A liquidation address is a persistent Bitcoin L1 deposit address. Each inbound BTC deposit automatically creates an order that swaps BTC and delivers the output asset to a fixed destination on any supported chain, including BSC (USDT, BNB), Tempo (USDC, PathUSD), and all other supported chain/asset pairs. Hand out a static bc1... address and skip calling /quote and /submit per deposit. Orders created from liquidation deposits have:
  • sourceChain = bitcoin
  • sourceAsset = BTC
  • destinationChain and destinationAsset taken from the liquidation address configuration
  • quoteId = null

Create a liquidation address

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

const res = await fetch(`${BASE_URL}/v1/liquidation-addresses`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.FLASHNET_API_KEY}`,
    'X-Idempotency-Key': `liq:create:${Date.now()}`,
  },
  body: JSON.stringify({
    label: 'Merchant settlement',
    destinationChain: 'ethereum',
    destinationAsset: 'USDC',
    destinationAddress: '0xYourEthereumAddress',
    feeBps: 5,
    slippageBps: 50,
    appFees: [
      // Inline appFees recipients must live on the fee settlement chain,
      // not the destination chain. For bitcoin:BTC -> ethereum:USDC,
      // settlement is Solana USDC, so this must be a Solana address.
      { recipient: 'So1FeeRecipient...', fee: 50 },
    ],
  }),
}).then((r) => r.json());

// res.l1DepositAddress is the reusable Bitcoin address.
console.log(res.l1DepositAddress);
Response fields:
  • liquidationAddressId: stable id.
  • l1DepositAddress: Bitcoin deposit address (bc1...).
  • sparkAddress: Spark address used internally for deposit claiming and sweeps.
  • destination.chain, destination.asset, destination.address: fixed payout destination.
  • appFees: array of configured fee recipients, present when a fee plan was configured at creation.
Response example:
{
  "liquidationAddressId": "liq_...",
  "sparkAddress": "spark1...",
  "l1DepositAddress": "bc1...",
  "destination": { "chain": "ethereum", "asset": "USDC", "address": "0x..." },
  "feeBps": 5,
  "slippageBps": 50,
  "enabled": true,
  "createdAt": "2026-02-04T01:00:00.000Z",
  "appFees": [
    { "recipient": "So1FeeRecipient...", "feeBps": 50 }
  ]
}

How are deposits attributed?

Each Bitcoin deposit is identified by (txid, vout), where vout is the output index that paid the liquidation address. A single Bitcoin transaction can contain multiple outputs to different liquidation addresses, or multiple outputs to the same address. For reliable attribution, use webhooks and store order.id from the payload.

ZeroConf

All Bitcoin L1 deposits to liquidation addresses are ZeroConf-eligible. See ZeroConf for the full offer flow, confirmation behavior, and accept or decline endpoints.

Manage liquidation addresses

  • GET /v1/liquidation-addresses lists enabled liquidation addresses.
  • GET /v1/liquidation-addresses/:id fetches a single address, enabled or disabled.
  • DELETE /v1/liquidation-addresses/:id disables an address (requires X-Idempotency-Key).

How do app fees and affiliates work?

Both primitives accept appFees or affiliateIds at creation. Flashnet resolves and validates the plan at that moment: affiliate profiles are looked up, recipient addresses are validated against the fee settlement chain, and the settlement chain itself is locked in. The resolved plan is then frozen onto the address. Every subsequent deposit replays the frozen plan without re-evaluating profiles. Editing an affiliate profile later does not affect addresses that were created before the edit. The two models are mutually exclusive. Pick one at creation time.
Fee typeBehavior
Inline appFeesArray of { recipient, fee } objects. Fees pay out immediately to each recipient during order execution. Recipients must be valid addresses on the fee settlement chain, not the destination chain.
Registered affiliateIdsArray where each entry is a plain affiliate id string or a { affiliateId, feeBps } object that overrides the profile’s default fee bps for this address only, without mutating the stored profile. Fees are held back, accumulated for dashboard visibility, and delivered to the affiliate’s configured payout destination on claim.
body: JSON.stringify({
  // accumulation example; liquidation uses destinationChain/destinationAsset/destinationAddress
  sourceChain: 'solana',
  sourceAsset: 'USDC',
  destinationAsset: 'BTC',
  recipientSparkAddress: 'spark1UserRecipient...',
  feeBps: 5,
  slippageBps: 50,
  affiliateIds: [
    'referrer-alice',
    { affiliateId: 'platform-fee', feeBps: 25 },
  ],
}),
Fee math follows the same rules as orchestration quotes. The platform fee is deducted first, then each app or affiliate fee is applied to the remainder. A 20% platform cut is retained on every app fee. The fee settlement chain depends on the route:
  • Destinations on a supported chain (any EVM chain, Solana, Tempo, and similar) settle fees on Solana USDC.
  • spark:USDB destinations settle fees on Spark USDB.
  • spark:BTC, bitcoin:BTC, and lightning:BTC destinations do not support fee plans, because the route has no stablecoin leg to settle against.
For a bitcoin:BTC -> ethereum:USDC liquidation address, the settlement chain is Solana USDC, so inline appFees recipients must be Solana addresses. The appFees field in create, list, and get responses reflects the frozen plan. Each entry always has recipient and feeBps. Entries backed by a registered affiliate also carry affiliateId, and their recipient is the profile’s configured payoutAddress. Inline entries omit affiliateId. The array is omitted entirely from responses for addresses created without a fee plan.
You cannot combine appFees and affiliateIds on the same address. Pick one model at creation time.
For fee math, affiliate registration, and claim payouts, see Quotes and Orders and Resource Management.

Webhooks

Deposits on both primitives produce normal order.* webhook events. Register an endpoint with POST /v1/webhooks, verify X-Flashnet-Signature on inbound requests, and treat webhook delivery as at-least-once. See Webhooks.

Next steps