Complete Guide to Deploying Rollups: Mainnet Walkthrough for Web3 Developers

Table of Contents
- Understanding Rollups: A Quick Primer
- Prerequisites for Rollup Deployment
- Setting Up Your Development Environment
- Choosing the Right Rollup Framework
- Configuring Your Rollup Solution
- Implementing Smart Contracts for Your Rollup
- Testing Your Rollup on Testnet
- Mainnet Deployment Walkthrough
- Monitoring and Maintaining Your Rollup
- Security Considerations and Best Practices
- Conclusion and Next Steps
Complete Guide to Deploying Rollups: Mainnet Walkthrough for Web3 Developers
Rollups have emerged as one of the most promising Layer 2 scaling solutions for blockchain networks, particularly Ethereum. By processing transactions off-chain and posting only the essential data on-chain, rollups deliver significant improvements in transaction throughput and cost-efficiency without sacrificing the security guarantees of the underlying blockchain.
Whether you're building a dApp that needs to scale beyond the limitations of the base layer, or you're exploring rollup technology as a scaling solution for an existing project, understanding how to deploy a rollup to mainnet is becoming an essential skill for Web3 developers.
In this comprehensive guide, we'll walk through the entire process of deploying a rollup to mainnet – from initial setup to final deployment and maintenance. We'll cover optimistic rollups and ZK-rollups, highlighting the differences in implementation approaches and requirements while providing practical code examples and best practices along the way.
By the end of this tutorial, you'll have the knowledge and confidence to deploy your own rollup solution to mainnet, unlocking new possibilities for your blockchain projects.
Understanding Rollups: A Quick Primer
Before diving into deployment, let's quickly review what rollups are and how they work. Rollups are Layer 2 scaling solutions that perform transaction execution outside the main blockchain (Layer 1) but post transaction data on Layer 1. This approach allows rollups to inherit the security properties of the underlying blockchain while significantly increasing throughput and reducing gas costs.
There are two main types of rollups:
Optimistic Rollups: These assume transactions are valid by default and only run computation through fraud proofs in case of disputes. Examples include Arbitrum, Optimism, and Mantle.
Zero-Knowledge Rollups (ZK-Rollups): These generate cryptographic validity proofs that verify the correctness of batched transactions. Examples include zkSync, StarkNet, and Polygon zkEVM.
Both approaches offer unique advantages in terms of security guarantees, finality time, and computational complexity. Your choice will depend on your specific requirements and technical constraints.
Prerequisites for Rollup Deployment
Before you start deploying a rollup to mainnet, make sure you have:
- Strong understanding of Ethereum and smart contract development
- Familiarity with the rollup technology you plan to implement
- Sufficient ETH for gas fees during deployment (depending on the specific rollup implementation, mainnet deployment can cost anywhere from 1-5 ETH)
- A secure key management solution for deploying contracts
- Access to necessary development tools (Hardhat, Foundry, Truffle, etc.)
If you're new to blockchain development, consider starting with our learning track to build the foundational knowledge needed for this advanced topic.
Setting Up Your Development Environment
Let's begin by setting up our development environment. We'll use Hardhat as our development framework for this tutorial, but the concepts can be applied to other tools as well.
bash
Create a new directory for your rollup project
mkdir my-rollup cd my-rollup
Initialize a new npm project
npm init -y
Install Hardhat and essential dependencies
npm install --save-dev hardhat @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
Initialize Hardhat in the project directory
npx hardhat
Select the "Create a basic sample project" option when prompted. This will create a basic Hardhat setup that we'll expand upon.
Next, configure your Hardhat environment to connect to both testnet and mainnet. Update your hardhat.config.js
file:
javascript require("@nomiclabs/hardhat-waffle");
// Load environment variables require('dotenv').config();
module.exports = {
solidity: "0.8.17",
networks: {
goerli: {
url: https://goerli.infura.io/v3/${process.env.INFURA_API_KEY}
,
accounts: [process.env.PRIVATE_KEY]
},
mainnet: {
url: https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}
,
accounts: [process.env.PRIVATE_KEY]
}
}
};
Create a .env
file to store your private keys and API endpoints securely:
PRIVATE_KEY=your_private_key_here INFURA_API_KEY=your_infura_api_key
Remember to add .env
to your .gitignore
file to prevent accidentally exposing your private keys.
Choosing the Right Rollup Framework
For this tutorial, we'll explore implementing both an Optimistic Rollup and a ZK-Rollup. Let's examine the available frameworks for each:
Optimistic Rollup Frameworks:
- Optimism SDK: A mature framework for building Optimistic Rollups
- Arbitrum Nitro: Arbitrum's latest stack for Optimistic Rollups
- Mantle: A modular Optimistic Rollup with customizable features
ZK-Rollup Frameworks:
- zkSync Era: A developer-friendly ZK-Rollup framework
- StarkNet: A permissionless ZK-Rollup using Cairo for smart contracts
- Polygon zkEVM: An EVM-compatible ZK-Rollup solution
For our tutorial, we'll use a simplified approach that illustrates the core concepts of rollup deployment without being tied to a specific framework. However, in practice, you would likely choose one of these established frameworks rather than building entirely from scratch.
Configuring Your Rollup Solution
The configuration process varies significantly depending on whether you're implementing an Optimistic Rollup or a ZK-Rollup. Let's look at the key components for each type:
Optimistic Rollup Configuration
For an Optimistic Rollup, you'll need to configure:
- Sequencer: The component responsible for ordering transactions
- Aggregator: The component that batches transactions and posts them to L1
- Fraud Proof System: The mechanism for disputing invalid state transitions
- Challenge Period: The time window during which transactions can be challenged
Here's an example configuration for a basic Optimistic Rollup:
javascript // optimisticRollupConfig.js module.exports = { sequencerAddress: "0x...", // Address of the sequencer challengePeriod: 604800, // 7 days in seconds minBondAmount: ethers.utils.parseEther("1"), // Minimum bond for challenges maxBatchSize: 100, // Maximum number of transactions per batch l1GasPrice: 30000000000, // 30 gwei batchPostingInterval: 3600 // Post batches hourly (in seconds) };
ZK-Rollup Configuration
For a ZK-Rollup, your configuration will focus on:
- Prover: The component that generates validity proofs
- Aggregator: The component that batches transactions
- Proof System Parameters: Parameters for the zero-knowledge proof system
- Circuit Configuration: Setup for the proving circuits
A simplified ZK-Rollup configuration might look like this:
javascript // zkRollupConfig.js module.exports = { proverAddress: "0x...", // Address of the prover provingScheme: "PLONK", // The ZK proving scheme maxBatchSize: 50, // Maximum number of transactions per batch recursionLevel: 2, // Levels of proof recursion l1GasPrice: 30000000000, // 30 gwei batchPostingInterval: 1800 // Post batches every 30 minutes (in seconds) };
Implementing Smart Contracts for Your Rollup
Regardless of the rollup type, you'll need to implement several key smart contracts for your rollup to function properly. Let's explore these core contracts:
1. Rollup Contract
This is the main contract that serves as the entry point for your rollup system:
solidity // contracts/Rollup.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.17;
import "./StateCommitment.sol"; import "./TransactionBatcher.sol";
contract Rollup { address public owner; StateCommitment public stateCommitment; TransactionBatcher public transactionBatcher;
// Rollup type flags
bool public isOptimistic;
event BatchSubmitted(uint256 indexed batchId, bytes32 stateRoot);
constructor(bool _isOptimistic) {
owner = msg.sender;
isOptimistic = _isOptimistic;
// Initialize dependent contracts
stateCommitment = new StateCommitment();
transactionBatcher = new TransactionBatcher(address(stateCommitment));
}
function submitBatch(bytes calldata _batch, bytes32 _stateRoot) external {
require(msg.sender == owner, "Only owner can submit batches");
// Process batch transactions
transactionBatcher.processBatch(_batch);
// Update state commitment
uint256 batchId = stateCommitment.commitState(_stateRoot);
emit BatchSubmitted(batchId, _stateRoot);
}
// Additional functions would vary based on rollup type (Optimistic vs ZK)
}
2. State Commitment Contract
This contract manages the state roots and state transitions:
solidity // contracts/StateCommitment.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.17;
contract StateCommitment { struct StateUpdate { bytes32 stateRoot; uint256 timestamp; bool finalized; }
mapping(uint256 => StateUpdate) public stateUpdates;
uint256 public latestBatchId;
event StateCommitted(uint256 indexed batchId, bytes32 stateRoot);
event StateFinalized(uint256 indexed batchId);
function commitState(bytes32 _stateRoot) external returns (uint256) {
uint256 batchId = latestBatchId + 1;
stateUpdates[batchId] = StateUpdate({
stateRoot: _stateRoot,
timestamp: block.timestamp,
finalized: false
});
latestBatchId = batchId;
emit StateCommitted(batchId, _stateRoot);
return batchId;
}
function finalizeState(uint256 _batchId) external {
require(_batchId <= latestBatchId, "Batch does not exist");
require(!stateUpdates[_batchId].finalized, "State already finalized");
stateUpdates[_batchId].finalized = true;
emit StateFinalized(_batchId);
}
function getStateRoot(uint256 _batchId) external view returns (bytes32) {
require(_batchId <= latestBatchId, "Batch does not exist");
return stateUpdates[_batchId].stateRoot;
}
}
3. Transaction Batcher Contract
This contract handles the batching of transactions:
solidity // contracts/TransactionBatcher.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.17;
contract TransactionBatcher { address public stateCommitmentContract;
event BatchProcessed(uint256 txCount);
constructor(address _stateCommitmentContract) {
stateCommitmentContract = _stateCommitmentContract;
}
function processBatch(bytes calldata _batch) external {
// In a real implementation, this would decode and process transactions
// For simplicity, we're just emitting an event with the transaction count
// This is a placeholder for the actual batch processing logic
// Assuming each transaction is 128 bytes for this example
uint256 txCount = _batch.length / 128;
emit BatchProcessed(txCount);
}
}
For an Optimistic Rollup, you would also need to implement a fraud proof system, while a ZK-Rollup would require a verification contract for the validity proofs. These components are more complex and would typically be handled by the frameworks mentioned earlier.
Testing Your Rollup on Testnet
Before deploying to mainnet, it's essential to thoroughly test your rollup on a testnet environment. This step helps identify and fix issues without risking real funds.
- First, deploy your contracts to a testnet like Goerli:
bash npx hardhat run scripts/deploy.js --network goerli
Your deployment script (scripts/deploy.js
) should look something like this:
javascript async function main() { // Get the contract factory const Rollup = await ethers.getContractFactory("Rollup");
// Deploy the contract (true for Optimistic, false for ZK) const rollup = await Rollup.deploy(true); await rollup.deployed();
console.log("Rollup deployed to:", rollup.address); }
main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- Next, create a test script to submit batches and verify state updates:
javascript // scripts/testRollup.js async function main() { // Get the deployed contract const rollupAddress = "YOUR_DEPLOYED_ROLLUP_ADDRESS"; const Rollup = await ethers.getContractFactory("Rollup"); const rollup = Rollup.attach(rollupAddress);
// Create a sample batch - in a real scenario this would contain encoded transactions const batch = ethers.utils.hexlify(ethers.utils.randomBytes(1280)); // 10 mock transactions
// Create a sample state root - in a real scenario this would be computed from the state const stateRoot = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("test state"));
// Submit a batch const tx = await rollup.submitBatch(batch, stateRoot); await tx.wait();
console.log("Batch submitted successfully!");
// Verify the state update const stateCommitmentAddress = await rollup.stateCommitment(); const StateCommitment = await ethers.getContractFactory("StateCommitment"); const stateCommitment = StateCommitment.attach(stateCommitmentAddress);
const latestBatchId = await stateCommitment.latestBatchId(); const storedStateRoot = await stateCommitment.getStateRoot(latestBatchId);
console.log(Latest batch ID: ${latestBatchId}
);
console.log(Stored state root: ${storedStateRoot}
);
console.log(Expected state root: ${stateRoot}
);
console.log(State roots match: ${storedStateRoot === stateRoot}
);
}
main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- Run the test script:
bash npx hardhat run scripts/testRollup.js --network goerli
Monitor the test execution on the testnet and check for any issues or unexpected behaviors. Use HackQuest's faucets to get testnet ETH if needed.
Mainnet Deployment Walkthrough
Once you've thoroughly tested your rollup on testnet and are confident in its functionality, you can proceed with mainnet deployment. This is a critical step that requires careful preparation and execution.
Preparing for Mainnet Deployment
-
Security Audit: Consider getting your contracts audited by a reputable security firm before mainnet deployment.
-
Gas Optimization: Ensure your contracts are optimized for gas efficiency, as mainnet deployment will be expensive.
-
Deployment Funds: Ensure you have sufficient ETH in your deployment wallet to cover gas costs.
-
Deployment Strategy: Plan your deployment sequence, especially if your system involves multiple interdependent contracts.
Deploying to Mainnet
With preparation complete, you can deploy to mainnet:
bash npx hardhat run scripts/deploy.js --network mainnet
This command will use your deployment script to deploy the contracts to Ethereum mainnet. Be patient, as mainnet transactions often take longer to confirm, especially during periods of high network congestion.
After deployment, verify all contract addresses and ensure they're correctly linked to each other. Store the contract addresses securely for future reference.
Verifying Contract Code
To enhance transparency, it's important to verify your contract code on Etherscan. This allows users to inspect and interact with your contracts directly through Etherscan's interface.
You can use the Hardhat Etherscan plugin for verification:
bash npm install --save-dev @nomiclabs/hardhat-etherscan
Add the plugin to your hardhat.config.js
file:
javascript require("@nomiclabs/hardhat-etherscan");
module.exports = { // ... existing configuration etherscan: { apiKey: process.env.ETHERSCAN_API_KEY } };
Verify your contracts:
bash npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS constructor_argument_1 constructor_argument_2
For our Rollup contract example, the verification command would be:
bash npx hardhat verify --network mainnet ROLLUP_CONTRACT_ADDRESS true
Assuming true
was the constructor argument indicating an Optimistic Rollup.
Monitoring and Maintaining Your Rollup
Once your rollup is deployed to mainnet, it's essential to monitor its performance and maintain its operation:
-
Health Monitoring: Set up monitoring systems to track key metrics such as transaction throughput, batch submission times, and gas costs.
-
Security Monitoring: Continuously monitor for potential security threats or unusual activity on your rollup.
-
Performance Optimization: Analyze usage patterns and optimize your rollup configuration for better performance and lower costs.
-
Regular Updates: Plan for regular maintenance updates to address improvements and fix issues.
-
Community Support: Establish channels for users to report issues and provide feedback.
Consider implementing a dashboard for monitoring your rollup's performance and health. This dashboard could track metrics such as:
- Active users
- Transaction volume
- Average transaction cost
- Batch submission frequency
- State root updates
- Challenge/proof verification status (depending on rollup type)
Security Considerations and Best Practices
Security is paramount when deploying a rollup to mainnet. Here are some essential security considerations and best practices:
-
Multi-signature Control: Use a multi-signature wallet for administrative control of critical functions.
-
Timelocks: Implement timelock mechanisms for significant state changes or upgrades.
-
Rate Limiting: Consider implementing rate limiting for certain operations to prevent spam or abuse.
-
Emergency Shutdown: Design a secure emergency shutdown mechanism for critical situations.
-
Formal Verification: Consider formal verification for critical contract components.
-
Conservative Upgrades: Implement upgrades conservatively, with thorough testing and gradual rollout.
-
Economic Security: Ensure your economic model incentivizes secure operation and discourages attacks.
-
Validator Diversity: For systems with validators, encourage validator diversity to prevent centralization.
-
Exit Mechanisms: Implement reliable user exit mechanisms that function even in degraded operation modes.
-
Regular Security Reviews: Schedule regular security reviews of your rollup implementation.
Conclusion and Next Steps
Deploying a rollup to mainnet is a significant undertaking that requires careful planning, thorough testing, and ongoing maintenance. In this tutorial, we've covered the essential steps and considerations for deploying a rollup solution, from initial setup to mainnet deployment and beyond.
While we've presented a simplified implementation for educational purposes, production rollups typically leverage established frameworks like Optimism, Arbitrum, or zkSync, which provide battle-tested components and infrastructure.
As you continue your rollup development journey, consider these next steps:
-
Explore Established Frameworks: Dive deeper into established rollup frameworks to leverage their optimized implementations and active communities.
-
Community Involvement: Join rollup developer communities to share knowledge and stay updated on best practices.
-
Contribution to Ecosystem: Consider contributing to the rollup ecosystem by developing tools, libraries, or documentation.
-
Advanced Optimizations: Explore advanced optimizations for your specific use case, such as calldata compression techniques or specialized proof systems.
-
Cross-Rollup Interoperability: Investigate interoperability solutions between different rollups and Layer 1.
Rollup technology continues to evolve rapidly, with ongoing research and development aimed at improving scalability, reducing costs, and enhancing user experience. By staying engaged with the ecosystem and continuing to learn, you'll be well-positioned to leverage these advancements in your projects.
Ready to deepen your understanding of rollups and other blockchain technologies? Check out our comprehensive learning tracks to become a certified developer across multiple ecosystems. Join our vibrant community of Web3 developers and participate in hackathons to put your new skills to the test. Visit HackQuest.io to start your Web3 development journey today!