Commit 1b7e41f5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Bedrock -> Develop (#2563)

* opnode: Reconcile epochs and handle reorgs (#309)

* opnode: Switch to uint64 in FindL2Heads function

* opnode: Properly initialize state loop

This properly sets the L1 head, L2 Head, and L2 Unsafe Head during
`state.Start()` in normal operation and when the current L1 head
has not reached the L1 genesis.

* opnode: Rename output functions

The old names where not good.
In addition, this standardizes the order of arguements and extends the
returns. The returns are not fully used, but will be.

* opnode: Add sanity check to L1 window in insertEpoch

This make sure that the L1 window starts at the correct block.

* opnode: Split out loop

Now each action occurs in a seperate function. The state loop is
still responsible for sequencing follow-up actions, but is not
responsible for setting state.

* opnode: Better name for batch prepare functions

This name indicates that it provides missing batches.

* opnode: Verify epochs as sequencer

Sequencers now verify epochs and will reorg if they see that verified
epochs do not match what they created.

* opnode: Mute driver test

* opnode: Set L2 Geth forkchoice on reorg

This is required to handle L1 reorgs (rather than just missing
batches).

* opnode: Bump op-geth version

Needed to fix a bug where the L2 geth node was missing the `mint` field
in deposit transactions when returning them over the JSON RPC server.

* opnode: Don't incorrectly advance safehead

There is some L2 block that is possible to fully derive from L1;
however, if it is ahead of the current state's safe head, do not
advance the current state's safehead because the chain has not
been verified up to that point.

* opnode: Don't rebuild L1 Genesis Block ID
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>

* opnode: More comments
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>

* opnode: Don't cast slice
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>

* opnode: PR comments

* opnode: Better error reason in attributesMatch

Include the details of the mismatched fields in addition to what
field it was. Will make diagnosing errors much easier.
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>

* go.mod: update to squashed l2 geth diff d5e1fc1a74bda3cbe5715d5732621460f9e00908

* opnode/rollup: use L2 block height in L1-info deposit tx block-height field (tx field, not info field) for uniqueness of deposit tx hash on L2

* feat: DepositFeed receive function

It is convenient for users to be able to deposit funds
directly to L2 by simply doing an ETH send to an address.
The prevents needing an ABI.

* Update slither db

* Add solidity test on df.receive() function

* Add warning message on DepositFeed.receive()

* specs: Add Withdrawals specification

* contracts: Add withdrawor and start of tests

* ops: Add withdrawor predeploy to docker setup

ops: Add contract code to Makefile and Dockerfile

* Add gaslimit to withdrawal encoding

* test(contracts): Withdrawal creation and proof gen

* deps: install entire goddamn monorepo using forge.

I needed the trie contracts over here, so now the whole monorepo
is a submodule of the specs repo. Happy now @norswap?

Why, you ask, did I do this rather than the alternatives available to
me?
1. Copying and pasting is gross.
2. `yarn add` would have installed a smaller dep, but it's also gross.
3. git submodules, while also gross in their own right, is at least
   consistent with how we're currently handling other dependencies, so
   I went with that.

* Add output root inclusion proof

Adds a check to ensure that withdrawal root is included in the
l2 output root.

* Add storage inclusion proof

Adds a check to ensure that the withdrawal message hash is included
  in the mapping of withdrawals in the withdrawer contract

* Improve testing against L2 node

* Add finalizationWindow var to WithdrawalVerifier

* Add WithdrawalVerified event

* Remove unused constructor argument

* Clean up Forge test of WithdrawalVerifier

* ops: Docker get l2 genesis hash on startup

Docker fixes

* contracts: ignore dirty submodules

* Don't run hardhat tests in CI

* Create Optimism Portal contract

* Move receive function to OptimismPortal

* specs: Add overview page (#286)

* Convert WithdrawalVerifier to a library

This changes the WithdrawalVerifier to be stateless, and moves all
checks into the OptimismPortal

* contracts: Rename Withdrawor to Withdrawer

* specs: define timestamp bounds of derivation process, clear up block derivation, define missing noTxPool field

* opnode/rollup: implement new timestamp bounds on block derivation batch filtering and filling

* opnode: fix sequencer L1 origin advancing, and fix e2e genesis time

* contracts: Add burn function to the Withdrawer contract

* contracts: Add natspec comments

* contracts: Remove timestamp from OutputRootProof

* contracts: undoL1ToL2Alias on withdrawals from a contract

Also uses AddressAliasHelper lib for aliasing instead of reimplementing
it.

* specs/rollup-node: improve spec based on review suggestions from @norswap

* opnode/rollup: implement review suggestions from @trianglesphere

* specs/rollup-node: document timestamp invariant between L2 and L1 origin

* opnode/rollup/driver: continue sequencing on current epoch if next L1 origin timestamp is not reached yet

* opnode/rollup: fix maxL2Time excl bound, and return adjusted maxL2time in long L1 gap case

* contracts: Add withdrawer tests

fixup! contracts: Add burn function to the Withdrawer contract

* Rename Finalization Window to Finalization Period

* contracts: Validate output root proof in portal

Moves the comparison into the Portal contract, and changes the
WithdrawalVerifier function from _verifyWithdrawerStorageRoot to
_deriveOutputRoot.

* contracts: Use solidity custom errors

* contracts: Use unchecked math when safe

* contracts: Add section dividers to contract layout

* specs: Update withdrawals spec to match impl

* contracts: Fix ETH burning in the withdrawer contract

* specs: Address review comments

* ops: Address review comments

* Update Dockerfile.opnode

* ops: Fix devnet

- Fixes the devnet config to correctly generate rollup/genesis files.
- Fixes a bug that causes the rollup node to get stuck when there's a large timestamp delta between genesis and the current time, as is the case in the devnet.

* Update opnode/rollup/driver/state.go
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>

* Update rollup.json

* Update predeploy addresses

The new predeploy addresses are incremented by 1 in order to avoid
conflicts with the system addresses.

* tests: Add tests for batch requests when the parent request fails

This test makes sure that we do not retry when the parent request fails. We don't, but it wasn't immediately clear.

I also removed a duplicate assertion lib since we're using `testify` everywhere else.

* opnode: Don't start sequencing blocks until past the L1 genesis (#325)

This has to be reverted to ensure that the sequencer starts producing
blocks at the same location as the verifier. Sometimes the previous
code would cause reorgs on the sequencer (expected), and sometimes
it would cause a chain split (unexpected and unexplained as of
writing this commit message 4/6/22).

* opnode: update to latest L2 geth

* readme: fix typos and break lines (#327)

* Update ref optimistic geth image (#328)

* ci: Improve timestamp management in devnet (#329)

This change sets timestamps to the current timestamp on fresh devnet deploys. This avoids the devnet having to fill in a bunch of blocks on startup. The timestamp is preserved between runs; `make devnet-clean` will erase the timestamp data if you want to start anew.

* contracts: Move withdrawal logic into abstract contract

* contracts: Move withdrawal hashing logic into library

* contracts: Update .gas-snapshot

* contracts: silence compiler warnings

* feat: expose debug http api (#334)

* opnode: Use l2 safe head instead of l2 head

Epochs should be inserted on top of the L2 Safe Head, not the
unsafe head. This was causing problems when a reorg would occur
and the node would start building in the wrong spot.

* opnode: Clear l1WindowBuf on insertEpoch failure

This is done to handle the case that the l1WindowBuf does not start
in the correct location (direct check is difficult with context).
The correct response to that is to clear the window and initialize
it from the correct block.

* hardhat: clean up the hardhat deposit task

* fix: oe => optimism (#338)

* opnode: Rework state test to build on safe head

This also got caught up in the unsafe/safe head confusion.

* opnode: fix setL1BlockValues selector used for deposits (#339)

Solidity ABI only looks at the function name and types, but does not
consider the name of the function parameters.

* rollup: fix address of the L1 Block Attributes contracts

* fix: typo in l1InfoPredeployAddress

* fix: gofmt and goimports

CI was asking for goimport'd when all it needed was gofmt.

* opnode: Import constants

* ops: fix formatting, add docker buildkit support

* ops: prune volumes on devnet-down

* fix: replace fraud with fault (#337)

* opnode: Fix bug in block creation (#345)

The NoTxPool flag was incorrectly set to falses in the InsertEpoch
function.

* opnode: Don't allow arbitrary mints (#346)

The ordering of `mint, value` was switched to be `value, mint`
when parsing emitted logs. This allows users to control their
mint rather than using `msg.value` which is being fed directly
into the event.

Thanks to Mofi (Inphi) for finding this.

* opnode,specs: deposit tx-hash uniqueness based on L1

* opnode/l1: verify critical receipt/log info we get from RPC

* opnode/test: reconstruct deposit hash in e2e test, fetch l2 deposit receipt

* opnode/l1: fix panic on channel send after close, sema channel can stay open

* go.mod: l2 geth update with improved deposit tx hash

* README: remove alpha branch + update description (#332)

* allow building everything from top-level makefile

* allow testing from top level makefile

* Update Makefile
Co-authored-by: default avatarJoshua Gutow <jgutow@optimism.io>

* opnode/l1: wrap errors and fmt test imports

* opnode: don't expose types.Block to derive or driver packages, prefer l2.ExecutionPayload

* opnode: add comments, fix genesis base case to get l1 origin

* init: integration tests (#344)
Co-authored-by: default avatarJaved Khan <tuxcanfly@gmail.com>

* opnode: Fix possible panic in sequencer batch creation (#342)

Previously we assumed that every deposit would be included in the L2
block. However, the L2 execution engine removes depoists that are
not valid. As such the number of deposits submitted to the execution
engine could be greater than the number of transactions returned
causing a panic.

This fixes the issue by counting the number of deposits returned
and using that to slice the payload transactions for batch submission.
(Deposits are not submitted in batches and instead read directly from L1).

This also does more sanity checks on the transactions included in the L2
block and rejects blocks with invalid deposit tx configurations. This is
done prior to marking the payload canonical with the FCU.

* opnode: Test Utilities (#319)

* opnode: Deduplicate fake chain source

The fake chain source is an easy way to create and L1 and L2 chains from
a simple string defition. There were previously two implementations and
now they are in a shared testutils package.

* opnode: Dedicated wait functions

This deduplicates wait for transactions on L1 and L2.

* opnode: New e2e test system

This splits out the initialization from the actual tests to eanble
multiple e2e tests. Some of the config is still not as dedpulicated
as I would like, but it is more centralized than it previously was.

Aims to move the setup from the tests and enable easier e2e tests.

* opnode: Silence FindSyncStartTest

* opnode: Add test for missing batches

This ensures the the sequencer follows the chain that is derived
from L1 rather than what it sequenced in the case that it misses
batch submission.

* opnode: Pull out system config to default cfg

This reduces duplication and makes tests easier to understand.

* opnode: Address PR comments

* opnode: Reduce flakiness in missing batch test

The proper way to do this is to wait for a safe block, but the API
is not yet ready for that.

* Fix sync-start bug + improve sync-start comments (#355)

Fix bug (L1 origin head with non-canonical ancestors)

This happened in the scenario where `l2ahead == true` (L1 origin of start is
ahead of know L1 head) and `start` had an ancestor whose L1 origin was known not
to be canonical.

Example: L1 head is at block X. `start` has an L1 origin with number X + 1 but its
parent L2 block has an L1 origin with number X whose blockhash is not canonical.
In this case, the algorithm still returned `start` as the unsafe L1 head, which is
incorrect.

This also touches a bunch of comments to generally improve understanbility.

* feat: deposit tx js helpers (#352)

* feat: js deposit tx

Hardhat task now lists the tx hash

* rollup: new deposit hashing

* nice api!

* fix: dockerfile

* devnet: Hardcode deposit feed bytecode (#359)

* opnode,l2os: Better lifecycle management (#358)

* opnode: Properly wait for limit client to shut down

We removed closing the semaphore channel because it would cause a
panic as we did not wait for in flight requests (and new requests)
to stop before closing the L1 source.

* l2os: Change shutdown procedure

This is now blocking, but also reuces the amount of error logs.

* opnode: Shutdown rollup nodes before clients/geth nodes

Also actually shuts down the L2 clients in the rollup node.

* opnode: Properly shut everything down

* style: clean up and comments for state.go (#356)

* opnode: Parameterize deposit contract address (#361)

The contract can be deployed to any address. It is set in the
rollup config file.

* opnode/test: Add reverted deposit with mint test

* fix(ci): use Foundry GHA (#367)

Nightly Foundry releases are kept around for 3 days. This means we cannot be pinning the download link on CI to a specific commit.

Instead, we use the Foundry Toolchain GHA which always uses the latest nightly version.

* opnode/test: check nonce increment in deposit fail

* integration-tests: End-to-end withdrawal test (#362)

Includes several fixes to make this work:

- Modifies the devnet to deploy the L1 contracts rather than include them in genesis.
- Changes the deployment of L2OutputOracle to support a custom start timestamp.
- Updates UserDeposits to properly filter out non-TransactionDeposited events.
- Updates the L2 output submitter to post the current L1 block with each output rather than the L2 checkpoint block
- Updates the opnode API to allow all vhosts and CORS domains.

* go.mod: bump l2geth version

* opnode: Update and deploy contracts (#360)

This is rather than hardcoding the contract bytecode. It slows down the e2e
tests (by having to wait 1 L1 block for contracts to be ready), but it
is more accurate to what occurs (and is now required as we initialize
actions).

This also pins the L2OutputOracle to v0.8.10 of solidity.

* specs: Update output root derivation (#357)

* specs: Update output root derivation

* specs: Separate version from payload

* opnode: libp2p setup

* p2p flags, config, and libp2p + discv5 setup

* specs: initial rollup node p2p spec

* specs: Fix definition of output root

* solidity: fail if updated gas snapshot isn't included

A new gas snapshot can be generated by running
`forge snapshot`

* p2p setup: implement review suggestions, fix toc, fix lint

* opnode/p2p: test with require instead of assert

* contracts: Fix reentrancy attack in WithdrawalsRelay (#378)

* contracts: Output oracle improvements (#370)

* contracts: Use correct uppercase for immutables

* itest: shorten submission interval for faster devnet tests

* contracts: Ensure submission interval is multiple of block time

* itest: Simplify withdrawals spec

* itest: Increase withdrawal finalization timeout

* itest: Bump withdrawal timeout to +5 minutes

* Bump timeout

* Bump again

* Add gas snap
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>

* Clean up withdrawals itest (#372)

* contracts: Use correct uppercase for immutables

* itest: shorten submission interval for faster devnet tests

* contracts: Ensure submission interval is multiple of block time

* itest: Simplify withdrawals spec

* itest: Increase withdrawal finalization timeout

* itest: Bump withdrawal timeout to +5 minutes

* itest: Extract logic into getTargetOutput

* Update 000_withdrawals.spec.ts
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>

* fix dead anchor link

* higher difficulty rather than number

* 'L1 attributes block' is not a thing + add link

* try to align sentences with lines

* ci: Add automated docker builds for opnode and l2 output submitter (#369)

* contracts: Add deleteL2Output function

* specs: Add deleteL2Output

* ci: Update Docker version in CircleCi (#381)

We ran into this on the monorepo too. Alpine 3.14 removes the `faccessat2` syscall. This caused "operation not permitted" errors while building. Upgrading Circle's Docker version fixes this. See https://wiki.alpinelinux.org/wiki/Release_Notes_for_Alpine_3.14.0#faccessat2 for more information.

* contracts: reuse code (#380)

* contracts: reuse the library to compute withdrawal hash

The `WithdrawalVerifier` has a method to compute the
withdrawal hash as part of a library. Use this library
in both the L1 and L2 contracts so that code can be reused.

* contracts: gas snapshot

* opnode: Better parsing of the TransactionDepositedEvent (#382)

This now asserts that the offset for the bytes field of the event
is the correct value rather than the previous incorrect check. This
check is not fully necessary, but good to have to validate that the
data is well formed.

* Makefile,ops: docker compose up instead of run to make devnet-down work, and fix makefile label (#385)

* opnode: Do not drop all deposits on parsing error (#383)

* Change deposit gasLimit from uint256 to uint64

This prevents the user from setting a gas limit that the rollup node
is not able to parse.

* opnode: Do not drop all deposits on parsing error

Only drop the affected deposit. This does mean loss of funds, but
this is also a case that should never happen. In addition, halting
the chain means that while funds are safe, we are exposed to a
denial of service attack if it would be possible to cause a parsing
error from the deposit feed contract.

* opnode/test: Provide flag to access geth logs (#389)

This enables a more verbose output without having to modify the test
itself. Provide the flag `gethlogs` to see logs from geth.

* opnode: Fix L1 Info Transactions

There are three issues being fixed:
1. Set the `from` field to the correct magic value (found by ToB)
2. Update the L2 EE to provide gas to deposits. Otherwise the
   deposit transactions immediatley out of gas and fail.
3. Update the manual ABI encoding to match what solidity expects.
   The previous version was similar to packed encodeding, but as
   such could not be parsed by solidity.

This also includes a regression test that the parsing of the L1 info
tx and what is recorded in the state for each block matches.

* contracts: import messengers (#393)

* chore: copy L1 and L2 Messengers from Monorepo

* test: Add Messenger test files

Monorepo test cases are copied in as comments from the monorepo ts tests.

* chore: Add OZ upgradable contracts

New remappings were also added in order to avoid excessively

long import statements.

* chore: Importe OZ and OP contracts as node_modules

Necessary because hardhat does not support remappings!?!

y u no?

* refactor: Remove replayMessage()

We no longer need this function as it was only necessary when the CTC
a maximum gas limit per epoch concept. In order to remove the function
I had to copy in the L1xDM interface rather than import from the
node_modules.

* chore: Remove unused files

The ts test file is made redundant by an itest in the itest package.
The sol test file was not being used for anything

* refactor: Remove Address Manager and Resolver

Instead the CTC and SCC are state variables

* refactor: Replace CTC with OptimismPortal

* refactor: Remove SCC

We don't need to replace it with the L2OutputOracle in the L1xDM,
because the verification is now done in the OptimismPortal itself.

* forge install: forge-std

* forge install: ds-test

* refactor: Move boilerplate test code into CommonTest

* test: Add sendMessage and pause tests for L1xDM

* test: L1CrossDomainMessenger sendMessage and pause

* refactor: replace L2ToL1MessagePasser with Withdrawer contract

Also adds a lib with constant values for new bedrock predeploys.

* chore: Make functions external, and reorder for CEI

For whatever reason a bunch of functions on the messengers were public,
when they could have been external. I fixed that, and removed the
slither annotations. Where possible (in the sendMessage functions), I
also reordered the events and calls to respect
Checks-Effects-Interactions. There was no risk previously, but this
removes any question, and quiets slither.

* refactor: Reorganize ts helpers

Move helpers/index into utils.ts, and add other files which are exported in

the new index.ts.

* test: Add mock proof generation script and helpers

* test: Add L1xDM relayMessageSucceeds tests

* test: Add proof generation scripts and helpers

* refactor: Add l2Sender check in L2xDM

* refactor: Copy in the L1 and L2 standard bridge

At this point they are simply verbatim.

* refactor: Bridges - fix import paths

* refactor: Token Bridge - replace messengers with Portal
Also remove the CrossDomainEnabled lib.

refactor: Token Bridge - replace messengers with Portal
Also remove the CrossDomainEnabled lib.

* style: Address/remove some slither disable comments

style: Address/remove some slither disable comments

* refactor: extract l2Sender check into a modifier

* refactor: Support deposits of ETH in L2 Bridge

This copies in the IL2ERC20Bridge interface so that payable can be added.

In the case that the L2 token address matches OVM_ETH, the value of the

call will be forwarded.

* interface: add IWithdrawer.sol

* contracts: add comments to L1 contracts

* contracts: use unchecked

* contracts: fix imports in common test

* test: L1CrossDomainMessenger

* test: L2CrossDomainMessenger

* contracts: fix typo

* tests: bridge tests

* contracts: remove extra message assignment

* contracts: update gas snapshot

* forge install: solmate

* contracts: remove usage of OVM_ETH

All `OVM_ETH` will be migrated to `ETH` with the upgrade
to bedrock. We do not want to allow for the creation of new
`OVM_ETH` by depositing `ETH` into the bridge and have it
create `OVM_ETH` on L2.

* contracts: add in L2StandardERC20

* contracts: add in token factory

The token factory will deploy tokens on L2
that correspond to tokens on L1. This allows for
easy deposits through the bridge.

* contracts: test rlp lib for computing contract addrs

This library lets you compute the contract address
based on the deployment account and nonce.

h/t @t11s

* contracts: test infra for bridge

* contracts: add note to self

* contracts: fix compiler warnings

* contracts: update snapshot

* contracts: add IDepositFeed

* contracts: type cast uint256 to uint64 in messenger

* test: fix merge

* contracts: modify paths to compile with hardhat

* hardhat: update config

* contracts: fix build

* forge tests: first yarn install

* contracts: lint

* contracts: update snapshot
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>

* contracts: fix abi encoding in proof verification (#395)

Use `abi.encode` instead of `abi.encodePacked` to ensure
a constant serialization. `abi.encode` will be sure to
pad the value to its size while `abi.encodePacked` will not
when operating on integers. There should not have been a bug here
because it was being called with a `bytes32`, which should
always be 32 bytes when returned from `abi.encodePacked`.

* contracts: don't unalias on L2->L1
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* opnode: attach p2p host and discovery to rollup node, update e2e test (#388)

* deps: update forge-std

* contracts: fix build

* build: fix bindings build

* fix: builds

* forge install: ds-test

* build: fix for good

* l2os: update bindings

* opnode: add seq nr to l1-info to fix driver bug

* feat: create opnode/predeploy package

This resolves an import cycle when trying to use
WithdrawalContractAddress in opnode/node. Otherwise the compiler
complains that opnode imports opnode/node which imports opnode.

* opnode: refactor RPC server / config, prototype batch serving

* feat: enforce size contraints on batch bundles

This algorithm is modeled mostly off the one used in the existing batch
submitter. The additional complexity enables us to configure a min and
max value such that max - min < max_l2_tx_size. Applied strictly,
submitting a large L2 tx would cause the batch construct to halt, since
the batch constructed without including batch `i` can be under the min,
while including batch `i` resulting in exceeding the maximum. The
additional complexity in the pruning algorithm is used to identify this
case and submit an undersized batch when it cannot be avoided. In the
general case, however, this strategy increases our expected
profitibability since the average batch size is closer to the desired
maximum.

* feat: add BundleBuilder helper class

* fix: typo in l2os config docs

* feat: add GetBatchBundle method to rollupclient.RollupClient

* feat: simplify l2os rollupclient init

* feat: reduce BSS poll interval

The current setup assumes the batches are submitted very soon after the
block is created on L2. Increasing the poll interval ensures that we
discover and publish new batches in response. Without modifying this
value, the subsequent changes will still fail even though the code is
correct.

* feat: add sequencer history db

* feat: add sequencer BSS driver to submit batches

* feat: add bss configuration

* feat: add LOG_TERMINAL flag to l2os, use non-global log instance

Also modifies the shutdown logic of l2os.Service to abort if stuck
publishing a transaction.

* feat: enable standalone bss, disable simple bss

* feat: remove simple bss

* feat: add bss to devnet

* fix: ci build bindings (#406)

* ci: build bindings in ci

* ci: install abigen

* ci: fix geth install

* bindings: regenerate

* contracts: no metadata hash in contracts

* bindings: regenerate

* opnode: Avoid busy-waiting while L2 head is behind L1

In the state select loop, we immediately request for new L2 blocks whenever
the latest L1 origin is behind L1. This can be an issue as we attempt to
re-request the latest L2 block without any delay. This is a DoS hazard
particularly when an L2 block cannot be retrieved because either the `L1Chain`
or `L2Chain` backends have errors.

Another problem with this is that other, more useful events in the state
select loop, are less likely to be scheduled by the Go runtime due to
the busy-wait.

Adding a small delay, before calling `reql2BlockCreation` of about 10ms
should be enough to prevent issues.

* opnode: Fix Incorrect error handling when creating an L2 block (#391)

* opnode: Fix Incorrect error handling when creating an L2 block

This is an issue ToB identified (issue #8). What happens is that
the ethereum.NotFound error would never be returned, but in general
if there is an error in the lookup, the state loop should keep
going.

* opnode: Properly wrap errors in l1/source.go

* opnode: Fuzz manual ABI parsing (#384)

* opnode: Add OptimismPortal deployed bytecode

This is important for fuzzing

* opnode: Add differential fuzzing test for deposit events

fixup: Proper tests

* build: Add fuzz target

* opnode: Fix ineffctual error assignment

* opnode: More fuzzing cleanup

The purpose of this is to make it more likely that the deposit
succeeds and that we fuzz what we actually want to fuzz (the
parsing).

* fix: DepositFeed.sol link

* opnode: gossip blocks topic validator and subscriber

* opnode/l2: ExecutionPayload SSZ encoding/decoding

* opnode/l2: check payload block hash consistency

* opnode/p2p,specs: update p2p block gossip validation

* opnode: refactor node initialization, change l1-head fan out, implement l2 payload driver receiver, setup block signing, update config / cli / e2e

* opnode/l2,opnode/rollup/driver: process incoming unsafe L2 blocks

* specs/rollup-node-p2p: fix toc

* opnode/p2p: compress published messages

* specs/rollup-node-p2p: clarify hardfork version start

* opnode/node: fix l1 head notify timeout bug

* opnode: fix receive p2p payload timeout bug, fix publishing buffer missing reset

* opnode: fix/improve new loggers

* opnode: fix p2p block distribution

* opnode/test: connect peers only after starting p2p application-layer protocols

* ops,opnode/p2p: fix opnode start up in devnet

* opnode/l2: use common.Big0 instead of big.NewInt(0), thx mark

* opnode: Add debug tracer to geth in e2e test (#418)

* Update reference geth entrypoint with variable parameters

* opnode: fix imports and code newlines style

* opnode,specs: gossip blocks validation seen cache fixes

* opnode/rollup/driver: no heuristics in driver, rely more on engine to handle reorgs

* opnode: tracer to watch node events during testing

* opnode,specs: clarify gossip params

* opnode/l2/ssz: fix lousy copy range

* opnode/rollup/driver/state: fix doc typo
Co-authored-by: default avatarJoshua Gutow <jgutow@optimism.io>

* Add version RPC (#432)

Meta:

- Fixes ENG-2200

* refactor: new messengers (#421)

* contracts: consolidate OptimismPortal
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: delete abstracts
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: base `CrossDomainMessenger` + L1 and L2

Implement the `L1CrossDomainMessenger` and
`L2CrossDomainMessenger` based on the base
`CrossDomainMessenger`. This makes the interfaces
the same on both sides.
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: `StandardBridge`

Also implement the `L1StandardBridge` and
`L2StandardBridge` based off of the base
`StandardBridge`
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: L2OutputOracle
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: standard bridge tests
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: cross domain hashing lib + tests
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: tests for cross domain messengers
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: L2ToL1MessagePasser
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: safe call lib
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: optimism mintable erc20
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: common test setup
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: optimism mintable token factory
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update forge-std
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: test OptimismPortal
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: L1Block attributes
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update libraries
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update foundry.toml
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: Burner
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: delete dead code
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update hh deploy scripts
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update gas snapshot
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* integration-tests: update for new messengers
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* ops: devnet up script new genesis
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* opnode: new bindings
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* l2os: new bindings
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* contracts: update gas snapshot
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>

* specs: L2ToL1MessagePasser

* specs: messengers

* specs: bridges

* specs: fix linting issues

* contracts: better contract for is optimism mintable

* contracts: fix some comments

* contracts: assert finalization window has passed

* bindings: regenerate

* contracts: address comments
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>

* opnode: p2p RPC, fix static-peers

* StateViz: Visualize rollup state changes (#419)

* StateViz: Visualize rollup state changes

* remove DEBUGMEs

* Add L1WindowBuf to viz

* move stateviz to opnode/cmd

* Add stateviz to docker-compose

* html lint

* Add SRI

* Fix linter

* Merge fixes

* Fixes from code review

* Imports

* Fix space
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>

* contracts: add backup logic for deposits

Introduces backup logic for deposits that prevents users from
accidentally making bad deposits. Deposits that don't complete
successfully will be returned back to the layer where the deposits were
made.

* contracts: add base gas to sent messages

Introduces base gas to the CrossDomainMessenger's sendMessage function.
Base gas is used to guarantee that all messages sent between messengers
will at the very least be able to store the message hash on the other
chain and therefore be replayable. Base gas scales dynamically with the
size of the message.

* contracts: use clearer message encoding

Uses the clearer abi.encodeWithSelector within CrossDomainMessenger's
sendMessage function. Doing this makes it much more apparent that a call
to the relayMessage function is being triggered.

* contracts: add L1BlockNumber predeploy

Re-introduces the L1BlockNumber predeployed contract for backwards
compatibility with the previous OVM_L1BlockNumber contract.

* opnode: p2p rpc client bindings, test update, minor p2p flag update

* opnode/p2p: set up connection notification before starting host B

* opnode/p2p: bundle p2p components into p2p node for separate testing

* opnode/p2p: minor style fix + rename

* opnode/p2p: add method to get peer info about self

* Fix SRI on stateviz (#440)

* Fix L2 Output Timestamps (#416)

There was an off by one error in the L2 Output Oracle contract in the timestamp to
block number conversion. In addition, the L2 Output Submitter (op proposer) did
not recognize that there was a mismatch between the timestamp/block number in
the header and the timestamp/block number from the contract.

This bug causes problems when trying to do withdrawals.


Fixes ENG-2128

* opnode: Withdrawal E2E test in go (#423)

This does the following:
- Adds withdrawal utilities (to opnode/withdrawals)
- Adds an end to end test in go of withdrawals
- Adds the L2 withdrawer contract
- Updates to a newer version of reference-go-ethereum


Fixes ENG-2202

* Adopt go.work, rename modules to prep for monorepo (#441)
Co-Authored-By: default avatarMatthew Slipper <me@matthewslipper.com>
Co-authored-by: default avatarprotolambda <proto@protolambda.com>

* remove git submodules

* optimistic-specs: monorepo merge, mv into protocol dir

* bedrock contracts dependencies

* monorepo merge: fix Go env

* monorepo merge: move contracts-bedrock into non-Lerna package

* monorepo merge: bring back CI

Known issues:

- There are broken links that `lychee` picks up on. These still need to be updated.
- Slither returns errors, both here and in `optimistic-specs`.
- `go-bip39` was updated to a newer version. The newer version broke a `bss-core` test, which had to be fixed.
- Forge is not compatible with Lerna. As a result, the `contracts-bedrock` package had to be moved out of the `packages` hierarchy.
- The devnet itests don't work because the Go modules aren't on the default branch. We need to decide if we merge to develop, or stay on a feature branch before fixing this.

* outline bedrock dirs, rm stale protocol repo files (#2562)

* monorepo merge: re-run yarn
Co-authored-by: default avatarJoshua Gutow <jgutow@optimism.io>
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
Co-authored-by: default avatarsmartcontracts <kelvin@optimism.io>
Co-authored-by: default avatarMurphy Law <Inphi@users.noreply.github.com>
Co-authored-by: default avatarnorswap <norswap@gmail.com>
Co-authored-by: default avatarJaved Khan <tuxcanfly@gmail.com>
Co-authored-by: default avatarMurphy Law <mlaw2501@gmail.com>
Co-authored-by: default avatarGeorgios Konstantopoulos <me@gakonst.com>
Co-authored-by: default avatarConner Fromknecht <conner@alum.mit.edu>
Co-authored-by: default avatarLuca Donno <30298476+lucadonnoh@users.noreply.github.com>
Co-authored-by: default avatarBen Wilson <bwilson@optimism.io>
Co-authored-by: default avatarBen Wilson <82120899+optimisticben@users.noreply.github.com>
parent f9c5e525
version: 2.1
orbs:
node: circleci/node@5.0.2
executors:
go-builder:
docker:
......@@ -307,10 +310,159 @@ jobs:
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push <<parameters.docker_tags>>
bedrock-markdown:
machine:
image: ubuntu-2004:202111-02
steps:
- checkout
- run:
name: markdown lint
command: |
docker run -v `pwd`:/workdir davidanson/markdownlint-cli2:0.4.0 "op-node/README.md" "./specs/**/*.md" "#**/node_modules"
- run:
name: link lint
command: |
docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback --exclude twitter.com --exclude-mail /input/README.md "/input/specs/**/*.md" "/input/meta/**/*.md" "/input/op-node/**/*.md" || exit 0
bedrock-solidity:
docker:
- image: ethereumoptimism/js-builder:0.0.4
steps:
- checkout
- run:
name: init submodules
command: |
git submodule sync --recursive
git submodule update --recursive --init
- run:
name: install
command: yarn install
working_directory: contracts-bedrock
- run:
name: lint
command: |
# remove prettierrc in root of repo since it doesn't work with non-Lerna packages
mv ../.eslintrc.js ../.eslintrc.bak
yarn lint:check
mv ../.eslintrc.bak ../.eslintrc.js
working_directory: contracts-bedrock
- run:
name: slither
command: yarn slither || exit 0
working_directory: contracts-bedrock
- run:
name: build forge
command: yarn build:forge
working_directory: contracts-bedrock
- run:
name: test forge
command: yarn test:forge
working_directory: contracts-bedrock
- run:
name: gas snapshot
command: forge snapshot
working_directory: contracts-bedrock
bedrock-go-tests:
docker:
- image: ethereumoptimism/go-builder:latest
steps:
- checkout
- run:
name: lint op-node
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-node
- run:
name: lint op-proposer
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-proposer
- run:
name: lint op-batcher
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-batcher
- run:
name: lint op-e2e
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-e2e
- run:
name: prep results dir
command: mkdir -p /test-results
- run:
name: test op-node
command: |
gotestsum --junitfile /test-results/op-node.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-node
- run:
name: test op-proposer
command: |
gotestsum --junitfile /test-results/op-proposer.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-proposer
- run:
name: test op-batcher
command: |
gotestsum --junitfile /test-results/op-batcher.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-batcher
- run:
name: test op-e2e
command: |
gotestsum --junitfile /test-results/op-e2e.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-e2e
- store_test_results:
path: /test-results
fuzz-op-node:
docker:
- image: ethereumoptimism/go-builder:latest
steps:
- checkout
- run:
name: Fuzz
command: make fuzz
working_directory: op-node
bedrock-integration-tests:
machine:
image: ubuntu-2004:202111-02
docker_layer_caching: true
steps:
- checkout
- run:
name: init submodules
command: |
git submodule sync --recursive
git submodule update --recursive --init
- node/install:
install-yarn: true
node-version: '16.13'
- run:
name: install Foundry
command: |
curl -L https://foundry.paradigm.xyz | bash
source /home/circleci/.bashrc
foundryup
- run:
name: build typescript
command: make build-ts
- run:
name: start devnet
command: make devnet-up
- run:
name: run itests
command: make test-integration
workflows:
main:
jobs:
- yarn-monorepo
- bedrock-go-tests
- bedrock-solidity
- bedrock-markdown
- fuzz-op-node
- go-lint-test-build:
name: batch-submitter-tests
binary_name: batch-submitter
......
......@@ -6,6 +6,8 @@ temp
coverage.json
*.tsbuildinfo
yarn-error.log
dist
artifacts
cache
......@@ -19,6 +21,9 @@ packages/contracts/hardhat*
packages/data-transport-layer/db
packages/integration-tests-bedrock/cache
packages/integration-tests-bedrock/artifacts
# vim
*.sw*
......@@ -26,3 +31,10 @@ packages/data-transport-layer/db
.env*
!.env.example
*.log
.devnet
# Ignore local fuzzing results
**/testdata/fuzz/
coverage.out
[submodule "tests"]
path = l2geth/tests/testdata
url = https://github.com/ethereum/tests
[submodule "packages/contracts-bedrock/lib/forge-std"]
path = contracts-bedrock/lib/forge-std
url = https://github.com/foundry-rs/forge-std.git
[submodule "packages/contracts-bedrock/lib/solmate"]
path = contracts-bedrock/lib/solmate
url = https://github.com/rari-capital/solmate.git
[submodule "packages/contracts-bedrock/lib/ds-test"]
path = contracts-bedrock/lib/ds-test
url = https://github.com/dapphub/ds-test.git
{
"line_length": {
"line_length": 120,
"strict": false,
"stern": true,
"code_blocks": false,
"tables": false,
},
"no-blanks-blockquote": false,
"single-title": false,
"no-emphasis-as-heading": false,
}
COMPOSEFLAGS=-d
ITESTS_L2_HOST=http://localhost:9545
build: build-go contracts integration-tests
.PHONY: build
build-go: submodules op-node op-proposer op-batcher
.PHONY: build-go
build-ts: submodules contracts integration-tests
.PHONY: build-ts
submodules:
# CI will checkout submodules on its own (and fails on these commands)
if [ -z "$$GITHUB_ENV" ]; then \
git submodule init; \
git submodule update; \
fi
.PHONY: submodules
op-node:
make -C ./op-node op-node
.PHONY: op-node
op-batcher:
make -C ./op-batcher op-batcher
.PHONY: op-batcher
op-proposer:
make -C ./op-proposer op-proposer
.PHONY: op-proposer
contracts:
cd ./contracts-bedrock && yarn install && yarn build
.PHONY: contracts
integration-tests:
cd ./packages/integration-tests-bedrock && yarn install && yarn build:contracts
.PHONY: integration-tests
clean:
rm -rf ./bin
.PHONY: clean
devnet-up:
@bash ./ops-bedrock/devnet-up.sh
.PHONY: devnet-up
devnet-down:
@(cd ./ops-bedrock && GENESIS_TIMESTAMP=$(shell date +%s) docker-compose stop)
.PHONY: devnet-down
devnet-clean:
rm -rf ./contracts-bedrock/deployments/devnetL1
rm -rf ./.devnet
cd ./ops-bedrock && docker-compose down
docker volume rm ops_l1_data
docker volume rm ops_l2_data
.PHONY: devnet-clean
test-unit:
make -C ./op-node test
make -C ./op-proposer test
make -C ./op-batcher test
make -C ./op-e2e test
cd ./contracts-bedrock && yarn test
.PHONY: test-unit
test-integration:
bash ./ops-bedrock/test-integration.sh \
./contracts-bedrock/deployments/devnetL1
.PHONY: test-integration
devnet-genesis:
bash ./ops-bedrock/devnet-genesis.sh
.PHONY: devnet-genesis
......@@ -31,21 +31,38 @@ Then check out our list of [good first issues](https://github.com/ethereum-optim
<pre>
root
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting faulty L2 output proposals
│ ├── <a href="./packages/fault-detector">fault-detector</a>:
│ ├── <a href="./packages/integration-tests-bedrock">integration-tests-bedrock</a> (BEDROCK upgrade): Bedrock integration tests.
│ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development
│ └── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
├── <a href="./go">go</a>
│ ├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ ├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities
│ ├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
│ └── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
│ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
~~ Production ~~
├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities
├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./infra/op-replica">infra/op-replica</a>: Deployment examples and resources for running an Optimism replica
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network
└── <a href="./ops">ops</a>: Tools for running Optimism nodes and networks
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a> (deprecated for BEDROCK upgrade)
├── <a href="./l2geth-exporter">l2geth-exporter</a>: A prometheus exporter to collect/serve metrics from an L2 geth node
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
├── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents
├── <a href="./teleportr">teleportr</a>: Bridge for teleporting ETH between L1 and L2 at low cost
~~ BEDROCK upgrade - Not production-ready yet, part of next major upgrade ~~
├── <a href="./contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts. To be merged with ./packages/contracts.
├── <a href="./op-batcher">op-batcher</a>: L2-Batch Submitter, submits bundles of batches to L1
├── <a href="./op-e2e">op-e2e</a>: End-to-End testing of all bedrock components in Go
├── <a href="./op-node">op-node</a>: rollup consensus-layer client.
├── <a href="./op-proposer">op-proposer</a>: L2-Output Submitter, submits proposals to L1
├── <a href="./ops-bedrock">ops-bedrock</a>: Bedrock devnet work
└── <a href="./specs">specs</a>: Specs of the rollup starting at the Bedrock upgrade
</pre>
## Branching Model and Releases
......
module github.com/ethereum-optimism/optimism/batch-submitter
go 1.16
go 1.18
replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ../bss-core
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ../l2geth
require (
github.com/ethereum-optimism/optimism/bss-core v0.0.0
github.com/ethereum-optimism/optimism/l2geth v0.0.0
github.com/ethereum/go-ethereum v1.10.16
github.com/getsentry/sentry-go v0.11.0
github.com/getsentry/sentry-go v0.12.0
github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
)
require (
github.com/VictoriaMetrics/fastcache v1.9.0 // indirect
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/base58 v1.0.3 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0 // indirect
github.com/elastic/gosigar v0.12.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/tsdb v0.10.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,6 +6,8 @@ import (
"strings"
"testing"
"github.com/tyler-smith/go-bip39"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
......@@ -100,7 +102,7 @@ func TestDerivePrivateKey(t *testing.T) {
name: "invalid mnemonic",
mnemonic: invalidMnemonic,
hdPath: validHDPath,
expErr: errors.New("Checksum incorrect"),
expErr: bip39.ErrInvalidMnemonic, // the bip39 lib spells mnemonic wrong...
},
{
name: "valid mnemonic invalid hdpath",
......
module github.com/ethereum-optimism/optimism/bss-core
go 1.16
go 1.18
require (
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0
github.com/ethereum/go-ethereum v1.10.16
github.com/getsentry/sentry-go v0.11.0
github.com/getsentry/sentry-go v0.12.0
github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
github.com/tyler-smith/go-bip39 v1.1.0
)
require (
github.com/VictoriaMetrics/fastcache v1.9.0 // indirect
github.com/allegro/bigcache v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/base58 v1.0.3 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/fjl/memsize v0.0.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/go-bexpr v0.1.11 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/huin/goupnp v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/gomega v1.16.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/tsdb v0.10.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/status-im/keycard-go v0.0.0-20211109104530-b0e0482ba91d // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
This diff is collapsed.
# Deps and test files
node_modules
lib
# build output
artifacts
forge-artifacts
cache
typechain
coverage*
module.exports = {
env: {
browser: false,
es2021: true,
mocha: true,
node: true,
},
plugins: ['@typescript-eslint'],
extends: [
'standard',
'plugin:prettier/recommended',
'plugin:node/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
},
rules: {
'node/no-unpublished-import': 'off',
'node/no-unsupported-features/es-syntax': [
'error',
{ ignores: ['modules'] },
],
},
}
CrossDomainHashing_Test:test_l2TransactionHash() (gas: 78639)
L1BlockTest:test_basefee() (gas: 7575)
L1BlockTest:test_hash() (gas: 7552)
L1BlockTest:test_number() (gas: 7651)
L1BlockTest:test_sequenceNumber() (gas: 7585)
L1BlockTest:test_timestamp() (gas: 7661)
L1BlockNumberTest:test_fallback() (gas: 10710)
L1BlockNumberTest:test_getL1BlockNumber() (gas: 10589)
L1BlockNumberTest:test_receive() (gas: 17440)
L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 10909)
L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 8366)
L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 31882)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 61129)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 44815)
L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 41631)
L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 78105)
L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 66345)
L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 10588)
L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 58425)
L1StandardBridge_Test:test_depositERC20() (gas: 371479)
L1StandardBridge_Test:test_depositERC20To() (gas: 373256)
L1StandardBridge_Test:test_depositETH() (gas: 105126)
L1StandardBridge_Test:test_depositETHTo() (gas: 111945)
L1StandardBridge_Test:test_donateETH() (gas: 17523)
L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438817)
L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 47983)
L1StandardBridge_Test:test_initialize() (gas: 14863)
L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085)
L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 30637)
L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 23565)
L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 22897)
L1StandardBridge_Test:test_receive() (gas: 99476)
L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10843)
L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8410)
L2CrossDomainMessenger_Test:test_L2MessengerPause() (gas: 31837)
L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageSucceeds() (gas: 57429)
L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageToSystemContract() (gas: 24567)
L2CrossDomainMessenger_Test:test_L2MessengerRelayShouldRevertIfPaused() (gas: 41599)
L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119681)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133118)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10588)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54795)
L2OutputOracleTest:testCannot_appendCurrentTimestamp() (gas: 18627)
L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 16734)
L2OutputOracleTest:testCannot_appendFutureTimestamp() (gas: 18708)
L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 16458)
L2OutputOracleTest:testCannot_appendUnexpectedTimestamp() (gas: 18893)
L2OutputOracleTest:testCannot_computePreHistoricalL2BlockNumber() (gas: 11093)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18793)
L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 77307)
L2OutputOracleTest:test_appendingAnotherOutput() (gas: 68605)
L2OutputOracleTest:test_computeL2BlockNumber() (gas: 14755)
L2OutputOracleTest:test_constructor() (gas: 33752)
L2OutputOracleTest:test_deleteL2Output() (gas: 64320)
L2OutputOracleTest:test_getL2Output() (gas: 74601)
L2OutputOracleTest:test_latestBlockTimestamp() (gas: 68377)
L2OutputOracleTest:test_nextTimestamp() (gas: 9236)
L2StandardBridge_Test:test_finalizeDeposit() (gas: 93169)
L2StandardBridge_Test:test_initialize() (gas: 14812)
L2StandardBridge_Test:test_receive() (gas: 136437)
L2StandardBridge_Test:test_withdraw() (gas: 352626)
L2StandardBridge_Test:test_withdrawTo() (gas: 353495)
L2ToL1MessagePasserTest:test_burn() (gas: 112001)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromContract() (gas: 67935)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA() (gas: 74851)
OptimismMintableTokenFactory_Test:test_bridge() (gas: 9850)
OptimismMintableTokenFactory_Test:test_burn() (gas: 52791)
OptimismMintableTokenFactory_Test:test_burnRevertsFromNotBridge() (gas: 13211)
OptimismMintableTokenFactory_Test:test_l1Token() (gas: 9779)
OptimismMintableTokenFactory_Test:test_l2Bridge() (gas: 9768)
OptimismMintableTokenFactory_Test:test_mint() (gas: 65732)
OptimismMintableTokenFactory_Test:test_mintRevertsFromNotBridge() (gas: 13213)
OptimismMintableTokenFactory_Test:test_remoteToken() (gas: 9762)
OptimismMintableTokenFactory_Test:test_bridge() (gas: 9772)
OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1106538)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2193987)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374)
OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11302)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9399)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 24797)
OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 19657)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 24478)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 24824)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 24497)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 24841)
OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 31519)
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 22949)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 31188)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 31804)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 26565)
artifacts
forge-artifacts
cache
typechain
coverage.out
.deps
deployments
\ No newline at end of file
hardhat.config.ts
scripts
test
# Deps and test files
node_modules
lib
contracts/test/*.t.sol
# build output
artifacts
forge-artifacts
cache
typechain
coverage*
# Other autogenerated files
gasReporterOutput.json
slither.db.json
module.exports = {
$schema: 'http://json.schemastore.org/prettierrc',
trailingComma: 'es5',
tabWidth: 2,
semi: false,
singleQuote: true,
arrowParens: 'always',
overrides: [
{
files: '*.sol',
options: {
// These options are native to Prettier.
printWidth: 100,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: true,
// These options are specific to the Solidity Plugin
explicitTypes: 'always',
compiler: '0.8.10',
},
},
],
}
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"compiler-version": "off",
"code-complexity": ["warn", 5],
"max-line-length": ["error", 100],
"func-param-name-mixedcase": "error",
"modifier-name-mixedcase": "error",
"ordering": "warn",
"avoid-low-level-calls": "off",
"event-name-camelcase": "off",
"reason-string": "off",
"avoid-tx-origin": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }]
}
}
# Deps and test files
node_modules
lib
contracts/test/*.t.sol
MIT License
Copyright (c) 2022 Optimism
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# Optimism: Bedrock Edition - Contracts
## Install
The repo currently uses a mix of typescript tests (run with HardHat) and solidity tests (run with Forge). The project
uses the default hardhat directory structure, and all build/test steps should be run using the yarn scripts to ensure
the correct options are set.
Install node modules with yarn (v1), and Node.js (14+).
```shell
yarn
```
See installation instructions for forge [here](https://github.com/gakonst/foundry).
## Build
```shell
yarn build
```
## Running Tests
First get the dependencies:
`git submodule init` and `git submodule update`
Then the full test suite can be executed via `yarn`:
```shell
yarn test
```
To run only typescript tests:
```shell
yarn test:hh
```
To run only solidity tests:
```shell
yarn test:forge
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { OptimismPortal } from "./OptimismPortal.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
/**
* @title L1CrossDomainMessenger
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages
* from L2 onto L1.
* This contract should be deployed behind an upgradable proxy
*/
contract L1CrossDomainMessenger is CrossDomainMessenger {
/*************
* Variables *
*************/
/**
* @notice Address of the OptimismPortal.
*/
OptimismPortal public portal;
/********************
* Public Functions *
********************/
/**
* @notice Initialize the L1CrossDomainMessenger
* @param _portal The OptimismPortal
*/
function initialize(OptimismPortal _portal) external {
portal = _portal;
address[] memory blockedSystemAddresses = new address[](1);
blockedSystemAddresses[0] = address(this);
_initialize(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, blockedSystemAddresses);
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Ensure that the L1CrossDomainMessenger can only be called
* by the OptimismPortal and the L2 sender is the L2CrossDomainMessenger.
*/
function _isSystemMessageSender() internal view override returns (bool) {
return msg.sender == address(portal) && portal.l2Sender() == otherMessenger;
}
/**
* @notice Sending a message in the L1CrossDomainMessenger involves
* depositing through the OptimismPortal.
*/
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal override {
portal.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
/**
* @title L1StandardBridge
* @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard
* tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits
* and listening to it for newly finalized withdrawals.
*/
contract L1StandardBridge is StandardBridge {
/**********
* Events *
**********/
event ETHDepositInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHWithdrawalFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
/********************
* Public Functions *
********************/
/**
* @dev initialize the L1StandardBridge with the address of the
* messenger in the same domain
*/
function initialize(address payable _messenger) public {
_initialize(_messenger, payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE));
}
/**
* @dev Get the address of the corresponding L2 bridge contract.
* This is a legacy getter, provided for backwards compatibility.
* @return Address of the corresponding L2 bridge contract.
*/
function l2TokenBridge() external returns (address) {
return address(otherBridge);
}
/**
* @dev Deposit an amount of the ETH to the caller's balance on L2.
* @param _minGasLimit limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositETH(uint32 _minGasLimit, bytes calldata _data) external payable onlyEOA {
_initiateETHDeposit(msg.sender, msg.sender, _minGasLimit, _data);
}
/**
* @dev Deposit an amount of ETH to a recipient's balance on L2.
* @param _to L2 address to credit the withdrawal to.
* @param _minGasLimit Gas limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositETHTo(
address _to,
uint32 _minGasLimit,
bytes calldata _data
) external payable {
_initiateETHDeposit(msg.sender, _to, _minGasLimit, _data);
}
/**
* @dev deposit an amount of the ERC20 to the caller's balance on L2.
* @param _l1Token Address of the L1 ERC20 we are depositing
* @param _l2Token Address of the L1 respective L2 ERC20
* @param _amount Amount of the ERC20 to deposit
* @param _minGasLimit limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositERC20(
address _l1Token,
address _l2Token,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external virtual onlyEOA {
_initiateERC20Deposit(
_l1Token,
_l2Token,
msg.sender,
msg.sender,
_amount,
_minGasLimit,
_data
);
}
/**
* @dev deposit an amount of ERC20 to a recipient's balance on L2.
* @param _l1Token Address of the L1 ERC20 we are depositing
* @param _l2Token Address of the L1 respective L2 ERC20
* @param _to L2 address to credit the withdrawal to.
* @param _amount Amount of the ERC20 to deposit.
* @param _minGasLimit Gas limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositERC20To(
address _l1Token,
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external virtual {
_initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _data);
}
function finalizeETHWithdrawal(
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable onlyOtherBridge {
emit ETHWithdrawalFinalized(_from, _to, _amount, _data);
finalizeBridgeETH(_from, _to, _amount, _data);
}
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
*
* @param _l1Token Address of L1 token to finalizeWithdrawal for.
* @param _l2Token Address of L2 token where withdrawal was initiated.
* @param _from L2 address initiating the transfer.
* @param _to L1 address to credit the withdrawal to.
* @param _amount Amount of the ERC20 to deposit.
* @param _data Data provided by the sender on L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external onlyOtherBridge {
emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/**********************
* Internal Functions *
**********************/
function _initiateETHDeposit(
address _from,
address _to,
uint32 _minGasLimit,
bytes memory _data
) internal {
emit ETHDepositInitiated(_from, _to, msg.value, _data);
_initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _data);
}
function _initiateERC20Deposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) internal {
emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data);
_initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _data);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title L2OutputOracle
* @notice The L2 state is committed to in this contract
* The payable keyword is used on appendL2Output to save gas on the msg.value check.
* This contract should be deployed behind an upgradable proxy
*/
// slither-disable-next-line locked-ether
contract L2OutputOracle is Ownable {
/**********
* Events *
**********/
/// @notice Emitted when an output is appended.
event l2OutputAppended(
bytes32 indexed _l2Output,
uint256 indexed _l1Timestamp,
uint256 indexed _l2timestamp
);
/// @notice Emitted when an output is deleted.
event l2OutputDeleted(
bytes32 indexed _l2Output,
uint256 indexed _l1Timestamp,
uint256 indexed _l2timestamp
);
/**********************
* Contract Variables *
**********************/
/// @notice The interval in seconds at which checkpoints must be submitted.
uint256 public immutable SUBMISSION_INTERVAL;
/// @notice The time between blocks on L2.
uint256 public immutable L2_BLOCK_TIME;
/// @notice The number of blocks in the chain before the first block in this contract.
uint256 public immutable HISTORICAL_TOTAL_BLOCKS;
/// @notice The timestamp of the first L2 block recorded in this contract.
uint256 public immutable STARTING_BLOCK_TIMESTAMP;
/// @notice The timestamp of the most recent L2 block recorded in this contract.
uint256 public latestBlockTimestamp;
/// @notice A mapping from L2 timestamps to the output root for the block with that timestamp.
mapping(uint256 => OutputProposal) internal l2Outputs;
/// @notice OutputProposal represents a commitment to the L2 state.
/// The timestamp is the L1 timestamp that the output root is posted.
/// This timestamp is used to verify that the finalization period
/// has passed since the output root was submitted.
struct OutputProposal {
bytes32 outputRoot;
uint256 timestamp;
}
/***************
* Constructor *
***************/
/**
* @notice Initialize the L2OutputOracle contract.
* @param _submissionInterval The desired interval in seconds at which
* checkpoints must be submitted.
* @param _l2BlockTime The desired L2 inter-block time in seconds.
* @param _genesisL2Output The initial L2 output of the L2 chain.
* @param _historicalTotalBlocks The number of blocks that preceding the
* initialization of the L2 chain.
* @param _startingBlockTimestamp The timestamp to start L2 block at.
*/
constructor(
uint256 _submissionInterval,
uint256 _l2BlockTime,
bytes32 _genesisL2Output,
uint256 _historicalTotalBlocks,
uint256 _startingBlockTimestamp,
address sequencer
) {
require(
_submissionInterval % _l2BlockTime == 0,
"Submission Interval must be a multiple of L2 Block Time"
);
SUBMISSION_INTERVAL = _submissionInterval;
L2_BLOCK_TIME = _l2BlockTime;
// solhint-disable-next-line not-rely-on-time
l2Outputs[_startingBlockTimestamp] = OutputProposal(_genesisL2Output, block.timestamp);
HISTORICAL_TOTAL_BLOCKS = _historicalTotalBlocks;
// solhint-disable-next-line not-rely-on-time
latestBlockTimestamp = _startingBlockTimestamp;
// solhint-disable-next-line not-rely-on-time
STARTING_BLOCK_TIMESTAMP = _startingBlockTimestamp;
_transferOwnership(sequencer);
}
/*********************************
* External and Public Functions *
*********************************/
/**
* @notice Accepts an L2 outputRoot and the timestamp of the corresponding L2 block. The
* timestamp must be equal to the current value returned by `nextTimestamp()` in order to be
* accepted.
* This function may only be called by the Sequencer.
* @param _l2Output The L2 output of the checkpoint block.
* @param _l2timestamp The L2 block timestamp that resulted in _l2Output.
* @param _l1Blockhash A block hash which must be included in the current chain.
* @param _l1Blocknumber The block number with the specified block hash.
*/
function appendL2Output(
bytes32 _l2Output,
uint256 _l2timestamp,
bytes32 _l1Blockhash,
uint256 _l1Blocknumber
) external payable onlyOwner {
require(_l2timestamp < block.timestamp, "Cannot append L2 output in future");
require(_l2timestamp == nextTimestamp(), "Timestamp not equal to next expected timestamp");
require(_l2Output != bytes32(0), "Cannot submit empty L2 output");
if (_l1Blockhash != bytes32(0)) {
// This check allows the sequencer to append an output based on a given L1 block,
// without fear that it will be reorged out.
// It will also revert if the blockheight provided is more than 256 blocks behind the
// chain tip (as the hash will return as zero). This does open the door to a griefing
// attack in which the sequencer's submission is censored until the block is no longer
// retrievable, if the sequencer is experiencing this attack it can simply leave out the
// blockhash value, and delay submission until it is confident that the L1 block is
// finalized.
require(
blockhash(_l1Blocknumber) == _l1Blockhash,
"Blockhash does not match the hash at the expected height."
);
}
l2Outputs[_l2timestamp] = OutputProposal(_l2Output, block.timestamp);
latestBlockTimestamp = _l2timestamp;
emit l2OutputAppended(_l2Output, block.timestamp, _l2timestamp);
}
/**
* @notice Deletes the most recent output.
* @param _proposal Represents the output proposal to delete
*/
function deleteL2Output(OutputProposal memory _proposal) external onlyOwner {
OutputProposal memory outputToDelete = l2Outputs[latestBlockTimestamp];
require(
_proposal.outputRoot == outputToDelete.outputRoot,
"Can only delete the most recent output."
);
require(_proposal.timestamp == outputToDelete.timestamp, "");
emit l2OutputDeleted(
outputToDelete.outputRoot,
outputToDelete.timestamp,
latestBlockTimestamp
);
delete l2Outputs[latestBlockTimestamp];
latestBlockTimestamp = latestBlockTimestamp - SUBMISSION_INTERVAL;
}
/**
* @notice Computes the timestamp of the next L2 block that needs to be checkpointed.
*/
function nextTimestamp() public view returns (uint256) {
return latestBlockTimestamp + SUBMISSION_INTERVAL;
}
/**
* @notice Returns the L2 output proposal given a target L2 block timestamp.
* Returns a null output proposal if none is found.
* @param _l2Timestamp The L2 block timestamp of the target block.
*/
function getL2Output(uint256 _l2Timestamp) external view returns (OutputProposal memory) {
return l2Outputs[_l2Timestamp];
}
/**
* @notice Computes the L2 block number given a target L2 block timestamp.
* @param _l2timestamp The L2 block timestamp of the target block.
*/
function computeL2BlockNumber(uint256 _l2timestamp) external view returns (uint256) {
require(
_l2timestamp >= STARTING_BLOCK_TIMESTAMP,
"Timestamp prior to startingBlockTimestamp"
);
// For the first block recorded (ie. _l2timestamp = STARTING_BLOCK_TIMESTAMP), the
// L2BlockNumber should be HISTORICAL_TOTAL_BLOCKS + 1.
unchecked {
return
HISTORICAL_TOTAL_BLOCKS +
((_l2timestamp - STARTING_BLOCK_TIMESTAMP) / L2_BLOCK_TIME);
}
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { L2OutputOracle } from "./L2OutputOracle.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol";
/**
* @title OptimismPortal
* This contract should be deployed behind an upgradable proxy.
*/
contract OptimismPortal {
/**********
* Errors *
**********/
/**
* @notice Error emitted when the output root proof is invalid.
*/
error InvalidOutputRootProof();
/**
* @notice Error emitted when the withdrawal inclusion proof is invalid.
*/
error InvalidWithdrawalInclusionProof();
/**
* @notice Error emitted when a withdrawal has already been finalized.
*/
error WithdrawalAlreadyFinalized();
/**
* @notice Error emitted on deposits which create a new contract with a non-zero target.
*/
error NonZeroCreationTarget();
/**********
* Events *
**********/
/**
* @notice Emitted when a Transaction is deposited from L1 to L2. The parameters of this
* event are read by the rollup node and used to derive deposit transactions on L2.
*/
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 mint,
uint256 value,
uint64 gasLimit,
bool isCreation,
bytes data
);
/**
* @notice Emitted when a withdrawal is finalized
*/
event WithdrawalFinalized(bytes32 indexed, bool success);
/*************
* Constants *
*************/
/**
* @notice Value used to reset the l2Sender, this is more efficient than setting it to zero.
*/
address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD;
/*************
* Variables *
*************/
/**
* @notice Minimum time that must elapse before a withdrawal can be finalized.
*/
uint256 public immutable FINALIZATION_PERIOD;
/**
* @notice Address of the L2OutputOracle.
*/
L2OutputOracle public immutable L2_ORACLE;
/**
* @notice Public variable which can be used to read the address of the L2 account which
* initated the withdrawal. Can also be used to determine whether or not execution is occuring
* downstream of a call to finalizeWithdrawalTransaction().
*/
address public l2Sender = DEFAULT_L2_SENDER;
/**
* @notice A list of withdrawal hashes which have been successfully finalized.
* Used for replay protection.
*/
mapping(bytes32 => bool) public finalizedWithdrawals;
/***************
* Constructor *
***************/
constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) {
L2_ORACLE = _l2Oracle;
FINALIZATION_PERIOD = _finalizationPeriod;
}
/********************
* Public Functions *
********************/
/**
* @notice Accepts value so that users can send ETH directly to this contract and
* have the funds be deposited to their address on L2.
* @dev This is intended as a convenience function for EOAs. Contracts should call the
* depositTransaction() function directly.
*/
receive() external payable {
depositTransaction(msg.sender, msg.value, 100000, false, bytes(""));
}
/**
* @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
* deriving deposit transactions.
* @param _to The L2 destination address.
* @param _value The ETH value to send in the deposit transaction.
* @param _gasLimit The L2 gasLimit.
* @param _isCreation Whether or not the transaction should be contract creation.
* @param _data The input data.
*/
function depositTransaction(
address _to,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) public payable {
// Differentiate between sending to address(0)
// and creating a contract
if (_isCreation && _to != address(0)) {
revert NonZeroCreationTarget();
}
address from = msg.sender;
// Transform the from-address to its alias if the caller is a contract.
if (msg.sender != tx.origin) {
from = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
}
emit TransactionDeposited(from, _to, msg.value, _value, _gasLimit, _isCreation, _data);
}
/**
* @notice Finalizes a withdrawal transaction.
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Gas to be forwarded to the target.
* @param _data Data to send to the target.
* @param _l2Timestamp L2 timestamp of the outputRoot.
* @param _outputRootProof Inclusion proof of the withdrawer contracts storage root.
* @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract.
*/
function finalizeWithdrawalTransaction(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data,
uint256 _l2Timestamp,
WithdrawalVerifier.OutputRootProof calldata _outputRootProof,
bytes calldata _withdrawalProof
) external payable {
// Prevent reentrency
require(_target != address(this), "Cannot send message to self.");
// Get the output root.
L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2Timestamp);
// Ensure that enough time has passed since the proposal was submitted
// before allowing a withdrawal. A fault proof should be submitted
// before this check is allowed to pass.
require(
block.timestamp > proposal.timestamp + FINALIZATION_PERIOD,
"Proposal is not yet finalized."
);
// Verify that the output root can be generated with the elements in the proof.
if (proposal.outputRoot != WithdrawalVerifier._deriveOutputRoot(_outputRootProof)) {
revert InvalidOutputRootProof();
}
// Verify that the hash of the withdrawal transaction's arguments are included in the
// storage hash of the withdrawer contract.
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash(
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
// Verify proof that a withdrawal on L2 was initated
if (
WithdrawalVerifier._verifyWithdrawalInclusion(
withdrawalHash,
_outputRootProof.withdrawerStorageRoot,
_withdrawalProof
) == false
) {
revert InvalidWithdrawalInclusionProof();
}
// Check that this withdrawal has not already been finalized.
if (finalizedWithdrawals[withdrawalHash] == true) {
revert WithdrawalAlreadyFinalized();
}
// Set the withdrawal as finalized
finalizedWithdrawals[withdrawalHash] = true;
// Save enough gas so that the call cannot use up all of the gas
require(gasleft() >= _gasLimit + 20000, "Insufficient gas to finalize withdrawal.");
// Set the l2Sender so that other contracts can know which account
// on L2 is making the withdrawal
l2Sender = _sender;
// Make the call and ensure that a contract cannot out of gas
// us by returning a huge amount of data
(bool success, ) = ExcessivelySafeCall.excessivelySafeCall(
_target,
_gasLimit,
_value,
0,
_data
);
// Be sure to reset the l2Sender
l2Sender = DEFAULT_L2_SENDER;
// All withdrawals are immediately finalized. Replayability can
// be achieved through contracts built on top of this contract
emit WithdrawalFinalized(withdrawalHash, success);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/**
* @title Burner
* @dev This contract is used to remove ETH from
* the L2 circulating supply as it is withdrawn.
*/
contract Burner {
constructor() payable {
selfdestruct(payable(address(this)));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/**
* @title L1Block
* @dev This is an L2 predeploy contract that holds values from the L1
* chain. It can only be updated by a special account that has no private
* key managed by the L2 system. Transactions sent to this contract can
* be thought of as "L2 system transactions".
*/
contract L1Block {
/**
* @notice Only the Depositor account may call setL1BlockValues().
*/
error OnlyDepositor();
/**
* @notice The depositor account is a special account that sends
* transactions to this contract.
*/
address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
/**
* @notice The latest L1 block number known by the L2 system
*/
uint64 public number;
/**
* @notice The latest L1 timestamp known by the L2 system
*/
uint64 public timestamp;
/**
* @notice The latest L1 basefee
*/
uint256 public basefee;
/**
* @notice The latest L1 blockhash
*/
bytes32 public hash;
/**
* @notice The number of L2 blocks in the same epoch
*/
uint64 public sequenceNumber;
/**
* @notice Sets the L1 values
* @param _number L1 blocknumber
* @param _timestamp L1 timestamp
* @param _basefee L1 basefee
* @param _hash L1 blockhash
* @param _sequenceNumber Number of L2 blocks since epoch start
*/
function setL1BlockValues(
uint64 _number,
uint64 _timestamp,
uint256 _basefee,
bytes32 _hash,
uint64 _sequenceNumber
) external {
if (msg.sender != DEPOSITOR_ACCOUNT) {
revert OnlyDepositor();
}
number = _number;
timestamp = _timestamp;
basefee = _basefee;
hash = _hash;
sequenceNumber = _sequenceNumber;
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { L1Block } from "./L1Block.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
/**
* @title L1BlockNumber
* @dev L1BlockNumber is a legacy contract that fills the roll of the OVM_L1BlockNumber contract in
* the old version of the Optimism system. Only necessary for backwards compatibility. If you want
* to access the L1 block number going forward, you should use the L1Block contract instead.
*
* ADDRESS: 0x4200000000000000000000000000000000000013
*/
contract L1BlockNumber {
receive() external payable {
uint256 l1BlockNumber = getL1BlockNumber();
assembly {
mstore(0, l1BlockNumber)
return(0, 32)
}
}
fallback() external payable {
uint256 l1BlockNumber = getL1BlockNumber();
assembly {
mstore(0, l1BlockNumber)
return(0, 32)
}
}
function getL1BlockNumber() public view returns (uint256) {
return L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES).number();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol";
contract L2CrossDomainMessenger is CrossDomainMessenger {
/********************
* Public Functions *
********************/
/**
* @notice initialize the L2CrossDomainMessenger by giving
* it the address of the L1CrossDomainMessenger on L1
*/
function initialize(address _l1CrossDomainMessenger) external {
address[] memory blockedSystemAddresses = new address[](2);
blockedSystemAddresses[0] = address(this);
blockedSystemAddresses[1] = Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER;
_initialize(_l1CrossDomainMessenger, blockedSystemAddresses);
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Only the L1CrossDomainMessenger can call the
* L2CrossDomainMessenger
*/
function _isSystemMessageSender() internal view override returns (bool) {
return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == otherMessenger;
}
/**
* @notice Sending a message from L2 to L1 involves calling the L2ToL1MessagePasser
* where it stores in a storage slot a commitment to the message being
* sent to L1. A proof is then verified against that storage slot on L1.
*/
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal override {
L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER))
.initiateWithdrawal{ value: _value }(_to, _gasLimit, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
/**
* @title L2StandardBridge
* @dev This contract is an L2 predeploy that is responsible for facilitating
* deposits of tokens from L1 to L2.
* TODO: ensure that this has 1:1 backwards compatibility
*/
contract L2StandardBridge is StandardBridge {
/**********
* Events *
**********/
event WithdrawalInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFailed(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
/********************
* Public Functions *
********************/
/**
* @notice Initialize the L2StandardBridge. This must only be callable
* once. `_initialize` ensures this.
*/
function initialize(address payable _otherBridge) public {
_initialize(payable(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER), _otherBridge);
}
/**
* @notice Withdraw tokens to self on L1
* @param _l2Token The L2 token address to withdraw
* @param _amount The amount of L2 token to withdraw
* @param _minGasLimit The min gas limit in the withdrawing call
* @param _data Additional calldata to pass along
*/
function withdraw(
address _l2Token,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external payable virtual {
_initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _data);
}
/**
* @notice Withdraw tokens to an address on L1
* @param _l2Token The L2 token address to withdraw
* @param _to The L1 account to withdraw to
* @param _amount The amount of L2 token to withdraw
* @param _minGasLimit The min gas limit in the withdrawing call
* @param _data Additional calldata to pass along
*/
function withdrawTo(
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external payable virtual {
_initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _minGasLimit, _data);
}
/**
* @notice Finalize the L1 to L2 deposit. This should only be callable by
* a deposit through the L1StandardBridge.
* @param _l1Token The L1 token address
* @param _l2Token The corresponding L2 token address
* @param _from The sender of the tokens
* @param _to The recipient of the tokens
* @param _amount The amount of tokens
* @param _data Additional calldata
*/
function finalizeDeposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable virtual {
if (_l1Token == address(0) && _l2Token == Lib_PredeployAddresses.OVM_ETH) {
finalizeBridgeETH(_from, _to, _amount, _data);
} else {
finalizeBridgeERC20(_l2Token, _l1Token, _from, _to, _amount, _data);
}
emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Handle withdrawals, taking into account the legacy form of ETH
* when it was represented as an ERC20 at the OVM_ETH contract.
* TODO: require(msg.value == _value) for OVM_ETH case?
*/
function _initiateWithdrawal(
address _l2Token,
address _from,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) internal {
address l1Token = OptimismMintableERC20(_l2Token).l1Token();
if (_l2Token == Lib_PredeployAddresses.OVM_ETH) {
_initiateBridgeETH(_from, _to, _amount, _minGasLimit, _data);
} else {
_initiateBridgeERC20(_l2Token, l1Token, _from, _to, _amount, _minGasLimit, _data);
}
emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { Burner } from "./Burner.sol";
/**
* @title L2ToL1MessagePasser
* TODO: should this be renamed to L2OptimismPortal?
*/
contract L2ToL1MessagePasser {
/**********
* Events *
**********/
/**
* @notice Emitted any time a withdrawal is initiated.
* @param nonce Unique value corresponding to each withdrawal.
* @param sender The L2 account address which initiated the withdrawal.
* @param target The L1 account address the call will be send to.
* @param value The ETH value submitted for withdrawal, to be forwarded to the target.
* @param gasLimit The minimum amount of gas that must be provided when withdrawing on L1.
* @param data The data to be forwarded to the target on L1.
*/
event WithdrawalInitiated(
uint256 indexed nonce,
address indexed sender,
address indexed target,
uint256 value,
uint256 gasLimit,
bytes data
);
/**
* @notice Emitted when the balance of this contract is burned.
*/
event WithdrawerBalanceBurnt(uint256 indexed amount);
/*************
* Variables *
*************/
/**
* @notice Includes the message hashes for all withdrawals
*/
mapping(bytes32 => bool) public sentMessages;
/**
* @notice A unique value hashed with each withdrawal.
*/
uint256 public nonce;
/********************
* Public Functions *
********************/
/**
* @notice Allow users to withdraw by sending ETH
* directly to this contract.
* TODO: maybe this should be only EOA
*/
receive() external payable {
initiateWithdrawal(msg.sender, 100000, bytes(""));
}
/**
* @notice Initiates a withdrawal to execute on L1.
* TODO: message hashes must be migrated since the legacy
* hashes are computed differently
* @param _target Address to call on L1 execution.
* @param _gasLimit GasLimit to provide on L1.
* @param _data Data to forward to L1 target.
*/
function initiateWithdrawal(
address _target,
uint256 _gasLimit,
bytes memory _data
) public payable {
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash(
nonce,
msg.sender,
_target,
msg.value,
_gasLimit,
_data
);
sentMessages[withdrawalHash] = true;
emit WithdrawalInitiated(nonce, msg.sender, _target, msg.value, _gasLimit, _data);
unchecked {
++nonce;
}
}
/**
* @notice Removes all ETH held in this contract from the state, by deploying a contract which
* immediately self destructs.
* For simplicity, this call is not incentivized as it costs very little to run.
* Inspired by https://etherscan.io/address/0xb69fba56b2e67e7dda61c8aa057886a8d1468575#code
*/
function burn() external {
uint256 balance = address(this).balance;
new Burner{ value: balance }();
emit WithdrawerBalanceBurnt(balance);
}
}
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.9;
// FROM: https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol
// TODO: Just use the original once we get our PR merged.
library ExcessivelySafeCall {
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _value Ether value to send with the call
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint256 _value,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title Lib_BedrockPredeployAddresses
* TODO: just merge this value into the monorepo
*/
library Lib_BedrockPredeployAddresses {
address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015;
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { Lib_RLPWriter } from "@eth-optimism/contracts/libraries/rlp/Lib_RLPWriter.sol";
/**
* @title CrossDomainHashing
* This library is responsible for holding cross domain utility
* functions.
* TODO(tynes): merge with Lib_CrossDomainUtils
* TODO(tynes): fill out more devdocs
*/
library CrossDomainHashing {
/**
* @notice Compute the L2 transaction hash given
* data about an L1 deposit transaction. This is useful for
* environments that do not have access to arbitrary
* RLP encoding functionality but have access to the
* standard web3 API
* TODO: rearrange args in a sane way
* @param _l1BlockHash The L1 block hash corresponding to the block
* the deposit was included in
* @param _logIndex The log index of the event that the deposit was
* created from. This can be found on the transaction receipt
* @param _from The sender of the deposit
* @param _to The L2 contract to be called by the deposit transaction
* @param _isCreate Indicates if the deposit creates a contract
* @param _mint The amount of ETH being minted by the transaction
* @param _value The amount of ETH send in the L2 call
* @param _gas The gas limit for the L2 call
*/
function L2TransactionHash(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes32) {
bytes memory raw = L2Transaction(
_l1BlockHash,
_logIndex,
_from,
_to,
_isCreate,
_mint,
_value,
_gas,
_data
);
return keccak256(raw);
}
/**
* @notice Compute the deposit transaction source hash.
* This value ensures that the L2 transaction hash is unique
* and deterministic based on L1 execution
* @param l1BlockHash The L1 blockhash corresponding to the block including
* the deposit
* @param logIndex The index of the log that created the deposit transaction
*/
function sourceHash(bytes32 l1BlockHash, uint256 logIndex) internal pure returns (bytes32) {
bytes32 depositId = keccak256(abi.encode(l1BlockHash, logIndex));
return keccak256(abi.encode(bytes32(0), depositId));
}
/**
* @notice RLP encode a deposit transaction
* This only works for user deposits, not system deposits
* TODO: better name + rearrange the input param ordering?
*/
function L2Transaction(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes memory) {
bytes32 source = sourceHash(_l1BlockHash, _logIndex);
bytes[] memory raw = new bytes[](7);
raw[0] = Lib_RLPWriter.writeBytes(bytes32ToBytes(source));
raw[1] = Lib_RLPWriter.writeAddress(_from);
if (_isCreate == true) {
require(_to == address(0));
raw[2] = Lib_RLPWriter.writeBytes("");
} else {
raw[2] = Lib_RLPWriter.writeAddress(_to);
}
raw[3] = Lib_RLPWriter.writeUint(_mint);
raw[4] = Lib_RLPWriter.writeUint(_value);
raw[5] = Lib_RLPWriter.writeUint(_gas);
raw[6] = Lib_RLPWriter.writeBytes(_data);
bytes memory encoded = Lib_RLPWriter.writeList(raw);
return abi.encodePacked(uint8(0x7e), encoded);
}
/**
* @notice Helper function to turn bytes32 into bytes
*/
function bytes32ToBytes(bytes32 input) internal pure returns (bytes memory) {
bytes memory b = new bytes(32);
assembly {
mstore(add(b, 32), input) // set the bytes data
}
return b;
}
/**
* @notice Adds the version to the nonce
*/
function addVersionToNonce(uint256 _nonce, uint16 _version)
internal
pure
returns (uint256 nonce)
{
assembly {
nonce := or(shl(240, _version), _nonce)
}
}
/**
* @notice Gets the version out of the nonce
*/
function getVersionFromNonce(uint256 _nonce) internal pure returns (uint16 version) {
assembly {
version := shr(240, _nonce)
}
}
/**
* @notice Encodes the cross domain message based on the version that
* is encoded in the nonce
*/
function getVersionedEncoding(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getEncodingV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/**
* @notice Compute the cross domain hash based on the versioned nonce
*/
function getVersionedHash(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getHashV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getHashV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/**
* @notice Compute the legacy cross domain serialization
*/
function getEncodingV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes memory) {
return Lib_CrossDomainUtils.encodeXDomainCalldata(_target, _sender, _data, _nonce);
}
/**
* @notice Compute the V1 cross domain serialization
*/
function getEncodingV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(uint256,address,address,uint256,uint256,bytes)",
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
}
/**
* @notice Compute the legacy hash of a cross domain message
*/
function getHashV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes32) {
return keccak256(getEncodingV0(_target, _sender, _data, _nonce));
}
/**
* @notice Compute the V1 hash of a cross domain message
*/
function getHashV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
return keccak256(getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Library Imports */
import {
Lib_SecureMerkleTrie
} from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
/**
* @title WithdrawalVerifier
* @notice A library with helper functions for verifying a withdrawal on L1.
*/
library WithdrawalVerifier {
/// @notice A struct containing the elements hashed together to generate the output root.
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/**
* @notice Derives the withdrawal hash according to the encoding in the L2 Withdrawer contract
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Gas to be forwarded to the target.
* @param _data Data to send to the target.
*/
function withdrawalHash(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
return keccak256(abi.encode(_nonce, _sender, _target, _value, _gasLimit, _data));
}
/**
* @notice Derives the output root corresponding to the elements provided in the proof.
* @param _outputRootProof The elements which were hashed together to generate the output root.
* @return Whether or not the output root matches the hashed output of the proof.
*/
function _deriveOutputRoot(OutputRootProof memory _outputRootProof)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
_outputRootProof.version,
_outputRootProof.stateRoot,
_outputRootProof.withdrawerStorageRoot,
_outputRootProof.latestBlockhash
)
);
}
/**
* @notice Verifies a proof that a given withdrawal hash is present in the Withdrawer contract's
* withdrawals mapping.
* @param _withdrawalHash Keccak256 hash of the withdrawal transaction data.
* @param _withdrawerStorageRoot Storage root of the withdrawer predeploy contract.
* @param _withdrawalProof Merkle trie inclusion proof for the desired node.
* @return Whether or not the inclusion proof was successful.
*/
function _verifyWithdrawalInclusion(
bytes32 _withdrawalHash,
bytes32 _withdrawerStorageRoot,
bytes memory _withdrawalProof
) internal pure returns (bool) {
bytes32 storageKey = keccak256(
abi.encode(
_withdrawalHash,
uint256(0) // The withdrawals mapping is at the first slot in the layout.
)
);
return
Lib_SecureMerkleTrie.verifyInclusionProof(
abi.encode(storageKey),
hex"01",
_withdrawalProof,
_withdrawerStorageRoot
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Testing utilities */
import { Test } from "forge-std/Test.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L2StandardBridge } from "../L2/L2StandardBridge.sol";
import { OptimismMintableTokenFactory } from "../universal/OptimismMintableTokenFactory.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { console } from "forge-std/console.sol";
contract CommonTest is Test {
address alice = address(128);
address bob = address(256);
address immutable ZERO_ADDRESS = address(0);
address immutable NON_ZERO_ADDRESS = address(1);
uint256 immutable NON_ZERO_VALUE = 100;
uint256 immutable ZERO_VALUE = 0;
uint64 immutable NON_ZERO_GASLIMIT = 50000;
bytes32 nonZeroHash = keccak256(abi.encode("NON_ZERO"));
bytes NON_ZERO_DATA = hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000";
function _setUp() public {
// Give alice and bob some ETH
vm.deal(alice, 1 << 16);
vm.deal(bob, 1 << 16);
vm.label(alice, "alice");
vm.label(bob, "bob");
}
}
contract L2OutputOracle_Initializer is CommonTest {
// Test target
L2OutputOracle oracle;
// Constructor arguments
address sequencer = 0x000000000000000000000000000000000000AbBa;
uint256 submissionInterval = 1800;
uint256 l2BlockTime = 2;
bytes32 genesisL2Output = keccak256(abi.encode(0));
uint256 historicalTotalBlocks = 100;
// Cache of the initial L2 timestamp
uint256 startingBlockTimestamp;
// By default the first block has timestamp zero, which will cause underflows in the tests
uint256 initTime = 1000;
function setUp() public virtual {
_setUp();
// Move time forward so we have a non-zero starting timestamp
vm.warp(initTime);
// Deploy the L2OutputOracle and transfer owernship to the sequencer
oracle = new L2OutputOracle(
submissionInterval,
l2BlockTime,
genesisL2Output,
historicalTotalBlocks,
initTime,
sequencer
);
startingBlockTimestamp = block.timestamp;
}
}
contract Messenger_Initializer is L2OutputOracle_Initializer {
OptimismPortal op;
L1CrossDomainMessenger L1Messenger;
L2CrossDomainMessenger L2Messenger;
L2ToL1MessagePasser messagePasser;
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit
);
event WithdrawalInitiated(
uint256 indexed nonce,
address indexed sender,
address indexed target,
uint256 value,
uint256 gasLimit,
bytes data
);
event RelayedMessage(bytes32 indexed msgHash);
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 mint,
uint256 value,
uint64 gasLimit,
bool isCreation,
bytes data
);
event WithdrawalFinalized(bytes32 indexed, bool success);
function setUp() public virtual override {
super.setUp();
// Deploy the OptimismPortal
op = new OptimismPortal(oracle, 100);
vm.label(address(op), "OptimismPortal");
L1Messenger = new L1CrossDomainMessenger();
L1Messenger.initialize(op);
L2CrossDomainMessenger l2m = new L2CrossDomainMessenger();
vm.etch(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, address(l2m).code);
L2Messenger = L2CrossDomainMessenger(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER);
L2Messenger.initialize(address(L1Messenger));
// Set the L2ToL1MessagePasser at the correct address
L2ToL1MessagePasser mp = new L2ToL1MessagePasser();
vm.etch(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER, address(mp).code);
messagePasser = L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER));
vm.label(
Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER,
"L2ToL1MessagePasser"
);
vm.label(
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
"L2CrossDomainMessenger"
);
vm.label(
AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)),
"L1CrossDomainMessenger_aliased"
);
}
}
contract Bridge_Initializer is Messenger_Initializer {
L1StandardBridge L1Bridge;
L2StandardBridge L2Bridge;
OptimismMintableTokenFactory L2TokenFactory;
OptimismMintableTokenFactory L1TokenFactory;
ERC20 L1Token;
OptimismMintableERC20 L2Token;
ERC20 NativeL2Token;
OptimismMintableERC20 RemoteL1Token;
event ETHDepositInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHWithdrawalFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event WithdrawalInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFailed(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ETHBridgeInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHBridgeFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20BridgeInitiated(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20BridgeFinalized(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
function setUp() public virtual override {
super.setUp();
vm.label(
Lib_PredeployAddresses.L2_STANDARD_BRIDGE,
"L2StandardBridge"
);
vm.label(
Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY,
"L2StandardTokenFactory"
);
// Deploy the L1 bridge and initialize it with the address of the
// L1CrossDomainMessenger
L1Bridge = new L1StandardBridge();
L1Bridge.initialize(payable(address(L1Messenger)));
vm.label(address(L1Bridge), "L1StandardBridge");
// Deploy the L2StandardBridge, move it to the correct predeploy
// address and then initialize it
L2StandardBridge l2B = new L2StandardBridge();
vm.etch(Lib_PredeployAddresses.L2_STANDARD_BRIDGE, address(l2B).code);
L2Bridge = L2StandardBridge(payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE));
L2Bridge.initialize(payable(address(L1Bridge)));
// Set up the L2 mintable token factory
OptimismMintableTokenFactory factory = new OptimismMintableTokenFactory();
vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code);
L2TokenFactory = OptimismMintableTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY);
L2TokenFactory.initialize(Lib_PredeployAddresses.L2_STANDARD_BRIDGE);
L1Token = new ERC20("Native L1 Token", "L1T");
// Deploy the L2 ERC20 now
L2Token = OptimismMintableERC20(L2TokenFactory.createStandardL2Token(
address(L1Token),
string(abi.encodePacked("L2-", L1Token.name())),
string(abi.encodePacked("L2-", L1Token.symbol()))
));
NativeL2Token = new ERC20("Native L2 Token", "L2T");
L1TokenFactory = new OptimismMintableTokenFactory();
L1TokenFactory.initialize(address(L1Bridge));
RemoteL1Token = OptimismMintableERC20(L1TokenFactory.createStandardL2Token(
address(NativeL2Token),
string(abi.encodePacked("L1-", NativeL2Token.name())),
string(abi.encodePacked("L1-", NativeL2Token.symbol()))
));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
contract CrossDomainHashing_Test is CommonTest {
function test_nonceVersioning(uint240 _nonce, uint16 _version) external {
uint256 nonce = CrossDomainHashing.addVersionToNonce(uint256(_nonce), _version);
uint16 version = CrossDomainHashing.getVersionFromNonce(nonce);
assertEq(version, _version);
}
// TODO(tynes): turn this into differential fuzzing
// it is very easy to do so with the typescript
function test_l2TransactionHash() external {
bytes32 l1BlockHash = 0xd1a498e053451fc90bd8a597051a1039010c8e55e2659b940d3070b326e4f4c5;
uint256 logIndex = 0x0;
address from = address(0xDe3829A23DF1479438622a08a116E8Eb3f620BB5);
address to = address(0xB7e390864a90b7b923C9f9310C6F98aafE43F707);
bool isCreate = false;
uint256 mint = 0xe043da617250000;
uint256 value = 0xde0b6b3a7640000;
uint256 gas = 0x2dc6c0;
bytes memory data = hex"";
bytes32 sourceHash = CrossDomainHashing.sourceHash(
l1BlockHash,
logIndex
);
assertEq(
sourceHash,
0x77fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd0735
);
bytes memory raw = CrossDomainHashing.L2Transaction(
l1BlockHash,
logIndex,
from,
to,
isCreate,
mint,
value,
gas,
data
);
assertEq(
raw,
hex"7ef862a077fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd073594de3829a23df1479438622a08a116e8eb3f620bb594b7e390864a90b7b923c9f9310c6f98aafe43f707880e043da617250000880de0b6b3a7640000832dc6c080"
);
bytes32 digest = CrossDomainHashing.L2TransactionHash(
l1BlockHash,
logIndex,
from,
to,
isCreate,
mint,
value,
gas,
data
);
assertEq(
digest,
0xf5f97d03e8be48a4b20ed70c9d8b11f1c851bf949bf602b7580985705bb09077
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { L1Block } from "../L2/L1Block.sol";
contract L1BlockTest is CommonTest {
L1Block lb;
address depositor;
bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1));
function setUp() external {
lb = new L1Block();
depositor = lb.DEPOSITOR_ACCOUNT();
vm.prank(depositor);
lb.setL1BlockValues(uint64(1), uint64(2), 3, NON_ZERO_HASH, uint64(4));
}
function test_updatesValues(uint64 n, uint64 t, uint256 b, bytes32 h, uint64 s) external {
vm.prank(depositor);
lb.setL1BlockValues(n, t, b, h, s);
assertEq(lb.number(), n);
assertEq(lb.timestamp(), t);
assertEq(lb.basefee(), b);
assertEq(lb.hash(), h);
assertEq(lb.sequenceNumber(), s);
}
function test_number() external {
assertEq(lb.number(), uint64(1));
}
function test_timestamp() external {
assertEq(lb.timestamp(), uint64(2));
}
function test_basefee() external {
assertEq(lb.basefee(), 3);
}
function test_hash() external {
assertEq(lb.hash(), NON_ZERO_HASH);
}
function test_sequenceNumber() external {
assertEq(lb.sequenceNumber(), uint64(4));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Test } from "forge-std/Test.sol";
import { L1Block } from "../L2/L1Block.sol";
import { L1BlockNumber } from "../L2/L1BlockNumber.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
contract L1BlockNumberTest is Test {
L1Block lb;
L1BlockNumber bn;
function setUp() external {
vm.etch(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES, address(new L1Block()).code);
lb = L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES);
bn = new L1BlockNumber();
vm.prank(lb.DEPOSITOR_ACCOUNT());
lb.setL1BlockValues(uint64(999), uint64(2), 3, keccak256(abi.encode(1)), uint64(4));
}
function test_getL1BlockNumber() external {
assertEq(bn.getL1BlockNumber(), 999);
}
function test_fallback() external {
(bool success, bytes memory ret) = address(bn).call(hex"");
assertEq(success, true);
assertEq(ret, abi.encode(999));
}
function test_receive() external {
(bool success, bytes memory ret) = address(bn).call{ value: 1 }(hex"");
assertEq(success, true);
assertEq(ret, abi.encode(999));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Testing utilities */
import { Messenger_Initializer } from "./CommonTest.t.sol";
import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol";
/* Libraries */
import {
AddressAliasHelper
} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {
Lib_DefaultValues
} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
/* Target contract dependencies */
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
/* Target contract */
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import {
ICrossDomainMessenger
} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";
contract L1CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing
address recipient = address(0xabbaacdc);
function setUp() public override {
super.setUp();
}
// pause: should pause the contract when called by the current owner
function test_L1MessengerPause() external {
L1Messenger.pause();
assert(L1Messenger.paused());
}
// pause: should not pause the contract when called by account other than the owner
function testCannot_L1MessengerPause() external {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(address(0xABBA));
L1Messenger.pause();
}
// the version is encoded in the nonce
function test_L1MessengerMessageVersion() external {
assertEq(
CrossDomainHashing.getVersionFromNonce(L1Messenger.messageNonce()),
L1Messenger.MESSAGE_VERSION()
);
}
// sendMessage: should be able to send a single message
// TODO: this same test needs to be done with the legacy message type
// by setting the message version to 0
function test_L1MessengerSendMessage() external {
// deposit transaction on the optimism portal should be called
vm.expectCall(
address(op),
abi.encodeWithSelector(
OptimismPortal.depositTransaction.selector,
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
0,
100 + L1Messenger.baseGas(hex"ff"),
false,
CrossDomainHashing.getVersionedEncoding(
L1Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
)
);
// TransactionDeposited event
vm.expectEmit(true, true, true, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)),
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
0,
0,
100 + L1Messenger.baseGas(hex"ff"),
false,
CrossDomainHashing.getVersionedEncoding(
L1Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
);
// SentMessage event
vm.expectEmit(true, true, true, true);
emit SentMessage(
recipient,
alice,
hex"ff",
L1Messenger.messageNonce(),
100
);
vm.prank(alice);
L1Messenger.sendMessage(recipient, hex"ff", uint32(100));
}
// sendMessage: should be able to send the same message twice
function test_L1MessengerTwiceSendMessage() external {
uint256 nonce = L1Messenger.messageNonce();
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
// the nonce increments for each message sent
assertEq(
nonce + 2,
L1Messenger.messageNonce()
);
}
function test_L1MessengerXDomainSenderReverts() external {
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
}
// xDomainMessageSender: should return the xDomainMsgSender address
// TODO: might need a test contract
// function test_xDomainSenderSetCorrectly() external {}
// relayMessage: should send a successful call to the target contract
function test_L1MessengerRelayMessageSucceeds() external {
address target = address(0xabcd);
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
vm.expectCall(target, hex"1111");
// set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.prank(address(op));
vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash(
0,
sender,
target,
0,
0,
hex"1111"
);
emit RelayedMessage(hash);
L1Messenger.relayMessage(
0, // nonce
sender,
target,
0, // value
0,
hex"1111"
);
// the message hash is in the successfulMessages mapping
assert(L1Messenger.successfulMessages(hash));
// it is not in the received messages mapping
assertEq(L1Messenger.receivedMessages(hash), false);
}
// relayMessage: should revert if attempting to relay a message sent to an L1 system contract
function test_L1MessengerRelayMessageToSystemContract() external {
// set the target to be the OptimismPortal
address target = address(op);
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
bytes memory message = hex"1111";
// set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
vm.prank(address(op));
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
}
// relayMessage: the xDomainMessageSender is reset to the original value
function test_L1MessengerxDomainMessageSenderResets() external {
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.prank(address(op));
L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
}
// relayMessage: should revert if paused
function test_L1MessengerRelayShouldRevertIfPaused() external {
vm.prank(L1Messenger.owner());
L1Messenger.pause();
vm.expectRevert("Pausable: paused");
L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
}
}
This diff is collapsed.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Messenger_Initializer } from "./CommonTest.t.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
import {
Lib_DefaultValues
} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol";
import { console } from "forge-std/console.sol";
contract L2CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing
address recipient = address(0xabbaacdc);
function setUp() public override {
super.setUp();
}
function test_L2MessengerPause() external {
L2Messenger.pause();
assert(L2Messenger.paused());
}
function testCannot_L2MessengerPause() external {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(address(0xABBA));
L2Messenger.pause();
}
function test_L2MessengerMessageVersion() external {
assertEq(
CrossDomainHashing.getVersionFromNonce(L2Messenger.messageNonce()),
L2Messenger.MESSAGE_VERSION()
);
}
function test_L2MessengerSendMessage() external {
vm.expectCall(
address(messagePasser),
abi.encodeWithSelector(
L2ToL1MessagePasser.initiateWithdrawal.selector,
address(L1Messenger),
100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding(
L2Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
)
);
// WithdrawalInitiated event
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(
messagePasser.nonce(),
address(L2Messenger),
address(L1Messenger),
0,
100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding(
L2Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
);
vm.prank(alice);
L2Messenger.sendMessage(recipient, hex"ff", uint32(100));
}
function test_L2MessengerTwiceSendMessage() external {
uint256 nonce = L2Messenger.messageNonce();
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
// the nonce increments for each message sent
assertEq(
nonce + 2,
L2Messenger.messageNonce()
);
}
function test_L2MessengerXDomainSenderReverts() external {
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
}
function test_L2MessengerRelayMessageSucceeds() external {
address target = address(0xabcd);
address sender = address(L1Messenger);
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
vm.expectCall(target, hex"1111");
vm.prank(caller);
vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash(
0,
sender,
target,
0,
0,
hex"1111"
);
emit RelayedMessage(hash);
L2Messenger.relayMessage(
0, // nonce
sender,
target,
0, // value
0,
hex"1111"
);
// the message hash is in the successfulMessages mapping
assert(L2Messenger.successfulMessages(hash));
// it is not in the received messages mapping
assertEq(L2Messenger.receivedMessages(hash), false);
}
// relayMessage: should revert if attempting to relay a message sent to an L1 system contract
function test_L2MessengerRelayMessageToSystemContract() external {
address target = address(messagePasser);
address sender = address(L1Messenger);
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
bytes memory message = hex"1111";
vm.prank(caller);
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
}
// relayMessage: the xDomainMessageSender is reset to the original value
function test_L2MessengerxDomainMessageSenderResets() external {
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
vm.prank(caller);
L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
}
// relayMessage: should revert if paused
function test_L2MessengerRelayShouldRevertIfPaused() external {
vm.prank(L2Messenger.owner());
L2Messenger.pause();
vm.expectRevert("Pausable: paused");
L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
}
}
This diff is collapsed.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { console } from "forge-std/console.sol";
contract L2StandardBridge_Test is Bridge_Initializer {
using stdStorage for StdStorage;
function setUp() public override {
super.setUp();
}
function test_initialize() external {
assertEq(
address(L2Bridge.messenger()),
address(L2Messenger)
);
assertEq(
address(L2Bridge.otherBridge()),
address(L1Bridge)
);
}
// receive
// - can accept ETH
function test_receive() external {
assertEq(address(messagePasser).balance, 0);
vm.expectEmit(true, true, true, true);
emit ETHBridgeInitiated(alice, alice, 100, hex"");
// TODO: L2Messenger should be called
// TODO: L2ToL1MessagePasser should be called
// TODO: withdrawal hash should be computed correctly
// TODO: events from each contract
vm.prank(alice, alice);
address(L2Bridge).call{ value: 100 }(hex"");
assertEq(address(messagePasser).balance, 100);
}
// withdraw
// - token is burned
// - emits WithdrawalInitiated
// - calls Withdrawer.initiateWithdrawal
function test_withdraw() external {
// Alice has 100 L2Token
deal(address(L2Token), alice, 100, true);
assertEq(L2Token.balanceOf(alice), 100);
vm.prank(alice, alice);
L2Bridge.withdraw(
address(L2Token),
100,
1000,
hex""
);
// TODO: events and calls
assertEq(L2Token.balanceOf(alice), 0);
}
// withdrawTo
// - token is burned
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
function test_withdrawTo() external {
deal(address(L2Token), alice, 100, true);
vm.prank(alice, alice);
L2Bridge.withdrawTo(
address(L2Token),
bob,
100,
1000,
hex""
);
// TODO: events and calls
assertEq(L2Token.balanceOf(alice), 0);
}
// finalizeDeposit
// - only callable by l1TokenBridge
// - supported token pair emits DepositFinalized
// - invalid deposit emits DepositFailed
// - invalid deposit calls Withdrawer.initiateWithdrawal
function test_finalizeDeposit() external {
// TODO: events and calls
vm.mockCall(
address(L2Bridge.messenger()),
abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector),
abi.encode(address(L2Bridge.otherBridge()))
);
vm.prank(address(L2Messenger));
L2Bridge.finalizeDeposit(
address(L1Token),
address(L2Token),
alice,
alice,
100,
hex""
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
contract L2ToL1MessagePasserTest is CommonTest {
L2ToL1MessagePasser messagePasser;
event WithdrawalInitiated(
uint256 indexed nonce,
address indexed sender,
address indexed target,
uint256 value,
uint256 gasLimit,
bytes data
);
event WithdrawerBalanceBurnt(uint256 indexed amount);
function setUp() virtual public {
messagePasser = new L2ToL1MessagePasser();
}
// Test: initiateWithdrawal should emit the correct log when called by a contract
function test_initiateWithdrawal_fromContract() external {
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(
messagePasser.nonce(),
address(this),
address(4),
100,
64000,
hex""
);
vm.deal(address(this), 2**64);
messagePasser.initiateWithdrawal{ value: 100 }(
address(4),
64000,
hex""
);
}
// Test: initiateWithdrawal should emit the correct log when called by an EOA
function test_initiateWithdrawal_fromEOA() external {
uint256 gasLimit = 64000;
address target = address(4);
uint256 value = 100;
bytes memory data = hex"ff";
uint256 nonce = messagePasser.nonce();
// EOA emulation
vm.prank(alice, alice);
vm.deal(alice, 2**64);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(
nonce,
alice,
target,
value,
gasLimit,
data
);
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash(
nonce,
alice,
target,
value,
gasLimit,
data
);
messagePasser.initiateWithdrawal{ value: value }(
target,
gasLimit,
data
);
// the sent messages mapping is filled
assertEq(messagePasser.sentMessages(withdrawalHash), true);
// the nonce increments
assertEq(nonce + 1, messagePasser.nonce());
}
// Test: burn should destroy the ETH held in the contract
function test_burn() external {
messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }(
NON_ZERO_ADDRESS,
NON_ZERO_GASLIMIT,
NON_ZERO_DATA
);
assertEq(address(messagePasser).balance, NON_ZERO_VALUE);
vm.expectEmit(true, false, false, false);
emit WithdrawerBalanceBurnt(NON_ZERO_VALUE);
messagePasser.burn();
// The Withdrawer should have no balance
assertEq(address(messagePasser).balance, 0);
}
}
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.0;
import {Bytes32AddressLib} from "solmate/utils/Bytes32AddressLib.sol";
// prettier-ignore
library LibRLP {
using Bytes32AddressLib for bytes32;
function computeAddress(address deployer, uint256 nonce) public pure returns (address) {
// The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0.
// A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it.
if (nonce == 0x00) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))).fromLast20Bytes();
if (nonce <= 0x7f) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))).fromLast20Bytes();
// Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length.
if (nonce <= type(uint8).max) return keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))).fromLast20Bytes();
if (nonce <= type(uint16).max) return keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))).fromLast20Bytes();
if (nonce <= type(uint24).max) return keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))).fromLast20Bytes();
// More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp
// 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce)
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
// 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex)
// We assume nobody can have a nonce large enough to require more than 32 bytes.
return keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))).fromLast20Bytes();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { LibRLP } from "./Lib_RLP.t.sol";
contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
event Mint(address indexed _account, uint256 _amount);
event Burn(address indexed _account, uint256 _amount);
function setUp() public override {
super.setUp();
}
function test_remoteToken() external {
assertEq(L2Token.remoteToken(), address(L1Token));
}
function test_bridge() external {
assertEq(L2Token.bridge(), address(L2Bridge));
}
function test_l1Token() external {
assertEq(L2Token.l1Token(), address(L1Token));
}
function test_l2Bridge() external {
assertEq(L2Token.l2Bridge(), address(L2Bridge));
}
function test_mint() external {
vm.expectEmit(true, true, true, true);
emit Mint(alice, 100);
vm.prank(address(L2Bridge));
L2Token.mint(alice, 100);
assertEq(L2Token.balanceOf(alice), 100);
}
function test_mintRevertsFromNotBridge() external {
// NOT the bridge
vm.expectRevert("Only L2 Bridge can mint and burn");
vm.prank(address(alice));
L2Token.mint(alice, 100);
}
function test_burn() external {
vm.prank(address(L2Bridge));
L2Token.mint(alice, 100);
vm.expectEmit(true, true, true, true);
emit Burn(alice, 100);
vm.prank(address(L2Bridge));
L2Token.burn(alice, 100);
assertEq(L2Token.balanceOf(alice), 0);
}
function test_burnRevertsFromNotBridge() external {
// NOT the bridge
vm.expectRevert("Only L2 Bridge can mint and burn");
vm.prank(address(alice));
L2Token.burn(alice, 100);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { LibRLP } from "./Lib_RLP.t.sol";
contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
event StandardL2TokenCreated(address indexed _remoteToken, address indexed _localToken);
event OptimismMintableTokenCreated(
address indexed _localToken,
address indexed _remoteToken,
address _deployer
);
function setUp() public override {
super.setUp();
}
function test_initializeShouldRevert() external {
vm.expectRevert("Already initialized.");
L2TokenFactory.initialize(address(L1Bridge));
}
function test_bridge() external {
assertEq(address(L2TokenFactory.bridge()), address(L2Bridge));
}
function test_createStandardL2Token() external {
address remote = address(4);
address local = LibRLP.computeAddress(address(L2TokenFactory), 1);
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(
remote,
local
);
vm.expectEmit(true, true, true, true);
emit OptimismMintableTokenCreated(
remote,
local,
alice
);
vm.prank(alice);
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
function test_createStandardL2TokenSameTwice() external {
address remote = address(4);
vm.prank(alice);
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
address local = LibRLP.computeAddress(address(L2TokenFactory), 2);
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(
remote,
local
);
vm.expectEmit(true, true, true, true);
emit OptimismMintableTokenCreated(
remote,
local,
alice
);
vm.prank(alice);
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
function test_createStandardL2TokenShouldRevertIfRemoteIsZero() external {
address remote = address(0);
vm.expectRevert("Must provide L1 token address");
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { CommonTest } from "./CommonTest.t.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
contract OptimismPortal_Test is CommonTest {
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 mint,
uint256 value,
uint64 gasLimit,
bool isCreation,
bytes data
);
// Dependencies
L2OutputOracle oracle;
OptimismPortal op;
function setUp() external {
oracle = new L2OutputOracle(
1800,
2,
keccak256(abi.encode(0)),
100,
1,
address(666)
);
op = new OptimismPortal(oracle, 7 days);
}
function test_OptimismPortalConstructor() external {
assertEq(op.FINALIZATION_PERIOD(), 7 days);
assertEq(address(op.L2_ORACLE()), address(oracle));
assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD);
}
function test_OptimismPortalReceiveEth() external {
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
alice,
alice,
100,
100,
100_000,
false,
hex""
);
// give alice money and send as an eoa
vm.deal(alice, 2**64);
vm.prank(alice, alice);
(bool s, ) = address(op).call{ value: 100 }(hex"");
assert(s);
assertEq(address(op).balance, 100);
}
// function test_OptimismPortalDepositTransaction() external {}
// Test: depositTransaction fails when contract creation has a non-zero destination address
function test_OptimismPortalContractCreationReverts() external {
// contract creation must have a target of address(0)
vm.expectRevert(abi.encodeWithSignature("NonZeroCreationTarget()"));
op.depositTransaction(address(1), 1, 0, true, hex"");
}
// Test: depositTransaction should emit the correct log when an EOA deposits a tx with 0 value
function test_depositTransaction_NoValueEOA() external {
// EOA emulation
vm.prank(address(this), address(this));
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
address(this),
NON_ZERO_ADDRESS,
ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
op.depositTransaction(
NON_ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
}
// Test: depositTransaction should emit the correct log when a contract deposits a tx with 0 value
function test_depositTransaction_NoValueContract() external {
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)),
NON_ZERO_ADDRESS,
ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
op.depositTransaction(
NON_ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
}
// Test: depositTransaction should emit the correct log when an EOA deposits a contract creation with 0 value
function test_depositTransaction_createWithZeroValueForEOA() external {
// EOA emulation
vm.prank(address(this), address(this));
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
address(this),
ZERO_ADDRESS,
ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
NON_ZERO_DATA
);
op.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA);
}
// Test: depositTransaction should emit the correct log when a contract deposits a contract creation with 0 value
function test_depositTransaction_createWithZeroValueForContract() external {
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)),
ZERO_ADDRESS,
ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
NON_ZERO_DATA
);
op.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA);
}
// Test: depositTransaction should increase its eth balance when an EOA deposits a transaction with ETH
function test_depositTransaction_withEthValueFromEOA() external {
// EOA emulation
vm.prank(address(this), address(this));
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
address(this),
NON_ZERO_ADDRESS,
NON_ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
op.depositTransaction{ value: NON_ZERO_VALUE }(
NON_ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
assertEq(address(op).balance, NON_ZERO_VALUE);
}
// Test: depositTransaction should increase its eth balance when a contract deposits a transaction with ETH
function test_depositTransaction_withEthValueFromContract() external {
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)),
NON_ZERO_ADDRESS,
NON_ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
op.depositTransaction{ value: NON_ZERO_VALUE }(
NON_ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
false,
NON_ZERO_DATA
);
}
// Test: depositTransaction should increase its eth balance when an EOA deposits a contract creation with ETH
function test_depositTransaction_withEthValueAndEOAContractCreation() external {
// EOA emulation
vm.prank(address(this), address(this));
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
address(this),
ZERO_ADDRESS,
NON_ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
hex""
);
op.depositTransaction{ value: NON_ZERO_VALUE }(
ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
hex""
);
assertEq(address(op).balance, NON_ZERO_VALUE);
}
// Test: depositTransaction should increase its eth balance when a contract deposits a contract creation with ETH
function test_depositTransaction_withEthValueAndContractContractCreation() external {
vm.expectEmit(true, true, false, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)),
ZERO_ADDRESS,
NON_ZERO_VALUE,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
NON_ZERO_DATA
);
op.depositTransaction{ value: NON_ZERO_VALUE }(
ZERO_ADDRESS,
ZERO_VALUE,
NON_ZERO_GASLIMIT,
true,
NON_ZERO_DATA
);
assertEq(address(op).balance, NON_ZERO_VALUE);
}
// TODO: test this deeply
// function test_verifyWithdrawal() external {}
function test_cannotVerifyRecentWithdrawal() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({
version: bytes32(0),
stateRoot: bytes32(0),
withdrawerStorageRoot: bytes32(0),
latestBlockhash: bytes32(0)
});
vm.expectRevert("Proposal is not yet finalized.");
op.finalizeWithdrawalTransaction(
0,
alice,
alice,
0,
0,
hex"",
0,
outputRootProof,
hex""
);
}
function test_invalidWithdrawalProof() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({
version: bytes32(0),
stateRoot: bytes32(0),
withdrawerStorageRoot: bytes32(0),
latestBlockhash: bytes32(0)
});
vm.warp(oracle.nextTimestamp() + op.FINALIZATION_PERIOD());
vm.expectRevert(abi.encodeWithSignature("InvalidOutputRootProof()"));
op.finalizeWithdrawalTransaction(
0,
alice,
alice,
0,
0,
hex"",
0,
outputRootProof,
hex""
);
}
}
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title OptimismMintableERC20
* This contract represents the remote representation
* of an ERC20 token. It is linked to the address of
* a token in another domain and tokens can be locked
* in the StandardBridge which will mint tokens in the
* other domain.
*/
contract OptimismMintableERC20 is ERC20 {
event Mint(address indexed _account, uint256 _amount);
event Burn(address indexed _account, uint256 _amount);
/**
* @notice The address of the token in the remote domain
*/
address public remoteToken;
/**
* @notice The address of the bridge responsible for
* minting. It is in the same domain.
*/
address public bridge;
/**
* @param _bridge Address of the L2 standard bridge.
* @param _remoteToken Address of the corresponding L1 token.
* @param _name ERC20 name.
* @param _symbol ERC20 symbol.
*/
constructor(
address _bridge,
address _remoteToken,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) {
remoteToken = _remoteToken;
bridge = _bridge;
}
/**
* @notice Returns the corresponding L1 token address.
* This is a legacy function and wraps the remoteToken value.
*/
function l1Token() public view returns (address) {
return remoteToken;
}
/**
* @notice The address of the bridge contract
* responsible for minting tokens. This is a legacy
* getter function
*/
function l2Bridge() public view returns (address) {
return bridge;
}
/**
* @notice A modifier that only allows the bridge to call
*/
modifier onlyBridge() {
require(msg.sender == bridge, "Only L2 Bridge can mint and burn");
_;
}
/**
* @notice ERC165
*/
// slither-disable-next-line external-function
function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {
bytes4 iface1 = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165
bytes4 iface2 = this.l1Token.selector ^ this.mint.selector ^ this.burn.selector;
bytes4 iface3 = this.remoteToken.selector ^ this.mint.selector ^ this.burn.selector;
return _interfaceId == iface1 || _interfaceId == iface3 || _interfaceId == iface2;
}
/**
* @notice The bridge can mint tokens
*/
// slither-disable-next-line external-function
function mint(address _to, uint256 _amount) public virtual onlyBridge {
_mint(_to, _amount);
emit Mint(_to, _amount);
}
/**
* @notice The bridge can burn tokens
*/
// slither-disable-next-line external-function
function burn(address _from, uint256 _amount) public virtual onlyBridge {
_burn(_from, _amount);
emit Burn(_from, _amount);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export * from './utils'
export * from './generateProofs'
export * from './constants'
This diff is collapsed.
This diff is collapsed.
Subproject commit c7a36fb236f298e04edf28e2fee385b80f53945f
Subproject commit 4d36e3f7e2168c8155c641eb0f80e85cd584bd1c
Subproject commit 851ea3baa4327f453da723df75b1093b58b964dc
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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