02 · DepositDemo

Fund a platform balance with any token. Settle USDC on Base.

Users arrive holding whatever they hold — an external wallet, a Coinbase balance, or a token bridged from another chain. Fireblocks Flow provides the swap and settlement infrastructure[†] to credit an embedded wallet, Fireblocks vault, or any provided deposit wallet.

External walletAny wallet, chain
Embedded or vaultUSDC on Base
Platform balance

Top up your account

Fund your balance from any wallet on any chain. Flow swaps it to USDC and credits your platform wallet.

Your application should make clear to end-users that asset conversion and cross-chain routing are executed by independent third-party providers. Users keep full control of their assets and must explicitly sign each transfer. On-chain transactions are final and cannot be reversed.

By continuing, you agree to our Terms of Service and Privacy Policy.

Mainnet only· no testnets

Flow runs on mainnet networks only. Use a sandbox environment id with real mainnet addresses for development.

  1. Create the deposit Flow

    Read the docs

    Server-side. Defines the platform-facing settlement asset (where deposits land) and the user's destination wallet. Reuse a single Flow across many user deposits — the destinationConfig.destinations entry identifies which user's wallet to credit on each transaction.

    flow.server.ts
    // Runs on your server — one-time per Flow config.
    const res = await fetch(
      `https://api.dynamic.xyz/v0/environments/${process.env.ENV_ID}/checkouts`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.DYNAMIC_API_TOKEN}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          mode: "deposit",
          settlementConfig: {
            strategy: "preferred_order",
            settlements: [
              {
                chainName: "EVM",
                chainId: "8453",
                symbol: "USDC",
                tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                tokenDecimals: 6,
              },
            ],
          },
          destinationConfig: {
            destinations: [
              {
                chainName: "EVM",
                type: "address",
                identifier: "<destination_address>",
              },
            ],
          },
        }),
      },
    );
    const { id: flowId } = await res.json();
  2. Create a deposit transaction

    Read the docs

    Per user. Opens a transaction against the Flow id and returns a session token that authenticates the rest of this user's deposit lifecycle.

    transaction.ts
    import { addEvmExtension } from "@dynamic-labs-sdk/evm";
    import { createCheckoutTransaction } from "@dynamic-labs-sdk/client";
    
    addEvmExtension();
    
    // Open a transaction against the Flow id returned in Step 01. Returns
    // both the transaction and a one-time sessionToken — store the latter.
    const { transaction, sessionToken } = await createCheckoutTransaction({
      checkoutId: flowId,
      amount: "5.00",
      currency: "USD",
    });
  3. Attach the user's source wallet

    Read the docs

    Declare the wallet the user is depositing from. Dynamic runs risk + sanctions screening here; a 403 means the source is blocked.

    attach.ts
    import {
      attachCheckoutTransactionSource,
      getActiveNetworkData,
      getPrimaryWalletAccount,
    } from "@dynamic-labs-sdk/client";
    
    const wallet = getPrimaryWalletAccount()!;
    const { networkData } = await getActiveNetworkData({ walletAccount: wallet });
    
    await attachCheckoutTransactionSource({
      transactionId: transaction.id,
      fromAddress: wallet.address,
      fromChainId: String(networkData.networkId),
      fromChainName: wallet.chain,
    });
    // 403 → source blocked by risk/sanctions screening.
  4. Get a quote

    Read the docs

    Quote the cross-chain swap from the user's chosen token into the platform's settlement asset. Quotes expire in 60 seconds — re-quote if the user takes longer to confirm.

    quote.ts
    import {
      getBalances,
      getCheckoutTransactionQuote,
    } from "@dynamic-labs-sdk/client";
    
    // First non-zero balance — production would let the user pick which
    // token to pay with.
    const [fromToken] = await getBalances({ walletAccount: wallet });
    
    const quoted = await getCheckoutTransactionQuote({
      transactionId: transaction.id,
      fromTokenAddress: fromToken.address,
      slippage: 0.005, // 0.5%
    });
    // quoted.quote.expiresAt — re-quote if the user takes longer than 60s.
  5. Prepare, sign, and broadcast

    Read the docs

    Three things happen here: Dynamic prepares the signing payload, the user signs in their external wallet, and your client notifies Dynamic that the chain tx is broadcast. The SDK collapses all three into one helper; the REST path keeps them separate so you can plug in your own wallet client.

    submit.ts
    import { submitCheckoutTransaction } from "@dynamic-labs-sdk/client";
    
    // One helper handles all three sub-stages:
    //   1. prepare  → fetch the signing payload
    //   2. sign     → wallet popup appears here
    //   3. broadcast → notify Dynamic of the on-chain txHash
    await submitCheckoutTransaction({
      transactionId: transaction.id,
      walletAccount: wallet,
    });
  6. Wait for settlement

    Read the docs

    We poll the transaction endpoint every ~3 seconds so the settlement lifecycle stays visible in the demo. Production should subscribe to the checkout.transaction.settlement.updated webhook instead — push-driven, no idle polling. On completed, credit the user's platform balance; on failed, surface a retry path.

    settle.ts
    import {
      getCheckoutTransaction,
      isTerminalState,
    } from "@dynamic-labs-sdk/client";
    
    // Poll every 3s — or subscribe via the lifecycle webhook.
    let tx = await getCheckoutTransaction({ transactionId: transaction.id });
    while (!isTerminalState(tx)) {
      await new Promise((r) => setTimeout(r, 3000));
      tx = await getCheckoutTransaction({ transactionId: transaction.id });
    }
    // tx.settlementState === "completed" | "failed"
What's next
Disclaimer

Dynamic does not control the swap, bridge, or routing protocols used to convert and deliver assets. Rates and fees are sourced from third-party providers and may change between quote and execution.

Cross-chain transfers carry risk — including slippage, partial fills, and failed conversions. On-chain transactions are final and cannot be reversed.

These materials are not investment, financial, legal, or tax advice. You are responsible for evaluation at your own discretion. Please review Dynamic's terms and conditions for full details on acceptable use.