LI.FI 2.0 Expansion. Learn more.
tl;dr
Deploying and managing smart contracts on EVM networks often incurs significant challenges due to RPC issues, gas price fluctuations, and other mysterious errors, especially when dealing with multiple networks. In response to these challenges, LI.FI has developed a custom scripting framework that combines Bash and Solidity scripting capabilities to streamline the deployment, configuration, and management of our hundreds of smart contracts across more than 25 networks. Today, we are excited to share this innovative solution with the public, aiming to enhance overall efficiency in multi-chain smart contract management.
In this article we will explain:
Challenges in multi-chain smart contract environments
LI.FI’s smart contract setup
LI.FI’s solution: a combination of Bash and Foundry scripts and tools
Closing thoughts and link to our GitHub repo
LI.FI is the leading bridge and DEX aggregator in crypto connecting 20 EVM chains, 12 bridges, 25 DEXs, and 5 DEX aggregators (and growing).
LI.FI’s product portfolio features sophisticated white-label B2B solutions that not only allow same-/cross-chain swapping capabilities but also grant arbitrary contract calls for the implementation of advanced cross-chain strategies such as yield aggregation, LP-zapping, NFT purchasing, and many more.
LI.FI also caters to the B2C market with jumper.exchange, crypto’s fast-growing “everything exchange” that empowers users to swap from a vast pool of tokens at the most competitive rates available - with optional insurance for added security - and to buy crypto with over 130 different fiat currencies.
Deploying smart contracts can be a tedious task. If you’ve done it before, you will most likely have run into various problems such as RPC’s that don't respond or throw errors, insufficient gas estimates, or just plain (error) mystery that cause your deploy script to fail. Add contract verification to that process and the likelihood of your script completing without errors quickly diminishes.
Successfully deploying contracts on one network alone is often cumbersome, but doing it across multiple networks (in LI.FI’s case over 25 of them, including testnets) quickly becomes a complex task that can easily take several days to complete. For example, a simple task like changing the configuration of a single contract in all networks requires LI.FI to execute a script 20 times or so - and that is only if all goes well.
Furthermore, keeping track of which code has been deployed where and which versions of the code have been audited (in which audit) is also important for anyone that considers security to be the top priority like LI.FI does.
And last but not least, it is also quite helpful to maintain a list of deployed-to-contract addresses in our contracts repo as a reference for other teams and developers and which other scripts can read from in case they need to interact with any of these contracts.
Before we dive into how we are responding to these challenges, we want to provide a brief introduction to our smart contract setup.
LI.FI’s smart contract setup is a so-called “diamond contract” setup, a concept developed by Nick Mudge and described in EIP2535. Its biggest advantage is that it bypasses the bytecode size limit for contract deployments which allows the contract to employ near-limitless amounts of business logic.
Our main (diamond) contract does not contain any business logic at all. Instead, it gains its functionality from almost 40 facet contracts, 12 helper/library contracts, and an additional 8 periphery contracts that it interacts with (e.g. for fee collection and message execution).
Editor’s Note: We will describe our diamond contract setup in more detail in a dedicated blog post that will follow soon.
A network-specific subset of the above-mentioned contracts has been deployed to about 25 EVM networks, of which 20 are mainnets. No need to ask Pepe to do the math - we can say with confidence that we have well over 300 contracts in action out there. It has taken us a considerable amount of time to deploy them and we frequently have to add contracts or change configurations.
With that much technical overhead, we became very serious about improving our smart contract management and making it more efficient. Now without further ado, let us show you how we leverage a simple programming language like Bash script to simplify multi-chain smart contract management.
Here at LI.FI we always strive for excellence, so the first logical step was to define basic requirements for efficient multi-chain smart contract management.
We came to the conclusion that LI.FI’s solution…
should keep our secrets safe at all times (e.g. private keys, API credentials, etc.)
should be robust enough to complete a given task without supervision/manual restarts
should support that scripts continue where they left off, if restarted for any reason
should be able to complete multi-network tasks in one script execution
should support multiple diamond contracts per network (e.g. we have one mutable diamond which can be extended and re-configured and one immutable diamond which cannot be altered by anyone at all)
should store basic information about all deployed contracts in a (JSON) log file that can be accessed by scripts, as well as by other contexts/applications
should allow to define a target state for each network and environment (staging/production). A target state describes which contracts in which version should be available on a given network.
be able to batch-update a given network (or all networks) to this target state. should provide a simple overview of the current deploy status in comparison to the mentioned target state (to show us where we still have gaps)
should check if all data required for deploying a contract (e.g. contract addresses for constructor arguments) is available prior to deployment – which helps us to make sure that contracts are deployed in the correct order
To meet all these requirements whilst trying to keep it as simple as possible, we went for a combination of Bash scripts that leverage various Foundry tools such as cast, anvil and script. Using Bash scripts on the governing, uppermost level allows us to not only re-run failed Foundry scripts but also to execute one particular task on multiple networks within one script execution.
We have one main script from which we control all other scripts: scriptMaster.sh
This script produces simple user dialogs to find out which use case to execute and in which network/environment (if not in all of our connected networks). The central script will then call other script files that are purpose-built to orchestrate specific tasks such as:
deploying a single contract (optional: add it to a diamond and/or verify it)
deploying a whole diamond setup to a new network which means deploying core facets, a diamond contract as well as a predefined list of functional facets, and the periphery that will be configured and added to the diamond contract after their deployment
changing the configuration of a smart contract
making a diamond contract immutable
proposing transactions / smart contract upgrades to our multi-sig wallet
verifying all contracts that have not yet been verified
batch-updating or parsing files (e.g. to produce a target state .json from a Google Sheet)
Most of the functionality that makes our scripts so powerful resides within various helper functions that are all collected in helperFunctions.sh. Some of these functions deal with logging, reading from the blockchain or local filesystem while others simply iterate over several networks to do stuff.
The main script also uses a config file called config.sh that allows us to tweak the script execution in many ways. For example we can set the number of attempts if something fails, change directories, activate or deactivate certain features (such as verification or “debug mode” with extended logging), start a local network and test deployments and scripts on this local network or exclude certain networks or contracts for a particular script execution.
Contract deployments are done by Foundry scripts written in Solidity located in folder script/deploy/facets/.... Each contract has its own deploy script (Deploy<ContractName>.s.sol) that gathers all necessary data (i.e. constructor and initializer parameters) from bridge-specific config files (which are located in config/... ) and deploys the contract using our custom Create3Factory contract to ensure that we have the same contract addresses across all EVM networks. Additionally, for each contract we have an update script (Update<ContractName>.s.sol) that adds the contract to our diamond (facets will be added via DiamondCut and periphery contracts will be registered in our diamond leveraging the code of our PeripheryRegistryFacet). Any facet changes (aka diamond cuts) have to be approved by a multisig wallet for which we use a Safe contract. This ensures that even if one private key is compromised, our contracts, and therefore, our users, are still safe at all times.
Our goal in sharing LI.FI’s journey is not in pursuit of perfection, but in the spirit of communal learning and building in public. Our contracts repository has grown organically to a considerable size and complexity and we are aware that the presented solution is highly customized to our needs and repo structure. However, we encourage the crypto community to adapt, build upon, and improve these scripts for whatever specific requirements their team might have. Other creative applications and constructive feedback will spur us on, and we're excited to see what other teams might achieve with this toolset. Please share your experiences, solutions and extensions with us.
Now, it's time for you to dive into the world of efficient multi-chain smart contract management. Explore our contracts repo and discover new possibilities: Github
LI.FI is leading the way as a bridge and DEX aggregator – come and be part of our journey.
Ready to experience seamless token swapping, or looking to buy crypto for fiat?
Try out Jumper, powered by LI.FI, offering the smoothest, cheapest routes with optional insurance to keep your funds safe. Discover a whole new way of interacting with the crypto world today.
Enjoyed reading our research? To learn more about us:
Disclaimer: This article is only meant for informational purposes. The projects mentioned in the article are our partners, but we encourage you to do your due diligence before using or buying tokens of any protocol mentioned. This is not financial advice.