Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,46 @@
address indexed newRewardsEligibilityOracle
);

/**
* @notice Emitted when the eligibility reclaim address is set
* @param oldReclaimAddress Previous eligibility reclaim address
* @param newReclaimAddress New eligibility reclaim address
*/
event IndexerEligibilityReclaimAddressSet(address indexed oldReclaimAddress, address indexed newReclaimAddress);

/**
* @notice Emitted when the subgraph reclaim address is set
* @param oldReclaimAddress Previous subgraph reclaim address
* @param newReclaimAddress New subgraph reclaim address
*/
event SubgraphDeniedReclaimAddressSet(address indexed oldReclaimAddress, address indexed newReclaimAddress);

/**
* @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 RewardsReclaimedDueToIndexerEligibility(
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 +304,32 @@
}
}

/**
* @inheritdoc IRewardsManager
* @dev Set to zero address to disable eligibility reclaim functionality
*/
function setIndexerEligibilityReclaimAddress(address newReclaimAddress) external override onlyGovernor {
address oldReclaimAddress = indexerEligibilityReclaimAddress;

if (oldReclaimAddress != newReclaimAddress) {
indexerEligibilityReclaimAddress = newReclaimAddress;
emit IndexerEligibilityReclaimAddressSet(oldReclaimAddress, newReclaimAddress);
}
}

/**
* @inheritdoc IRewardsManager
* @dev Set to zero address to disable subgraph reclaim functionality
*/
function setSubgraphDeniedReclaimAddress(address newReclaimAddress) external override onlyGovernor {
address oldReclaimAddress = subgraphDeniedReclaimAddress;

if (oldReclaimAddress != newReclaimAddress) {
subgraphDeniedReclaimAddress = newReclaimAddress;
emit SubgraphDeniedReclaimAddressSet(oldReclaimAddress, newReclaimAddress);
}
}

// -- 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 RewardsReclaimedDueToIndexerEligibility(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, set to zero to disable
address public indexerEligibilityReclaimAddress;
/// @notice Address to receive tokens denied due to subgraph denylist, set to zero to disable
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