Fees (Fee)
What is Fee
Fee (commission) — is a percentage of the transfer amount that is retained by the bridge when transferring tokens between networks.
Why is fee needed?
Commission (fee) is charged by the bridge during cross-chain token transfers to cover operational costs and ensure economic sustainability of the protocol.
Important
Fee is charged only on the EVM network side. On the TVM side, no fee is applied.
Who receives the fee?
- Fee accumulates on the MultiVault contract
- Recipient:
governanceaddress (contract administrator) - Fee withdrawal:
skim()function transfers accumulated fees to the governance address
Types of Fees
Deposit Fee
Fee is charged when depositing tokens from EVM to TVM network.
User sends 1000 USDT → Fee 10 USDT (1%) → 990 USDT goes to TVMWhen applied:
- When calling
deposit()on the MultiVault contract - When calling
depositByNativeToken()for native tokens (ETH, BNB, etc.)
Withdraw Fee
Fee is charged when withdrawing tokens from TVM to EVM network.
User withdraws 1000 USDT from TVM → Fee 10 USDT (1%) → Receives 990 USDT in EVMWhen applied:
- When calling
saveWithdrawNative()/saveWithdrawAlien()on the MultiVault contract - After event confirmation by relays
Fee Collection Scheme
EVM → TVM (Deposit):
- User sends 1000 tokens
MultiVault.deposit()- Calculate fee:
1000 × depositFee / 10000 - Fee (10) stays on MultiVault
- 990 tokens transferred to TVM
TVM → EVM (Withdraw):
- User initiates withdraw of 1000 tokens in TVM
- Event created, relay nodes confirm
- User calls
saveWithdraw*()on MultiVault - Calculate fee:
1000 × withdrawFee / 10000 - Fee (10) stays on MultiVault
- User receives 990 tokens
Default and Token-specific Fee
Two Configuration Levels
Fees are configured at two levels:
| Level | Description | When Used |
|---|---|---|
| Default Fee | Global default values | When activating a new token |
| Token-specific Fee | Individual token settings | After token activation |
Default Fee
Global fee settings, separated by token type:
| Parameter | Description |
|---|---|
defaultNativeDepositFee | Default deposit fee for Native tokens (originally issued in TVM) |
defaultNativeWithdrawFee | Default withdraw fee for Native tokens |
defaultAlienDepositFee | Default deposit fee for Alien tokens (originally issued in EVM) |
defaultAlienWithdrawFee | Default withdraw fee for Alien tokens |
Token-specific Fee
Individual settings for each token:
| Parameter | Description |
|---|---|
tokens_[token].depositFee | Deposit fee for specific token |
tokens_[token].withdrawFee | Withdraw fee for specific token |
How Fee is Applied
- First use of token →
_activateToken()is called - Token type is determined (Native or Alien)
- Default fees are copied based on type:
- Native:
defaultNativeDepositFee/defaultNativeWithdrawFee - Alien:
defaultAlienDepositFee/defaultAlienWithdrawFee
- Native:
- Values are written to
tokens_[token].depositFee/.withdrawFee - All subsequent operations use token-specific fee
Important
After token activation, changing default fee DOES NOT affect this token. To change the fee for an already activated token, you must explicitly call setTokenDepositFee() / setTokenWithdrawFee().
Fee Configuration
Setting default fee (for new tokens):
setDefaultNativeDepositFee(100); // 1% for Native tokens (from TVM) deposit
setDefaultNativeWithdrawFee(50); // 0.5% for Native tokens withdraw
setDefaultAlienDepositFee(100); // 1% for Alien tokens (from EVM) deposit
setDefaultAlienWithdrawFee(50); // 0.5% for Alien tokens withdrawSetting token-specific fee (for existing tokens):
setTokenDepositFee(tokenAddress, 200); // 2% deposit for specific token
setTokenWithdrawFee(tokenAddress, 100); // 1% withdraw for specific tokenData Structure
Constants
| Constant | Value | Description |
|---|---|---|
MAX_BPS | 10,000 | 100% in basis points (1 BPS = 0.01%) |
FEE_LIMIT | 5,000 | Maximum allowed fee = 50% |
Token Structure
Stores token information, including fees. Each token has individual fee settings.
| Attribute | Type | Description |
|---|---|---|
activation | uint | Block number of token activation |
blacklisted | bool | Token blacklist flag |
depositFee | uint | Deposit fee (in BPS) |
withdrawFee | uint | Withdrawal fee (in BPS) |
isNative | bool | true = Native token (from TVM), false = Alien token (from EVM) |
custom | address | Custom token address (deprecated) |
depositLimit | uint256 | Deposit limit (see Limits) |
Storage Variables
| Variable | Type | Description |
|---|---|---|
tokens_ | mapping(address => Token) | Mapping token address → configuration |
defaultNativeDepositFee | uint | Default fee for Native token (from TVM) deposit |
defaultNativeWithdrawFee | uint | Default fee for Native token withdrawal |
defaultAlienDepositFee | uint | Default fee for Alien token (from EVM) deposit |
defaultAlienWithdrawFee | uint | Default fee for Alien token withdrawal |
fees | mapping(address => uint) | Accumulated fees per token (used in skim()) |
Fee Enum
| Value | Description |
|---|---|
Deposit | Deposit fee |
Withdraw | Withdrawal fee |
Fee Calculation
Formula
fee = amount × feeRate / MAX_BPSWhere:
amount— transfer amountfeeRate— fee rate in BPS (basis points)MAX_BPS= 10,000 (100%)
Calculation Function
| Function | Description |
|---|---|
_calculateMovementFee() | Calculates fee based on operation type (Deposit/Withdraw) and token rate |
Calculation Examples
| Amount | Fee Rate (BPS) | Fee Rate (%) | Fee |
|---|---|---|---|
| 1,000,000 | 10 | 0.1% | 1,000 |
| 1,000,000 | 100 | 1% | 10,000 |
| 1,000,000 | 500 | 5% | 50,000 |
| 1,000,000 | 5,000 | 50% | 500,000 (maximum) |
Fee Management
Default Fee Setting Functions
| Function | Parameters | Description |
|---|---|---|
setDefaultNativeDepositFee() | fee: uint | Sets default deposit fee for Native tokens (from TVM) |
setDefaultNativeWithdrawFee() | fee: uint | Sets default withdraw fee for Native tokens |
setDefaultAlienDepositFee() | fee: uint | Sets default deposit fee for Alien tokens (from EVM) |
setDefaultAlienWithdrawFee() | fee: uint | Sets default withdraw fee for Alien tokens |
Token Fee Setting Functions
| Function | Parameters | Description |
|---|---|---|
setTokenDepositFee() | token: address, fee: uint | Sets deposit fee for specific token |
setTokenWithdrawFee() | token: address, fee: uint | Sets withdraw fee for specific token |
Fee Withdrawal
| Function | Parameters | Description |
|---|---|---|
skim() | token: address | Transfers accumulated token fees to governance address |
The function transfers accumulated fee from the internal fees[token] mapping to the governance address. For Native tokens it calls mint(), for Alien — safeTransfer().
Access Rights
| Action | Required Role | Contract |
|---|---|---|
| Set default fee | governance | MultiVaultFacetFees |
| Set token fee | governance | MultiVaultFacetFees |
| Withdraw fees (skim) | governance or management | MultiVaultFacetFees |
Events
| Event | Parameters | Description |
|---|---|---|
UpdateDefaultNativeDepositFee | fee | Change in default deposit fee for Native tokens |
UpdateDefaultNativeWithdrawFee | fee | Change in default withdraw fee for Native tokens |
UpdateDefaultAlienDepositFee | fee | Change in default deposit fee for Alien tokens |
UpdateDefaultAlienWithdrawFee | fee | Change in default withdraw fee for Alien tokens |
UpdateTokenDepositFee | token, fee | Change in deposit fee for token |
UpdateTokenWithdrawFee | token, fee | Change in withdraw fee for token |
EarnTokenFee | token, amount | Fee accrual on deposit/withdraw |
SkimFee | token, amount | Withdrawal of accumulated fees |
API Reference
POST /payload/build
Returns fee information in response:
| Field | Type | Description |
|---|---|---|
feeAmount | string | Calculated fee in token's smallest units |
tokenAmount | string | Token amount after fee deduction |
Risks and Edge Cases
| Risk/Error | Cause | Protection/Solution |
|---|---|---|
| Fee > Amount | Fee exceeds transfer amount | Transaction reverts (underflow) |
| Fee = 50%+ | Attempt to set fee above limit | Check fee <= FEE_LIMIT |
| Zero governance | Fee recipient not set | governance set during initialization |
| Token not activated | Fee not set for token | Token activates automatically on first use |
| Default fee change | Doesn't affect existing tokens | Must explicitly call setTokenFee for each token |
Error Codes
| Error Code | Description |
|---|---|
"Fee: limit exceeded" | Attempt to set fee > FEE_LIMIT (5000 BPS = 50%) |
"Fees: no fees to skim" | Calling skim() when accumulated fee = 0 |
"Actors: only governance" | Calling setDefaultFee not from governance address |
"Actors: only governance or management" | Calling skim() not from governance/management |