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
281 changes: 263 additions & 18 deletions packages/contracts/contracts/rewards/RewardsManager.sol

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions packages/contracts/contracts/rewards/RewardsManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

pragma solidity ^0.7.6 || 0.8.27;

import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";
import { IRewardsEligibility } from "@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 @@ -76,3 +78,20 @@ contract RewardsManagerV5Storage is RewardsManagerV4Storage {
/// @notice Address of the subgraph service
IRewardsIssuer public subgraphService;
}

/**
* @title RewardsManagerV6Storage
* @author Edge & Node
* @notice Storage layout for RewardsManager V6
* 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;
}
20 changes: 20 additions & 0 deletions packages/contracts/contracts/tests/MockERC165.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity 0.7.6;

import { IERC165 } from "@openzeppelin/contracts/introspection/IERC165.sol";

/**
* @title MockERC165
* @author Edge & Node
* @dev Minimal implementation of IERC165 for testing
* @notice Used to test interface validation - supports only ERC165, not specific interfaces
*/
contract MockERC165 is IERC165 {
/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
76 changes: 76 additions & 0 deletions packages/contracts/contracts/tests/MockIssuanceAllocator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// solhint-disable gas-increment-by-one, gas-indexed-events, named-parameters-mapping, use-natspec

pragma solidity 0.7.6;
pragma abicoder v2;

import { IERC165 } from "@openzeppelin/contracts/introspection/IERC165.sol";
import { TargetIssuancePerBlock } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocatorTypes.sol";
import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";
import { IIssuanceTarget } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol";

/**
* @title MockIssuanceAllocator
* @dev A simple mock contract for the IssuanceAllocator interfaces used by RewardsManager.
*/
contract MockIssuanceAllocator is IERC165, IIssuanceAllocationDistribution {
/// @dev Mapping to store TargetIssuancePerBlock for each target
mapping(address => TargetIssuancePerBlock) private _targetIssuance;

/**
* @dev Call beforeIssuanceAllocationChange on a target
* @param target The target contract address
*/
function callBeforeIssuanceAllocationChange(address target) external {
IIssuanceTarget(target).beforeIssuanceAllocationChange();
}

/**
* @inheritdoc IIssuanceAllocationDistribution
*/
function getTargetIssuancePerBlock(address target) external view override returns (TargetIssuancePerBlock memory) {
return _targetIssuance[target];
}

/**
* @inheritdoc IIssuanceAllocationDistribution
* @dev Mock always returns current block number
*/
function distributeIssuance() external view override returns (uint256) {
return block.number;
}

/**
* @dev Set target issuance directly for testing
* @param target The target contract address
* @param allocatorIssuance The allocator issuance per block
* @param selfIssuance The self issuance per block
* @param callBefore Whether to call beforeIssuanceAllocationChange on the target
*/
function setTargetAllocation(
address target,
uint256 allocatorIssuance,
uint256 selfIssuance,
bool callBefore
) external {
if (callBefore) {
IIssuanceTarget(target).beforeIssuanceAllocationChange();
}
_targetIssuance[target] = TargetIssuancePerBlock({
allocatorIssuancePerBlock: allocatorIssuance,
allocatorIssuanceBlockAppliedTo: block.number,
selfIssuancePerBlock: selfIssuance,
selfIssuanceBlockAppliedTo: block.number
});
}

/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return
interfaceId == type(IIssuanceAllocationDistribution).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// solhint-disable named-parameters-mapping

pragma solidity 0.7.6;

import { IRewardsEligibility } from "@graphprotocol/interfaces/contracts/issuance/eligibility/IRewardsEligibility.sol";
import { IERC165 } from "@openzeppelin/contracts/introspection/IERC165.sol";

/**
* @title MockRewardsEligibilityOracle
* @author Edge & Node
* @notice A simple mock contract for the RewardsEligibilityOracle interface
* @dev A simple mock contract for the RewardsEligibilityOracle interface
*/
contract MockRewardsEligibilityOracle is IRewardsEligibility, IERC165 {
/// @dev Mapping to store eligibility status for each indexer
mapping(address => bool) private eligible;

/// @dev Mapping to track which indexers have been explicitly set
mapping(address => bool) private isSet;

/// @dev Default response for indexers not explicitly set
bool private defaultResponse;

/**
* @notice Constructor
* @param newDefaultResponse Default response for isEligible
*/
constructor(bool newDefaultResponse) {
defaultResponse = newDefaultResponse;
}

/**
* @notice Set whether a specific indexer is eligible
* @param indexer The indexer address
* @param eligibility Whether the indexer is eligible
*/
function setIndexerEligible(address indexer, bool eligibility) external {
eligible[indexer] = eligibility;
isSet[indexer] = true;
}

/**
* @notice Set the default response for indexers not explicitly set
* @param newDefaultResponse The default response
*/
function setDefaultResponse(bool newDefaultResponse) external {
defaultResponse = newDefaultResponse;
}

/**
* @inheritdoc IRewardsEligibility
*/
function isEligible(address indexer) external view override returns (bool) {
// If the indexer has been explicitly set, return that value
if (isSet[indexer]) {
return eligible[indexer];
}

// Otherwise return the default response
return defaultResponse;
}

/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == type(IRewardsEligibility).interfaceId || interfaceId == type(IERC165).interfaceId;
}
}
105 changes: 105 additions & 0 deletions packages/contracts/contracts/tests/MockSubgraphService.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// solhint-disable named-parameters-mapping

pragma solidity 0.7.6;

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

/**
* @title MockSubgraphService
* @author Edge & Node
* @notice A mock contract for testing SubgraphService as a rewards issuer
* @dev Implements IRewardsIssuer interface to simulate SubgraphService behavior in tests
*/
contract MockSubgraphService is IRewardsIssuer {
/// @dev Struct to store allocation data
struct Allocation {
bool isActive;
address indexer;
bytes32 subgraphDeploymentId;
uint256 tokens;
uint256 accRewardsPerAllocatedToken;
uint256 accRewardsPending;
}

/// @dev Mapping of allocation ID to allocation data
mapping(address => Allocation) private allocations;

/// @dev Mapping of subgraph deployment ID to total allocated tokens
mapping(bytes32 => uint256) private subgraphAllocatedTokens;

/**
* @notice Set allocation data for testing
* @param allocationId The allocation ID
* @param isActive Whether the allocation is active
* @param indexer The indexer address
* @param subgraphDeploymentId The subgraph deployment ID
* @param tokens Amount of allocated tokens
* @param accRewardsPerAllocatedToken Rewards snapshot
* @param accRewardsPending Accumulated rewards pending
*/
function setAllocation(
address allocationId,
bool isActive,
address indexer,
bytes32 subgraphDeploymentId,
uint256 tokens,
uint256 accRewardsPerAllocatedToken,
uint256 accRewardsPending
) external {
allocations[allocationId] = Allocation({
isActive: isActive,
indexer: indexer,
subgraphDeploymentId: subgraphDeploymentId,
tokens: tokens,
accRewardsPerAllocatedToken: accRewardsPerAllocatedToken,
accRewardsPending: accRewardsPending
});
}

/**
* @notice Set total allocated tokens for a subgraph
* @param subgraphDeploymentId The subgraph deployment ID
* @param tokens Total tokens allocated
*/
function setSubgraphAllocatedTokens(bytes32 subgraphDeploymentId, uint256 tokens) external {
subgraphAllocatedTokens[subgraphDeploymentId] = tokens;
}

/**
* @inheritdoc IRewardsIssuer
*/
function getAllocationData(
address allocationId
)
external
view
override
returns (
bool isActive,
address indexer,
bytes32 subgraphDeploymentId,
uint256 tokens,
uint256 accRewardsPerAllocatedToken,
uint256 accRewardsPending
)
{
Allocation memory allocation = allocations[allocationId];
return (
allocation.isActive,
allocation.indexer,
allocation.subgraphDeploymentId,
allocation.tokens,
allocation.accRewardsPerAllocatedToken,
allocation.accRewardsPending
);
}

/**
* @inheritdoc IRewardsIssuer
*/
function getSubgraphAllocatedTokens(bytes32 subgraphDeploymentId) external view override returns (uint256) {
return subgraphAllocatedTokens[subgraphDeploymentId];
}
}
2 changes: 1 addition & 1 deletion packages/contracts/test/.solcover.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const skipFiles = ['bancor', 'ens', 'erc1056', 'arbitrum', 'tests/arbitrum']
const skipFiles = ['bancor', 'ens', 'erc1056', 'arbitrum', 'tests', '*Mock.sol']

module.exports = {
providerOptions: {
Expand Down
Loading