Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f6ebb3d
Add architecture diagrams and developer guide for Custom Gas Token im…
doutv Oct 8, 2025
6e47dd6
Implement restrictions in CrossDomainMessenger by adding require stat…
doutv Oct 8, 2025
e331201
Add gasPayingToken function to OptimismPortal2 for retrieving gas tok…
doutv Oct 8, 2025
711695d
Add gas paying token functionality to SystemConfig and OptimismPortal2
doutv Oct 8, 2025
aecd27b
fix error
doutv Oct 8, 2025
6bc0d9b
Add SetupCustomGasToken script and MockOKB contract for custom gas to…
doutv Oct 8, 2025
9e11532
Updated the script to extract and handle the OptimismPortalProxy addr…
doutv Oct 8, 2025
147cb4e
fix Transactor error
doutv Oct 8, 2025
d05fe88
fix: error
Oct 9, 2025
17c569c
revert changes
doutv Oct 9, 2025
fae0c6d
Update custom gas token setup script
doutv Oct 9, 2025
a1155f5
Add OKB token interface and implement burner functionality
doutv Oct 13, 2025
3bdb346
fix compiler
doutv Oct 13, 2025
f2b3dd9
Enhance CGT setup script with OKB total supply checks
doutv Oct 13, 2025
6eccc8d
Refactor OKBBurner contract and setup script
doutv Oct 13, 2025
f688760
Update cgt-only-contract.sh to improve OKB supply reporting and balan…
doutv Oct 13, 2025
14d4692
refine logic
albbm Oct 13, 2025
5c76719
add Libraries
albbm Oct 13, 2025
14741b6
Revert "Implement restrictions in CrossDomainMessenger by adding requ…
doutv Oct 13, 2025
14b374a
revert L1BlockCGT, fix CGT metadata on L2
doutv Oct 13, 2025
0064de4
remove doc
doutv Oct 13, 2025
1e82c5b
Remove unused OptimismPortal reference and related checks from SetupC…
doutv Oct 13, 2025
79c89f6
feat: add log of update gas paying token
Oct 13, 2025
e202ff6
feat: enable custom gas token feature in deployment script
doutv Oct 13, 2025
a9d2f68
Refactor cgt-only-contract.sh to simplify OKB supply reporting
doutv Oct 13, 2025
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
6 changes: 6 additions & 0 deletions op-node/rollup/derive/system_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"log/slog"

"github.com/hashicorp/go-multierror"

Expand All @@ -24,6 +25,7 @@ var (
SystemConfigUpdateEIP1559Params = common.Hash{31: 4}
SystemConfigUpdateOperatorFeeParams = common.Hash{31: 5}
SystemConfigUpdateMinBaseFee = common.Hash{31: 6}
SystemConfigUpdateGasPayingToken = common.Hash{31: 7}
)

var (
Expand Down Expand Up @@ -194,6 +196,10 @@ func ProcessSystemConfigUpdateLogEvent(destSysCfg *eth.SystemConfig, ev *types.L
}
destSysCfg.MinBaseFee = minBaseFee
return nil
case SystemConfigUpdateGasPayingToken:
evBytes, _ := ev.MarshalJSON()
slog.Default().Info("ignoring L1 sysCfg update to gas-paying token", "txHash", ev.TxHash, "event", string(evBytes))
return nil
default:
return fmt.Errorf("unrecognized L1 sysCfg update type: %s", updateType)
}
Expand Down
8 changes: 8 additions & 0 deletions packages/contracts-bedrock/interfaces/L1/IOKB.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IOKB is IERC20 {
function triggerBridge() external;
}
10 changes: 10 additions & 0 deletions packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase {
error OptimismPortal_ProofNotOldEnough();
error OptimismPortal_Unproven();
error OptimismPortal_InvalidLockboxState();
error OptimismPortal_OnlyCustomGasToken();
error OutOfGas();
error UnexpectedList();
error UnexpectedString();
Expand All @@ -49,6 +50,15 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase {
function anchorStateRegistry() external view returns (IAnchorStateRegistry);
function ethLockbox() external view returns (IETHLockbox);
function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) external view;
function depositERC20Transaction(
address _to,
uint256 _mint,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
)
external;
function depositTransaction(
address _to,
uint256 _value,
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts-bedrock/interfaces/L1/ISystemConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ interface ISystemConfig is IProxyAdminOwnedBase {
function setFeature(bytes32 _feature, bool _enabled) external;
function isFeatureEnabled(bytes32) external view returns (bool);
function isCustomGasToken() external view returns (bool);
function gasPayingToken() external view returns (address address_, uint8 decimals_);
function gasPayingTokenName() external view returns (string memory name_);
function gasPayingTokenSymbol() external view returns (string memory symbol_);
function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external;

function __constructor__() external;
}
199 changes: 199 additions & 0 deletions packages/contracts-bedrock/scripts/SetupCustomGasToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {Script} from "forge-std/Script.sol";
import {console2 as console} from "forge-std/console2.sol";
import {stdJson} from "forge-std/StdJson.sol";

// Contracts
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {DepositedOKBAdapter} from "src/L1/DepositedOKBAdapter.sol";
import {OKBBurner} from "src/L1/OKBBurner.sol";

// Interfaces
import {IOKB} from "interfaces/L1/IOKB.sol";
import {ISystemConfig} from "interfaces/L1/ISystemConfig.sol";
import {IOptimismPortal2} from "interfaces/L1/IOptimismPortal2.sol";
import {IL1Block} from "interfaces/L2/IL1Block.sol";

// Libraries
import {Features} from "src/libraries/Features.sol";
import {GasPayingToken} from "src/libraries/GasPayingToken.sol";
import {LibString} from "@solady/utils/LibString.sol";
import {Predeploys} from "src/libraries/Predeploys.sol";

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

/// @title MockOKB
/// @notice Mock OKB token for testing custom gas token setup
contract MockOKB is ERC20, ERC20Burnable {
constructor() ERC20("Mock OKB", "OKB") {
_mint(msg.sender, 21000000 * 10 ** decimals());
}

function decimals() public pure override returns (uint8) {
return 18;
}

/// @notice Burn all tokens of msg.sender
function triggerBridge() external {
_burn(msg.sender, balanceOf(msg.sender));
}
}

/// @title SetupCustomGasToken
/// @notice Foundry script to set up and verify custom gas token configuration
/// @dev This script:
/// 1. Deploys mock OKB token with triggerBridge functionality
/// 2. Deploys OKBBurner implementation contract for minimal proxy pattern
/// 3. Deploys DepositedOKBAdapter with burner implementation reference
/// 4. Sets gas paying token in SystemConfig storage
/// 5. Verifies all configurations on L1
/// 6. Provides test function for deposit functionality
contract SetupCustomGasToken is Script {
using stdJson for string;

// Addresses to be loaded from deployment artifacts
address systemConfigProxy;
address optimismPortalProxy;
address l1BlockAddress;
address deployerAddress;

// Deployed contracts
MockOKB okbToken;
OKBBurner burnerImplementation;
DepositedOKBAdapter adapter;

// Configuration
bytes32 constant TOKEN_NAME = bytes32("Mock OKB");
bytes32 constant TOKEN_SYMBOL = bytes32("OKB");
uint8 constant TOKEN_DECIMALS = 18;

function setUp() public {
// Get deployer address from msg.sender (set by forge script --private-key)
deployerAddress = msg.sender;
console.log("Deployer address:", deployerAddress);

// Parse addresses from environment variables
systemConfigProxy = vm.envAddress("SYSTEM_CONFIG_PROXY_ADDRESS");
optimismPortalProxy = vm.envAddress("OPTIMISM_PORTAL_PROXY_ADDRESS");

console.log("SystemConfig Proxy:", systemConfigProxy);
console.log("OptimismPortal Proxy:", optimismPortalProxy);

// L1Block is a predeploy on L2 at a fixed address
l1BlockAddress = Predeploys.L1_BLOCK_ATTRIBUTES;
console.log("L1Block Address:", l1BlockAddress);
}

function run() public {
console.log("\n=== Starting Custom Gas Token Setup ===\n");

vm.startBroadcast(msg.sender);

// Step 1: Deploy Mock OKB Token
console.log("Step 1: Deploying Mock OKB Token...");
deployMockOKB();

// Step 2: Deploy OKBBurner Implementation
console.log("\nStep 2: Deploying OKBBurner Implementation...");
deployBurnerImplementation();

// Step 3: Deploy DepositedOKBAdapter
console.log("\nStep 3: Deploying DepositedOKBAdapter...");
deployAdapter();

// Step 4: Set gas paying token in SystemConfig storage
console.log("\nStep 4: Setting gas paying token in SystemConfig storage...");
setGasPayingToken();

vm.stopBroadcast();

// Step 5: Verify all configurations
console.log("\n=== Verification Phase ===\n");
verifyL1Configuration();
}

/// @notice Deploy mock OKB token with 21M supply
function deployMockOKB() internal {
okbToken = new MockOKB();
console.log(" MockOKB deployed at:", address(okbToken));
console.log(" Token name:", okbToken.name());
console.log(" Token symbol:", okbToken.symbol());
console.log(" Token decimals:", okbToken.decimals());
console.log(" Total supply:", okbToken.totalSupply() / 1e18, "OKB");
console.log(" Deployer balance:", okbToken.balanceOf(deployerAddress) / 1e18, "OKB");
}

/// @notice Deploy OKBBurner implementation contract
function deployBurnerImplementation() internal {
burnerImplementation = new OKBBurner(address(okbToken)); // adapter address will be set later
console.log(" OKBBurner Implementation deployed at:", address(burnerImplementation));
console.log(" Burner OKB token:", address(burnerImplementation.OKB()));
}

/// @notice Deploy DepositedOKBAdapter
function deployAdapter() internal {
adapter = new DepositedOKBAdapter(
address(okbToken),
payable(optimismPortalProxy),
address(burnerImplementation)
);
console.log(" DepositedOKBAdapter deployed at:", address(adapter));
console.log(" Adapter name:", adapter.name());
console.log(" Adapter symbol:", adapter.symbol());
console.log(" OKB token:", address(adapter.OKB()));
console.log(" Portal:", address(adapter.PORTAL()));
console.log(" Burner implementation:", adapter.BURNER_IMPLEMENTATION());
}

/// @notice Set gas paying token in SystemConfig storage
/// @dev This writes to the GasPayingToken storage slots directly
function setGasPayingToken() internal {
ISystemConfig systemConfig = ISystemConfig(systemConfigProxy);
// adapter is the gas paying token
systemConfig.setGasPayingToken(address(adapter), TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL);
}

/// @notice Verify L1 configuration
function verifyL1Configuration() internal view {
console.log("Step 5: Verifying L1 Configuration...\n");

ISystemConfig systemConfig = ISystemConfig(systemConfigProxy);
// Check 1: SystemConfig isCustomGasToken
bool isCustomGasToken = systemConfig.isCustomGasToken();
console.log(" [CHECK 1] SystemConfig.isCustomGasToken():", isCustomGasToken);
require(isCustomGasToken, "FAILED: SystemConfig custom gas token not enabled");

// Check 2: SystemConfig gasPayingToken
(address tokenAddr, uint8 decimals) = systemConfig.gasPayingToken();
console.log(" [CHECK 2] SystemConfig.gasPayingToken():");
console.log(" Address:", tokenAddr);
console.log(" Decimals:", decimals);
require(tokenAddr == address(adapter), "FAILED: Token address mismatch");
require(decimals == 18, "FAILED: Token decimals must be 18");

// Check 4: DepositedOKBAdapter configuration
console.log(" [CHECK 4] DepositedOKBAdapter configuration:");
console.log(" OKB Token:", address(adapter.OKB()));
console.log(" Portal:", address(adapter.PORTAL()));
require(address(adapter.OKB()) == address(okbToken), "FAILED: Adapter OKB mismatch");
require(address(adapter.PORTAL()) == optimismPortalProxy, "FAILED: Adapter portal mismatch");

// Check 5: OKBBurner Implementation configuration
console.log(" [CHECK 5] OKBBurner Implementation configuration:");
console.log(" OKB Token:", address(burnerImplementation.OKB()));
require(address(burnerImplementation.OKB()) == address(okbToken), "FAILED: Burner OKB mismatch");

// Check 6: Adapter burner implementation reference
console.log(" [CHECK 6] Adapter burner implementation:");
console.log(" Burner Implementation:", adapter.BURNER_IMPLEMENTATION());
require(adapter.BURNER_IMPLEMENTATION() == address(burnerImplementation), "FAILED: Adapter burner implementation mismatch");

// Check 7: Adapter approval to portal
uint256 allowance = adapter.allowance(address(adapter), optimismPortalProxy);
console.log(" [CHECK 7] Adapter approval to Portal:", allowance);
require(allowance == type(uint256).max, "FAILED: Adapter should pre-approve portal");
}
}
Loading