Skip to content
147 changes: 130 additions & 17 deletions packages/contracts/contracts/rewards/RewardsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
// TODO: Re-enable and fix issues when publishing a new version
// solhint-disable gas-increment-by-one, gas-indexed-events, gas-small-strings, gas-strict-inequalities

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";

Check warning on line 9 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @openzeppelin/contracts/math/SafeMath.sol
import { IERC165 } from "@openzeppelin/contracts/introspection/IERC165.sol";

Check warning on line 10 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @openzeppelin/contracts/introspection/IERC165.sol

import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol";
import { Managed } from "../governance/Managed.sol";
import { MathUtils } from "../staking/libs/MathUtils.sol";
import { IGraphToken } from "@graphprotocol/interfaces/contracts/contracts/token/IGraphToken.sol";

Check warning on line 15 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/contracts/token/IGraphToken.sol

import { RewardsManagerV6Storage } from "./RewardsManagerStorage.sol";
import { IRewardsIssuer } from "@graphprotocol/interfaces/contracts/contracts/rewards/IRewardsIssuer.sol";

Check warning on line 18 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/contracts/rewards/IRewardsIssuer.sol
import { IRewardsManager } from "@graphprotocol/interfaces/contracts/contracts/rewards/IRewardsManager.sol";

Check warning on line 19 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/contracts/rewards/IRewardsManager.sol
import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";

Check warning on line 20 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol
import { IIssuanceTarget } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol";

Check warning on line 21 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol
import { IRewardsEligibility } from "@graphprotocol/interfaces/contracts/issuance/eligibility/IRewardsEligibility.sol";

Check warning on line 22 in packages/contracts/contracts/rewards/RewardsManager.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManager.sol doesn't exist in: @graphprotocol/interfaces/contracts/issuance/eligibility/IRewardsEligibility.sol

/**
* @title Rewards Manager Contract
Expand Down Expand Up @@ -108,6 +108,48 @@
address indexed newRewardsEligibilityOracle
);

/**
* @notice Emitted when the eligibility reclaim address is set
* @param oldEligibilityReclaimAddress Previous eligibility reclaim address
* @param newEligibilityReclaimAddress New eligibility reclaim address
*/
event EligibilityReclaimAddressSet(
address indexed oldEligibilityReclaimAddress,
address indexed newEligibilityReclaimAddress
);

/**
* @notice Emitted when the subgraph reclaim address is set
* @param oldSubgraphReclaimAddress Previous subgraph reclaim address
* @param newSubgraphReclaimAddress New subgraph reclaim address
*/
event SubgraphReclaimAddressSet(
address indexed oldSubgraphReclaimAddress,
address indexed newSubgraphReclaimAddress
);

/**
* @notice Emitted when denied rewards are reclaimed due to eligibility
* @param indexer Address of the indexer whose rewards were denied
* @param allocationID Address of the allocation
* @param amount Amount of rewards reclaimed
*/
event RewardsReclaimedDueToEligibility(address indexed indexer, address indexed allocationID, uint256 amount);

/**
* @notice Emitted when denied rewards are reclaimed due to subgraph denylist
* @param indexer Address of the indexer whose rewards were denied
* @param allocationID Address of the allocation
* @param subgraphDeploymentID Subgraph deployment ID that was denied
* @param amount Amount of rewards reclaimed
*/
event RewardsReclaimedDueToSubgraphDenylist(
address indexed indexer,
address indexed allocationID,
bytes32 indexed subgraphDeploymentID,
uint256 amount
);

// -- Modifiers --

/**
Expand Down Expand Up @@ -264,6 +306,30 @@
}
}

/**
* @inheritdoc IRewardsManager
* @dev Set to zero address to disable eligibility reclaim functionality
*/
function setIndexerEligibilityReclaimAddress(address newEligibilityReclaimAddress) external override onlyGovernor {
if (indexerEligibilityReclaimAddress != newEligibilityReclaimAddress) {
address oldEligibilityReclaimAddress = indexerEligibilityReclaimAddress;
indexerEligibilityReclaimAddress = newEligibilityReclaimAddress;
emit EligibilityReclaimAddressSet(oldEligibilityReclaimAddress, newEligibilityReclaimAddress);
}
}

/**
* @inheritdoc IRewardsManager
* @dev Set to zero address to disable subgraph reclaim functionality
*/
function setSubgraphDeniedReclaimAddress(address newSubgraphReclaimAddress) external override onlyGovernor {
if (subgraphDeniedReclaimAddress != newSubgraphReclaimAddress) {
address oldSubgraphReclaimAddress = subgraphDeniedReclaimAddress;
subgraphDeniedReclaimAddress = newSubgraphReclaimAddress;
emit SubgraphReclaimAddressSet(oldSubgraphReclaimAddress, newSubgraphReclaimAddress);
}
}

// -- Denylist --

/**
Expand Down Expand Up @@ -494,6 +560,60 @@
return newAccrued.mul(_tokens).div(FIXED_POINT_SCALING_FACTOR);
}

/**
* @notice Checks for and handles denial and reclaim of rewards due to subgraph deny list
* @dev If denied, emits RewardsDenied event and mints to reclaim address if configured
* @param indexer Address of the indexer
* @param allocationID Address of the allocation
* @param subgraphDeploymentID Subgraph deployment ID
* @param rewards Amount of rewards that would be distributed
* @return True if rewards are denied, false otherwise
*/
function _rewardsDeniedDueToSubgraphDenyList(
address indexer,
address allocationID,
bytes32 subgraphDeploymentID,
uint256 rewards
) private returns (bool) {
if (isDenied(subgraphDeploymentID)) {
emit RewardsDenied(indexer, allocationID);

// If a reclaim address is set, mint the denied rewards there
if (0 < rewards && subgraphDeniedReclaimAddress != address(0)) {
graphToken().mint(subgraphDeniedReclaimAddress, rewards);
emit RewardsReclaimedDueToSubgraphDenylist(indexer, allocationID, subgraphDeploymentID, rewards);
}
return true;
}
return false;
}

/**
* @notice Checks for and handles denial and reclaim of rewards due to indexer eligibility
* @dev If denied, emits RewardsDeniedDueToEligibility event and mints to reclaim address if configured
* @param indexer Address of the indexer
* @param allocationID Address of the allocation
* @param rewards Amount of rewards that would be distributed
* @return True if rewards are denied, false otherwise
*/
function _rewardsDeniedDueToIndexerEligibility(
address indexer,
address allocationID,
uint256 rewards
) private returns (bool) {
if (address(rewardsEligibilityOracle) != address(0) && !rewardsEligibilityOracle.isEligible(indexer)) {
emit RewardsDeniedDueToEligibility(indexer, allocationID, rewards);

// If a reclaim address is set, mint the denied rewards there
if (0 < rewards && indexerEligibilityReclaimAddress != address(0)) {
graphToken().mint(indexerEligibilityReclaimAddress, rewards);
emit RewardsReclaimedDueToEligibility(indexer, allocationID, rewards);
}
return true;
}
return false;
}

/**
* @inheritdoc IRewardsManager
* @dev This function can only be called by an authorized rewards issuer which are
Expand All @@ -518,31 +638,24 @@

uint256 updatedAccRewardsPerAllocatedToken = onSubgraphAllocationUpdate(subgraphDeploymentID);

// Do not do rewards on denied subgraph deployments ID
if (isDenied(subgraphDeploymentID)) {
emit RewardsDenied(indexer, _allocationID);
return 0;
}

uint256 rewards = 0;
if (isActive) {
// Calculate rewards accrued by this allocation
rewards = accRewardsPending.add(
_calcRewards(tokens, accRewardsPerAllocatedToken, updatedAccRewardsPerAllocatedToken)
);
}

// Do not reward if indexer is not eligible based on rewards eligibility
if (address(rewardsEligibilityOracle) != address(0) && !rewardsEligibilityOracle.isEligible(indexer)) {
emit RewardsDeniedDueToEligibility(indexer, _allocationID, rewards);
return 0;
}
if (_rewardsDeniedDueToSubgraphDenyList(indexer, _allocationID, subgraphDeploymentID, rewards)) return 0;

if (rewards > 0) {
// Mint directly to rewards issuer for the reward amount
// The rewards issuer contract will do bookkeeping of the reward and
// assign in proportion to each stakeholder incentive
graphToken().mint(rewardsIssuer, rewards);
}
if (_rewardsDeniedDueToIndexerEligibility(indexer, _allocationID, rewards)) return 0;

// Mint rewards to the rewards issuer
if (rewards > 0) {
// Mint directly to rewards issuer for the reward amount
// The rewards issuer contract will do bookkeeping of the reward and
// assign in proportion to each stakeholder incentive
graphToken().mint(rewardsIssuer, rewards);
}

emit HorizonRewardsAssigned(indexer, _allocationID, rewards);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

pragma solidity ^0.7.6 || 0.8.27;

import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";

Check warning on line 10 in packages/contracts/contracts/rewards/RewardsManagerStorage.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManagerStorage.sol doesn't exist in: @graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol
import { IRewardsEligibility } from "@graphprotocol/interfaces/contracts/issuance/eligibility/IRewardsEligibility.sol";

Check warning on line 11 in packages/contracts/contracts/rewards/RewardsManagerStorage.sol

View workflow job for this annotation

GitHub Actions / Lint Files

Import in packages/contracts/contracts/rewards/RewardsManagerStorage.sol doesn't exist in: @graphprotocol/interfaces/contracts/issuance/eligibility/IRewardsEligibility.sol
import { IRewardsIssuer } from "@graphprotocol/interfaces/contracts/contracts/rewards/IRewardsIssuer.sol";
import { IRewardsManager } from "@graphprotocol/interfaces/contracts/contracts/rewards/IRewardsManager.sol";
import { Managed } from "../governance/Managed.sol";
Expand Down Expand Up @@ -83,11 +83,15 @@
* @title RewardsManagerV6Storage
* @author Edge & Node
* @notice Storage layout for RewardsManager V6
* Includes support for Rewards Eligibility Oracle and Issuance Allocator.
* Includes support for Rewards Eligibility Oracle, Issuance Allocator, and Reclaim Addresses.
*/
contract RewardsManagerV6Storage is RewardsManagerV5Storage {
/// @notice Address of the rewards eligibility oracle contract
IRewardsEligibility public rewardsEligibilityOracle;
/// @notice Address of the issuance allocator
IIssuanceAllocationDistribution public issuanceAllocator;
/// @notice Address to receive tokens denied due to indexer eligibility checks
address public indexerEligibilityReclaimAddress;
/// @notice Address to receive tokens denied due to subgraph denylist
address public subgraphDeniedReclaimAddress;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('RewardsManager interfaces', () => {
})

it('IRewardsManager should have stable interface ID', () => {
expect(IRewardsManager__factory.interfaceId).to.equal('0xa31d8306')
expect(IRewardsManager__factory.interfaceId).to.equal('0x731e44f0')
})
})

Expand Down
Loading
Loading