From ad358fba5e493b09a16aec6981ca319dced3088c Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 16 Nov 2023 18:43:51 -0300 Subject: [PATCH 01/31] feat: subgraph availability manager contract --- .../rewards/SubgraphAvailabilityManager.sol | 195 ++++++++++++ .../test/rewards/subgraphAvailability.test.ts | 288 ++++++++++++++++++ 2 files changed, 483 insertions(+) create mode 100644 packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol create mode 100644 packages/contracts/test/rewards/subgraphAvailability.test.ts diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol new file mode 100644 index 000000000..d12f3e47c --- /dev/null +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../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 or not. When enough oracles have voted to deny or + * allow a subgraph, it calls the RewardsManager Contract to set the correct state. + * The number of oracles and the execution threshold are set at deployment time. + * Only governance can set the oracles. + */ +contract SubgraphAvailabilityManager is Governed { + // -- Immutable -- + + uint256 public immutable maxOracles; + uint256 public immutable executionThreshold; + IRewardsManager private immutable rewardsManager; + + // -- State -- + + uint256 public voteTimeLimit; + address[] public oracles; + mapping(bytes32 => mapping(uint256 => uint256)) public lastDenyVote; + mapping(bytes32 => mapping(uint256 => uint256)) 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 oracleIndex, + uint256 timestamp + ); + + // -- Modifiers -- + + modifier onlyOracle(uint256 _oracleIndex) { + require(_oracleIndex < maxOracles, "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 _maxOracles Maximum number of oracles + * @param _executionThreshold Number of votes required to execute a deny or allow call to the RewardsManager + * @param _voteTimeLimit Vote time limit in seconds + */ + constructor( + address _governor, + address _rewardsManager, + uint256 _maxOracles, + uint256 _executionThreshold, + uint256 _voteTimeLimit + ) { + require(_governor != address(0), "SAM: governor must be set"); + require(_rewardsManager != address(0), "SAM: rewardsManager must be set"); + require(_maxOracles > 1, "SAM: maxOracles must be greater than 1"); + require(_executionThreshold > 1, "SAM: executionThreshold must be greater than 1"); + + Governed._initialize(_governor); + rewardsManager = IRewardsManager(_rewardsManager); + + maxOracles = _maxOracles; + executionThreshold = _executionThreshold; + voteTimeLimit = _voteTimeLimit; + oracles = new address[](_maxOracles); + + // Initialize oracles array with empty addresses + for (uint256 i = 0; i < _maxOracles; i++) { + oracles[i] = address(0); + } + } + + // -- Functions -- + + /** + * @dev Set the vote time limit + * @param _voteTimeLimit Vote time limit in seconds + */ + function setVoteTimeLimit(uint256 _voteTimeLimit) external onlyGovernor { + voteTimeLimit = _voteTimeLimit; + emit VoteTimeLimitSet(_voteTimeLimit); + } + + /** + * @dev Set oracle address with index + * @param _index Index of the oracle + * @param _oracle Address of the oracle + */ + function setOracle(uint256 _index, address _oracle) external onlyGovernor { + require(_index < maxOracles, "SAM: index out of bounds"); + oracles[_index] = _oracle; + emit OracleSet(_index, _oracle); + } + + /** + * @dev Vote deny or allow for a subgraph. + * @param _subgraphDeploymentID Subgraph deployment ID + * @param _deny True to deny, false to allow + * @param _oracleIndex Index of the oracle voting + */ + function voteDenied( + bytes32 _subgraphDeploymentID, + bool _deny, + uint256 _oracleIndex + ) external onlyOracle(_oracleIndex) { + _voteDenied(_subgraphDeploymentID, _deny, _oracleIndex); + } + + /** + * @dev Vote deny or allow for a subgraph. + * When oracles cast their votes we store the timestamp of the vote. + * @param _subgraphDeploymentID Subgraph deployment ID + * @param _deny True to deny, false to allow + * @param _oracleIndex Index of the oracle voting + */ + function _voteDenied( + bytes32 _subgraphDeploymentID, + bool _deny, + uint256 _oracleIndex + ) private { + uint256 timestamp = block.timestamp; + + if (_deny) { + _checkVotes(_subgraphDeploymentID, _deny, _oracleIndex); + lastDenyVote[_subgraphDeploymentID][_oracleIndex] = timestamp; + } else { + _checkVotes(_subgraphDeploymentID, _deny, _oracleIndex); + lastAllowVote[_subgraphDeploymentID][_oracleIndex] = timestamp; + } + + emit OracleVote(_subgraphDeploymentID, _deny, _oracleIndex, timestamp); + } + + /** + * @dev 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. + * 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 + * @param _oracleIndex Index of the oracle voting + */ + function _checkVotes( + bytes32 _subgraphDeploymentID, + bool _deny, + uint256 _oracleIndex + ) private { + // init with 1 for current oracle's vote + uint256 votes = 1; + + // timeframe for a vote to be valid + uint256 voteTimeValiditiy = block.timestamp - voteTimeLimit; + + // corresponding votes based on _deny for a subgraph deployment + mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny + ? lastDenyVote[_subgraphDeploymentID] + : lastAllowVote[_subgraphDeploymentID]; + + for (uint256 i = 0; i < maxOracles; i++) { + // check if oracle has voted, skip check for current oracle + if (i != _oracleIndex && lastVoteForSubgraph[i] > voteTimeValiditiy) { + votes++; + } + + // check if execution threshold is reached + if (votes == executionThreshold) { + rewardsManager.setDenied(_subgraphDeploymentID, _deny); + break; + } + } + } +} diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts new file mode 100644 index 000000000..c0691bdc0 --- /dev/null +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -0,0 +1,288 @@ +import { expect } from 'chai' +import { constants } from 'ethers' + +import { ethers } from 'hardhat' + +import { SubgraphAvailabilityManager } from '../../build/types/SubgraphAvailabilityManager' +import { IRewardsManager } from '../../build/types/IRewardsManager' + +import { NetworkFixture } from '../lib/fixtures' +import * as deployment from '../lib/deployment' +import { getAccounts, randomAddress, Account, randomHexBytes } from '../lib/testHelpers' + +const { AddressZero } = constants + +describe('SubgraphAvailabilityManager', () => { + let me: Account + let governor: Account + let oracleOne: Account + let oracleTwo: Account + let oracleThree: Account + + let fixture: NetworkFixture + + const maxOracles = '5' + const executionThreshold = '3' + const voteTimeLimit = '5' // 5 seconds + let rewardsManager: IRewardsManager + let subgraphAvailabilityManager: SubgraphAvailabilityManager + + const subgraphDeploymentID1 = randomHexBytes() + + before(async () => { + ;[me, governor, oracleOne, oracleTwo, oracleThree] = await getAccounts() + + fixture = new NetworkFixture() + ;({ rewardsManager } = await fixture.load(governor.signer)) + subgraphAvailabilityManager = (await deployment.deployContract( + 'SubgraphAvailabilityManager', + governor.signer, + governor.address, + rewardsManager.address, + maxOracles, + executionThreshold, + voteTimeLimit, + )) as unknown as SubgraphAvailabilityManager + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + describe('deployment', () => { + it('should deploy', async () => { + expect(subgraphAvailabilityManager.address).to.be.properAddress + }) + + it('should revert if governor is address zero', async () => { + await expect( + deployment.deployContract( + 'SubgraphAvailabilityManager', + governor.signer, + AddressZero, + rewardsManager.address, + maxOracles, + executionThreshold, + voteTimeLimit, + ), + ).to.be.revertedWith('SAM: governor must be set') + }) + + it('should revert if rewardsManager is address zero', async () => { + await expect( + deployment.deployContract( + 'SubgraphAvailabilityManager', + governor.signer, + governor.address, + AddressZero, + maxOracles, + executionThreshold, + voteTimeLimit, + ), + ).to.be.revertedWith('SAM: rewardsManager must be set') + }) + }) + + describe('initializer', () => { + it('should init governor', async () => { + expect(await subgraphAvailabilityManager.governor()).to.be.equal(governor.address) + }) + + it('should init maxOracles and executionThreshold', async () => { + expect(await subgraphAvailabilityManager.maxOracles()).to.be.equal(maxOracles) + expect(await subgraphAvailabilityManager.executionThreshold()).to.be.equal(executionThreshold) + }) + + it('should init oracles with address zero', async () => { + expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(AddressZero) + expect(await subgraphAvailabilityManager.oracles(1)).to.be.equal(AddressZero) + expect(await subgraphAvailabilityManager.oracles(2)).to.be.equal(AddressZero) + expect(await subgraphAvailabilityManager.oracles(3)).to.be.equal(AddressZero) + expect(await subgraphAvailabilityManager.oracles(4)).to.be.equal(AddressZero) + await expect(subgraphAvailabilityManager.oracles(5)).to.be.reverted + }) + }) + + describe('set vote limit', async () => { + it('sets voteTimeLimit successfully', async () => { + const newVoteTimeLimit = 10 + await expect( + subgraphAvailabilityManager.connect(governor.signer).setVoteTimeLimit(newVoteTimeLimit), + ) + .emit(subgraphAvailabilityManager, 'VoteTimeLimitSet') + .withArgs(newVoteTimeLimit) + expect(await subgraphAvailabilityManager.voteTimeLimit()).to.be.equal(newVoteTimeLimit) + }) + + it('should fail if not called by governor', async () => { + const newVoteTimeLimit = 10 + await expect( + subgraphAvailabilityManager.connect(me.signer).setVoteTimeLimit(newVoteTimeLimit), + ).to.be.revertedWith('Only Governor can call') + }) + }) + + describe('set oracles', () => { + it('sets an oracle successfully', async () => { + const oracle = randomAddress() + await expect(subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracle)) + .emit(subgraphAvailabilityManager, 'OracleSet') + .withArgs(0, oracle) + expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(oracle) + }) + + it('should fail if not called by governor', async () => { + const oracle = randomAddress() + await expect( + subgraphAvailabilityManager.connect(me.signer).setOracle(0, oracle), + ).to.be.revertedWith('Only Governor can call') + }) + + it('should allow setting oracle to address zero', async () => { + await expect(subgraphAvailabilityManager.connect(governor.signer).setOracle(0, AddressZero)) + .emit(subgraphAvailabilityManager, 'OracleSet') + .withArgs(0, AddressZero) + expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(AddressZero) + }) + + it('should fail if index is out of bounds', async () => { + const oracle = randomAddress() + await expect( + subgraphAvailabilityManager.connect(governor.signer).setOracle(5, oracle), + ).to.be.revertedWith('SAM: index out of bounds') + }) + }) + + describe('vote denied', async () => { + beforeEach(async () => { + await subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracleOne.address) + await subgraphAvailabilityManager.connect(governor.signer).setOracle(1, oracleTwo.address) + await subgraphAvailabilityManager.connect(governor.signer).setOracle(2, oracleThree.address) + await rewardsManager + .connect(governor.signer) + .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) + }) + + it('votes denied successfully', async () => { + const denied = true + const tx = await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + const timestamp = (await ethers.provider.getBlock('latest')).timestamp + await expect(tx) + .to.emit(subgraphAvailabilityManager, 'OracleVote') + .withArgs(subgraphDeploymentID1, denied, 0, timestamp) + }) + + it('should fail if not called by oracle', async () => { + const denied = true + await expect( + subgraphAvailabilityManager.connect(me.signer).voteDenied(subgraphDeploymentID1, denied, 0), + ).to.be.revertedWith('SAM: caller must be oracle') + }) + + it('should fail if index is out of bounds', async () => { + const denied = true + await expect( + subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 5), + ).to.be.revertedWith('SAM: index out of bounds') + }) + + it('should fail if oracle used an incorrect index', async () => { + const denied = true + await expect( + subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 1), + ).to.be.revertedWith('SAM: caller must be oracle') + }) + + it('should still be allowed if only one oracle has voted', async () => { + const denied = true + const tx = await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await expect(tx).to.emit(subgraphAvailabilityManager, 'OracleVote') + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + + it('should be denied or allowed if majority of oracles have voted', async () => { + // 3/5 oracles vote denied = true + let denied = true + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo.signer) + .voteDenied(subgraphDeploymentID1, denied, 1) + const tx = await subgraphAvailabilityManager + .connect(oracleThree.signer) + .voteDenied(subgraphDeploymentID1, denied, 2) + await expect(tx) + .to.emit(rewardsManager, 'RewardsDenylistUpdated') + .withArgs(subgraphDeploymentID1, tx.blockNumber) + + // check that subgraph is denied + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true + + // 3/5 oracles vote denied = false + denied = false + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo.signer) + .voteDenied(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager + .connect(oracleThree.signer) + .voteDenied(subgraphDeploymentID1, denied, 2) + + // check that subgraph is not denied + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + + it('should not be denied if the same oracle votes three times', async () => { + const denied = true + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + + it('should not be denied if voteTimeLimit has passed and not enough oracles have voted', async () => { + // 2/3 oracles vote denied = true + const denied = true + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDenied(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo.signer) + .voteDenied(subgraphDeploymentID1, denied, 1) + + // increase time by 6 seconds + await ethers.provider.send('evm_increaseTime', [6]) + // last oracle votes denied = true + const tx = await subgraphAvailabilityManager + .connect(oracleThree.signer) + .voteDenied(subgraphDeploymentID1, denied, 2) + await expect(tx).to.not.emit(rewardsManager, 'RewardsDenylistUpdated') + + // subgraph state didn't change because enough time has passed so that + // previous votes are no longer valid + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + }) +}) From 77e8dc346fdae53be4c21b66a1c51171a6c22af1 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Mon, 20 Nov 2023 14:00:00 -0300 Subject: [PATCH 02/31] feat: vote denied many --- .../rewards/SubgraphAvailabilityManager.sol | 31 ++++--- .../test/rewards/subgraphAvailability.test.ts | 86 +++++++++++++++++++ 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index d12f3e47c..87b568457 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -118,6 +118,7 @@ contract SubgraphAvailabilityManager is Governed { /** * @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 @@ -130,6 +131,24 @@ contract SubgraphAvailabilityManager is Governed { _voteDenied(_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 voteDeniedMany( + 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++) { + _voteDenied(_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. @@ -137,11 +156,7 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny True to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function _voteDenied( - bytes32 _subgraphDeploymentID, - bool _deny, - uint256 _oracleIndex - ) private { + function _voteDenied(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { uint256 timestamp = block.timestamp; if (_deny) { @@ -163,11 +178,7 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny True to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function _checkVotes( - bytes32 _subgraphDeploymentID, - bool _deny, - uint256 _oracleIndex - ) private { + function _checkVotes(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { // init with 1 for current oracle's vote uint256 votes = 1; diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index c0691bdc0..c24bc9e77 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -28,6 +28,8 @@ describe('SubgraphAvailabilityManager', () => { let subgraphAvailabilityManager: SubgraphAvailabilityManager const subgraphDeploymentID1 = randomHexBytes() + const subgraphDeploymentID2 = randomHexBytes() + const subgraphDeploymentID3 = randomHexBytes() before(async () => { ;[me, governor, oracleOne, oracleTwo, oracleThree] = await getAccounts() @@ -285,4 +287,88 @@ describe('SubgraphAvailabilityManager', () => { expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false }) }) + + describe('vote many', async () => { + beforeEach(async () => { + await subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracleOne.address) + await subgraphAvailabilityManager.connect(governor.signer).setOracle(1, oracleTwo.address) + await subgraphAvailabilityManager.connect(governor.signer).setOracle(2, oracleThree.address) + await rewardsManager + .connect(governor.signer) + .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) + }) + + it('votes many successfully', async () => { + const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] + const denied = [true, false, true] + const tx = await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDeniedMany(subgraphs, denied, 0) + const timestamp = (await ethers.provider.getBlock('latest')).timestamp + await expect(tx) + .to.emit(subgraphAvailabilityManager, 'OracleVote') + .withArgs(subgraphDeploymentID1, true, 0, timestamp) + await expect(tx) + .to.emit(subgraphAvailabilityManager, 'OracleVote') + .withArgs(subgraphDeploymentID2, false, 0, timestamp) + await expect(tx) + .to.emit(subgraphAvailabilityManager, 'OracleVote') + .withArgs(subgraphDeploymentID3, true, 0, timestamp) + }) + + it('should change subgraph state if majority of oracles have voted', async () => { + const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] + const denied = [true, false, true] + // 3/5 oracles vote denied = true + await subgraphAvailabilityManager + .connect(oracleOne.signer) + .voteDeniedMany(subgraphs, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo.signer) + .voteDeniedMany(subgraphs, denied, 1) + + const tx = await subgraphAvailabilityManager + .connect(oracleThree.signer) + .voteDeniedMany(subgraphs, denied, 2) + + await expect(tx) + .to.emit(rewardsManager, 'RewardsDenylistUpdated') + .withArgs(subgraphDeploymentID1, tx.blockNumber) + await expect(tx) + .to.emit(rewardsManager, 'RewardsDenylistUpdated') + .withArgs(subgraphDeploymentID2, 0) + await expect(tx) + .to.emit(rewardsManager, 'RewardsDenylistUpdated') + .withArgs(subgraphDeploymentID3, tx.blockNumber) + + // check that subgraphs are denied + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true + expect(await rewardsManager.isDenied(subgraphDeploymentID2)).to.be.false + expect(await rewardsManager.isDenied(subgraphDeploymentID3)).to.be.true + }) + + it('should fail if not called by oracle', async () => { + const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] + const denied = [true, false, true] + await expect( + subgraphAvailabilityManager.connect(me.signer).voteDeniedMany(subgraphs, denied, 0), + ).to.be.revertedWith('SAM: caller must be oracle') + }) + + it('should fail if index is out of bounds', async () => { + const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] + const denied = [true, false, true] + await expect( + subgraphAvailabilityManager.connect(oracleOne.signer).voteDeniedMany(subgraphs, denied, 5), + ).to.be.revertedWith('SAM: index out of bounds') + }) + + it('should fail if oracle used an incorrect index', async () => { + const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] + const denied = [true, false, true] + await expect( + subgraphAvailabilityManager.connect(oracleOne.signer).voteDeniedMany(subgraphs, denied, 1), + ).to.be.revertedWith('SAM: caller must be oracle') + }) + }) }) From 1805cd994087302b109f1d441961a0ac61c7fc0a Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 24 Nov 2023 17:38:34 -0300 Subject: [PATCH 03/31] fix: pr feedback changes --- .../rewards/SubgraphAvailabilityManager.sol | 63 +++++++++++-------- .../test/rewards/subgraphAvailability.test.ts | 52 +++++++-------- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 87b568457..fdde84e34 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -17,15 +17,27 @@ import { IRewardsManager } from "../rewards/IRewardsManager.sol"; contract SubgraphAvailabilityManager is Governed { // -- Immutable -- + // Maximum number of oracles uint256 public immutable maxOracles; + + // Number of votes required to execute a deny or allow call to the RewardsManager uint256 public immutable executionThreshold; + + // Address of the RewardsManager contract IRewardsManager private immutable rewardsManager; // -- State -- + // Time limit for a vote to be valid uint256 public voteTimeLimit; + + // Array of oracle addresses address[] public oracles; + + // Mapping of subgraph deployment ID to oracle index to timestamp of last deny vote mapping(bytes32 => mapping(uint256 => uint256)) public lastDenyVote; + + // Mapping of subgraph deployment ID to oracle index to timestamp of last allow vote mapping(bytes32 => mapping(uint256 => uint256)) public lastAllowVote; // -- Events -- @@ -46,7 +58,7 @@ contract SubgraphAvailabilityManager is Governed { event OracleVote( bytes32 indexed subgraphDeploymentID, bool deny, - uint256 oracleIndex, + uint256 indexed oracleIndex, uint256 timestamp ); @@ -87,11 +99,6 @@ contract SubgraphAvailabilityManager is Governed { executionThreshold = _executionThreshold; voteTimeLimit = _voteTimeLimit; oracles = new address[](_maxOracles); - - // Initialize oracles array with empty addresses - for (uint256 i = 0; i < _maxOracles; i++) { - oracles[i] = address(0); - } } // -- Functions -- @@ -123,12 +130,12 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny True to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function voteDenied( + function vote( bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex ) external onlyOracle(_oracleIndex) { - _voteDenied(_subgraphDeploymentID, _deny, _oracleIndex); + _vote(_subgraphDeploymentID, _deny, _oracleIndex); } /** @@ -138,33 +145,38 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny Array of booleans, true to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function voteDeniedMany( + 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++) { - _voteDenied(_subgraphDeploymentID[i], _deny[i], _oracleIndex); + _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 _voteDenied(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { + function _vote(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { uint256 timestamp = block.timestamp; - if (_deny) { - _checkVotes(_subgraphDeploymentID, _deny, _oracleIndex); - lastDenyVote[_subgraphDeploymentID][_oracleIndex] = timestamp; - } else { - _checkVotes(_subgraphDeploymentID, _deny, _oracleIndex); - lastAllowVote[_subgraphDeploymentID][_oracleIndex] = timestamp; + // corresponding votes based on _deny for a subgraph deployment + mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny + ? lastDenyVote[_subgraphDeploymentID] + : lastAllowVote[_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); @@ -172,15 +184,13 @@ contract SubgraphAvailabilityManager is Governed { /** * @dev 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. * 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 - * @param _oracleIndex Index of the oracle voting + * @return True if execution threshold is reached */ - function _checkVotes(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { - // init with 1 for current oracle's vote - uint256 votes = 1; + function checkVotes(bytes32 _subgraphDeploymentID, bool _deny) public view returns (bool) { + uint256 votes = 0; // timeframe for a vote to be valid uint256 voteTimeValiditiy = block.timestamp - voteTimeLimit; @@ -191,16 +201,17 @@ contract SubgraphAvailabilityManager is Governed { : lastAllowVote[_subgraphDeploymentID]; for (uint256 i = 0; i < maxOracles; i++) { - // check if oracle has voted, skip check for current oracle - if (i != _oracleIndex && lastVoteForSubgraph[i] > voteTimeValiditiy) { + // check if vote is within the vote time limit + if (lastVoteForSubgraph[i] > voteTimeValiditiy) { votes++; } // check if execution threshold is reached if (votes == executionThreshold) { - rewardsManager.setDenied(_subgraphDeploymentID, _deny); - break; + return true; } } + + return false; } } diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index c24bc9e77..75635b91e 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -173,7 +173,7 @@ describe('SubgraphAvailabilityManager', () => { const denied = true const tx = await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) const timestamp = (await ethers.provider.getBlock('latest')).timestamp await expect(tx) .to.emit(subgraphAvailabilityManager, 'OracleVote') @@ -183,7 +183,7 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if not called by oracle', async () => { const denied = true await expect( - subgraphAvailabilityManager.connect(me.signer).voteDenied(subgraphDeploymentID1, denied, 0), + subgraphAvailabilityManager.connect(me.signer).vote(subgraphDeploymentID1, denied, 0), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -192,7 +192,7 @@ describe('SubgraphAvailabilityManager', () => { await expect( subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 5), + .vote(subgraphDeploymentID1, denied, 5), ).to.be.revertedWith('SAM: index out of bounds') }) @@ -201,7 +201,7 @@ describe('SubgraphAvailabilityManager', () => { await expect( subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 1), + .vote(subgraphDeploymentID1, denied, 1), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -209,7 +209,7 @@ describe('SubgraphAvailabilityManager', () => { const denied = true const tx = await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await expect(tx).to.emit(subgraphAvailabilityManager, 'OracleVote') expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false }) @@ -219,13 +219,13 @@ describe('SubgraphAvailabilityManager', () => { let denied = true await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager .connect(oracleTwo.signer) - .voteDenied(subgraphDeploymentID1, denied, 1) + .vote(subgraphDeploymentID1, denied, 1) const tx = await subgraphAvailabilityManager .connect(oracleThree.signer) - .voteDenied(subgraphDeploymentID1, denied, 2) + .vote(subgraphDeploymentID1, denied, 2) await expect(tx) .to.emit(rewardsManager, 'RewardsDenylistUpdated') .withArgs(subgraphDeploymentID1, tx.blockNumber) @@ -237,13 +237,13 @@ describe('SubgraphAvailabilityManager', () => { denied = false await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager .connect(oracleTwo.signer) - .voteDenied(subgraphDeploymentID1, denied, 1) + .vote(subgraphDeploymentID1, denied, 1) await subgraphAvailabilityManager .connect(oracleThree.signer) - .voteDenied(subgraphDeploymentID1, denied, 2) + .vote(subgraphDeploymentID1, denied, 2) // check that subgraph is not denied expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false @@ -253,13 +253,13 @@ describe('SubgraphAvailabilityManager', () => { const denied = true await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false }) @@ -269,17 +269,17 @@ describe('SubgraphAvailabilityManager', () => { const denied = true await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDenied(subgraphDeploymentID1, denied, 0) + .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager .connect(oracleTwo.signer) - .voteDenied(subgraphDeploymentID1, denied, 1) + .vote(subgraphDeploymentID1, denied, 1) // increase time by 6 seconds await ethers.provider.send('evm_increaseTime', [6]) // last oracle votes denied = true const tx = await subgraphAvailabilityManager .connect(oracleThree.signer) - .voteDenied(subgraphDeploymentID1, denied, 2) + .vote(subgraphDeploymentID1, denied, 2) await expect(tx).to.not.emit(rewardsManager, 'RewardsDenylistUpdated') // subgraph state didn't change because enough time has passed so that @@ -303,7 +303,7 @@ describe('SubgraphAvailabilityManager', () => { const denied = [true, false, true] const tx = await subgraphAvailabilityManager .connect(oracleOne.signer) - .voteDeniedMany(subgraphs, denied, 0) + .voteMany(subgraphs, denied, 0) const timestamp = (await ethers.provider.getBlock('latest')).timestamp await expect(tx) .to.emit(subgraphAvailabilityManager, 'OracleVote') @@ -320,16 +320,12 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] // 3/5 oracles vote denied = true - await subgraphAvailabilityManager - .connect(oracleOne.signer) - .voteDeniedMany(subgraphs, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo.signer) - .voteDeniedMany(subgraphs, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo.signer).voteMany(subgraphs, denied, 1) const tx = await subgraphAvailabilityManager .connect(oracleThree.signer) - .voteDeniedMany(subgraphs, denied, 2) + .voteMany(subgraphs, denied, 2) await expect(tx) .to.emit(rewardsManager, 'RewardsDenylistUpdated') @@ -351,7 +347,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(me.signer).voteDeniedMany(subgraphs, denied, 0), + subgraphAvailabilityManager.connect(me.signer).voteMany(subgraphs, denied, 0), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -359,7 +355,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(oracleOne.signer).voteDeniedMany(subgraphs, denied, 5), + subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 5), ).to.be.revertedWith('SAM: index out of bounds') }) @@ -367,7 +363,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(oracleOne.signer).voteDeniedMany(subgraphs, denied, 1), + subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 1), ).to.be.revertedWith('SAM: caller must be oracle') }) }) From f4942f2afb857548ae4b1ce5ba447047f4d8aed9 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Mon, 27 Nov 2023 14:42:00 -0300 Subject: [PATCH 04/31] fix: use natspec for state variables comments --- .../rewards/SubgraphAvailabilityManager.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index fdde84e34..f3241c447 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -17,27 +17,27 @@ import { IRewardsManager } from "../rewards/IRewardsManager.sol"; contract SubgraphAvailabilityManager is Governed { // -- Immutable -- - // Maximum number of oracles + /// @notice Maximum number of oracles uint256 public immutable maxOracles; - // Number of votes required to execute a deny or allow call to the RewardsManager + /// @notice Number of votes required to execute a deny or allow call to the RewardsManager uint256 public immutable executionThreshold; - // Address of the RewardsManager contract + /// @dev Address of the RewardsManager contract IRewardsManager private immutable rewardsManager; // -- State -- - // Time limit for a vote to be valid + /// @notice Time limit for a vote to be valid uint256 public voteTimeLimit; - // Array of oracle addresses + /// @notice Array of oracle addresses address[] public oracles; - // Mapping of subgraph deployment ID to oracle index to timestamp of last deny vote + /// @notice Mapping of subgraph deployment ID to oracle index to timestamp of last deny vote mapping(bytes32 => mapping(uint256 => uint256)) public lastDenyVote; - // Mapping of subgraph deployment ID to oracle index to timestamp of last allow vote + /// @notice Mapping of subgraph deployment ID to oracle index to timestamp of last allow vote mapping(bytes32 => mapping(uint256 => uint256)) public lastAllowVote; // -- Events -- From fa9b153a1c010829fdc8632748b2580ae3eb5d4b Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Mon, 27 Nov 2023 17:57:50 -0300 Subject: [PATCH 05/31] fix: use currentNonce to refresh votes, init oracles with valid addressess --- .../rewards/SubgraphAvailabilityManager.sol | 36 ++- .../test/rewards/subgraphAvailability.test.ts | 301 +++++++++++++----- .../sdk/src/deployments/lib/types/contract.ts | 2 +- 3 files changed, 242 insertions(+), 97 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index f3241c447..ccc0426c7 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -28,17 +28,20 @@ contract SubgraphAvailabilityManager is Governed { // -- State -- + /// @dev Nonce for generating votes on subgraph deployment IDs + uint256 private currentNonce; + /// @notice Time limit for a vote to be valid uint256 public voteTimeLimit; /// @notice Array of oracle addresses address[] public oracles; - /// @notice Mapping of subgraph deployment ID to oracle index to timestamp of last deny vote - mapping(bytes32 => mapping(uint256 => uint256)) public lastDenyVote; + /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last deny vote + mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastDenyVote; - /// @notice Mapping of subgraph deployment ID to oracle index to timestamp of last allow vote - mapping(bytes32 => mapping(uint256 => uint256)) public lastAllowVote; + /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last allow vote + mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastAllowVote; // -- Events -- @@ -85,12 +88,19 @@ contract SubgraphAvailabilityManager is Governed { address _rewardsManager, uint256 _maxOracles, uint256 _executionThreshold, - uint256 _voteTimeLimit + uint256 _voteTimeLimit, + address[] memory _oracles ) { require(_governor != address(0), "SAM: governor must be set"); require(_rewardsManager != address(0), "SAM: rewardsManager must be set"); require(_maxOracles > 1, "SAM: maxOracles must be greater than 1"); require(_executionThreshold > 1, "SAM: executionThreshold must be greater than 1"); + require(_oracles.length == _maxOracles, "SAM: oracles length must equal maxOracles"); + + // Oracles should not be address zero + for (uint256 i = 0; i < _oracles.length; i++) { + require(_oracles[i] != address(0), "SAM: oracle cannot be address zero"); + } Governed._initialize(_governor); rewardsManager = IRewardsManager(_rewardsManager); @@ -98,7 +108,7 @@ contract SubgraphAvailabilityManager is Governed { maxOracles = _maxOracles; executionThreshold = _executionThreshold; voteTimeLimit = _voteTimeLimit; - oracles = new address[](_maxOracles); + oracles = _oracles; } // -- Functions -- @@ -109,6 +119,7 @@ contract SubgraphAvailabilityManager is Governed { */ function setVoteTimeLimit(uint256 _voteTimeLimit) external onlyGovernor { voteTimeLimit = _voteTimeLimit; + currentNonce++; emit VoteTimeLimitSet(_voteTimeLimit); } @@ -119,7 +130,12 @@ contract SubgraphAvailabilityManager is Governed { */ function setOracle(uint256 _index, address _oracle) external onlyGovernor { require(_index < maxOracles, "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); } @@ -170,8 +186,8 @@ contract SubgraphAvailabilityManager is Governed { // corresponding votes based on _deny for a subgraph deployment mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny - ? lastDenyVote[_subgraphDeploymentID] - : lastAllowVote[_subgraphDeploymentID]; + ? lastDenyVote[currentNonce][_subgraphDeploymentID] + : lastAllowVote[currentNonce][_subgraphDeploymentID]; lastVoteForSubgraph[_oracleIndex] = timestamp; // check if execution threshold is reached, if it is call the RewardsManager @@ -197,8 +213,8 @@ contract SubgraphAvailabilityManager is Governed { // corresponding votes based on _deny for a subgraph deployment mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny - ? lastDenyVote[_subgraphDeploymentID] - : lastAllowVote[_subgraphDeploymentID]; + ? lastDenyVote[currentNonce][_subgraphDeploymentID] + : lastAllowVote[currentNonce][_subgraphDeploymentID]; for (uint256 i = 0; i < maxOracles; i++) { // check if vote is within the vote time limit diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 75635b91e..4006f42a9 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -1,29 +1,47 @@ +import hre from 'hardhat' import { expect } from 'chai' import { constants } from 'ethers' import { ethers } from 'hardhat' +import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' + import { SubgraphAvailabilityManager } from '../../build/types/SubgraphAvailabilityManager' import { IRewardsManager } from '../../build/types/IRewardsManager' import { NetworkFixture } from '../lib/fixtures' -import * as deployment from '../lib/deployment' -import { getAccounts, randomAddress, Account, randomHexBytes } from '../lib/testHelpers' + +import { + GraphNetworkContracts, + randomAddress, + randomHexBytes, + deploy, + DeployType +} from '@graphprotocol/sdk' const { AddressZero } = constants describe('SubgraphAvailabilityManager', () => { - let me: Account - let governor: Account - let oracleOne: Account - let oracleTwo: Account - let oracleThree: Account + const graph = hre.graph() + let me: SignerWithAddress + let governor: SignerWithAddress + + let oracles: string[] + let oracleOne: SignerWithAddress + let oracleTwo: SignerWithAddress + let oracleThree: SignerWithAddress + let oracleFour: SignerWithAddress + let oracleFive: SignerWithAddress + + let newOracle: SignerWithAddress let fixture: NetworkFixture const maxOracles = '5' const executionThreshold = '3' const voteTimeLimit = '5' // 5 seconds + + let contracts: GraphNetworkContracts let rewardsManager: IRewardsManager let subgraphAvailabilityManager: SubgraphAvailabilityManager @@ -32,19 +50,37 @@ describe('SubgraphAvailabilityManager', () => { const subgraphDeploymentID3 = randomHexBytes() before(async () => { - ;[me, governor, oracleOne, oracleTwo, oracleThree] = await getAccounts() - - fixture = new NetworkFixture() - ;({ rewardsManager } = await fixture.load(governor.signer)) - subgraphAvailabilityManager = (await deployment.deployContract( - 'SubgraphAvailabilityManager', - governor.signer, - governor.address, - rewardsManager.address, - maxOracles, - executionThreshold, - voteTimeLimit, - )) as unknown as SubgraphAvailabilityManager + ;[me, oracleOne, oracleTwo, oracleThree, oracleFour, oracleFive, newOracle] = + await graph.getTestAccounts() + ;({ governor } = await graph.getNamedAccounts()) + + oracles = [ + oracleOne.address, + oracleTwo.address, + oracleThree.address, + oracleFour.address, + oracleFive.address, + ] + + fixture = new NetworkFixture(graph.provider) + contracts = await fixture.load(governor) + rewardsManager = contracts.RewardsManager as IRewardsManager + const deployResult = (await deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + governor.address, + rewardsManager.address, + maxOracles, + executionThreshold, + voteTimeLimit, + oracles, + ] + } + )) + subgraphAvailabilityManager = deployResult.contract as SubgraphAvailabilityManager }) beforeEach(async function () { @@ -60,30 +96,68 @@ describe('SubgraphAvailabilityManager', () => { expect(subgraphAvailabilityManager.address).to.be.properAddress }) + it('should revert if an oracle is address zero', async () => { + await expect( + deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + governor.address, + rewardsManager.address, + maxOracles, + executionThreshold, + voteTimeLimit, + [ + AddressZero, + oracleTwo.address, + oracleThree.address, + oracleFour.address, + oracleFive.address, + ], + ] + } + ), + ).to.be.revertedWith('SAM: oracle cannot be address zero') + }) + it('should revert if governor is address zero', async () => { await expect( - deployment.deployContract( - 'SubgraphAvailabilityManager', - governor.signer, - AddressZero, - rewardsManager.address, - maxOracles, - executionThreshold, - voteTimeLimit, + deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + AddressZero, + rewardsManager.address, + maxOracles, + executionThreshold, + voteTimeLimit, + oracles, + ] + } ), ).to.be.revertedWith('SAM: governor must be set') }) it('should revert if rewardsManager is address zero', async () => { await expect( - deployment.deployContract( - 'SubgraphAvailabilityManager', - governor.signer, - governor.address, - AddressZero, - maxOracles, - executionThreshold, - voteTimeLimit, + deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + governor.address, + AddressZero, + maxOracles, + executionThreshold, + voteTimeLimit, + oracles, + ] + } ), ).to.be.revertedWith('SAM: rewardsManager must be set') }) @@ -99,13 +173,14 @@ describe('SubgraphAvailabilityManager', () => { expect(await subgraphAvailabilityManager.executionThreshold()).to.be.equal(executionThreshold) }) - it('should init oracles with address zero', async () => { - expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(AddressZero) - expect(await subgraphAvailabilityManager.oracles(1)).to.be.equal(AddressZero) - expect(await subgraphAvailabilityManager.oracles(2)).to.be.equal(AddressZero) - expect(await subgraphAvailabilityManager.oracles(3)).to.be.equal(AddressZero) - expect(await subgraphAvailabilityManager.oracles(4)).to.be.equal(AddressZero) - await expect(subgraphAvailabilityManager.oracles(5)).to.be.reverted + it('should init voteTimeLimit', async () => { + expect(await subgraphAvailabilityManager.voteTimeLimit()).to.be.equal(voteTimeLimit) + }) + + it('should init oracles', async () => { + for (let i = 0; i < oracles.length; i++) { + expect(await subgraphAvailabilityManager.oracles(i)).to.be.equal(oracles[i]) + } }) }) @@ -113,7 +188,7 @@ describe('SubgraphAvailabilityManager', () => { it('sets voteTimeLimit successfully', async () => { const newVoteTimeLimit = 10 await expect( - subgraphAvailabilityManager.connect(governor.signer).setVoteTimeLimit(newVoteTimeLimit), + subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(newVoteTimeLimit), ) .emit(subgraphAvailabilityManager, 'VoteTimeLimitSet') .withArgs(newVoteTimeLimit) @@ -123,7 +198,7 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if not called by governor', async () => { const newVoteTimeLimit = 10 await expect( - subgraphAvailabilityManager.connect(me.signer).setVoteTimeLimit(newVoteTimeLimit), + subgraphAvailabilityManager.connect(me).setVoteTimeLimit(newVoteTimeLimit), ).to.be.revertedWith('Only Governor can call') }) }) @@ -131,7 +206,7 @@ describe('SubgraphAvailabilityManager', () => { describe('set oracles', () => { it('sets an oracle successfully', async () => { const oracle = randomAddress() - await expect(subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracle)) + await expect(subgraphAvailabilityManager.connect(governor).setOracle(0, oracle)) .emit(subgraphAvailabilityManager, 'OracleSet') .withArgs(0, oracle) expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(oracle) @@ -140,39 +215,38 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if not called by governor', async () => { const oracle = randomAddress() await expect( - subgraphAvailabilityManager.connect(me.signer).setOracle(0, oracle), + subgraphAvailabilityManager.connect(me).setOracle(0, oracle), ).to.be.revertedWith('Only Governor can call') }) - it('should allow setting oracle to address zero', async () => { - await expect(subgraphAvailabilityManager.connect(governor.signer).setOracle(0, AddressZero)) - .emit(subgraphAvailabilityManager, 'OracleSet') - .withArgs(0, AddressZero) - expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(AddressZero) + it('should fail if setting oracle to address zero', async () => { + await expect( + subgraphAvailabilityManager.connect(governor).setOracle(0, AddressZero), + ).to.revertedWith('SAM: oracle cannot be address zero') }) it('should fail if index is out of bounds', async () => { const oracle = randomAddress() await expect( - subgraphAvailabilityManager.connect(governor.signer).setOracle(5, oracle), + subgraphAvailabilityManager.connect(governor).setOracle(5, oracle), ).to.be.revertedWith('SAM: index out of bounds') }) }) - describe('vote denied', async () => { + describe('voting', async () => { beforeEach(async () => { - await subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracleOne.address) - await subgraphAvailabilityManager.connect(governor.signer).setOracle(1, oracleTwo.address) - await subgraphAvailabilityManager.connect(governor.signer).setOracle(2, oracleThree.address) + await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) + await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) + await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) await rewardsManager - .connect(governor.signer) + .connect(governor) .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) }) it('votes denied successfully', async () => { const denied = true const tx = await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) const timestamp = (await ethers.provider.getBlock('latest')).timestamp await expect(tx) @@ -183,7 +257,7 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if not called by oracle', async () => { const denied = true await expect( - subgraphAvailabilityManager.connect(me.signer).vote(subgraphDeploymentID1, denied, 0), + subgraphAvailabilityManager.connect(me).vote(subgraphDeploymentID1, denied, 0), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -191,7 +265,7 @@ describe('SubgraphAvailabilityManager', () => { const denied = true await expect( subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 5), ).to.be.revertedWith('SAM: index out of bounds') }) @@ -200,7 +274,7 @@ describe('SubgraphAvailabilityManager', () => { const denied = true await expect( subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 1), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -208,7 +282,7 @@ describe('SubgraphAvailabilityManager', () => { it('should still be allowed if only one oracle has voted', async () => { const denied = true const tx = await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await expect(tx).to.emit(subgraphAvailabilityManager, 'OracleVote') expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false @@ -218,13 +292,13 @@ describe('SubgraphAvailabilityManager', () => { // 3/5 oracles vote denied = true let denied = true await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager - .connect(oracleTwo.signer) + .connect(oracleTwo) .vote(subgraphDeploymentID1, denied, 1) const tx = await subgraphAvailabilityManager - .connect(oracleThree.signer) + .connect(oracleThree) .vote(subgraphDeploymentID1, denied, 2) await expect(tx) .to.emit(rewardsManager, 'RewardsDenylistUpdated') @@ -236,13 +310,13 @@ describe('SubgraphAvailabilityManager', () => { // 3/5 oracles vote denied = false denied = false await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager - .connect(oracleTwo.signer) + .connect(oracleTwo) .vote(subgraphDeploymentID1, denied, 1) await subgraphAvailabilityManager - .connect(oracleThree.signer) + .connect(oracleThree) .vote(subgraphDeploymentID1, denied, 2) // check that subgraph is not denied @@ -252,13 +326,13 @@ describe('SubgraphAvailabilityManager', () => { it('should not be denied if the same oracle votes three times', async () => { const denied = true await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false @@ -268,17 +342,17 @@ describe('SubgraphAvailabilityManager', () => { // 2/3 oracles vote denied = true const denied = true await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .vote(subgraphDeploymentID1, denied, 0) await subgraphAvailabilityManager - .connect(oracleTwo.signer) + .connect(oracleTwo) .vote(subgraphDeploymentID1, denied, 1) // increase time by 6 seconds await ethers.provider.send('evm_increaseTime', [6]) // last oracle votes denied = true const tx = await subgraphAvailabilityManager - .connect(oracleThree.signer) + .connect(oracleThree) .vote(subgraphDeploymentID1, denied, 2) await expect(tx).to.not.emit(rewardsManager, 'RewardsDenylistUpdated') @@ -290,11 +364,11 @@ describe('SubgraphAvailabilityManager', () => { describe('vote many', async () => { beforeEach(async () => { - await subgraphAvailabilityManager.connect(governor.signer).setOracle(0, oracleOne.address) - await subgraphAvailabilityManager.connect(governor.signer).setOracle(1, oracleTwo.address) - await subgraphAvailabilityManager.connect(governor.signer).setOracle(2, oracleThree.address) + await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) + await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) + await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) await rewardsManager - .connect(governor.signer) + .connect(governor) .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) }) @@ -302,7 +376,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] const tx = await subgraphAvailabilityManager - .connect(oracleOne.signer) + .connect(oracleOne) .voteMany(subgraphs, denied, 0) const timestamp = (await ethers.provider.getBlock('latest')).timestamp await expect(tx) @@ -320,11 +394,11 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] // 3/5 oracles vote denied = true - await subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo.signer).voteMany(subgraphs, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).voteMany(subgraphs, denied, 1) const tx = await subgraphAvailabilityManager - .connect(oracleThree.signer) + .connect(oracleThree) .voteMany(subgraphs, denied, 2) await expect(tx) @@ -347,7 +421,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(me.signer).voteMany(subgraphs, denied, 0), + subgraphAvailabilityManager.connect(me).voteMany(subgraphs, denied, 0), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -355,7 +429,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 5), + subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 5), ).to.be.revertedWith('SAM: index out of bounds') }) @@ -363,8 +437,63 @@ describe('SubgraphAvailabilityManager', () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] await expect( - subgraphAvailabilityManager.connect(oracleOne.signer).voteMany(subgraphs, denied, 1), + subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 1), ).to.be.revertedWith('SAM: caller must be oracle') }) }) + + describe('refreshing votes', () => { + beforeEach(async () => { + await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) + await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) + await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) + await rewardsManager + .connect(governor) + .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) + }) + + it('should refresh votes if an oracle is replaced', async () => { + const denied = true + // 2/3 oracles vote denied = true + await subgraphAvailabilityManager + .connect(oracleOne) + .vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo) + .vote(subgraphDeploymentID1, denied, 1) + + // replace oracleOne with a new oracle + await subgraphAvailabilityManager.connect(governor).setOracle(2, newOracle.address) + + // new oracle votes denied = true + await subgraphAvailabilityManager + .connect(newOracle) + .vote(subgraphDeploymentID1, denied, 2) + + // subgraph shouldn't be denied because setting a new oracle should refresh the votes + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + + it('should refresh votes if voteTimeLimit changes', async () => { + const denied = true + // 2/3 oracles vote denied = true + await subgraphAvailabilityManager + .connect(oracleOne) + .vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager + .connect(oracleTwo) + .vote(subgraphDeploymentID1, denied, 1) + + // change voteTimeLimit to 10 seconds + await subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(10) + + // last oracle votes denied = true + await subgraphAvailabilityManager + .connect(oracleThree) + .vote(subgraphDeploymentID1, denied, 2) + + // subgraph shouldn't be denied because voteTimeLimit should refresh the votes + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false + }) + }) }) diff --git a/packages/sdk/src/deployments/lib/types/contract.ts b/packages/sdk/src/deployments/lib/types/contract.ts index 754ef4b1b..700689445 100644 --- a/packages/sdk/src/deployments/lib/types/contract.ts +++ b/packages/sdk/src/deployments/lib/types/contract.ts @@ -2,4 +2,4 @@ import type { BigNumber, Contract } from 'ethers' export type ContractList = Partial> -export type ContractParam = string | BigNumber | number +export type ContractParam = string | BigNumber | number | Array From 08e269f8f7feb54bdce9fa6eeec8d299d295c5c5 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Tue, 28 Nov 2023 09:54:18 -0300 Subject: [PATCH 06/31] fix: update functions docs --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index ccc0426c7..901e2d162 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -114,7 +114,7 @@ contract SubgraphAvailabilityManager is Governed { // -- Functions -- /** - * @dev Set the vote time limit + * @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 { @@ -124,7 +124,7 @@ contract SubgraphAvailabilityManager is Governed { } /** - * @dev Set oracle address with index + * @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 */ From 35f639202b3164174147d47000662f1cf394f189 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Tue, 28 Nov 2023 09:58:47 -0300 Subject: [PATCH 07/31] fix: removed unnecessary code in tests --- .../test/rewards/subgraphAvailability.test.ts | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 4006f42a9..47bad0c97 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -81,6 +81,9 @@ describe('SubgraphAvailabilityManager', () => { } )) subgraphAvailabilityManager = deployResult.contract as SubgraphAvailabilityManager + await rewardsManager + .connect(governor) + .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) }) beforeEach(async function () { @@ -234,15 +237,6 @@ describe('SubgraphAvailabilityManager', () => { }) describe('voting', async () => { - beforeEach(async () => { - await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) - await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) - await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) - await rewardsManager - .connect(governor) - .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) - }) - it('votes denied successfully', async () => { const denied = true const tx = await subgraphAvailabilityManager @@ -363,15 +357,6 @@ describe('SubgraphAvailabilityManager', () => { }) describe('vote many', async () => { - beforeEach(async () => { - await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) - await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) - await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) - await rewardsManager - .connect(governor) - .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) - }) - it('votes many successfully', async () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] @@ -443,15 +428,6 @@ describe('SubgraphAvailabilityManager', () => { }) describe('refreshing votes', () => { - beforeEach(async () => { - await subgraphAvailabilityManager.connect(governor).setOracle(0, oracleOne.address) - await subgraphAvailabilityManager.connect(governor).setOracle(1, oracleTwo.address) - await subgraphAvailabilityManager.connect(governor).setOracle(2, oracleThree.address) - await rewardsManager - .connect(governor) - .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) - }) - it('should refresh votes if an oracle is replaced', async () => { const denied = true // 2/3 oracles vote denied = true From 0896337b40c0ec859e21068022121bad2c9d9567 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 8 Dec 2023 17:36:01 -0300 Subject: [PATCH 08/31] fix: typo, comments and changed maxOracles to be a constant --- .../rewards/SubgraphAvailabilityManager.sol | 47 ++++++++++--------- .../test/rewards/subgraphAvailability.test.ts | 47 ++++++++++++++++--- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 901e2d162..43ce865ee 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -2,23 +2,26 @@ pragma solidity ^0.7.6; -import "../governance/Governed.sol"; +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 or not. When enough oracles have voted to deny or - * allow a subgraph, it calls the RewardsManager Contract to set the correct state. - * The number of oracles and the execution threshold are set at deployment time. + * 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 number of oracles and the execution threshold are set at deployment time. * Only governance can set the oracles. */ contract SubgraphAvailabilityManager is Governed { + using SafeMathUpgradeable for uint256; + // -- Immutable -- - /// @notice Maximum number of oracles - uint256 public immutable maxOracles; + /// @notice Number of oracles + uint256 public constant numOracles = 5; /// @notice Number of votes required to execute a deny or allow call to the RewardsManager uint256 public immutable executionThreshold; @@ -28,19 +31,22 @@ contract SubgraphAvailabilityManager is Governed { // -- State -- - /// @dev Nonce for generating votes on subgraph deployment IDs + /// @dev Nonce for generating votes on subgraph deployment IDs. + /// Increased whenever oracles or voteTimeLimit change, to invalidate old votes. uint256 private currentNonce; /// @notice Time limit for a vote to be valid uint256 public voteTimeLimit; /// @notice Array of oracle addresses - address[] public oracles; + address[numOracles] public oracles; /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last deny vote + /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastDenyVote; /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last allow vote + /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastAllowVote; // -- Events -- @@ -68,7 +74,7 @@ contract SubgraphAvailabilityManager is Governed { // -- Modifiers -- modifier onlyOracle(uint256 _oracleIndex) { - require(_oracleIndex < maxOracles, "SAM: index out of bounds"); + require(_oracleIndex < numOracles, "SAM: index out of bounds"); require(msg.sender == oracles[_oracleIndex], "SAM: caller must be oracle"); _; } @@ -79,23 +85,19 @@ contract SubgraphAvailabilityManager is Governed { * @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 _maxOracles Maximum number of oracles * @param _executionThreshold Number of votes required to execute a deny or allow call to the RewardsManager * @param _voteTimeLimit Vote time limit in seconds */ constructor( address _governor, address _rewardsManager, - uint256 _maxOracles, uint256 _executionThreshold, uint256 _voteTimeLimit, - address[] memory _oracles + address[numOracles] memory _oracles ) { require(_governor != address(0), "SAM: governor must be set"); require(_rewardsManager != address(0), "SAM: rewardsManager must be set"); - require(_maxOracles > 1, "SAM: maxOracles must be greater than 1"); - require(_executionThreshold > 1, "SAM: executionThreshold must be greater than 1"); - require(_oracles.length == _maxOracles, "SAM: oracles length must equal maxOracles"); + require(_executionThreshold >= numOracles.div(2).add(1), "SAM: executionThreshold too low"); // Oracles should not be address zero for (uint256 i = 0; i < _oracles.length; i++) { @@ -105,7 +107,6 @@ contract SubgraphAvailabilityManager is Governed { Governed._initialize(_governor); rewardsManager = IRewardsManager(_rewardsManager); - maxOracles = _maxOracles; executionThreshold = _executionThreshold; voteTimeLimit = _voteTimeLimit; oracles = _oracles; @@ -129,7 +130,7 @@ contract SubgraphAvailabilityManager is Governed { * @param _oracle Address of the oracle */ function setOracle(uint256 _index, address _oracle) external onlyGovernor { - require(_index < maxOracles, "SAM: index out of bounds"); + require(_index < numOracles, "SAM: index out of bounds"); require(_oracle != address(0), "SAM: oracle cannot be address zero"); oracles[_index] = _oracle; @@ -181,7 +182,11 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny True to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function _vote(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { + function _vote( + bytes32 _subgraphDeploymentID, + bool _deny, + uint256 _oracleIndex + ) private { uint256 timestamp = block.timestamp; // corresponding votes based on _deny for a subgraph deployment @@ -209,16 +214,16 @@ contract SubgraphAvailabilityManager is Governed { uint256 votes = 0; // timeframe for a vote to be valid - uint256 voteTimeValiditiy = block.timestamp - voteTimeLimit; + uint256 voteTimeValidity = block.timestamp - voteTimeLimit; // corresponding votes based on _deny for a subgraph deployment mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny ? lastDenyVote[currentNonce][_subgraphDeploymentID] : lastAllowVote[currentNonce][_subgraphDeploymentID]; - for (uint256 i = 0; i < maxOracles; i++) { + for (uint256 i = 0; i < numOracles; i++) { // check if vote is within the vote time limit - if (lastVoteForSubgraph[i] > voteTimeValiditiy) { + if (lastVoteForSubgraph[i] > voteTimeValidity) { votes++; } diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 47bad0c97..2ced18463 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -37,7 +37,6 @@ describe('SubgraphAvailabilityManager', () => { let fixture: NetworkFixture - const maxOracles = '5' const executionThreshold = '3' const voteTimeLimit = '5' // 5 seconds @@ -73,7 +72,6 @@ describe('SubgraphAvailabilityManager', () => { args: [ governor.address, rewardsManager.address, - maxOracles, executionThreshold, voteTimeLimit, oracles, @@ -81,6 +79,7 @@ describe('SubgraphAvailabilityManager', () => { } )) subgraphAvailabilityManager = deployResult.contract as SubgraphAvailabilityManager + await rewardsManager .connect(governor) .setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) @@ -99,6 +98,25 @@ describe('SubgraphAvailabilityManager', () => { expect(subgraphAvailabilityManager.address).to.be.properAddress }) + it('should revert if oracles array is less than 5', async () => { + await expect( + deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + governor.address, + rewardsManager.address, + executionThreshold, + voteTimeLimit, + [oracleOne.address, oracleTwo.address, oracleThree.address, oracleFour.address], + ] + } + ), + ).to.be.reverted + }) + it('should revert if an oracle is address zero', async () => { await expect( deploy( @@ -109,7 +127,6 @@ describe('SubgraphAvailabilityManager', () => { args: [ governor.address, rewardsManager.address, - maxOracles, executionThreshold, voteTimeLimit, [ @@ -135,7 +152,6 @@ describe('SubgraphAvailabilityManager', () => { args: [ AddressZero, rewardsManager.address, - maxOracles, executionThreshold, voteTimeLimit, oracles, @@ -155,7 +171,6 @@ describe('SubgraphAvailabilityManager', () => { args: [ governor.address, AddressZero, - maxOracles, executionThreshold, voteTimeLimit, oracles, @@ -164,6 +179,25 @@ describe('SubgraphAvailabilityManager', () => { ), ).to.be.revertedWith('SAM: rewardsManager must be set') }) + + it('should revert if executionThreshold is too low', async () => { + await expect( + deploy( + DeployType.Deploy, + governor, + { + name: "SubgraphAvailabilityManager", + args: [ + governor.address, + rewardsManager.address, + '2', + voteTimeLimit, + oracles, + ] + } + ), + ).to.be.revertedWith('SAM: executionThreshold too low') + }) }) describe('initializer', () => { @@ -171,8 +205,7 @@ describe('SubgraphAvailabilityManager', () => { expect(await subgraphAvailabilityManager.governor()).to.be.equal(governor.address) }) - it('should init maxOracles and executionThreshold', async () => { - expect(await subgraphAvailabilityManager.maxOracles()).to.be.equal(maxOracles) + it('should init executionThreshold', async () => { expect(await subgraphAvailabilityManager.executionThreshold()).to.be.equal(executionThreshold) }) From ad4baec4b6a203a5fb96d2fac9f53b2b08f1c40b Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Mon, 11 Dec 2023 10:07:29 -0300 Subject: [PATCH 09/31] fix: change constant variable name to match style guide --- .../rewards/SubgraphAvailabilityManager.sol | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 43ce865ee..ae1a0fa36 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -21,7 +21,7 @@ contract SubgraphAvailabilityManager is Governed { // -- Immutable -- /// @notice Number of oracles - uint256 public constant numOracles = 5; + 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; @@ -39,7 +39,7 @@ contract SubgraphAvailabilityManager is Governed { uint256 public voteTimeLimit; /// @notice Array of oracle addresses - address[numOracles] public oracles; + address[NUM_ORACLES] public oracles; /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last deny vote /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp @@ -74,7 +74,7 @@ contract SubgraphAvailabilityManager is Governed { // -- Modifiers -- modifier onlyOracle(uint256 _oracleIndex) { - require(_oracleIndex < numOracles, "SAM: index out of bounds"); + require(_oracleIndex < NUM_ORACLES, "SAM: index out of bounds"); require(msg.sender == oracles[_oracleIndex], "SAM: caller must be oracle"); _; } @@ -93,11 +93,14 @@ contract SubgraphAvailabilityManager is Governed { address _rewardsManager, uint256 _executionThreshold, uint256 _voteTimeLimit, - address[numOracles] memory _oracles + 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 >= numOracles.div(2).add(1), "SAM: executionThreshold too low"); + 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++) { @@ -130,7 +133,7 @@ contract SubgraphAvailabilityManager is Governed { * @param _oracle Address of the oracle */ function setOracle(uint256 _index, address _oracle) external onlyGovernor { - require(_index < numOracles, "SAM: index out of bounds"); + require(_index < NUM_ORACLES, "SAM: index out of bounds"); require(_oracle != address(0), "SAM: oracle cannot be address zero"); oracles[_index] = _oracle; @@ -221,7 +224,7 @@ contract SubgraphAvailabilityManager is Governed { ? lastDenyVote[currentNonce][_subgraphDeploymentID] : lastAllowVote[currentNonce][_subgraphDeploymentID]; - for (uint256 i = 0; i < numOracles; i++) { + for (uint256 i = 0; i < NUM_ORACLES; i++) { // check if vote is within the vote time limit if (lastVoteForSubgraph[i] > voteTimeValidity) { votes++; From a67ad20b4bd4936d208f671676f6e013d9eec33e Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 21 Dec 2023 18:51:53 -0300 Subject: [PATCH 10/31] chore: deploy to staging --- packages/contracts/addresses-staging.json | 19 ++ .../config/graph.arbitrum-hardhat.yml | 10 + .../config/graph.arbitrum-localhost.yml | 10 + packages/contracts/config/graph.hardhat.yml | 10 + packages/contracts/config/graph.localhost.yml | 10 + packages/contracts/config/graph.sepolia.yml | 10 + .../test/rewards/subgraphAvailability.test.ts | 223 ++++++------------ .../network/deployment/contracts/list.ts | 1 + 8 files changed, 138 insertions(+), 155 deletions(-) diff --git a/packages/contracts/addresses-staging.json b/packages/contracts/addresses-staging.json index e9b0c344f..1e5a93c89 100644 --- a/packages/contracts/addresses-staging.json +++ b/packages/contracts/addresses-staging.json @@ -424,6 +424,25 @@ "runtimeCodeHash": "0x3cb8a7336234c7826c8f5a437ad2c6615c2e4b4201096a49cb6876264ac47182", "txHash": "0xf0765b7fe12702a7de3644bb1bcd6549bce1b8b738f2136e94bf39d10521b3e3" } + }, + "SubgraphAvailabilityManager": { + "address": "0x8fa39178Bdf76aca23635FF343BeeED7aA1D45ec", + "constructorArgs": [ + "0x559081D91F5Ff43dfE51A07C216F8E6893805B35", + "0x9a86322dEa5136C74ee6d1b06F4Ab9A6bB2724E0", + "5", + "300", + [ + "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", + "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", + "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", + "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", + "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D" + ] + ], + "creationCodeHash": "0x70ff001c11d5120782503f0d129f388d8800571f7e9dfbc81bbff048e3a58444", + "runtimeCodeHash": "0x237aca4c61c6d9b391322c0b24e2d26d9d540787507c6d30c474c86866b15dea", + "txHash": "0xf68af53d379c9ffbfa4794d5226fcd92e95bbce7017f5eefaacd147d0f5f40bc" } } } diff --git a/packages/contracts/config/graph.arbitrum-hardhat.yml b/packages/contracts/config/graph.arbitrum-hardhat.yml index bdc7a87a9..6ae4d50d6 100644 --- a/packages/contracts/config/graph.arbitrum-hardhat.yml +++ b/packages/contracts/config/graph.arbitrum-hardhat.yml @@ -150,3 +150,13 @@ contracts: - fn: "syncAllContracts" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian + SubgraphAvailabilityManager: + init: + governor: "${{Env.deployer}}" + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] + calls: + - fn: "transferOwnership" + owner: *governor diff --git a/packages/contracts/config/graph.arbitrum-localhost.yml b/packages/contracts/config/graph.arbitrum-localhost.yml index d1364b581..42fbdebf2 100644 --- a/packages/contracts/config/graph.arbitrum-localhost.yml +++ b/packages/contracts/config/graph.arbitrum-localhost.yml @@ -150,3 +150,13 @@ contracts: - fn: "syncAllContracts" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian + SubgraphAvailabilityManager: + init: + governor: "${{Env.deployer}}" + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] + calls: + - fn: "transferOwnership" + owner: *governor diff --git a/packages/contracts/config/graph.hardhat.yml b/packages/contracts/config/graph.hardhat.yml index 59068752a..9f7cc6f5d 100644 --- a/packages/contracts/config/graph.hardhat.yml +++ b/packages/contracts/config/graph.hardhat.yml @@ -159,3 +159,13 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + SubgraphAvailabilityManager: + init: + governor: "${{Env.deployer}}" + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] + calls: + - fn: "transferOwnership" + owner: *governor diff --git a/packages/contracts/config/graph.localhost.yml b/packages/contracts/config/graph.localhost.yml index 423052b98..87e199325 100644 --- a/packages/contracts/config/graph.localhost.yml +++ b/packages/contracts/config/graph.localhost.yml @@ -159,3 +159,13 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + SubgraphAvailabilityManager: + init: + governor: "${{Env.deployer}}" + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] + calls: + - fn: "transferOwnership" + owner: *governor diff --git a/packages/contracts/config/graph.sepolia.yml b/packages/contracts/config/graph.sepolia.yml index d830cd9d9..eee30807c 100644 --- a/packages/contracts/config/graph.sepolia.yml +++ b/packages/contracts/config/graph.sepolia.yml @@ -159,3 +159,13 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + SubgraphAvailabilityManager: + init: + governor: "${{Env.deployer}}" + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] + calls: + - fn: "transferOwnership" + owner: *governor diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 2ced18463..76276ac16 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -16,7 +16,7 @@ import { randomAddress, randomHexBytes, deploy, - DeployType + DeployType, } from '@graphprotocol/sdk' const { AddressZero } = constants @@ -49,7 +49,7 @@ describe('SubgraphAvailabilityManager', () => { const subgraphDeploymentID3 = randomHexBytes() before(async () => { - ;[me, oracleOne, oracleTwo, oracleThree, oracleFour, oracleFive, newOracle] = + ;[me, oracleOne, oracleTwo, oracleThree, oracleFour, oracleFive, newOracle] = await graph.getTestAccounts() ;({ governor } = await graph.getNamedAccounts()) @@ -64,20 +64,11 @@ describe('SubgraphAvailabilityManager', () => { fixture = new NetworkFixture(graph.provider) contracts = await fixture.load(governor) rewardsManager = contracts.RewardsManager as IRewardsManager - const deployResult = (await deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - governor.address, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - oracles, - ] - } - )) + console.log('Llega hasta aca?') + const deployResult = await deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [governor.address, rewardsManager.address, executionThreshold, voteTimeLimit, oracles], + }) subgraphAvailabilityManager = deployResult.contract as SubgraphAvailabilityManager await rewardsManager @@ -100,102 +91,64 @@ describe('SubgraphAvailabilityManager', () => { it('should revert if oracles array is less than 5', async () => { await expect( - deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - governor.address, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - [oracleOne.address, oracleTwo.address, oracleThree.address, oracleFour.address], - ] - } - ), + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [ + governor.address, + rewardsManager.address, + executionThreshold, + voteTimeLimit, + [oracleOne.address, oracleTwo.address, oracleThree.address, oracleFour.address], + ], + }), ).to.be.reverted }) it('should revert if an oracle is address zero', async () => { await expect( - deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - governor.address, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - [ - AddressZero, - oracleTwo.address, - oracleThree.address, - oracleFour.address, - oracleFive.address, - ], - ] - } - ), + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [ + governor.address, + rewardsManager.address, + executionThreshold, + voteTimeLimit, + [ + AddressZero, + oracleTwo.address, + oracleThree.address, + oracleFour.address, + oracleFive.address, + ], + ], + }), ).to.be.revertedWith('SAM: oracle cannot be address zero') }) it('should revert if governor is address zero', async () => { await expect( - deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - AddressZero, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - oracles, - ] - } - ), + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [AddressZero, rewardsManager.address, executionThreshold, voteTimeLimit, oracles], + }), ).to.be.revertedWith('SAM: governor must be set') }) it('should revert if rewardsManager is address zero', async () => { await expect( - deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - governor.address, - AddressZero, - executionThreshold, - voteTimeLimit, - oracles, - ] - } - ), + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [governor.address, AddressZero, executionThreshold, voteTimeLimit, oracles], + }), ).to.be.revertedWith('SAM: rewardsManager must be set') }) it('should revert if executionThreshold is too low', async () => { await expect( - deploy( - DeployType.Deploy, - governor, - { - name: "SubgraphAvailabilityManager", - args: [ - governor.address, - rewardsManager.address, - '2', - voteTimeLimit, - oracles, - ] - } - ), + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [governor.address, rewardsManager.address, '2', voteTimeLimit, oracles], + }), ).to.be.revertedWith('SAM: executionThreshold too low') }) }) @@ -223,9 +176,7 @@ describe('SubgraphAvailabilityManager', () => { describe('set vote limit', async () => { it('sets voteTimeLimit successfully', async () => { const newVoteTimeLimit = 10 - await expect( - subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(newVoteTimeLimit), - ) + await expect(subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(newVoteTimeLimit)) .emit(subgraphAvailabilityManager, 'VoteTimeLimitSet') .withArgs(newVoteTimeLimit) expect(await subgraphAvailabilityManager.voteTimeLimit()).to.be.equal(newVoteTimeLimit) @@ -250,9 +201,9 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if not called by governor', async () => { const oracle = randomAddress() - await expect( - subgraphAvailabilityManager.connect(me).setOracle(0, oracle), - ).to.be.revertedWith('Only Governor can call') + await expect(subgraphAvailabilityManager.connect(me).setOracle(0, oracle)).to.be.revertedWith( + 'Only Governor can call', + ) }) it('should fail if setting oracle to address zero', async () => { @@ -291,18 +242,14 @@ describe('SubgraphAvailabilityManager', () => { it('should fail if index is out of bounds', async () => { const denied = true await expect( - subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 5), + subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 5), ).to.be.revertedWith('SAM: index out of bounds') }) it('should fail if oracle used an incorrect index', async () => { const denied = true await expect( - subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 1), + subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 1), ).to.be.revertedWith('SAM: caller must be oracle') }) @@ -318,12 +265,8 @@ describe('SubgraphAvailabilityManager', () => { it('should be denied or allowed if majority of oracles have voted', async () => { // 3/5 oracles vote denied = true let denied = true - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo) - .vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) const tx = await subgraphAvailabilityManager .connect(oracleThree) .vote(subgraphDeploymentID1, denied, 2) @@ -336,15 +279,9 @@ describe('SubgraphAvailabilityManager', () => { // 3/5 oracles vote denied = false denied = false - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo) - .vote(subgraphDeploymentID1, denied, 1) - await subgraphAvailabilityManager - .connect(oracleThree) - .vote(subgraphDeploymentID1, denied, 2) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) // check that subgraph is not denied expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false @@ -352,15 +289,9 @@ describe('SubgraphAvailabilityManager', () => { it('should not be denied if the same oracle votes three times', async () => { const denied = true - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false }) @@ -368,12 +299,8 @@ describe('SubgraphAvailabilityManager', () => { it('should not be denied if voteTimeLimit has passed and not enough oracles have voted', async () => { // 2/3 oracles vote denied = true const denied = true - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo) - .vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) // increase time by 6 seconds await ethers.provider.send('evm_increaseTime', [6]) @@ -393,9 +320,7 @@ describe('SubgraphAvailabilityManager', () => { it('votes many successfully', async () => { const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] const denied = [true, false, true] - const tx = await subgraphAvailabilityManager - .connect(oracleOne) - .voteMany(subgraphs, denied, 0) + const tx = await subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 0) const timestamp = (await ethers.provider.getBlock('latest')).timestamp await expect(tx) .to.emit(subgraphAvailabilityManager, 'OracleVote') @@ -464,20 +389,14 @@ describe('SubgraphAvailabilityManager', () => { it('should refresh votes if an oracle is replaced', async () => { const denied = true // 2/3 oracles vote denied = true - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo) - .vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) // replace oracleOne with a new oracle await subgraphAvailabilityManager.connect(governor).setOracle(2, newOracle.address) // new oracle votes denied = true - await subgraphAvailabilityManager - .connect(newOracle) - .vote(subgraphDeploymentID1, denied, 2) + await subgraphAvailabilityManager.connect(newOracle).vote(subgraphDeploymentID1, denied, 2) // subgraph shouldn't be denied because setting a new oracle should refresh the votes expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false @@ -486,20 +405,14 @@ describe('SubgraphAvailabilityManager', () => { it('should refresh votes if voteTimeLimit changes', async () => { const denied = true // 2/3 oracles vote denied = true - await subgraphAvailabilityManager - .connect(oracleOne) - .vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager - .connect(oracleTwo) - .vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) // change voteTimeLimit to 10 seconds await subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(10) // last oracle votes denied = true - await subgraphAvailabilityManager - .connect(oracleThree) - .vote(subgraphDeploymentID1, denied, 2) + await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) // subgraph shouldn't be denied because voteTimeLimit should refresh the votes expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false diff --git a/packages/sdk/src/deployments/network/deployment/contracts/list.ts b/packages/sdk/src/deployments/network/deployment/contracts/list.ts index 11966e06e..11fdccb6f 100644 --- a/packages/sdk/src/deployments/network/deployment/contracts/list.ts +++ b/packages/sdk/src/deployments/network/deployment/contracts/list.ts @@ -13,6 +13,7 @@ export const GraphNetworkSharedContractNameList = [ 'RewardsManager', 'DisputeManager', 'AllocationExchange', + 'SubgraphAvailabilityManager', ] as const export const GraphNetworkOptionalContractNameList = [ 'IENS', From ddbcc86cd89e09ddee9eb542a12a83520cea2495 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 21 Dec 2023 18:56:44 -0300 Subject: [PATCH 11/31] fix: emit oracle set event on init --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index ae1a0fa36..b112a171c 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -104,7 +104,10 @@ contract SubgraphAvailabilityManager is Governed { // Oracles should not be address zero for (uint256 i = 0; i < _oracles.length; i++) { - require(_oracles[i] != address(0), "SAM: oracle cannot be address zero"); + address oracle = _oracles[i]; + require(oracle != address(0), "SAM: oracle cannot be address zero"); + oracles[i] = oracle; + emit OracleSet(i, oracle); } Governed._initialize(_governor); @@ -112,7 +115,6 @@ contract SubgraphAvailabilityManager is Governed { executionThreshold = _executionThreshold; voteTimeLimit = _voteTimeLimit; - oracles = _oracles; } // -- Functions -- From 26f1c7a4494e63ebec2bb008072d656486aa6d7d Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Tue, 9 Jan 2024 17:31:14 -0300 Subject: [PATCH 12/31] fix: change mapping for an array to make storage cheaper --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index b112a171c..759cf73e0 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -43,11 +43,11 @@ contract SubgraphAvailabilityManager is Governed { /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last deny vote /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp - mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastDenyVote; + mapping(uint256 => mapping(bytes32 => uint256[NUM_ORACLES])) public lastDenyVote; /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last allow vote /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp - mapping(uint256 => mapping(bytes32 => mapping(uint256 => uint256))) public lastAllowVote; + mapping(uint256 => mapping(bytes32 => uint256[NUM_ORACLES])) public lastAllowVote; // -- Events -- @@ -195,7 +195,7 @@ contract SubgraphAvailabilityManager is Governed { uint256 timestamp = block.timestamp; // corresponding votes based on _deny for a subgraph deployment - mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny + uint256[NUM_ORACLES] storage lastVoteForSubgraph = _deny ? lastDenyVote[currentNonce][_subgraphDeploymentID] : lastAllowVote[currentNonce][_subgraphDeploymentID]; lastVoteForSubgraph[_oracleIndex] = timestamp; @@ -222,7 +222,7 @@ contract SubgraphAvailabilityManager is Governed { uint256 voteTimeValidity = block.timestamp - voteTimeLimit; // corresponding votes based on _deny for a subgraph deployment - mapping(uint256 => uint256) storage lastVoteForSubgraph = _deny + uint256[NUM_ORACLES] storage lastVoteForSubgraph = _deny ? lastDenyVote[currentNonce][_subgraphDeploymentID] : lastAllowVote[currentNonce][_subgraphDeploymentID]; From eeeb31498472140725710614a164264ce27b252d Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 10 Jan 2024 17:17:12 -0300 Subject: [PATCH 13/31] fix: update comments --- .../rewards/SubgraphAvailabilityManager.sol | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 759cf73e0..aac500736 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -12,8 +12,9 @@ import { IRewardsManager } from "../rewards/IRewardsManager.sol"; * @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 number of oracles and the execution threshold are set at deployment time. - * Only governance can set the oracles. + * 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; @@ -41,12 +42,12 @@ contract SubgraphAvailabilityManager is Governed { /// @notice Array of oracle addresses address[NUM_ORACLES] public oracles; - /// @notice Mapping of current nonce to subgraph deployment ID to oracle index to timestamp of last deny vote - /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp + /// @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 oracle index to timestamp of last allow vote - /// currentNonce => subgraphDeploymentId => oracleIndex => timestamp + /// @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 -- @@ -87,6 +88,7 @@ contract SubgraphAvailabilityManager is Governed { * @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, From 1ad793545c644be4798b98f8b4557c3253e0d89f Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 10 Jan 2024 17:40:36 -0300 Subject: [PATCH 14/31] fix: make currentNonce public --- .../contracts/contracts/rewards/SubgraphAvailabilityManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index aac500736..34b0ce79e 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -34,7 +34,7 @@ contract SubgraphAvailabilityManager is Governed { /// @dev Nonce for generating votes on subgraph deployment IDs. /// Increased whenever oracles or voteTimeLimit change, to invalidate old votes. - uint256 private currentNonce; + uint256 public currentNonce; /// @notice Time limit for a vote to be valid uint256 public voteTimeLimit; From fbdf6af5e6132a6c838b885ad263ec3c06ed280b Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 1 Feb 2024 15:08:48 -0300 Subject: [PATCH 15/31] fix: added input validation for executionThreshold (OZ L-01) --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 1 + .../contracts/test/rewards/subgraphAvailability.test.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 34b0ce79e..738f207df 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -103,6 +103,7 @@ contract SubgraphAvailabilityManager is Governed { _executionThreshold >= NUM_ORACLES.div(2).add(1), "SAM: executionThreshold too low" ); + require(_executionThreshold <= NUM_ORACLES, "SAM: executionThreshold too high"); // Oracles should not be address zero for (uint256 i = 0; i < _oracles.length; i++) { diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 76276ac16..0c454dd87 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -151,6 +151,15 @@ describe('SubgraphAvailabilityManager', () => { }), ).to.be.revertedWith('SAM: executionThreshold too low') }) + + it('should revert if executionThreshold is too high', async () => { + await expect( + deploy(DeployType.Deploy, governor, { + name: 'SubgraphAvailabilityManager', + args: [governor.address, rewardsManager.address, '6', voteTimeLimit, oracles], + }), + ).to.be.revertedWith('SAM: executionThreshold too high') + }) }) describe('initializer', () => { From 94e6d05ec5cfdea50cd3899ce1797e2e0ff01820 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 1 Feb 2024 15:40:01 -0300 Subject: [PATCH 16/31] docs: added missing docstrings for events (OZ L-03) --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 738f207df..4323ae71c 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -54,16 +54,23 @@ contract SubgraphAvailabilityManager is Governed { /** * @dev Emitted when an oracle is set + * @param index Index of the oracle + * @param oracle Address of the oracle */ event OracleSet(uint256 indexed index, address indexed oracle); /** * @dev Emitted when the vote time limit is set + * @param voteTimeLimit Vote time limit in seconds */ event VoteTimeLimitSet(uint256 voteTimeLimit); /** * @dev Emitted when an oracle votes to deny or allow a subgraph + * @param subgraphDeploymentID Subgraph deployment ID + * @param deny True to deny, false to allow + * @param oracleIndex Index of the oracle voting + * @param timestamp Timestamp of the vote */ event OracleVote( bytes32 indexed subgraphDeploymentID, From 9f9db2e2fd88e1a4ab964f375b73138ac6cc7ecf Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 1 Feb 2024 17:17:09 -0300 Subject: [PATCH 17/31] fix: change OracleVote event order (OZ N-01) --- .../contracts/rewards/SubgraphAvailabilityManager.sol | 4 ++-- .../contracts/test/rewards/subgraphAvailability.test.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 4323ae71c..70539b3c6 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -210,12 +210,12 @@ contract SubgraphAvailabilityManager is Governed { : lastAllowVote[currentNonce][_subgraphDeploymentID]; lastVoteForSubgraph[_oracleIndex] = timestamp; + emit OracleVote(_subgraphDeploymentID, _deny, _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); } /** diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 0c454dd87..0dbbd1abb 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -283,6 +283,12 @@ describe('SubgraphAvailabilityManager', () => { .to.emit(rewardsManager, 'RewardsDenylistUpdated') .withArgs(subgraphDeploymentID1, tx.blockNumber) + // check events order + const receipt = await tx.wait() + expect(receipt.events[0].event).to.be.equal('OracleVote') + const rewardsManangerEvent = rewardsManager.interface.parseLog(receipt.logs[1]).name + expect(rewardsManangerEvent).to.be.equal('RewardsDenylistUpdated') + // check that subgraph is denied expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true From f2aa2fa34fa91c8dadbbc632773a5a2706a3ef15 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 1 Feb 2024 17:25:35 -0300 Subject: [PATCH 18/31] fix: gas optimization (OZ N-02) --- .../rewards/SubgraphAvailabilityManager.sol | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 70539b3c6..349415283 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -2,8 +2,6 @@ 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"; @@ -17,8 +15,6 @@ import { IRewardsManager } from "../rewards/IRewardsManager.sol"; * Governor can transfer ownership to a new governor. */ contract SubgraphAvailabilityManager is Governed { - using SafeMathUpgradeable for uint256; - // -- Immutable -- /// @notice Number of oracles @@ -106,14 +102,11 @@ contract SubgraphAvailabilityManager is Governed { ) { 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" - ); + require(_executionThreshold >= (NUM_ORACLES / 2) + 1, "SAM: executionThreshold too low"); require(_executionThreshold <= NUM_ORACLES, "SAM: executionThreshold too high"); // Oracles should not be address zero - for (uint256 i = 0; i < _oracles.length; i++) { + for (uint256 i; i < _oracles.length; i++) { address oracle = _oracles[i]; require(oracle != address(0), "SAM: oracle cannot be address zero"); oracles[i] = oracle; @@ -183,7 +176,7 @@ contract SubgraphAvailabilityManager is Governed { uint256 _oracleIndex ) external onlyOracle(_oracleIndex) { require(_subgraphDeploymentID.length == _deny.length, "!length"); - for (uint256 i = 0; i < _subgraphDeploymentID.length; i++) { + for (uint256 i; i < _subgraphDeploymentID.length; i++) { _vote(_subgraphDeploymentID[i], _deny[i], _oracleIndex); } } @@ -226,7 +219,7 @@ contract SubgraphAvailabilityManager is Governed { * @return True if execution threshold is reached */ function checkVotes(bytes32 _subgraphDeploymentID, bool _deny) public view returns (bool) { - uint256 votes = 0; + uint256 votes; // timeframe for a vote to be valid uint256 voteTimeValidity = block.timestamp - voteTimeLimit; @@ -236,7 +229,7 @@ contract SubgraphAvailabilityManager is Governed { ? lastDenyVote[currentNonce][_subgraphDeploymentID] : lastAllowVote[currentNonce][_subgraphDeploymentID]; - for (uint256 i = 0; i < NUM_ORACLES; i++) { + for (uint256 i; i < NUM_ORACLES; i++) { // check if vote is within the vote time limit if (lastVoteForSubgraph[i] > voteTimeValidity) { votes++; From a52066c325bf0d0dd37d162918f3ec9587a014aa Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 2 Feb 2024 12:09:40 -0300 Subject: [PATCH 19/31] fix: clear vote for earlier rounds (OZ L-02) --- .../rewards/SubgraphAvailabilityManager.sol | 6 +++++ .../test/rewards/subgraphAvailability.test.ts | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 349415283..9e562b6fe 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -203,6 +203,12 @@ contract SubgraphAvailabilityManager is Governed { : lastAllowVote[currentNonce][_subgraphDeploymentID]; lastVoteForSubgraph[_oracleIndex] = timestamp; + // clear opposite vote for a subgraph deployment if it exists + uint256[NUM_ORACLES] storage oppositeVoteForSubgraph = _deny + ? lastAllowVote[currentNonce][_subgraphDeploymentID] + : lastDenyVote[currentNonce][_subgraphDeploymentID]; + oppositeVoteForSubgraph[_oracleIndex] = 0; + emit OracleVote(_subgraphDeploymentID, _deny, _oracleIndex, timestamp); // check if execution threshold is reached, if it is call the RewardsManager diff --git a/packages/contracts/test/rewards/subgraphAvailability.test.ts b/packages/contracts/test/rewards/subgraphAvailability.test.ts index 0dbbd1abb..1f04a3bbc 100644 --- a/packages/contracts/test/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/rewards/subgraphAvailability.test.ts @@ -329,6 +329,31 @@ describe('SubgraphAvailabilityManager', () => { // previous votes are no longer valid expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false }) + + it('clears opposite vote when voting', async () => { + const denied = true + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) + await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) + await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) + + // 3/5 oracles vote denied = true so subgraph is denied + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true + + // oracleOne changes its vote to denied = false + await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, false, 0) + + // last deny vote should be 0 for oracleOne + expect( + await subgraphAvailabilityManager.lastDenyVote(0, subgraphDeploymentID1, 0), + ).to.be.equal(0) + + // executionThreshold isn't met now since oracleOne changed its vote + expect(await subgraphAvailabilityManager.checkVotes(subgraphDeploymentID1, denied)).to.be + .false + + // subgraph is still denied in rewards manager because only one oracle changed its vote + expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true + }) }) describe('vote many', async () => { From 1ce46f0caede4430c30ab5d597830ddf4a78e22b Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 2 Feb 2024 12:32:14 -0300 Subject: [PATCH 20/31] fix: change to if/else --- .../rewards/SubgraphAvailabilityManager.sol | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 9e562b6fe..8248f81c9 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -197,17 +197,15 @@ contract SubgraphAvailabilityManager is Governed { ) 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; - - // clear opposite vote for a subgraph deployment if it exists - uint256[NUM_ORACLES] storage oppositeVoteForSubgraph = _deny - ? lastAllowVote[currentNonce][_subgraphDeploymentID] - : lastDenyVote[currentNonce][_subgraphDeploymentID]; - oppositeVoteForSubgraph[_oracleIndex] = 0; + if (_deny) { + lastDenyVote[currentNonce][_subgraphDeploymentID][_oracleIndex] = timestamp; + // clear opposite vote for a subgraph deployment if it exists + lastAllowVote[currentNonce][_subgraphDeploymentID][_oracleIndex] = 0; + } else { + lastAllowVote[currentNonce][_subgraphDeploymentID][_oracleIndex] = timestamp; + // clear opposite vote for a subgraph deployment if it exists + lastDenyVote[currentNonce][_subgraphDeploymentID][_oracleIndex] = 0; + } emit OracleVote(_subgraphDeploymentID, _deny, _oracleIndex, timestamp); From ef35364d332f88fa6cfd646c30a448efaeeede67 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 2 Feb 2024 13:28:54 -0300 Subject: [PATCH 21/31] fix: remove setDeniedMany since it's unused (OZ N-03) --- .../contracts/rewards/IRewardsManager.sol | 3 --- .../contracts/rewards/RewardsManager.sol | 17 ----------------- packages/contracts/test/rewards/rewards.test.ts | 15 --------------- 3 files changed, 35 deletions(-) diff --git a/packages/contracts/contracts/rewards/IRewardsManager.sol b/packages/contracts/contracts/rewards/IRewardsManager.sol index 87d3c2455..b70c13d64 100644 --- a/packages/contracts/contracts/rewards/IRewardsManager.sol +++ b/packages/contracts/contracts/rewards/IRewardsManager.sol @@ -25,9 +25,6 @@ interface IRewardsManager { function setDenied(bytes32 _subgraphDeploymentID, bool _deny) external; - function setDeniedMany(bytes32[] calldata _subgraphDeploymentID, bool[] calldata _deny) - external; - function isDenied(bytes32 _subgraphDeploymentID) external view returns (bool); // -- Getters -- diff --git a/packages/contracts/contracts/rewards/RewardsManager.sol b/packages/contracts/contracts/rewards/RewardsManager.sol index f7b7f6384..2c405e76d 100644 --- a/packages/contracts/contracts/rewards/RewardsManager.sol +++ b/packages/contracts/contracts/rewards/RewardsManager.sol @@ -144,23 +144,6 @@ contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsMa _setDenied(_subgraphDeploymentID, _deny); } - /** - * @dev Denies to claim rewards for multiple subgraph. - * NOTE: Can only be called by the subgraph availability oracle - * @param _subgraphDeploymentID Array of subgraph deployment ID - * @param _deny Array of denied status for claiming rewards for each subgraph - */ - function setDeniedMany(bytes32[] calldata _subgraphDeploymentID, bool[] calldata _deny) - external - override - onlySubgraphAvailabilityOracle - { - require(_subgraphDeploymentID.length == _deny.length, "!length"); - for (uint256 i = 0; i < _subgraphDeploymentID.length; i++) { - _setDenied(_subgraphDeploymentID[i], _deny[i]); - } - } - /** * @dev Internal: Denies to claim rewards for a subgraph. * @param _subgraphDeploymentID Subgraph deployment ID diff --git a/packages/contracts/test/rewards/rewards.test.ts b/packages/contracts/test/rewards/rewards.test.ts index 5c4438b78..861249342 100644 --- a/packages/contracts/test/rewards/rewards.test.ts +++ b/packages/contracts/test/rewards/rewards.test.ts @@ -213,21 +213,6 @@ describe('Rewards', () => { .withArgs(subgraphDeploymentID1, blockNum + 1) expect(await rewardsManager.isDenied(subgraphDeploymentID1)).eq(true) }) - - it('reject deny subgraph w/ many if not the oracle', async function () { - const deniedSubgraphs = [subgraphDeploymentID1, subgraphDeploymentID2] - const tx = rewardsManager.connect(oracle).setDeniedMany(deniedSubgraphs, [true, true]) - await expect(tx).revertedWith('Caller must be the subgraph availability oracle') - }) - - it('should deny subgraph w/ many', async function () { - await rewardsManager.connect(governor).setSubgraphAvailabilityOracle(oracle.address) - - const deniedSubgraphs = [subgraphDeploymentID1, subgraphDeploymentID2] - await rewardsManager.connect(oracle).setDeniedMany(deniedSubgraphs, [true, true]) - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).eq(true) - expect(await rewardsManager.isDenied(subgraphDeploymentID2)).eq(true) - }) }) }) From bcf69579662fcf3e2fee87cf3a0aa5ee4196cf6d Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 13 Mar 2024 10:22:51 -0300 Subject: [PATCH 22/31] fix: indentation --- .../rewards/SubgraphAvailabilityManager.sol | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol index 8248f81c9..a8e3d0e30 100644 --- a/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol +++ b/packages/contracts/contracts/rewards/SubgraphAvailabilityManager.sol @@ -68,12 +68,7 @@ contract SubgraphAvailabilityManager is Governed { * @param oracleIndex Index of the oracle voting * @param timestamp Timestamp of the vote */ - event OracleVote( - bytes32 indexed subgraphDeploymentID, - bool deny, - uint256 indexed oracleIndex, - uint256 timestamp - ); + event OracleVote(bytes32 indexed subgraphDeploymentID, bool deny, uint256 indexed oracleIndex, uint256 timestamp); // -- Modifiers -- @@ -155,11 +150,7 @@ contract SubgraphAvailabilityManager is Governed { * @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) { + function vote(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) external onlyOracle(_oracleIndex) { _vote(_subgraphDeploymentID, _deny, _oracleIndex); } @@ -190,11 +181,7 @@ contract SubgraphAvailabilityManager is Governed { * @param _deny True to deny, false to allow * @param _oracleIndex Index of the oracle voting */ - function _vote( - bytes32 _subgraphDeploymentID, - bool _deny, - uint256 _oracleIndex - ) private { + function _vote(bytes32 _subgraphDeploymentID, bool _deny, uint256 _oracleIndex) private { uint256 timestamp = block.timestamp; if (_deny) { From 4c01fc495949d340fd8cf019ad32d4ba8ce5e0fb Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Tue, 12 Mar 2024 19:30:11 -0300 Subject: [PATCH 23/31] chore: deploy to arbitrum-sepolia and remove sepolia --- packages/contracts/addresses-staging.json | 38 +++++++++---------- .../config/graph.arbitrum-hardhat.yml | 16 ++++---- .../config/graph.arbitrum-localhost.yml | 16 ++++---- .../config/graph.arbitrum-sepolia.yml | 16 +++++++- packages/contracts/config/graph.hardhat.yml | 10 ----- packages/contracts/config/graph.localhost.yml | 10 ----- packages/contracts/config/graph.sepolia.yml | 10 ----- .../deployment/config/rewardsManager.test.ts | 11 +----- .../network/deployment/contracts/list.ts | 2 +- .../network/deployment/contracts/load.ts | 2 + packages/sdk/src/gre/accounts.ts | 1 - 11 files changed, 56 insertions(+), 76 deletions(-) diff --git a/packages/contracts/addresses-staging.json b/packages/contracts/addresses-staging.json index e3708b172..26ec5d5da 100644 --- a/packages/contracts/addresses-staging.json +++ b/packages/contracts/addresses-staging.json @@ -203,6 +203,25 @@ "runtimeCodeHash": "0xd853da2001c213dd00d674114c254f18a7b5a36aabc5452fb492b9b4faf2faa9", "txHash": "0xf868871475dc711c5c03818b16a922f740bc2fc314d24586df2255161a907422" } + }, + "SubgraphAvailabilityManager": { + "address": "0x9bDC3264596850E7F5d141A8D898dFA7001355CC", + "constructorArgs": [ + "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", + "0x913A282574A0AD91EecAdCbaf2800fDf516E27B1", + "5", + "300", + [ + "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A", + "0x633259dB4A33DB664d4C3Fa57BEc45E5030131E2", + "0x9FC436A69E290227b99A9CE0468a1b9D73068E80", + "0xC9b90ab0B9cA63fDBfB46f66BE813ea94561614d", + "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" + ] + ], + "creationCodeHash": "0x473bd948303724e315a33afd256ab83decae71a422b904a1d1592e113792bada", + "runtimeCodeHash": "0x4ab7cd273062d8e7bc85ba3a1114f1c10f7c0c098caf179e09412bf92d664159", + "txHash": "0x44bfe0c2e857295ec91649bff53a82ca2d9ad9d21974d01b3c56f55ebefde979" } }, "11155111": { @@ -424,25 +443,6 @@ "runtimeCodeHash": "0x3cb8a7336234c7826c8f5a437ad2c6615c2e4b4201096a49cb6876264ac47182", "txHash": "0xf0765b7fe12702a7de3644bb1bcd6549bce1b8b738f2136e94bf39d10521b3e3" } - }, - "SubgraphAvailabilityManager": { - "address": "0x8fa39178Bdf76aca23635FF343BeeED7aA1D45ec", - "constructorArgs": [ - "0x559081D91F5Ff43dfE51A07C216F8E6893805B35", - "0x9a86322dEa5136C74ee6d1b06F4Ab9A6bB2724E0", - "5", - "300", - [ - "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", - "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", - "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", - "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D", - "0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D" - ] - ], - "creationCodeHash": "0x70ff001c11d5120782503f0d129f388d8800571f7e9dfbc81bbff048e3a58444", - "runtimeCodeHash": "0x237aca4c61c6d9b391322c0b24e2d26d9d540787507c6d30c474c86866b15dea", - "txHash": "0xf68af53d379c9ffbfa4794d5226fcd92e95bbce7017f5eefaacd147d0f5f40bc" } } } diff --git a/packages/contracts/config/graph.arbitrum-hardhat.yml b/packages/contracts/config/graph.arbitrum-hardhat.yml index 91a86d2c2..ec4a161b1 100644 --- a/packages/contracts/config/graph.arbitrum-hardhat.yml +++ b/packages/contracts/config/graph.arbitrum-hardhat.yml @@ -2,7 +2,12 @@ general: arbitrator: &arbitrator "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0" # Arbitration Council governor: &governor "0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b" # Graph Council authority: &authority "0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d" # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle "0xd03ea8624C8C5987235048901fB614fDcA89b117" # Subgraph Availability Oracle + availabilityOracles: &availabilityOracles # Subgraph Availability Oracles + - "0xd03ea8624C8C5987235048901fB614fDcA89b117" + - "0xd03ea8624C8C5987235048901fB614fDcA89b117" + - "0xd03ea8624C8C5987235048901fB614fDcA89b117" + - "0xd03ea8624C8C5987235048901fB614fDcA89b117" + - "0xd03ea8624C8C5987235048901fB614fDcA89b117" pauseGuardian: &pauseGuardian "0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC" # Protocol pause guardian allocationExchangeOwner: &allocationExchangeOwner "0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9" # Allocation Exchange owner @@ -131,7 +136,7 @@ contracts: - fn: "setIssuancePerBlock" issuancePerBlock: "114155251141552511415" # per block increase of total supply, blocks in a year = 365*60*60*24/12 - fn: "setSubgraphAvailabilityOracle" - subgraphAvailabilityOracle: *availabilityOracle + subgraphAvailabilityOracle: "${{SubgraphAvailabilityManager.address}}" - fn: "syncAllContracts" AllocationExchange: init: @@ -151,11 +156,8 @@ contracts: pauseGuardian: *pauseGuardian SubgraphAvailabilityManager: init: - governor: "${{Env.deployer}}" + governor: *governor rewardsManager: "${{RewardsManager.address}}" executionThreshold: 5 voteTimeLimit: 300 - oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] - calls: - - fn: "transferOwnership" - owner: *governor + oracles: *availabilityOracles diff --git a/packages/contracts/config/graph.arbitrum-localhost.yml b/packages/contracts/config/graph.arbitrum-localhost.yml index 42fbdebf2..62598a07c 100644 --- a/packages/contracts/config/graph.arbitrum-localhost.yml +++ b/packages/contracts/config/graph.arbitrum-localhost.yml @@ -2,7 +2,12 @@ general: arbitrator: &arbitrator "0x4237154FE0510FdE3575656B60c68a01B9dCDdF8" # Arbitration Council governor: &governor "0x1257227a2ECA34834940110f7B5e341A5143A2c4" # Graph Council authority: &authority "0x12B8D08b116E1E3cc29eE9Cf42bB0AA8129C3215" # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle "0x7694a48065f063a767a962610C6717c59F36b445" # Subgraph Availability Oracle + availabilityOracles: &availabilityOracles # Subgraph Availability Oracles + - "0x7694a48065f063a767a962610C6717c59F36b445" + - "0x7694a48065f063a767a962610C6717c59F36b445" + - "0x7694a48065f063a767a962610C6717c59F36b445" + - "0x7694a48065f063a767a962610C6717c59F36b445" + - "0x7694a48065f063a767a962610C6717c59F36b445" pauseGuardian: &pauseGuardian "0x601060e0DC5349AA55EC73df5A58cB0FC1cD2e3C" # Protocol pause guardian allocationExchangeOwner: &allocationExchangeOwner "0xbD38F7b67a591A5cc7D642e1026E5095B819d952" # Allocation Exchange owner @@ -132,7 +137,7 @@ contracts: - fn: "setIssuancePerBlock" issuancePerBlock: "6036500000000000000" # per block increase of total supply, blocks in a year = 365*60*60*24/12 - fn: "setSubgraphAvailabilityOracle" - subgraphAvailabilityOracle: *availabilityOracle + subgraphAvailabilityOracle: "${{SubgraphAvailabilityManager.address}}" - fn: "syncAllContracts" AllocationExchange: init: @@ -152,11 +157,8 @@ contracts: pauseGuardian: *pauseGuardian SubgraphAvailabilityManager: init: - governor: "${{Env.deployer}}" + governor: *governor rewardsManager: "${{RewardsManager.address}}" executionThreshold: 5 voteTimeLimit: 300 - oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] - calls: - - fn: "transferOwnership" - owner: *governor + oracles: *availabilityOracles diff --git a/packages/contracts/config/graph.arbitrum-sepolia.yml b/packages/contracts/config/graph.arbitrum-sepolia.yml index 930feae82..537e41bf1 100644 --- a/packages/contracts/config/graph.arbitrum-sepolia.yml +++ b/packages/contracts/config/graph.arbitrum-sepolia.yml @@ -2,7 +2,12 @@ general: arbitrator: &arbitrator "0x1726A5d52e279d02ff4732eCeB2D67BFE5Add328" # EOA (TODO: update to a multisig) governor: &governor "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3" # EOA (TODO: update to a multisig) authority: &authority "0x49D4CFC037430cA9355B422bAeA7E9391e1d3215" # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle "0x5e4e823Ed094c035133eEC5Ec0d08ae1Af04e9Fa" # Subgraph Availability Oracle + availabilityOracles: &availabilityOracles # Array of Subgraph Availability Oracles + - "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A" + - "0x633259dB4A33DB664d4C3Fa57BEc45E5030131E2" + - "0x9FC436A69E290227b99A9CE0468a1b9D73068E80" + - "0xC9b90ab0B9cA63fDBfB46f66BE813ea94561614d" + - "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" pauseGuardian: &pauseGuardian "0xa0444508232dA3FA6C2f96a5f105f3f0cc0d20D7" # Protocol pause guardian allocationExchangeOwner: &allocationExchangeOwner "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3" # Allocation Exchange owner @@ -131,7 +136,7 @@ contracts: - fn: "setIssuancePerBlock" issuancePerBlock: "6036500000000000000" # per block increase of total supply, blocks in a year = 365*60*60*24/12 - fn: "setSubgraphAvailabilityOracle" - subgraphAvailabilityOracle: *availabilityOracle + subgraphAvailabilityOracle: "${{SubgraphAvailabilityManager.address}}" - fn: "syncAllContracts" AllocationExchange: init: @@ -149,3 +154,10 @@ contracts: - fn: "syncAllContracts" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian + SubgraphAvailabilityManager: + init: + governor: *governor + rewardsManager: "${{RewardsManager.address}}" + executionThreshold: 5 + voteTimeLimit: 300 + oracles: *availabilityOracles diff --git a/packages/contracts/config/graph.hardhat.yml b/packages/contracts/config/graph.hardhat.yml index 1bb9e03bf..47b069aad 100644 --- a/packages/contracts/config/graph.hardhat.yml +++ b/packages/contracts/config/graph.hardhat.yml @@ -158,13 +158,3 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" - SubgraphAvailabilityManager: - init: - governor: "${{Env.deployer}}" - rewardsManager: "${{RewardsManager.address}}" - executionThreshold: 5 - voteTimeLimit: 300 - oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] - calls: - - fn: "transferOwnership" - owner: *governor diff --git a/packages/contracts/config/graph.localhost.yml b/packages/contracts/config/graph.localhost.yml index 87e199325..423052b98 100644 --- a/packages/contracts/config/graph.localhost.yml +++ b/packages/contracts/config/graph.localhost.yml @@ -159,13 +159,3 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" - SubgraphAvailabilityManager: - init: - governor: "${{Env.deployer}}" - rewardsManager: "${{RewardsManager.address}}" - executionThreshold: 5 - voteTimeLimit: 300 - oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] - calls: - - fn: "transferOwnership" - owner: *governor diff --git a/packages/contracts/config/graph.sepolia.yml b/packages/contracts/config/graph.sepolia.yml index eee30807c..d830cd9d9 100644 --- a/packages/contracts/config/graph.sepolia.yml +++ b/packages/contracts/config/graph.sepolia.yml @@ -159,13 +159,3 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" - SubgraphAvailabilityManager: - init: - governor: "${{Env.deployer}}" - rewardsManager: "${{RewardsManager.address}}" - executionThreshold: 5 - voteTimeLimit: 300 - oracles: [*availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle, *availabilityOracle] - calls: - - fn: "transferOwnership" - owner: *governor diff --git a/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts b/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts index c0005f81b..a3234dcba 100644 --- a/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts +++ b/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts @@ -4,16 +4,9 @@ import { NamedAccounts } from '@graphprotocol/sdk/gre' describe('RewardsManager configuration', () => { const { - getNamedAccounts, - contracts: { RewardsManager, Controller }, + contracts: { RewardsManager, Controller, SubgraphAvailabilityManager }, } = hre.graph() - let namedAccounts: NamedAccounts - - before(async () => { - namedAccounts = await getNamedAccounts() - }) - it('should be controlled by Controller', async function () { const controller = await RewardsManager.controller() expect(controller).eq(Controller.address) @@ -21,6 +14,6 @@ describe('RewardsManager configuration', () => { it('should allow subgraph availability oracle to deny rewards', async function () { const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() - expect(availabilityOracle).eq(namedAccounts.availabilityOracle.address) + expect(availabilityOracle).eq(SubgraphAvailabilityManager.address) }) }) diff --git a/packages/sdk/src/deployments/network/deployment/contracts/list.ts b/packages/sdk/src/deployments/network/deployment/contracts/list.ts index 14449a5ac..1b0dc1730 100644 --- a/packages/sdk/src/deployments/network/deployment/contracts/list.ts +++ b/packages/sdk/src/deployments/network/deployment/contracts/list.ts @@ -13,7 +13,6 @@ export const GraphNetworkSharedContractNameList = [ 'RewardsManager', 'DisputeManager', 'AllocationExchange', - 'SubgraphAvailabilityManager', ] as const export const GraphNetworkOptionalContractNameList = [ 'IENS', @@ -35,6 +34,7 @@ export const GraphNetworkL2ContractNameList = [ 'L2GNS', 'L2Staking', 'L2GraphTokenGateway', + 'SubgraphAvailabilityManager', ] as const export const GraphNetworkContractNameList = [ diff --git a/packages/sdk/src/deployments/network/deployment/contracts/load.ts b/packages/sdk/src/deployments/network/deployment/contracts/load.ts index 74ab9e1a8..d453ae5d9 100644 --- a/packages/sdk/src/deployments/network/deployment/contracts/load.ts +++ b/packages/sdk/src/deployments/network/deployment/contracts/load.ts @@ -41,6 +41,7 @@ import type { L2GNS, L2Curation, StakingExtension, + SubgraphAvailabilityManager, } from '@graphprotocol/contracts' import { ContractList } from '../../../lib/types/contract' import { loadArtifact } from '../../../lib/deploy/artifacts' @@ -63,6 +64,7 @@ export interface GraphNetworkContracts extends ContractList Date: Tue, 12 Mar 2024 19:31:04 -0300 Subject: [PATCH 24/31] fix: remove debugging log --- .../contracts/test/unit/rewards/subgraphAvailability.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts b/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts index f86105a03..7d82c2960 100644 --- a/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts @@ -64,7 +64,6 @@ describe('SubgraphAvailabilityManager', () => { fixture = new NetworkFixture(graph.provider) contracts = await fixture.load(governor) rewardsManager = contracts.RewardsManager as IRewardsManager - console.log('Llega hasta aca?') const deployResult = await deploy(DeployType.Deploy, governor, { name: 'SubgraphAvailabilityManager', args: [governor.address, rewardsManager.address, executionThreshold, voteTimeLimit, oracles], From b24377ee9e88ce290b0df22667129406942c66b9 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 13 Mar 2024 14:49:02 -0300 Subject: [PATCH 25/31] fix: e2e tests --- .../config/l1/rewardsManager.test.ts | 11 ++++++++- .../config/l2/rewardsManager.test.ts | 7 +++++- .../deployment/config/rewardsManager.test.ts | 8 +------ .../unit/rewards/subgraphAvailability.test.ts | 4 ++-- packages/sdk/src/gre/accounts.ts | 23 ++++++++++++++----- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/contracts/test/e2e/deployment/config/l1/rewardsManager.test.ts b/packages/contracts/test/e2e/deployment/config/l1/rewardsManager.test.ts index 0f9807d92..b7781fd96 100644 --- a/packages/contracts/test/e2e/deployment/config/l1/rewardsManager.test.ts +++ b/packages/contracts/test/e2e/deployment/config/l1/rewardsManager.test.ts @@ -1,17 +1,26 @@ import { expect } from 'chai' import hre from 'hardhat' import { isGraphL2ChainId } from '@graphprotocol/sdk' +import { NamedAccounts } from '@graphprotocol/sdk/gre' describe('[L1] RewardsManager configuration', () => { const graph = hre.graph() const { RewardsManager } = graph.contracts - before(function () { + let namedAccounts: NamedAccounts + + before(async function () { if (isGraphL2ChainId(graph.chainId)) this.skip() + namedAccounts = await graph.getNamedAccounts() }) it('issuancePerBlock should match "issuancePerBlock" in the config file', async function () { const value = await RewardsManager.issuancePerBlock() expect(value).eq('114693500000000000000') // hardcoded as it's set with a function call rather than init parameter }) + + it('should allow subgraph availability oracle to deny rewards', async function () { + const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() + expect(availabilityOracle).eq(namedAccounts.availabilityOracle.address) + }) }) diff --git a/packages/contracts/test/e2e/deployment/config/l2/rewardsManager.test.ts b/packages/contracts/test/e2e/deployment/config/l2/rewardsManager.test.ts index 9759ba78d..c324dc6bf 100644 --- a/packages/contracts/test/e2e/deployment/config/l2/rewardsManager.test.ts +++ b/packages/contracts/test/e2e/deployment/config/l2/rewardsManager.test.ts @@ -4,7 +4,7 @@ import hre from 'hardhat' describe('[L2] RewardsManager configuration', () => { const graph = hre.graph() - const { RewardsManager } = graph.contracts + const { RewardsManager, SubgraphAvailabilityManager } = graph.contracts before(function () { if (isGraphL1ChainId(graph.chainId)) this.skip() @@ -14,4 +14,9 @@ describe('[L2] RewardsManager configuration', () => { const value = await RewardsManager.issuancePerBlock() expect(value).eq('6036500000000000000') // hardcoded as it's set with a function call rather than init parameter }) + + it('should allow subgraph availability manager to deny rewards', async function () { + const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() + expect(availabilityOracle).eq(SubgraphAvailabilityManager.address) + }) }) diff --git a/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts b/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts index a3234dcba..e120392b6 100644 --- a/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts +++ b/packages/contracts/test/e2e/deployment/config/rewardsManager.test.ts @@ -1,19 +1,13 @@ import { expect } from 'chai' import hre from 'hardhat' -import { NamedAccounts } from '@graphprotocol/sdk/gre' describe('RewardsManager configuration', () => { const { - contracts: { RewardsManager, Controller, SubgraphAvailabilityManager }, + contracts: { RewardsManager, Controller }, } = hre.graph() it('should be controlled by Controller', async function () { const controller = await RewardsManager.controller() expect(controller).eq(Controller.address) }) - - it('should allow subgraph availability oracle to deny rewards', async function () { - const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() - expect(availabilityOracle).eq(SubgraphAvailabilityManager.address) - }) }) diff --git a/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts b/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts index 7d82c2960..93352a5b1 100644 --- a/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts +++ b/packages/contracts/test/unit/rewards/subgraphAvailability.test.ts @@ -6,8 +6,8 @@ import { ethers } from 'hardhat' import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { SubgraphAvailabilityManager } from '../../build/types/SubgraphAvailabilityManager' -import { IRewardsManager } from '../../build/types/IRewardsManager' +import { SubgraphAvailabilityManager } from '../../../build/types/SubgraphAvailabilityManager' +import { IRewardsManager } from '../../../build/types/IRewardsManager' import { NetworkFixture } from '../lib/fixtures' diff --git a/packages/sdk/src/gre/accounts.ts b/packages/sdk/src/gre/accounts.ts index c7adcc588..ed9b216c3 100644 --- a/packages/sdk/src/gre/accounts.ts +++ b/packages/sdk/src/gre/accounts.ts @@ -13,6 +13,7 @@ const namedAccountList: AccountNames[] = [ 'arbitrator', 'governor', 'authority', + 'availabilityOracle', 'pauseGuardian', 'allocationExchangeOwner', ] @@ -24,8 +25,15 @@ export async function getNamedAccounts( const namedAccounts = namedAccountList.reduce( async (accountsPromise, name) => { const accounts = await accountsPromise - const address = getItemValue(readConfig(graphConfigPath, true), `general/${name}`) - accounts[name] = await SignerWithAddress.create(provider.getSigner(address)) + let address + try { + address = getItemValue(readConfig(graphConfigPath, true), `general/${name}`) + } catch (e) { + // Skip if not found + } + if (address) { + accounts[name] = await SignerWithAddress.create(provider.getSigner(address)) + } return accounts }, Promise.resolve({} as NamedAccounts), @@ -45,10 +53,13 @@ export async function getTestAccounts( ): Promise { // Get list of privileged accounts we don't want as test accounts const namedAccounts = await getNamedAccounts(provider, graphConfigPath) - const blacklist = namedAccountList.map((a) => { - const account = namedAccounts[a] - return account.address - }) + const blacklist = namedAccountList.reduce((accounts, name) => { + const account = namedAccounts[name] + if (account) { + accounts.push(account.address) + } + return accounts + }, []) blacklist.push((await getDeployer(provider)).address) // Get signers and filter out blacklisted accounts From 09271fd82f2cc0d9cdc8828cdf8bfea0cbb7d135 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 13 Mar 2024 17:38:46 -0300 Subject: [PATCH 26/31] fix: building error --- packages/sdk/src/gre/accounts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/gre/accounts.ts b/packages/sdk/src/gre/accounts.ts index ed9b216c3..003ef19ea 100644 --- a/packages/sdk/src/gre/accounts.ts +++ b/packages/sdk/src/gre/accounts.ts @@ -53,7 +53,7 @@ export async function getTestAccounts( ): Promise { // Get list of privileged accounts we don't want as test accounts const namedAccounts = await getNamedAccounts(provider, graphConfigPath) - const blacklist = namedAccountList.reduce((accounts, name) => { + const blacklist = namedAccountList.reduce((accounts: string[], name) => { const account = namedAccounts[name] if (account) { accounts.push(account.address) From 6428bda9179889da8f9adfc31e5cbcf7d89120ea Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Wed, 1 May 2024 13:55:15 -0300 Subject: [PATCH 27/31] fix: update deployment --- packages/contracts/addresses-staging.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts/addresses-staging.json b/packages/contracts/addresses-staging.json index 26ec5d5da..601e6ffd6 100644 --- a/packages/contracts/addresses-staging.json +++ b/packages/contracts/addresses-staging.json @@ -205,11 +205,11 @@ } }, "SubgraphAvailabilityManager": { - "address": "0x9bDC3264596850E7F5d141A8D898dFA7001355CC", + "address": "0x8E0492d809D07E6b612818416ff27f8988ba5c53", "constructorArgs": [ "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", "0x913A282574A0AD91EecAdCbaf2800fDf516E27B1", - "5", + "3", "300", [ "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A", @@ -219,9 +219,9 @@ "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" ] ], - "creationCodeHash": "0x473bd948303724e315a33afd256ab83decae71a422b904a1d1592e113792bada", - "runtimeCodeHash": "0x4ab7cd273062d8e7bc85ba3a1114f1c10f7c0c098caf179e09412bf92d664159", - "txHash": "0x44bfe0c2e857295ec91649bff53a82ca2d9ad9d21974d01b3c56f55ebefde979" + "creationCodeHash": "0xc34842fa42ee2f21e3a435b52fa69a122a5de795448faa890fbc63d4838f45da", + "runtimeCodeHash": "0x2db3a9799e9733ffd5470a39d9f126d74ff82b60eeab2bbab2f2e76f7ec920c9", + "txHash": "0x684a6d54fa6fd3c48add9fc32d2624a1242a19cf94952d55730d4086eb6e7f8a" } }, "11155111": { From b45b21f950215aec70b8c5a6692af1a09a93c61e Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 2 May 2024 11:28:05 -0300 Subject: [PATCH 28/31] fix: new deployment --- packages/contracts/addresses-staging.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/addresses-staging.json b/packages/contracts/addresses-staging.json index 601e6ffd6..12bb04648 100644 --- a/packages/contracts/addresses-staging.json +++ b/packages/contracts/addresses-staging.json @@ -205,10 +205,10 @@ } }, "SubgraphAvailabilityManager": { - "address": "0x8E0492d809D07E6b612818416ff27f8988ba5c53", + "address": "0xF00AA63f1a8eE67BAD135759eC7a6542b1d56a8f", "constructorArgs": [ "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", - "0x913A282574A0AD91EecAdCbaf2800fDf516E27B1", + "0x00b9d319E3D09E83c62f453B44354049Dd93a345", "3", "300", [ @@ -220,8 +220,8 @@ ] ], "creationCodeHash": "0xc34842fa42ee2f21e3a435b52fa69a122a5de795448faa890fbc63d4838f45da", - "runtimeCodeHash": "0x2db3a9799e9733ffd5470a39d9f126d74ff82b60eeab2bbab2f2e76f7ec920c9", - "txHash": "0x684a6d54fa6fd3c48add9fc32d2624a1242a19cf94952d55730d4086eb6e7f8a" + "runtimeCodeHash": "0xe6c0ccd0b0514dd55e58806b1c84f09805dd6202985722579c11d7e252df6b8d", + "txHash": "0x5c1dd91c76e547fe31a5df822353b50e93eb795b915aac0ea8f8364229e9c29c" } }, "11155111": { From 152521b6432b79e3a16a462036e07459daa1d6d9 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 30 May 2024 16:13:48 -0300 Subject: [PATCH 29/31] chore: testnet deployment --- packages/contracts/addresses.json | 19 +++++++++++++++++++ .../config/graph.arbitrum-sepolia.yml | 10 +++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/contracts/addresses.json b/packages/contracts/addresses.json index ae66613a2..d7a9a027c 100644 --- a/packages/contracts/addresses.json +++ b/packages/contracts/addresses.json @@ -1221,6 +1221,25 @@ "creationCodeHash": "0x20cd202f7991716a84c097da5fbd365fd27f7f35f241f82c529ad7aba18b814b", "runtimeCodeHash": "0x5f396ffd54b6cd6b3faded0f366c5d7e148cc54743926061be2dfd12a75391de", "txHash": "0x2cefbc169b8ae51c263d0298956d86a397b05f11f076b71c918551f63fe33784" + }, + "SubgraphAvailabilityManager": { + "address": "0xf1B210a878B67A3A6787F24cfEF156611A433CeB", + "constructorArgs": [ + "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", + "0x00b9d319E3D09E83c62f453B44354049Dd93a345", + "3", + "300", + [ + "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A", + "0x633259dB4A33DB664d4C3Fa57BEc45E5030131E2", + "0x9FC436A69E290227b99A9CE0468a1b9D73068E80", + "0xC9b90ab0B9cA63fDBfB46f66BE813ea94561614d", + "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" + ] + ], + "creationCodeHash": "0xc34842fa42ee2f21e3a435b52fa69a122a5de795448faa890fbc63d4838f45da", + "runtimeCodeHash": "0xe6c0ccd0b0514dd55e58806b1c84f09805dd6202985722579c11d7e252df6b8d", + "txHash": "0xd1df6d74c89a29a8dac647b7d068a8be65a6b022ac7df80be0b7bcb12be60e25" } }, "11155111": { diff --git a/packages/contracts/config/graph.arbitrum-sepolia.yml b/packages/contracts/config/graph.arbitrum-sepolia.yml index 537e41bf1..c5fe97010 100644 --- a/packages/contracts/config/graph.arbitrum-sepolia.yml +++ b/packages/contracts/config/graph.arbitrum-sepolia.yml @@ -3,11 +3,11 @@ general: governor: &governor "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3" # EOA (TODO: update to a multisig) authority: &authority "0x49D4CFC037430cA9355B422bAeA7E9391e1d3215" # Authority that signs payment vouchers availabilityOracles: &availabilityOracles # Array of Subgraph Availability Oracles - - "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A" - - "0x633259dB4A33DB664d4C3Fa57BEc45E5030131E2" - - "0x9FC436A69E290227b99A9CE0468a1b9D73068E80" - - "0xC9b90ab0B9cA63fDBfB46f66BE813ea94561614d" - - "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" + - "0x5e4e823Ed094c035133eEC5Ec0d08ae1Af04e9Fa" + - "0x5aeE4c46cF9260E85E630ca7d9D757D5D5DbaFD6" + - "0x7369Cf2a917296c36f506144f3dE552403d1e1f1" + - "0x1e1f84c07e0471fc979f6f08228b0bd34cda9e54" + - "0x711aEA1f358DFAf74D34B4B525F9190e631F406C" pauseGuardian: &pauseGuardian "0xa0444508232dA3FA6C2f96a5f105f3f0cc0d20D7" # Protocol pause guardian allocationExchangeOwner: &allocationExchangeOwner "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3" # Allocation Exchange owner From 113fe69f9ee434f4c7275a56911c95625d1f88fd Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 30 May 2024 17:17:10 -0300 Subject: [PATCH 30/31] fix: testnet deployment --- packages/contracts/addresses.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/contracts/addresses.json b/packages/contracts/addresses.json index d7a9a027c..55289fbcf 100644 --- a/packages/contracts/addresses.json +++ b/packages/contracts/addresses.json @@ -1223,23 +1223,23 @@ "txHash": "0x2cefbc169b8ae51c263d0298956d86a397b05f11f076b71c918551f63fe33784" }, "SubgraphAvailabilityManager": { - "address": "0xf1B210a878B67A3A6787F24cfEF156611A433CeB", + "address": "0x71D9aE967d1f31fbbD1817150902de78f8f2f73E", "constructorArgs": [ "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", - "0x00b9d319E3D09E83c62f453B44354049Dd93a345", + "0x1F49caE7669086c8ba53CC35d1E9f80176d67E79", "3", "300", [ - "0xb4d396a40BB976118Bb2c9DB9C5539e65c7Fe64A", - "0x633259dB4A33DB664d4C3Fa57BEc45E5030131E2", - "0x9FC436A69E290227b99A9CE0468a1b9D73068E80", - "0xC9b90ab0B9cA63fDBfB46f66BE813ea94561614d", - "0xf3B8F917EcFA569089CF6A0988F9a9F5155cEF83" + "0x5e4e823Ed094c035133eEC5Ec0d08ae1Af04e9Fa", + "0x5aeE4c46cF9260E85E630ca7d9D757D5D5DbaFD6", + "0x7369Cf2a917296c36f506144f3dE552403d1e1f1", + "0x1e1f84c07e0471fc979f6f08228b0bd34cda9e54", + "0x711aEA1f358DFAf74D34B4B525F9190e631F406C" ] ], "creationCodeHash": "0xc34842fa42ee2f21e3a435b52fa69a122a5de795448faa890fbc63d4838f45da", - "runtimeCodeHash": "0xe6c0ccd0b0514dd55e58806b1c84f09805dd6202985722579c11d7e252df6b8d", - "txHash": "0xd1df6d74c89a29a8dac647b7d068a8be65a6b022ac7df80be0b7bcb12be60e25" + "runtimeCodeHash": "0x3907d0fe5a1fa28fee51100e57a3b193dfcee6720163067011e787262b1749bb", + "txHash": "0xb00751b4dc1c0603fe1b8b9ae9de8840ad1c29b42489c5bb267d80b10ae44ef0" } }, "11155111": { From 19faf45822cb1050768fcd202ff4c94476d9d423 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Thu, 20 Jun 2024 19:06:12 -0300 Subject: [PATCH 31/31] chore: deploy SAM to arbitrum-one --- packages/contracts/addresses.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/contracts/addresses.json b/packages/contracts/addresses.json index 55289fbcf..00617adca 100644 --- a/packages/contracts/addresses.json +++ b/packages/contracts/addresses.json @@ -790,6 +790,25 @@ "creationCodeHash": "0xbfc20ab9b880712ab90b5dec9d2a14c724b0bf7c20f02ede8ea76610bd41b6ef", "runtimeCodeHash": "0xd7fdd744c7a88993435a2978876b1e4341c5e0fb4d611011bb56e8738ab2485d", "txHash": "0xcc449d1ca1007fba76b25e987ea0d39164acf4027c10c40bd669ede1c65dc569" + }, + "SubgraphAvailabilityManager": { + "address": "0x1cB555359319A94280aCf85372Ac2323AaE2f5fd", + "constructorArgs": [ + "0x8C6de8F8D562f3382417340A6994601eE08D3809", + "0x971B9d3d0Ae3ECa029CAB5eA1fB0F72c85e6a525", + "3", + "300", + [ + "0xdcAA0a094F2Eb7cF7f73248EE64217D59B7B938d", + "0xBD9dc46cb1dd9F31dCbF0617c2Dd5f77A21dB8e8", + "0x16eAd4088d4308a7A4E0F7a1455ed56CDf1AC8AA", + "0x61923453906Eadc15fc1F610B8D1b67bc27658c2", + "0x10eb33C5E2fb6c7a37B110Cc4930d03A9e4C4D09" + ] + ], + "creationCodeHash": "0xc34842fa42ee2f21e3a435b52fa69a122a5de795448faa890fbc63d4838f45da", + "runtimeCodeHash": "0x52fcfd39c6ab3cf5ed4a736bc38eb1153d73c8cf1ca9e23370badc7843467ab4", + "txHash": "0x2eb44036d157e39c56377403029aebde4961028b404fc8c3f4cadc0f299d06dd" } }, "421613": {