diff --git a/core/chains/evm/gas/rollups/op_l1_oracle.go b/core/chains/evm/gas/rollups/op_l1_oracle.go index ae8bc054b7a..5643dc1a661 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle.go @@ -104,43 +104,43 @@ func NewOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy // Encode calldata for l1BaseFee method l1BaseFeeCalldata, _, err := encodeCalldata(L1BaseFeeAbiString, l1BaseFeeMethod) if err != nil { - return nil, fmt.Errorf("failed to encode l1BaseFee calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", l1BaseFeeMethod, chainType, err) } // Encode calldata for isEcotone method isEcotoneCalldata, isEcotoneMethodAbi, err := encodeCalldata(OPIsEcotoneAbiString, isEcotoneMethod) if err != nil { - return nil, fmt.Errorf("failed to encode isEcotone calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", isEcotoneMethod, chainType, err) } // Encode calldata for isFjord method isFjordCalldata, isFjordMethodAbi, err := encodeCalldata(OPIsFjordAbiString, isFjordMethod) if err != nil { - return nil, fmt.Errorf("failed to encode isFjord calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", isFjordMethod, chainType, err) } // Encode calldata for baseFeeScalar method baseFeeScalarCalldata, _, err := encodeCalldata(OPBaseFeeScalarAbiString, baseFeeScalarMethod) if err != nil { - return nil, fmt.Errorf("failed to encode baseFeeScalar calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", baseFeeScalarMethod, chainType, err) } // Encode calldata for blobBaseFee method blobBaseFeeCalldata, _, err := encodeCalldata(OPBlobBaseFeeAbiString, blobBaseFeeMethod) if err != nil { - return nil, fmt.Errorf("failed to encode blobBaseFee calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", blobBaseFeeMethod, chainType, err) } // Encode calldata for blobBaseFeeScalar method blobBaseFeeScalarCalldata, _, err := encodeCalldata(OPBlobBaseFeeScalarAbiString, blobBaseFeeScalarMethod) if err != nil { - return nil, fmt.Errorf("failed to encode blobBaseFeeScalar calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", blobBaseFeeScalarMethod, chainType, err) } // Encode calldata for decimals method decimalsCalldata, _, err := encodeCalldata(OPDecimalsAbiString, decimalsMethod) if err != nil { - return nil, fmt.Errorf("failed to encode decimals calldata: %w", err) + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", decimalsMethod, chainType, err) } return &optimismL1Oracle{ diff --git a/core/chains/evm/gas/rollups/op_l1_oracle_test.go b/core/chains/evm/gas/rollups/op_l1_oracle_test.go index 62c85732aa0..666407e2c77 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle_test.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle_test.go @@ -348,3 +348,60 @@ func TestOPL1Oracle_CalculateFjordGasPrice(t *testing.T) { assert.Error(t, err) }) } + +func TestOPL1Oracle_CalculateCustomCalldataGasPrice(t *testing.T) { + baseFee := big.NewInt(100000000) + blobBaseFee := big.NewInt(25000000) + baseFeeScalar := big.NewInt(10) + blobBaseFeeScalar := big.NewInt(5) + decimals := big.NewInt(6) + oracleAddress := common.HexToAddress("0x0000000000000000000000000000000044433322").String() + + t.Parallel() + + t.Run("correctly fetches gas price if chain has custom calldata", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, false) + expectedPriceHex := "0x0000000000000000000000000000000000000000000000000000000000000032" + + daOracle := CreateTestDAOracle(t, toml.OPOracle, oracleAddress, "0x0000000000000000000000000000000000001234") + oracle, err := NewOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, daOracle) + require.NoError(t, err) + + ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { + callMsg := args.Get(1).(ethereum.CallMsg) + blockNumber := args.Get(2).(*big.Int) + require.NotNil(t, callMsg.To) + require.Equal(t, oracleAddress, callMsg.To.String()) + require.Nil(t, blockNumber) + }).Return(hexutil.MustDecode(expectedPriceHex), nil).Once() + + price, err := oracle.GetDAGasPrice(tests.Context(t)) + require.NoError(t, err) + require.Equal(t, big.NewInt(50), price) + }) + + t.Run("malformed custom API response falls back to default OP gas oracle API", func(t *testing.T) { + ethClient := setupUpgradeCheck(t, oracleAddress, true, false) + mockBatchContractCall(t, ethClient, oracleAddress, baseFee, baseFeeScalar, blobBaseFee, blobBaseFeeScalar, decimals) + malformedCustomGasAPIResponse := "0x0000000000000000000000000000000000000032" + + daOracle := CreateTestDAOracle(t, toml.OPOracle, oracleAddress, "0x0000000000000000000000000000000000001234") + oracle, err := NewOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainOptimismBedrock, daOracle) + require.NoError(t, err) + + ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { + callMsg := args.Get(1).(ethereum.CallMsg) + blockNumber := args.Get(2).(*big.Int) + require.NotNil(t, callMsg.To) + require.Equal(t, oracleAddress, callMsg.To.String()) + require.Nil(t, blockNumber) + }).Return(hexutil.MustDecode(malformedCustomGasAPIResponse), nil).Once() + + gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) + require.NoError(t, err) + scaledGasPrice := big.NewInt(16125000000) // baseFee * scalar * 16 + blobBaseFee * scalar + scale := big.NewInt(16000000) // Scaled by 16 * 10 ^ decimals + expectedGasPrice := new(big.Int).Div(scaledGasPrice, scale) + assert.Equal(t, expectedGasPrice, gasPrice) + }) +}