A Penny saved is a Penny Earned
KyberSwap users can now enjoy an impressive reduction of up to 70% in gas fee costs on selected chains!
And this is just the beginning.
Our commitment to continuously improve user experience is unwavering.
The Journey So Far:
In KyberSwap’s mission to bring the best DeFi trading & earning experience for our users, our team has been working tirelessly to integrate more liquidity sources across our 14 supported chains (including PMMs), and researching how we can refine our routing algorithm to optimise returns for our users.
Recognising gas costs plays a pivotal role our DEX Aggregator performance and user experience, we ensure that performance improvements and cost-reductions go hand in hand.
A recent release several weeks ago brought a 10% savings in gas fees on all chains. But we weren’t content. Our latest DEX Aggregator version has elevated these savings to an astounding 70% on Arbitrum, Optimism, and soon, Base.
Why Arbitum, Optimsim and Base?
“calldata is expensive but computation is cheap” – 0xngmi
Transaction fee intricacies differ across networks. In most networks, it’s a simple calculation of [gas_price * gas_used]. But on networks like Arbitrum, Optimism, and Base, the transaction fee is slightly more complex due to an additional Layer 1 cost. This L1 cost, significantly affected by the transaction’s data size, can considerably influence the overall transaction fee.
For a clearer perspective, consider these formulas:
Regular networks (Ethereum, BNB Chain, Polygon, etc):
transaction_fee = gas_price * gas_used
L2 Networks like Arbitrum, Optimism, Base:
transaction_fee = l2_cost + l1_cost
l2_cost = l2_gas_price * l2_gas_used
l1_cost = l1_base_gas_price * l1_gas_used (depends on tx_calldata)
With our observations indicating that while l2_gas_price remains relatively low, it’s evident that on networks like Arbitrum and Optimism, while on-chain computation is cost-effective, transaction size isn’t.
Thus, curtailing the transaction size becomes key to gas fee reduction.
In the event of gas price surges on Ethereum (which mainnet users frequently experience), l1_cost is even more expensive compares to l2_cost.
With: l1_base_gas_price (Ethereum gas price) = 10 gweis, l2_gas_price = 0.02 gweis.
Reducing l1_gas_used by 1000 units is as good as reducing l2_gas_used by 500,000 units (which is huge).
Calldata Size Minimization through On-Chain Storage
As mentioned above, the calldata of the transaction plays an important factor in the total transaction_fee. So our prime focus was to reduce the calldata. This necessitated revisiting our encoding mechanisms, especially for transactions through multiple pools.
To provide our users with the most efficient swaps possible, KyberSwap Aggregator’s algorithm identifies optimal trading routes between tokens through our integrated liquidity sources.
By navigating through various pools, it secures the best rates for users. To instruct the smart contract on executing the swap, significant data, encompassing all the chosen pools, must be encoded and transmitted.
Consider the following example below: A trade converting 2000 USDT to 1523.41 OP involves 12 pools. To execute this swap in the Smart Contract, details of all 12 pools must be encoded and transmitted.
Each pool offers a unique swapping interface and necessitates a specific data set to compute the output and execute the swap. For L1 chains, such as Ethereum, BNB Chain, and Polygon, embedding this information directly in the transaction’s calldata is more cost-effective than accessing on-chain storage. However, on L2 chains like Arbitrum, Optimism, and Base, incorporating this data into the transaction calldata is pricier than retrieving from on-chain storages.
With these considerations, here’s how our team devised a solution to reduce the calldata:
Required information: struct: pool_address: address - 20 bytes token_in: address - 20 bytes token_out: address - 20 bytes pool_swap_fee: uint24 - 3 bytes swap_amount: uint256 - 32 bytes price_limit: uint160 - 20 bytes Total: 115 bytes Potential Reductions: - token0, token1, swapFee can be fetched from Elastic pool - use another logic to fetch token_in, for example: from the previous swap. Revised struct: pool_address: address - 20 bytes swap_amount: uint256 - 32 bytes price_limit: uint160 - 20 bytes Total: 72 bytes => reduce 43 bytes from the previous data (37.4% reduction)
These modifications were applied across all integrated DEXes, creating a substantial reduction in the transaction’s calldata size.
Streamlining Padding Bytes & Data Size:
Calldata optimization isn’t solely about reducing data. It’s also about intelligent data structuring, eliminating unnecessary padding, and adopting flexible data types to suit real-world scenarios.
When encoding with ABI (Application Binary Interface), a struct will align each of its members to fit into a 32 bytes layout. This means even a bytes4 member occupies a 32 bytes slot, leaving 28 bytes as padding zeros.
For compatibility and versatility across all tokens and pools, we utilise safe data types for each parameter, such as using uint256 for the swap_amount (can vary widely in size). However, this versatility results in extra padding bytes to fill up gaps in smaller data values.
Using the same example shown above, while the swap_amount is kept at uint256 (32 bytes) for flexibility, in most cases the swap_amount can fit into a smaller data size.
New struct: pool_address: address - 20 bytes swap_amount: uint256 - 32 bytes price_limit: uint160 - 20 bytes Total: 72 bytes => reduce 43 bytes from the previous data.
For typical ERC20tokens with 18 decimals, the swap_amount might only need a uint96 (around 12 bytes). Stablecoins, often with 6 decimals, could use even less: uint48 can handle values up to $280M, while uint40 can represent up to $1M.
Assuming swap from USDC to WETH, with amount of 1,000,000 USDC. Since USDC has a decimal of 6, swap_amount = 1,000,000,000,000 ~ 2^40 => The swap_amount can be reduced from 32 bytes to just 5 bytes New struct: pool_address: 20 bytes swap_amount: 5 bytes price_limit: 20 bytes Total: 45 bytes => reduce another 27 bytes from the optimized data (60.87% reduction from original)
Note: Additional optimisations, like for price_limit, aren’t detailed here but are implemented.
Consequently, we can streamline the data by removing excess padding zeros during compression.
Tailored Calldata Size Reductions:
We’ve also implemented various custom improvements to minimize calldata size, which vary depending on specific conditions and logics.
Quantifying the Savings
We analyzed over 50 real-world transactions across various pools, setting our benchmark parameters at L2_Gas_Price = 0.019 Gweis and L1_Gas_Price = 6.7 GWeis.
- Minimum Savings: 55.31%
- Maximum Savings: 68.26%
- Average Savings: 59.96%
- Notably, these results could amplify if tested with a higher L1_Gas_Price, such as 22 GWeis or even surging beyond 100+ GWeis.
- See the detailed benchmark results here:
For context, consider this sample transaction:
- Before optimization, the total fees stood at
- L2 Gas Used:
- L2 Gas Price:
- L1 Gas Used:
- L1 Gas Price:
- Total Fees:
- L2 Gas Used:
- Post-optimization, the fees reduced to
- L2 Gas Used:
- L2 Gas Price:
- L1 Gas Used:
- L1 Gas Price:
- Total Fees:
- L2 Gas Used:
Influencing Factors of Gas Fee Reduction
The potential gas savings largely depend on:
- Number of pools utilised in the swap.
- Prevailing L1 and L2 base gas prices.
Number of pools utilised in the swap:
The amount of gas saved is proportional to the number of pools utilised during the swap. Optimisation efforts are geared towards enhancing each pool’s structure. Hence the more pools a swap involves, the higher the potential savings, thanks to pool-specific optimisations.
Prevailing L1 and L2 Base Gas Prices:
While L2 base gas prices remain consistently low, fluctuations in the L1 gas price can impact overall savings.
Our efforts have successfully optimized L1_gas_used, but it may slightly increase L2_gas_used due to decoding processes and storage reads. The overall savings in percentage terms are influenced by both L1 and L2 base gas prices.
Notably, L2 base gas prices remain low, with rates like 0.1 gweis on Arbitrum and around 0.02 gweis or even less on Optimism. In contrast, L1 gas prices can fluctuate significantly, often during peak market activities.
More Enhancements Ahead
“I’m never satisfied with what I do. I always think I can do it a lot better.” – Michael Jackson
Our pursuit to bring the best DeFi trading & earning experience continues.
We’re channeling our energies into both L1 and L2 gas optimization strategies, including refining our router contract, devising a mapping mechanism, and more.
Prioritizing L1 Gas Optimisation
Given the comparatively higher transaction fees on the mainnet (Ethereum), refining L1 Gas Optimization remains at the forefront of our priorities.
Refinements in L2 Gas Optimization
Enhancing the DEX Aggregator Router:
Our initial focus was on refining underlying logics, maintaining the original Router contract, thus sparing our users from re-approving token allowances. The next iteration of Router optimisation promises to bring savings of at least an additional 10%, and in some scenarios, up to 22%.
Our plan is to store information directly on-chain, utilising indexes in the calldata to retrieve corresponding data. By ensuring index values don’t surpass a certain limit, say uint32 (equivalent to ~2^32 on-chain records), we’re aiming for a substantial reduction in the calldata size in our upcoming version.
Our vision at KyberSwap is simple: making DeFi accessible to millions.
Our mission is to offer a holistic DeFi platform for onboarding, trading, earning, and insights. Our continuous strides in enhancing user experience, be it via performance upgrades or gas fee reductions, reaffirm our commitment to this vision. With KyberSwap, users can anticipate an even brighter, efficient future in their DeFi journey.