diff --git a/eth/api.go b/eth/api.go index 95b915444a..da85be1848 100644 --- a/eth/api.go +++ b/eth/api.go @@ -715,6 +715,60 @@ func (api *ScrollAPI) GetNumSkippedTransactions(ctx context.Context) (uint64, er return rawdb.ReadNumSkippedTransactions(api.eth.ChainDb()), nil } +// SyncStatus includes L2 block sync height, L1 rollup sync height, +// L1 message sync height, and L2 finalized block height. +type SyncStatus struct { + L2BlockSyncHeight uint64 `json:"l2BlockSyncHeight,omitempty"` + L1RollupSyncHeight uint64 `json:"l1RollupSyncHeight,omitempty"` + L1MessageSyncHeight uint64 `json:"l1MessageSyncHeight,omitempty"` + L2FinalizedBlockHeight uint64 `json:"l2FinalizedBlockHeight,omitempty"` +} + +// SyncStatus returns the overall rollup status including L2 block sync height, L1 rollup sync height, +// L1 message sync height, and L2 finalized block height. +func (api *ScrollAPI) SyncStatus(_ context.Context) *SyncStatus { + status := &SyncStatus{} + + l2BlockHeader := api.eth.blockchain.CurrentHeader() + if l2BlockHeader != nil { + status.L2BlockSyncHeight = l2BlockHeader.Number.Uint64() + } + + l1RollupSyncHeightPtr := rawdb.ReadRollupEventSyncedL1BlockNumber(api.eth.ChainDb()) + if l1RollupSyncHeightPtr != nil { + status.L1RollupSyncHeight = *l1RollupSyncHeightPtr + } + + l1MessageSyncHeightPtr := rawdb.ReadSyncedL1BlockNumber(api.eth.ChainDb()) + if l1MessageSyncHeightPtr != nil { + status.L1MessageSyncHeight = *l1MessageSyncHeightPtr + } + + l2FinalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(api.eth.ChainDb()) + if l2FinalizedBlockHeightPtr != nil { + status.L2FinalizedBlockHeight = *l2FinalizedBlockHeightPtr + } + + return status +} + +// EstimateL1DataFee returns an estimate of the L1 data fee required to +// process the given transaction against the current pending block. +func (api *ScrollAPI) EstimateL1DataFee(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*hexutil.Uint64, error) { + bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + if blockNrOrHash != nil { + bNrOrHash = *blockNrOrHash + } + + l1DataFee, err := ethapi.EstimateL1MsgFee(ctx, api.eth.APIBackend, args, bNrOrHash, nil, 0, api.eth.APIBackend.RPCGasCap(), api.eth.APIBackend.ChainConfig()) + if err != nil { + return nil, fmt.Errorf("failed to estimate L1 data fee: %w", err) + } + + result := hexutil.Uint64(l1DataFee.Uint64()) + return &result, nil +} + // RPCTransaction is the standard RPC transaction return type with some additional skip-related fields. type RPCTransaction struct { ethapi.RPCTransaction diff --git a/eth/api_backend.go b/eth/api_backend.go index 7aa7525e97..b44510f6b9 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -76,6 +76,14 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentBlock().Header(), nil } + if number == rpc.FinalizedBlockNumber { + finalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(b.eth.ChainDb()) + if finalizedBlockHeightPtr == nil { + return nil, errors.New("L2 finalized block height not found in database") + } + number = rpc.BlockNumber(*finalizedBlockHeightPtr) + return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil + } return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil } @@ -110,6 +118,14 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentBlock(), nil } + if number == rpc.FinalizedBlockNumber { + finalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(b.eth.ChainDb()) + if finalizedBlockHeightPtr == nil { + return nil, errors.New("L2 finalized block height not found in database") + } + number = rpc.BlockNumber(*finalizedBlockHeightPtr) + return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil + } return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index c99e69c1e8..2e676ac7bc 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -170,6 +170,13 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block } else if pendingBlock == nil && lastBlock > headBlock { return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) } + if lastBlock == rpc.FinalizedBlockNumber { + if latestFinalizedHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber); err == nil { + lastBlock = rpc.BlockNumber(latestFinalizedHeader.Number.Uint64()) + } else { + return nil, nil, 0, 0, err + } + } // ensure not trying to retrieve before genesis if rpc.BlockNumber(blocks) > lastBlock+1 { blocks = int(lastBlock + 1) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index afe5a63b31..0b7502ca13 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -897,6 +897,13 @@ web3._extend({ call: 'scroll_getSkippedTransactionHashes', params: 2 }), + new web3._extend.Method({ + name: 'estimateL1DataFee', + call: 'scroll_estimateL1DataFee', + params: 2, + inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter], + outputFormatter: web3._extend.utils.toDecimal + }), ], properties: [ @@ -912,6 +919,10 @@ web3._extend({ name: 'numSkippedTransactions', getter: 'scroll_getNumSkippedTransactions' }), + new web3._extend.Property({ + name: 'syncStatus', + getter: 'scroll_syncStatus', + }), ] }); ` diff --git a/params/version.go b/params/version.go index 224a381a4e..ba9bfba05e 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 1 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 1 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string )