diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index bb892f85bec8..a506116f33a0 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -28,6 +28,10 @@ func (r Receipt) MarshalJSON() ([]byte, error) { BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex hexutil.Uint `json:"transactionIndex"` + L1GasPrice *hexutil.Big `json:"l1GasPrice,omitempty"` + L1GasUsed *hexutil.Big `json:"l1GasUsed,omitempty"` + L1Fee *hexutil.Big `json:"l1Fee,omitempty"` + FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` } var enc Receipt enc.Type = hexutil.Uint64(r.Type) @@ -42,6 +46,10 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.BlockHash = r.BlockHash enc.BlockNumber = (*hexutil.Big)(r.BlockNumber) enc.TransactionIndex = hexutil.Uint(r.TransactionIndex) + enc.L1GasPrice = (*hexutil.Big)(r.L1GasPrice) + enc.L1GasUsed = (*hexutil.Big)(r.L1GasUsed) + enc.L1Fee = (*hexutil.Big)(r.L1Fee) + enc.FeeScalar = r.FeeScalar return json.Marshal(&enc) } @@ -60,6 +68,10 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { BlockHash *common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex *hexutil.Uint `json:"transactionIndex"` + L1GasPrice *hexutil.Big `json:"l1GasPrice,omitempty"` + L1GasUsed *hexutil.Big `json:"l1GasUsed,omitempty"` + L1Fee *hexutil.Big `json:"l1Fee,omitempty"` + FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` } var dec Receipt if err := json.Unmarshal(input, &dec); err != nil { @@ -106,5 +118,17 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { if dec.TransactionIndex != nil { r.TransactionIndex = uint(*dec.TransactionIndex) } + if dec.L1GasPrice != nil { + r.L1GasPrice = (*big.Int)(dec.L1GasPrice) + } + if dec.L1GasUsed != nil { + r.L1GasUsed = (*big.Int)(dec.L1GasUsed) + } + if dec.L1Fee != nil { + r.L1Fee = (*big.Int)(dec.L1Fee) + } + if dec.FeeScalar != nil { + r.FeeScalar = dec.FeeScalar + } return nil } diff --git a/core/types/receipt.go b/core/types/receipt.go index 7356f5a931dc..d04c22b0ae9b 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -69,6 +69,12 @@ type Receipt struct { BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *big.Int `json:"blockNumber,omitempty"` TransactionIndex uint `json:"transactionIndex"` + + // OVM legacy: extend receipts with their L1 price (if a rollup tx) + L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` + L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` + L1Fee *big.Int `json:"l1Fee,omitempty"` + FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` } type receiptMarshaling struct { @@ -79,6 +85,12 @@ type receiptMarshaling struct { GasUsed hexutil.Uint64 BlockNumber *hexutil.Big TransactionIndex hexutil.Uint + + // Optimism: extend receipts with their L1 price (if a rollup tx) + L1GasPrice *hexutil.Big + L1GasUsed *hexutil.Big + L1Fee *hexutil.Big + FeeScalar *big.Float } // receiptRLP is the consensus encoding of a receipt. @@ -438,5 +450,26 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu logIndex++ } } + if config.Optimism != nil && len(txs) >= 2 { // need at least an info tx and a non-info tx + if data := txs[0].Data(); len(data) >= 4+32*8 { // function selector + 8 arguments to setL1BlockValues + l1Basefee := new(big.Int).SetBytes(data[4+32*2 : 4+32*3]) // arg index 2 + overhead := new(big.Int).SetBytes(data[4+32*6 : 4+32*7]) // arg index 6 + scalar := new(big.Int).SetBytes(data[4+32*7 : 4+32*8]) // arg index 7 + fscalar := new(big.Float).SetInt(scalar) // legacy: format fee scalar as big Float + fdivisor := new(big.Float).SetUint64(1_000_000) // 10**6, i.e. 6 decimals + feeScalar := new(big.Float).Quo(fscalar, fdivisor) + for i := 0; i < len(rs); i++ { + if !txs[i].IsDepositTx() { + rs[i].L1GasPrice = l1Basefee + rs[i].L1GasUsed = new(big.Int).SetUint64(txs[i].RollupDataGas()) + rs[i].L1Fee = L1Cost(txs[i].RollupDataGas(), l1Basefee, overhead, scalar) + rs[i].FeeScalar = feeScalar + } + } + } else { + return fmt.Errorf("L1 info tx only has %d bytes, cannot read gas price parameters", len(data)) + } + } + return nil } diff --git a/core/types/rollup_l1_cost.go b/core/types/rollup_l1_cost.go index d812580f37cb..a93348bf9cfd 100644 --- a/core/types/rollup_l1_cost.go +++ b/core/types/rollup_l1_cost.go @@ -33,26 +33,20 @@ type RollupMessage interface { // Returns nil if there is no cost. type L1CostFunc func(blockNum uint64, msg RollupMessage) *big.Int -var big10 = big.NewInt(10) - var ( L1BaseFeeSlot = common.BigToHash(big.NewInt(1)) - OverheadSlot = common.BigToHash(big.NewInt(3)) - ScalarSlot = common.BigToHash(big.NewInt(4)) - DecimalsSlot = common.BigToHash(big.NewInt(5)) + OverheadSlot = common.BigToHash(big.NewInt(5)) + ScalarSlot = common.BigToHash(big.NewInt(6)) ) -var ( - OVM_GasPriceOracleAddr = common.HexToAddress("0x420000000000000000000000000000000000000F") - L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015") -) +var L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015") // NewL1CostFunc returns a function used for calculating L1 fee cost. // This depends on the oracles because gas costs can change over time. // It returns nil if there is no applicable cost function. func NewL1CostFunc(config *params.ChainConfig, statedb vm.StateDB) L1CostFunc { cacheBlockNum := ^uint64(0) - var l1BaseFee, overhead, scalar, decimals, divisor *big.Int + var l1BaseFee, overhead, scalar *big.Int return func(blockNum uint64, msg RollupMessage) *big.Int { rollupDataGas := msg.RollupDataGas() // Only fake txs for RPC view-calls are 0. if config.Optimism == nil || msg.IsDepositTx() || rollupDataGas == 0 { @@ -60,17 +54,18 @@ func NewL1CostFunc(config *params.ChainConfig, statedb vm.StateDB) L1CostFunc { } if blockNum != cacheBlockNum { l1BaseFee = statedb.GetState(L1BlockAddr, L1BaseFeeSlot).Big() - overhead = statedb.GetState(OVM_GasPriceOracleAddr, OverheadSlot).Big() - scalar = statedb.GetState(OVM_GasPriceOracleAddr, ScalarSlot).Big() - decimals = statedb.GetState(OVM_GasPriceOracleAddr, DecimalsSlot).Big() - divisor = new(big.Int).Exp(big10, decimals, nil) + overhead = statedb.GetState(L1BlockAddr, OverheadSlot).Big() + scalar = statedb.GetState(L1BlockAddr, ScalarSlot).Big() cacheBlockNum = blockNum } - l1GasUsed := new(big.Int).SetUint64(rollupDataGas) - l1GasUsed = l1GasUsed.Add(l1GasUsed, overhead) - l1Cost := l1GasUsed.Mul(l1GasUsed, l1BaseFee) - l1Cost = l1Cost.Mul(l1Cost, scalar) - l1Cost = l1Cost.Div(l1Cost, divisor) - return l1Cost + return L1Cost(rollupDataGas, l1BaseFee, overhead, scalar) } } + +func L1Cost(rollupDataGas uint64, l1BaseFee, overhead, scalar *big.Int) *big.Int { + l1GasUsed := new(big.Int).SetUint64(rollupDataGas) + l1GasUsed = l1GasUsed.Add(l1GasUsed, overhead) + l1Cost := l1GasUsed.Mul(l1GasUsed, l1BaseFee) + l1Cost = l1Cost.Mul(l1Cost, scalar) + return l1Cost.Div(l1Cost, big.NewInt(1_000_000)) +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e3f9b8c94cf0..181bcb0470a2 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1682,6 +1682,12 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common. "logsBloom": receipt.Bloom, "type": hexutil.Uint(tx.Type()), } + if s.b.ChainConfig().Optimism != nil && !tx.IsDepositTx() { + fields["l1GasPrice"] = (*hexutil.Big)(receipt.L1GasPrice) + fields["l1GasUsed"] = (*hexutil.Big)(receipt.L1GasUsed) + fields["l1Fee"] = (*hexutil.Big)(receipt.L1Fee) + fields["l1FeeScalar"] = receipt.FeeScalar.String() + } // Assign the effective gas price paid if !s.b.ChainConfig().IsLondon(bigblock) { fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())