diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 450ffbc47d..8d4b3774e3 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -954,7 +954,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc config.BlockOverrides.Apply(&vmctx) } // Execute the trace - msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) + msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee(), vmctx.ExchangeRates) if err != nil { return nil, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 69828cfe8e..f14efb8a16 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -52,6 +52,8 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +var emptyExchangeRates = make(common.ExchangeRates) + // EthereumAPI provides an API to access Ethereum related information. type EthereumAPI struct { b Backend @@ -1161,14 +1163,14 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S defer cancel() // Get a new instance of the EVM. - msg, err := args.ToMessage(globalGasCap, header.BaseFee) - if err != nil { - return nil, err - } blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil, b.ChainConfig(), state) if blockOverrides != nil { blockOverrides.Apply(&blockCtx) } + msg, err := args.ToMessage(globalGasCap, header.BaseFee, blockCtx.ExchangeRates) + if err != nil { + return nil, err + } evm, vmError := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx) // Wait for the context to be done and cancel the evm. Even if the @@ -1821,7 +1823,19 @@ func AccessList(ctx context.Context, b CeloBackend, blockNrOrHash rpc.BlockNumbe statedb := db.Copy() // Set the accesslist to the last al args.AccessList = &accessList - msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee) + baseFee := header.BaseFee + + exchangeRates := emptyExchangeRates + if args.FeeCurrency != nil { + // Always use the header's parent here, since we want to create the list at the + // queried block, but want to use the exchange rates before (at the beginning of) + // the queried block + exchangeRates, err = b.GetExchangeRates(ctx, header.ParentHash) + if err != nil { + return nil, 0, nil, fmt.Errorf("get exchange rates for block: %v err: %w", header.Hash(), err) + } + } + msg, err := args.ToMessage(b.RPCGasCap(), baseFee, exchangeRates) if err != nil { return nil, 0, nil, err } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index e187fac427..7b2a0dc582 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -24,6 +24,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/exchange" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" @@ -60,8 +61,12 @@ type TransactionArgs struct { BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"` BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - // Celo specific (CIP-64) + // Celo specific + + // CIP-64, CIP-66 FeeCurrency *common.Address `json:"feeCurrency,omitempty"` + // CIP-66 + MaxFeeInFeeCurrency *hexutil.Big `json:"maxFeeInFeeCurrency,omitempty"` } // from retrieves the transaction sender address. @@ -124,6 +129,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b CeloBackend) err Value: args.Value, Data: (*hexutil.Bytes)(&data), AccessList: args.AccessList, + + FeeCurrency: args.FeeCurrency, + MaxFeeInFeeCurrency: args.MaxFeeInFeeCurrency, } latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap()) @@ -205,6 +213,12 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b CeloBackend) if err != nil { return err } + if args.IsFeeCurrencyDenominated() { + price, err = b.ConvertToCurrency(ctx, head.Hash(), price, args.FeeCurrency) + if err != nil { + return fmt.Errorf("can't convert suggested gasTipCap to fee-currency: %w", err) + } + } args.GasPrice = (*hexutil.Big)(price) } return nil @@ -216,6 +230,15 @@ func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *typ if args.BlobHashes != nil && args.BlobFeeCap == nil { // ExcessBlobGas must be set for a Cancun block. blobBaseFee := eip4844.CalcBlobFee(*head.ExcessBlobGas) + if args.IsFeeCurrencyDenominated() { + // wether the blob-fee will be used like that in Cel2 or not, + // at least this keeps it consistent with the rest of the gas-fees + var err error + blobBaseFee, err = b.ConvertToCurrency(ctx, head.Hash(), blobBaseFee, args.FeeCurrency) + if err != nil { + return fmt.Errorf("can't convert blob-fee to fee-currency: %w", err) + } + } // Set the max fee to be 2 times larger than the previous block's blob base fee. // The additional slack allows the tx to not become invalidated if the base // fee is rising. @@ -233,6 +256,12 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ if err != nil { return err } + if args.IsFeeCurrencyDenominated() { + tip, err = b.ConvertToCurrency(ctx, head.Hash(), tip, args.FeeCurrency) + if err != nil { + return fmt.Errorf("can't convert suggested gasTipCap to fee-currency: %w", err) + } + } args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) } // Set maxFeePerGas if it is missing. @@ -240,9 +269,17 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ // Set the max fee to be 2 times larger than the previous block's base fee. // The additional slack allows the tx to not become invalidated if the base // fee is rising. + baseFee := head.BaseFee + if args.IsFeeCurrencyDenominated() { + var err error + baseFee, err = b.ConvertToCurrency(ctx, head.Hash(), baseFee, args.FeeCurrency) + if err != nil { + return fmt.Errorf("can't convert base-fee to fee-currency: %w", err) + } + } val := new(big.Int).Add( args.MaxPriorityFeePerGas.ToInt(), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + new(big.Int).Mul(baseFee, big.NewInt(2)), ) args.MaxFeePerGas = (*hexutil.Big)(val) } @@ -256,7 +293,7 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. -func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*core.Message, error) { +func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, exchangeRates common.ExchangeRates) (*core.Message, error) { // Reject invalid combinations of pre- and post-1559 fee styles if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -308,6 +345,13 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes gasPrice = new(big.Int) if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { + if args.IsFeeCurrencyDenominated() { + var err error + baseFee, err = exchange.ConvertGoldToCurrency(exchangeRates, args.FeeCurrency, baseFee) + if err != nil { + return nil, err + } + } gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) } } @@ -417,3 +461,10 @@ func (args *TransactionArgs) ToTransaction() *types.Transaction { func (args *TransactionArgs) IsEIP4844() bool { return args.BlobHashes != nil || args.BlobFeeCap != nil } + +// IsFeeCurrencyDenominated returns wether the gas-price related +// fields are denominated in a given fee currency or in the native token. +// This effectively is only true for CIP-64 transactions. +func (args *TransactionArgs) IsFeeCurrencyDenominated() bool { + return args.FeeCurrency != nil && args.MaxFeeInFeeCurrency == nil +}