diff --git a/CONFIG.md b/CONFIG.md new file mode 100644 index 0000000000..657d870443 --- /dev/null +++ b/CONFIG.md @@ -0,0 +1,1219 @@ +[//]: # (Documentation generated from docs.toml - DO NOT EDIT.) +This document describes the TOML format for configuration. +## Example + +```toml +ChainID = '1' # Required + +[[Nodes]] +Name = 'fake' # Required +WSURL = 'wss://foo.bar/ws' +HTTPURL = 'https://foo.bar' # Required + +``` + +## Global +```toml +ChainID = '1' # Example +Enabled = true # Default +AutoCreateKey = true # Default +BlockBackfillDepth = 10 # Default +BlockBackfillSkip = false # Default +ChainType = 'arbitrum' # Example +FinalityDepth = 50 # Default +FinalityTagEnabled = false # Default +FlagsContractAddress = '0xae4E781a6218A8031764928E88d457937A954fC3' # Example +LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' # Example +LogBackfillBatchSize = 1000 # Default +LogPollInterval = '15s' # Default +LogKeepBlocksDepth = 100000 # Default +LogPrunePageSize = 0 # Default +BackupLogPollerBlockDelay = 100 # Default +MinContractPayment = '10000000000000 juels' # Default +MinIncomingConfirmations = 3 # Default +NonceAutoSync = true # Default +NoNewHeadsThreshold = '3m' # Default +OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' # Example +RPCDefaultBatchSize = 250 # Default +RPCBlockQueryDelay = 1 # Default +FinalizedBlockOffset = 0 # Default +LogBroadcasterEnabled = true # Default +NoNewFinalizedHeadsThreshold = '0' # Default +``` + + +### ChainID +```toml +ChainID = '1' # Example +``` +ChainID is the EVM chain ID. Mandatory. + +### Enabled +```toml +Enabled = true # Default +``` +Enabled enables this chain. + +### AutoCreateKey +```toml +AutoCreateKey = true # Default +``` +AutoCreateKey, if set to true, will ensure that there is always at least one transmit key for the given chain. + +### BlockBackfillDepth +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +BlockBackfillDepth = 10 # Default +``` +BlockBackfillDepth specifies the number of blocks before the current HEAD that the log broadcaster will try to re-consume logs from. + +### BlockBackfillSkip +```toml +BlockBackfillSkip = false # Default +``` +BlockBackfillSkip enables skipping of very long backfills. + +### ChainType +```toml +ChainType = 'arbitrum' # Example +``` +ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. +Available types: `arbitrum`, `celo`, `gnosis`, `hedera`, `kroma`, `metis`, `optimismBedrock`, `scroll`, `wemix`, `xlayer`, `zksync` + +### FinalityDepth +```toml +FinalityDepth = 50 # Default +``` +FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID, so it should not be necessary to change this under normal operation. +BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain +There is not a large performance penalty to setting this relatively high (on the order of hundreds) +It is practically limited by the number of heads we store in the database and should be less than this with a comfortable margin. +If a transaction is mined in a block more than this many blocks ago, and is reorged out, we will NOT retransmit this transaction and undefined behaviour can occur including gaps in the nonce sequence that require manual intervention to fix. +Therefore, this number represents a number of blocks we consider large enough that no re-org this deep will ever feasibly happen. + +Special cases: +`FinalityDepth`=0 would imply that transactions can be final even before they were mined into a block. This is not supported. +`FinalityDepth`=1 implies that transactions are final after we see them in one block. + +Examples: + +Transaction sending: +A transaction is sent at block height 42 + +`FinalityDepth` is set to 5 +A re-org occurs at height 44 starting at block 41, transaction is marked for rebroadcast +A re-org occurs at height 46 starting at block 41, transaction is marked for rebroadcast +A re-org occurs at height 47 starting at block 41, transaction is NOT marked for rebroadcast + +### FinalityTagEnabled +```toml +FinalityTagEnabled = false # Default +``` +FinalityTagEnabled means that the chain supports the finalized block tag when querying for a block. If FinalityTagEnabled is set to true for a chain, then FinalityDepth field is ignored. +Finality for a block is solely defined by the finality related tags provided by the chain's RPC API. This is a placeholder and hasn't been implemented yet. + +### FlagsContractAddress +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +FlagsContractAddress = '0xae4E781a6218A8031764928E88d457937A954fC3' # Example +``` +FlagsContractAddress can optionally point to a [Flags contract](../contracts/src/v0.8/Flags.sol). If set, the node will lookup that contract for each job that supports flags contracts (currently OCR and FM jobs are supported). If the job's contractAddress is set as hibernating in the FlagsContractAddress address, it overrides the standard update parameters (such as heartbeat/threshold). + +### LinkContractAddress +```toml +LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' # Example +``` +LinkContractAddress is the canonical ERC-677 LINK token contract address on the given chain. Note that this is usually autodetected from chain ID. + +### LogBackfillBatchSize +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +LogBackfillBatchSize = 1000 # Default +``` +LogBackfillBatchSize sets the batch size for calling FilterLogs when we backfill missing logs. + +### LogPollInterval +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +LogPollInterval = '15s' # Default +``` +LogPollInterval works in conjunction with Feature.LogPoller. Controls how frequently the log poller polls for logs. Defaults to the block production rate. + +### LogKeepBlocksDepth +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +LogKeepBlocksDepth = 100000 # Default +``` +LogKeepBlocksDepth works in conjunction with Feature.LogPoller. Controls how many blocks the poller will keep, must be greater than FinalityDepth+1. + +### LogPrunePageSize +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +LogPrunePageSize = 0 # Default +``` +LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. + +### BackupLogPollerBlockDelay +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +BackupLogPollerBlockDelay = 100 # Default +``` +BackupLogPollerBlockDelay works in conjunction with Feature.LogPoller. Controls the block delay of Backup LogPoller, affecting how far behind the latest finalized block it starts and how often it runs. +BackupLogPollerDelay=0 will disable Backup LogPoller (_not recommended for production environment_). + +### MinContractPayment +```toml +MinContractPayment = '10000000000000 juels' # Default +``` +MinContractPayment is the minimum payment in LINK required to execute a direct request job. This can be overridden on a per-job basis. + +### MinIncomingConfirmations +```toml +MinIncomingConfirmations = 3 # Default +``` +MinIncomingConfirmations is the minimum required confirmations before a log event will be consumed. + +### NonceAutoSync +```toml +NonceAutoSync = true # Default +``` +NonceAutoSync enables automatic nonce syncing on startup. Chainlink nodes will automatically try to sync its local nonce with the remote chain on startup and fast forward if necessary. This is almost always safe but can be disabled in exceptional cases by setting this value to false. + +### NoNewHeadsThreshold +```toml +NoNewHeadsThreshold = '3m' # Default +``` +NoNewHeadsThreshold controls how long to wait after receiving no new heads before `NodePool` marks rpc endpoints as +out-of-sync, and `HeadTracker` logs warnings. + +Set to zero to disable out-of-sync checking. + +### OperatorFactoryAddress +```toml +OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' # Example +``` +OperatorFactoryAddress is the address of the canonical operator forwarder contract on the given chain. Note that this is usually autodetected from chain ID. + +### RPCDefaultBatchSize +```toml +RPCDefaultBatchSize = 250 # Default +``` +RPCDefaultBatchSize is the default batch size for batched RPC calls. + +### RPCBlockQueryDelay +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +RPCBlockQueryDelay = 1 # Default +``` +RPCBlockQueryDelay controls the number of blocks to trail behind head in the block history estimator and balance monitor. +For example, if this is set to 3, and we receive block 10, block history estimator will fetch block 7. + +CAUTION: You might be tempted to set this to 0 to use the latest possible +block, but it is possible to receive a head BEFORE that block is actually +available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false +"zero" blocks that are missing transactions. + +### FinalizedBlockOffset +```toml +FinalizedBlockOffset = 0 # Default +``` +FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. + +### LogBroadcasterEnabled +```toml +LogBroadcasterEnabled = true # Default +``` +LogBroadcasterEnabled is a feature flag for LogBroadcaster, by default it's true. + +### NoNewFinalizedHeadsThreshold +```toml +NoNewFinalizedHeadsThreshold = '0' # Default +``` +NoNewFinalizedHeadsThreshold controls how long to wait for new finalized block before `NodePool` marks rpc endpoints as +out-of-sync. Only applicable if `FinalityTagEnabled=true` + +Set to zero to disable. + +## Transactions +```toml +[Transactions] +Enabled = true # Default +ForwardersEnabled = false # Default +MaxInFlight = 16 # Default +MaxQueued = 250 # Default +ReaperInterval = '1h' # Default +ReaperThreshold = '168h' # Default +ResendAfterThreshold = '1m' # Default +``` + + +### Enabled +```toml +Enabled = true # Default +``` +Enabled is a feature flag for the Transaction Manager. This flag also enables or disables the gas estimator since it is dependent on the TXM to start it. + +### ForwardersEnabled +```toml +ForwardersEnabled = false # Default +``` +ForwardersEnabled enables or disables sending transactions through forwarder contracts. + +### MaxInFlight +```toml +MaxInFlight = 16 # Default +``` +MaxInFlight controls how many transactions are allowed to be "in-flight" i.e. broadcast but unconfirmed at any one time. You can consider this a form of transaction throttling. + +The default is set conservatively at 16 because this is a pessimistic minimum that both geth and parity will hold without evicting local transactions. If your node is falling behind and you need higher throughput, you can increase this setting, but you MUST make sure that your ETH node is configured properly otherwise you can get nonce gapped and your node will get stuck. + +0 value disables the limit. Use with caution. + +### MaxQueued +```toml +MaxQueued = 250 # Default +``` +MaxQueued is the maximum number of unbroadcast transactions per key that are allowed to be enqueued before jobs will start failing and rejecting send of any further transactions. This represents a sanity limit and generally indicates a problem with your ETH node (transactions are not getting mined). + +Do NOT blindly increase this value thinking it will fix things if you start hitting this limit because transactions are not getting mined, you will instead only make things worse. + +In deployments with very high burst rates, or on chains with large re-orgs, you _may_ consider increasing this. + +0 value disables any limit on queue size. Use with caution. + +### ReaperInterval +```toml +ReaperInterval = '1h' # Default +``` +ReaperInterval controls how often the EthTx reaper will run. + +### ReaperThreshold +```toml +ReaperThreshold = '168h' # Default +``` +ReaperThreshold indicates how old an EthTx ought to be before it can be reaped. + +### ResendAfterThreshold +```toml +ResendAfterThreshold = '1m' # Default +``` +ResendAfterThreshold controls how long to wait before re-broadcasting a transaction that has not yet been confirmed. + +## Transactions.AutoPurge +```toml +[Transactions.AutoPurge] +Enabled = false # Default +DetectionApiUrl = 'https://example.api.io' # Example +Threshold = 5 # Example +MinAttempts = 3 # Example +``` + + +### Enabled +```toml +Enabled = false # Default +``` +Enabled enables or disables automatically purging transactions that have been idenitified as terminally stuck (will never be included on-chain). This feature is only expected to be used by ZK chains. + +### DetectionApiUrl +```toml +DetectionApiUrl = 'https://example.api.io' # Example +``` +DetectionApiUrl configures the base url of a custom endpoint used to identify terminally stuck transactions. + +### Threshold +```toml +Threshold = 5 # Example +``` +Threshold configures the number of blocks a transaction has to remain unconfirmed before it is evaluated for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. + +### MinAttempts +```toml +MinAttempts = 3 # Example +``` +MinAttempts configures the minimum number of broadcasted attempts a transaction has to have before it is evaluated further for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. Ensure the gas estimator configs take more bump attempts before reaching the configured max gas price. + +## Transactions.TransactionManagerV2 +```toml +[Transactions.TransactionManagerV2] +Enabled = false # Default +BlockTime = '10s' # Example +CustomURL = 'https://example.api.io' # Example +DualBroadcast = false # Example +``` + + +### Enabled +```toml +Enabled = false # Default +``` +Enabled enables TransactionManagerV2. + +### BlockTime +```toml +BlockTime = '10s' # Example +``` +BlockTime controls the frequency of the backfill loop of TransactionManagerV2. + +### CustomURL +```toml +CustomURL = 'https://example.api.io' # Example +``` +CustomURL configures the base url of a custom endpoint used by the ChainDualBroadcast chain type. + +### DualBroadcast +```toml +DualBroadcast = false # Example +``` +DualBroadcast enables DualBroadcast functionality. + +## BalanceMonitor +```toml +[BalanceMonitor] +Enabled = true # Default +``` + + +### Enabled +```toml +Enabled = true # Default +``` +Enabled balance monitoring for all keys. + +## GasEstimator +```toml +[GasEstimator] +Mode = 'BlockHistory' # Default +PriceDefault = '20 gwei' # Default +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' # Default +PriceMin = '1 gwei' # Default +LimitDefault = 500_000 # Default +LimitMax = 500_000 # Default +LimitMultiplier = '1.0' # Default +LimitTransfer = 21_000 # Default +EstimateLimit = false # Default +BumpMin = '5 gwei' # Default +BumpPercent = 20 # Default +BumpThreshold = 3 # Default +BumpTxDepth = 16 # Example +EIP1559DynamicFees = false # Default +FeeCapDefault = '100 gwei' # Default +TipCapDefault = '1 wei' # Default +TipCapMin = '1 wei' # Default +``` + + +### Mode +```toml +Mode = 'BlockHistory' # Default +``` +Mode controls what type of gas estimator is used. + +- `FixedPrice` uses static configured values for gas price (can be set via API call). +- `BlockHistory` dynamically adjusts default gas price based on heuristics from mined blocks. +- `L2Suggested` mode is deprecated and replaced with `SuggestedPrice`. +- `SuggestedPrice` is a mode which uses the gas price suggested by the rpc endpoint via `eth_gasPrice`. +- `Arbitrum` is a special mode only for use with Arbitrum blockchains. It uses the suggested gas price (up to `ETH_MAX_GAS_PRICE_WEI`, with `1000 gwei` default) as well as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` default). + +Chainlink nodes decide what gas price to use using an `Estimator`. It ships with several simple and battle-hardened built-in estimators that should work well for almost all use-cases. Note that estimators will change their behaviour slightly depending on if you are in EIP-1559 mode or not. + +You can also use your own estimator for gas price by selecting the `FixedPrice` estimator and using the exposed API to set the price. + +An important point to note is that the Chainlink node does _not_ ship with built-in support for go-ethereum's `estimateGas` call. This is for several reasons, including security and reliability. We have found empirically that it is not generally safe to rely on the remote ETH node's idea of what gas price should be. + +### PriceDefault +```toml +PriceDefault = '20 gwei' # Default +``` +PriceDefault is the default gas price to use when submitting transactions to the blockchain. Will be overridden by the built-in `BlockHistoryEstimator` if enabled, and might be increased if gas bumping is enabled. + +(Only applies to legacy transactions) + +Can be used with the `chainlink setgasprice` to be updated while the node is still running. + +### PriceMax +```toml +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' # Default +``` +PriceMax is the maximum gas price. Chainlink nodes will never pay more than this for a transaction. +This applies to both legacy and EIP1559 transactions. +Note that it is impossible to disable the maximum limit. Setting this value to zero will prevent paying anything for any transaction (which can be useful in some rare cases). +Most chains by default have the maximum set to 2**256-1 Wei which is the maximum allowed gas price on EVM-compatible chains, and is so large it may as well be unlimited. + +### PriceMin +```toml +PriceMin = '1 gwei' # Default +``` +PriceMin is the minimum gas price. Chainlink nodes will never pay less than this for a transaction. + +(Only applies to legacy transactions) + +It is possible to force the Chainlink node to use a fixed gas price by setting a combination of these, e.g. + +```toml +EIP1559DynamicFees = false +PriceMax = 100 +PriceMin = 100 +PriceDefault = 100 +BumpThreshold = 0 +Mode = 'FixedPrice' +``` + +### LimitDefault +```toml +LimitDefault = 500_000 # Default +``` +LimitDefault sets default gas limit for outgoing transactions. This should not need to be changed in most cases. +Some job types, such as Keeper jobs, might set their own gas limit unrelated to this value. + +### LimitMax +```toml +LimitMax = 500_000 # Default +``` +LimitMax sets a maximum for _estimated_ gas limits. This currently only applies to `Arbitrum` `GasEstimatorMode`. + +### LimitMultiplier +```toml +LimitMultiplier = '1.0' # Default +``` +LimitMultiplier is the factor by which a transaction's GasLimit is multiplied before transmission. So if the value is 1.1, and the GasLimit for a transaction is 10, 10% will be added before transmission. + +This factor is always applied, so includes L2 transactions which uses a default gas limit of 1 and is also applied to `LimitDefault`. + +### LimitTransfer +```toml +LimitTransfer = 21_000 # Default +``` +LimitTransfer is the gas limit used for an ordinary ETH transfer. + +### EstimateLimit +```toml +EstimateLimit = false # Default +``` +EstimateLimit enables estimating gas limits for transactions. This feature respects the gas limit provided during transaction creation as an upper bound. + +### BumpMin +```toml +BumpMin = '5 gwei' # Default +``` +BumpMin is the minimum fixed amount of wei by which gas is bumped on each transaction attempt. + +### BumpPercent +```toml +BumpPercent = 20 # Default +``` +BumpPercent is the percentage by which to bump gas on a transaction that has exceeded `BumpThreshold`. The larger of `BumpPercent` and `BumpMin` is taken for gas bumps. + +The `SuggestedPriceEstimator` adds the larger of `BumpPercent` and `BumpMin` on top of the price provided by the RPC when bumping a transaction's gas. + +### BumpThreshold +```toml +BumpThreshold = 3 # Default +``` +BumpThreshold is the number of blocks to wait for a transaction stuck in the mempool before automatically bumping the gas price. Set to 0 to disable gas bumping completely. + +### BumpTxDepth +```toml +BumpTxDepth = 16 # Example +``` +BumpTxDepth is the number of transactions to gas bump starting from oldest. Set to 0 for no limit (i.e. bump all). Can not be greater than EVM.Transactions.MaxInFlight. If not set, defaults to EVM.Transactions.MaxInFlight. + +### EIP1559DynamicFees +```toml +EIP1559DynamicFees = false # Default +``` +EIP1559DynamicFees torces EIP-1559 transaction mode. Enabling EIP-1559 mode can help reduce gas costs on chains that support it. This is supported only on official Ethereum mainnet and testnets. It is not recommended to enable this setting on Polygon because the EIP-1559 fee market appears to be broken on all Polygon chains and EIP-1559 transactions are less likely to be included than legacy transactions. + +#### Technical details + +Chainlink nodes include experimental support for submitting transactions using type 0x2 (EIP-1559) envelope. + +EIP-1559 mode is enabled by default on the Ethereum Mainnet, but can be enabled on a per-chain basis or globally. + +This might help to save gas on spikes. Chainlink nodes should react faster on the upleg and avoid overpaying on the downleg. It might also be possible to set `EVM.GasEstimator.BlockHistory.BatchSize` to a smaller value such as 12 or even 6 because tip cap should be a more consistent indicator of inclusion time than total gas price. This would make Chainlink nodes more responsive and should reduce response time variance. Some experimentation is required to find optimum settings. + +Set with caution, if you set this on a chain that does not actually support EIP-1559 your node will be broken. + +In EIP-1559 mode, the total price for the transaction is the minimum of base fee + tip cap and fee cap. More information can be found on the [official EIP](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). + +Chainlink's implementation of EIP-1559 works as follows: + +If you are using FixedPriceEstimator: +- With gas bumping disabled, it will submit all transactions with `feecap=PriceMax` and `tipcap=GasTipCapDefault` +- With gas bumping enabled, it will submit all transactions initially with `feecap=GasFeeCapDefault` and `tipcap=GasTipCapDefault`. + +If you are using BlockHistoryEstimator (default for most chains): +- With gas bumping disabled, it will submit all transactions with `feecap=PriceMax` and `tipcap=` +- With gas bumping enabled (default for most chains) it will submit all transactions initially with `feecap = ( current block base fee * (1.125 ^ N) + tipcap )` where N is configurable by setting `EVM.GasEstimator.BlockHistory.EIP1559FeeCapBufferBlocks` but defaults to `gas bump threshold+1` and `tipcap=` + +Bumping works as follows: + +- Increase tipcap by `max(tipcap * (1 + BumpPercent), tipcap + BumpMin)` +- Increase feecap by `max(feecap * (1 + BumpPercent), feecap + BumpMin)` + +A quick note on terminology - Chainlink nodes use the same terms used internally by go-ethereum source code to describe various prices. This is not the same as the externally used terms. For reference: + +- Base Fee Per Gas = BaseFeePerGas +- Max Fee Per Gas = FeeCap +- Max Priority Fee Per Gas = TipCap + +In EIP-1559 mode, the following changes occur to how configuration works: + +- All new transactions will be sent as type 0x2 transactions specifying a TipCap and FeeCap. Be aware that existing pending legacy transactions will continue to be gas bumped in legacy mode. +- `BlockHistoryEstimator` will apply its calculations (gas percentile etc) to the TipCap and this value will be used for new transactions (GasPrice will be ignored) +- `FixedPriceEstimator` will use `GasTipCapDefault` instead of `GasPriceDefault` for the tip cap +- `FixedPriceEstimator` will use `GasFeeCapDefault` instaed of `GasPriceDefault` for the fee cap +- `PriceMin` is ignored for new transactions and `GasTipCapMinimum` is used instead (default 0) +- `PriceMax` still represents that absolute upper limit that Chainlink will ever spend (total) on a single tx +- `Keeper.GasTipCapBufferPercent` is ignored in EIP-1559 mode and `Keeper.GasTipCapBufferPercent` is used instead + +### FeeCapDefault +```toml +FeeCapDefault = '100 gwei' # Default +``` +FeeCapDefault controls the fixed initial fee cap, if EIP1559 mode is enabled and `FixedPrice` gas estimator is used. + +### TipCapDefault +```toml +TipCapDefault = '1 wei' # Default +``` +TipCapDefault is the default gas tip to use when submitting transactions to the blockchain. Will be overridden by the built-in `BlockHistoryEstimator` if enabled, and might be increased if gas bumping is enabled. + +(Only applies to EIP-1559 transactions) + +### TipCapMin +```toml +TipCapMin = '1 wei' # Default +``` +TipCapMinimum is the minimum gas tip to use when submitting transactions to the blockchain. + +(Only applies to EIP-1559 transactions) + +## GasEstimator.DAOracle +```toml +[GasEstimator.DAOracle] +OracleType = 'opstack' # Example +OracleAddress = '0x420000000000000000000000000000000000000F' # Example +CustomGasPriceCalldata = '' # Default +``` + + +### OracleType +```toml +OracleType = 'opstack' # Example +``` +OracleType refers to the oracle family this config belongs to. Currently the available oracle types are: 'opstack', 'arbitrum', 'zksync', and 'custom_calldata'. + +### OracleAddress +```toml +OracleAddress = '0x420000000000000000000000000000000000000F' # Example +``` +OracleAddress is the address of the oracle contract. + +### CustomGasPriceCalldata +```toml +CustomGasPriceCalldata = '' # Default +``` +CustomGasPriceCalldata is optional and can be set to call a custom gas price function at the given OracleAddress. + +## GasEstimator.LimitJobType +```toml +[GasEstimator.LimitJobType] +OCR = 100_000 # Example +OCR2 = 100_000 # Example +DR = 100_000 # Example +VRF = 100_000 # Example +FM = 100_000 # Example +Keeper = 100_000 # Example +``` + + +### OCR +```toml +OCR = 100_000 # Example +``` +OCR overrides LimitDefault for OCR jobs. + +### OCR2 +```toml +OCR2 = 100_000 # Example +``` +OCR2 overrides LimitDefault for OCR2 jobs. + +### DR +```toml +DR = 100_000 # Example +``` +DR overrides LimitDefault for Direct Request jobs. + +### VRF +```toml +VRF = 100_000 # Example +``` +VRF overrides LimitDefault for VRF jobs. + +### FM +```toml +FM = 100_000 # Example +``` +FM overrides LimitDefault for Flux Monitor jobs. + +### Keeper +```toml +Keeper = 100_000 # Example +``` +Keeper overrides LimitDefault for Keeper jobs. + +## GasEstimator.BlockHistory +```toml +[GasEstimator.BlockHistory] +BatchSize = 25 # Default +BlockHistorySize = 8 # Default +CheckInclusionBlocks = 12 # Default +CheckInclusionPercentile = 90 # Default +EIP1559FeeCapBufferBlocks = 13 # Example +TransactionPercentile = 60 # Default +``` +These settings allow you to configure how your node calculates gas prices when using the block history estimator. +In most cases, leaving these values at their defaults should give good results. + +### BatchSize +```toml +BatchSize = 25 # Default +``` +BatchSize sets the maximum number of blocks to fetch in one batch in the block history estimator. +If the `BatchSize` variable is set to 0, it defaults to `EVM.RPCDefaultBatchSize`. + +### BlockHistorySize +```toml +BlockHistorySize = 8 # Default +``` +BlockHistorySize controls the number of past blocks to keep in memory to use as a basis for calculating a percentile gas price. + +### CheckInclusionBlocks +```toml +CheckInclusionBlocks = 12 # Default +``` +CheckInclusionBlocks is the number of recent blocks to use to detect if there is a transaction propagation/connectivity issue, and to prevent bumping in these cases. +This can help avoid the situation where RPC nodes are not propagating transactions for some non-price-related reason (e.g. go-ethereum bug, networking issue etc) and bumping gas would not help. + +Set to zero to disable connectivity checking completely. + +### CheckInclusionPercentile +```toml +CheckInclusionPercentile = 90 # Default +``` +CheckInclusionPercentile controls the percentile that a transaction must have been higher than for all the blocks in the inclusion check window in order to register as a connectivity issue. + +For example, if CheckInclusionBlocks=12 and CheckInclusionPercentile=90 then further bumping will be prevented for any transaction with any attempt that has a higher price than the 90th percentile for the most recent 12 blocks. + +### EIP1559FeeCapBufferBlocks +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +EIP1559FeeCapBufferBlocks = 13 # Example +``` +EIP1559FeeCapBufferBlocks controls the buffer blocks to add to the current base fee when sending a transaction. By default, the gas bumping threshold + 1 block is used. + +(Only applies to EIP-1559 transactions) + +### TransactionPercentile +```toml +TransactionPercentile = 60 # Default +``` +TransactionPercentile specifies gas price to choose. E.g. if the block history contains four transactions with gas prices `[100, 200, 300, 400]` then picking 25 for this number will give a value of 200. If the calculated gas price is higher than `GasPriceDefault` then the higher price will be used as the base price for new transactions. + +Must be in range 0-100. + +Only has an effect if gas updater is enabled. + +Think of this number as an indicator of how aggressive you want your node to price its transactions. + +Setting this number higher will cause the Chainlink node to select higher gas prices. + +Setting it lower will tend to set lower gas prices. + +## GasEstimator.FeeHistory +```toml +[GasEstimator.FeeHistory] +CacheTimeout = '10s' # Default +``` + + +### CacheTimeout +```toml +CacheTimeout = '10s' # Default +``` +CacheTimeout is the time to wait in order to refresh the cached values stored in the FeeHistory estimator. A small jitter is applied so the timeout won't be exactly the same each time. + +You want this value to be close to the block time. For slower chains, like Ethereum, you can set it to 12s, the same as the block time. For faster chains you can skip a block or two +and set it to two times the block time i.e. on Optimism you can set it to 4s. Ideally, you don't want to go lower than 1s since the RTT times of the RPC requests will be comparable to +the timeout. The estimator is already adding a buffer to account for a potential increase in prices within one or two blocks. On the other hand, slower frequency will fail to refresh +the prices and end up in stale values. + +## HeadTracker +```toml +[HeadTracker] +HistoryDepth = 100 # Default +MaxBufferSize = 3 # Default +SamplingInterval = '1s' # Default +FinalityTagBypass = true # Default +MaxAllowedFinalityDepth = 10000 # Default +PersistenceEnabled = true # Default +``` +The head tracker continually listens for new heads from the chain. + +In addition to these settings, it log warnings if `EVM.NoNewHeadsThreshold` is exceeded without any new blocks being emitted. + +### HistoryDepth +```toml +HistoryDepth = 100 # Default +``` +HistoryDepth tracks the top N blocks on top of the latest finalized block to keep in the `heads` database table. +Note that this can easily result in MORE than `N + finality depth` records since in the case of re-orgs we keep multiple heads for a particular block height. +Higher values help reduce number of RPC requests performed by TXM's Finalizer and improve TXM's Confirmer reorg protection on restarts. +At the same time, setting the value too high could lead to higher CPU consumption. The following formula could be used to calculate the optimal value: `expected_downtime_on_restart/block_time`. + +### MaxBufferSize +```toml +MaxBufferSize = 3 # Default +``` +MaxBufferSize is the maximum number of heads that may be +buffered in front of the head tracker before older heads start to be +dropped. You may think of it as something like the maximum permittable "lag" +for the head tracker before we start dropping heads to keep up. + +### SamplingInterval +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +SamplingInterval = '1s' # Default +``` +SamplingInterval means that head tracker callbacks will at maximum be made once in every window of this duration. This is a performance optimisation for fast chains. Set to 0 to disable sampling entirely. + +### FinalityTagBypass +```toml +FinalityTagBypass = true # Default +``` +FinalityTagBypass disables FinalityTag support in HeadTracker and makes it track blocks up to FinalityDepth from the most recent head. +It should only be used on chains with an extremely large actual finality depth (the number of blocks between the most recent head and the latest finalized block). +Has no effect if `FinalityTagsEnabled` = false + +### MaxAllowedFinalityDepth +```toml +MaxAllowedFinalityDepth = 10000 # Default +``` +MaxAllowedFinalityDepth - defines maximum number of blocks between the most recent head and the latest finalized block. +If actual finality depth exceeds this number, HeadTracker aborts backfill and returns an error. +Has no effect if `FinalityTagsEnabled` = false + +### PersistenceEnabled +```toml +PersistenceEnabled = true # Default +``` +PersistenceEnabled defines whether HeadTracker needs to store heads in the database. +Persistence is helpful on chains with large finality depth, where fetching blocks from the latest to the latest finalized takes a lot of time. +On chains with fast finality, the persistence layer does not improve the chain's load time and only consumes database resources (mainly IO). +NOTE: persistence should not be disabled for products that use LogBroadcaster, as it might lead to missed on-chain events. + +## KeySpecific +```toml +[[KeySpecific]] +Key = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +GasEstimator.PriceMax = '79 gwei' # Example +``` + + +### Key +```toml +Key = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +``` +Key is the account to apply these settings to + +### PriceMax +```toml +GasEstimator.PriceMax = '79 gwei' # Example +``` +GasEstimator.PriceMax overrides the maximum gas price for this key. See EVM.GasEstimator.PriceMax. + +## NodePool +```toml +[NodePool] +PollFailureThreshold = 5 # Default +PollInterval = '10s' # Default +SelectionMode = 'HighestHead' # Default +SyncThreshold = 5 # Default +LeaseDuration = '0s' # Default +NodeIsSyncingEnabled = false # Default +FinalizedBlockPollInterval = '5s' # Default +EnforceRepeatableRead = true # Default +DeathDeclarationDelay = '1m' # Default +NewHeadsPollInterval = '0s' # Default +VerifyChainID = true # Default +``` +The node pool manages multiple RPC endpoints. + +In addition to these settings, `EVM.NoNewHeadsThreshold` controls how long to wait after receiving no new heads before marking the node as out-of-sync. + +### PollFailureThreshold +```toml +PollFailureThreshold = 5 # Default +``` +PollFailureThreshold indicates how many consecutive polls must fail in order to mark a node as unreachable. + +Set to zero to disable poll checking. + +### PollInterval +```toml +PollInterval = '10s' # Default +``` +PollInterval controls how often to poll the node to check for liveness. + +Set to zero to disable poll checking. + +### SelectionMode +```toml +SelectionMode = 'HighestHead' # Default +``` +SelectionMode controls node selection strategy: +- HighestHead: use the node with the highest head number +- RoundRobin: rotate through nodes, per-request +- PriorityLevel: use the node with the smallest order number +- TotalDifficulty: use the node with the greatest total difficulty + +### SyncThreshold +```toml +SyncThreshold = 5 # Default +``` +SyncThreshold controls how far a node may lag behind the best node before being marked out-of-sync. +Depending on `SelectionMode`, this represents a difference in the number of blocks (`HighestHead`, `RoundRobin`, `PriorityLevel`), or total difficulty (`TotalDifficulty`). + +Set to 0 to disable this check. + +### LeaseDuration +```toml +LeaseDuration = '0s' # Default +``` +LeaseDuration is the minimum duration that the selected "best" node (as defined by SelectionMode) will be used, +before switching to a better one if available. It also controls how often the lease check is done. +Setting this to a low value (under 1m) might cause RPC to switch too aggressively. +Recommended value is over 5m + +Set to '0s' to disable + +### NodeIsSyncingEnabled +```toml +NodeIsSyncingEnabled = false # Default +``` +NodeIsSyncingEnabled is a flag that enables `syncing` health check on each reconnection to an RPC. +Node transitions and remains in `Syncing` state while RPC signals this state (In case of Ethereum `eth_syncing` returns anything other than false). +All of the requests to node in state `Syncing` are rejected. + +Set true to enable this check + +### FinalizedBlockPollInterval +```toml +FinalizedBlockPollInterval = '5s' # Default +``` +FinalizedBlockPollInterval controls how often to poll RPC for new finalized blocks. +The finalized block is only used to report to the `pool_rpc_node_highest_finalized_block` metric. We plan to use it +in RPCs health assessment in the future. +If `FinalityTagEnabled = false`, poll is not performed and `pool_rpc_node_highest_finalized_block` is +reported based on latest block and finality depth. + +Set to 0 to disable. + +### EnforceRepeatableRead +```toml +EnforceRepeatableRead = true # Default +``` +EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +`highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +block. + +Set false to disable + +### DeathDeclarationDelay +```toml +DeathDeclarationDelay = '1m' # Default +``` +DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +Should be greater than `FinalizedBlockPollInterval`. +Unhealthy RPC will not be picked to handle a request even if this option is set to a nonzero value. + +### NewHeadsPollInterval +```toml +NewHeadsPollInterval = '0s' # Default +``` +NewHeadsPollInterval define an interval for polling new block periodically using http client rather than subscribe to ws feed + +Set to 0 to disable. + +### VerifyChainID +```toml +VerifyChainID = true # Default +``` +VerifyChainID enforces RPC Client ChainIDs to match configured ChainID + +## NodePool.Errors +:warning: **_ADVANCED_**: _Do not change these settings unless you know what you are doing._ +```toml +[NodePool.Errors] +NonceTooLow = '(: |^)nonce too low' # Example +NonceTooHigh = '(: |^)nonce too high' # Example +ReplacementTransactionUnderpriced = '(: |^)replacement transaction underpriced' # Example +LimitReached = '(: |^)limit reached' # Example +TransactionAlreadyInMempool = '(: |^)transaction already in mempool' # Example +TerminallyUnderpriced = '(: |^)terminally underpriced' # Example +InsufficientEth = '(: |^)insufficeint eth' # Example +TxFeeExceedsCap = '(: |^)tx fee exceeds cap' # Example +L2FeeTooLow = '(: |^)l2 fee too low' # Example +L2FeeTooHigh = '(: |^)l2 fee too high' # Example +L2Full = '(: |^)l2 full' # Example +TransactionAlreadyMined = '(: |^)transaction already mined' # Example +Fatal = '(: |^)fatal' # Example +ServiceUnavailable = '(: |^)service unavailable' # Example +TooManyResults = '(: |^)too many results' # Example +``` +Errors enable the node to provide custom regex patterns to match against error messages from RPCs. + +### NonceTooLow +```toml +NonceTooLow = '(: |^)nonce too low' # Example +``` +NonceTooLow is a regex pattern to match against nonce too low errors. + +### NonceTooHigh +```toml +NonceTooHigh = '(: |^)nonce too high' # Example +``` +NonceTooHigh is a regex pattern to match against nonce too high errors. + +### ReplacementTransactionUnderpriced +```toml +ReplacementTransactionUnderpriced = '(: |^)replacement transaction underpriced' # Example +``` +ReplacementTransactionUnderpriced is a regex pattern to match against replacement transaction underpriced errors. + +### LimitReached +```toml +LimitReached = '(: |^)limit reached' # Example +``` +LimitReached is a regex pattern to match against limit reached errors. + +### TransactionAlreadyInMempool +```toml +TransactionAlreadyInMempool = '(: |^)transaction already in mempool' # Example +``` +TransactionAlreadyInMempool is a regex pattern to match against transaction already in mempool errors. + +### TerminallyUnderpriced +```toml +TerminallyUnderpriced = '(: |^)terminally underpriced' # Example +``` +TerminallyUnderpriced is a regex pattern to match against terminally underpriced errors. + +### InsufficientEth +```toml +InsufficientEth = '(: |^)insufficeint eth' # Example +``` +InsufficientEth is a regex pattern to match against insufficient eth errors. + +### TxFeeExceedsCap +```toml +TxFeeExceedsCap = '(: |^)tx fee exceeds cap' # Example +``` +TxFeeExceedsCap is a regex pattern to match against tx fee exceeds cap errors. + +### L2FeeTooLow +```toml +L2FeeTooLow = '(: |^)l2 fee too low' # Example +``` +L2FeeTooLow is a regex pattern to match against l2 fee too low errors. + +### L2FeeTooHigh +```toml +L2FeeTooHigh = '(: |^)l2 fee too high' # Example +``` +L2FeeTooHigh is a regex pattern to match against l2 fee too high errors. + +### L2Full +```toml +L2Full = '(: |^)l2 full' # Example +``` +L2Full is a regex pattern to match against l2 full errors. + +### TransactionAlreadyMined +```toml +TransactionAlreadyMined = '(: |^)transaction already mined' # Example +``` +TransactionAlreadyMined is a regex pattern to match against transaction already mined errors. + +### Fatal +```toml +Fatal = '(: |^)fatal' # Example +``` +Fatal is a regex pattern to match against fatal errors. + +### ServiceUnavailable +```toml +ServiceUnavailable = '(: |^)service unavailable' # Example +``` +ServiceUnavailable is a regex pattern to match against service unavailable errors. + +### TooManyResults +```toml +TooManyResults = '(: |^)too many results' # Example +``` +TooManyResults is a regex pattern to match an eth_getLogs error indicating the result set is too large to return + +## OCR +```toml +[OCR] +ContractConfirmations = 4 # Default +ContractTransmitterTransmitTimeout = '10s' # Default +DatabaseTimeout = '10s' # Default +DeltaCOverride = "168h" # Default +DeltaCJitterOverride = "1h" # Default +ObservationGracePeriod = '1s' # Default +``` + + +### ContractConfirmations +```toml +ContractConfirmations = 4 # Default +``` +ContractConfirmations sets `OCR.ContractConfirmations` for this EVM chain. + +### ContractTransmitterTransmitTimeout +```toml +ContractTransmitterTransmitTimeout = '10s' # Default +``` +ContractTransmitterTransmitTimeout sets `OCR.ContractTransmitterTransmitTimeout` for this EVM chain. + +### DatabaseTimeout +```toml +DatabaseTimeout = '10s' # Default +``` +DatabaseTimeout sets `OCR.DatabaseTimeout` for this EVM chain. + +### DeltaCOverride +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +DeltaCOverride = "168h" # Default +``` +DeltaCOverride (and `DeltaCJitterOverride`) determine the config override DeltaC. +DeltaC is the maximum age of the latest report in the contract. If the maximum age is exceeded, a new report will be +created by the report generation protocol. + +### DeltaCJitterOverride +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +DeltaCJitterOverride = "1h" # Default +``` +DeltaCJitterOverride is the range for jitter to add to `DeltaCOverride`. + +### ObservationGracePeriod +```toml +ObservationGracePeriod = '1s' # Default +``` +ObservationGracePeriod sets `OCR.ObservationGracePeriod` for this EVM chain. + +## Nodes +```toml +[[Nodes]] +Name = 'foo' # Example +WSURL = 'wss://web.socket/test' # Example +HTTPURL = 'https://foo.web' # Example +HTTPURLExtraWrite = 'https://foo.web/extra' # Example +SendOnly = false # Default +Order = 100 # Default +``` + + +### Name +```toml +Name = 'foo' # Example +``` +Name is a unique (per-chain) identifier for this node. + +### WSURL +```toml +WSURL = 'wss://web.socket/test' # Example +``` +WSURL is the WS(S) endpoint for this node. Required for primary nodes when `LogBroadcasterEnabled` is `true` + +### HTTPURL +```toml +HTTPURL = 'https://foo.web' # Example +``` +HTTPURL is the HTTP(S) endpoint for this node. Required for all nodes. + +### HTTPURLExtraWrite +```toml +HTTPURLExtraWrite = 'https://foo.web/extra' # Example +``` +HTTPURLExtraWrite is the HTTP(S) endpoint used for chains that require a separate endpoint for writing on-chain. + +### SendOnly +```toml +SendOnly = false # Default +``` +SendOnly limits usage to sending transaction broadcasts only. With this enabled, only HTTPURL is required, and WSURL is not used. + +### Order +```toml +Order = 100 # Default +``` +Order of the node in the pool, will takes effect if `SelectionMode` is `PriorityLevel` or will be used as a tie-breaker for `HighestHead` and `TotalDifficulty` + +## OCR2.Automation +```toml +[OCR2.Automation] +GasLimit = 5400000 # Default +``` + + +### GasLimit +```toml +GasLimit = 5400000 # Default +``` +GasLimit controls the gas limit for transmit transactions from ocr2automation job. + +## Workflow +```toml +[Workflow] +FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +GasLimitDefault = 400_000 # Default +``` + + +### FromAddress +```toml +FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +``` +FromAddress is Address of the transmitter key to use for workflow writes. + +### ForwarderAddress +```toml +ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +``` +ForwarderAddress is the keystone forwarder contract address on chain. + +### GasLimitDefault +```toml +GasLimitDefault = 400_000 # Default +``` +GasLimitDefault is the default gas limit for workflow transactions. + diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000000..c47f927bee --- /dev/null +++ b/config_test.go @@ -0,0 +1,22 @@ +package chainlink_evm + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" +) + +var ( + //go:embed CONFIG.md + configMD string +) + +//go:generate go run ./pkg/scmd/config-docs +func TestConfigDocs(t *testing.T) { + cfg, err := toml.GenerateDocs() + assert.NoError(t, err, "invalid config docs") + assert.Equal(t, configMD, cfg, "CONFIG.md is out of date. Run 'go generate .' to regenerate.") +} diff --git a/go.mod b/go.mod index 8949e76d9b..b64f7c6415 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/jackc/pgtype v1.14.4 github.com/jmoiron/sqlx v1.4.0 github.com/jpillora/backoff v1.0.0 + github.com/kylelemons/godebug v1.1.0 github.com/leanovate/gopter v0.2.11 github.com/lib/pq v1.10.9 github.com/onsi/gomega v1.36.2 @@ -19,7 +20,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.63.0 github.com/shopspring/decimal v1.4.0 - github.com/smartcontractkit/chainlink-common v0.7.1-0.20250429205337-7ee5f91ed065 + github.com/smartcontractkit/chainlink-common v0.7.1-0.20250430133340-d04a3b64e331 github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250408161305-721208f43882 github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250502210357-2df484128afa github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20250502210357-2df484128afa @@ -116,7 +117,6 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marcboeker/go-duckdb v1.8.3 // indirect diff --git a/go.sum b/go.sum index e05caf5b03..7cd7398361 100644 --- a/go.sum +++ b/go.sum @@ -612,8 +612,8 @@ github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJV github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartcontractkit/chainlink-common v0.7.1-0.20250429205337-7ee5f91ed065 h1:8UsqHv+LkrO2KdXbfZd5qejsy4Z4Xz5/DNVfUGT6gTM= -github.com/smartcontractkit/chainlink-common v0.7.1-0.20250429205337-7ee5f91ed065/go.mod h1:vHs/mPpAztdKJtzRKLnmLinmpS78fBh9sAuyKqQrQ+I= +github.com/smartcontractkit/chainlink-common v0.7.1-0.20250430133340-d04a3b64e331 h1:+bN5yuawZeQeVw7vG+Me7kXLFK1vcesVeku3mdgRwqI= +github.com/smartcontractkit/chainlink-common v0.7.1-0.20250430133340-d04a3b64e331/go.mod h1:UGZVg18zzyJ1KimGM5SSmxG5jb7aCSq3OwQQKfhHGyE= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250408161305-721208f43882 h1:teDwTZ0GXlxQ65lgVbB44ffbIHlEh4N8wW7zav4lt9c= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250408161305-721208f43882/go.mod h1:NVoJQoPYr6BorpaXTusoIH1IYTySCmanQ8Q1yv3mNh4= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250502210357-2df484128afa h1:WJNO3646oVvLQtPf+8YUoAOjBWg6XbGUctnOPLp4ZTE= diff --git a/pkg/cmd/config-docs/main.go b/pkg/cmd/config-docs/main.go new file mode 100644 index 0000000000..fab32cced9 --- /dev/null +++ b/pkg/cmd/config-docs/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + + "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" +) + +var outDir = flag.String("o", "", "output directory") + +func main() { + s, err := toml.GenerateDocs() + if err != nil { + log.Fatalln("Failed to generate docs:", err) + } + if err = os.WriteFile(filepath.Join(*outDir, "CONFIG.md"), []byte(s), 0600); err != nil { + fmt.Fprintf(os.Stderr, "failed to write config docs: %v\n", err) + os.Exit(1) + } +} diff --git a/pkg/config/toml/config_test.go b/pkg/config/toml/config_test.go index cdd0924ce2..6cf4913c52 100644 --- a/pkg/config/toml/config_test.go +++ b/pkg/config/toml/config_test.go @@ -1,23 +1,40 @@ -package toml_test +package toml import ( + _ "embed" "fmt" + "math" + stdbig "math/big" + "strings" "testing" + "time" + "github.com/ethereum/go-ethereum/common" + "github.com/kylelemons/godebug/diff" + "github.com/pelletier/go-toml/v2" + "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" + "github.com/smartcontractkit/chainlink-common/pkg/config/configtest" + "github.com/smartcontractkit/chainlink-framework/multinode" + + "github.com/smartcontractkit/chainlink-evm/pkg/assets" + "github.com/smartcontractkit/chainlink-evm/pkg/config/chaintype" + "github.com/smartcontractkit/chainlink-evm/pkg/types" + "github.com/smartcontractkit/chainlink-evm/pkg/utils/big" ) func TestEVMConfig_ValidateConfig(t *testing.T) { name := "fake" - for _, id := range toml.DefaultIDs { + for _, id := range DefaultIDs { t.Run(fmt.Sprintf("chainID-%s", id), func(t *testing.T) { - evmCfg := &toml.EVMConfig{ + evmCfg := &EVMConfig{ ChainID: id, - Chain: toml.Defaults(id), - Nodes: toml.EVMNodes{{ + Chain: Defaults(id), + Nodes: EVMNodes{{ Name: &name, WSURL: config.MustParseURL("wss://foo.test/ws"), HTTPURL: config.MustParseURL("http://foo.test"), @@ -28,3 +45,346 @@ func TestEVMConfig_ValidateConfig(t *testing.T) { }) } } + +func TestDefaults_fieldsNotNil(t *testing.T) { + unknown := Defaults(nil) + + // exceptional nilable fields + unknown.ChainType = chaintype.NewConfig("arbitrum") + unknown.FlagsContractAddress = asEIP55Address(t, "0x1234567890abcdefaC8a1b4E58707D29258707D2") + unknown.LinkContractAddress = asEIP55Address(t, "0xabcdef1234567890aC8a1b4E58707D29258707D2") + unknown.OperatorFactoryAddress = asEIP55Address(t, "0xababab12341234aC8a1b4E58707D29258707D292") + addr, err := types.NewEIP55Address("0x2a3e23c6f242F5345320814aC8a1b4E58707D292") + require.NoError(t, err) + unknown.Workflow.FromAddress = &addr + unknown.Workflow.ForwarderAddress = &addr + unknown.Workflow.GasLimitDefault = ptr(uint64(400000)) + unknown.Transactions.TransactionManagerV2.BlockTime = new(config.Duration) + unknown.Transactions.TransactionManagerV2.CustomURL = new(config.URL) + unknown.Transactions.TransactionManagerV2.DualBroadcast = ptr(false) + unknown.Transactions.AutoPurge.Threshold = ptr(uint32(0)) + unknown.Transactions.AutoPurge.MinAttempts = ptr(uint32(0)) + unknown.Transactions.AutoPurge.DetectionApiUrl = new(config.URL) + unknown.GasEstimator.BlockHistory.EIP1559FeeCapBufferBlocks = ptr[uint16](10) + oracleType := DAOracleOPStack + unknown.GasEstimator.DAOracle.OracleType = &oracleType + unknown.GasEstimator.DAOracle.OracleAddress = new(types.EIP55Address) + unknown.GasEstimator.DAOracle.CustomGasPriceCalldata = new(string) + unknown.GasEstimator.LimitJobType = GasLimitJobType{ + OCR: ptr[uint32](7), + OCR2: ptr[uint32](13), + DR: ptr[uint32](25), + VRF: ptr[uint32](37), + FM: ptr[uint32](42), + Keeper: ptr[uint32](51), + } + unknown.GasEstimator.BumpTxDepth = ptr[uint32](15) + unknown.NodePool.Errors = ClientErrors{ + NonceTooLow: ptr("too-low"), + NonceTooHigh: ptr("too-high"), + ReplacementTransactionUnderpriced: ptr("under"), + LimitReached: ptr("limit"), + TransactionAlreadyInMempool: ptr("already"), + TerminallyUnderpriced: ptr("terminal"), + InsufficientEth: ptr("insufficient"), + TxFeeExceedsCap: ptr("exceeds"), + L2FeeTooLow: ptr("low-fee"), + L2FeeTooHigh: ptr("high-fee"), + L2Full: ptr("full"), + TransactionAlreadyMined: ptr("mined"), + Fatal: ptr("fatal"), + ServiceUnavailable: ptr("unavailable"), + TooManyResults: ptr("too-many"), + } + + configtest.AssertFieldsNotNil(t, unknown) +} + +func TestDocs(t *testing.T) { + t.Run("complete", func(t *testing.T) { + configtest.AssertDocsTOMLComplete[EVMConfig](t, docsTOML) + }) + + t.Run("aligned", func(t *testing.T) { + var docDefaults EVMConfig + require.NoError(t, configtest.DocDefaultsOnly(strings.NewReader(docsTOML), &docDefaults, config.DecodeTOML)) + + require.Equal(t, chaintype.ChainType(""), docDefaults.ChainType.ChainType()) + docDefaults.ChainType = nil + + // clean up KeySpecific as a special case + require.Len(t, docDefaults.KeySpecific, 1) + ks := KeySpecific{Key: new(types.EIP55Address), + GasEstimator: KeySpecificGasEstimator{PriceMax: new(assets.Wei)}} + require.Equal(t, ks, docDefaults.KeySpecific[0]) + docDefaults.KeySpecific = nil + + // EVM.GasEstimator.BumpTxDepth doesn't have a constant default - it is derived from another field + require.Zero(t, *docDefaults.GasEstimator.BumpTxDepth) + docDefaults.GasEstimator.BumpTxDepth = nil + + // per-job limits are nilable + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.OCR) + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.OCR2) + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.DR) + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.Keeper) + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.VRF) + require.Zero(t, *docDefaults.GasEstimator.LimitJobType.FM) + docDefaults.GasEstimator.LimitJobType = GasLimitJobType{} + + // EIP1559FeeCapBufferBlocks doesn't have a constant default - it is derived from another field + require.Zero(t, *docDefaults.GasEstimator.BlockHistory.EIP1559FeeCapBufferBlocks) + docDefaults.GasEstimator.BlockHistory.EIP1559FeeCapBufferBlocks = nil + + // addresses w/o global values + require.Zero(t, *docDefaults.FlagsContractAddress) + require.Zero(t, *docDefaults.LinkContractAddress) + require.Zero(t, *docDefaults.OperatorFactoryAddress) + docDefaults.FlagsContractAddress = nil + docDefaults.LinkContractAddress = nil + docDefaults.OperatorFactoryAddress = nil + require.Empty(t, docDefaults.Workflow.FromAddress) + require.Empty(t, docDefaults.Workflow.ForwarderAddress) + gasLimitDefault := uint64(400_000) + require.Equal(t, &gasLimitDefault, docDefaults.Workflow.GasLimitDefault) + + docDefaults.Workflow.FromAddress = nil + docDefaults.Workflow.ForwarderAddress = nil + docDefaults.Workflow.GasLimitDefault = &gasLimitDefault + docDefaults.NodePool.Errors = ClientErrors{} + + // Transactions.AutoPurge configs are only set if the feature is enabled + docDefaults.Transactions.AutoPurge.DetectionApiUrl = nil + docDefaults.Transactions.AutoPurge.Threshold = nil + docDefaults.Transactions.AutoPurge.MinAttempts = nil + + // TransactionManagerV2 configs are only set if the feature is enabled + docDefaults.Transactions.TransactionManagerV2.BlockTime = nil + docDefaults.Transactions.TransactionManagerV2.CustomURL = nil + docDefaults.Transactions.TransactionManagerV2.DualBroadcast = nil + + // Fallback DA oracle is not set + docDefaults.GasEstimator.DAOracle = DAOracle{} + + fallbackDefaults := Defaults(nil) + assertTOML(t, fallbackDefaults, docDefaults.Chain) + }) +} + +//go:embed testdata/config-full.toml +var fullTOML string + +var fullConfig = EVMConfig{ + ChainID: big.NewI(42), + Enabled: ptr(false), + Chain: Chain{ + AutoCreateKey: ptr(false), + BalanceMonitor: BalanceMonitor{ + Enabled: ptr(true), + }, + BlockBackfillDepth: ptr[uint32](100), + BlockBackfillSkip: ptr(true), + ChainType: chaintype.NewConfig("Optimism"), + FinalityDepth: ptr[uint32](42), + FinalityTagEnabled: ptr[bool](true), + FlagsContractAddress: ptr(types.MustEIP55Address("0xae4E781a6218A8031764928E88d457937A954fC3")), + FinalizedBlockOffset: ptr[uint32](16), + + GasEstimator: GasEstimator{ + Mode: ptr("SuggestedPrice"), + EIP1559DynamicFees: ptr(true), + BumpPercent: ptr[uint16](10), + BumpThreshold: ptr[uint32](6), + BumpTxDepth: ptr[uint32](6), + BumpMin: assets.NewWeiI(100), + FeeCapDefault: assets.NewWeiI(math.MaxInt64), + LimitDefault: ptr[uint64](12), + LimitMax: ptr[uint64](17), + LimitMultiplier: ptr(decimal.RequireFromString("1.234")), + LimitTransfer: ptr[uint64](100), + EstimateLimit: ptr(false), + TipCapDefault: assets.NewWeiI(2), + TipCapMin: assets.NewWeiI(1), + PriceDefault: assets.NewWeiI(math.MaxInt64), + PriceMax: assets.NewWei(new(stdbig.Int).SetBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})), + PriceMin: assets.NewWeiI(13), + + DAOracle: DAOracle{ + OracleType: ptr(DAOracleOPStack), + OracleAddress: ptr(types.MustEIP55Address("0xae4E781a6218A8031764928E88d457937A954fC3")), + CustomGasPriceCalldata: ptr("0x1234asdf"), + }, + + LimitJobType: GasLimitJobType{ + OCR: ptr[uint32](1001), + DR: ptr[uint32](1002), + VRF: ptr[uint32](1003), + FM: ptr[uint32](1004), + Keeper: ptr[uint32](1005), + OCR2: ptr[uint32](1006), + }, + + BlockHistory: BlockHistoryEstimator{ + BatchSize: ptr[uint32](17), + BlockHistorySize: ptr[uint16](12), + CheckInclusionBlocks: ptr[uint16](18), + CheckInclusionPercentile: ptr[uint16](19), + EIP1559FeeCapBufferBlocks: ptr[uint16](13), + TransactionPercentile: ptr[uint16](15), + }, + FeeHistory: FeeHistoryEstimator{ + CacheTimeout: config.MustNewDuration(time.Second), + }, + }, + + KeySpecific: []KeySpecific{ + { + Key: ptr(types.MustEIP55Address("0x2a3e23c6f242F5345320814aC8a1b4E58707D292")), + GasEstimator: KeySpecificGasEstimator{ + PriceMax: assets.NewWei(new(stdbig.Int).SetBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})), + }, + }, + }, + + LinkContractAddress: ptr(types.MustEIP55Address("0x538aAaB4ea120b2bC2fe5D296852D948F07D849e")), + LogBackfillBatchSize: ptr[uint32](17), + LogPollInterval: config.MustNewDuration(time.Minute), + LogKeepBlocksDepth: ptr[uint32](100000), + LogPrunePageSize: ptr[uint32](0), + BackupLogPollerBlockDelay: ptr[uint64](532), + MinContractPayment: commonassets.NewLinkFromJuels(math.MaxInt64), + MinIncomingConfirmations: ptr[uint32](13), + NonceAutoSync: ptr(true), + NoNewHeadsThreshold: config.MustNewDuration(time.Minute), + OperatorFactoryAddress: ptr(types.MustEIP55Address("0xa5B85635Be42F21f94F28034B7DA440EeFF0F418")), + LogBroadcasterEnabled: ptr(true), + RPCDefaultBatchSize: ptr[uint32](17), + RPCBlockQueryDelay: ptr[uint16](10), + NoNewFinalizedHeadsThreshold: config.MustNewDuration(time.Hour), + + Transactions: Transactions{ + Enabled: ptr(true), + MaxInFlight: ptr[uint32](19), + MaxQueued: ptr[uint32](99), + ReaperInterval: config.MustNewDuration(time.Minute), + ReaperThreshold: config.MustNewDuration(time.Minute), + ResendAfterThreshold: config.MustNewDuration(time.Hour), + ForwardersEnabled: ptr(true), + AutoPurge: AutoPurgeConfig{ + Enabled: ptr(false), + Threshold: ptr[uint32](42), + MinAttempts: ptr[uint32](13), + DetectionApiUrl: config.MustParseURL("http://example.net"), + }, + TransactionManagerV2: TransactionManagerV2Config{ + Enabled: ptr(false), + DualBroadcast: ptr(true), + BlockTime: config.MustNewDuration(42 * time.Second), + CustomURL: config.MustParseURL("http://txs.org"), + }, + }, + + HeadTracker: HeadTracker{ + HistoryDepth: ptr[uint32](15), + MaxBufferSize: ptr[uint32](17), + SamplingInterval: config.MustNewDuration(time.Hour), + FinalityTagBypass: ptr[bool](false), + MaxAllowedFinalityDepth: ptr[uint32](1500), + PersistenceEnabled: ptr(false), + }, + + NodePool: NodePool{ + PollFailureThreshold: ptr[uint32](5), + PollInterval: config.MustNewDuration(time.Minute), + SelectionMode: ptr(multinode.NodeSelectionModeHighestHead), + SyncThreshold: ptr[uint32](13), + LeaseDuration: config.MustNewDuration(0), + NodeIsSyncingEnabled: ptr(true), + FinalizedBlockPollInterval: config.MustNewDuration(time.Second), + EnforceRepeatableRead: ptr(true), + DeathDeclarationDelay: config.MustNewDuration(time.Minute), + VerifyChainID: ptr(true), + NewHeadsPollInterval: config.MustNewDuration(0), + Errors: ClientErrors{ + NonceTooLow: ptr[string]("(: |^)nonce too low"), + NonceTooHigh: ptr[string]("(: |^)nonce too high"), + ReplacementTransactionUnderpriced: ptr[string]("(: |^)replacement transaction underpriced"), + LimitReached: ptr[string]("(: |^)limit reached"), + TransactionAlreadyInMempool: ptr[string]("(: |^)transaction already in mempool"), + TerminallyUnderpriced: ptr[string]("(: |^)terminally underpriced"), + InsufficientEth: ptr[string]("(: |^)insufficient eth"), + TxFeeExceedsCap: ptr[string]("(: |^)tx fee exceeds cap"), + L2FeeTooLow: ptr[string]("(: |^)l2 fee too low"), + L2FeeTooHigh: ptr[string]("(: |^)l2 fee too high"), + L2Full: ptr[string]("(: |^)l2 full"), + TransactionAlreadyMined: ptr[string]("(: |^)transaction already mined"), + Fatal: ptr[string]("(: |^)fatal"), + ServiceUnavailable: ptr[string]("(: |^)service unavailable"), + TooManyResults: ptr[string]("(: |^)too many results"), + }, + }, + OCR: OCR{ + ContractConfirmations: ptr[uint16](11), + ContractTransmitterTransmitTimeout: config.MustNewDuration(time.Minute), + DatabaseTimeout: config.MustNewDuration(time.Second), + DeltaCOverride: config.MustNewDuration(time.Hour), + DeltaCJitterOverride: config.MustNewDuration(time.Second), + ObservationGracePeriod: config.MustNewDuration(time.Second), + }, + OCR2: OCR2{ + Automation: Automation{ + GasLimit: ptr[uint32](540), + }, + }, + Workflow: Workflow{ + FromAddress: ptr(types.MustEIP55Address("0x627306090abaB3A6e1400e9345bC60c78a8BEf57")), + ForwarderAddress: ptr(types.MustEIP55Address("0x9FBDa871d559710256a2502A2517b794B482Db40")), + GasLimitDefault: ptr[uint64](400000), + }, + }, + Nodes: EVMNodes{ + { + Name: ptr("foo"), + HTTPURL: config.MustParseURL("https://foo.web"), + WSURL: config.MustParseURL("wss://web.socket/test/foo"), + HTTPURLExtraWrite: config.MustParseURL("https://foo.web/extra"), + SendOnly: ptr(false), + Order: ptr[int32](0), + }, + }, +} + +func TestTOMLConfig_FullMarshal(t *testing.T) { + configtest.AssertFullMarshal(t, fullConfig, fullTOML) +} + +func TestTOMLConfig_SetFrom(t *testing.T) { + var config EVMConfig + config.SetFrom(&fullConfig) + require.Equal(t, fullConfig, config) +} + +func ptr[T any](t T) *T { + return &t +} + +func assertTOML[T any](t *testing.T, fallback, docs T) { + t.Helper() + t.Logf("fallback: %#v", fallback) + t.Logf("docs: %#v", docs) + fb, err := toml.Marshal(fallback) + require.NoError(t, err) + db, err := toml.Marshal(docs) + require.NoError(t, err) + fs, ds := string(fb), string(db) + assert.Equal(t, fs, ds, diff.Diff(fs, ds)) +} + +func asEIP55Address(t *testing.T, s string) *types.EIP55Address { + t.Helper() + if !common.IsHexAddress(s) { + t.Fatal("invalid address: " + s) + } + a := types.EIP55AddressFromAddress(common.HexToAddress(s)) + return &a +} diff --git a/pkg/config/toml/defaults.go b/pkg/config/toml/defaults.go index b1d29142cd..4f27dadc67 100644 --- a/pkg/config/toml/defaults.go +++ b/pkg/config/toml/defaults.go @@ -11,7 +11,7 @@ import ( "slices" "strings" - cconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-evm/pkg/config/chaintype" "github.com/smartcontractkit/chainlink-evm/pkg/utils/big" @@ -141,17 +141,17 @@ func readConfig(path string, reader func(name string) ([]byte, error)) (*big.Big return nil, Chain{}, fmt.Errorf("error reading file: %w", err) } - var config = struct { + var cfg = struct { ChainID *big.Big Chain }{} // decode from toml to a chain config - if err := cconfig.DecodeTOML(bytes.NewReader(bts), &config); err != nil { + if err := config.DecodeTOML(bytes.NewReader(bts), &cfg); err != nil { return nil, Chain{}, fmt.Errorf("error in TOML decoding %s: %w", path, err) } - return config.ChainID, config.Chain, nil + return cfg.ChainID, cfg.Chain, nil } // DefaultsNamed returns the default Chain values, optionally for the given chainID, as well as a name if the chainID is known. diff --git a/pkg/config/toml/docs.go b/pkg/config/toml/docs.go new file mode 100644 index 0000000000..e7ed43a8f2 --- /dev/null +++ b/pkg/config/toml/docs.go @@ -0,0 +1,18 @@ +package toml + +import ( + _ "embed" + + "github.com/smartcontractkit/chainlink-common/pkg/config/configdoc" +) + +//go:embed docs.toml +var docsTOML string + +//go:embed example.toml +var exampleConfig string + +func GenerateDocs() (string, error) { + return configdoc.Generate(docsTOML, `[//]: # (Documentation generated from docs.toml - DO NOT EDIT.) +This document describes the TOML format for configuration.`, exampleConfig, nil) +} diff --git a/pkg/config/toml/docs.toml b/pkg/config/toml/docs.toml new file mode 100644 index 0000000000..b61947521b --- /dev/null +++ b/pkg/config/toml/docs.toml @@ -0,0 +1,516 @@ +# ChainID is the EVM chain ID. Mandatory. +ChainID = '1' # Example +# Enabled enables this chain. +Enabled = true # Default +# AutoCreateKey, if set to true, will ensure that there is always at least one transmit key for the given chain. +AutoCreateKey = true # Default +# **ADVANCED** +# BlockBackfillDepth specifies the number of blocks before the current HEAD that the log broadcaster will try to re-consume logs from. +BlockBackfillDepth = 10 # Default +# BlockBackfillSkip enables skipping of very long backfills. +BlockBackfillSkip = false # Default +# ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. +# Available types: `arbitrum`, `celo`, `gnosis`, `hedera`, `kroma`, `metis`, `optimismBedrock`, `scroll`, `wemix`, `xlayer`, `zksync` +ChainType = 'arbitrum' # Example +# FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID, so it should not be necessary to change this under normal operation. +# BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain +# There is not a large performance penalty to setting this relatively high (on the order of hundreds) +# It is practically limited by the number of heads we store in the database and should be less than this with a comfortable margin. +# If a transaction is mined in a block more than this many blocks ago, and is reorged out, we will NOT retransmit this transaction and undefined behaviour can occur including gaps in the nonce sequence that require manual intervention to fix. +# Therefore, this number represents a number of blocks we consider large enough that no re-org this deep will ever feasibly happen. +# +# Special cases: +# `FinalityDepth`=0 would imply that transactions can be final even before they were mined into a block. This is not supported. +# `FinalityDepth`=1 implies that transactions are final after we see them in one block. +# +# Examples: +# +# Transaction sending: +# A transaction is sent at block height 42 +# +# `FinalityDepth` is set to 5 +# A re-org occurs at height 44 starting at block 41, transaction is marked for rebroadcast +# A re-org occurs at height 46 starting at block 41, transaction is marked for rebroadcast +# A re-org occurs at height 47 starting at block 41, transaction is NOT marked for rebroadcast +FinalityDepth = 50 # Default +# FinalityTagEnabled means that the chain supports the finalized block tag when querying for a block. If FinalityTagEnabled is set to true for a chain, then FinalityDepth field is ignored. +# Finality for a block is solely defined by the finality related tags provided by the chain's RPC API. This is a placeholder and hasn't been implemented yet. +FinalityTagEnabled = false # Default +# **ADVANCED** +# FlagsContractAddress can optionally point to a [Flags contract](../contracts/src/v0.8/Flags.sol). If set, the node will lookup that contract for each job that supports flags contracts (currently OCR and FM jobs are supported). If the job's contractAddress is set as hibernating in the FlagsContractAddress address, it overrides the standard update parameters (such as heartbeat/threshold). +FlagsContractAddress = '0xae4E781a6218A8031764928E88d457937A954fC3' # Example +# LinkContractAddress is the canonical ERC-677 LINK token contract address on the given chain. Note that this is usually autodetected from chain ID. +LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' # Example +# **ADVANCED** +# LogBackfillBatchSize sets the batch size for calling FilterLogs when we backfill missing logs. +LogBackfillBatchSize = 1000 # Default +# **ADVANCED** +# LogPollInterval works in conjunction with Feature.LogPoller. Controls how frequently the log poller polls for logs. Defaults to the block production rate. +LogPollInterval = '15s' # Default +# **ADVANCED** +# LogKeepBlocksDepth works in conjunction with Feature.LogPoller. Controls how many blocks the poller will keep, must be greater than FinalityDepth+1. +LogKeepBlocksDepth = 100000 # Default +# **ADVANCED** +# LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. +LogPrunePageSize = 0 # Default +# **ADVANCED** +# BackupLogPollerBlockDelay works in conjunction with Feature.LogPoller. Controls the block delay of Backup LogPoller, affecting how far behind the latest finalized block it starts and how often it runs. +# BackupLogPollerDelay=0 will disable Backup LogPoller (_not recommended for production environment_). +BackupLogPollerBlockDelay = 100 # Default +# MinContractPayment is the minimum payment in LINK required to execute a direct request job. This can be overridden on a per-job basis. +MinContractPayment = '10000000000000 juels' # Default +# MinIncomingConfirmations is the minimum required confirmations before a log event will be consumed. +MinIncomingConfirmations = 3 # Default +# NonceAutoSync enables automatic nonce syncing on startup. Chainlink nodes will automatically try to sync its local nonce with the remote chain on startup and fast forward if necessary. This is almost always safe but can be disabled in exceptional cases by setting this value to false. +NonceAutoSync = true # Default +# NoNewHeadsThreshold controls how long to wait after receiving no new heads before `NodePool` marks rpc endpoints as +# out-of-sync, and `HeadTracker` logs warnings. +# +# Set to zero to disable out-of-sync checking. +NoNewHeadsThreshold = '3m' # Default +# OperatorFactoryAddress is the address of the canonical operator forwarder contract on the given chain. Note that this is usually autodetected from chain ID. +OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' # Example +# RPCDefaultBatchSize is the default batch size for batched RPC calls. +RPCDefaultBatchSize = 250 # Default +# **ADVANCED** +# RPCBlockQueryDelay controls the number of blocks to trail behind head in the block history estimator and balance monitor. +# For example, if this is set to 3, and we receive block 10, block history estimator will fetch block 7. +# +# CAUTION: You might be tempted to set this to 0 to use the latest possible +# block, but it is possible to receive a head BEFORE that block is actually +# available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false +# "zero" blocks that are missing transactions. +RPCBlockQueryDelay = 1 # Default +# FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +# For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +# With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +# Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +# This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +# CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +# PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +# For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +# The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +# Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +# CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. +FinalizedBlockOffset = 0 # Default +# LogBroadcasterEnabled is a feature flag for LogBroadcaster, by default it's true. +LogBroadcasterEnabled = true # Default +# NoNewFinalizedHeadsThreshold controls how long to wait for new finalized block before `NodePool` marks rpc endpoints as +# out-of-sync. Only applicable if `FinalityTagEnabled=true` +# +# Set to zero to disable. +NoNewFinalizedHeadsThreshold = '0' # Default + +[Transactions] +# Enabled is a feature flag for the Transaction Manager. This flag also enables or disables the gas estimator since it is dependent on the TXM to start it. +Enabled = true # Default +# ForwardersEnabled enables or disables sending transactions through forwarder contracts. +ForwardersEnabled = false # Default +# MaxInFlight controls how many transactions are allowed to be "in-flight" i.e. broadcast but unconfirmed at any one time. You can consider this a form of transaction throttling. +# +# The default is set conservatively at 16 because this is a pessimistic minimum that both geth and parity will hold without evicting local transactions. If your node is falling behind and you need higher throughput, you can increase this setting, but you MUST make sure that your ETH node is configured properly otherwise you can get nonce gapped and your node will get stuck. +# +# 0 value disables the limit. Use with caution. +MaxInFlight = 16 # Default +# MaxQueued is the maximum number of unbroadcast transactions per key that are allowed to be enqueued before jobs will start failing and rejecting send of any further transactions. This represents a sanity limit and generally indicates a problem with your ETH node (transactions are not getting mined). +# +# Do NOT blindly increase this value thinking it will fix things if you start hitting this limit because transactions are not getting mined, you will instead only make things worse. +# +# In deployments with very high burst rates, or on chains with large re-orgs, you _may_ consider increasing this. +# +# 0 value disables any limit on queue size. Use with caution. +MaxQueued = 250 # Default +# ReaperInterval controls how often the EthTx reaper will run. +ReaperInterval = '1h' # Default +# ReaperThreshold indicates how old an EthTx ought to be before it can be reaped. +ReaperThreshold = '168h' # Default +# ResendAfterThreshold controls how long to wait before re-broadcasting a transaction that has not yet been confirmed. +ResendAfterThreshold = '1m' # Default + +[Transactions.AutoPurge] +# Enabled enables or disables automatically purging transactions that have been idenitified as terminally stuck (will never be included on-chain). This feature is only expected to be used by ZK chains. +Enabled = false # Default +# DetectionApiUrl configures the base url of a custom endpoint used to identify terminally stuck transactions. +DetectionApiUrl = 'https://example.api.io' # Example +# Threshold configures the number of blocks a transaction has to remain unconfirmed before it is evaluated for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. +Threshold = 5 # Example +# MinAttempts configures the minimum number of broadcasted attempts a transaction has to have before it is evaluated further for being terminally stuck. This threshold is only applied if there is no custom API to identify stuck transactions provided by the chain. Ensure the gas estimator configs take more bump attempts before reaching the configured max gas price. +MinAttempts = 3 # Example + +[Transactions.TransactionManagerV2] +# Enabled enables TransactionManagerV2. +Enabled = false # Default +# BlockTime controls the frequency of the backfill loop of TransactionManagerV2. +BlockTime = '10s' # Example +# CustomURL configures the base url of a custom endpoint used by the ChainDualBroadcast chain type. +CustomURL = 'https://example.api.io' # Example +# DualBroadcast enables DualBroadcast functionality. +DualBroadcast = false # Example + +[BalanceMonitor] +# Enabled balance monitoring for all keys. +Enabled = true # Default + +[GasEstimator] +# Mode controls what type of gas estimator is used. +# +# - `FixedPrice` uses static configured values for gas price (can be set via API call). +# - `BlockHistory` dynamically adjusts default gas price based on heuristics from mined blocks. +# - `L2Suggested` mode is deprecated and replaced with `SuggestedPrice`. +# - `SuggestedPrice` is a mode which uses the gas price suggested by the rpc endpoint via `eth_gasPrice`. +# - `Arbitrum` is a special mode only for use with Arbitrum blockchains. It uses the suggested gas price (up to `ETH_MAX_GAS_PRICE_WEI`, with `1000 gwei` default) as well as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` default). +# +# Chainlink nodes decide what gas price to use using an `Estimator`. It ships with several simple and battle-hardened built-in estimators that should work well for almost all use-cases. Note that estimators will change their behaviour slightly depending on if you are in EIP-1559 mode or not. +# +# You can also use your own estimator for gas price by selecting the `FixedPrice` estimator and using the exposed API to set the price. +# +# An important point to note is that the Chainlink node does _not_ ship with built-in support for go-ethereum's `estimateGas` call. This is for several reasons, including security and reliability. We have found empirically that it is not generally safe to rely on the remote ETH node's idea of what gas price should be. +Mode = 'BlockHistory' # Default +# PriceDefault is the default gas price to use when submitting transactions to the blockchain. Will be overridden by the built-in `BlockHistoryEstimator` if enabled, and might be increased if gas bumping is enabled. +# +# (Only applies to legacy transactions) +# +# Can be used with the `chainlink setgasprice` to be updated while the node is still running. +PriceDefault = '20 gwei' # Default +# PriceMax is the maximum gas price. Chainlink nodes will never pay more than this for a transaction. +# This applies to both legacy and EIP1559 transactions. +# Note that it is impossible to disable the maximum limit. Setting this value to zero will prevent paying anything for any transaction (which can be useful in some rare cases). +# Most chains by default have the maximum set to 2**256-1 Wei which is the maximum allowed gas price on EVM-compatible chains, and is so large it may as well be unlimited. +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' # Default +# PriceMin is the minimum gas price. Chainlink nodes will never pay less than this for a transaction. +# +# (Only applies to legacy transactions) +# +# It is possible to force the Chainlink node to use a fixed gas price by setting a combination of these, e.g. +# +# ```toml +# EIP1559DynamicFees = false +# PriceMax = 100 +# PriceMin = 100 +# PriceDefault = 100 +# BumpThreshold = 0 +# Mode = 'FixedPrice' +# ``` +PriceMin = '1 gwei' # Default +# LimitDefault sets default gas limit for outgoing transactions. This should not need to be changed in most cases. +# Some job types, such as Keeper jobs, might set their own gas limit unrelated to this value. +LimitDefault = 500_000 # Default +# LimitMax sets a maximum for _estimated_ gas limits. This currently only applies to `Arbitrum` `GasEstimatorMode`. +LimitMax = 500_000 # Default +# LimitMultiplier is the factor by which a transaction's GasLimit is multiplied before transmission. So if the value is 1.1, and the GasLimit for a transaction is 10, 10% will be added before transmission. +# +# This factor is always applied, so includes L2 transactions which uses a default gas limit of 1 and is also applied to `LimitDefault`. +LimitMultiplier = '1.0' # Default +# LimitTransfer is the gas limit used for an ordinary ETH transfer. +LimitTransfer = 21_000 # Default +# EstimateLimit enables estimating gas limits for transactions. This feature respects the gas limit provided during transaction creation as an upper bound. +EstimateLimit = false # Default +# BumpMin is the minimum fixed amount of wei by which gas is bumped on each transaction attempt. +BumpMin = '5 gwei' # Default +# BumpPercent is the percentage by which to bump gas on a transaction that has exceeded `BumpThreshold`. The larger of `BumpPercent` and `BumpMin` is taken for gas bumps. +# +# The `SuggestedPriceEstimator` adds the larger of `BumpPercent` and `BumpMin` on top of the price provided by the RPC when bumping a transaction's gas. +BumpPercent = 20 # Default +# BumpThreshold is the number of blocks to wait for a transaction stuck in the mempool before automatically bumping the gas price. Set to 0 to disable gas bumping completely. +BumpThreshold = 3 # Default +# BumpTxDepth is the number of transactions to gas bump starting from oldest. Set to 0 for no limit (i.e. bump all). Can not be greater than EVM.Transactions.MaxInFlight. If not set, defaults to EVM.Transactions.MaxInFlight. +BumpTxDepth = 16 # Example +# EIP1559DynamicFees torces EIP-1559 transaction mode. Enabling EIP-1559 mode can help reduce gas costs on chains that support it. This is supported only on official Ethereum mainnet and testnets. It is not recommended to enable this setting on Polygon because the EIP-1559 fee market appears to be broken on all Polygon chains and EIP-1559 transactions are less likely to be included than legacy transactions. +# +# #### Technical details +# +# Chainlink nodes include experimental support for submitting transactions using type 0x2 (EIP-1559) envelope. +# +# EIP-1559 mode is enabled by default on the Ethereum Mainnet, but can be enabled on a per-chain basis or globally. +# +# This might help to save gas on spikes. Chainlink nodes should react faster on the upleg and avoid overpaying on the downleg. It might also be possible to set `EVM.GasEstimator.BlockHistory.BatchSize` to a smaller value such as 12 or even 6 because tip cap should be a more consistent indicator of inclusion time than total gas price. This would make Chainlink nodes more responsive and should reduce response time variance. Some experimentation is required to find optimum settings. +# +# Set with caution, if you set this on a chain that does not actually support EIP-1559 your node will be broken. +# +# In EIP-1559 mode, the total price for the transaction is the minimum of base fee + tip cap and fee cap. More information can be found on the [official EIP](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). +# +# Chainlink's implementation of EIP-1559 works as follows: +# +# If you are using FixedPriceEstimator: +# - With gas bumping disabled, it will submit all transactions with `feecap=PriceMax` and `tipcap=GasTipCapDefault` +# - With gas bumping enabled, it will submit all transactions initially with `feecap=GasFeeCapDefault` and `tipcap=GasTipCapDefault`. +# +# If you are using BlockHistoryEstimator (default for most chains): +# - With gas bumping disabled, it will submit all transactions with `feecap=PriceMax` and `tipcap=` +# - With gas bumping enabled (default for most chains) it will submit all transactions initially with `feecap = ( current block base fee * (1.125 ^ N) + tipcap )` where N is configurable by setting `EVM.GasEstimator.BlockHistory.EIP1559FeeCapBufferBlocks` but defaults to `gas bump threshold+1` and `tipcap=` +# +# Bumping works as follows: +# +# - Increase tipcap by `max(tipcap * (1 + BumpPercent), tipcap + BumpMin)` +# - Increase feecap by `max(feecap * (1 + BumpPercent), feecap + BumpMin)` +# +# A quick note on terminology - Chainlink nodes use the same terms used internally by go-ethereum source code to describe various prices. This is not the same as the externally used terms. For reference: +# +# - Base Fee Per Gas = BaseFeePerGas +# - Max Fee Per Gas = FeeCap +# - Max Priority Fee Per Gas = TipCap +# +# In EIP-1559 mode, the following changes occur to how configuration works: +# +# - All new transactions will be sent as type 0x2 transactions specifying a TipCap and FeeCap. Be aware that existing pending legacy transactions will continue to be gas bumped in legacy mode. +# - `BlockHistoryEstimator` will apply its calculations (gas percentile etc) to the TipCap and this value will be used for new transactions (GasPrice will be ignored) +# - `FixedPriceEstimator` will use `GasTipCapDefault` instead of `GasPriceDefault` for the tip cap +# - `FixedPriceEstimator` will use `GasFeeCapDefault` instaed of `GasPriceDefault` for the fee cap +# - `PriceMin` is ignored for new transactions and `GasTipCapMinimum` is used instead (default 0) +# - `PriceMax` still represents that absolute upper limit that Chainlink will ever spend (total) on a single tx +# - `Keeper.GasTipCapBufferPercent` is ignored in EIP-1559 mode and `Keeper.GasTipCapBufferPercent` is used instead +EIP1559DynamicFees = false # Default +# FeeCapDefault controls the fixed initial fee cap, if EIP1559 mode is enabled and `FixedPrice` gas estimator is used. +FeeCapDefault = '100 gwei' # Default +# TipCapDefault is the default gas tip to use when submitting transactions to the blockchain. Will be overridden by the built-in `BlockHistoryEstimator` if enabled, and might be increased if gas bumping is enabled. +# +# (Only applies to EIP-1559 transactions) +TipCapDefault = '1 wei' # Default +# TipCapMinimum is the minimum gas tip to use when submitting transactions to the blockchain. +# +# (Only applies to EIP-1559 transactions) +TipCapMin = '1 wei' # Default + +[GasEstimator.DAOracle] +# OracleType refers to the oracle family this config belongs to. Currently the available oracle types are: 'opstack', 'arbitrum', 'zksync', and 'custom_calldata'. +OracleType = 'opstack' # Example +# OracleAddress is the address of the oracle contract. +OracleAddress = '0x420000000000000000000000000000000000000F' # Example +# CustomGasPriceCalldata is optional and can be set to call a custom gas price function at the given OracleAddress. +CustomGasPriceCalldata = '' # Default + +[GasEstimator.LimitJobType] +# OCR overrides LimitDefault for OCR jobs. +OCR = 100_000 # Example +# OCR2 overrides LimitDefault for OCR2 jobs. +OCR2 = 100_000 # Example +# DR overrides LimitDefault for Direct Request jobs. +DR = 100_000 # Example +# VRF overrides LimitDefault for VRF jobs. +VRF = 100_000 # Example +# FM overrides LimitDefault for Flux Monitor jobs. +FM = 100_000 # Example +# Keeper overrides LimitDefault for Keeper jobs. +Keeper = 100_000 # Example + + +# These settings allow you to configure how your node calculates gas prices when using the block history estimator. +# In most cases, leaving these values at their defaults should give good results. +[GasEstimator.BlockHistory] +# BatchSize sets the maximum number of blocks to fetch in one batch in the block history estimator. +# If the `BatchSize` variable is set to 0, it defaults to `EVM.RPCDefaultBatchSize`. +BatchSize = 25 # Default +# BlockHistorySize controls the number of past blocks to keep in memory to use as a basis for calculating a percentile gas price. +BlockHistorySize = 8 # Default +# CheckInclusionBlocks is the number of recent blocks to use to detect if there is a transaction propagation/connectivity issue, and to prevent bumping in these cases. +# This can help avoid the situation where RPC nodes are not propagating transactions for some non-price-related reason (e.g. go-ethereum bug, networking issue etc) and bumping gas would not help. +# +# Set to zero to disable connectivity checking completely. +CheckInclusionBlocks = 12 # Default +# CheckInclusionPercentile controls the percentile that a transaction must have been higher than for all the blocks in the inclusion check window in order to register as a connectivity issue. +# +# For example, if CheckInclusionBlocks=12 and CheckInclusionPercentile=90 then further bumping will be prevented for any transaction with any attempt that has a higher price than the 90th percentile for the most recent 12 blocks. +CheckInclusionPercentile = 90 # Default +# **ADVANCED** +# EIP1559FeeCapBufferBlocks controls the buffer blocks to add to the current base fee when sending a transaction. By default, the gas bumping threshold + 1 block is used. +# +# (Only applies to EIP-1559 transactions) +EIP1559FeeCapBufferBlocks = 13 # Example +# TransactionPercentile specifies gas price to choose. E.g. if the block history contains four transactions with gas prices `[100, 200, 300, 400]` then picking 25 for this number will give a value of 200. If the calculated gas price is higher than `GasPriceDefault` then the higher price will be used as the base price for new transactions. +# +# Must be in range 0-100. +# +# Only has an effect if gas updater is enabled. +# +# Think of this number as an indicator of how aggressive you want your node to price its transactions. +# +# Setting this number higher will cause the Chainlink node to select higher gas prices. +# +# Setting it lower will tend to set lower gas prices. +TransactionPercentile = 60 # Default + +[GasEstimator.FeeHistory] +# CacheTimeout is the time to wait in order to refresh the cached values stored in the FeeHistory estimator. A small jitter is applied so the timeout won't be exactly the same each time. +# +# You want this value to be close to the block time. For slower chains, like Ethereum, you can set it to 12s, the same as the block time. For faster chains you can skip a block or two +# and set it to two times the block time i.e. on Optimism you can set it to 4s. Ideally, you don't want to go lower than 1s since the RTT times of the RPC requests will be comparable to +# the timeout. The estimator is already adding a buffer to account for a potential increase in prices within one or two blocks. On the other hand, slower frequency will fail to refresh +# the prices and end up in stale values. +CacheTimeout = '10s' # Default + +# The head tracker continually listens for new heads from the chain. +# +# In addition to these settings, it log warnings if `EVM.NoNewHeadsThreshold` is exceeded without any new blocks being emitted. +[HeadTracker] +# HistoryDepth tracks the top N blocks on top of the latest finalized block to keep in the `heads` database table. +# Note that this can easily result in MORE than `N + finality depth` records since in the case of re-orgs we keep multiple heads for a particular block height. +# Higher values help reduce number of RPC requests performed by TXM's Finalizer and improve TXM's Confirmer reorg protection on restarts. +# At the same time, setting the value too high could lead to higher CPU consumption. The following formula could be used to calculate the optimal value: `expected_downtime_on_restart/block_time`. +HistoryDepth = 100 # Default +# MaxBufferSize is the maximum number of heads that may be +# buffered in front of the head tracker before older heads start to be +# dropped. You may think of it as something like the maximum permittable "lag" +# for the head tracker before we start dropping heads to keep up. +MaxBufferSize = 3 # Default +# **ADVANCED** +# SamplingInterval means that head tracker callbacks will at maximum be made once in every window of this duration. This is a performance optimisation for fast chains. Set to 0 to disable sampling entirely. +SamplingInterval = '1s' # Default +# FinalityTagBypass disables FinalityTag support in HeadTracker and makes it track blocks up to FinalityDepth from the most recent head. +# It should only be used on chains with an extremely large actual finality depth (the number of blocks between the most recent head and the latest finalized block). +# Has no effect if `FinalityTagsEnabled` = false +FinalityTagBypass = true # Default +# MaxAllowedFinalityDepth - defines maximum number of blocks between the most recent head and the latest finalized block. +# If actual finality depth exceeds this number, HeadTracker aborts backfill and returns an error. +# Has no effect if `FinalityTagsEnabled` = false +MaxAllowedFinalityDepth = 10000 # Default +# PersistenceEnabled defines whether HeadTracker needs to store heads in the database. +# Persistence is helpful on chains with large finality depth, where fetching blocks from the latest to the latest finalized takes a lot of time. +# On chains with fast finality, the persistence layer does not improve the chain's load time and only consumes database resources (mainly IO). +# NOTE: persistence should not be disabled for products that use LogBroadcaster, as it might lead to missed on-chain events. +PersistenceEnabled = true # Default + +[[KeySpecific]] +# Key is the account to apply these settings to +Key = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +# GasEstimator.PriceMax overrides the maximum gas price for this key. See EVM.GasEstimator.PriceMax. +GasEstimator.PriceMax = '79 gwei' # Example + +# The node pool manages multiple RPC endpoints. +# +# In addition to these settings, `EVM.NoNewHeadsThreshold` controls how long to wait after receiving no new heads before marking the node as out-of-sync. +[NodePool] +# PollFailureThreshold indicates how many consecutive polls must fail in order to mark a node as unreachable. +# +# Set to zero to disable poll checking. +PollFailureThreshold = 5 # Default +# PollInterval controls how often to poll the node to check for liveness. +# +# Set to zero to disable poll checking. +PollInterval = '10s' # Default +# SelectionMode controls node selection strategy: +# - HighestHead: use the node with the highest head number +# - RoundRobin: rotate through nodes, per-request +# - PriorityLevel: use the node with the smallest order number +# - TotalDifficulty: use the node with the greatest total difficulty +SelectionMode = 'HighestHead' # Default +# SyncThreshold controls how far a node may lag behind the best node before being marked out-of-sync. +# Depending on `SelectionMode`, this represents a difference in the number of blocks (`HighestHead`, `RoundRobin`, `PriorityLevel`), or total difficulty (`TotalDifficulty`). +# +# Set to 0 to disable this check. +SyncThreshold = 5 # Default +# LeaseDuration is the minimum duration that the selected "best" node (as defined by SelectionMode) will be used, +# before switching to a better one if available. It also controls how often the lease check is done. +# Setting this to a low value (under 1m) might cause RPC to switch too aggressively. +# Recommended value is over 5m +# +# Set to '0s' to disable +LeaseDuration = '0s' # Default +# NodeIsSyncingEnabled is a flag that enables `syncing` health check on each reconnection to an RPC. +# Node transitions and remains in `Syncing` state while RPC signals this state (In case of Ethereum `eth_syncing` returns anything other than false). +# All of the requests to node in state `Syncing` are rejected. +# +# Set true to enable this check +NodeIsSyncingEnabled = false # Default +# FinalizedBlockPollInterval controls how often to poll RPC for new finalized blocks. +# The finalized block is only used to report to the `pool_rpc_node_highest_finalized_block` metric. We plan to use it +# in RPCs health assessment in the future. +# If `FinalityTagEnabled = false`, poll is not performed and `pool_rpc_node_highest_finalized_block` is +# reported based on latest block and finality depth. +# +# Set to 0 to disable. +FinalizedBlockPollInterval = '5s' # Default +# EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +# `highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +# block. +# +# Set false to disable +EnforceRepeatableRead = true # Default +# DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +# Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +# trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +# Should be greater than `FinalizedBlockPollInterval`. +# Unhealthy RPC will not be picked to handle a request even if this option is set to a nonzero value. +DeathDeclarationDelay = '1m' # Default +# NewHeadsPollInterval define an interval for polling new block periodically using http client rather than subscribe to ws feed +# +# Set to 0 to disable. +NewHeadsPollInterval = '0s' # Default +# VerifyChainID enforces RPC Client ChainIDs to match configured ChainID +VerifyChainID = true # Default +# **ADVANCED** +# Errors enable the node to provide custom regex patterns to match against error messages from RPCs. +[NodePool.Errors] +# NonceTooLow is a regex pattern to match against nonce too low errors. +NonceTooLow = '(: |^)nonce too low' # Example +# NonceTooHigh is a regex pattern to match against nonce too high errors. +NonceTooHigh = '(: |^)nonce too high' # Example +# ReplacementTransactionUnderpriced is a regex pattern to match against replacement transaction underpriced errors. +ReplacementTransactionUnderpriced = '(: |^)replacement transaction underpriced' # Example +# LimitReached is a regex pattern to match against limit reached errors. +LimitReached = '(: |^)limit reached' # Example +# TransactionAlreadyInMempool is a regex pattern to match against transaction already in mempool errors. +TransactionAlreadyInMempool = '(: |^)transaction already in mempool' # Example +# TerminallyUnderpriced is a regex pattern to match against terminally underpriced errors. +TerminallyUnderpriced = '(: |^)terminally underpriced' # Example +# InsufficientEth is a regex pattern to match against insufficient eth errors. +InsufficientEth = '(: |^)insufficeint eth' # Example +# TxFeeExceedsCap is a regex pattern to match against tx fee exceeds cap errors. +TxFeeExceedsCap = '(: |^)tx fee exceeds cap' # Example +# L2FeeTooLow is a regex pattern to match against l2 fee too low errors. +L2FeeTooLow = '(: |^)l2 fee too low' # Example +# L2FeeTooHigh is a regex pattern to match against l2 fee too high errors. +L2FeeTooHigh = '(: |^)l2 fee too high' # Example +# L2Full is a regex pattern to match against l2 full errors. +L2Full = '(: |^)l2 full' # Example +# TransactionAlreadyMined is a regex pattern to match against transaction already mined errors. +TransactionAlreadyMined = '(: |^)transaction already mined' # Example +# Fatal is a regex pattern to match against fatal errors. +Fatal = '(: |^)fatal' # Example +# ServiceUnavailable is a regex pattern to match against service unavailable errors. +ServiceUnavailable = '(: |^)service unavailable' # Example +# TooManyResults is a regex pattern to match an eth_getLogs error indicating the result set is too large to return +TooManyResults = '(: |^)too many results' # Example + +[OCR] +# ContractConfirmations sets `OCR.ContractConfirmations` for this EVM chain. +ContractConfirmations = 4 # Default +# ContractTransmitterTransmitTimeout sets `OCR.ContractTransmitterTransmitTimeout` for this EVM chain. +ContractTransmitterTransmitTimeout = '10s' # Default +# DatabaseTimeout sets `OCR.DatabaseTimeout` for this EVM chain. +DatabaseTimeout = '10s' # Default +# **ADVANCED** +# DeltaCOverride (and `DeltaCJitterOverride`) determine the config override DeltaC. +# DeltaC is the maximum age of the latest report in the contract. If the maximum age is exceeded, a new report will be +# created by the report generation protocol. +DeltaCOverride = "168h" # Default +# **ADVANCED** +# DeltaCJitterOverride is the range for jitter to add to `DeltaCOverride`. +DeltaCJitterOverride = "1h" # Default +# ObservationGracePeriod sets `OCR.ObservationGracePeriod` for this EVM chain. +ObservationGracePeriod = '1s' # Default + +[[Nodes]] +# Name is a unique (per-chain) identifier for this node. +Name = 'foo' # Example +# WSURL is the WS(S) endpoint for this node. Required for primary nodes when `LogBroadcasterEnabled` is `true` +WSURL = 'wss://web.socket/test' # Example +# HTTPURL is the HTTP(S) endpoint for this node. Required for all nodes. +HTTPURL = 'https://foo.web' # Example +# HTTPURLExtraWrite is the HTTP(S) endpoint used for chains that require a separate endpoint for writing on-chain. +HTTPURLExtraWrite = 'https://foo.web/extra' # Example +# SendOnly limits usage to sending transaction broadcasts only. With this enabled, only HTTPURL is required, and WSURL is not used. +SendOnly = false # Default +# Order of the node in the pool, will takes effect if `SelectionMode` is `PriorityLevel` or will be used as a tie-breaker for `HighestHead` and `TotalDifficulty` +Order = 100 # Default + +[OCR2.Automation] +# GasLimit controls the gas limit for transmit transactions from ocr2automation job. +GasLimit = 5400000 # Default + +[Workflow] +# FromAddress is Address of the transmitter key to use for workflow writes. +FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +# ForwarderAddress is the keystone forwarder contract address on chain. +ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +# GasLimitDefault is the default gas limit for workflow transactions. +GasLimitDefault = 400_000 # Default diff --git a/pkg/config/toml/example.toml b/pkg/config/toml/example.toml new file mode 100644 index 0000000000..5704bb362b --- /dev/null +++ b/pkg/config/toml/example.toml @@ -0,0 +1,6 @@ +ChainID = '1' # Required + +[[Nodes]] +Name = 'fake' # Required +WSURL = 'wss://foo.bar/ws' +HTTPURL = 'https://foo.bar' # Required diff --git a/pkg/config/toml/testdata/config-full.toml b/pkg/config/toml/testdata/config-full.toml new file mode 100644 index 0000000000..9a364dc43c --- /dev/null +++ b/pkg/config/toml/testdata/config-full.toml @@ -0,0 +1,161 @@ +ChainID = '42' +Enabled = false +AutoCreateKey = false +BlockBackfillDepth = 100 +BlockBackfillSkip = true +ChainType = 'Optimism' +FinalityDepth = 42 +FinalityTagEnabled = true +FlagsContractAddress = '0xae4E781a6218A8031764928E88d457937A954fC3' +LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' +LogBackfillBatchSize = 17 +LogPollInterval = '1m0s' +LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 532 +MinIncomingConfirmations = 13 +MinContractPayment = '9.223372036854775807 link' +NonceAutoSync = true +NoNewHeadsThreshold = '1m0s' +OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' +LogBroadcasterEnabled = true +RPCDefaultBatchSize = 17 +RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 16 +NoNewFinalizedHeadsThreshold = '1h0m0s' + +[Transactions] +Enabled = true +ForwardersEnabled = true +MaxInFlight = 19 +MaxQueued = 99 +ReaperInterval = '1m0s' +ReaperThreshold = '1m0s' +ResendAfterThreshold = '1h0m0s' + +[Transactions.AutoPurge] +Enabled = false +Threshold = 42 +MinAttempts = 13 +DetectionApiUrl = 'http://example.net' + +[Transactions.TransactionManagerV2] +Enabled = false +BlockTime = '42s' +CustomURL = 'http://txs.org' +DualBroadcast = true + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'SuggestedPrice' +PriceDefault = '9.223372036854775807 ether' +PriceMax = '281.474976710655 micro' +PriceMin = '13 wei' +LimitDefault = 12 +LimitMax = 17 +LimitMultiplier = '1.234' +LimitTransfer = 100 +EstimateLimit = false +BumpMin = '100 wei' +BumpPercent = 10 +BumpThreshold = 6 +BumpTxDepth = 6 +EIP1559DynamicFees = true +FeeCapDefault = '9.223372036854775807 ether' +TipCapDefault = '2 wei' +TipCapMin = '1 wei' + +[GasEstimator.LimitJobType] +OCR = 1001 +OCR2 = 1006 +DR = 1002 +VRF = 1003 +FM = 1004 +Keeper = 1005 + +[GasEstimator.BlockHistory] +BatchSize = 17 +BlockHistorySize = 12 +CheckInclusionBlocks = 18 +CheckInclusionPercentile = 19 +EIP1559FeeCapBufferBlocks = 13 +TransactionPercentile = 15 + +[GasEstimator.FeeHistory] +CacheTimeout = '1s' + +[GasEstimator.DAOracle] +OracleType = 'opstack' +OracleAddress = '0xae4E781a6218A8031764928E88d457937A954fC3' +CustomGasPriceCalldata = '0x1234asdf' + +[HeadTracker] +HistoryDepth = 15 +MaxBufferSize = 17 +SamplingInterval = '1h0m0s' +MaxAllowedFinalityDepth = 1500 +FinalityTagBypass = false +PersistenceEnabled = false + +[[KeySpecific]] +Key = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' + +[KeySpecific.GasEstimator] +PriceMax = '79.228162514264337593543950335 gether' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '1m0s' +SelectionMode = 'HighestHead' +SyncThreshold = 13 +LeaseDuration = '0s' +NodeIsSyncingEnabled = true +FinalizedBlockPollInterval = '1s' +EnforceRepeatableRead = true +DeathDeclarationDelay = '1m0s' +NewHeadsPollInterval = '0s' +VerifyChainID = true + +[NodePool.Errors] +NonceTooLow = '(: |^)nonce too low' +NonceTooHigh = '(: |^)nonce too high' +ReplacementTransactionUnderpriced = '(: |^)replacement transaction underpriced' +LimitReached = '(: |^)limit reached' +TransactionAlreadyInMempool = '(: |^)transaction already in mempool' +TerminallyUnderpriced = '(: |^)terminally underpriced' +InsufficientEth = '(: |^)insufficient eth' +TxFeeExceedsCap = '(: |^)tx fee exceeds cap' +L2FeeTooLow = '(: |^)l2 fee too low' +L2FeeTooHigh = '(: |^)l2 fee too high' +L2Full = '(: |^)l2 full' +TransactionAlreadyMined = '(: |^)transaction already mined' +Fatal = '(: |^)fatal' +ServiceUnavailable = '(: |^)service unavailable' +TooManyResults = '(: |^)too many results' + +[OCR] +ContractConfirmations = 11 +ContractTransmitterTransmitTimeout = '1m0s' +DatabaseTimeout = '1s' +DeltaCOverride = '1h0m0s' +DeltaCJitterOverride = '1s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 540 + +[Workflow] +FromAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57' +ForwarderAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40' +GasLimitDefault = 400000 + +[[Nodes]] +Name = 'foo' +WSURL = 'wss://web.socket/test/foo' +HTTPURL = 'https://foo.web' +HTTPURLExtraWrite = 'https://foo.web/extra' +SendOnly = false +Order = 0