Fee Structure
The deBridge Protocol implements a comprehensive fee structure to sustain the network, incentivize validators, and ensure long-term protocol health. This page explains all fee types, how they’re calculated, and strategies for optimization.
Fee Types
deBridge uses two primary fee types:
Fixed Native Fee A flat fee paid in the native token (ETH, BNB, etc.) per transaction
Transfer Fee A percentage-based fee on the transferred asset amount (measured in basis points)
Fixed Native Fee
A flat fee paid in native currency for each transaction.
How It Works
contracts/transfers/DeBridgeGate.sol:736-750
// Collect fixed native fee for non-native token transfers
uint256 nativeFee = chainFees.fixedNativeFee == 0
? globalFixedNativeFee
: chainFees.fixedNativeFee;
// Apply discount
nativeFee = _applyDiscount (nativeFee, discountInfo.discountFixBps);
if ( msg .value < nativeFee) revert TransferAmountNotCoverFees ();
else if ( msg .value > nativeFee) {
// Refund extra ETH
_safeTransferETH ( msg.sender , msg .value - nativeFee);
}
bytes32 nativeDebridgeId = getDebridgeId ( getChainId (), address ( 0 ));
getDebridgeFeeInfo[nativeDebridgeId].collectedFees += nativeFee;
Fee Levels
Global Default
Chain-Specific
Asset-Specific
Fallback fee when chain-specific fee is not set: contracts/transfers/DeBridgeGate.sol:94
uint256 public override globalFixedNativeFee;
Query current global fee: uint256 globalFee = deBridgeGate. globalFixedNativeFee ();
Per-chain fees can be set for different destination chains: struct ChainSupportInfo {
uint256 fixedNativeFee; // Chain-specific fee
bool isSupported;
uint16 transferFeeBps;
}
If set to 0, falls back to global fee. When using useAssetFee = true, fee is paid in the transferred asset: contracts/transfers/DeBridgeGate.sol:722-733
if (_useAssetFee) {
if (_tokenAddress == address ( 0 )) {
// For native token: use global/chain native fee
assetsFixedFee = chainFees.fixedNativeFee == 0
? globalFixedNativeFee
: chainFees.fixedNativeFee;
} else {
// For ERC20: use asset-specific fee
assetsFixedFee = debridgeFee.getChainFee[_chainIdTo];
if (assetsFixedFee == 0 ) revert NotSupportedFixedFee ();
}
}
Auto-Update Mechanism
Fees can be automatically adjusted by authorized updater:
contracts/transfers/DeBridgeGate.sol:366-373
function autoUpdateFixedNativeFee (
uint256 _globalFixedNativeFee
) external onlyFeeContractUpdater {
globalFixedNativeFee = _globalFixedNativeFee;
emit FixedNativeFeeAutoUpdated (_globalFixedNativeFee);
}
This allows dynamic fee adjustment based on network conditions.
Transfer Fee
A percentage-based fee on the transferred amount.
Calculation
contracts/transfers/DeBridgeGate.sol:754-759
// Calculate transfer fee in basis points (BPS)
uint256 transferFee = (chainFees.transferFeeBps == 0
? globalTransferFeeBps
: chainFees.transferFeeBps)
* (_amount - assetsFixedFee) / BPS_DENOMINATOR;
transferFee = _applyDiscount (transferFee, discountInfo.discountTransferBps);
BPS Denominator : 10,000 (meaning 1 BPS = 0.01%)
Example :
Transfer amount: 1,000 tokens
Transfer fee: 10 BPS (0.1%)
Fee = 1,000 * 10 / 10,000 = 1 token
Fee Levels
Global Default
Chain-Specific
contracts/transfers/DeBridgeGate.sol:96
uint16 public override globalTransferFeeBps;
Query: uint16 globalFeeBps = deBridgeGate. globalTransferFeeBps ();
// Example: 10 = 0.1%
struct ChainSupportInfo {
uint256 fixedNativeFee;
bool isSupported;
uint16 transferFeeBps; // Chain-specific transfer fee
}
Falls back to global if set to 0.
Total Fee Calculation
The complete fee calculation process:
Example: Calculate Total Fees
function calculateTotalFees (
address tokenAddress ,
uint256 amount ,
uint256 chainIdTo ,
bool useAssetFee ,
address sender
) public view returns (
uint256 fixedFee ,
uint256 transferFee ,
uint256 totalFee
) {
ChainSupportInfo memory chainFees = deBridgeGate. getChainToConfig (chainIdTo);
DiscountInfo memory discount = deBridgeGate. feeDiscount (sender);
// 1. Calculate fixed fee
if (useAssetFee && tokenAddress != address ( 0 )) {
// Asset-specific fixed fee
bytes32 debridgeId = deBridgeGate. getDebridgeId (
getNativeChainId (tokenAddress),
tokenAddress
);
fixedFee = deBridgeGate. getDebridgeChainAssetFixedFee (debridgeId, chainIdTo);
} else {
// Native fixed fee
fixedFee = chainFees.fixedNativeFee == 0
? deBridgeGate. globalFixedNativeFee ()
: chainFees.fixedNativeFee;
}
// Apply fixed fee discount
fixedFee = fixedFee * ( 10000 - discount.discountFixBps) / 10000 ;
// 2. Calculate transfer fee
uint16 transferFeeBps = chainFees.transferFeeBps == 0
? deBridgeGate. globalTransferFeeBps ()
: chainFees.transferFeeBps;
transferFee = (amount - fixedFee) * transferFeeBps / 10000 ;
// Apply transfer fee discount
transferFee = transferFee * ( 10000 - discount.discountTransferBps) / 10000 ;
// 3. Total
totalFee = fixedFee + transferFee;
}
Fee Discounts
The protocol supports per-address fee discounts:
contracts/transfers/DeBridgeGate.sol:34-37
struct DiscountInfo {
uint16 discountFixBps; // Fixed fee discount in BPS (0-10000)
uint16 discountTransferBps; // Transfer fee discount in BPS (0-10000)
}
mapping ( address => DiscountInfo) public feeDiscount;
Setting Discounts
Only admin can set discounts:
contracts/transfers/DeBridgeGate.sol:552-564
function updateFeeDiscount (
address _address ,
uint16 _discountFixBps ,
uint16 _discountTransferBps
) external onlyAdmin {
if (_address == address ( 0 ) ||
_discountFixBps > BPS_DENOMINATOR ||
_discountTransferBps > BPS_DENOMINATOR
) revert WrongArgument ();
DiscountInfo storage discountInfo = feeDiscount[_address];
discountInfo.discountFixBps = _discountFixBps;
discountInfo.discountTransferBps = _discountTransferBps;
}
Applying Discounts
contracts/transfers/DeBridgeGate.sol:847-852
function _applyDiscount (
uint256 amount ,
uint16 discountBps
) internal pure returns ( uint256 ) {
return amount - amount * discountBps / BPS_DENOMINATOR;
}
Example :
Original fee: 100 tokens
Discount: 2000 BPS (20%)
Discounted fee = 100 - (100 * 2000 / 10000) = 80 tokens
Discount Use Cases
Protocols or users doing frequent transfers can receive volume discounts: // 10% discount on both fees
updateFeeDiscount (highVolumeAddress, 1000 , 1000 );
Strategic partners can get preferential rates: // 20% on fixed, 15% on transfer
updateFeeDiscount (partnerAddress, 2000 , 1500 );
Token holders could receive discounts through governance: // 5% discount for token holders
updateFeeDiscount (tokenHolderAddress, 500 , 500 );
useAssetFee Parameter
The useAssetFee parameter controls whether fees are paid in the transferred asset or native token:
useAssetFee = false
useAssetFee = true
Pay fixed fee in native token (ETH, BNB, etc.): IERC20 (token). approve ( address (deBridgeGate), amount);
// Send native token for fees
deBridgeGate.send{value : fixedFee}(
tokenAddress,
amount,
chainIdTo,
receiver,
"" ,
false , // Pay fee in native token
0 ,
""
);
Pros :
Simpler for users (just send ETH/BNB)
Works for any token
Cons :
Requires native token balance
Two separate tokens needed
Deduct fee from transferred asset: // Fee is deducted from the transferred amount
deBridgeGate. send (
tokenAddress,
amount, // Includes fee
chainIdTo,
receiver,
"" ,
true , // Pay fee in asset
0 ,
""
);
Pros :
Only need one token
Better UX for some scenarios
Cons :
Must be supported for the asset
Receiver gets less than sent amount
Note : For native tokens (address(0)), useAssetFee is automatically set to true.
Fee Collection and Withdrawal
Fees are tracked per asset:
contracts/transfers/DeBridgeGate.sol:22-26
struct DebridgeFeeInfo {
uint256 collectedFees; // Total fees collected
uint256 withdrawnFees; // Total fees withdrawn
mapping ( uint256 => uint256 ) getChainFee; // Asset-specific fees per chain
}
Withdrawing Fees
Fees can be withdrawn by the fee proxy:
contracts/transfers/DeBridgeGate.sol:509-526
function withdrawFee ( bytes32 _debridgeId ) external nonReentrant onlyFeeProxy {
DebridgeFeeInfo storage debridgeFee = getDebridgeFeeInfo[_debridgeId];
// Calculate withdrawable amount
uint256 amount = debridgeFee.collectedFees - debridgeFee.withdrawnFees;
if (amount == 0 ) revert NotEnoughReserves ();
debridgeFee.withdrawnFees += amount;
// Transfer to fee proxy
if (_debridgeId == getDebridgeId ( getChainId (), address ( 0 ))) {
_safeTransferETH (feeProxy, amount);
} else {
IERC20Upgradeable (getDebridge[_debridgeId].tokenAddress)
. safeTransfer (feeProxy, amount);
}
emit WithdrawnFee (_debridgeId, amount);
}
Execution Fees
For cross-chain calls, additional execution fee is required:
SubmissionAutoParamsTo memory autoParams = SubmissionAutoParamsTo ({
executionFee : 0.01 ether , // Fee for executor/keeper
flags : flags,
fallbackAddress : fallbackAddress,
data : calldata
});
The execution fee:
Goes to the keeper who executes the claim
Must cover gas costs on destination chain
Should include keeper incentive
Calculation :
// Estimate execution fee
uint256 destinationGasPrice = getGasPrice (destinationChain);
uint256 estimatedGas = 300000 ; // Estimate for your function
uint256 buffer = 20 ; // 20% buffer
executionFee = estimatedGas * destinationGasPrice * ( 100 + buffer) / 100 ;
Fee Optimization Strategies
Batch Transfers Combine multiple small transfers into fewer large ones to save on fixed fees
Apply for Discounts High-volume users should contact deBridge team for discount arrangements
Use Asset Fees When possible, use useAssetFee=true to avoid holding multiple tokens
Monitor Fee Updates Track FixedNativeFeeAutoUpdated events for automatic fee adjustments
Example: Query All Fees
Example: Fee Query Helper
contract FeeHelper {
IDeBridgeGate public deBridgeGate;
struct FeeQuote {
uint256 fixedNativeFee;
uint256 transferFeeBps;
uint256 assetFixedFee;
uint256 estimatedTotalFee;
}
function quoteFees (
address tokenAddress ,
uint256 amount ,
uint256 chainIdTo ,
bool useAssetFee
) external view returns ( FeeQuote memory quote ) {
ChainSupportInfo memory chainConfig =
deBridgeGate. getChainToConfig (chainIdTo);
// Get fixed native fee
quote.fixedNativeFee = chainConfig.fixedNativeFee == 0
? deBridgeGate. globalFixedNativeFee ()
: chainConfig.fixedNativeFee;
// Get transfer fee BPS
quote.transferFeeBps = chainConfig.transferFeeBps == 0
? deBridgeGate. globalTransferFeeBps ()
: chainConfig.transferFeeBps;
// Calculate total
if (useAssetFee && tokenAddress != address ( 0 )) {
bytes32 debridgeId = deBridgeGate. getDebridgeId (
getChainId (),
tokenAddress
);
quote.assetFixedFee = deBridgeGate. getDebridgeChainAssetFixedFee (
debridgeId,
chainIdTo
);
uint256 transferFee = (amount - quote.assetFixedFee)
* quote.transferFeeBps / 10000 ;
quote.estimatedTotalFee = quote.assetFixedFee + transferFee;
} else {
uint256 transferFee = amount * quote.transferFeeBps / 10000 ;
quote.estimatedTotalFee = quote.fixedNativeFee + transferFee;
}
}
}
Fee Distribution
Collected fees are distributed to:
Validators
Validators earn majority of fees as rewards for securing the network
Treasury
Protocol treasury receives portion for development and operations
Liquidity Providers
LPs may earn fees in certain configurations
Governance
Future fee distribution controlled by DAO governance
Admin Fee Management
Update Global Fees
contracts/transfers/DeBridgeGate.sol:401-408
function updateGlobalFee (
uint256 _globalFixedNativeFee ,
uint16 _globalTransferFeeBps
) external onlyAdmin {
globalFixedNativeFee = _globalFixedNativeFee;
globalTransferFeeBps = _globalTransferFeeBps;
emit FixedNativeFeeUpdated (_globalFixedNativeFee, _globalTransferFeeBps);
}
Update Chain-Specific Fees
contracts/transfers/DeBridgeGate.sol:381-396
function updateChainSupport (
uint256 [] memory _chainIds ,
ChainSupportInfo [] memory _chainSupportInfo ,
bool _isChainFrom
) external onlyAdmin {
if (_chainIds.length != _chainSupportInfo.length) revert WrongArgument ();
for ( uint256 i = 0 ; i < _chainIds.length; i ++ ) {
if (_isChainFrom) {
getChainFromConfig[_chainIds[i]] = _chainSupportInfo[i];
} else {
getChainToConfig[_chainIds[i]] = _chainSupportInfo[i];
}
emit ChainsSupportUpdated (_chainIds[i], _chainSupportInfo[i], _isChainFrom);
}
}
Update Asset-Specific Fees
contracts/transfers/DeBridgeGate.sol:414-424
function updateAssetFixedFees (
bytes32 _debridgeId ,
uint256 [] memory _supportedChainIds ,
uint256 [] memory _assetFeesInfo
) external onlyAdmin {
if (_supportedChainIds.length != _assetFeesInfo.length) revert WrongArgument ();
DebridgeFeeInfo storage debridgeFee = getDebridgeFeeInfo[_debridgeId];
for ( uint256 i = 0 ; i < _supportedChainIds.length; i ++ ) {
debridgeFee.getChainFee[_supportedChainIds[i]] = _assetFeesInfo[i];
}
}
Events
// Fee update events
event FixedNativeFeeUpdated (
uint256 globalFixedNativeFee ,
uint256 globalTransferFeeBps
);
event FixedNativeFeeAutoUpdated ( uint256 globalFixedNativeFee );
event WithdrawnFee ( bytes32 debridgeId , uint256 fee );
Best Practices
Always Check Fees Before Transfer
Query current fees to ensure user has sufficient balance: uint256 fixedFee = deBridgeGate. globalFixedNativeFee ();
uint256 transferFeeBps = deBridgeGate. globalTransferFeeBps ();
uint256 totalFee = fixedFee + (amount * transferFeeBps / 10000 );
require (userBalance >= amount + totalFee, "Insufficient balance" );
If user sends excess native token, it’s automatically refunded. Inform users about this.
Show users:
Fixed fee amount
Transfer fee amount
Total fee
Amount they’ll receive (after fees)
Listen for fee update events to keep UI updated: deBridgeGate . on ( 'FixedNativeFeeAutoUpdated' , ( newFee ) => {
updateFeeDisplay ( newFee );
});
Never hardcode fee values in your application. Always query current fees dynamically as they can change through governance or auto-updates.
Next Steps
Integration: Sending Assets Learn how to implement transfers with proper fee handling
Asset Transfers Understand how fees are collected during transfers
Cross-Chain Messaging Learn about execution fees for cross-chain calls
Integration Overview Start building with deBridge Protocol