Skip to content

Commit 6fa1e07

Browse files
authored
opcm: Add support for V2 contracts in addGameType (#17782)
* opcm: Add support for V2 contracts in addGameType Tests failing when V2 is enabled because the expected L2 chain ID is loaded incorrectly. * opcm: Make a bunch more tests pass. * opcm: Fix test_addGameType_reusedDelayedWETH_succeeds by deploying a proxy and initialising correctly. * opcm: Check gameType via created game. * opcm: Add additional checks into existing test. * opcm: Assert all common immutable fields of dispute games. * opcm: Fix imports * opcm: Inherit DisputeGames higher up in tests. * opcm: Assert actual chain ID * opcm: Load anchor state registry from the system config * opcm: Remove outdated comment.
1 parent eed9012 commit 6fa1e07

File tree

6 files changed

+350
-194
lines changed

6 files changed

+350
-194
lines changed

packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,22 @@
452452
"name": "IdentityPrecompileCallFailed",
453453
"type": "error"
454454
},
455+
{
456+
"inputs": [
457+
{
458+
"internalType": "bytes32",
459+
"name": "devFeature",
460+
"type": "bytes32"
461+
}
462+
],
463+
"name": "InvalidDevFeatureAccess",
464+
"type": "error"
465+
},
466+
{
467+
"inputs": [],
468+
"name": "InvalidGameArgsLength",
469+
"type": "error"
470+
},
455471
{
456472
"inputs": [],
457473
"name": "InvalidGameConfigs",

packages/contracts-bedrock/snapshots/semver-lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
"sourceCodeHash": "0xfca613b5d055ffc4c3cbccb0773ddb9030abedc1aa6508c9e2e7727cc0cd617b"
2121
},
2222
"src/L1/OPContractsManager.sol:OPContractsManager": {
23-
"initCodeHash": "0x0ef3fcb6fee1f73c95a48269bba8c83ee463a6f116f36f8d88edb247f72e8a05",
24-
"sourceCodeHash": "0x16a845ddb5ee469e81f3d817ee9f6d6ff5697ace0bc399bd1e0f26e05546f781"
23+
"initCodeHash": "0x02af0627bee699be308b7d83ceb41655c38ae6856ed3842246917ae696475d64",
24+
"sourceCodeHash": "0x76f8991025c5346053444d50c4c0b63faa1739deebdbad99360fe37ca068ed15"
2525
},
2626
"src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": {
2727
"initCodeHash": "0x2eaa345ba05582c67b40a1eb7ec9d54823aa08468e697e2d6c04bb74cc574abc",

packages/contracts-bedrock/src/L1/OPContractsManager.sol

Lines changed: 137 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol";
3939
import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol";
4040
import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol";
4141
import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol";
42+
import { ISystemConfig } from "../../interfaces/L1/ISystemConfig.sol";
4243

4344
contract OPContractsManagerContractsContainer {
4445
/// @notice Addresses of the Blueprint contracts.
@@ -571,111 +572,159 @@ contract OPContractsManagerGameTypeAdder is OPContractsManagerBase {
571572
outputs[i].delayedWETH = gameConfig.delayedWETH;
572573
}
573574

574-
// Determine the contract name and blueprints for the game type.
575-
string memory gameContractName;
576-
address blueprint1;
577-
address blueprint2;
578-
uint256 gameL2ChainId;
575+
// Grab the DisputeGameFactory and AnchorStateRegistry for the chain.
576+
IDisputeGameFactory dgf = getDisputeGameFactory(gameConfig.systemConfig);
579577

580-
// Separate context to avoid stack too deep.
581-
{
582-
// Grab the blueprints once since we'll need it multiple times below.
583-
OPContractsManager.Blueprints memory bps = getBlueprints();
578+
// Grab the existing game implementation from the DisputeGameFactory.
579+
IFaultDisputeGame existingGame =
580+
IFaultDisputeGame(address(getGameImplementation(dgf, gameConfig.disputeGameType)));
584581

585-
// Determine the contract name and blueprints for the game type.
582+
// Super games don't support V2 contracts yet so fallback to the V1 contracts for those game types.
583+
if (
584+
isDevFeatureEnabled(DevFeatures.DEPLOY_V2_DISPUTE_GAMES)
585+
&& gameConfig.disputeGameType.raw() != GameTypes.SUPER_PERMISSIONED_CANNON.raw()
586+
&& gameConfig.disputeGameType.raw() != GameTypes.SUPER_CANNON.raw()
587+
&& gameConfig.disputeGameType.raw() != GameTypes.SUPER_CANNON_KONA.raw()
588+
) {
586589
if (
587590
gameConfig.disputeGameType.raw() == GameTypes.CANNON.raw()
588591
|| (
589592
isDevFeatureEnabled(DevFeatures.CANNON_KONA)
590593
&& gameConfig.disputeGameType.raw() == GameTypes.CANNON_KONA.raw()
591594
)
592595
) {
593-
gameContractName = "FaultDisputeGame";
594-
blueprint1 = bps.permissionlessDisputeGame1;
595-
blueprint2 = bps.permissionlessDisputeGame2;
596-
gameL2ChainId = l2ChainId;
596+
address impl = implementations().faultDisputeGameV2Impl;
597+
bytes memory gameArgs = abi.encodePacked(
598+
gameConfig.disputeAbsolutePrestate, // 32 bytes
599+
gameConfig.vm, // 20 bytes
600+
address(getAnchorStateRegistry(ISystemConfig(gameConfig.systemConfig))), // 20
601+
// bytes
602+
address(outputs[i].delayedWETH), // 20 bytes
603+
l2ChainId // 32 bytes
604+
);
605+
setDGFImplementation(dgf, gameConfig.disputeGameType, IDisputeGame(impl), gameArgs);
606+
outputs[i].faultDisputeGame = IFaultDisputeGame(impl);
597607
} else if (gameConfig.disputeGameType.raw() == GameTypes.PERMISSIONED_CANNON.raw()) {
598-
gameContractName = "PermissionedDisputeGame";
599-
blueprint1 = bps.permissionedDisputeGame1;
600-
blueprint2 = bps.permissionedDisputeGame2;
601-
gameL2ChainId = l2ChainId;
602-
} else if (
603-
gameConfig.disputeGameType.raw() == GameTypes.SUPER_CANNON.raw()
604-
|| (
605-
isDevFeatureEnabled(DevFeatures.CANNON_KONA)
606-
&& gameConfig.disputeGameType.raw() == GameTypes.SUPER_CANNON_KONA.raw()
607-
)
608-
) {
609-
gameContractName = "SuperFaultDisputeGame";
610-
blueprint1 = bps.superPermissionlessDisputeGame1;
611-
blueprint2 = bps.superPermissionlessDisputeGame2;
612-
gameL2ChainId = 0;
613-
} else if (gameConfig.disputeGameType.raw() == GameTypes.SUPER_PERMISSIONED_CANNON.raw()) {
614-
gameContractName = "SuperPermissionedDisputeGame";
615-
blueprint1 = bps.superPermissionedDisputeGame1;
616-
blueprint2 = bps.superPermissionedDisputeGame2;
617-
gameL2ChainId = 0;
608+
address impl = implementations().permissionedDisputeGameV2Impl;
609+
bytes memory gameArgs = abi.encodePacked(
610+
gameConfig.disputeAbsolutePrestate, // 32 bytes
611+
gameConfig.vm, // 20 bytes
612+
address(getAnchorStateRegistry(ISystemConfig(gameConfig.systemConfig))), // 20 bytes
613+
address(outputs[i].delayedWETH), // 20 bytes
614+
l2ChainId, // 32 bytes
615+
getProposer(dgf, IPermissionedDisputeGame(address(existingGame)), gameConfig.disputeGameType), // 20
616+
// bytes
617+
getChallenger(dgf, IPermissionedDisputeGame(address(existingGame)), gameConfig.disputeGameType) // 20
618+
// bytes
619+
);
620+
setDGFImplementation(dgf, gameConfig.disputeGameType, IDisputeGame(impl), gameArgs);
621+
outputs[i].faultDisputeGame = IFaultDisputeGame(payable(impl));
618622
} else {
619623
revert OPContractsManagerGameTypeAdder_UnsupportedGameType();
620624
}
621-
}
622-
623-
// Grab the DisputeGameFactory and AnchorStateRegistry for the chain.
624-
IDisputeGameFactory dgf = getDisputeGameFactory(gameConfig.systemConfig);
625+
} else {
626+
// Determine the contract name and blueprints for the game type.
627+
string memory gameContractName;
628+
address blueprint1;
629+
address blueprint2;
630+
uint256 gameL2ChainId;
631+
632+
// Separate context to avoid stack too deep.
633+
{
634+
// Grab the blueprints once since we'll need it multiple times below.
635+
OPContractsManager.Blueprints memory bps = getBlueprints();
636+
637+
// Determine the contract name and blueprints for the game type.
638+
if (
639+
gameConfig.disputeGameType.raw() == GameTypes.CANNON.raw()
640+
|| (
641+
isDevFeatureEnabled(DevFeatures.CANNON_KONA)
642+
&& gameConfig.disputeGameType.raw() == GameTypes.CANNON_KONA.raw()
643+
)
644+
) {
645+
gameContractName = "FaultDisputeGame";
646+
blueprint1 = bps.permissionlessDisputeGame1;
647+
blueprint2 = bps.permissionlessDisputeGame2;
648+
gameL2ChainId = l2ChainId;
649+
} else if (gameConfig.disputeGameType.raw() == GameTypes.PERMISSIONED_CANNON.raw()) {
650+
gameContractName = "PermissionedDisputeGame";
651+
blueprint1 = bps.permissionedDisputeGame1;
652+
blueprint2 = bps.permissionedDisputeGame2;
653+
gameL2ChainId = l2ChainId;
654+
} else if (
655+
gameConfig.disputeGameType.raw() == GameTypes.SUPER_CANNON.raw()
656+
|| (
657+
isDevFeatureEnabled(DevFeatures.CANNON_KONA)
658+
&& gameConfig.disputeGameType.raw() == GameTypes.SUPER_CANNON_KONA.raw()
659+
)
660+
) {
661+
gameContractName = "SuperFaultDisputeGame";
662+
blueprint1 = bps.superPermissionlessDisputeGame1;
663+
blueprint2 = bps.superPermissionlessDisputeGame2;
664+
gameL2ChainId = 0;
665+
} else if (gameConfig.disputeGameType.raw() == GameTypes.SUPER_PERMISSIONED_CANNON.raw()) {
666+
gameContractName = "SuperPermissionedDisputeGame";
667+
blueprint1 = bps.superPermissionedDisputeGame1;
668+
blueprint2 = bps.superPermissionedDisputeGame2;
669+
gameL2ChainId = 0;
670+
} else {
671+
revert OPContractsManagerGameTypeAdder_UnsupportedGameType();
672+
}
673+
}
625674

626-
// Grab the existing game implementation from the DisputeGameFactory.
627-
IFaultDisputeGame existingGame =
628-
IFaultDisputeGame(address(getGameImplementation(dgf, gameConfig.disputeGameType)));
675+
// Encode the constructor data for the game type.
676+
bytes memory constructorData;
677+
if (gameConfig.permissioned) {
678+
constructorData = encodePermissionedFDGConstructor(
679+
IFaultDisputeGame.GameConstructorParams(
680+
gameConfig.disputeGameType,
681+
gameConfig.disputeAbsolutePrestate,
682+
gameConfig.disputeMaxGameDepth,
683+
gameConfig.disputeSplitDepth,
684+
gameConfig.disputeClockExtension,
685+
gameConfig.disputeMaxClockDuration,
686+
gameConfig.vm,
687+
outputs[i].delayedWETH,
688+
getAnchorStateRegistry(gameConfig.systemConfig),
689+
gameL2ChainId
690+
),
691+
getProposerV1(IPermissionedDisputeGame(address(existingGame))),
692+
getChallengerV1(IPermissionedDisputeGame(address(existingGame)))
693+
);
694+
} else {
695+
constructorData = encodePermissionlessFDGConstructor(
696+
IFaultDisputeGame.GameConstructorParams(
697+
gameConfig.disputeGameType,
698+
gameConfig.disputeAbsolutePrestate,
699+
gameConfig.disputeMaxGameDepth,
700+
gameConfig.disputeSplitDepth,
701+
gameConfig.disputeClockExtension,
702+
gameConfig.disputeMaxClockDuration,
703+
gameConfig.vm,
704+
outputs[i].delayedWETH,
705+
getAnchorStateRegistry(gameConfig.systemConfig),
706+
gameL2ChainId
707+
)
708+
);
709+
}
629710

630-
// Encode the constructor data for the game type.
631-
bytes memory constructorData;
632-
if (gameConfig.permissioned) {
633-
constructorData = encodePermissionedFDGConstructor(
634-
IFaultDisputeGame.GameConstructorParams(
635-
gameConfig.disputeGameType,
636-
gameConfig.disputeAbsolutePrestate,
637-
gameConfig.disputeMaxGameDepth,
638-
gameConfig.disputeSplitDepth,
639-
gameConfig.disputeClockExtension,
640-
gameConfig.disputeMaxClockDuration,
641-
gameConfig.vm,
642-
outputs[i].delayedWETH,
643-
getAnchorStateRegistry(gameConfig.systemConfig),
644-
gameL2ChainId
645-
),
646-
getProposerV1(IPermissionedDisputeGame(address(existingGame))),
647-
getChallengerV1(IPermissionedDisputeGame(address(existingGame)))
648-
);
649-
} else {
650-
constructorData = encodePermissionlessFDGConstructor(
651-
IFaultDisputeGame.GameConstructorParams(
652-
gameConfig.disputeGameType,
653-
gameConfig.disputeAbsolutePrestate,
654-
gameConfig.disputeMaxGameDepth,
655-
gameConfig.disputeSplitDepth,
656-
gameConfig.disputeClockExtension,
657-
gameConfig.disputeMaxClockDuration,
658-
gameConfig.vm,
659-
outputs[i].delayedWETH,
660-
getAnchorStateRegistry(gameConfig.systemConfig),
661-
gameL2ChainId
711+
// Deploy the new game type.
712+
outputs[i].faultDisputeGame = IFaultDisputeGame(
713+
Blueprint.deployFrom(
714+
blueprint1,
715+
blueprint2,
716+
computeSalt(l2ChainId, gameConfig.saltMixer, gameContractName),
717+
constructorData
662718
)
663719
);
664-
}
665720

666-
// Deploy the new game type.
667-
outputs[i].faultDisputeGame = IFaultDisputeGame(
668-
Blueprint.deployFrom(
669-
blueprint1,
670-
blueprint2,
671-
computeSalt(l2ChainId, gameConfig.saltMixer, gameContractName),
672-
constructorData
673-
)
674-
);
721+
// As a last step, register the new game type with the DisputeGameFactory. If the game
722+
// type already exists, then its implementation will be overwritten.
723+
setDGFImplementation(
724+
dgf, gameConfig.disputeGameType, IDisputeGame(address(outputs[i].faultDisputeGame))
725+
);
726+
}
675727

676-
// As a last step, register the new game type with the DisputeGameFactory. If the game
677-
// type already exists, then its implementation will be overwritten.
678-
setDGFImplementation(dgf, gameConfig.disputeGameType, IDisputeGame(address(outputs[i].faultDisputeGame)));
679728
dgf.setInitBond(gameConfig.disputeGameType, gameConfig.initialBond);
680729

681730
// Emit event for the newly added game type with the new and old implementations.
@@ -2089,9 +2138,9 @@ contract OPContractsManager is ISemver {
20892138

20902139
// -------- Constants and Variables --------
20912140

2092-
/// @custom:semver 4.4.0
2141+
/// @custom:semver 4.5.0
20932142
function version() public pure virtual returns (string memory) {
2094-
return "4.4.0";
2143+
return "4.5.0";
20952144
}
20962145

20972146
OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder;

0 commit comments

Comments
 (0)