-
Notifications
You must be signed in to change notification settings - Fork 7
[Refactor] Speed-up deployment. #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
After benchmark the process is not faster. |
BenchmarkAfter more rigorous testing, here are the results No deployment file
One deployment file
|
sparrowDom
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff! Left some comments
| "PENDLE_ORIGIN_ARM_SY": "0xbcae2Eb1cc47F137D8B2D351B0E0ea8DdA4C6184" | ||
| } | ||
| } | ||
| "contracts": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, the JSON seems less human readable with the new format. Is it more easily machine readable?
| # ╚══════════════════════════════════════════════════════════════════════════════╝ | ||
|
|
||
| # [Optional] Deployer address (corresponding to your private key) | ||
| # DEPLOYER_ADDRESS= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably rather set in console with a whitespace (to prevent storing to console history) rather than file? If it is in the file one can more easily forget to unset it.
| // Production chains | ||
| chainNames[1] = "Ethereum Mainnet"; | ||
| chainNames[146] = "Sonic Mainnet"; | ||
| chainNames[8453] = "Base Mainnet"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a deployments-17000.json in the build folder. Should we add holesky here?
| if (state == State.REAL_DEPLOYING) { | ||
| vm.startBroadcast(deployer); | ||
| } | ||
| if (state == State.FORK_TEST || state == State.FORK_DEPLOYING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be simplified to if (isSimulation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: else if
| if (state == State.REAL_DEPLOYING) { | ||
| vm.stopBroadcast(); | ||
| } | ||
| if (state == State.FORK_TEST || state == State.FORK_DEPLOYING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also can be simplified to if (isSimulation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit else if fits better
| /// | ||
| /// @param log Whether logging is enabled | ||
| /// @param prop The proposal to simulate | ||
| function simulate(bool log, GovProposal memory prop) internal { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how proposal can be in any stage of lifecycle and is then pushed to completion
|
|
||
| // ===== Step 7: Build & Handle Governance Proposal ===== | ||
| // Call the child contract's _buildGovernanceProposal() if implemented | ||
| _buildGovernanceProposal(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we validate governance proposal after it has been built?
- e.g. that the description has been set
- or that there is at least one action or one transaction being broadcasted?
| // avoiding unnecessary compilation and execution of historical deployment files. | ||
| // Scripts are numbered (e.g., 001_, 002_...) and sorted alphabetically, | ||
| // so only the last N scripts (most recent) will be considered for deployment. | ||
| uint256 public maxDeploymentFiles = 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be reasoned from the deploy times marked in the build/deployments-X.json file? If working off of non latest block, the chain's current time can be compared to the deployments.json time. To figure out if deployment needs to be ran or not.
Though then again things aren't so obvious when the governance proposal still needs to be executed.
|
|
||
| // Skip if the governance proposal for this script was already executed | ||
| // This means the script's purpose has been fully accomplished | ||
| if (deployFile.proposalExecuted()) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a similar issue with these proposal executions on origin-dollar repo. I think we would really benefit if there is a process that is able to fetch if and when a proposal has been executed on chain and add it to deployments file:
"executions": [
{
"name": "001_CoreMainnet",
"timestamp": 1723685111,
"gov_proposa_timestamp": 1723785111
},
This way we don't need to go back into source files and set proposalExecuted=true once the proposal is executed. And it will also be correct when a fork is ran on a past BLOCK_NUMBER. Which currently isn't true, as picking a very old block number would mean we need to set proposalExecuted=false in some of the deployment files
| string memory deployFileName = deployFile.name(); | ||
|
|
||
| // Check deployment history to see if this script was already run | ||
| bool alreadyDeployed = resolver.executionExists(deployFileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should consider that the fork might not have been initialized on the latest block.
Though supporting this would increase complexity quite a lot (similar to above comment regarding proposal executions)
Maybe it would be good to decide if deployments support forking off of older block number or no
Overview
Major refactoring of the deployment framework to improve speed, scalability, and developer experience. This transforms the deployment system from a basic script-based approach to a sophisticated, maintainable framework.
71 files changed | +3,733 lines | -2,687 lines
Motivation
The old deployment process had several limitations that made it difficult to maintain as the number of deployment scripts grew:
1. Manual Script Registration
Every new deployment script required:
DeployManager.solrun()function2. Inefficient JSON Serialization
The old approach read and wrote JSON line-by-line, which was:
vm.serializeUintcall overwrote previous data)3. AddressResolver Recreated Per Script
A new
AddressResolverwas instantiated in every script'srun()function, wasting gas and setup time.4. Address Passing via Constructor
Scripts had to receive addresses as constructor parameters, creating tight coupling:
Key Changes
1. Dynamic Script Discovery
Scripts are now auto-discovered from chain-specific folders (
mainnet/,sonic/). No more manual registration.2. Struct-Based JSON with Arrays
JSON is now parsed once using struct deserialization, and written once at the end:
New JSON format:
{ "contracts": [{"name": "LIDO_ARM", "implementation": "0x..."}], "executions": [{"name": "003_UpgradeLidoARMScript", "timestamp": 123}] }3. Persistent Resolver at Deterministic Address
A single
Resolvercontract is deployed viavm.etchto a deterministic address. All scripts access the same instance:4. Resolver-Based Address Lookups
Scripts now query the Resolver directly - no constructor params needed:
5. Standardized Deployment Lifecycle
New
AbstractDeployScriptprovides a clean lifecycle with override hooks:_execute()- Deploy contracts, register them_buildGovernanceProposal()- Define governance actions_fork()- Post-deployment verification6. Multi-Chain Support
Built-in support for multiple chains via folder-based organization:
script/deploy/mainnet/script/deploy/sonic/Benefits
CI Improvements
New Workflow Triggers
Before: CI only ran on
pull_requestandpushtomain.After: Added two new triggers:
cron: '0 6 * * *') – Daily at 6:00 AM UTC for comprehensive testingworkflow_dispatch) – Trigger full test suite from GitHub UI on demandProfile-Based Invariant Testing
Invariant tests now dynamically select their Foundry profile based on the trigger:
pull_requestlitepush(feature branch)litepush(main)cischeduleciworkflow_dispatchciThis provides fast feedback on PRs while ensuring thorough testing on main and scheduled runs.
Reusable Setup Action
Created
.github/actions/setup/action.ymlto DRY up the workflow:key: soldeer-${{ hashFiles('soldeer.lock') }})Smart Job Skipping
Jobs that don't need to run on scheduled builds are skipped:
lint,build,unit-tests→ Skip onschedule(no code changes to validate)smoke-tests,fork-tests→ Always run (validate live chain state)ciprofile (too slow for PR feedback)Time Reduction
The PR workflow is now 8× faster by using the
liteprofile, while scheduled runs maintain the same rigor with 20× more invariant test iterations.New Architecture
How to Write a New Deployment Script
script/deploy/mainnet/NNN_YourScript.s.solNNN_DescriptiveName.s.sol$NNN_DescriptiveName"NNN_DescriptiveName"See
script/deploy/mainnet/000_Example.s.solfor a complete template.Note