Created
April 2, 2026 14:46
-
-
Save denniswon/876d96f073fa37adce99eb5a77886e0b to your computer and use it in GitHub Desktop.
Newton Contract Deployment Infrastructure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| ★ Insight ───────────────────────────────────── | |
| Article Context (getrecon.substack.com): The Recon article introduces safe-utils and tenderly-utils — Foundry libraries for | |
| automating Safe multisig transaction proposals and Tenderly fork-based simulations. The key thesis: move governance/deployment | |
| operations from manual UI clicks to reviewable, version-controlled Foundry scripts with pre-execution simulation. This is directly | |
| relevant to Newton because we already have Foundry scripts but lack multisig governance and pre-deployment simulation infrastructure. | |
| ───────────────────────────────────────────────── | |
| --- | |
| Deep Analysis: Newton Contract Deployment Infrastructure | |
| Summary | |
| Newton has a well-structured deployment pipeline with environment differentiation (stagef/prod), chain auto-detection (source vs | |
| destination), retry with backoff, and deployment JSON as the single source of truth. However, there are 5 critical gaps and 8 | |
| high-value improvements that would materially improve security, reliability, and operational efficiency — especially as we approach | |
| mainnet production operations. | |
| --- | |
| Key Findings | |
| What's Working Well: | |
| - DeploymentLib.sol provides a clean single-source-of-truth pattern for contract addresses | |
| - Shell scripts have chain auto-detection, RPC validation, retry with exponential backoff | |
| - Deployment state tracking (.deploy-state/) prevents accidental re-deploys | |
| - Slither runs in CI on every PR | |
| - .env files are properly gitignored | |
| - Config JSONs are environment-scoped (newton_prover_config.{env}.json) | |
| Critical Gaps: | |
| 1. No multisig for production deployments — All deploy scripts use a single EOA (PRIVATE_KEY). The deployer key is a single point of | |
| compromise. The Recon article's safe-utils pattern directly addresses this. | |
| 2. No pre-deployment simulation — Deploy scripts go straight to broadcast (--broadcast). No dry-run or fork simulation before | |
| committing real transactions. A failed upgrade on mainnet is expensive. | |
| 3. No deployment diff/review step — When upgrade=true, there's no automated comparison of what's changing (storage layout, function | |
| selectors, new state variables). Upgrades are trust-the-developer. | |
| 4. Secrets in .env files on disk — While gitignored, the .env file contains 7 private keys, 2 API keys (OpenAI, Anthropic), and RPC | |
| URLs with embedded API keys, all as plaintext on the developer machine. The mainnet .env.mainnet has a funded deployer key. | |
| 5. No post-deployment verification beyond Etherscan — After deployment, we verify source code on Etherscan but don't verify that | |
| deployed bytecode matches expected, that proxy admin is correct, that initializers were called properly, or that storage layout | |
| hasn't been corrupted. | |
| --- | |
| Detailed Analysis by Area | |
| A. Environment Separation (stagef vs prod) | |
| Current state: The DEPLOYMENT_ENV variable (default stagef) is the sole differentiator. The same scripts, same patterns, same key | |
| management for both environments. | |
| ┌────────────────┬────────────────┬─────────────────────┬───────────────────────────────┐ | |
| │ Aspect │ stagef │ prod │ Gap │ | |
| ├────────────────┼────────────────┼─────────────────────┼───────────────────────────────┤ | |
| │ Key source │ .env file │ .env / .env.mainnet │ Same mechanism │ | |
| ├────────────────┼────────────────┼─────────────────────┼───────────────────────────────┤ | |
| │ Approval flow │ None │ None │ No human-in-the-loop for prod │ | |
| ├────────────────┼────────────────┼─────────────────────┼───────────────────────────────┤ | |
| │ Simulation │ None │ None │ No dry-run │ | |
| ├────────────────┼────────────────┼─────────────────────┼───────────────────────────────┤ | |
| │ Rollback plan │ Redeploy │ ??? │ No documented prod rollback │ | |
| ├────────────────┼────────────────┼─────────────────────┼───────────────────────────────┤ | |
| │ Deployment log │ Console output │ Console output │ No persistent audit trail │ | |
| └────────────────┴────────────────┴─────────────────────┴───────────────────────────────┘ | |
| Risk: A developer running make deploy-avs chain_id=1 env=prod from their laptop with the wrong .env.mainnet loaded could deploy | |
| incorrect contracts to Ethereum mainnet with no review gate. | |
| B. Upgrade Safety | |
| The upgrade path in deploy-avs.sh (lines 119-120) simply passes --sig "run(bool)" true to the deployer script, which calls | |
| NewtonProverDeploymentLib.upgradeContracts(). This: | |
| - Deploys new implementation contracts | |
| - Calls UpgradeableProxyLib.upgrade(proxy, newImpl) on each proxy | |
| - Writes updated deployment JSON | |
| Missing safeguards: | |
| - No storage layout compatibility check (forge inspect --storage-layout diff) | |
| - No function selector collision check between old and new implementations | |
| - No bytecode size check against EIP-170 (24576 bytes) before broadcast | |
| - No initialization state verification after upgrade | |
| - No timelock — upgrades take effect immediately | |
| C. Key Management | |
| Current key distribution from .env: | |
| ┌──────────────────────────────────────────────┬─────────────────────────────┬──────────────────────────────────┐ | |
| │ Key │ Purpose │ Risk Level │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ PRIVATE_KEY │ Deployer + ProxyAdmin owner │ Critical — controls all upgrades │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ GATEWAY_OPERATOR_SIGNER_PRIVATE_KEY │ Gateway operator │ High │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ OPERATOR_1/2_SIGNER_PRIVATE_KEY │ Operators │ High │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ NEWTON_RPC_TASK_GENERATOR_SIGNER_PRIVATE_KEY │ Task generator auth │ High │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ AGGREGATOR_SIGNER_PRIVATE_KEY │ Same as PRIVATE_KEY │ Critical — shared key │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ REWARDS_OWNER_KEY │ Reward distribution │ Medium │ | |
| ├──────────────────────────────────────────────┼─────────────────────────────┼──────────────────────────────────┤ | |
| │ OPENAI_API_KEY / ANTHROPIC_API_KEY │ AI audit agent │ Low │ | |
| └──────────────────────────────────────────────┴─────────────────────────────┴──────────────────────────────────┘ | |
| The AGGREGATOR_SIGNER_PRIVATE_KEY is identical to PRIVATE_KEY (both 0x563189...). This means the aggregator and the deployer/proxy | |
| admin share the same key — compromising one compromises all contracts. | |
| D. CI Security Pipeline | |
| Current CI for contracts: | |
| - Slither runs on every PR (slither.yml) but with continue-on-error: true — findings don't block merges | |
| - No forge test in the Slither workflow | |
| - No cargo audit or cargo deny for Solidity dependencies | |
| - No automated gas snapshot comparison between PRs | |
| - No deployment simulation in CI for upgrade PRs | |
| --- | |
| Recommendations (Prioritized) | |
| Now, this is where the Recon article's patterns and our codebase analysis converge. Here are concrete improvements, ordered by | |
| impact: | |
| Priority 1: Immediate Security Wins | |
| 1.1 — Separate deployer key from aggregator key (TODAY) | |
| The shared PRIVATE_KEY == AGGREGATOR_SIGNER_PRIVATE_KEY is the highest-risk finding. If the aggregator is compromised at runtime, the | |
| attacker owns the proxy admin and can upgrade any contract. | |
| 1.2 — Add deployment dry-run mode | |
| Before any --broadcast, add a --dry-run flag to all deploy scripts that runs forge script WITHOUT --broadcast, just simulation: | |
| # In deploy-avs.sh, before the actual deployment | |
| if [ "${DRY_RUN:-false}" = "true" ]; then | |
| forge_cmd="$forge_cmd" # No --broadcast | |
| echo "DRY RUN MODE: simulating deployment without broadcasting" | |
| else | |
| forge_cmd="$forge_cmd --broadcast" | |
| fi | |
| 1.3 — Make Slither blocking in CI | |
| Remove continue-on-error: true from slither.yml, or at minimum fail on High/Critical findings. | |
| Priority 2: Governance Infrastructure | |
| 2.1 — Integrate safe-utils for production operations | |
| This is the direct application of the Recon article. For mainnet/prod, deployments and admin operations should go through a Safe | |
| multisig: | |
| // In AdminScript.s.sol, add Safe proposal mode | |
| import {Safe} from "safe-utils/Safe.sol"; | |
| if (isProduction()) { | |
| // Propose to multisig instead of direct execution | |
| safe.proposeTransaction( | |
| taskManager, | |
| abi.encodeCall(ITaskManager.setTaskGenerator, (newGenerator)), | |
| deployer | |
| ); | |
| } else { | |
| // Direct execution for stagef/local | |
| vm.broadcast(deployer); | |
| taskManager.setTaskGenerator(newGenerator); | |
| } | |
| 2.2 — Integrate tenderly-utils for fork simulation | |
| Before any prod deployment, automatically create a Tenderly Virtual TestNet fork and simulate the full deployment: | |
| # New make target | |
| simulate-deploy: | |
| TENDERLY_FORK=true forge script script/NewtonProverDeployer.s.sol \ | |
| --rpc-url $(tenderly fork-url) \ | |
| --broadcast | |
| 2.3 — Add a deployment timelock for prod upgrades | |
| A 48-hour timelock on proxy upgrades for production contracts. This gives the team time to review and the community time to react. | |
| Priority 3: Automated Safety Checks | |
| 3.1 — Pre-upgrade storage layout validation | |
| Add a script that compares storage layouts before allowing upgrades: | |
| # New target: make check-upgrade chain_id=1 env=prod | |
| check-upgrade: | |
| forge inspect src/NewtonProverTaskManager.sol:NewtonProverTaskManager storage-layout > new_layout.json | |
| # Compare against stored layout from last deployment | |
| diff <(jq -S . deployments/layouts/NewtonProverTaskManager.json) <(jq -S . new_layout.json) | |
| 3.2 — Post-deployment invariant checks | |
| After every deployment, run a verification script that checks: | |
| - Proxy admin is the expected address | |
| - All proxies point to new implementations | |
| - Initializers have been called (check initialized state variables) | |
| - Contract sizes are under EIP-170 limit | |
| - Key functions are callable (smoke test) | |
| // script/VerifyDeployment.s.sol | |
| function run() external view { | |
| DeploymentData memory d = DeploymentLib.readDeploymentJson(block.chainid); | |
| // Verify proxy implementations | |
| require(getImpl(d.newtonProverTaskManager) == d.newtonProverTaskManagerImpl); | |
| require(getImpl(d.newtonProverServiceManager) == d.newtonProverServiceManagerImpl); | |
| // Verify proxy admin | |
| address admin = getAdmin(d.newtonProverTaskManager); | |
| require(admin != address(0), "ProxyAdmin not set"); | |
| // Verify initializer state | |
| require(TaskManager(d.newtonProverTaskManager).initialized(), "Not initialized"); | |
| // Verify EIP-170 | |
| require(d.newtonProverTaskManagerImpl.code.length <= 24576, "EIP-170 exceeded"); | |
| } | |
| 3.3 — Deployment audit trail | |
| Every deployment should produce a structured log file (not just console output): | |
| { | |
| "timestamp": "2026-04-02T10:30:00Z", | |
| "chain_id": 1, | |
| "env": "prod", | |
| "deployer": "0x...", | |
| "action": "upgrade", | |
| "contracts_changed": ["NewtonProverTaskManager", "ChallengeVerifier"], | |
| "tx_hashes": ["0x...", "0x..."], | |
| "gas_used": 4500000, | |
| "bytecode_hashes": { "NewtonProverTaskManager": "0x..." }, | |
| "verified_on_etherscan": true | |
| } | |
| Priority 4: Key Management Improvements | |
| 4.1 — Migrate production keys to hardware wallets or AWS KMS | |
| For mainnet, the deployer key should be in a hardware wallet (Ledger/Trezor via --ledger flag in Forge) or AWS KMS (via a custom | |
| signer). Plaintext .env files for mainnet keys are inadequate. | |
| # Forge supports hardware wallets natively | |
| forge script script/NewtonProverDeployer.s.sol \ | |
| --ledger --sender 0x... \ | |
| --rpc-url $RPC_URL --broadcast | |
| 4.2 — Rotate the shared deployer/aggregator key | |
| Generate a new key for the aggregator signer. Update .env and the config JSON. Transfer proxy admin ownership to a multisig. | |
| --- | |
| Implementation Roadmap | |
| ┌──────────────────────┬────────────────┬────────────────────────────────────────────────────────────────┬────────┐ | |
| │ Phase │ Timeline │ Items │ Effort │ | |
| ├──────────────────────┼────────────────┼────────────────────────────────────────────────────────────────┼────────┤ | |
| │ Quick wins │ This week │ 1.1 (key separation), 1.2 (dry-run), 1.3 (Slither blocking) │ Low │ | |
| ├──────────────────────┼────────────────┼────────────────────────────────────────────────────────────────┼────────┤ | |
| │ Governance │ Next 2 weeks │ 2.1 (safe-utils), 3.3 (audit trail), 4.2 (key rotation) │ Medium │ | |
| ├──────────────────────┼────────────────┼────────────────────────────────────────────────────────────────┼────────┤ | |
| │ Safety automation │ Next month │ 3.1 (storage layout), 3.2 (post-deploy checks), 2.2 (Tenderly) │ Medium │ | |
| ├──────────────────────┼────────────────┼────────────────────────────────────────────────────────────────┼────────┤ | |
| │ Production hardening │ Before mainnet │ 2.3 (timelock), 4.1 (hardware wallets), full E2E simulation │ High │ | |
| └──────────────────────┴────────────────┴────────────────────────────────────────────────────────────────┴────────┘ | |
| ★ Insight ───────────────────────────────────── | |
| The Recon article's core thesis maps directly to Newton's gaps. Newton already has the scripts — what's missing is the governance | |
| wrapper (Safe multisig for prod), the simulation layer (Tenderly forks before broadcast), and the verification layer (post-deploy | |
| invariant checks). The upgrade from "scripts that deploy" to "scripts that propose, simulate, then deploy" is the security maturity | |
| jump the article advocates. Newton's existing DeploymentLib + AdminLib pattern is actually well-suited for wrapping with safe-utils | |
| because admin operations are already command-dispatched — you'd add a Safe proposal path alongside the direct execution path. | |
| ───────────────────────────────────────────────── | |
| --- | |
| This is a starting point for a broader brainstorm. Several of these items have meaningful design choices (e.g., timelock duration, | |
| multisig threshold, which operations go through governance vs direct execution). Want me to drill into any specific area — say, | |
| designing the Safe multisig integration, building the storage layout validation script, or setting up the Tenderly simulation | |
| pipeline? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment