-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: subgraph availability manager contract #882
Merged
Merged
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
ad358fb
feat: subgraph availability manager contract
Maikol 77e8dc3
feat: vote denied many
Maikol 1805cd9
fix: pr feedback changes
Maikol f4942f2
fix: use natspec for state variables comments
Maikol fa9b153
fix: use currentNonce to refresh votes, init oracles with valid addre…
Maikol 08e269f
fix: update functions docs
Maikol 35f6392
fix: removed unnecessary code in tests
Maikol 0896337
fix: typo, comments and changed maxOracles to be a constant
Maikol ad4baec
fix: change constant variable name to match style guide
Maikol a67ad20
chore: deploy to staging
Maikol ddbcc86
fix: emit oracle set event on init
Maikol 48ffb3a
Merge remote-tracking branch 'origin/main' into mde/sam-contract
Maikol 26f1c7a
fix: change mapping for an array to make storage cheaper
Maikol eeeb314
fix: update comments
Maikol 1ad7935
fix: make currentNonce public
Maikol fbdf6af
fix: added input validation for executionThreshold (OZ L-01)
Maikol 94e6d05
docs: added missing docstrings for events (OZ L-03)
Maikol 9f9db2e
fix: change OracleVote event order (OZ N-01)
Maikol f2aa2fa
fix: gas optimization (OZ N-02)
Maikol a52066c
fix: clear vote for earlier rounds (OZ L-02)
Maikol 1ce46f0
fix: change to if/else
Maikol ef35364
fix: remove setDeniedMany since it's unused (OZ N-03)
Maikol 9562b28
Merge pull request #935 from graphprotocol/mde/pr935-fix-oz-l-01
Maikol fa9d0cb
Merge pull request #936 from graphprotocol/mde/pr936-fix-oz-l-03
Maikol 0c547c4
Merge pull request #937 from graphprotocol/mde/pr937-fix-oz-n-01
Maikol 0698989
Merge pull request #938 from graphprotocol/mde/pr938-fix-oz-n-02
Maikol 374e387
Merge pull request #939 from graphprotocol/mde/pr939-fix-oz-l-02
Maikol 58463a8
Merge pull request #940 from graphprotocol/mde/pr400-fix-ox-n-03
Maikol 06885f3
Merge branch 'main' into mde/sam-contract
Maikol bcf6957
fix: indentation
Maikol 4c01fc4
chore: deploy to arbitrum-sepolia and remove sepolia
Maikol 87bb02d
fix: remove debugging log
Maikol b24377e
fix: e2e tests
Maikol 09271fd
fix: building error
Maikol 6428bda
fix: update deployment
Maikol b45b21f
fix: new deployment
Maikol 152521b
chore: testnet deployment
Maikol 113fe69
fix: testnet deployment
Maikol 19faf45
chore: deploy SAM to arbitrum-one
Maikol 7fe43b8
Merge pull request #961 from graphprotocol/mde/sam-contract-deployments
Maikol File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
245 changes: 245 additions & 0 deletions
245
packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
pragma solidity ^0.7.6; | ||
|
||
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; | ||
|
||
import { Governed } from "../governance/Governed.sol"; | ||
import { IRewardsManager } from "../rewards/IRewardsManager.sol"; | ||
|
||
/** | ||
* @title Subgraph Availability Manager | ||
* @dev Manages the availability of subgraphs by allowing oracles to vote on whether | ||
* a subgraph should be denied rewards or not. When enough oracles have voted to deny or | ||
* allow rewards for a subgraph, it calls the RewardsManager Contract to set the correct | ||
* state. The oracles and the execution threshold are set at deployment time. | ||
* Only governance can update the oracles and voteTimeLimit. | ||
* Governor can transfer ownership to a new governor. | ||
*/ | ||
contract SubgraphAvailabilityManager is Governed { | ||
using SafeMathUpgradeable for uint256; | ||
|
||
// -- Immutable -- | ||
|
||
/// @notice Number of oracles | ||
uint256 public constant NUM_ORACLES = 5; | ||
|
||
/// @notice Number of votes required to execute a deny or allow call to the RewardsManager | ||
uint256 public immutable executionThreshold; | ||
|
||
/// @dev Address of the RewardsManager contract | ||
IRewardsManager private immutable rewardsManager; | ||
|
||
// -- State -- | ||
|
||
/// @dev Nonce for generating votes on subgraph deployment IDs. | ||
/// Increased whenever oracles or voteTimeLimit change, to invalidate old votes. | ||
uint256 public currentNonce; | ||
|
||
/// @notice Time limit for a vote to be valid | ||
uint256 public voteTimeLimit; | ||
|
||
/// @notice Array of oracle addresses | ||
address[NUM_ORACLES] public oracles; | ||
|
||
/// @notice Mapping of current nonce to subgraph deployment ID to an array of timestamps of last deny vote | ||
/// currentNonce => subgraphDeploymentId => timestamp[oracleIndex] | ||
mapping(uint256 => mapping(bytes32 => uint256[NUM_ORACLES])) public lastDenyVote; | ||
|
||
/// @notice Mapping of current nonce to subgraph deployment ID to an array of timestamp of last allow vote | ||
/// currentNonce => subgraphDeploymentId => timestamp[oracleIndex] | ||
mapping(uint256 => mapping(bytes32 => uint256[NUM_ORACLES])) public lastAllowVote; | ||
|
||
// -- Events -- | ||
|
||
/** | ||
* @dev Emitted when an oracle is set | ||
*/ | ||
event OracleSet(uint256 indexed index, address indexed oracle); | ||
|
||
/** | ||
* @dev Emitted when the vote time limit is set | ||
*/ | ||
event VoteTimeLimitSet(uint256 voteTimeLimit); | ||
|
||
/** | ||
* @dev Emitted when an oracle votes to deny or allow a subgraph | ||
*/ | ||
event OracleVote( | ||
bytes32 indexed subgraphDeploymentID, | ||
bool deny, | ||
uint256 indexed oracleIndex, | ||
uint256 timestamp | ||
); | ||
|
||
// -- Modifiers -- | ||
|
||
modifier onlyOracle(uint256 _oracleIndex) { | ||
require(_oracleIndex < NUM_ORACLES, "SAM: index out of bounds"); | ||
require(msg.sender == oracles[_oracleIndex], "SAM: caller must be oracle"); | ||
_; | ||
} | ||
|
||
// -- Constructor -- | ||
|
||
/** | ||
* @dev Contract constructor | ||
* @param _governor Account that can set or remove oracles and set the vote time limit | ||
* @param _rewardsManager Address of the RewardsManager contract | ||
* @param _executionThreshold Number of votes required to execute a deny or allow call to the RewardsManager | ||
* @param _voteTimeLimit Vote time limit in seconds | ||
* @param _oracles Array of oracle addresses, must be NUM_ORACLES in length. | ||
*/ | ||
constructor( | ||
address _governor, | ||
address _rewardsManager, | ||
uint256 _executionThreshold, | ||
uint256 _voteTimeLimit, | ||
address[NUM_ORACLES] memory _oracles | ||
) { | ||
require(_governor != address(0), "SAM: governor must be set"); | ||
require(_rewardsManager != address(0), "SAM: rewardsManager must be set"); | ||
require( | ||
_executionThreshold >= NUM_ORACLES.div(2).add(1), | ||
"SAM: executionThreshold too low" | ||
); | ||
|
||
// Oracles should not be address zero | ||
for (uint256 i = 0; i < _oracles.length; i++) { | ||
address oracle = _oracles[i]; | ||
require(oracle != address(0), "SAM: oracle cannot be address zero"); | ||
oracles[i] = oracle; | ||
emit OracleSet(i, oracle); | ||
} | ||
|
||
Governed._initialize(_governor); | ||
rewardsManager = IRewardsManager(_rewardsManager); | ||
|
||
executionThreshold = _executionThreshold; | ||
voteTimeLimit = _voteTimeLimit; | ||
} | ||
|
||
// -- Functions -- | ||
|
||
/** | ||
* @dev Set the vote time limit. Refreshes all existing votes by incrementing the current nonce. | ||
* @param _voteTimeLimit Vote time limit in seconds | ||
*/ | ||
function setVoteTimeLimit(uint256 _voteTimeLimit) external onlyGovernor { | ||
voteTimeLimit = _voteTimeLimit; | ||
currentNonce++; | ||
emit VoteTimeLimitSet(_voteTimeLimit); | ||
} | ||
|
||
/** | ||
* @dev Set oracle address with index. Refreshes all existing votes by incrementing the current nonce. | ||
* @param _index Index of the oracle | ||
* @param _oracle Address of the oracle | ||
*/ | ||
function setOracle(uint256 _index, address _oracle) external onlyGovernor { | ||
require(_index < NUM_ORACLES, "SAM: index out of bounds"); | ||
require(_oracle != address(0), "SAM: oracle cannot be address zero"); | ||
|
||
oracles[_index] = _oracle; | ||
// Increment the current nonce to refresh all existing votes for subgraph deployment IDs | ||
currentNonce++; | ||
|
||
emit OracleSet(_index, _oracle); | ||
} | ||
|
||
/** | ||
* @dev Vote deny or allow for a subgraph. | ||
* NOTE: Can only be called by an oracle. | ||
* @param _subgraphDeploymentID Subgraph deployment ID | ||
* @param _deny True to deny, false to allow | ||
* @param _oracleIndex Index of the oracle voting | ||
*/ | ||
function vote( | ||
bytes32 _subgraphDeploymentID, | ||
bool _deny, | ||
uint256 _oracleIndex | ||
) external onlyOracle(_oracleIndex) { | ||
_vote(_subgraphDeploymentID, _deny, _oracleIndex); | ||
} | ||
|
||
/** | ||
* @dev Vote deny or allow for many subgraphs. | ||
* NOTE: Can only be called by an oracle. | ||
* @param _subgraphDeploymentID Array of subgraph deployment IDs | ||
* @param _deny Array of booleans, true to deny, false to allow | ||
* @param _oracleIndex Index of the oracle voting | ||
*/ | ||
function voteMany( | ||
bytes32[] calldata _subgraphDeploymentID, | ||
bool[] calldata _deny, | ||
uint256 _oracleIndex | ||
) external onlyOracle(_oracleIndex) { | ||
require(_subgraphDeploymentID.length == _deny.length, "!length"); | ||
for (uint256 i = 0; i < _subgraphDeploymentID.length; i++) { | ||
_vote(_subgraphDeploymentID[i], _deny[i], _oracleIndex); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Vote deny or allow for a subgraph. | ||
* When oracles cast their votes we store the timestamp of the vote. | ||
* Check if the execution threshold has been reached for a subgraph. | ||
* If execution threshold is reached we call the RewardsManager to set the correct state. | ||
* @param _subgraphDeploymentID Subgraph deployment ID | ||
* @param _deny True to deny, false to allow | ||
* @param _oracleIndex Index of the oracle voting | ||
*/ | ||
function _vote( | ||
bytes32 _subgraphDeploymentID, | ||
bool _deny, | ||
uint256 _oracleIndex | ||
) private { | ||
uint256 timestamp = block.timestamp; | ||
|
||
// corresponding votes based on _deny for a subgraph deployment | ||
uint256[NUM_ORACLES] storage lastVoteForSubgraph = _deny | ||
? lastDenyVote[currentNonce][_subgraphDeploymentID] | ||
: lastAllowVote[currentNonce][_subgraphDeploymentID]; | ||
lastVoteForSubgraph[_oracleIndex] = timestamp; | ||
|
||
// check if execution threshold is reached, if it is call the RewardsManager | ||
if (checkVotes(_subgraphDeploymentID, _deny)) { | ||
rewardsManager.setDenied(_subgraphDeploymentID, _deny); | ||
} | ||
|
||
emit OracleVote(_subgraphDeploymentID, _deny, _oracleIndex, timestamp); | ||
} | ||
|
||
/** | ||
* @dev Check if the execution threshold has been reached for a subgraph. | ||
* For a vote to be valid it needs to be within the vote time limit. | ||
* @param _subgraphDeploymentID Subgraph deployment ID | ||
* @param _deny True to deny, false to allow | ||
* @return True if execution threshold is reached | ||
*/ | ||
function checkVotes(bytes32 _subgraphDeploymentID, bool _deny) public view returns (bool) { | ||
uint256 votes = 0; | ||
|
||
// timeframe for a vote to be valid | ||
uint256 voteTimeValidity = block.timestamp - voteTimeLimit; | ||
|
||
// corresponding votes based on _deny for a subgraph deployment | ||
uint256[NUM_ORACLES] storage lastVoteForSubgraph = _deny | ||
? lastDenyVote[currentNonce][_subgraphDeploymentID] | ||
: lastAllowVote[currentNonce][_subgraphDeploymentID]; | ||
|
||
for (uint256 i = 0; i < NUM_ORACLES; i++) { | ||
// check if vote is within the vote time limit | ||
if (lastVoteForSubgraph[i] > voteTimeValidity) { | ||
votes++; | ||
} | ||
|
||
// check if execution threshold is reached | ||
if (votes == executionThreshold) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we don't set the council as the governor from the get go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using the deployer while I'm testing so I don't have to use the multisig.