Commit 55044e96 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into aj/fpp-logs

parents 2f74d184 67b2d61d
...@@ -1348,6 +1348,20 @@ workflows: ...@@ -1348,6 +1348,20 @@ workflows:
context: context:
- oplabs-gcr - oplabs-gcr
platforms: "linux/amd64,linux/arm64" platforms: "linux/amd64,linux/arm64"
- docker-build:
name: op-program-docker-build
docker_file: op-program/Dockerfile
docker_name: op-program
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
docker_context: .
- docker-publish:
name: op-program-docker-publish
docker_file: op-program/Dockerfile
docker_name: op-program
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
context:
- oplabs-gcr
platforms: "linux/amd64,linux/arm64"
- docker-build: - docker-build:
name: op-proposer-docker-build name: op-proposer-docker-build
docker_file: op-proposer/Dockerfile docker_file: op-proposer/Dockerfile
......
...@@ -187,7 +187,8 @@ module.exports = { ...@@ -187,7 +187,8 @@ module.exports = {
children: [ children: [
'/docs/security/faq.md', '/docs/security/faq.md',
'/docs/security/policy.md', '/docs/security/policy.md',
'/docs/security/pause.md' '/docs/security/pause.md',
'/docs/security/forced-withdrawal.md',
] ]
}, },
], // end of sidebar ], // end of sidebar
......
---
title: Forced withdrawal from an OP Stack blockchain
lang: en-US
---
## What is this?
Any assets you own on an OP Stack blockchain are backed by equivalent assets on the underlying L1, locked in a bridge.
In this article you learn how to withdraw these assets directly from L1.
Note that the steps here do require access to an L2 endpoint.
However, that L2 endpoint can be a read-only replica.
## Setup
The code to go along with this article is available at [our tutorials repository](https://github.com/ethereum-optimism/optimism-tutorial/tree/main/op-stack/forced-withdrawal).
1. Clone the repository, move to the correct directory, and install the required dependencies.
```sh
git clone https://github.com/ethereum-optimism/optimism-tutorial.git
cd optimism-tutorial/op-stack/forced-withdrawal
npm install
```
1. Copy the environment setup variables.
```sh
cp .env.example .env
```
1. Edit `.env` to set these variables:
| Variable | Meaning |
| -------------------- | ------- |
| L1URL | URL to L1 (Goerli if you followed the directions on this site)
| L2URL | URL to the L2 from which you are withdrawing
| PRIV_KEY | Private key for an account that has ETH on L2. It also needs ETH on L1 to submit transactions
| OPTIMISM_PORTAL_ADDR | Address of the `OptimismPortalProxy` on L1.
## Withdrawal
### ETH withdrawals
The easiest way to withdraw ETH is to send it to the bridge, or the cross domain messenger, on L2.
1. Enter the Hardhat console.
```sh
npx hardhat console --network l1
```
1. Specify the amount of ETH you want to transfer.
This code transfers one hundred'th of an ETH.
```js
transferAmt = BigInt(0.01 * 1e18)
```
1. Create a contract object for the [`OptimismPortal`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol) contract.
```js
optimismContracts = require("@eth-optimism/contracts-bedrock")
optimismPortalData = optimismContracts.getContractDefinition("OptimismPortal")
optimismPortal = new ethers.Contract(process.env.OPTIMISM_PORTAL_ADDR, optimismPortalData.abi, await ethers.getSigner())
```
1. Send the transaction.
```js
txn = await optimismPortal.depositTransaction(
optimismContracts.predeploys.L2StandardBridge,
transferAmt,
1e6, false, []
)
rcpt = await txn.wait()
```
1. To [prove](https://sdk.optimism.io/classes/crosschainmessenger#proveMessage-2) and [finalize](https://sdk.optimism.io/classes/crosschainmessenger#finalizeMessage-2) the message we need the hash.
Optimism's [core-utils package](https://www.npmjs.com/package/@eth-optimism/core-utils) has the necessary function.
```js
optimismCoreUtils = require("@eth-optimism/core-utils")
withdrawalData = new optimismCoreUtils.DepositTx({
from: (await ethers.getSigner()).address,
to: optimismContracts.predeploys.L2StandardBridge,
mint: 0,
value: ethers.BigNumber.from(transferAmt),
gas: 1e6,
isSystemTransaction: false,
data: "",
domain: optimismCoreUtils.SourceHashDomain.UserDeposit,
l1BlockHash: rcpt.blockHash,
logIndex: rcpt.logs[0].logIndex,
})
withdrawalHash = withdrawalData.hash()
```
1. Create the object for the L1 contracts, [as explained in the documentation](../build/sdk.md).
You will create an object similar to this one:
```js
L1Contracts = {
StateCommitmentChain: '0x0000000000000000000000000000000000000000',
CanonicalTransactionChain: '0x0000000000000000000000000000000000000000',
BondManager: '0x0000000000000000000000000000000000000000',
AddressManager: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8',
L1CrossDomainMessenger: '0x27E8cBC25C0Aa2C831a356bbCcc91f4e7c48EeeE',
L1StandardBridge: '0x154EaA56f8cB658bcD5d4b9701e1483A414A14Df',
OptimismPortal: '0x4AD19e14C1FD57986dae669BE4ee9C904431572C',
L2OutputOracle: '0x65B41B7A2550140f57b603472686D743B4b940dB'
}
```
1. Create the data structure for the standard bridge.
```js
bridges = {
Standard: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.StandardBridgeAdapter
},
ETH: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.ETHBridgeAdapter
}
}
```
1. Create [a cross domain messenger](https://sdk.optimism.io/classes/crosschainmessenger).
This step, and subsequent ETH withdrawal steps, are explained in [this tutorial](https://github.com/ethereum-optimism/optimism-tutorial/tree/main/cross-dom-bridge-eth).
```js
optimismSDK = require("@eth-optimism/sdk")
l2Provider = new ethers.providers.JsonRpcProvider(process.env.L2URL)
await l2Provider._networkPromise
crossChainMessenger = new optimismSDK.CrossChainMessenger({
l1ChainId: ethers.provider.network.chainId,
l2ChainId: l2Provider.network.chainId,
l1SignerOrProvider: await ethers.getSigner(),
l2SignerOrProvider: l2Provider,
bedrock: true,
contracts: {
l1: l1Contracts
},
bridges: bridges
})
```
1. Wait for the message status for the withdrawal to become `READY_TO_PROVE`.
By default the state root is written every four minutes, so you're likely to need to need to wait.
```js
await crossChainMessenger.waitForMessageStatus(withdrawalHash,
optimismSDK.MessageStatus.READY_TO_PROVE)
```
1. Submit the withdrawal proof.
```js
await crossChainMessenger.proveMessage(withdrawalHash)
```
1. Wait for the message status for the withdrawal to become `READY_FOR_RELAY`.
This waits the challenge period (7 days in production, but a lot less on test networks).
```js
await crossChainMessenger.waitForMessageStatus(withdrawalHash,
optimismSDK.MessageStatus.READY_FOR_RELAY)
```
1. Finalize the withdrawal.
See that your balance changes by the withdrawal amount.
```js
myAddr = (await ethers.getSigner()).address
balance0 = await ethers.provider.getBalance(myAddr)
finalTxn = await crossChainMessenger.finalizeMessage(withdrawalHash)
finalRcpt = await finalTxn.wait()
balance1 = await ethers.provider.getBalance(myAddr)
withdrawnAmt = BigInt(balance1)-BigInt(balance0)
```
::: tip transferAmt > withdrawnAmt
Your L1 balance doesn't increase by the entire `transferAmt` because of the cost of `crossChainMessenger.finalizeMessage`, which submits a transaction.
:::
\ No newline at end of file
...@@ -189,6 +189,6 @@ require ( ...@@ -189,6 +189,6 @@ require (
nhooyr.io/websocket v1.8.7 // indirect nhooyr.io/websocket v1.8.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.11.5 => github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878 replace github.com/ethereum/go-ethereum v1.11.5 => github.com/ethereum-optimism/op-geth v1.101105.1-0.20230420183214-24ae687be390
//replace github.com/ethereum/go-ethereum v1.11.5 => ../go-ethereum //replace github.com/ethereum/go-ethereum v1.11.5 => ../go-ethereum
...@@ -184,8 +184,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 ...@@ -184,8 +184,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878 h1:pk3lFrP6zay7+jT+yoFAWxvGbP1Z/5lsorimXGrQoxE= github.com/ethereum-optimism/op-geth v1.101105.1-0.20230420183214-24ae687be390 h1:8Ijv72z/XSpb3ep/hiOEdRKwStGsV8Ve9knU1Ck8Mf8=
github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878/go.mod h1:SGLXBOtu2JlKrNoUG76EatI2uJX/WZRY4nmEyvE9Q38= github.com/ethereum-optimism/op-geth v1.101105.1-0.20230420183214-24ae687be390/go.mod h1:SGLXBOtu2JlKrNoUG76EatI2uJX/WZRY4nmEyvE9Q38=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -40,6 +41,28 @@ func TestMissingGasLimit(t *testing.T) { ...@@ -40,6 +41,28 @@ func TestMissingGasLimit(t *testing.T) {
require.Nil(t, res) require.Nil(t, res)
} }
// TestTxGasSameAsBlockGasLimit tests that op-geth rejects transactions that attempt to use the full block gas limit.
// The L1 Info deposit always takes gas so the effective gas limit is lower than the full block gas limit.
func TestTxGasSameAsBlockGasLimit(t *testing.T) {
InitParallel(t)
cfg := DefaultSystemConfig(t)
sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
defer sys.Close()
ethPrivKey := sys.cfg.Secrets.Alice
tx := types.MustSignNewTx(ethPrivKey, types.LatestSignerForChainID(cfg.L2ChainIDBig()), &types.DynamicFeeTx{
ChainID: cfg.L2ChainIDBig(),
Gas: 29_999_999,
})
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
l2Seq := sys.Clients["sequencer"]
err = l2Seq.SendTransaction(ctx, tx)
require.ErrorContains(t, err, txpool.ErrGasLimit.Error())
}
// TestInvalidDepositInFCU runs an invalid deposit through a FCU/GetPayload/NewPayload/FCU set of calls. // TestInvalidDepositInFCU runs an invalid deposit through a FCU/GetPayload/NewPayload/FCU set of calls.
// This tests that deposits must always allow the block to be built even if they are invalid. // This tests that deposits must always allow the block to be built even if they are invalid.
func TestInvalidDepositInFCU(t *testing.T) { func TestInvalidDepositInFCU(t *testing.T) {
......
FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder
ARG VERSION=v0.0.0
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
# build op-program with the shared go.mod & go.sum files
COPY ./op-program /app/op-program
COPY ./op-node /app/op-node
COPY ./op-chain-ops /app/op-chain-ops
COPY ./op-service /app/op-service
COPY ./op-bindings /app/op-bindings
COPY ./go.mod /app/go.mod
COPY ./go.sum /app/go.sum
COPY ./.git /app/.git
WORKDIR /app/op-program
RUN go mod download
ARG TARGETOS TARGETARCH
RUN make op-program VERSION="$VERSION" GOOS=$TARGETOS GOARCH=$TARGETARCH
FROM alpine:3.15
COPY --from=builder /app/op-program/bin/op-program /usr/local/bin
CMD ["op-program"]
...@@ -12,7 +12,7 @@ const config: DeployConfig = { ...@@ -12,7 +12,7 @@ const config: DeployConfig = {
optimistAllowlistAllowlistAttestor: optimistAllowlistAllowlistAttestor:
'0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistAllowlistCoinbaseQuestAttestor: optimistAllowlistCoinbaseQuestAttestor:
'0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', '0x9A75024c09b48B78205dfCf9D9FC5E026CD9A416',
} }
export default config export default config
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment