From 771f9a9dcf52b5befeada9ecf7c4bf14d3bf32d3 Mon Sep 17 00:00:00 2001 From: Rembrandt Kuipers <50174308+RembrandtK@users.noreply.github.com> Date: Tue, 9 Dec 2025 18:05:39 +0000 Subject: [PATCH 1/4] docs: document large eligibility period edge case (TRST-L-1) Addresses audit finding TRST-L-1 by documenting the behavior when eligibility period is set to an extremely large value. For never- registered indexers with zero timestamp, when eligibilityPeriod is large enough that (block.timestamp < 0 + eligibilityPeriod) evaluates to true, all indexers become eligible. - Added NatSpec documentation to isEligible() function - Updated contract-level documentation - Added detailed edge case section to RewardsEligibilityOracle.md - Added comprehensive test validating the edge case behavior --- .../eligibility/RewardsEligibilityOracle.md | 14 ++++- .../eligibility/RewardsEligibilityOracle.sol | 17 +++++-- .../RewardsEligibilityOracle.test.ts | 51 +++++++++++++++++++ 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md index 8e0a07eeb..f39a88004 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md @@ -4,7 +4,9 @@ The RewardsEligibilityOracle is a smart contract that manages indexer eligibilit ## Overview -The contract operates on a "deny by default" principle - all indexers are initially ineligible for rewards until their eligibility is explicitly renewed by an authorized oracle. Once eligibility is renewed, indexers remain eligible for a configurable period before their eligibility expires and needs to be renewed again. +The contract operates on a "deny by default" principle - indexers are not eligible for rewards until their eligibility is explicitly validated by an authorized oracle. (See edge case below for extremely large eligibility periods.) After eligibility is initially validated or renewed, indexers remain eligible for a configurable period before their eligibility expires and needs to be renewed again. We generally refer to all validation as "renewal" for simplicity. + +**Very large eligibility period edge case**: If the eligibility period is set to an extremely large value that exceeds the current block timestamp relative to the genesis block, all indexers (including those who have never been registered) will be considered eligible. This is an edge case; if configured with an eligibility period that includes the genesis block, all indexers are eligible. ## Key Features @@ -141,6 +143,16 @@ This design ensures that: - Operators can disable eligibility validation entirely if needed - Individual indexer eligibility has time limits +### Edge Case: Large Eligibility Periods + +The eligibility check `block.timestamp < indexerEligibilityTimestamps[indexer] + eligibilityPeriod` has specific behavior when the eligibility period is set to an extremely large value: + +- For indexers who have never been registered, `indexerEligibilityTimestamps[indexer]` is 0 (zero-initialized storage) +- If `block.timestamp < eligibilityPeriod`, then `block.timestamp < 0 + eligibilityPeriod` +- This means **all indexers are eligible**, including those who have never been explicitly approved + +For normal operations with reasonable eligibility periods (e.g., 14 days), indexers who have never been registered will correctly be ineligible since `block.timestamp < 0 + 14 days` will be false for any realistic block timestamp. + In normal operation, the first condition is expected to be the only one that applies. The other two conditions provide fail-safes for oracle failures, or in extreme cases an operator override. For normal operational failure of oracles, the system gracefully degrades into a "allow all" mode. This mechanism is not perfect in that oracles could still be updating but allowing far fewer indexers than they should. However this is regarded as simple mechanism that is good enough to start with and provide a foundation for future improvements and decentralization. While this simple model allows the criteria for providing good service to evolve over time (which is essential for the long-term health of the network), it captures sufficient information on-chain for indexers to be able to monitor their eligibility. This is important to ensure that even in the absence of other sources of information regarding observed indexer service, indexers have a good transparency about if they are being observed to be providing good service, and for how long their current approval is valid. diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol index 4b5d72acc..d2e85a619 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol @@ -12,9 +12,12 @@ import { BaseUpgradeable } from "../common/BaseUpgradeable.sol"; * @title RewardsEligibilityOracle * @author Edge & Node * @notice This contract allows authorized oracles to mark indexers as eligible to receive rewards - * with an expiration mechanism. Indexers are denied by default until they are explicitly marked as eligible, - * and their eligibility expires after a configurable eligible period. - * The contract also includes a global eligibility check toggle and an oracle update timeout mechanism. + * with an expiration mechanism. Under normal configuration with reasonable eligibility periods, indexers + * are denied by default until they are explicitly marked as eligible, and their eligibility expires after + * a configurable eligible period. The contract also includes a global eligibility check toggle and an + * oracle update timeout mechanism. + * @dev Note: If the eligibility period is set to an extremely large value that exceeds the current + * block timestamp, all indexers (including those never registered) will be eligible. * @custom:security-contact Please email security+contracts@thegraph.com if you find any bugs. We might have an active bug bounty program. */ contract RewardsEligibilityOracle is @@ -216,6 +219,14 @@ contract RewardsEligibilityOracle is /** * @inheritdoc IRewardsEligibility + * @dev Returns true if any of the following conditions are met: + * 1. Eligibility validation is disabled globally + * 2. Oracle timeout has been exceeded (fail-safe to allow all indexers) + * 3. block.timestamp < indexerEligibilityTimestamps[indexer] + eligibilityPeriod + * + * Note on condition 3: For indexers who have never been registered, indexerEligibilityTimestamps[indexer] + * is 0. If eligibilityPeriod is set to an extremely large value exceeding block.timestamp, the check + * becomes (block.timestamp < 0 + eligibilityPeriod), which will be true, making all indexers eligible. */ function isEligible(address indexer) external view override returns (bool) { RewardsEligibilityOracleData storage $ = _getRewardsEligibilityOracleStorage(); diff --git a/packages/issuance/test/tests/eligibility/RewardsEligibilityOracle.test.ts b/packages/issuance/test/tests/eligibility/RewardsEligibilityOracle.test.ts index 9f5b585a5..19c0e40c0 100644 --- a/packages/issuance/test/tests/eligibility/RewardsEligibilityOracle.test.ts +++ b/packages/issuance/test/tests/eligibility/RewardsEligibilityOracle.test.ts @@ -666,5 +666,56 @@ describe('RewardsEligibilityOracle', () => { // Now indexer should be allowed again expect(await rewardsEligibilityOracle.isEligible(accounts.indexer1.address)).to.be.true }) + + it('should return true for never-registered indexer when eligibility period exceeds block timestamp', async function () { + // This test validates the edge case identified in audit finding TRST-L-1: + // When eligibility period is set to an extremely large value that exceeds current block timestamp, + // all indexers (including those who have never been registered) become eligible. + + // Use a fresh deployment to avoid shared state contamination + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const freshRewardsEligibilityOracle = await deployRewardsEligibilityOracle(graphTokenAddress, accounts.governor) + + // Grant necessary roles + await freshRewardsEligibilityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await freshRewardsEligibilityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable eligibility validation + await freshRewardsEligibilityOracle.connect(accounts.operator).setEligibilityValidation(true) + + // Set a non-zero lastOracleUpdateTime and very long oracle timeout to isolate the eligibility period check + await freshRewardsEligibilityOracle + .connect(accounts.operator) + .renewIndexerEligibility([accounts.nonGovernor.address], '0x') + await freshRewardsEligibilityOracle.connect(accounts.operator).setOracleUpdateTimeout(365 * 24 * 60 * 60) // 1 year + + // Get current block timestamp + const currentBlock = await ethers.provider.getBlock('latest') + const blockTimestamp = currentBlock ? currentBlock.timestamp : 0 + + // Verify indexer1 has never been registered (renewal time should be 0) + const renewalTime = await freshRewardsEligibilityOracle.getEligibilityRenewalTime(accounts.indexer1.address) + expect(renewalTime).to.equal(0) + + // With normal eligibility period (14 days), never-registered indexer should be ineligible + // because block.timestamp < 0 + 14 days is false for any realistic timestamp + expect(await freshRewardsEligibilityOracle.isEligible(accounts.indexer1.address)).to.be.false + + // Now set eligibility period to a value larger than current block timestamp + // This makes the check: block.timestamp < 0 + eligibilityPeriod become true + const largeEligibilityPeriod = BigInt(blockTimestamp) + BigInt(365 * 24 * 60 * 60) // Current time + 1 year + await freshRewardsEligibilityOracle.connect(accounts.operator).setEligibilityPeriod(largeEligibilityPeriod) + + // Now the never-registered indexer should be eligible + // because block.timestamp < indexerEligibilityTimestamps[indexer] + eligibilityPeriod + // becomes block.timestamp < 0 + largeEligibilityPeriod, which is true + expect(await freshRewardsEligibilityOracle.isEligible(accounts.indexer1.address)).to.be.true + + // Verify this applies to any never-registered indexer + expect(await freshRewardsEligibilityOracle.isEligible(accounts.indexer2.address)).to.be.true + + // When the eligibility period exceeds block timestamp, all indexers become eligible + }) }) }) From c3cbcaeb303a116718f989d7bc0068ede933a733 Mon Sep 17 00:00:00 2001 From: Rembrandt Kuipers <50174308+RembrandtK@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:25:02 +0000 Subject: [PATCH 2/4] docs: document race condition between config changes and reward claims (TRST-L-2) Add comprehensive documentation for TRST-L-2 race condition where configuration changes (reducing eligibility period or enabling validation) can cause indexers to permanently lose rewards if their claim transactions are in-flight when the change occurs. Changes include: - NatSpec warnings on setEligibilityPeriod() and setEligibilityValidation() - Contract-level security warning in RewardsEligibilityOracle - Operational considerations section in documentation with mitigation strategies - Monitoring guidance for operators and indexers No code changes - mitigation relies on operational practices. --- .../eligibility/RewardsEligibilityOracle.md | 105 +++++++++++++++++- .../eligibility/RewardsEligibilityOracle.sol | 13 +++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md index f39a88004..f1737937a 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md @@ -161,6 +161,107 @@ It might initially seem safer to allow indexers by default unless an oracle expl In general to be rewarded for providing service on The Graph, there is expected to be proof provided of good operation (such as for proof of indexing). While proof should be required to receive rewards, the system is designed for participants to have confidence is being able to adequately prove good operation (and in the case of oracles, be seen by at least one observer) that is sufficient to allow the indexer to receive rewards. The oracle model is in general far more suited to collecting evidence of good operation, from multiple independent observers, rather than any observer being able to establish that an indexer is not providing good service. +## Operational Considerations + +### Race Conditions with Configuration Changes + +Configuration changes can create race conditions with in-flight reward claim transactions, potentially causing indexers to permanently lose rewards. + +When an indexer submits a transaction to claim rewards through the RewardsManager: + +1. The indexer is eligible at the time of transaction submission +2. The transaction enters the mempool and waits for execution +3. A configuration change occurs (e.g., reducing `eligibilityPeriod` or enabling `eligibilityValidation`) +4. The transaction executes after the indexer is no longer eligible +5. **The indexer is denied rewards** resulting in permanent loss for the indexer + +This occurs because the RewardsManager's `takeRewards()` function returns 0 rewards for ineligible indexers, but the calling contract (Staking or SubgraphService) still marks the allocation as processed. + +Circumstances potentially leading to this race condition: + +1. **Reducing eligibility period** (`setEligibilityPeriod`): + - Shortening the eligibility window may cause recently-approved indexers to become ineligible + - Indexers near the end of their eligibility period become ineligible immediately + +2. **Enabling eligibility validation** (`setEligibilityValidation`): + - Switching from disabled (all eligible) to enabled (oracle-based) + - Indexers without recent oracle renewals become ineligible immediately + +3. **Oracle update delays**: + - If oracles do not renew an indexer's eligibility before it expires + - Combined with network congestion delaying claim transactions + +4. **Network conditions**: + - High gas prices causing indexers to delay transaction submission + - Network congestion delaying transaction execution + - Multiple blocks between submission and execution + +#### Mitigation Strategies + +Operators and indexers should implement these practices: + +**For Operators:** + +1. **Announce configuration changes in advance**: + - Publish planned changes to eligibility period or validation state + - Provide sufficient notice (e.g., 24-48 hours) before executing changes + - Use governance forums, Discord, or official communication channels + +2. **Implement two-step process for critical changes**: + - First transaction: Announce the pending change with a delay period + - Second transaction: Execute the change after the delay + - This is a governance/operational practice, not enforced by the contract + +3. **Avoid sudden reductions in eligibility**: + - When reducing eligibility period, consider gradual reductions + - Monitor pending transactions in the mempool before making changes + - Time changes for periods of low network activity + +4. **Coordinate with oracle operations**: + - Ensure oracles are actively renewing indexer eligibility + - Verify oracle health before enabling eligibility validation + - Monitor `lastOracleUpdateTime` to detect oracle failures + +**For Indexers:** + +1. **Monitor eligibility status closely**: + - Regularly check `isEligible()` and `getEligibilityRenewalTime()` + - Calculate when eligibility will expire (`renewalTime + eligibilityPeriod`) + - Set up alerts for approaching expiration + +2. **Claim rewards with sufficient margin**: + - Don't wait until the last moment of eligibility period + - Account for network congestion and gas price volatility + - Consider claiming more frequently rather than in large batches + +3. **Watch for configuration change announcements**: + - Monitor governance communications and proposals + - Subscribe to operator announcements + - Plan claim transactions around announced changes + +4. **Use appropriate gas pricing**: + - During announced configuration changes, use higher gas prices + - Ensure transactions execute quickly during critical windows + - Monitor transaction status and resubmit if necessary + +5. **Understand the risk**: + - Be aware that rewards can be permanently lost due to race conditions + - Factor this risk into reward claiming strategies + +#### Monitoring and Detection + +Operators should monitor: + +- `RewardsDeniedDueToEligibility` events +- Time between configuration changes and claim transactions + +Indexers should monitor: + +- Their own eligibility status via `isEligible()` +- `EligibilityPeriodUpdated` events +- `EligibilityValidationUpdated` events +- `IndexerEligibilityRenewed` events for their address + ## Events ```solidity @@ -193,8 +294,8 @@ The system is deployed with reasonable defaults but can be adjusted as required. ### Normal Operation -1. Oracles periodically call `renewIndexerEligibility()` to renew eligibility for indexers -2. Reward systems call `isEligible()` to check indexer eligibility +1. Oracle nodes periodically call `renewIndexerEligibility()` to renew eligibility for indexers +2. Reward Manager calls `isEligible()` to check indexer eligibility 3. Operators adjust parameters as needed via configuration functions 4. The operation of the system is monitored and adjusted as needed diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol index d2e85a619..064f7be4f 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol @@ -19,6 +19,11 @@ import { BaseUpgradeable } from "../common/BaseUpgradeable.sol"; * @dev Note: If the eligibility period is set to an extremely large value that exceeds the current * block timestamp, all indexers (including those never registered) will be eligible. * @custom:security-contact Please email security+contracts@thegraph.com if you find any bugs. We might have an active bug bounty program. + * @custom:security-warning Configuration changes (eligibility period, validation toggle) can create race + * conditions with in-flight reward claim transactions. When configuration changes make an indexer ineligible + * between transaction submission and execution, the indexer permanently loses those rewards. + * Operators should announce configuration changes in advance and consider implementing + * a two-step process (announce delay, then execute) for changes that reduce eligibility. */ contract RewardsEligibilityOracle is BaseUpgradeable, @@ -133,6 +138,10 @@ contract RewardsEligibilityOracle is * @dev Only callable by accounts with the OPERATOR_ROLE * @param eligibilityPeriod New eligibility period in seconds * @return True if the state is as requested (eligibility period is set to the specified value) + * @custom:warning Configuration changes can affect in-flight reward claim transactions. Reducing the + * eligibility period may cause indexers to lose rewards if their claim transactions execute after + * they become ineligible due to the configuration change. Consider announcing configuration changes + * in advance and using a two-step process (announce, then execute) for changes that reduce eligibility. */ function setEligibilityPeriod(uint256 eligibilityPeriod) external override onlyRole(OPERATOR_ROLE) returns (bool) { RewardsEligibilityOracleData storage $ = _getRewardsEligibilityOracleStorage(); @@ -171,6 +180,10 @@ contract RewardsEligibilityOracle is * @dev Only callable by accounts with the OPERATOR_ROLE * @param enabled True to enable eligibility validation, false to disable * @return True if successfully set (always the case for current code) + * @custom:warning Enabling eligibility validation can affect in-flight reward claim transactions. + * Indexers who submitted claim transactions while validation was disabled may lose rewards if + * validation is enabled before their transactions execute and they are not marked as eligible. + * Consider announcing this change in advance to allow indexers to adjust their claiming behavior. */ function setEligibilityValidation(bool enabled) external override onlyRole(OPERATOR_ROLE) returns (bool) { RewardsEligibilityOracleData storage $ = _getRewardsEligibilityOracleStorage(); From 46f88dd66094c99248d183627c44510baaa787e5 Mon Sep 17 00:00:00 2001 From: Rembrandt Kuipers <50174308+RembrandtK@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:08:51 +0000 Subject: [PATCH 3/4] fix: resolve fuzz test failure in testGetBalance_WhenCollectedOverThawing (#1268) Replace vm.assume with bounded inputs to fix "vm.assume rejected too many inputs" error. The previous implementation used overly restrictive constraints that caused the fuzzer to reject most random inputs. Now limits amountThawing and amountCollected to half of MAX_STAKING_TOKENS, guaranteeing valid deposit ranges while maintaining test coverage. --- .../horizon/test/unit/escrow/getters.t.sol | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/horizon/test/unit/escrow/getters.t.sol b/packages/horizon/test/unit/escrow/getters.t.sol index 262192125..ded655b39 100644 --- a/packages/horizon/test/unit/escrow/getters.t.sol +++ b/packages/horizon/test/unit/escrow/getters.t.sol @@ -34,12 +34,21 @@ contract GraphEscrowGettersTest is GraphEscrowTest { uint256 amountDeposit, uint256 amountThawing, uint256 amountCollected - ) public useGateway useDeposit(amountDeposit) { - vm.assume(amountThawing > 0); - vm.assume(amountDeposit > 0); - vm.assume(amountDeposit >= amountThawing); - vm.assume(amountDeposit >= amountCollected); - vm.assume(amountDeposit - amountCollected < amountThawing); + ) public useGateway { + // Limit thawing and collected to half of MAX_STAKING_TOKENS to ensure valid deposit range + amountThawing = bound(amountThawing, 1, MAX_STAKING_TOKENS / 2); + amountCollected = bound(amountCollected, 1, MAX_STAKING_TOKENS / 2); + + // amountDeposit must be: + // - >= amountThawing (so we can thaw that amount) + // - >= amountCollected (so we can collect that amount) + // - < amountThawing + amountCollected (so that after collecting, balance < thawing) + // With the above bounds, this range is guaranteed to be valid + uint256 minDeposit = amountThawing > amountCollected ? amountThawing : amountCollected; + uint256 maxDeposit = amountThawing + amountCollected - 1; + amountDeposit = bound(amountDeposit, minDeposit, maxDeposit); + + _depositTokens(users.verifier, users.indexer, amountDeposit); // thaw some funds _thawEscrow(users.verifier, users.indexer, amountThawing); From 1bdf837f7a3de1407894d392e02239d75b0c89fa Mon Sep 17 00:00:00 2001 From: Rembrandt Kuipers <50174308+RembrandtK@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:25:26 +0000 Subject: [PATCH 4/4] docs: fix grammar and terminology in RewardsEligibilityOracle - Change 'eligible period' to 'eligibility period' for consistency - Fix 'have confidence is being able' to 'have confidence in being able' - Fix 'have a good transparency' to 'have good transparency' - Change 'Reward Manager' to 'RewardsManager' for correct contract naming --- .../contracts/eligibility/RewardsEligibilityOracle.md | 6 +++--- .../contracts/eligibility/RewardsEligibilityOracle.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md index f1737937a..60449c6d4 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.md @@ -155,11 +155,11 @@ For normal operations with reasonable eligibility periods (e.g., 14 days), index In normal operation, the first condition is expected to be the only one that applies. The other two conditions provide fail-safes for oracle failures, or in extreme cases an operator override. For normal operational failure of oracles, the system gracefully degrades into a "allow all" mode. This mechanism is not perfect in that oracles could still be updating but allowing far fewer indexers than they should. However this is regarded as simple mechanism that is good enough to start with and provide a foundation for future improvements and decentralization. -While this simple model allows the criteria for providing good service to evolve over time (which is essential for the long-term health of the network), it captures sufficient information on-chain for indexers to be able to monitor their eligibility. This is important to ensure that even in the absence of other sources of information regarding observed indexer service, indexers have a good transparency about if they are being observed to be providing good service, and for how long their current approval is valid. +While this simple model allows the criteria for providing good service to evolve over time (which is essential for the long-term health of the network), it captures sufficient information on-chain for indexers to be able to monitor their eligibility. This is important to ensure that even in the absence of other sources of information regarding observed indexer service, indexers have good transparency about if they are being observed to be providing good service, and for how long their current approval is valid. It might initially seem safer to allow indexers by default unless an oracle explicitly denies an indexer. While that might seem safer from the perspective of the RewardsEligibilityOracle in isolation, in the absence of a more sophisticated voting system it would make the system vulnerable to a single bad oracle denying many indexers. The design of deny by default is better suited to allowing redundant oracles to be working in parallel, where only one needs to be successfully detecting indexers that are providing quality service, as well as eventually allowing different oracles to have different approval criteria and/or inputs. Therefore deny by default facilitates a more resilient and open oracle system that is less vulnerable to a single points of failure, and more open to increasing decentralization over time. -In general to be rewarded for providing service on The Graph, there is expected to be proof provided of good operation (such as for proof of indexing). While proof should be required to receive rewards, the system is designed for participants to have confidence is being able to adequately prove good operation (and in the case of oracles, be seen by at least one observer) that is sufficient to allow the indexer to receive rewards. The oracle model is in general far more suited to collecting evidence of good operation, from multiple independent observers, rather than any observer being able to establish that an indexer is not providing good service. +In general to be rewarded for providing service on The Graph, there is expected to be proof provided of good operation (such as for proof of indexing). While proof should be required to receive rewards, the system is designed for participants to have confidence in being able to adequately prove good operation (and in the case of oracles, be seen by at least one observer) that is sufficient to allow the indexer to receive rewards. The oracle model is in general far more suited to collecting evidence of good operation, from multiple independent observers, rather than any observer being able to establish that an indexer is not providing good service. ## Operational Considerations @@ -295,7 +295,7 @@ The system is deployed with reasonable defaults but can be adjusted as required. ### Normal Operation 1. Oracle nodes periodically call `renewIndexerEligibility()` to renew eligibility for indexers -2. Reward Manager calls `isEligible()` to check indexer eligibility +2. RewardsManager calls `isEligible()` to check indexer eligibility 3. Operators adjust parameters as needed via configuration functions 4. The operation of the system is monitored and adjusted as needed diff --git a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol index 064f7be4f..567705e17 100644 --- a/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol +++ b/packages/issuance/contracts/eligibility/RewardsEligibilityOracle.sol @@ -14,7 +14,7 @@ import { BaseUpgradeable } from "../common/BaseUpgradeable.sol"; * @notice This contract allows authorized oracles to mark indexers as eligible to receive rewards * with an expiration mechanism. Under normal configuration with reasonable eligibility periods, indexers * are denied by default until they are explicitly marked as eligible, and their eligibility expires after - * a configurable eligible period. The contract also includes a global eligibility check toggle and an + * a configurable eligibility period. The contract also includes a global eligibility check toggle and an * oracle update timeout mechanism. * @dev Note: If the eligibility period is set to an extremely large value that exceeds the current * block timestamp, all indexers (including those never registered) will be eligible.