Using the OP Stack Client SDK
This tutorial explains how to use the OP Stack Client SDK when working with native and non-native supported chains.
- The OP Stack Client SDK (opens in a new tab) natively supports multiple OP Chains: OP, Base, etc. To see whether a specific OP Chain is supported directly, see the documentation (opens in a new tab).
- If you are using a chain that is not natively supported, for example an OP Stack chain you just created, you can continue to use the OP Stack Client SDK (opens in a new tab).
You just need to provide some contract addresses to the
CrossDomainMessenger
because they aren't preconfigured. This tutorial walks you through that process.
Before You Begin
To configure the CrossDomainMessenger
, you'll first need to locate the L1 contract addresses and specify the zero address for unused SDK contracts.
L1 contract addresses
If you followed the directions in Getting Started, the contract addresses are in .../optimism/packages/contracts-bedrock/deployments
, which you created when you deployed the L1 contracts.
Contract name when creating CrossDomainMessenger | File with address |
---|---|
AddressManager | Lib_AddressManager.json |
L1CrossDomainMessenger | Proxy__OVM_L1CrossDomainMessenger.json |
L1StandardBridge | Proxy__OVM_L1StandardBridge.json |
OptimismPortal | OptimismPortalProxy.json |
L2OutputOracle | L2OutputOracleProxy.json |
SDK contract addresses
Some contracts are required by the SDK as a sanity check, but are not actually used. For these contracts you can just specify the zero address:
StateCommitmentChain
CanonicalTransactionChain
BondManager
In JavaScript you can create the zero address using the expression "0x".padEnd(42, "0")
.
Create the CrossChainMessenger
Object
These directions assume you are inside the Hardhat console (opens in a new tab).
They further assume that your project already includes the Optimism SDK @eth-optimism/sdk
(opens in a new tab).
Import the SDK
optimismSDK = require("@eth-optimism/sdk")
Set the configuration parameters
Variable name | Value |
---|---|
l1Url | URL to an RPC provider for L1, for example https://eth-goerli.g.alchemy.com/v2/<api key> |
l2Url | URL to your OP Stack. If running on the same computer, it is http://localhost:8545 |
privKey | The private key for an account that has some ETH on the L1 |
Create the providers (opens in a new tab) and signers (opens in a new tab)
l1Provider = new ethers.providers.JsonRpcProvider(l1Url)
l2Provider = new ethers.providers.JsonRpcProvider(l2Url)
l1Signer = new ethers.Wallet(privKey).connect(l1Provider)
l2Signer = new ethers.Wallet(privKey).connect(l2Provider)
Create the L1 contracts structure
zeroAddr = "0x".padEnd(42, "0")
l1Contracts = {
StateCommitmentChain: zeroAddr,
CanonicalTransactionChain: zeroAddr,
BondManager: zeroAddr,
// These contracts have the addresses you found out earlier.
AddressManager: "0x....", // Lib_AddressManager.json
L1CrossDomainMessenger: "0x....", // Proxy__OVM_L1CrossDomainMessenger.json
L1StandardBridge: "0x....", // Proxy__OVM_L1StandardBridge.json
OptimismPortal: "0x....", // OptimismPortalProxy.json
L2OutputOracle: "0x....", // L2OutputOracleProxy.json
}
Create the data structure for the standard bridge
bridges = {
Standard: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.StandardBridgeAdapter
},
ETH: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.ETHBridgeAdapter
}
}
Create the CrossChainMessenger
(opens in a new tab) object
crossChainMessenger = new optimismSDK.CrossChainMessenger({
bedrock: true,
contracts: {
l1: l1Contracts
},
bridges: bridges,
l1ChainId: await l1Signer.getChainId(),
l2ChainId: await l2Signer.getChainId(),
l1SignerOrProvider: l1Signer,
l2SignerOrProvider: l2Signer,
})
Verify SDK Functionality
To verify the SDK's functionality, transfer some ETH from L1 to L2.
Get the current balances
balances0 = [
await l1Provider.getBalance(l1Signer.address),
await l2Provider.getBalance(l1Signer.address)
]
Transfer 1 gwei
tx = await crossChainMessenger.depositETH(1e9)
rcpt = await tx.wait()
Get the balances after the transfer
balances1 = [
await l1Provider.getBalance(l1Signer.address),
await l2Provider.getBalance(l1Signer.address)
]
See that the L1 balance changed
It probably changed by a lot more than 1 gwei because of the cost of the transaction.
(balances0[0]-balances1[0])/1e9
See that the L2 balance changed
This process might take a few minutes.
((await l2Provider.getBalance(l1Signer.address))-balances0[1])/1e9
That's it! You're all set! 🎉