You wrote a smart contract in Solidity, tested it on Hardhat, but when deploying to Optimism you suddenly get a gas bill of $2000. Or the contract doesn't verify on optimistic.etherscan because you forgot to account for predeploy addresses. Sounds familiar? One of our clients lost $1500 on L1 data fee due to unoptimized calldata — we rewrote the deployment script and the fee dropped to $300. Our team has 5+ years of experience in blockchain development and has completed over 30 successful deployments on L2. Let's break down how to properly deploy a contract to OP Mainnet, configure tools, optimize gas, and verify the code end-to-end. Deploying smart contracts to Optimism requires accounting for L2 specifics: two-component fees, system contracts, and separate API keys for scanning. We'll show you how to avoid common mistakes and save on gas.
Configuration for Deployment
Add Optimism to Hardhat or Foundry:
// hardhat.config.ts networks: { optimism: { url: process.env.OPTIMISM_RPC ?? "https://mainnet.optimism.io", accounts: [process.env.DEPLOYER_PRIVATE_KEY!], chainId: 10, }, "optimism-sepolia": { url: "https://sepolia.optimism.io", accounts: [process.env.DEPLOYER_PRIVATE_KEY!], chainId: 11155420, }, } # Foundry forge create src/MyContract.sol:MyContract \ --rpc-url https://mainnet.optimism.io \ --private-key $DEPLOYER_PRIVATE_KEY \ --etherscan-api-key $OPTIMISM_ETHERSCAN_KEY \ --verify How Gas on Optimism Works and How to Optimize It
The key feature of Optimism (and all OP Stack chains) is that the gas fee consists of two parts:
- L2 execution gas — like regular Ethereum gas, but significantly cheaper (paid in ETH).
- L1 data fee — cost of publishing the transaction calldata to Ethereum mainnet (for data availability). After EIP-4844 (blobs), the cost dropped by 90%+.
The contract GasPriceOracle (address 0x420000000000000000000000000000000000000F) provides methods l1BaseFee(), baseFee(), gasPrice() to calculate the total cost.
| Fee Type | Description | Typical Share |
|---|---|---|
| L2 execution gas | Computation on L2, like Ethereum | 10-30% |
| L1 data fee | Publication of calldata on L1 (Ethereum) | 70-90% (pre-EIP-4844) |
| After EIP-4844 | Blobs reduced L1 data fee by 90%+ | ~30-50% of total |
To reduce L1 data fee, minimize calldata size: use events instead of storage, pass short arguments (uint128 instead of uint256), batch-deploy contracts in a single transaction. Deploying via Create2 with a fixed address also reduces overhead.
Verification of Proxy Contracts
Optimism has its own Etherscan (optimistic.etherscan.io) with a separate API key:
npx hardhat verify --network optimism \ --contract contracts/MyToken.sol:MyToken \ DEPLOYED_ADDRESS \ "Constructor Arg 1" "Constructor Arg 2" For proxies (OpenZeppelin TransparentUpgradeableProxy), verify the implementation and proxy separately, then link the proxy to the implementation via the Etherscan UI.
OP Stack Specifics
Predeploys — system contracts at fixed addresses:
-
0x4200...0006— L2CrossDomainMessenger (for L1↔L2 messages) -
0x4200...0010— L2StandardBridge (bridging ERC-20) -
0x4200...000F— GasPriceOracle
The sequencer confirms transactions in ~2 seconds, but finality on L1 takes ~7 days (challenge period). For most dApps this is not critical, but consider it when working with L1 withdrawals. Block time is 2 seconds: this affects parameters if your contract uses block.timestamp for timeouts or TWAP.
Common Mistakes When Deploying to Optimism
- Forgot to set chainId = 10 (Mainnet) or 11155420 (Sepolia) — transactions fail.
- Didn't configure a separate API key for Optimism Etherscan — verification fails.
- Proxy contracts verified in wrong order — first implementation, then proxy.
- L1 data fee after EIP-4844 is still calculated incorrectly — use GasPriceOracle for current values.
Deployment via Foundry Script
// script/Deploy.s.sol pragma solidity ^0.8.20; import "forge-std/Script.sol"; import "../src/MyProtocol.sol"; contract DeployScript is Script { function run() external { uint256 deployerKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); vm.startBroadcast(deployerKey); MyProtocol protocol = new MyProtocol( 0x4200000000000000000000000000000000000006 // L2CrossDomainMessenger ); console.log("Deployed at:", address(protocol)); vm.stopBroadcast(); } } forge script script/Deploy.s.sol:DeployScript \ --rpc-url https://mainnet.optimism.io \ --broadcast \ --verify \ -vvvv The --verify flag automatically verifies the contract after deployment. Foundry saves deployment addresses in the broadcast/ directory.
Why Choose Optimism for Your dApp?
Optimism is an EVM-equivalent L2: your Solidity contracts work without changes. Fees are 10–50x lower than on Ethereum mainnet. Finality is ~2 seconds. The ecosystem supports Hardhat, Foundry, ethers.js, viem. Typical gas savings after EIP-4844 are significant.
What's Included in Our Work
- Development and configuration of deployment scripts (Hardhat/Foundry)
- Gas optimization: minimizing L1 data fee, using batch deployment
- Verification of all contracts (including proxies) on optimistic.etherscan
- CI/CD setup for automatic deployment to Sepolia and Mainnet
- Documentation on deployment and contract management
- One month of post-deployment support
Process
| Stage | Duration | Result |
|---|---|---|
| Analysis | 1-2 days | Architecture, contract list |
| Design | 2-3 days | Scripts, configurations |
| Implementation | 3-5 days | Deployment to Sepolia |
| Testing | 2-3 days | Test report |
| Deployment | 1 day | Contracts on Mainnet, verified |
| Support | 1 month | Monitoring, hotfixes if needed |
Testing on Optimism Sepolia
Before mainnet deployment, we test on Optimism Sepolia (chainId 11155420). Get test ETH from the official faucet or Alchemy/Infura. Optimism Sepolia mirrors the mainnet configuration, including system contracts.
If you need to deploy a contract to Optimism with guaranteed gas optimization and correct verification, contact us to discuss your project. Order the service and get a reliable deployment on Optimism.







