Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
820 changes: 820 additions & 0 deletions bindings/bindings/l1sequencer.go

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions contracts/contracts/l1/L1Sequencer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.24;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/// @title L1Sequencer
/// @notice L1 contract for managing the sequencer address.
/// The sequencer address can be updated by the owner (multisig recommended).
contract L1Sequencer is OwnableUpgradeable {
// ============ Storage ============

/// @notice Current sequencer address
address public sequencer;

// ============ Events ============

/// @notice Emitted when sequencer is updated
event SequencerUpdated(address indexed oldSequencer, address indexed newSequencer);

// ============ Initializer ============

/// @notice Initialize the contract
/// @param _owner Contract owner (multisig recommended)
/// @param _initialSequencer Initial sequencer address (can be address(0) to set later)
function initialize(address _owner, address _initialSequencer) external initializer {
require(_owner != address(0), "invalid owner");

__Ownable_init();
_transferOwnership(_owner);

// Set initial sequencer if provided
if (_initialSequencer != address(0)) {
sequencer = _initialSequencer;
emit SequencerUpdated(address(0), _initialSequencer);
}
}

// ============ Admin Functions ============

/// @notice Update sequencer address (takes effect immediately)
/// @param newSequencer New sequencer address
function updateSequencer(address newSequencer) external onlyOwner {
require(newSequencer != address(0), "invalid sequencer");
require(newSequencer != sequencer, "same sequencer");

address oldSequencer = sequencer;
sequencer = newSequencer;

emit SequencerUpdated(oldSequencer, newSequencer);
}

// ============ View Functions ============

/// @notice Get current sequencer address
function getSequencer() external view returns (address) {
return sequencer;
}
}
9 changes: 9 additions & 0 deletions contracts/deploy/013-DeployProxys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const deployContractProxies = async (

const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName
const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName
const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName

const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName
const L1ETHGatewayProxyStorageName = ProxyStorageName.L1ETHGatewayProxyStorageName
Expand Down Expand Up @@ -112,6 +113,13 @@ export const deployContractProxies = async (
return err
}

// ************************ sequencer contracts deploy ************************
// L1SequencerProxy deploy
err = await deployContractProxyByStorageName(hre, path, deployer, L1SequencerProxyStorageName)
if (err != "") {
return err
}

// ************************ rollup contracts deploy ************************
// RollupProxy deploy
err = await deployContractProxyByStorageName(hre, path, deployer, RollupProxyStorageName)
Expand Down Expand Up @@ -274,6 +282,7 @@ export const deployContractProxiesConcurrently = async (
ProxyStorageName.L1CrossDomainMessengerProxyStorageName,
ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName,
ProxyStorageName.L1StakingProxyStorageName,
ProxyStorageName.L1SequencerProxyStorageName,
ProxyStorageName.RollupProxyStorageName,
ProxyStorageName.L1GatewayRouterProxyStorageName,
ProxyStorageName.L1ETHGatewayProxyStorageName,
Expand Down
20 changes: 20 additions & 0 deletions contracts/deploy/014-DeployImpls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ export const deployContractImplsConcurrently = async (

deployPromises.push(deployContract(L1StakingFactoryName, StakingImplStorageName, [L1CrossDomainMessengerProxyAddress]))

// L1Sequencer deploy (no constructor args)
const L1SequencerFactoryName = ContractFactoryName.L1Sequencer
const L1SequencerImplStorageName = ImplStorageName.L1SequencerStorageName
deployPromises.push(deployContract(L1SequencerFactoryName, L1SequencerImplStorageName))

const results = await Promise.all(deployPromises)

for (const result of results) {
Expand Down Expand Up @@ -382,6 +387,21 @@ export const deployContractImpls = async (
return err
}

// ************************ sequencer contracts deploy ************************
// L1Sequencer deploy
const L1SequencerFactoryName = ContractFactoryName.L1Sequencer
const L1SequencerImplStorageName = ImplStorageName.L1SequencerStorageName
Factory = await hre.ethers.getContractFactory(L1SequencerFactoryName)
contract = await Factory.deploy()
await contract.deployed()
console.log("%s=%s ; TX_HASH: %s", L1SequencerImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash)
blockNumber = await hre.ethers.provider.getBlockNumber()
console.log("BLOCK_NUMBER: %s", blockNumber)
err = await storage(path, L1SequencerImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0)
if (err != '') {
return err
}

// return
return ''
}
Expand Down
9 changes: 9 additions & 0 deletions contracts/deploy/019-AdminTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const AdminTransferConcurrently = async (
ProxyStorageName.L1CrossDomainMessengerProxyStorageName,
ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName,
ProxyStorageName.L1StakingProxyStorageName,
ProxyStorageName.L1SequencerProxyStorageName, // Added L1Sequencer
ProxyStorageName.RollupProxyStorageName,
ProxyStorageName.L1GatewayRouterProxyStorageName,
ProxyStorageName.L1ETHGatewayProxyStorageName,
Expand Down Expand Up @@ -159,6 +160,7 @@ export const AdminTransfer = async (

const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName
const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName
const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName

const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName
const L1ETHGatewayProxyStorageName = ProxyStorageName.L1ETHGatewayProxyStorageName
Expand Down Expand Up @@ -192,6 +194,13 @@ export const AdminTransfer = async (
return err
}

// ************************ sequencer contracts admin change ************************
// L1SequencerProxy admin change
err = await AdminTransferByProxyStorageName(hre, path, deployer, L1SequencerProxyStorageName)
if (err != '') {
return err
}

// ************************ rollup contracts admin change ************************
// RollupProxy admin change
err = await AdminTransferByProxyStorageName(hre, path, deployer, RollupProxyStorageName)
Expand Down
89 changes: 89 additions & 0 deletions contracts/deploy/022-SequencerInit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import "@nomiclabs/hardhat-web3";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";

import {
HardhatRuntimeEnvironment
} from 'hardhat/types';
import { assertContractVariable, getContractAddressByName, awaitCondition } from "../src/deploy-utils";
import { ethers } from 'ethers'

import {
ImplStorageName,
ProxyStorageName,
ContractFactoryName,
} from "../src/types"

export const SequencerInit = async (
hre: HardhatRuntimeEnvironment,
path: string,
deployer: any,
configTmp: any
): Promise<string> => {
// L1Sequencer addresses
const L1SequencerProxyAddress = getContractAddressByName(path, ProxyStorageName.L1SequencerProxyStorageName)
const L1SequencerImplAddress = getContractAddressByName(path, ImplStorageName.L1SequencerStorageName)
const L1SequencerFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1Sequencer)

const IL1SequencerProxy = await hre.ethers.getContractAt(ContractFactoryName.DefaultProxyInterface, L1SequencerProxyAddress, deployer)

if (
(await IL1SequencerProxy.implementation()).toLocaleLowerCase() !== L1SequencerImplAddress.toLocaleLowerCase()
) {
console.log('Upgrading the L1Sequencer proxy...')

// Owner is the deployer (will be transferred to multisig in production)
const owner = await deployer.getAddress()

// Get initial sequencer address from config (first sequencer address)
// Note: l2SequencerAddresses is defined in contracts/src/deploy-config/l1.ts
const initialSequencer = (configTmp.l2SequencerAddresses && configTmp.l2SequencerAddresses.length > 0)
? configTmp.l2SequencerAddresses[0]
: ethers.constants.AddressZero

console.log('Initial sequencer address:', initialSequencer)

// Upgrade and initialize the proxy with owner and initial sequencer
// Note: We set sequencer in initialize() to avoid TransparentUpgradeableProxy admin restriction
await IL1SequencerProxy.upgradeToAndCall(
L1SequencerImplAddress,
L1SequencerFactory.interface.encodeFunctionData('initialize', [owner, initialSequencer])
)

await awaitCondition(
async () => {
return (
(await IL1SequencerProxy.implementation()).toLocaleLowerCase() === L1SequencerImplAddress.toLocaleLowerCase()
)
},
3000,
1000
)

const contractTmp = new ethers.Contract(
L1SequencerProxyAddress,
L1SequencerFactory.interface,
deployer,
)

await assertContractVariable(
contractTmp,
'owner',
owner,
)

if (initialSequencer !== ethers.constants.AddressZero) {
await assertContractVariable(
contractTmp,
'sequencer',
initialSequencer,
)
console.log('L1SequencerProxy upgrade success, initial sequencer set:', initialSequencer)
} else {
console.log('L1SequencerProxy upgrade success (no initial sequencer set)')
}
}
return ''
}

export default SequencerInit
4 changes: 3 additions & 1 deletion contracts/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import StakingInit from './018-StakingInit'
import {AdminTransfer,AdminTransferByProxyStorageName, AdminTransferConcurrently} from './019-AdminTransfer'
import ContractInit from './020-ContractInit'
import StakingRegister from './021-StakingRegister'
import SequencerInit from './022-SequencerInit'


export {
Expand All @@ -28,5 +29,6 @@ export {
AdminTransferByProxyStorageName,
AdminTransferConcurrently,
ContractInit,
StakingRegister
StakingRegister,
SequencerInit
}
6 changes: 6 additions & 0 deletions contracts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const ContractFactoryName = {
MultipleVersionRollupVerifier: 'MultipleVersionRollupVerifier',
// staking
L1Staking: 'L1Staking',
// L1 sequencer
L1Sequencer: 'L1Sequencer',
// gateway
L1GatewayRouter: 'L1GatewayRouter',
L1StandardERC20Gateway: 'L1StandardERC20Gateway',
Expand All @@ -40,6 +42,8 @@ const ProxyStorageName = {
RollupProxyStorageName: 'Proxy__Rollup',
// staking
L1StakingProxyStorageName: 'Proxy__L1Staking',
// L1 sequencer
L1SequencerProxyStorageName: 'Proxy__L1Sequencer',
// gateway
L1GatewayRouterProxyStorageName: 'Proxy__L1GatewayRouter',
L1StandardERC20GatewayProxyStorageName: 'Proxy__L1StandardERC20Gateway',
Expand Down Expand Up @@ -71,6 +75,8 @@ const ImplStorageName = {
MultipleVersionRollupVerifierStorageName: 'Impl__MultipleVersionRollupVerifier',
// staking
L1StakingStorageName: 'Impl__L1Staking',
// L1 sequencer
L1SequencerStorageName: 'Impl__L1Sequencer',
// gateway
L1GatewayRouterStorageName: 'Impl__L1GatewayRouter',
L1StandardERC20GatewayStorageName: 'Impl__L1StandardERC20Gateway',
Expand Down
7 changes: 7 additions & 0 deletions contracts/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
AdminTransferConcurrently,
ContractInit,
StakingRegister,
SequencerInit,
} from '../deploy/index'
import { ethers } from "ethers";

Expand Down Expand Up @@ -120,6 +121,12 @@ task("initialize")
console.log('Staking init failed, err: ', err)
return
}
console.log('\n---------------------------------- Sequencer init ----------------------------------')
err = await SequencerInit(hre, storagePath, deployer, config)
if (err != '') {
console.log('Sequencer init failed, err: ', err)
return
}
console.log('\n---------------------------------- Admin Transfer ----------------------------------')
if (concurrent === 'true') {
err = await AdminTransferConcurrently(hre, storagePath, deployer, config)
Expand Down
3 changes: 0 additions & 3 deletions node/blocktag/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const (

// Config holds the configuration for BlockTagService
type Config struct {
L1Addr string
RollupAddress common.Address
SafeConfirmations uint64
PollInterval time.Duration
Expand All @@ -36,8 +35,6 @@ func DefaultConfig() *Config {

// SetCliContext sets the configuration from CLI context
func (c *Config) SetCliContext(ctx *cli.Context) error {
c.L1Addr = ctx.GlobalString(flags.L1NodeAddr.Name)

// Determine RollupAddress: use explicit flag, or mainnet default, or error
if ctx.GlobalBool(flags.MainnetFlag.Name) {
c.RollupAddress = node.MainnetRollupContractAddress
Expand Down
11 changes: 3 additions & 8 deletions node/blocktag/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,18 @@ type BlockTagService struct {
// NewBlockTagService creates a new BlockTagService
func NewBlockTagService(
ctx context.Context,
l1Client *ethclient.Client,
l2Client *types.RetryableClient,
config *Config,
logger tmlog.Logger,
) (*BlockTagService, error) {
if config.L1Addr == "" {
return nil, fmt.Errorf("L1 RPC address is required")
if l1Client == nil {
return nil, fmt.Errorf("L1 client is required")
}
if config.RollupAddress == (common.Address{}) {
return nil, fmt.Errorf("Rollup contract address is required")
}

l1Client, err := ethclient.Dial(config.L1Addr)
if err != nil {
return nil, fmt.Errorf("failed to connect to L1: %w", err)
}

rollup, err := bindings.NewRollup(config.RollupAddress, l1Client)
if err != nil {
return nil, fmt.Errorf("failed to create rollup binding: %w", err)
Expand Down Expand Up @@ -122,7 +118,6 @@ func (s *BlockTagService) Stop() {
s.logger.Info("Stopping BlockTagService")
s.cancel()
<-s.stop
s.l1Client.Close()
s.logger.Info("BlockTagService stopped")
}

Expand Down
Loading
Loading