Skip to main content
This guide explains which fees apply when bridging, how funds move through a transaction, and the best practices to follow when implementing custom fees.

Fees breakdown

Every bridge transfer can incur the following fees:
  • Your custom fee: The extra amount you charge on top of the bridge transfer. Arc keeps 10% of this amount. The remaining 90% goes to the fee recipient you configure on the source blockchain.
  • CCTP protocol fee: For Fast Transfers only, CCTP collects a fee that varies by source chain. Standard Transfers (SLOW speed) do not incur this fee. For current fee rates, see CCTP Fees. For how to configure the transfer speed, see Configure Transfer Speed and Maximum Cost.
  • CCTP Forwarding Service fee: When you use the Forwarding Service to submit the mint transaction on the destination chain, it collects a service fee.

Funds flow

The following example shows what happens when a user initiates a 1,000 USDC Fast Transfer with a 1% custom fee and the Forwarding Service enabled:
  1. The user initiates a 1000 USDC bridge transfer on the source blockchain.
  2. You add a 10 USDC (1%) custom fee.
  3. The source wallet signs a transaction for 1,010 USDC (bridge amount + custom fee).
  4. The 10 USDC custom fee is split on the source blockchain:
    • Arc receives 1 USDC (10%).
    • Your fee recipient receives 9 USDC (remaining 90%).
  5. The 1000 USDC bridge amount is forwarded to CCTP.
  6. CCTP takes a protocol fee (0.10 USDC in this example) for a Fast Transfer.
  7. The Forwarding Service deducts its fee (0.20 USDC in this example) from the amount to be minted on the destination chain.
  8. The destination wallet receives 999.70 USDC on the destination chain.
This flow is illustrated in the following diagram:

Best practices for custom fees

Follow these best practices when implementing custom fees:
  • Treat the custom fee as an amount added on top of the bridge transfer. Do not subtract it from the bridge amount.
  • Validate that the user’s wallet balance covers both the bridge amount and the custom fee. The following code shows an example balance check:
TypeScript
const requiredBalance = parseFloat(amount) + parseFloat(customFee);
if (userBalance < requiredBalance) {
  throw new Error(`Insufficient balance. Need ${requiredBalance} USDC`);
}
  • Use a fee recipient address on the source blockchain. Do not use an address on the destination.
  • In your UI, display the following to the user before they confirm the transaction:
    • The total source wallet debit: bridge amount + custom fee
    • The full fee breakdown: bridge amount, custom fee, CCTP Fast Transfer fee (if applicable), and Forwarding Service fee (if using the Forwarding Service)
  • Return human-readable decimal strings. For example, 10 rather than 10000000 for 10 USDC. App Kit handles base-unit conversion internally.