Skip to content

Commit 96c6ec1

Browse files
authored
feat: add CGT system feature (#18405)
* feat: add CGT system feature (#703) * fix: sys feature CI (#706) * fix: liquidity controller fuzz (#708) * fix: setup CGT system feature in deploy script (#711) * fix: unused import (#713) * test: add CGT missing tests (#714)
1 parent 6c62c5b commit 96c6ec1

23 files changed

+325
-163
lines changed

.circleci/config.yml

Lines changed: 82 additions & 70 deletions
Large diffs are not rendered by default.

op-deployer/pkg/deployer/devfeatures.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ var (
1717

1818
// DeployV2DisputeGamesDevFlag enables deployment of V2 dispute game contracts.
1919
DeployV2DisputeGamesDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000100")
20-
21-
// CustomGasTokenDevFlag enables the custom gas token.
22-
CustomGasTokenDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000001000")
2320
)
2421

2522
// IsDevFeatureEnabled checks if a specific development feature is enabled in a feature bitmap.

op-deployer/pkg/deployer/integration_test/apply_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,6 @@ func TestEndToEndApply(t *testing.T) {
321321
Symbol: "CGT",
322322
InitialLiquidity: (*hexutil.Big)(amount),
323323
}
324-
// CGT config for OPCM
325-
intent.GlobalDeployOverrides = map[string]interface{}{
326-
"devFeatureBitmap": deployer.CustomGasTokenDevFlag,
327-
}
328324

329325
require.NoError(t, deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{
330326
DeploymentTarget: deployer.DeploymentTargetLive,

packages/contracts-bedrock/scripts/deploy/Deploy.s.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ contract Deploy is Deployer {
169169
deploySuperchain();
170170
}
171171

172+
// Override useCustomGasToken config if system feature flag is set
173+
if (Config.sysFeatureCustomGasToken()) {
174+
cfg.setUseCustomGasToken(true);
175+
}
176+
172177
deployImplementations({ _isInterop: cfg.useInterop() });
173178

174179
// Deploy Current OPChain Contracts

packages/contracts-bedrock/scripts/libraries/Config.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,13 @@ library Config {
286286
return vm.envOr("DEV_FEATURE__DEPLOY_V2_DISPUTE_GAMES", false);
287287
}
288288

289-
/// @notice Returns true if the development feature custom gas token is enabled.
290-
function devFeatureCustomGasToken() internal view returns (bool) {
291-
return vm.envOr("DEV_FEATURE__CUSTOM_GAS_TOKEN", false);
292-
}
293-
294289
/// @notice Returns true if the development feature opcm_v2 is enabled.
295290
function devFeatureOpcmV2() internal view returns (bool) {
296291
return vm.envOr("DEV_FEATURE__OPCM_V2", false);
297292
}
293+
294+
/// @notice Returns true if the system feature custom_gas_token is enabled.
295+
function sysFeatureCustomGasToken() internal view returns (bool) {
296+
return vm.envOr("SYS_FEATURE__CUSTOM_GAS_TOKEN", false);
297+
}
298298
}

packages/contracts-bedrock/src/libraries/DevFeatures.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ library DevFeatures {
2222
bytes32 public constant DEPLOY_V2_DISPUTE_GAMES =
2323
bytes32(0x0000000000000000000000000000000000000000000000000000000000000100);
2424

25-
/// @notice The feature that enables the custom gas token.
26-
bytes32 public constant CUSTOM_GAS_TOKEN =
27-
bytes32(0x0000000000000000000000000000000000000000000000000000000000001000);
28-
2925
/// @notice The feature that enables the OPContractsManagerV2 contract.
3026
bytes32 public constant OPCM_V2 = bytes32(0x0000000000000000000000000000000000000000000000000000000000010000);
3127

packages/contracts-bedrock/test/L1/FeesDepositor.t.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { FeesDepositor } from "src/L1/FeesDepositor.sol";
99
import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol";
1010
import { Proxy } from "src/universal/Proxy.sol";
1111
import { Features } from "src/libraries/Features.sol";
12-
import { DevFeatures } from "src/libraries/DevFeatures.sol";
1312

1413
/// @title FeesDepositor_TestInit
1514
/// @notice Base test contract with initialization for `FeesDepositor` tests.
@@ -98,7 +97,7 @@ contract FeesDepositor_Receive_Test is FeesDepositor_TestInit {
9897
}
9998

10099
function testFuzz_receive_atOrAboveThreshold_succeeds(uint256 _sendAmount) external {
101-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
100+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
102101

103102
// Handling the fork tests scenario case for the fork tests
104103
uint256 depositFeesRecipientBalanceBefore = depositFeesRecipient.balance;
@@ -126,7 +125,7 @@ contract FeesDepositor_Receive_Test is FeesDepositor_TestInit {
126125
}
127126

128127
function testFuzz_receive_multipleDeposits_succeeds(uint256 _firstAmount, uint256 _secondAmount) external {
129-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
128+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
130129

131130
// Handling the fork tests scenario
132131
uint256 depositFeesRecipientBalanceBefore = depositFeesRecipient.balance;

packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
1515
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
1616
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
1717
import { Features } from "src/libraries/Features.sol";
18-
import { DevFeatures } from "src/libraries/DevFeatures.sol";
1918

2019
// Interfaces
2120
import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol";
@@ -333,7 +332,7 @@ contract L1StandardBridge_Paused_Test is CommonTest {
333332
contract L1StandardBridge_Receive_Test is CommonTest {
334333
/// @notice Tests receive bridges ETH successfully.
335334
function test_receive_succeeds() external {
336-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
335+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
337336
uint256 portalBalanceBefore = address(optimismPortal2).balance;
338337
uint256 ethLockboxBalanceBefore = address(ethLockbox).balance;
339338

@@ -377,6 +376,20 @@ contract L1StandardBridge_Receive_Test is CommonTest {
377376
(bool revertsAsExpected,) = address(l1StandardBridge).call{ value: 100 }(hex"");
378377
assertTrue(revertsAsExpected, "expectRevert: call did not revert");
379378
}
379+
380+
/// @notice Tests that receive reverts when custom gas token is enabled and value is sent.
381+
function testFuzz_receive_withCustomGasToken_reverts(uint256 _value) external {
382+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
383+
384+
_value = bound(_value, 1, type(uint128).max);
385+
vm.deal(alice, _value);
386+
387+
vm.prank(alice, alice);
388+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
389+
390+
(bool revertsAsExpected,) = address(l1StandardBridge).call{ value: _value }(hex"");
391+
assertTrue(revertsAsExpected, "expectRevert: call did not revert");
392+
}
380393
}
381394

382395
/// @title L1StandardBridge_DepositETH_Test
@@ -388,7 +401,7 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit {
388401
/// Only EOA can call depositETH.
389402
/// ETH ends up in the optimismPortal.
390403
function test_depositETH_fromEOA_succeeds() external {
391-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
404+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
392405
_preBridgeETH({ isLegacy: true, value: 500 });
393406
uint256 portalBalanceBefore = address(optimismPortal2).balance;
394407
uint256 ethLockboxBalanceBefore = address(ethLockbox).balance;
@@ -404,7 +417,7 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit {
404417

405418
/// @notice Tests that depositing ETH succeeds for an EOA using 7702 delegation.
406419
function test_depositETH_fromEOA7702_succeeds() external {
407-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
420+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
408421
// Set alice to have 7702 code.
409422
vm.etch(alice, abi.encodePacked(hex"EF0100", address(0)));
410423

@@ -428,6 +441,32 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit {
428441
vm.prank(alice);
429442
l1StandardBridge.depositETH{ value: 1 }(300, hex"");
430443
}
444+
445+
/// @notice Tests that depositETH reverts when custom gas token is enabled and value is sent.
446+
function testFuzz_depositETH_withCustomGasToken_reverts(uint256 _value, uint32 _minGasLimit) external {
447+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
448+
449+
_value = bound(_value, 1, type(uint128).max);
450+
vm.deal(alice, _value);
451+
452+
vm.prank(alice, alice);
453+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
454+
l1StandardBridge.depositETH{ value: _value }(_minGasLimit, hex"dead");
455+
}
456+
457+
/// @notice Tests that depositETH reverts when custom gas token is enabled for EOA with 7702 delegation.
458+
function testFuzz_depositETH_fromEOA7702WithCustomGasToken_reverts(uint256 _value, uint32 _minGasLimit) external {
459+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
460+
_value = bound(_value, 1, type(uint128).max);
461+
462+
// Set alice to have 7702 code.
463+
vm.etch(alice, abi.encodePacked(hex"EF0100", address(0)));
464+
465+
vm.deal(alice, _value);
466+
vm.prank(alice, alice);
467+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
468+
l1StandardBridge.depositETH{ value: _value }(_minGasLimit, hex"dead");
469+
}
431470
}
432471

433472
/// @title L1StandardBridge_DepositETHTo_Test
@@ -439,7 +478,7 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit {
439478
/// EOA or contract can call depositETHTo.
440479
/// ETH ends up in the optimismPortal.
441480
function test_depositETHTo_succeeds() external {
442-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
481+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
443482
_preBridgeETHTo({ isLegacy: true, value: 600 });
444483
uint256 portalBalanceBefore = address(optimismPortal2).balance;
445484
uint256 ethLockboxBalanceBefore = address(ethLockbox).balance;
@@ -457,7 +496,7 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit {
457496
/// @param _to Random recipient address
458497
/// @param _amount Random ETH amount to deposit
459498
function testFuzz_depositETHTo_randomRecipient_succeeds(address _to, uint256 _amount) external {
460-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
499+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
461500
vm.assume(_to != address(0));
462501
_amount = bound(_amount, 1, 10 ether);
463502

@@ -475,6 +514,23 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit {
475514
assertEq(address(optimismPortal2).balance, portalBalanceBefore + _amount);
476515
}
477516
}
517+
518+
/// @notice Tests that depositETHTo reverts when custom gas token is enabled and value is sent.
519+
function testFuzz_depositETHTo_withCustomGasToken_reverts(
520+
address _to,
521+
uint256 _value,
522+
uint32 _minGasLimit
523+
)
524+
external
525+
{
526+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
527+
vm.assume(_to != address(0));
528+
_value = bound(_value, 1, type(uint128).max);
529+
vm.deal(alice, _value);
530+
vm.prank(alice);
531+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
532+
l1StandardBridge.depositETHTo{ value: _value }(_to, _minGasLimit, hex"dead");
533+
}
478534
}
479535

480536
/// @title L1StandardBridge_DepositERC20_Test
@@ -786,7 +842,7 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit {
786842
/// Only EOA can call bridgeETH.
787843
/// ETH ends up in the optimismPortal.
788844
function test_bridgeETH_succeeds() external {
789-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
845+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
790846
_preBridgeETH({ isLegacy: false, value: 500 });
791847
uint256 portalBalanceBefore = address(optimismPortal2).balance;
792848
uint256 ethLockboxBalanceBefore = address(ethLockbox).balance;
@@ -806,7 +862,7 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit {
806862
/// Only EOA can call bridgeETHTo.
807863
/// ETH ends up in the optimismPortal.
808864
function test_bridgeETHTo_succeeds() external {
809-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
865+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
810866
_preBridgeETHTo({ isLegacy: false, value: 600 });
811867
uint256 portalBalanceBefore = address(optimismPortal2).balance;
812868
uint256 ethLockboxBalanceBefore = address(ethLockbox).balance;
@@ -878,4 +934,35 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit {
878934
vm.expectRevert("StandardBridge: cannot send to messenger");
879935
l1StandardBridge.finalizeBridgeETH{ value: 100 }(alice, messenger, 100, hex"");
880936
}
937+
938+
/// @notice Tests that bridgeETH reverts when custom gas token is enabled and value is sent.
939+
function testFuzz_bridgeETH_withCustomGasToken_reverts(uint256 _value, uint32 _minGasLimit) external {
940+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
941+
942+
_value = bound(_value, 1, type(uint128).max);
943+
vm.deal(alice, _value);
944+
945+
vm.prank(alice, alice);
946+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
947+
l1StandardBridge.bridgeETH{ value: _value }(_minGasLimit, hex"dead");
948+
}
949+
950+
/// @notice Tests that bridgeETHTo reverts when custom gas token is enabled and value is sent.
951+
function testFuzz_bridgeETHTo_withCustomGasToken_reverts(
952+
address _to,
953+
uint256 _value,
954+
uint32 _minGasLimit
955+
)
956+
external
957+
{
958+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
959+
960+
vm.assume(_to != address(0));
961+
_value = bound(_value, 1, type(uint128).max);
962+
vm.deal(alice, _value);
963+
964+
vm.prank(alice);
965+
vm.expectRevert(IOptimismPortal2.OptimismPortal_NotAllowedOnCGTMode.selector);
966+
l1StandardBridge.bridgeETHTo{ value: _value }(_to, _minGasLimit, hex"dead");
967+
}
881968
}

packages/contracts-bedrock/test/L2/FeeSplitter.t.sol

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { ReentrantMockFeeVault } from "test/mocks/ReentrantMockFeeVault.sol";
1313
// Libraries
1414
import { Predeploys } from "src/libraries/Predeploys.sol";
1515
import { Types } from "src/libraries/Types.sol";
16-
import { DevFeatures } from "src/libraries/DevFeatures.sol";
1716

1817
// Interfaces
1918
import { IFeeSplitter } from "interfaces/L2/IFeeSplitter.sol";
@@ -42,10 +41,6 @@ contract FeeSplitter_TestInit is CommonTest {
4241

4342
/// @notice Test setup.
4443
function setUp() public virtual override {
45-
// Resolve features and skip whole test suite if custom gas token is enabled
46-
resolveFeaturesFromEnv();
47-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
48-
4944
// Enable revenue sharing before calling parent setUp
5045
super.enableRevenueShare();
5146
super.setUp();

packages/contracts-bedrock/test/L2/FeeVault.t.sol

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import { CommonTest } from "test/setup/CommonTest.sol";
88
import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol";
99
import { IFeeVault } from "interfaces/L2/IFeeVault.sol";
1010
import { IL2ToL1MessagePasser } from "interfaces/L2/IL2ToL1MessagePasser.sol";
11+
import { IL2ToL1MessagePasserCGT } from "interfaces/L2/IL2ToL1MessagePasserCGT.sol";
1112

1213
// Libraries
1314
import { Hashing } from "src/libraries/Hashing.sol";
1415
import { Types } from "src/libraries/Types.sol";
1516
import { Predeploys } from "src/libraries/Predeploys.sol";
16-
import { DevFeatures } from "src/libraries/DevFeatures.sol";
17+
import { Features } from "src/libraries/Features.sol";
1718

1819
/// @title FeeVault_Uncategorized_Test
1920
/// @notice Abstract test contract for fee feeVault testing.
@@ -83,7 +84,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest {
8384

8485
/// @notice Tests that `withdraw` successfully initiates a withdrawal to L1.
8586
function test_withdraw_toL1_succeeds() external {
86-
skipIfDevFeatureEnabled(DevFeatures.CUSTOM_GAS_TOKEN);
87+
skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN);
8788

8889
// Setup L1 withdrawal
8990
vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner());
@@ -145,6 +146,31 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest {
145146
assertEq(Predeploys.L2_TO_L1_MESSAGE_PASSER.balance, amount);
146147
}
147148

149+
/// @notice Tests that withdraw to L1 reverts when custom gas token is enabled and value is sent.
150+
function testFuzz_withdraw_toL1WithCustomGasToken_reverts(uint256 _amount) external {
151+
skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN);
152+
153+
// Setup L1 withdrawal
154+
vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner());
155+
feeVault.setWithdrawalNetwork(Types.WithdrawalNetwork.L1);
156+
157+
// Set recipient
158+
vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner());
159+
feeVault.setRecipient(recipient);
160+
161+
// Set minimum withdrawal amount
162+
vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner());
163+
feeVault.setMinWithdrawalAmount(minWithdrawalAmount);
164+
165+
// Set the balance to be greater than the minimum withdrawal amount
166+
_amount = bound(_amount, feeVault.minWithdrawalAmount() + 1, type(uint128).max);
167+
vm.deal(address(feeVault), _amount);
168+
169+
// Withdrawal should revert due to CGT mode
170+
vm.expectRevert(IL2ToL1MessagePasserCGT.L2ToL1MessagePasserCGT_NotAllowedOnCGTMode.selector);
171+
feeVault.withdraw();
172+
}
173+
148174
/// @notice Tests that `withdraw` successfully initiates a withdrawal to L2.
149175
function test_withdraw_toL2_succeeds() public {
150176
_setupL2Withdrawal();

0 commit comments

Comments
 (0)