@exodus/ethereum-api
Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Ethereum and EVM-based blockchains. See Asset Packages for more detail on this package`s role.
Fees
This section explains how fees currently work in Exodus and provides guidance on troubleshooting and tuning them via remote configuration. It reverse-engineers the existing unified implementation. The document aims to help readers understand the fee mechanism and suggest ways to improve it further.
Gas Price
Excluding custom fees, feeData.gasPrice
is used to resolve the transaction's gas price (legacy) and max gas price (EIP-1559).
The gasPrice
value is, in preference order, loaded from:
- Remote config
- The node via the fee data monitor and web socket.
gasPrice = baseFeePerGas * gasPriceMultiplier + tipGasPrice
- The default configuration.
Using remote config is never recommended, as manually updating this value won't keep up with network activity.
Fee and remote monitors are executed as soon as the wallet starts. The default fee data fields defined in code would be overridden (if they are set) before the user can interact with the wallet.
To tune the fee resolution, we can:
- Increase or decrease
gasPriceMultiplier
via remote config. For example, 1.2
would bump the current network gas price by 20%.
- Set a
min
value via remote config. Gas price will never be lower than this value.
- Set a
max
value via remote config. Gas price will never be higher than this value.
If the resolved gasPrice
is not accurate or lower than the network's, the transaction may get stuck in pending status or may not be broadcast to the blockchain.
This is an undesirable situation:
- We could increase the nonce for the second transaction, but if the first one drops, the second one will also fail, even if the fees of the second one are high enough.
- If the wallet assumes the transaction was dropped and the nonce is reused, the network will interpret this unrelated second transaction as attempting to replace the first one (transaction replacement underprice error).
- The user submitting a second transaction to "retry" could be dangerous. Both transactions might get mined, causing the user to execute the swap twice instead of once.
One escape valve could be to update the min
, max
, or gasPriceMultiplier
via remote config. This should be a temporary patch that needs to be rolled back once the node's gas price stabilizes.
Base Gas Price
The base gas price (or base fee) is the minimum amount of gas required to be paid per unit of gas to include a transaction in a block.
It can be found in feeData.baseFeePerGas
. The fee monitor keeps it up to date by monitoring the latest block's information. It could be tuned by remote config, but this is not recommended.
The feeData.baseFeePerGas
is used when showing an "estimated fee" in the UI and to calculate the max gas price. gasPrice = baseFeePerGas * gasPriceMultiplier + tipGasPrice
Gas Limit
The gasLimit
refers to the maximum amount of gas a transaction or block is allowed to consume during its execution.
Most EVMs assume 21,000 when transferring the main asset. Some EVMs, like Mantle, Arbitrum One, and Arbitrum Nova, use a different gas limit. These gas limits need to be estimated, similar to when performing smart contract calls.
For smart contract calls, like sending or swapping, the wallet estimates a gas limit based on the contract, the sender, the receiver, the amounts, etc. The gas limit is then bumped by a 20% extraPercentage
as a safety net.
If the gas limit estimation fails, the code may fall back to a high default value defined per asset (asset.gasLimit
).
Currently, there is no way to tune the gas limit resolution via remote config, and "extra percentage" values are hardcoded in the code.
Both the bumped gasLimit
and the bumped gasPrice
resolved when showing the fees are reused when sending.
The higher the gas limit and the gas price, the higher the max network fee, even though not all that gas may be used. The higher the max network fee, the lower the user's available balance.
Additional L2 Fees
L2 networks may have additional fees. Arbitrum One, Arbitrum Nova, and Mantle include the fees in the gas estimation (forceGasLimitEstimation
). Base and Aptos need an additional fee estimation to get this extra fee (estimateL1DataFee
).
The L2 extra fee needs to be considered when showing the fee to the user, resolving the available balance, and creating the transaction. The fee may be implicit in the transaction.
Custom Fees
Custom fees allow users to set a custom gasPrice
. The wallet allows a range, as follows:
Note: gasPrice = baseFeePerGas * gasPriceMultiplier + tipGasPrice
recommended: gasPrice
min: gasPrice * gasPriceMinimumRate
max: gasPrice * gasPriceMaximumRate
If the user picks the recommended value, the custom fee is not set, leaving the default fee behavior.
Custom fees can be disabled by setting rbfEnabled: false
via remote config. Currently, only Ethereum allows custom fees. Other EVMs could be included in the future.
The wallet allows acceleration when custom fees are allowed (and vice versa).
If the user picks a different custom fee in that range, the fee is calculated as customFee * gasLimit
.
A transaction created with a low custom fee may be stuck for a while, blocking future transactions, and it may create issues with the nonce, as explained above.
Min and max values can be tuned by changing gasPriceMinimumRate
and gasPriceMaximumRate
via remote config.
TX formulas
Note: gasPrice = baseFeePerGas * gasPriceMultiplier + tipGasPrice
Table shows how the txs are create for both legacy and 1559 logic for major use case:
Legacy Gas Price | gasPrice | gasPrice | gasPrice | customFee | customFee | max(current gasPrice,original tx gasPrice) * BUMP_RATE 1.2 |
Legacy Gas Limit | 21000 | 21000 | estimated + 20% extra | 21000 | estimated + 20% extra | original tx gasLimit |
- | - | - | - | - | - | - |
1559 Max gas Price | gasPrice | gasPrice | gasPrice | customFee | customFee | max(current gasPrice, original tx gasPrice) * BUMP_RATE 1.2 |
1559 Tip gas Price | tipGasPrice | gasPrice | tipGasPrice | customFee | tipGasPrice | orginal tx tipGasPrice * BUMP_RATE 1.2 |
1559 Gas Limit | 21000 | 21000 | estimated + 20% extra | 21000 | estimated + 20% extra | original tx gasLimit |
Fee Data Example
This is the default fee data configuration for Ethereum. Other EVMs have different default fee data.
All these values can be tuned via remote config, although it may not be recommended as they are also tuned by the fee monitors:
baseFeePerGas
: 50 Gwei
– Reference value for the base gas price. It is quickly updated by the fee monitor.
gasPrice
: 77 Gwei
– baseFeePerGas * gasPriceMultiplier + tipGasPrice
severGasPrice
: 75 Gwei
– Reference value for the network gas price. It is quickly updated by the fee monitor.
tipGasPrice
: 2 Gwei
– Controls the maxPriorityFeePerGas
for all transactions when EIP-1559 is enabled.
eip1559Enabled
: true
– Enables or disables EIP-1559. A value of false
means legacy fees and transactions.
rbfEnabled
: true
– Enables custom fees and acceleration. Currently, this is only available for Ethereum but can be expanded to other EVMs.
gasPriceMaximumRate
: 1.3
– Controls the maximum value in the custom fee slider.
gasPriceMinimumRate
: 0.5
– Controls the minimum value in the custom fee slider.
gasPriceMultiplier
: 1
– Controls the gasPrice
and maxFeePerGas
of transactions, resolved as current gas price * this multiplier
.
max
: 250 Gwei
– Controls the maximum gas price value that can be set.
min
: 1 Gwei
– Controls the minimum gas price value that can be set.
fuelThreshold
: 0.025 ETH
– If the balance falls below this value and the user holds tokens, a low balance warning is displayed.
enableFeeDelegation
: false
– Not implemented.
origin
: 75 Gwei
– Legacy; consider removing.
swapFee
: 0.05 ETH
– Legacy; consider removing.