Skip to content

Commit

Permalink
Merge branch 'develop' into chore/ocr3-data-feeds-telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
austinborn authored Sep 18, 2024
2 parents 88b9f93 + a9a4f74 commit 1cfcfc5
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 171 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-onions-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#changed Make Mantle use default OP stack l1 gas oracle in core
4 changes: 4 additions & 0 deletions core/chains/evm/gas/rollups/arbitrum_l1_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func (o *arbitrumL1Oracle) Name() string {
return o.logger.Name()
}

func (o *arbitrumL1Oracle) ChainType(_ context.Context) chaintype.ChainType {
return o.chainType
}

func (o *arbitrumL1Oracle) Start(ctx context.Context) error {
return o.StartOnce(o.Name(), func() error {
go o.run()
Expand Down
1 change: 1 addition & 0 deletions core/chains/evm/gas/rollups/l1_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type L1Oracle interface {

GasPrice(ctx context.Context) (*assets.Wei, error)
GetGasCost(ctx context.Context, tx *types.Transaction, blockNum *big.Int) (*assets.Wei, error)
ChainType(ctx context.Context) chaintype.ChainType
}

type l1OracleClient interface {
Expand Down
1 change: 0 additions & 1 deletion core/chains/evm/gas/rollups/l1_oracle_abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@ const OPBaseFeeScalarAbiString = `[{"inputs":[],"name":"baseFeeScalar","outputs"
const OPBlobBaseFeeAbiString = `[{"inputs":[],"name":"blobBaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
const OPBlobBaseFeeScalarAbiString = `[{"inputs":[],"name":"blobBaseFeeScalar","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]`
const OPDecimalsAbiString = `[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]`
const MantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
48 changes: 48 additions & 0 deletions core/chains/evm/gas/rollups/mocks/l1_oracle.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 4 additions & 82 deletions core/chains/evm/gas/rollups/op_l1_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,6 @@ const (
// decimals is a hex encoded call to:
// `function decimals() public pure returns (uint256);`
decimalsMethod = "decimals"
// tokenRatio fetches the tokenRatio used for Mantle's gas price calculation
// tokenRatio is a hex encoded call to:
// `function tokenRatio() public pure returns (uint256);`
tokenRatioMethod = "tokenRatio"
// OPGasOracleAddress is the address of the precompiled contract that exists on Optimism, Base and Mantle.
OPGasOracleAddress = "0x420000000000000000000000000000000000000F"
// KromaGasOracleAddress is the address of the precompiled contract that exists on Kroma.
Expand Down Expand Up @@ -192,16 +188,6 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy
return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", decimalsMethod, chainType, err)
}

// Encode calldata for tokenRatio method
tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString))
if err != nil {
return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", tokenRatioMethod, chainType, err)
}
tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod)
if err != nil {
return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", tokenRatioMethod, chainType, err)
}

return &optimismL1Oracle{
client: ethClient,
pollPeriod: PollPeriod,
Expand All @@ -223,7 +209,6 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy
blobBaseFeeCalldata: blobBaseFeeCalldata,
blobBaseFeeScalarCalldata: blobBaseFeeScalarCalldata,
decimalsCalldata: decimalsCalldata,
tokenRatioCalldata: tokenRatioCalldata,
isEcotoneCalldata: isEcotoneCalldata,
isEcotoneMethodAbi: isEcotoneMethodAbi,
isFjordCalldata: isFjordCalldata,
Expand All @@ -235,6 +220,10 @@ func (o *optimismL1Oracle) Name() string {
return o.logger.Name()
}

func (o *optimismL1Oracle) ChainType(_ context.Context) chaintype.ChainType {
return o.chainType
}

func (o *optimismL1Oracle) Start(ctx context.Context) error {
return o.StartOnce(o.Name(), func() error {
go o.run()
Expand Down Expand Up @@ -362,10 +351,6 @@ func (o *optimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transac
}

func (o *optimismL1Oracle) GetDAGasPrice(ctx context.Context) (*big.Int, error) {
if o.chainType == chaintype.ChainMantle {
return o.getMantleGasPrice(ctx)
}

err := o.checkForUpgrade(ctx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -463,69 +448,6 @@ func (o *optimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error)
return new(big.Int).SetBytes(b), nil
}

// Returns the gas price for Mantle. The formula is the same as Optimism Bedrock (getV1GasPrice), but the tokenRatio parameter is multiplied
func (o *optimismL1Oracle) getMantleGasPrice(ctx context.Context) (*big.Int, error) {
// call oracle to get l1BaseFee and tokenRatio
rpcBatchCalls := []rpc.BatchElem{
{
Method: "eth_call",
Args: []any{
map[string]interface{}{
"from": common.Address{},
"to": o.l1OracleAddress,
"data": hexutil.Bytes(o.l1BaseFeeCalldata),
},
"latest",
},
Result: new(string),
},
{
Method: "eth_call",
Args: []any{
map[string]interface{}{
"from": common.Address{},
"to": o.l1OracleAddress,
"data": hexutil.Bytes(o.tokenRatioCalldata),
},
"latest",
},
Result: new(string),
},
}

err := o.client.BatchCallContext(ctx, rpcBatchCalls)
if err != nil {
return nil, fmt.Errorf("fetch gas price parameters batch call failed: %w", err)
}
if rpcBatchCalls[0].Error != nil {
return nil, fmt.Errorf("%s call failed in a batch: %w", l1BaseFeeMethod, err)
}
if rpcBatchCalls[1].Error != nil {
return nil, fmt.Errorf("%s call failed in a batch: %w", tokenRatioMethod, err)
}

// Extract values from responses
l1BaseFeeResult := *(rpcBatchCalls[0].Result.(*string))
tokenRatioResult := *(rpcBatchCalls[1].Result.(*string))

// Decode the responses into bytes
l1BaseFeeBytes, err := hexutil.Decode(l1BaseFeeResult)
if err != nil {
return nil, fmt.Errorf("failed to decode %s rpc result: %w", l1BaseFeeMethod, err)
}
tokenRatioBytes, err := hexutil.Decode(tokenRatioResult)
if err != nil {
return nil, fmt.Errorf("failed to decode %s rpc result: %w", tokenRatioMethod, err)
}

// Convert bytes to big int for calculations
l1BaseFee := new(big.Int).SetBytes(l1BaseFeeBytes)
tokenRatio := new(big.Int).SetBytes(tokenRatioBytes)

// multiply l1BaseFee and tokenRatio and return
return new(big.Int).Mul(l1BaseFee, tokenRatio), nil
}

// Returns the scaled gas price using baseFeeScalar, l1BaseFee, blobBaseFeeScalar, and blobBaseFee fields from the oracle
// Confirmed the same calculation is used to determine gas price for both Ecotone and Fjord
func (o *optimismL1Oracle) getEcotoneFjordGasPrice(ctx context.Context) (*big.Int, error) {
Expand Down
88 changes: 0 additions & 88 deletions core/chains/evm/gas/rollups/op_l1_oracle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,94 +111,6 @@ func TestOPL1Oracle_ReadV1GasPrice(t *testing.T) {
}
}

func TestOPL1Oracle_ReadMantleGasPrice(t *testing.T) {
l1BaseFee := big.NewInt(100)
tokenRatio := big.NewInt(40)
oracleAddress := common.HexToAddress("0x1234").String()

t.Parallel()
t.Run("correctly fetches gas price if chain is Mantle", func(t *testing.T) {
// Encode calldata for l1BaseFee method
l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString))
require.NoError(t, err)
l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod)
require.NoError(t, err)

// Encode calldata for tokenRatio method
tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString))
require.NoError(t, err)
tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod)
require.NoError(t, err)

ethClient := mocks.NewL1OracleClient(t)
ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) {
rpcElements := args.Get(1).([]rpc.BatchElem)
require.Equal(t, 2, len(rpcElements))
for _, rE := range rpcElements {
require.Equal(t, "eth_call", rE.Method)
require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"])
require.Equal(t, "latest", rE.Args[1])
}
require.Equal(t, hexutil.Bytes(l1BaseFeeCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"])
require.Equal(t, hexutil.Bytes(tokenRatioCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"])

res1 := common.BigToHash(l1BaseFee).Hex()
res2 := common.BigToHash(tokenRatio).Hex()

rpcElements[0].Result = &res1
rpcElements[1].Result = &res2
}).Return(nil).Once()

oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress)
require.NoError(t, err)

gasPrice, err := oracle.GetDAGasPrice(tests.Context(t))
require.NoError(t, err)

assert.Equal(t, new(big.Int).Mul(l1BaseFee, tokenRatio), gasPrice)
})

t.Run("fetching Mantle price but rpc returns bad data", func(t *testing.T) {
ethClient := mocks.NewL1OracleClient(t)
ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) {
rpcElements := args.Get(1).([]rpc.BatchElem)
var badData = "zzz"
rpcElements[0].Result = &badData
rpcElements[1].Result = &badData
}).Return(nil).Once()

oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress)
require.NoError(t, err)
_, err = oracle.GetDAGasPrice(tests.Context(t))
assert.Error(t, err)
})

t.Run("fetching Mantle price but rpc parent call errors", func(t *testing.T) {
ethClient := mocks.NewL1OracleClient(t)
ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Return(fmt.Errorf("revert")).Once()

oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress)
require.NoError(t, err)
_, err = oracle.GetDAGasPrice(tests.Context(t))
assert.Error(t, err)
})

t.Run("fetching Mantle price but one of the sub rpc call errors", func(t *testing.T) {
ethClient := mocks.NewL1OracleClient(t)
ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) {
rpcElements := args.Get(1).([]rpc.BatchElem)
res := common.BigToHash(l1BaseFee).Hex()
rpcElements[0].Result = &res
rpcElements[1].Error = fmt.Errorf("revert")
}).Return(nil).Once()

oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress)
require.NoError(t, err)
_, err = oracle.GetDAGasPrice(tests.Context(t))
assert.Error(t, err)
})
}

func setupUpgradeCheck(t *testing.T, oracleAddress string, isFjord, isEcotone bool) *mocks.L1OracleClient {
trueHex := "0x0000000000000000000000000000000000000000000000000000000000000001"
falseHex := "0x0000000000000000000000000000000000000000000000000000000000000000"
Expand Down
4 changes: 4 additions & 0 deletions core/chains/evm/gas/rollups/zkSync_l1_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ func (o *zkSyncL1Oracle) Name() string {
return o.logger.Name()
}

func (o *zkSyncL1Oracle) ChainType(_ context.Context) chaintype.ChainType {
return o.chainType
}

func (o *zkSyncL1Oracle) Start(ctx context.Context) error {
return o.StartOnce(o.Name(), func() error {
go o.run()
Expand Down

0 comments on commit 1cfcfc5

Please sign in to comment.