Skip to content

Commit

Permalink
KS-398 gas limit on tx meta (#14214)
Browse files Browse the repository at this point in the history
* change to make gas limit sendeable on transaction submit and make the default limit configurable

* fix tests

* fix docs test

---------

Co-authored-by: Bolek Kulbabinski <[email protected]>
  • Loading branch information
ettec and bolekk authored Aug 23, 2024
1 parent af335c1 commit 32a2ccd
Show file tree
Hide file tree
Showing 23 changed files with 147 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/mighty-points-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal allow gas limit to be specified when submitting transaction
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl
require.NoError(t, err)

targetCapabilityConfig := newCapabilityConfig()

configWithLimit, err := values.WrapMap(map[string]any{"gasLimit": 500000})
require.NoError(t, err)

targetCapabilityConfig.DefaultConfig = values.Proto(configWithLimit).GetMapValue()

targetCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTargetConfig{
RemoteTargetConfig: &pb.RemoteTargetConfig{
RequestHashExcludedAttributes: []string{"signed_report.Signatures"},
Expand Down
32 changes: 25 additions & 7 deletions core/capabilities/targets/write_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func decodeReportMetadata(data []byte) (metadata ReportV1Metadata, err error) {
type Config struct {
// Address of the contract that will get the forwarded report
Address string
// Optional gas limit that overrides the default limit sent to the chain writer
GasLimit *uint64
}

type Inputs struct {
Expand Down Expand Up @@ -222,19 +224,23 @@ func (cap *WriteTarget) Execute(ctx context.Context, rawRequest capabilities.Cap

switch {
case transmissionInfo.State == 0: // NOT_ATTEMPTED
cap.lggr.Infow("non-empty report - tranasmission not attempted - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID)
cap.lggr.Infow("non-empty report - transmission not attempted - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID)
case transmissionInfo.State == 1: // SUCCEEDED
cap.lggr.Infow("returning without a tranmission attempt - report already onchain ", "executionID", request.Metadata.WorkflowExecutionID)
cap.lggr.Infow("returning without a transmission attempt - report already onchain ", "executionID", request.Metadata.WorkflowExecutionID)
return success(), nil
case transmissionInfo.State == 2: // INVALID_RECEIVER
cap.lggr.Infow("returning without a tranmission attempt - transmission already attempted, receiver was marked as invalid", "executionID", request.Metadata.WorkflowExecutionID)
cap.lggr.Infow("returning without a transmission attempt - transmission already attempted, receiver was marked as invalid", "executionID", request.Metadata.WorkflowExecutionID)
return success(), nil
case transmissionInfo.State == 3: // FAILED
if transmissionInfo.GasLimit.Uint64() > cap.receiverGasMinimum {
cap.lggr.Infow("returning without a tranmission attempt - transmission already attempted and failed, sufficient gas was provided", "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", cap.receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit)
receiverGasMinimum := cap.receiverGasMinimum
if request.Config.GasLimit != nil {
receiverGasMinimum = *request.Config.GasLimit - FORWARDER_CONTRACT_LOGIC_GAS_COST
}
if transmissionInfo.GasLimit.Uint64() > receiverGasMinimum {
cap.lggr.Infow("returning without a transmission attempt - transmission already attempted and failed, sufficient gas was provided", "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit)
return success(), nil
} else {
cap.lggr.Infow("non-empty report - retrying a failed transmission - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", cap.receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit)
cap.lggr.Infow("non-empty report - retrying a failed transmission - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit)
}
default:
return nil, fmt.Errorf("unexpected transmission state: %v", transmissionInfo.State)
Expand Down Expand Up @@ -269,10 +275,22 @@ func (cap *WriteTarget) Execute(ctx context.Context, rawRequest capabilities.Cap
cap.lggr.Debugw("Transaction raw report", "report", hex.EncodeToString(req.RawReport))

meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID}
if request.Config.GasLimit != nil {
meta.GasLimit = new(big.Int).SetUint64(*request.Config.GasLimit)
}

value := big.NewInt(0)
if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", req, txID.String(), cap.forwarderAddress, &meta, value); err != nil {
return nil, err
if commontypes.ErrSettingTransactionGasLimitNotSupported.Is(err) {
meta.GasLimit = nil
if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", req, txID.String(), cap.forwarderAddress, &meta, value); err != nil {
return nil, fmt.Errorf("failed to submit transaction: %w", err)
}
} else {
return nil, fmt.Errorf("failed to submit transaction: %w", err)
}
}

cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", txID)
return success(), nil
}
Expand Down
59 changes: 54 additions & 5 deletions core/capabilities/targets/write_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestWriteTarget(t *testing.T) {
TransmissionId: [32]byte{},
Transmitter: common.HexToAddress("0x0"),
}
}).Once()
})

cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(nil).Once()

Expand All @@ -103,25 +103,74 @@ func TestWriteTarget(t *testing.T) {
require.NotNil(t, response)
})

t.Run("fails when ChainReader's GetLatestValue returns error", func(t *testing.T) {
t.Run("fails when ChainWriter's SubmitTransaction returns error", func(t *testing.T) {
req := capabilities.CapabilityRequest{
Metadata: validMetadata,
Config: config,
Inputs: validInputs,
}
cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmissionInfo", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("reader error"))
cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(errors.New("writer error"))

_, err = writeTarget.Execute(ctx, req)
require.Error(t, err)
})

t.Run("fails when ChainWriter's SubmitTransaction returns error", func(t *testing.T) {
t.Run("passes gas limit set on config to the chain writer", func(t *testing.T) {
configGasLimit, err := values.NewMap(map[string]any{
"Address": forwarderAddr,
"GasLimit": 500000,
})
require.NoError(t, err)
req := capabilities.CapabilityRequest{
Metadata: validMetadata,
Config: configGasLimit,
Inputs: validInputs,
}

meta := types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID, GasLimit: big.NewInt(500000)}
cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(types.ErrSettingTransactionGasLimitNotSupported)

_, err2 := writeTarget.Execute(ctx, req)
require.Error(t, err2)
})

t.Run("retries without gas limit when ChainWriter's SubmitTransaction returns error due to gas limit not supported", func(t *testing.T) {
configGasLimit, err := values.NewMap(map[string]any{
"Address": forwarderAddr,
"GasLimit": 500000,
})
require.NoError(t, err)
req := capabilities.CapabilityRequest{
Metadata: validMetadata,
Config: configGasLimit,
Inputs: validInputs,
}

meta := types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID, GasLimit: big.NewInt(500000)}
cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(types.ErrSettingTransactionGasLimitNotSupported)
meta = types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID}
cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(nil)

configGasLimit, err = values.NewMap(map[string]any{
"Address": forwarderAddr,
})
req = capabilities.CapabilityRequest{
Metadata: validMetadata,
Config: configGasLimit,
Inputs: validInputs,
}

_, err2 := writeTarget.Execute(ctx, req)
require.Error(t, err2)
})

t.Run("fails when ChainReader's GetLatestValue returns error", func(t *testing.T) {
req := capabilities.CapabilityRequest{
Metadata: validMetadata,
Config: config,
Inputs: validInputs,
}
cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(errors.New("writer error"))
cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmissionInfo", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("reader error"))

_, err = writeTarget.Execute(ctx, req)
require.Error(t, err)
Expand Down
4 changes: 4 additions & 0 deletions core/chains/evm/config/chain_scoped_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ func (b *workflowConfig) FromAddress() *types.EIP55Address {
func (b *workflowConfig) ForwarderAddress() *types.EIP55Address {
return b.c.ForwarderAddress
}

func (b *workflowConfig) DefaultGasLimit() uint64 {
return b.c.DefaultGasLimit
}
1 change: 1 addition & 0 deletions core/chains/evm/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type BlockHistory interface {
type Workflow interface {
FromAddress() *types.EIP55Address
ForwarderAddress() *types.EIP55Address
DefaultGasLimit() uint64
}

type NodePool interface {
Expand Down
15 changes: 9 additions & 6 deletions core/chains/evm/config/toml/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ func (a *Automation) setFrom(f *Automation) {
type Workflow struct {
FromAddress *types.EIP55Address `toml:",omitempty"`
ForwarderAddress *types.EIP55Address `toml:",omitempty"`
DefaultGasLimit uint64
}

func (m *Workflow) setFrom(f *Workflow) {
Expand All @@ -530,6 +531,8 @@ func (m *Workflow) setFrom(f *Workflow) {
if v := f.ForwarderAddress; v != nil {
m.ForwarderAddress = v
}

m.DefaultGasLimit = f.DefaultGasLimit
}

type BalanceMonitor struct {
Expand All @@ -549,12 +552,12 @@ type GasEstimator struct {
PriceMax *assets.Wei
PriceMin *assets.Wei

LimitDefault *uint64
LimitMax *uint64
LimitMultiplier *decimal.Decimal
LimitTransfer *uint64
LimitJobType GasLimitJobType `toml:",omitempty"`
EstimateGasLimit *bool
LimitDefault *uint64
LimitMax *uint64
LimitMultiplier *decimal.Decimal
LimitTransfer *uint64
LimitJobType GasLimitJobType `toml:",omitempty"`
EstimateGasLimit *bool

BumpMin *assets.Wei
BumpPercent *uint16
Expand Down
3 changes: 3 additions & 0 deletions core/chains/evm/config/toml/defaults/fallback.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,6 @@ ObservationGracePeriod = '1s'

[OCR2.Automation]
GasLimit = 5400000

[Workflow]
DefaultGasLimit = 400_000
2 changes: 2 additions & 0 deletions core/config/docs/chains-evm.toml
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,5 @@ GasLimit = 5400000 # Default
FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example
# ForwarderAddress is the keystone forwarder contract address on chain.
ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example
# DefaultGasLimit is the default gas limit for workflow transactions.
DefaultGasLimit = 400_000 # Default
3 changes: 3 additions & 0 deletions core/config/docs/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ func TestDoc(t *testing.T) {
docDefaults.OperatorFactoryAddress = nil
require.Empty(t, docDefaults.Workflow.FromAddress)
require.Empty(t, docDefaults.Workflow.ForwarderAddress)
require.Equal(t, uint64(400_000), docDefaults.Workflow.DefaultGasLimit)

docDefaults.Workflow.FromAddress = nil
docDefaults.Workflow.ForwarderAddress = nil
docDefaults.Workflow.DefaultGasLimit = uint64(400_000)
docDefaults.NodePool.Errors = evmcfg.ClientErrors{}

// Transactions.AutoPurge configs are only set if the feature is enabled
Expand Down
2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/prometheus/client_golang v1.17.0
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-automation v1.0.4
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823093917-c07a4fa0caa5
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823143943-86fc7c5deb84
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7
github.com/spf13/cobra v1.8.0
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1186,8 +1186,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8um
github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 h1:LAgJTg9Yr/uCo2g7Krp88Dco2U45Y6sbJVl8uKoLkys=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95/go.mod h1:/ZWraCBaDDgaIN1prixYcbVvIk/6HeED9+8zbWQ+TMo=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823093917-c07a4fa0caa5 h1:+XvRzgHlcaZYLMJ5HR3HzOjvXNmpVKQFZbuHZiRno68=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823093917-c07a4fa0caa5/go.mod h1:5rmU5YKBkIOwWkuNZi26sMXlBUBm6weBFXh+8BEEp2s=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823143943-86fc7c5deb84 h1:W8jK09xMKjhnduR4FsyM2aQKe+4/K1EsAhfJQgv2DEk=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823143943-86fc7c5deb84/go.mod h1:5rmU5YKBkIOwWkuNZi26sMXlBUBm6weBFXh+8BEEp2s=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw=
Expand Down
7 changes: 6 additions & 1 deletion core/services/relay/evm/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,16 @@ func (w *chainWriter) SubmitTransaction(ctx context.Context, contract, method st
}
}

gasLimit := methodConfig.GasLimit
if meta != nil && meta.GasLimit != nil {
gasLimit = meta.GasLimit.Uint64()
}

req := evmtxmgr.TxRequest{
FromAddress: methodConfig.FromAddress,
ToAddress: common.HexToAddress(toAddress),
EncodedPayload: calldata,
FeeLimit: methodConfig.GasLimit,
FeeLimit: gasLimit,
Meta: txMeta,
IdempotencyKey: &transactionID,
Strategy: w.sendStrategy,
Expand Down
3 changes: 2 additions & 1 deletion core/services/relay/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R
// Initialize write target capability if configuration is defined
if chain.Config().EVM().Workflow().ForwarderAddress() != nil {
ctx := context.Background()
capability, err := NewWriteTarget(ctx, relayer, chain, lggr)
capability, err := NewWriteTarget(ctx, relayer, chain, chain.Config().EVM().Workflow().DefaultGasLimit(),
lggr)
if err != nil {
return nil, fmt.Errorf("failed to initialize write target: %w", err)
}
Expand Down
7 changes: 3 additions & 4 deletions core/services/relay/evm/write_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
)

func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain, lggr logger.Logger) (*targets.WriteTarget, error) {
func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain, defaultGasLimit uint64, lggr logger.Logger) (*targets.WriteTarget, error) {
// generate ID based on chain selector
id := fmt.Sprintf("write_%[email protected]", chain.ID())
chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64())
Expand Down Expand Up @@ -47,7 +47,6 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain
return nil, err
}

var gasLimit uint64 = 400_000
chainWriterConfig := relayevmtypes.ChainWriterConfig{
Contracts: map[string]*relayevmtypes.ContractConfig{
"forwarder": {
Expand All @@ -57,7 +56,7 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain
ChainSpecificName: "report",
Checker: "simulate",
FromAddress: config.FromAddress().Address(),
GasLimit: gasLimit,
GasLimit: defaultGasLimit,
},
},
},
Expand All @@ -75,5 +74,5 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain
return nil, err
}

return targets.NewWriteTarget(logger.Named(lggr, "WriteTarget"), id, cr, cw, config.ForwarderAddress().String(), gasLimit), nil
return targets.NewWriteTarget(logger.Named(lggr, "WriteTarget"), id, cr, cw, config.ForwarderAddress().String(), defaultGasLimit), nil
}
8 changes: 5 additions & 3 deletions core/services/relay/evm/write_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,11 @@ func TestEvmWrite(t *testing.T) {
require.Equal(t, signatures, payload["signatures"])
}).Once()

defaultGasLimit := uint64(400_000)

t.Run("succeeds with valid report", func(t *testing.T) {
ctx := testutils.Context(t)
capability, err := evm.NewWriteTarget(ctx, relayer, chain, lggr)
capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, lggr)
require.NoError(t, err)

req := capabilities.CapabilityRequest{
Expand All @@ -216,7 +218,7 @@ func TestEvmWrite(t *testing.T) {

t.Run("fails with invalid config", func(t *testing.T) {
ctx := testutils.Context(t)
capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t))
capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, logger.TestLogger(t))
require.NoError(t, err)

invalidConfig, err := values.NewMap(map[string]any{
Expand All @@ -236,7 +238,7 @@ func TestEvmWrite(t *testing.T) {

t.Run("fails when TXM CreateTransaction returns error", func(t *testing.T) {
ctx := testutils.Context(t)
capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t))
capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, logger.TestLogger(t))
require.NoError(t, err)

req := capabilities.CapabilityRequest{
Expand Down
7 changes: 7 additions & 0 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8588,6 +8588,7 @@ GasLimit controls the gas limit for transmit transactions from ocr2automation jo
[EVM.Workflow]
FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example
ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example
DefaultGasLimit = 400_000 # Default
```


Expand All @@ -8603,6 +8604,12 @@ ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example
```
ForwarderAddress is the keystone forwarder contract address on chain.

### DefaultGasLimit
```toml
DefaultGasLimit = 400_000 # Default
```
DefaultGasLimit is the default gas limit for workflow transactions.

## Cosmos
```toml
[[Cosmos]]
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ require (
github.com/smartcontractkit/chain-selectors v1.0.21
github.com/smartcontractkit/chainlink-automation v1.0.4
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823093917-c07a4fa0caa5
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240823143943-86fc7c5deb84
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2
github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827
Expand Down
Loading

0 comments on commit 32a2ccd

Please sign in to comment.