@@ -462,8 +462,9 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest {
462462 emit RespectedGameTypeSet (_ty, Timestamp.wrap (respectedGameTypeUpdatedAt));
463463 vm.prank (optimismPortal2.guardian ());
464464 optimismPortal2.setRespectedGameType (_ty);
465-
465+ // GameType changes, but the timestamp doesn't.
466466 assertEq (optimismPortal2.respectedGameType ().raw (), _ty.raw ());
467+ assertEq (optimismPortal2.respectedGameTypeUpdatedAt (), respectedGameTypeUpdatedAt);
467468 }
468469
469470 /// @dev Tests that the guardian can set the `respectedGameTypeUpdatedAt` timestamp to current timestamp.
@@ -472,12 +473,15 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest {
472473 {
473474 _elapsed = uint64 (bound (_elapsed, 0 , type (uint64 ).max - uint64 (block .timestamp )));
474475 GameType _ty = GameType.wrap (type (uint32 ).max);
475- uint64 _timestamp = uint64 (block .timestamp ) + _elapsed;
476- vm.warp (_timestamp);
477- // TODO: event?
476+ uint64 _newRespectedGameTypeUpdatedAt = uint64 (block .timestamp ) + _elapsed;
477+ GameType _existingGameType = optimismPortal2.respectedGameType ();
478+ vm.warp (_newRespectedGameTypeUpdatedAt);
479+ emit RespectedGameTypeSet (_existingGameType, Timestamp.wrap (_newRespectedGameTypeUpdatedAt));
478480 vm.prank (optimismPortal2.guardian ());
479481 optimismPortal2.setRespectedGameType (_ty);
480- assertEq (optimismPortal2.respectedGameTypeUpdatedAt (), _timestamp);
482+ // GameType doesn't change, but the timestamp does.
483+ assertEq (optimismPortal2.respectedGameType ().raw (), _existingGameType.raw ());
484+ assertEq (optimismPortal2.respectedGameTypeUpdatedAt (), _newRespectedGameTypeUpdatedAt);
481485 }
482486
483487 /// @dev Tests that `proveWithdrawalTransaction` reverts when paused.
@@ -1252,6 +1256,88 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest {
12521256 assertTrue (optimismPortal2.finalizedWithdrawals (withdrawalHash));
12531257 }
12541258
1259+ /// @dev Tests that `finalizeWithdrawalTransaction` succeeds even if the respected game type is changed.
1260+ function test_finalizeWithdrawalTransaction_wasRespectedGameType_succeeds (
1261+ address _sender ,
1262+ address _target ,
1263+ uint256 _value ,
1264+ uint256 _gasLimit ,
1265+ bytes memory _data ,
1266+ GameType _newGameType
1267+ )
1268+ external
1269+ {
1270+ vm.assume (
1271+ _target != address (optimismPortal2) // Cannot call the optimism portal or a contract
1272+ && _target.code.length == 0 // No accounts with code
1273+ && _target != CONSOLE // The console has no code but behaves like a contract
1274+ && uint160 (_target) > 9 // No precompiles (or zero address)
1275+ );
1276+
1277+ // Bound to prevent changes in respectedGameTypeUpdatedAt
1278+ _newGameType = GameType.wrap (uint32 (bound (_newGameType.raw (), 0 , type (uint32 ).max - 1 )));
1279+
1280+ // Total ETH supply is currently about 120M ETH.
1281+ uint256 value = bound (_value, 0 , 200_000_000 ether);
1282+ vm.deal (address (optimismPortal2), value);
1283+
1284+ uint256 gasLimit = bound (_gasLimit, 0 , 50_000_000 );
1285+ uint256 nonce = l2ToL1MessagePasser.messageNonce ();
1286+
1287+ // Get a withdrawal transaction and mock proof from the differential testing script.
1288+ Types.WithdrawalTransaction memory _tx = Types.WithdrawalTransaction ({
1289+ nonce: nonce,
1290+ sender: _sender,
1291+ target: _target,
1292+ value: value,
1293+ gasLimit: gasLimit,
1294+ data: _data
1295+ });
1296+ (
1297+ bytes32 stateRoot ,
1298+ bytes32 storageRoot ,
1299+ bytes32 outputRoot ,
1300+ bytes32 withdrawalHash ,
1301+ bytes [] memory withdrawalProof
1302+ ) = ffi.getProveWithdrawalTransactionInputs (_tx);
1303+
1304+ // Create the output root proof
1305+ Types.OutputRootProof memory proof = Types.OutputRootProof ({
1306+ version: bytes32 (uint256 (0 )),
1307+ stateRoot: stateRoot,
1308+ messagePasserStorageRoot: storageRoot,
1309+ latestBlockhash: bytes32 (uint256 (0 ))
1310+ });
1311+
1312+ // Ensure the values returned from ffi are correct
1313+ assertEq (outputRoot, Hashing.hashOutputRootProof (proof));
1314+ assertEq (withdrawalHash, Hashing.hashWithdrawal (_tx));
1315+
1316+ // Setup the dispute game to return the output root
1317+ vm.mockCall (address (game), abi.encodeCall (game.rootClaim, ()), abi.encode (outputRoot));
1318+
1319+ // Prove the withdrawal transaction
1320+ optimismPortal2.proveWithdrawalTransaction (_tx, _proposedGameIndex, proof, withdrawalProof);
1321+ (IDisputeGame _game ,) = optimismPortal2.provenWithdrawals (withdrawalHash, address (this ));
1322+ assertTrue (_game.rootClaim ().raw () != bytes32 (0 ));
1323+
1324+ // Resolve the dispute game
1325+ game.resolveClaim (0 , 0 );
1326+ game.resolve ();
1327+
1328+ // Warp past the finalization period
1329+ vm.warp (block .timestamp + optimismPortal2.proofMaturityDelaySeconds () + 1 );
1330+
1331+ // Change the respectedGameType
1332+ vm.prank (optimismPortal2.guardian ());
1333+ optimismPortal2.setRespectedGameType (_newGameType);
1334+
1335+ // Withdrawal transaction still finalizable
1336+ vm.expectCallMinGas (_tx.target, _tx.value, uint64 (_tx.gasLimit), _tx.data);
1337+ optimismPortal2.finalizeWithdrawalTransaction (_tx);
1338+ assertTrue (optimismPortal2.finalizedWithdrawals (withdrawalHash));
1339+ }
1340+
12551341 /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal's dispute game has been blacklisted.
12561342 function test_finalizeWithdrawalTransaction_blacklisted_reverts () external {
12571343 vm.expectEmit (true , true , true , true );
0 commit comments