Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0179e8a
test(withdraw-post-karst): acceptance test for withdrawing on karst
0xCoati May 7, 2026
9782ee9
test(acceptance-test-l2cm-karst): add acceptance test for doing a min…
0xCoati May 11, 2026
a118a9c
test(karst-fork-tests): skip upgrade executing when already on karst,…
0xCoati May 11, 2026
329e54d
test(acceptance-test-l2cm-karst): make the test proper e2e
0xCoati May 11, 2026
0a8fe52
Merge branch 'develop' into test/l2cm-upgrade-accaptance-test
0xCoati May 12, 2026
1971c09
fix(acceptance-test-l2cm-karst): remove redundant flag check
0xCoati May 13, 2026
a12f6d0
Revert "test(acceptance-test-l2cm-karst): make the test proper e2e"
0xCoati May 13, 2026
e2eb850
Revert "test(acceptance-test-l2cm-karst): add acceptance test for doi…
0xCoati May 13, 2026
81bcb09
fix(acceptance-test-l2cm-karst): remove karst activation check in for…
0xCoati May 13, 2026
da9943e
test(acceptance-test-l2cm-karst): add test to check the presence of t…
0xCoati May 13, 2026
331ce17
fix(acceptance-test-l2cm-karst): fix testname lint error
0xCoati May 13, 2026
96f15bc
fix(acceptance-test-l2cm-karst): remove misleading comment
0xCoati May 13, 2026
7cd0a82
fix(acceptance-test-l2cm-karst): skip execute and events test when cu…
0xCoati May 15, 2026
71506e6
fix(acceptance-test-l2cm-karst): rework betanet upgrade test to work …
0xCoati May 18, 2026
fff70c2
fix(acceptance-test-l2cm-karst): fix rerun command and make l2BlockAf…
0xCoati May 19, 2026
d292e28
fix(acceptance-test-l2cm-karst): fix usage example
0xCoati May 19, 2026
e05e520
fix(acceptance-test-l2cm-karst): add missing latest fallback for the …
0xCoati May 19, 2026
5185926
fix(acceptance-test-l2cm-karst): fix semgrep errors
0xCoati May 20, 2026
ba4153b
fix(acceptance-test-l2cm-karst): all tests fixed
0xCoati May 21, 2026
33b1700
fix(acceptance-test-l2cm-karst): move tests to separate file
0xCoati May 21, 2026
373b1ee
fix(acceptance-test-l2cm-karst): fix linter issues
0xCoati May 21, 2026
281e44a
Merge branch 'develop' into test/l2cm-upgrade-accaptance-test
0xCoati May 21, 2026
521f991
fix(acceptance-test-l2cm-karst): improve env var comment
0xCoati May 21, 2026
2017222
test(acceptance-test-l2cm-karst): make a withdrawal acceptance test o…
0xCoati May 21, 2026
9f54a9c
fix(acceptance-test-l2cm-karst): fix natspec comment for _executeCurr…
0xCoati May 21, 2026
c1181f3
fix(acceptance-test-l2cm-karst): reuse assertion logic in events tests
0xCoati May 21, 2026
ba75e97
Merge branch 'develop' into test/l2cm-upgrade-accaptance-test
0xCoati May 22, 2026
599e4a9
fix(acceptance-test-l2cm-karst): add comment for fork offset
0xCoati May 25, 2026
bdd84c8
fix(acceptance-test-l2cm-karst): inline log transformation
0xCoati May 25, 2026
c9ea936
fix(acceptance-test-l2cm-karst): cleanup
0xCoati May 25, 2026
d4711ae
fix(acceptance-test-l2cm-karst): include data in _findBundleStart
0xCoati May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"testing"

gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
opforks "github.com/ethereum-optimism/optimism/op-core/forks"
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
"github.com/ethereum-optimism/optimism/op-devstack/presets"
"github.com/ethereum-optimism/optimism/op-devstack/sysgo"
ps "github.com/ethereum-optimism/optimism/op-proposer/proposer"
"github.com/ethereum-optimism/optimism/op-service/eth"
)

func withdrawalOpts(gameType gameTypes.GameType) []presets.Option {
func withdrawalOpts(gameType gameTypes.GameType, extra ...presets.Option) []presets.Option {
opts := []presets.Option{
presets.WithTimeTravelEnabled(),
presets.WithDeployerOptions(
Expand All @@ -27,16 +28,16 @@ func withdrawalOpts(gameType gameTypes.GameType) []presets.Option {
cfg.DisputeGameType = uint32(gameType)
}),
}
return opts
return append(opts, extra...)
}

func newSystem(t devtest.T, gameType gameTypes.GameType) *presets.Minimal {
return presets.NewMinimal(t, withdrawalOpts(gameType)...)
func newSystem(t devtest.T, gameType gameTypes.GameType, extra ...presets.Option) *presets.Minimal {
return presets.NewMinimal(t, withdrawalOpts(gameType, extra...)...)
}

func TestWithdrawal(gt *testing.T, gameType gameTypes.GameType) {
func TestWithdrawal(gt *testing.T, gameType gameTypes.GameType, extra ...presets.Option) {
t := devtest.ParallelT(gt)
sys := newSystem(t, gameType)
sys := newSystem(t, gameType, extra...)

bridge := sys.StandardBridge()
bridge.VerifyRespectedGameType(gameType)
Expand Down Expand Up @@ -77,3 +78,46 @@ func TestWithdrawal(gt *testing.T, gameType gameTypes.GameType) {
expectedL1UserBalance = expectedL1UserBalance.Sub(withdrawal.FinalizeGasCost()).Add(withdrawalAmount)
l1User.VerifyBalanceExact(expectedL1UserBalance)
}

// TestWithdrawalAfterUpgrade is like TestWithdrawal but waits for the given fork to activate
// before initiating the withdrawal, exercising the upgrade path rather than genesis activation.
func TestWithdrawalAfterUpgrade(gt *testing.T, gameType gameTypes.GameType, fork opforks.Name, extra ...presets.Option) {
t := devtest.ParallelT(gt)
sys := newSystem(t, gameType, extra...)

sys.L2Chain.AwaitActivation(t, fork)

bridge := sys.StandardBridge()
bridge.VerifyRespectedGameType(gameType)

initialL1Balance := eth.OneThirdEther

l1User := sys.FunderL1.NewFundedEOA(initialL1Balance)
l2User := l1User.AsEL(sys.L2EL)
depositAmount := eth.OneTenthEther
withdrawalAmount := eth.OneHundredthEther

deposit := bridge.Deposit(depositAmount, l1User)
expectedL1UserBalance := initialL1Balance.Sub(depositAmount).Sub(deposit.GasCost())
l1User.VerifyBalanceExact(expectedL1UserBalance)
expectedL2UserBalance := depositAmount
l2User.VerifyBalanceExact(expectedL2UserBalance)

withdrawal := bridge.InitiateWithdrawal(withdrawalAmount, l2User)
expectedL2UserBalance = expectedL2UserBalance.Sub(withdrawalAmount).Sub(withdrawal.InitiateGasCost())
l2User.VerifyBalanceExact(expectedL2UserBalance)

withdrawal.Prove(l1User)
expectedL1UserBalance = expectedL1UserBalance.Sub(withdrawal.ProveGasCost())
l1User.VerifyBalanceExact(expectedL1UserBalance)

sys.AdvanceTime(bridge.GameResolutionDelay())
withdrawal.WaitForDisputeGameResolved()

sys.AdvanceTime(max(bridge.WithdrawalDelay()-bridge.GameResolutionDelay(), bridge.DisputeGameFinalityDelay()))

t.Logger().Info("Attempting to finalize", "proofMaturity", bridge.WithdrawalDelay(), "gameResolutionDelay", bridge.GameResolutionDelay(), "gameFinalityDelay", bridge.DisputeGameFinalityDelay())
withdrawal.Finalize(l1User)
expectedL1UserBalance = expectedL1UserBalance.Sub(withdrawal.FinalizeGasCost()).Add(withdrawalAmount)
l1User.VerifyBalanceExact(expectedL1UserBalance)
}
28 changes: 28 additions & 0 deletions op-acceptance-tests/tests/karst/withdrawal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package karst

import (
"testing"

"github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/base/withdrawal"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
opforks "github.com/ethereum-optimism/optimism/op-core/forks"
"github.com/ethereum-optimism/optimism/op-devstack/presets"
"github.com/ethereum-optimism/optimism/op-devstack/sysgo"
)

// TestWithdrawal_Karst creates a withdrawal from the L2StandardBridge and
// observes the full withdrawal flow, including finalization on L1.
func TestWithdrawal_Karst(gt *testing.T) {
withdrawal.TestWithdrawal(gt, gameTypes.CannonGameType,
presets.WithDeployerOptions(sysgo.WithKarstAtGenesis),
)
}

// TestWithdrawal_KarstUpgrade is the same withdrawal flow but on a network that
// started pre-Karst and activated Karst mid-chain via a scheduled upgrade.
func TestWithdrawal_KarstUpgrade(gt *testing.T) {
offset := uint64(10) // arbitrary offset to have a few blocks before Karst
withdrawal.TestWithdrawalAfterUpgrade(gt, gameTypes.CannonGameType, opforks.Karst,
Comment thread
maurelian marked this conversation as resolved.
presets.WithDeployerOptions(sysgo.WithKarstAtOffset(&offset)),
)
}
24 changes: 24 additions & 0 deletions packages/contracts-bedrock/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,30 @@ test-l2-fork-upgrade *ARGS:
test-l2-fork-upgrade-rerun *ARGS:
just test-l2-fork-upgrade {{ARGS}} --rerun -vvvv

# Prepares the environment for L2CM activation (betanet fork) tests.
# Env Vars:
# - L2_FORK_RPC_URL must be set to a post-Karst betanet L2 RPC URL.
# - L2_BLOCK_BEFORE_FORK must be set to the block right before the fork
# - L2_FORK_BLOCK_NUMBER optional: pin to a block after the activation block (defaults to latest).
prepare-l2cm-activation-env *ARGS:
#!/bin/bash
set -euo pipefail
export L2_FORK_TEST=true
export L2CM_ACTIVATION_TEST=true
export DEV_FEATURE__L2CM=true
export FOUNDRY_FORK_RETRIES=10
export FOUNDRY_FORK_RETRY_BACKOFF=1000
{{ARGS}} \
--match-path "test/L2/betanet-fork/**"

# Runs L2CM activation (betanet fork) tests.
# Usage: L2_FORK_RPC_URL=<url> L2_BLOCK_BEFORE_FORK=<block> just test-l2cm-activation-test [ARGS]
test-l2cm-activation-test *ARGS:
just prepare-l2cm-activation-env "just test {{ARGS}}"

test-l2cm-activation-test-rerun *ARGS:
just test-l2cm-activation-test {{ARGS}} --rerun -vvvv

########################################################
# DEPLOY #
########################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@
# documented here to avoid false validation failures while maintaining the validation rules
# for standard contract tests.
src_validation = [
"test/invariants/", # Invariant testing framework - no direct src counterpart
"test/opcm/", # OP Chain Manager tests - may have different structure
"test/scripts/", # Script tests - test deployment/utility scripts, not contracts
"test/integration/", # Integration tests - test multiple contracts together
"test/cannon/MIPS64Memory.t.sol", # Tests external MIPS implementation
"test/dispute/lib/LibClock.t.sol", # Tests library utilities
"test/dispute/lib/LibGameId.t.sol", # Tests library utilities
"test/libraries/DeployUtils.t.sol", # Tests library utilities - no direct src counterpart
"test/setup/DeployVariations.t.sol", # Tests deployment variations
"test/setup/PastNUTBundles.t.sol", # Tests a test-only library (test/setup/PastNUTBundles.sol)
"test/universal/BenchmarkTest.t.sol", # Performance benchmarking tests
"test/universal/ExtendedPause.t.sol", # Tests extended functionality
"test/vendor/Initializable.t.sol", # Tests external vendor code
"test/vendor/InitializableOZv5.t.sol", # Tests external vendor code
"test/L2/fork/L2ForkUpgrade.t.sol", # Tests L2 fork upgrade workflow
"test/L2/L2GenesisForkUpgrade.t.sol", # Tests L2 genesis fork upgrade workflow
"test/dispute/zk/ZKDisputeGameIntegration.t.sol", # Integration tests for ZK dispute game lifecycle
"test/invariants/", # Invariant testing framework - no direct src counterpart
"test/opcm/", # OP Chain Manager tests - may have different structure
"test/scripts/", # Script tests - test deployment/utility scripts, not contracts
"test/integration/", # Integration tests - test multiple contracts together
"test/cannon/MIPS64Memory.t.sol", # Tests external MIPS implementation
"test/dispute/lib/LibClock.t.sol", # Tests library utilities
"test/dispute/lib/LibGameId.t.sol", # Tests library utilities
"test/libraries/DeployUtils.t.sol", # Tests library utilities - no direct src counterpart
"test/setup/DeployVariations.t.sol", # Tests deployment variations
"test/setup/PastNUTBundles.t.sol", # Tests a test-only library (test/setup/PastNUTBundles.sol)
"test/universal/BenchmarkTest.t.sol", # Performance benchmarking tests
"test/universal/ExtendedPause.t.sol", # Tests extended functionality
"test/vendor/Initializable.t.sol", # Tests external vendor code
"test/vendor/InitializableOZv5.t.sol", # Tests external vendor code
"test/L2/fork/L2ForkUpgrade.t.sol", # Tests L2 fork upgrade workflow
"test/L2/betanet-fork/L2VerifyBetanetForkUpgrade.t.sol", # Tests L2 betanet fork upgrade workflow - no src counterpart
"test/L2/L2GenesisForkUpgrade.t.sol", # Tests L2 genesis fork upgrade workflow
"test/dispute/zk/ZKDisputeGameIntegration.t.sol", # Integration tests for ZK dispute game lifecycle
]

# PATHS EXCLUDED FROM CONTRACT NAME FILE PATH VALIDATION:
Expand Down
17 changes: 17 additions & 0 deletions packages/contracts-bedrock/scripts/libraries/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,30 @@ library Config {
return vm.envOr("L2_FORK_TEST", false);
}

/// @notice Returns true if this is a L2CM activation test.
function l2CMActivationTest() internal view returns (bool) {
return vm.envOr("L2CM_ACTIVATION_TEST", false);
}

/// @notice Returns the L2 RPC URL for forking.
function l2ForkRpcUrl() internal view returns (string memory) {
return vm.envString("L2_FORK_RPC_URL");
}

/// @notice Returns the L2 block after the fork.
function l2BlockAfterFork() internal view returns (uint256) {
if (l2CMActivationTest()) {
return vm.envOr("L2_FORK_BLOCK_NUMBER", uint256(0));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be fixed before merge.

The justfile documents L2_BLOCK_AFTER_FORK, but this reads L2_FORK_BLOCK_NUMBER and defaults to 0. I assume 0 is meant to preserve the existing "use latest" behaviour from the normal L2 fork setup. But activation mode passes this value directly to createSelectFork(url, block), so following the documented command makes the post upgrade fork select explicit block 0, not latest or the intended after fork block.

Could we do one of the following:

  • Read a mandatory L2_BLOCK_AFTER_FORK
  • Keep 0 as latest and have _executeCurrentBundleOrSwitchFork() call createSelectFork(url) overload when this returns 0?

The pre/post activation flow can become:

  • Fork at L2_BLOCK_BEFORE_FORK
  • Capture pre upgrade state
  • Fork at block 0
  • Verify post upgrade state

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in d292e28 and e05e520

}
revert("Config: l2BlockAfterFork called outside of L2CM activation test");
}

/// @notice Returns the L2 block number to fork at. Defaults to 0 (latest).
/// If L2CM activation test is enabled, returns the block before the fork.
function l2ForkBlockNumber() internal view returns (uint256) {
if (l2CMActivationTest()) {
return vm.envUint("L2_BLOCK_BEFORE_FORK");
}
Comment on lines +313 to +315
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be better to have this its own getter?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only place where it's used, as we are taking advantage of the existing test setup which forks based on this getter.

return vm.envOr("L2_FORK_BLOCK_NUMBER", uint256(0));
}

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"sourceCodeHash": "0x0a29792f3226dac84fdb44150b622a165fe65da3b678f6540acecdfe440772bb"
},
"src/L2/L2ProxyAdmin.sol:L2ProxyAdmin": {
"initCodeHash": "0x1b392652cf6009f47ed8c794c1f117ea51a626234fe419660bc273b5ad03b020",
"initCodeHash": "0xd9d6f7850ca2f14376b4a9c8b7693edd820803baddf42da91c0d452c207914a8",
"sourceCodeHash": "0x43379a0ceae81d693c0fb50d3b7e8b1836e7a2cae0b7931593d2324d09e59ba3"
},
"src/L2/L2StandardBridge.sol:L2StandardBridge": {
Expand Down
Loading