This tutorial teaches you the basics of OP Mainnet development.
OP Mainnet is EVM equivalent, meaning we run a slightly modified version of the same geth you run on mainnet.
Therefore, the differences between OP Mainnet development and Ethereum development are minor.
But a few differences do exist.
If you prefer watching tutorials rather than reading them, we have this great video covering the getting started content:
To access any Ethereum type network you need an endpoint. We recommend you get one from Alchemy, our preferred provider. See here for step by step directions on using Alchemy.
Alternatively, we have other great providers that support our networks.
For development purposes we recommend you use either a local development node or Optimism Goerli. That way you don't need to spend real money. If you need ETH on OP Goerli for testing purposes, you can use this faucet.
We have Hardhat's Greeter contract on OP Goerli, at address 0x575E9B4f2c3945d7CF07cb76628d29DF471692B8. You can verify your development stack configuration by interacting with it.
As you can see in the different development stacks below, the way you deploy contracts and interact with them on OP Mainnet or OP Goerli is almost identical to the way you do it with L1 Ethereum. The most visible difference is that you have to specify a different endpoint (of course). The list of other differences is here.
In Hardhat you use a configuration similar to this one.
Follow these steps to add OP Goerli support to an existing Hardhat project (or a newly created one).
-
Define your network configuration in
.env:# Put the mnemonic for an account on OP Goerli here MNEMONIC=test test test test test test test test test test test junk # API KEY for Alchemy ALCHEMY_API_KEY= # URL to access OP Goerli (if not using Alchemy) OPTIMISM_GOERLI_URL=
-
Add
dotenvto your project:yarn add dotenv
-
Edit
hardhat.config.js:-
Use
.envfor your blockchain configuration:require('dotenv').config()
-
Get the correct URL from the configuration:
const optimismGoerliUrl = process.env.ALCHEMY_API_KEY ? `https://opt-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}` : process.env.OPTIMISM_GOERLI_URL
-
Add a network definition in
module.exports.networks:
"optimism-goerli": { url: optimismGoerliUrl, accounts: { mnemonic: process.env.MNEMONIC } }
-
-
Run the console:
cd hardhat yarn yarn hardhat console --network optimism-goerli -
Connect to the Greeter contract:
Greeter = await ethers.getContractFactory("Greeter") greeter = await Greeter.attach("0x575E9B4f2c3945d7CF07cb76628d29DF471692B8")
-
Read information from the contract:
await greeter.greet()
-
Submit a transaction, wait for it to be processed, and see that it affected the state.
tx = await greeter.setGreeting(`Hardhat: Hello ${new Date()}`) rcpt = await tx.wait() await greeter.greet()
To deploy a contract from the Hardhat console:
Greeter = await ethers.getContractFactory("Greeter")
greeter = await Greeter.deploy("Greeter from hardhat")
console.log(`Contract address: ${greeter.address}`)
await greeter.greet()
In Truffle you use a configuration similar to this one.
Follow these steps to add OP Goerli support to an existing Truffle project.
-
Define your network configuration in
.env:# Put the mnemonic for an account on OP Goerli here MNEMONIC=test test test test test test test test test test test junk # API KEY for Alchemy ALCHEMY_API_KEY= # URL to access OP Goerli (if not using Alchemy) OPTIMISM_GOERLI_URL=
-
Add
dotenvand@truffle/hdwallet-providerto your project:yarn add dotenv @truffle/hdwallet-provider
-
Edit
truffle-config.js:-
Uncomment this line:
const HDWalletProvider = require('@truffle/hdwallet-provider')
-
Use
.envfor your network configuration:require('dotenv').config()
-
Get the correct URL:
const optimismGoerliUrl = process.env.ALCHEMY_API_KEY ? `https://opt-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}` : process.env.OPTIMISM_GOERLI_URL
-
Add a network definition in
module.exports.networks:"optimism-goerli": { provider: () => new HDWalletProvider( process.env.MNEMONIC, optimismGoerliUrl), network_id: 420 }
-
-
Compile the contract and run the console.
truffle compile truffle console --network optimism-goerli
-
Connect to the Greeter contact.
greeter = await Greeter.at("0x575E9B4f2c3945d7CF07cb76628d29DF471692B8")
-
Read information from the contact.
await greeter.greet()
-
Submit a transaction.
tx = await greeter.setGreeting(`Truffle: Hello ${new Date()}`)
-
Wait a few seconds for the transaction to be processed.s
-
See that the greeting has changed.
greeter.greet()
You deploy a new contract from the console.
greeter = await Greeter.new("Greeter from Truffle")
Wait a few seconds for the deployment to actually happen and then verify.
console.log(`Contract address: ${greeter.address}`)
await greeter.greet()
In Remix you access OP Goerli (or OP Mainnet) through your own wallet.
-
Add OP Goerli to your wallet. The easiest way to do this is to use chainid.link.
-
Log on with your wallet to OP Goerli.
-
Browse to Remix.
-
Select the Environment Injected Provider - MetaMask.
-
Accept the connection in the wallet.
-
Make sure your environment is Injected Web3 and the network ID is 420.
-
Download Greeter.sol and upload (
) it to Remix under contracts. -
Right-click contracts > Greeter.sol and select Compile.
-
Open contracts > artifacts and see that there's a
Greeter.jsonfile. This file is the compiled version, the API for the contract, etc. -
Scroll down. In the At Address field, type the contract address
0x575E9B4f2c3945d7CF07cb76628d29DF471692B8. Then, click At Address. Expand the contract to see you can interact with it.
-
Click greet and expand the transaction result in the console (bottom right).
-
Type a greeting (preferably, one that starts with the word
Remix) and then click setGreeting. Approve the transaction in your wallet. Note that if the greeting includes a comma you need to enclose it in quotes.
-
See the results on the console and then click greet again to see the greeting changed (see it under the greet button).
You deploy a new contract:
Foundry does not give us a JavaScript console, everything can be done from the shell command line.
-
Set the RPC URL and the contract address.
export ETH_RPC_URL= << Your Goerli URL goes here >> export GREETER=0x575E9B4f2c3945d7CF07cb76628d29DF471692B8
-
Call
greet(). Notice that the response is provided in hex.cast call $GREETER "greet()"
-
Call
greet()again, and this time translate to ASCIIcast call $GREETER "greet()" | cast --to-ascii
-
Put your mnemonic in a file
mnem.delmeand send a transaction.cast send --mnemonic-path mnem.delme $GREETER "setGreeting(string)" "Foundry hello" --legacy
-
Test that the greeting has changed:
cast call $GREETER "greet()" | cast --to-ascii
Use this command:
forge create --mnemonic-path ./mnem.delme Greeter \
--constructor-args "Greeter from Foundry" --legacyThis library is provided as an npm package, which is different from what forge expects. Here is how you can import it without importing the entire OP-Stack monorepo:
-
Install the JavaScript tools if you don't already have them: Node.js and yarn.
-
Install the
@eth-optimism/contractslibrary underlib.cd lib yarn add @eth-optimism/contracts -
If you are using
git, addnode_modulesto.gitignore. -
The remapping that
forgededuces is not the same as what you would have with hardhat. To ensure source code compatibility, create a file (in the application's root directory) calledremappings.txtwith this content:@eth-optimism/=lib/node_modules/@eth-optimism/
You can now run forge build with contracts that use the Optimism contract library.
If you want to develop in Python, you can use the Brownie toolstack.
-
Change to the
browniedirectory undergetting-started. -
Specify your mnemonic in
.env:# Put the mnemonic for an account on OP Goerli here MNEMONIC=test test test test test test test test test test test junk
-
Install packages.
pip3 install eth-brownie pip3 install dotenv
-
Update the
optimism-testnetwork.brownie networks modify optimism-test chainid=420 \ explorer=goerli-optimism.etherscan.io \ host= << OP Goerli URL >> -
Start the console.
brownie console --network optimism-test
Note that the default color scheme assumes a dark background. If your default background is light, you might want to create a file
brownie-config.yamlwith this content:console: show_colors: true color_style: manni
-
Read
.envimport os from dotenv import load_dotenv load_dotenv()
-
Add your accounts to the
accountslist:from eth_account import Account acct = Account.from_mnemonic(os.getenv("MNEMONIC")) accounts.add(acct.privateKey)
-
Create an object for the contract:
greeter = Greeter.at("0x575E9B4f2c3945d7CF07cb76628d29DF471692B8")
-
View the current greeting:
greeter.greet()
-
Modify the greeting and see the new one:
greeter.setGreeting("Brownie hello", {'from': accounts[0] }) greeter.greet()
Use this command:
Greeter.deploy("Hello", {'from': accounts[0]})-
Install Apeworx and create a new project.
pip3 install eth-ape ape init <type project name>
-
Install plugins. In this tutorial we use Solidity, but Vyper is also supported by Apeworx.
ape plugins install optimism solidity
-
Import your account. Type this command, followed by your mnemonic (the 12 word phrase) and a pass phrase to protect it.
ape accounts import test --use-mnemonic -
Create an Alchemy account and create an Optimistic Goerli app. See here for detailed directions.
-
Edit the configuration file,
ape-config.yaml:name: greeter default_ecosystem: optimism geth: optimism: goerli: uri: https://goerli.optimism.io # Normally the uri would be: # https://opt-goerli.g.alchemy.com/v2/<key>
-
Start the console:
ape console --network optimism:goerli
-
Connect to the Greeter contract:
greeter = project.get_contract("Greeter").at("0x575E9B4f2c3945d7CF07cb76628d29DF471692B8")
-
Read information from the contract:
greeter.greet()
-
Submit a transaction.
acct = accounts.load("test") greeter.setGreeting("Apeworx says hi ("+acct.address+")", sender=acct)
Sign the transaction and provide the passphrase if necessary. Ignore error messages if you get them.
-
Verify the greeting changed.
greeter.greet()
To deploy a contract from the Apeworx console:
project.get_contract("Greeter").deploy("Hello", sender=acct)
It is best to start development with the EVM provided by the development stack. Not only is it faster, but such EVMs often have extra features, such as the ability to log messages from Solidity or a graphical user interface.
After you are done with that development, debug your decentralized application using either a development node or the Goerli test network. This lets you debug parts that are OP Mainnet specific such as calls to bridges to transfer assets between layers.
Only when you have a version that works well on a test network should you deploy to the production network, where every transaction has a cost.
You don't have to upload your source code to block explorers, but it is a good idea. On the test network it lets you issue queries and transactions from the explorer's user interface. On the production network it lets users know exactly what your contract does, which is conducive to trust.
Just remember, if you use the Etherscan API, you need one API key for OP Mainnet and a separate one for OP Goerli.


