From fe747d167b841729c72b14f90c50d1d1b544a444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 20 May 2024 16:58:56 -0300 Subject: [PATCH] chore: lint horizon package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- packages/horizon/contracts/GraphDirectory.sol | 20 +- .../horizon/contracts/escrow/GraphEscrow.sol | 50 +- .../contracts/escrow/GraphEscrowStorage.sol | 4 +- .../contracts/interfaces/IGraphToken.sol | 26 +- .../interfaces/IHorizonStakingBase.sol | 122 ++- .../interfaces/IHorizonStakingExtension.sol | 65 +- .../interfaces/IHorizonStakingTypes.sol | 5 +- .../horizon/contracts/interfaces/IManaged.sol | 4 +- .../IStakingBackwardsCompatibility.sol | 91 +-- .../contracts/mocks/ControllerMock.sol | 92 +-- .../horizon/contracts/mocks/MockGRTToken.sol | 39 +- .../contracts/payments/GraphPayments.sol | 9 +- .../payments/GraphPaymentsStorage.sol | 4 +- .../contracts/staking/HorizonStaking.sol | 710 +++++++++--------- .../staking/HorizonStakingExtension.sol | 221 +++--- .../staking/HorizonStakingStorage.sol | 45 +- .../staking/StakingBackwardsCompatibility.sol | 304 ++++---- .../contracts/staking/utilities/Managed.sol | 75 +- packages/horizon/package.json | 1 + packages/solhint-graph-config/index.js | 2 + packages/solhint-plugin-graph/index.js | 6 + yarn.lock | 1 + 22 files changed, 953 insertions(+), 943 deletions(-) diff --git a/packages/horizon/contracts/GraphDirectory.sol b/packages/horizon/contracts/GraphDirectory.sol index b509f792a..f36b0f9b3 100644 --- a/packages/horizon/contracts/GraphDirectory.sol +++ b/packages/horizon/contracts/GraphDirectory.sol @@ -24,15 +24,15 @@ contract GraphDirectory { address public immutable GRAPH_PAYMENTS; address public immutable GRAPH_ESCROW; - constructor(address _controller) { - CONTROLLER = _controller; - STAKING = IController(_controller).getContractProxy(keccak256("Staking")); - EPOCH_MANAGER = IController(_controller).getContractProxy(keccak256("EpochManager")); - GRAPH_TOKEN = IController(_controller).getContractProxy(keccak256("GraphToken")); - GRAPH_TOKEN_GATEWAY = IController(_controller).getContractProxy(keccak256("GraphTokenGateway")); - REWARDS_MANAGER = IController(_controller).getContractProxy(keccak256("RewardsManager")); - CURATION = IController(_controller).getContractProxy(keccak256("Curation")); - GRAPH_PAYMENTS = IController(_controller).getContractProxy(keccak256("GraphPayments")); - GRAPH_ESCROW = IController(_controller).getContractProxy(keccak256("GraphEscrow")); + constructor(address controller) { + CONTROLLER = controller; + STAKING = IController(controller).getContractProxy(keccak256("Staking")); + EPOCH_MANAGER = IController(controller).getContractProxy(keccak256("EpochManager")); + GRAPH_TOKEN = IController(controller).getContractProxy(keccak256("GraphToken")); + GRAPH_TOKEN_GATEWAY = IController(controller).getContractProxy(keccak256("GraphTokenGateway")); + REWARDS_MANAGER = IController(controller).getContractProxy(keccak256("RewardsManager")); + CURATION = IController(controller).getContractProxy(keccak256("Curation")); + GRAPH_PAYMENTS = IController(controller).getContractProxy(keccak256("GraphPayments")); + GRAPH_ESCROW = IController(controller).getContractProxy(keccak256("GraphEscrow")); } } diff --git a/packages/horizon/contracts/escrow/GraphEscrow.sol b/packages/horizon/contracts/escrow/GraphEscrow.sol index b1c21f54d..6e6921a82 100644 --- a/packages/horizon/contracts/escrow/GraphEscrow.sol +++ b/packages/horizon/contracts/escrow/GraphEscrow.sol @@ -9,18 +9,6 @@ import { GraphEscrowStorageV1Storage } from "./GraphEscrowStorage.sol"; import { TokenUtils } from "../libraries/TokenUtils.sol"; contract GraphEscrow is IGraphEscrow, GraphEscrowStorageV1Storage, GraphDirectory { - // -- Errors -- - - error GraphEscrowNotGraphPayments(); - error GraphEscrowInputsLengthMismatch(); - error GraphEscrowInsufficientThawAmount(); - error GraphEscrowInsufficientAmount(uint256 available, uint256 required); - error GraphEscrowNotThawing(); - error GraphEscrowStillThawing(uint256 currentTimestamp, uint256 thawEndTimestamp); - error GraphEscrowThawingPeriodTooLong(uint256 thawingPeriod, uint256 maxThawingPeriod); - error GraphEscrowCollectorNotAuthorized(address sender, address dataService); - error GraphEscrowCollectorInsufficientAmount(uint256 available, uint256 required); - // -- Events -- event AuthorizedCollector(address indexed sender, address indexed dataService); @@ -39,23 +27,35 @@ contract GraphEscrow is IGraphEscrow, GraphEscrowStorageV1Storage, GraphDirector event Withdraw(address indexed sender, address indexed receiver, uint256 amount); event Collect(address indexed sender, address indexed receiver, uint256 amount); + // -- Errors -- + + error GraphEscrowNotGraphPayments(); + error GraphEscrowInputsLengthMismatch(); + error GraphEscrowInsufficientThawAmount(); + error GraphEscrowInsufficientAmount(uint256 available, uint256 required); + error GraphEscrowNotThawing(); + error GraphEscrowStillThawing(uint256 currentTimestamp, uint256 thawEndTimestamp); + error GraphEscrowThawingPeriodTooLong(uint256 thawingPeriod, uint256 maxThawingPeriod); + error GraphEscrowCollectorNotAuthorized(address sender, address dataService); + error GraphEscrowCollectorInsufficientAmount(uint256 available, uint256 required); + // -- Constructor -- constructor( - address _controller, - uint256 _revokeCollectorThawingPeriod, - uint256 _withdrawEscrowThawingPeriod - ) GraphDirectory(_controller) { - if (_revokeCollectorThawingPeriod > MAX_THAWING_PERIOD) { - revert GraphEscrowThawingPeriodTooLong(_revokeCollectorThawingPeriod, MAX_THAWING_PERIOD); + address controller, + uint256 revokeCollectorThawingPeriod, + uint256 withdrawEscrowThawingPeriod + ) GraphDirectory(controller) { + if (revokeCollectorThawingPeriod > MAX_THAWING_PERIOD) { + revert GraphEscrowThawingPeriodTooLong(revokeCollectorThawingPeriod, MAX_THAWING_PERIOD); } - if (_withdrawEscrowThawingPeriod > MAX_THAWING_PERIOD) { - revert GraphEscrowThawingPeriodTooLong(_withdrawEscrowThawingPeriod, MAX_THAWING_PERIOD); + if (withdrawEscrowThawingPeriod > MAX_THAWING_PERIOD) { + revert GraphEscrowThawingPeriodTooLong(withdrawEscrowThawingPeriod, MAX_THAWING_PERIOD); } - revokeCollectorThawingPeriod = _revokeCollectorThawingPeriod; - withdrawEscrowThawingPeriod = _withdrawEscrowThawingPeriod; + revokeCollectorThawingPeriod = revokeCollectorThawingPeriod; + withdrawEscrowThawingPeriod = withdrawEscrowThawingPeriod; } // approve a data service to collect funds @@ -67,7 +67,9 @@ contract GraphEscrow is IGraphEscrow, GraphEscrowStorageV1Storage, GraphDirector // thaw a data service's collector authorization function thawCollector(address dataService) external { - authorizedCollectors[msg.sender][dataService].thawEndTimestamp = block.timestamp + revokeCollectorThawingPeriod; + authorizedCollectors[msg.sender][dataService].thawEndTimestamp = + block.timestamp + + REVOKE_COLLECTOR_THAWING_PERIOD; emit ThawCollector(msg.sender, dataService); } @@ -147,7 +149,7 @@ contract GraphEscrow is IGraphEscrow, GraphEscrowStorageV1Storage, GraphDirector // Set amount to thaw account.amountThawing = amount; // Set when the thaw is complete (thawing period number of seconds after current timestamp) - account.thawEndTimestamp = block.timestamp + withdrawEscrowThawingPeriod; + account.thawEndTimestamp = block.timestamp + WITHDRAW_ESCROW_THAWING_PERIOD; emit Thaw(msg.sender, receiver, amount, account.amountThawing, account.thawEndTimestamp); } diff --git a/packages/horizon/contracts/escrow/GraphEscrowStorage.sol b/packages/horizon/contracts/escrow/GraphEscrowStorage.sol index 158b7fae0..89a49e756 100644 --- a/packages/horizon/contracts/escrow/GraphEscrowStorage.sol +++ b/packages/horizon/contracts/escrow/GraphEscrowStorage.sol @@ -17,8 +17,8 @@ contract GraphEscrowStorageV1Storage { uint256 public constant MAX_THAWING_PERIOD = 90 days; // Thawing period for authorized collectors - uint256 public immutable revokeCollectorThawingPeriod; + uint256 public immutable REVOKE_COLLECTOR_THAWING_PERIOD; // The duration (in seconds) in which escrow funds are thawing before they can be withdrawn - uint256 public immutable withdrawEscrowThawingPeriod; + uint256 public immutable WITHDRAW_ESCROW_THAWING_PERIOD; } diff --git a/packages/horizon/contracts/interfaces/IGraphToken.sol b/packages/horizon/contracts/interfaces/IGraphToken.sol index 27f68a0d2..d00e81fc1 100644 --- a/packages/horizon/contracts/interfaces/IGraphToken.sol +++ b/packages/horizon/contracts/interfaces/IGraphToken.sol @@ -9,30 +9,28 @@ interface IGraphToken is IERC20 { function burn(uint256 amount) external; - function burnFrom(address _from, uint256 amount) external; + function burnFrom(address from, uint256 amount) external; - function mint(address _to, uint256 _amount) external; + function mint(address to, uint256 amount) external; // -- Mint Admin -- - function addMinter(address _account) external; + function addMinter(address account) external; - function removeMinter(address _account) external; + function removeMinter(address account) external; function renounceMinter() external; - function isMinter(address _account) external view returns (bool); - // -- Permit -- function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s ) external; // -- Allowance -- @@ -40,4 +38,6 @@ interface IGraphToken is IERC20 { function increaseAllowance(address spender, uint256 addedValue) external returns (bool); function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); + + function isMinter(address account) external view returns (bool); } diff --git a/packages/horizon/contracts/interfaces/IHorizonStakingBase.sol b/packages/horizon/contracts/interfaces/IHorizonStakingBase.sol index 1fc539f96..41f1ae1df 100644 --- a/packages/horizon/contracts/interfaces/IHorizonStakingBase.sol +++ b/packages/horizon/contracts/interfaces/IHorizonStakingBase.sol @@ -113,20 +113,20 @@ interface IHorizonStakingBase is IHorizonStakingTypes { ); // deposit stake - function stake(uint256 _tokens) external; + function stake(uint256 tokens) external; - function stakeTo(address _serviceProvider, uint256 _tokens) external; + function stakeTo(address serviceProvider, uint256 tokens) external; // can be called by anyone if the indexer has provisioned stake to this verifier - function stakeToProvision(address _serviceProvider, address _verifier, uint256 _tokens) external; + function stakeToProvision(address serviceProvider, address verifier, uint256 tokens) external; // create a provision function provision( - address _serviceProvider, - address _verifier, - uint256 _tokens, - uint32 _maxVerifierCut, - uint64 _thawingPeriod + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod ) external; /** @@ -135,65 +135,56 @@ interface IHorizonStakingBase is IHorizonStakingTypes { * service, where the data service is the verifier. Only authorized verifiers can be used. * This function can be called by the service provider or by an operator authorized by the provider * for this specific verifier. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) - * @param _tokens The amount of tokens that will be locked and slashable - * @param _maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing - * @param _thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) + * @param tokens The amount of tokens that will be locked and slashable + * @param maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing + * @param thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision */ function provisionLocked( - address _serviceProvider, - address _verifier, - uint256 _tokens, - uint32 _maxVerifierCut, - uint64 _thawingPeriod + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod ) external; // initiate a thawing to remove tokens from a provision - function thaw(address _serviceProvider, address _verifier, uint256 _tokens) external returns (bytes32); + function thaw(address serviceProvider, address verifier, uint256 tokens) external returns (bytes32); // add more tokens from idle stake to an existing provision - function addToProvision(address _serviceProvider, address _verifier, uint256 _tokens) external; + function addToProvision(address serviceProvider, address verifier, uint256 tokens) external; // moves thawed stake from a provision back into the provider's available stake - function deprovision(address _serviceProvider, address _verifier, uint256 _tokens) external; + function deprovision(address serviceProvider, address verifier, uint256 tokens) external; // moves thawed stake from one provision into another provision - function reprovision( - address _serviceProvider, - address _oldVerifier, - address _newVerifier, - uint256 _tokens - ) external; + function reprovision(address serviceProvider, address oldVerifier, address newVerifier, uint256 tokens) external; // moves thawed stake back to the owner's account - stake is removed from the protocol - function unstake(uint256 _tokens) external; + function unstake(uint256 tokens) external; // delegate tokens to a provider on a data service - function delegate(address _serviceProvider, address _verifier, uint256 _tokens, uint256 _minSharesOut) external; + function delegate(address serviceProvider, address verifier, uint256 tokens, uint256 minSharesOut) external; // undelegate (thaw) delegated tokens from a provision - function undelegate(address _serviceProvider, address _verifier, uint256 _shares) external; + function undelegate(address serviceProvider, address verifier, uint256 shares) external; // withdraw delegated tokens after thawing function withdrawDelegated( - address _serviceProvider, - address _verifier, - address _newServiceProvider, - uint256 _minSharesForNewProvider + address serviceProvider, + address verifier, + address newServiceProvider, + uint256 minSharesForNewProvider ) external; function slash( - address _serviceProvider, - uint256 _tokens, - uint256 _verifierCutAmount, - address _verifierCutDestination + address serviceProvider, + uint256 tokens, + uint256 verifierCutAmount, + address verifierCutDestination ) external; - // staked tokens that are currently not provisioned, aka idle stake - // `getStake(serviceProvider) - ServiceProvider.tokensProvisioned` - function getIdleStake(address _serviceProvider) external view returns (uint256 tokens); - /** * @notice Withdraw indexer tokens once the thawing period has passed. * @dev This is only needed during the transition period while we still have @@ -201,36 +192,41 @@ interface IHorizonStakingBase is IHorizonStakingTypes { */ function withdraw() external; - function setDelegationSlashingEnabled(bool _enabled) external; + function setDelegationSlashingEnabled(bool enabled) external; - /** - * @notice Check if an operator is authorized for the caller on a specific verifier / data service. - * @param _operator The address to check for auth - * @param _serviceProvider The service provider on behalf of whom they're claiming to act - * @param _verifier The verifier / data service on which they're claiming to act - */ - function isAuthorized(address _operator, address _serviceProvider, address _verifier) external view returns (bool); - - function getProviderTokensAvailable(address _serviceProvider, address _verifier) external view returns (uint256); - function setMaxThawingPeriod(uint64 _maxThawingPeriod) external; + function setMaxThawingPeriod(uint64 maxThawingPeriod) external; - function setAllowedLockedVerifier(address _verifier, bool _allowed) external; + function setAllowedLockedVerifier(address verifier, bool allowed) external; /** * @notice Add tokens to a delegation pool (without getting shares). * Used by data services to pay delegation fees/rewards. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned - * @param _tokens The amount of tokens to add to the delegation pool + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned + * @param tokens The amount of tokens to add to the delegation pool */ - function addToDelegationPool(address _serviceProvider, address _verifier, uint256 _tokens) external; + function addToDelegationPool(address serviceProvider, address verifier, uint256 tokens) external; function setProvisionParameters( - address _serviceProvider, - address _verifier, - uint32 _maxVerifierCut, - uint64 _thawingPeriod + address serviceProvider, + address verifier, + uint32 maxVerifierCut, + uint64 thawingPeriod ) external; - function acceptProvisionParameters(address _serviceProvider) external; + function acceptProvisionParameters(address serviceProvider) external; + + // staked tokens that are currently not provisioned, aka idle stake + // `getStake(serviceProvider) - ServiceProvider.tokensProvisioned` + function getIdleStake(address serviceProvider) external view returns (uint256 tokens); + + /** + * @notice Check if an operator is authorized for the caller on a specific verifier / data service. + * @param operator The address to check for auth + * @param serviceProvider The service provider on behalf of whom they're claiming to act + * @param verifier The verifier / data service on which they're claiming to act + */ + function isAuthorized(address operator, address serviceProvider, address verifier) external view returns (bool); + + function getProviderTokensAvailable(address serviceProvider, address verifier) external view returns (uint256); } diff --git a/packages/horizon/contracts/interfaces/IHorizonStakingExtension.sol b/packages/horizon/contracts/interfaces/IHorizonStakingExtension.sol index 546f9fefa..7c5da23e2 100644 --- a/packages/horizon/contracts/interfaces/IHorizonStakingExtension.sol +++ b/packages/horizon/contracts/interfaces/IHorizonStakingExtension.sol @@ -18,57 +18,56 @@ interface IHorizonStakingExtension { uint256 feeCut ); + /** + * @notice Authorize or unauthorize an address to be an operator for the caller on a data service. + * @param operator Address to authorize or unauthorize + * @param verifier The verifier / data service on which they'll be allowed to operate + * @param allowed Whether the operator is authorized or not + */ + function setOperator(address operator, address verifier, bool allowed) external; + + // for vesting contracts + function setOperatorLocked(address operator, address verifier, bool allowed) external; + + function setDelegationFeeCut(address serviceProvider, address verifier, uint256 feeType, uint256 feeCut) external; + function getStake(address serviceProvider) external view returns (uint256); - function getDelegatedTokensAvailable(address _serviceProvider, address _verifier) external view returns (uint256); + function getDelegatedTokensAvailable(address serviceProvider, address verifier) external view returns (uint256); + function getTokensAvailable( - address _serviceProvider, - address _verifier, - uint32 _delegationRatio + address serviceProvider, + address verifier, + uint32 delegationRatio ) external view returns (uint256); function getServiceProvider( address serviceProvider ) external view returns (IHorizonStakingTypes.ServiceProvider memory); - /** - * @notice Authorize or unauthorize an address to be an operator for the caller on a data service. - * @param _operator Address to authorize or unauthorize - * @param _verifier The verifier / data service on which they'll be allowed to operate - * @param _allowed Whether the operator is authorized or not - */ - function setOperator(address _operator, address _verifier, bool _allowed) external; - - // for vesting contracts - function setOperatorLocked(address _operator, address _verifier, bool _allowed) external; - function getMaxThawingPeriod() external view returns (uint64); function getDelegationPool( - address _serviceProvider, - address _verifier + address serviceProvider, + address verifier ) external view returns (IHorizonStakingTypes.DelegationPool memory); + function getDelegation( - address _delegator, - address _serviceProvider, - address _verifier + address delegator, + address serviceProvider, + address verifier ) external view returns (IHorizonStakingTypes.Delegation memory); - function getThawRequest(bytes32 _thawRequestId) external view returns (IHorizonStakingTypes.ThawRequest memory); + + function getThawRequest(bytes32 thawRequestId) external view returns (IHorizonStakingTypes.ThawRequest memory); + function getProvision( - address _serviceProvider, - address _verifier + address serviceProvider, + address verifier ) external view returns (IHorizonStakingTypes.Provision memory); - function setDelegationFeeCut( - address _serviceProvider, - address _verifier, - uint256 _feeType, - uint256 _feeCut - ) external; - function getDelegationFeeCut( - address _serviceProvider, - address _verifier, - uint256 _feeType + address serviceProvider, + address verifier, + uint256 feeType ) external view returns (uint256); } diff --git a/packages/horizon/contracts/interfaces/IHorizonStakingTypes.sol b/packages/horizon/contracts/interfaces/IHorizonStakingTypes.sol index f58a004b7..f737d52ee 100644 --- a/packages/horizon/contracts/interfaces/IHorizonStakingTypes.sol +++ b/packages/horizon/contracts/interfaces/IHorizonStakingTypes.sol @@ -3,6 +3,9 @@ pragma solidity >=0.6.12 <0.9.0; pragma abicoder v2; +// TODO: create custom var-name-mixedcase +/* solhint-disable var-name-mixedcase */ + interface IHorizonStakingTypes { struct Provision { // service provider tokens in the provision @@ -77,7 +80,7 @@ interface IHorizonStakingTypes { // Tokens on the Service Provider stake (staked by the provider) uint256 tokensStaked; // Tokens used in allocations - uint256 __DEPRECATED_tokensAllocated; + uint256 __DEPRECATED_tokensAllocated; // solhint-disable-line graph/leading-underscore // Tokens locked for withdrawal subject to thawing period uint256 __DEPRECATED_tokensLocked; // Block when locked tokens can be withdrawn diff --git a/packages/horizon/contracts/interfaces/IManaged.sol b/packages/horizon/contracts/interfaces/IManaged.sol index 5aa086ba7..13c129b2f 100644 --- a/packages/horizon/contracts/interfaces/IManaged.sol +++ b/packages/horizon/contracts/interfaces/IManaged.sol @@ -13,9 +13,9 @@ interface IManaged { /** * @notice (Deprecated) Set the controller that manages this contract * @dev Only the current controller can set a new controller - * @param _controller Address of the new controller + * @param controller Address of the new controller */ - function setController(address _controller) external; + function setController(address controller) external; /** * @notice Get the Controller that manages this contract diff --git a/packages/horizon/contracts/interfaces/IStakingBackwardsCompatibility.sol b/packages/horizon/contracts/interfaces/IStakingBackwardsCompatibility.sol index 43c93662a..088ee3776 100644 --- a/packages/horizon/contracts/interfaces/IStakingBackwardsCompatibility.sol +++ b/packages/horizon/contracts/interfaces/IStakingBackwardsCompatibility.sol @@ -12,14 +12,6 @@ pragma abicoder v2; * that includes the full functionality. */ interface IStakingBackwardsCompatibility { - /** - * @dev Emitted when `delegator` delegated `tokens` to the `serviceProvider`, the delegator - * gets `shares` for the delegation pool proportionally to the tokens staked. - * This event is here for backwards compatibility, the tokens are delegated - * on the subgraph data service provision. - */ - event StakeDelegated(address indexed serviceProvider, address indexed delegator, uint256 tokens, uint256 shares); - /** * @dev Allocate GRT tokens for the purpose of serving queries of a subgraph deployment * An allocation is created in the allocate() function and closed in closeAllocation() @@ -31,10 +23,32 @@ interface IStakingBackwardsCompatibility { uint256 createdAtEpoch; // Epoch when it was created uint256 closedAtEpoch; // Epoch when it was closed uint256 collectedFees; // Collected fees for the allocation - uint256 __DEPRECATED_effectiveAllocation; // solhint-disable-line var-name-mixedcase + uint256 DEPRECATED_effectiveAllocation; // solhint-disable-line var-name-mixedcase uint256 accRewardsPerAllocatedToken; // Snapshot used for reward calc uint256 distributedRebates; // Collected rebates that have been rebated } + + /** + * @dev Possible states an allocation can be. + * States: + * - Null = indexer == address(0) + * - Active = not Null && tokens > 0 + * - Closed = Active && closedAtEpoch != 0 + */ + enum AllocationState { + Null, + Active, + Closed + } + + /** + * @dev Emitted when `delegator` delegated `tokens` to the `serviceProvider`, the delegator + * gets `shares` for the delegation pool proportionally to the tokens staked. + * This event is here for backwards compatibility, the tokens are delegated + * on the subgraph data service provision. + */ + event StakeDelegated(address indexed serviceProvider, address indexed delegator, uint256 tokens, uint256 shares); + /** * @dev Emitted when `serviceProvider` stakes `tokens` amount. */ @@ -95,35 +109,22 @@ interface IStakingBackwardsCompatibility { */ event SetOperator(address indexed indexer, address indexed operator, bool allowed); - /** - * @dev Possible states an allocation can be. - * States: - * - Null = indexer == address(0) - * - Active = not Null && tokens > 0 - * - Closed = Active && closedAtEpoch != 0 - */ - enum AllocationState { - Null, - Active, - Closed - } - /** * @notice Set the address of the counterpart (L1 or L2) staking contract. * @dev This function can only be called by the governor. - * @param _counterpart Address of the counterpart staking contract in the other chain, without any aliasing. + * @param counterpart Address of the counterpart staking contract in the other chain, without any aliasing. */ - function setCounterpartStakingAddress(address _counterpart) external; + function setCounterpartStakingAddress(address counterpart) external; /** * @notice Close an allocation and free the staked tokens. * To be eligible for rewards a proof of indexing must be presented. * Presenting a bad proof is subject to slashable condition. * To opt out of rewards set _poi to 0x0 - * @param _allocationID The allocation identifier - * @param _poi Proof of indexing submitted for the allocated period + * @param allocationID The allocation identifier + * @param poi Proof of indexing submitted for the allocated period */ - function closeAllocation(address _allocationID, bytes32 _poi) external; + function closeAllocation(address allocationID, bytes32 poi) external; /** * @notice Collect query fees from state channels and assign them to an allocation. @@ -132,58 +133,58 @@ interface IStakingBackwardsCompatibility { * 1) Accept calls with zero tokens. * 2) Accept calls after an allocation passed the dispute period, in that case, all * the received tokens are burned. - * @param _tokens Amount of tokens to collect - * @param _allocationID Allocation where the tokens will be assigned + * @param tokens Amount of tokens to collect + * @param allocationID Allocation where the tokens will be assigned */ - function collect(uint256 _tokens, address _allocationID) external; + function collect(uint256 tokens, address allocationID) external; /** * @notice Return true if operator is allowed for indexer. - * @param _operator Address of the operator - * @param _indexer Address of the indexer + * @param operator Address of the operator + * @param indexer Address of the indexer * @return True if operator is allowed for indexer, false otherwise */ - function isOperator(address _operator, address _indexer) external view returns (bool); + function isOperator(address operator, address indexer) external view returns (bool); /** * @notice Getter that returns if an indexer has any stake. - * @param _indexer Address of the indexer + * @param indexer Address of the indexer * @return True if indexer has staked tokens */ - function hasStake(address _indexer) external view returns (bool); + function hasStake(address indexer) external view returns (bool); /** * @notice Get the total amount of tokens staked by the indexer. - * @param _indexer Address of the indexer + * @param indexer Address of the indexer * @return Amount of tokens staked by the indexer */ - function getIndexerStakedTokens(address _indexer) external view returns (uint256); + function getIndexerStakedTokens(address indexer) external view returns (uint256); /** * @notice Return the allocation by ID. - * @param _allocationID Address used as allocation identifier + * @param allocationID Address used as allocation identifier * @return Allocation data */ - function getAllocation(address _allocationID) external view returns (Allocation memory); + function getAllocation(address allocationID) external view returns (Allocation memory); /** * @notice Return the current state of an allocation - * @param _allocationID Allocation identifier + * @param allocationID Allocation identifier * @return AllocationState enum with the state of the allocation */ - function getAllocationState(address _allocationID) external view returns (AllocationState); + function getAllocationState(address allocationID) external view returns (AllocationState); /** * @notice Return if allocationID is used. - * @param _allocationID Address used as signer by the indexer for an allocation + * @param allocationID Address used as signer by the indexer for an allocation * @return True if allocationID already used */ - function isAllocation(address _allocationID) external view returns (bool); + function isAllocation(address allocationID) external view returns (bool); /** * @notice Return the total amount of tokens allocated to subgraph. - * @param _subgraphDeploymentID Deployment ID for the subgraph + * @param subgraphDeploymentID Deployment ID for the subgraph * @return Total tokens allocated to subgraph */ - function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); + function getSubgraphAllocatedTokens(bytes32 subgraphDeploymentID) external view returns (uint256); } diff --git a/packages/horizon/contracts/mocks/ControllerMock.sol b/packages/horizon/contracts/mocks/ControllerMock.sol index f8c976cf9..a30993a89 100644 --- a/packages/horizon/contracts/mocks/ControllerMock.sol +++ b/packages/horizon/contracts/mocks/ControllerMock.sol @@ -12,7 +12,7 @@ import { IManaged } from "@graphprotocol/contracts/contracts/governance/IManaged */ contract ControllerMock is IController { /// @dev Track contract ids to contract proxy address - mapping(bytes32 => address) private _registry; + mapping(bytes32 contractName => address contractAddress) private _registry; address public governor; bool internal _paused; bool internal _partialPaused; @@ -23,59 +23,42 @@ contract ControllerMock is IController { /** * Constructor for the Controller mock - * @param _governor Address of the governor + * @param governor_ Address of the governor */ - constructor(address _governor) { - governor = _governor; - } - - /** - * @notice Getter to access governor - * @return Address of the governor - */ - function getGovernor() external view override returns (address) { - return governor; + constructor(address governor_) { + governor = governor_; } // -- Registry -- /** * @notice Register contract id and mapped address - * @param _id Contract id (keccak256 hash of contract name) - * @param _contractAddress Contract address + * @param id Contract id (keccak256 hash of contract name) + * @param contractAddress Contract address */ - function setContractProxy(bytes32 _id, address _contractAddress) external override { - require(_contractAddress != address(0), "Contract address must be set"); - _registry[_id] = _contractAddress; - emit SetContractProxy(_id, _contractAddress); + function setContractProxy(bytes32 id, address contractAddress) external override { + require(contractAddress != address(0), "Contract address must be set"); + _registry[id] = contractAddress; + emit SetContractProxy(id, contractAddress); } /** * @notice Unregister a contract address - * @param _id Contract id (keccak256 hash of contract name) - */ - function unsetContractProxy(bytes32 _id) external override { - _registry[_id] = address(0); - emit SetContractProxy(_id, address(0)); - } - - /** - * @notice Get contract proxy address by its id - * @param _id Contract id (keccak256 hash of contract name) - * @return Address of the proxy contract for the provided id + * @param id Contract id (keccak256 hash of contract name) */ - function getContractProxy(bytes32 _id) external view override returns (address) { - return _registry[_id]; + function unsetContractProxy(bytes32 id) external override { + _registry[id] = address(0); + emit SetContractProxy(id, address(0)); } /** * @notice Update a contract's controller - * @param _id Contract id (keccak256 hash of contract name) - * @param _controller New Controller address + * @param id Contract id (keccak256 hash of contract name) + * @param controller New Controller address */ - function updateController(bytes32 _id, address _controller) external override { - require(_controller != address(0), "Controller must be set"); - return IManaged(_registry[_id]).setController(_controller); + function updateController(bytes32 id, address controller) external override { + require(controller != address(0), "Controller must be set"); + return IManaged(_registry[id]).setController(controller); } // -- Pausing -- @@ -83,28 +66,45 @@ contract ControllerMock is IController { /** * @notice Change the partial paused state of the contract * Partial pause is intended as a partial pause of the protocol - * @param _toPause True if the contracts should be (partially) paused, false otherwise + * @param toPause True if the contracts should be (partially) paused, false otherwise */ - function setPartialPaused(bool _toPause) external override { - _partialPaused = _toPause; + function setPartialPaused(bool toPause) external override { + _partialPaused = toPause; } /** * @notice Change the paused state of the contract * Full pause most of protocol functions - * @param _toPause True if the contracts should be paused, false otherwise + * @param toPause True if the contracts should be paused, false otherwise */ - function setPaused(bool _toPause) external override { - _paused = _toPause; + function setPaused(bool toPause) external override { + _paused = toPause; } /** * @notice Change the Pause Guardian - * @param _newPauseGuardian The address of the new Pause Guardian + * @param newPauseGuardian The address of the new Pause Guardian + */ + function setPauseGuardian(address newPauseGuardian) external override { + require(newPauseGuardian != address(0), "PauseGuardian must be set"); + _pauseGuardian = newPauseGuardian; + } + + /** + * @notice Getter to access governor + * @return Address of the governor + */ + function getGovernor() external view override returns (address) { + return governor; + } + + /** + * @notice Get contract proxy address by its id + * @param id Contract id (keccak256 hash of contract name) + * @return Address of the proxy contract for the provided id */ - function setPauseGuardian(address _newPauseGuardian) external override { - require(_newPauseGuardian != address(0), "PauseGuardian must be set"); - _pauseGuardian = _newPauseGuardian; + function getContractProxy(bytes32 id) external view override returns (address) { + return _registry[id]; } /** diff --git a/packages/horizon/contracts/mocks/MockGRTToken.sol b/packages/horizon/contracts/mocks/MockGRTToken.sol index a39faaed3..1212f3e70 100644 --- a/packages/horizon/contracts/mocks/MockGRTToken.sol +++ b/packages/horizon/contracts/mocks/MockGRTToken.sol @@ -1,46 +1,47 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; contract MockGRTToken is ERC20, IGraphToken { constructor() ERC20("Graph Token", "GRT") {} function burn(uint256 amount) external {} - function burnFrom(address _from, uint256 amount) external { - _burn(_from, amount); - } - - function mint(address to, uint256 amount) public { - _mint(to, amount); + function burnFrom(address from, uint256 amount) external { + _burn(from, amount); } // -- Mint Admin -- - function addMinter(address _account) external {} + function addMinter(address account) external {} - function removeMinter(address _account) external {} + function removeMinter(address account) external {} function renounceMinter() external {} - function isMinter(address _account) external view returns (bool) {} - // -- Permit -- function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s ) external {} // -- Allowance -- function increaseAllowance(address spender, uint256 addedValue) external returns (bool) {} + function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) {} + + function isMinter(address account) external view returns (bool) {} + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } } diff --git a/packages/horizon/contracts/payments/GraphPayments.sol b/packages/horizon/contracts/payments/GraphPayments.sol index 2a2fa96ba..41ba9f063 100644 --- a/packages/horizon/contracts/payments/GraphPayments.sol +++ b/packages/horizon/contracts/payments/GraphPayments.sol @@ -9,6 +9,7 @@ import { GraphPaymentsStorageV1Storage } from "./GraphPaymentsStorage.sol"; import { TokenUtils } from "../libraries/TokenUtils.sol"; contract GraphPayments is IGraphPayments, GraphPaymentsStorageV1Storage, GraphDirectory { + uint256 private immutable MAX_PPM = 1000000; // 100% in parts per million // -- Errors -- error GraphPaymentsNotThawing(); @@ -22,12 +23,10 @@ contract GraphPayments is IGraphPayments, GraphPaymentsStorageV1Storage, GraphDi // -- Parameters -- - uint256 private immutable MAX_PPM = 1000000; // 100% in parts per million - // -- Constructor -- - constructor(address _controller, uint256 _protocolPaymentCut) GraphDirectory(_controller) { - protocolPaymentCut = _protocolPaymentCut; + constructor(address controller, uint256 protocolPaymentCut) GraphDirectory(controller) { + protocolPaymentCut = protocolPaymentCut; } // collect funds from a sender, pay cuts and forward the rest to the receiver @@ -43,7 +42,7 @@ contract GraphPayments is IGraphPayments, GraphPaymentsStorageV1Storage, GraphDi TokenUtils.pullTokens(graphToken, msg.sender, amount); // Pay protocol cut - uint256 tokensProtocol = (amount * protocolPaymentCut) / MAX_PPM; + uint256 tokensProtocol = (amount * PROTOCOL_PAYMENT_CUT) / MAX_PPM; TokenUtils.burnTokens(graphToken, tokensProtocol); // Pay data service cut diff --git a/packages/horizon/contracts/payments/GraphPaymentsStorage.sol b/packages/horizon/contracts/payments/GraphPaymentsStorage.sol index 7d4c1c338..09a8691d8 100644 --- a/packages/horizon/contracts/payments/GraphPaymentsStorage.sol +++ b/packages/horizon/contracts/payments/GraphPaymentsStorage.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.24; -import { IGraphPayments } from "../interfaces/IGraphPayments.sol"; - contract GraphPaymentsStorageV1Storage { // The graph protocol payment cut - uint256 public immutable protocolPaymentCut; + uint256 public immutable PROTOCOL_PAYMENT_CUT; } diff --git a/packages/horizon/contracts/staking/HorizonStaking.sol b/packages/horizon/contracts/staking/HorizonStaking.sol index 883e1f611..a773d8dd0 100644 --- a/packages/horizon/contracts/staking/HorizonStaking.sol +++ b/packages/horizon/contracts/staking/HorizonStaking.sol @@ -58,20 +58,20 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp error HorizonStakingInsufficientTokens(uint256 expected, uint256 available); error HorizonStakingSlippageProtection(uint256 minExpectedAmount, uint256 actualAmount); - modifier onlyAuthorized(address _serviceProvider, address _verifier) { - if (!isAuthorized(msg.sender, _serviceProvider, _verifier)) { - revert HorizonStakingNotAuthorized(msg.sender, _serviceProvider, _verifier); + modifier onlyAuthorized(address serviceProvider, address verifier) { + if (!isAuthorized(msg.sender, serviceProvider, verifier)) { + revert HorizonStakingNotAuthorized(msg.sender, serviceProvider, verifier); } _; } constructor( - address _controller, - address _stakingExtensionAddress, - address _subgraphDataServiceAddress - ) Managed(_controller) { - STAKING_EXTENSION_ADDRESS = _stakingExtensionAddress; - SUBGRAPH_DATA_SERVICE_ADDRESS = _subgraphDataServiceAddress; + address controller, + address stakingExtensionAddress, + address subgraphDataServiceAddress + ) Managed(controller) { + STAKING_EXTENSION_ADDRESS = stakingExtensionAddress; + SUBGRAPH_DATA_SERVICE_ADDRESS = subgraphDataServiceAddress; } /** @@ -111,63 +111,46 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp /** * @notice Deposit tokens on the caller's stake. - * @param _tokens Amount of tokens to stake + * @param tokens Amount of tokens to stake */ - function stake(uint256 _tokens) external override { - stakeTo(msg.sender, _tokens); - } - - /** - * @notice Deposit tokens on the service provider stake, on behalf of the service provider. - * @param _serviceProvider Address of the indexer - * @param _tokens Amount of tokens to stake - */ - function stakeTo(address _serviceProvider, uint256 _tokens) public override notPartialPaused { - if (_tokens == 0) { - revert HorizonStakingInvalidZeroTokens(); - } - - // Transfer tokens to stake from caller to this contract - TokenUtils.pullTokens(_graphToken(), msg.sender, _tokens); - - // Stake the transferred tokens - _stake(_serviceProvider, _tokens); + function stake(uint256 tokens) external override { + stakeTo(msg.sender, tokens); } function setProvisionParameters( - address _serviceProvider, - address _verifier, - uint32 _maxVerifierCut, - uint64 _thawingPeriod - ) external override notPartialPaused onlyAuthorized(_serviceProvider, _verifier) { - Provision storage prov = provisions[_serviceProvider][_verifier]; - prov.maxVerifierCutPending = _maxVerifierCut; - prov.thawingPeriodPending = _thawingPeriod; - emit ProvisionParametersStaged(_serviceProvider, _verifier, _maxVerifierCut, _thawingPeriod); - } - - function acceptProvisionParameters(address _serviceProvider) external override notPartialPaused { + address serviceProvider, + address verifier, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) external override notPartialPaused onlyAuthorized(serviceProvider, verifier) { + Provision storage prov = _provisions[serviceProvider][verifier]; + prov.maxVerifierCutPending = maxVerifierCut; + prov.thawingPeriodPending = thawingPeriod; + emit ProvisionParametersStaged(serviceProvider, verifier, maxVerifierCut, thawingPeriod); + } + + function acceptProvisionParameters(address serviceProvider) external override notPartialPaused { address verifier = msg.sender; - Provision storage prov = provisions[_serviceProvider][verifier]; + Provision storage prov = _provisions[serviceProvider][verifier]; prov.maxVerifierCut = prov.maxVerifierCutPending; prov.thawingPeriod = prov.thawingPeriodPending; - emit ProvisionParametersSet(_serviceProvider, verifier, prov.maxVerifierCut, prov.thawingPeriod); + emit ProvisionParametersSet(serviceProvider, verifier, prov.maxVerifierCut, prov.thawingPeriod); } /** * @notice Deposit tokens on the service provider stake, on behalf of the service provider, provisioned * to a specific verifier. The provider must have previously provisioned stake to that verifier. - * @param _serviceProvider Address of the indexer - * @param _verifier Address of the verifier - * @param _tokens Amount of tokens to stake + * @param serviceProvider Address of the indexer + * @param verifier Address of the verifier + * @param tokens Amount of tokens to stake */ function stakeToProvision( - address _serviceProvider, - address _verifier, - uint256 _tokens + address serviceProvider, + address verifier, + uint256 tokens ) external override notPartialPaused { - stakeTo(_serviceProvider, _tokens); - _addToProvision(_serviceProvider, _verifier, _tokens); + stakeTo(serviceProvider, tokens); + _addToProvision(serviceProvider, verifier, tokens); } /** @@ -176,24 +159,24 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp * service, where the data service is the verifier. * This function can be called by the service provider or by an operator authorized by the provider * for this specific verifier. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) - * @param _tokens The amount of tokens that will be locked and slashable - * @param _maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing - * @param _thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) + * @param tokens The amount of tokens that will be locked and slashable + * @param maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing + * @param thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision */ function provision( - address _serviceProvider, - address _verifier, - uint256 _tokens, - uint32 _maxVerifierCut, - uint64 _thawingPeriod - ) external override notPartialPaused onlyAuthorized(_serviceProvider, _verifier) { - if (getIdleStake(_serviceProvider) < _tokens) { + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) external override notPartialPaused onlyAuthorized(serviceProvider, verifier) { + if (getIdleStake(serviceProvider) < tokens) { revert HorizonStakingInsufficientCapacity(); } - _createProvision(_serviceProvider, _tokens, _verifier, _maxVerifierCut, _thawingPeriod); + _createProvision(serviceProvider, tokens, verifier, maxVerifierCut, thawingPeriod); } /** @@ -202,75 +185,73 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp * service, where the data service is the verifier. Only authorized verifiers can be used. * This function can be called by the service provider or by an operator authorized by the provider * for this specific verifier. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) - * @param _tokens The amount of tokens that will be locked and slashable - * @param _maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing - * @param _thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned (who will be able to slash the tokens) + * @param tokens The amount of tokens that will be locked and slashable + * @param maxVerifierCut The maximum cut, expressed in PPM, that a verifier can transfer instead of burning when slashing + * @param thawingPeriod The period in seconds that the tokens will be thawing before they can be removed from the provision */ function provisionLocked( - address _serviceProvider, - address _verifier, - uint256 _tokens, - uint32 _maxVerifierCut, - uint64 _thawingPeriod - ) external override notPartialPaused onlyAuthorized(_serviceProvider, _verifier) { - if (getIdleStake(_serviceProvider) < _tokens) { + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) external override notPartialPaused onlyAuthorized(serviceProvider, verifier) { + if (getIdleStake(serviceProvider) < tokens) { revert HorizonStakingInsufficientCapacity(); } - if (!allowedLockedVerifiers[_verifier]) { - revert HorizonStakingInvalidVerifier(_verifier); + if (!_allowedLockedVerifiers[verifier]) { + revert HorizonStakingInvalidVerifier(verifier); } - _createProvision(_serviceProvider, _tokens, _verifier, _maxVerifierCut, _thawingPeriod); + _createProvision(serviceProvider, tokens, verifier, maxVerifierCut, thawingPeriod); } /** * @notice Add more tokens to an existing provision. * This function can be called by the service provider or by an operator authorized by the provider * for this specific verifier. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned - * @param _tokens The amount of tokens to add to the provision + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned + * @param tokens The amount of tokens to add to the provision */ function addToProvision( - address _serviceProvider, - address _verifier, - uint256 _tokens - ) external override notPartialPaused onlyAuthorized(_serviceProvider, _verifier) { - _addToProvision(_serviceProvider, _verifier, _tokens); + address serviceProvider, + address verifier, + uint256 tokens + ) external override notPartialPaused onlyAuthorized(serviceProvider, verifier) { + _addToProvision(serviceProvider, verifier, tokens); } /** * @notice Start thawing tokens to remove them from a provision. * This function can be called by the service provider or by an operator authorized by the provider * for this specific verifier. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned - * @param _tokens The amount of tokens to thaw + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned + * @param tokens The amount of tokens to thaw */ function thaw( - address _serviceProvider, - address _verifier, - uint256 _tokens - ) external override notPartialPaused onlyAuthorized(_serviceProvider, _verifier) returns (bytes32) { - if (_tokens == 0) { + address serviceProvider, + address verifier, + uint256 tokens + ) external override notPartialPaused onlyAuthorized(serviceProvider, verifier) returns (bytes32) { + if (tokens == 0) { revert HorizonStakingInvalidZeroTokens(); } - Provision storage prov = provisions[_serviceProvider][_verifier]; - ServiceProviderInternal storage serviceProvider = serviceProviders[_serviceProvider]; - bytes32 thawRequestId = keccak256( - abi.encodePacked(_serviceProvider, _verifier, serviceProvider.nextThawRequestNonce) - ); - serviceProvider.nextThawRequestNonce += 1; - ThawRequest storage thawRequest = thawRequests[thawRequestId]; + Provision storage prov = _provisions[serviceProvider][verifier]; + ServiceProviderInternal storage sp = _serviceProviders[serviceProvider]; + bytes32 thawRequestId = keccak256(abi.encodePacked(serviceProvider, verifier, sp.nextThawRequestNonce)); + sp.nextThawRequestNonce += 1; + ThawRequest storage thawRequest = _thawRequests[thawRequestId]; - require(getProviderTokensAvailable(_serviceProvider, _verifier) >= _tokens, "insufficient tokens available"); - prov.tokensThawing = prov.tokensThawing + _tokens; + require(getProviderTokensAvailable(serviceProvider, verifier) >= tokens, "insufficient tokens available"); + prov.tokensThawing = prov.tokensThawing + tokens; if (prov.sharesThawing == 0) { - thawRequest.shares = _tokens; + thawRequest.shares = tokens; } else { - thawRequest.shares = (prov.sharesThawing * _tokens) / prov.tokensThawing; + thawRequest.shares = (prov.sharesThawing * tokens) / prov.tokensThawing; } thawRequest.thawingUntil = uint64(block.timestamp + uint256(prov.thawingPeriod)); @@ -282,51 +263,23 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp if (prov.nThawRequests == 0) { prov.firstThawRequestId = thawRequestId; } else { - thawRequests[prov.lastThawRequestId].next = thawRequestId; + _thawRequests[prov.lastThawRequestId].next = thawRequestId; } prov.lastThawRequestId = thawRequestId; prov.nThawRequests += 1; - emit ProvisionThawInitiated(_serviceProvider, _verifier, _tokens, thawRequest.thawingUntil, thawRequestId); + emit ProvisionThawInitiated(serviceProvider, verifier, tokens, thawRequest.thawingUntil, thawRequestId); return thawRequestId; } - /** - * @notice Get the amount of service provider's tokens in a provision that have finished thawing - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned - */ - function getThawedTokens(address _serviceProvider, address _verifier) external view returns (uint256) { - Provision storage prov = provisions[_serviceProvider][_verifier]; - if (prov.nThawRequests == 0) { - return 0; - } - bytes32 thawRequestId = prov.firstThawRequestId; - uint256 tokens = 0; - while (thawRequestId != bytes32(0)) { - ThawRequest storage thawRequest = thawRequests[thawRequestId]; - if (thawRequest.thawingUntil <= block.timestamp) { - tokens += (thawRequest.shares * prov.tokensThawing) / prov.sharesThawing; - } else { - break; - } - thawRequestId = thawRequest.next; - } - return tokens; - } - // moves thawed stake from a provision back into the provider's available stake - function deprovision( - address _serviceProvider, - address _verifier, - uint256 _tokens - ) external override notPartialPaused { - require(isAuthorized(msg.sender, _serviceProvider, _verifier), "!auth"); - if (_tokens == 0) { + function deprovision(address serviceProvider, address verifier, uint256 tokens) external override notPartialPaused { + require(isAuthorized(msg.sender, serviceProvider, verifier), "!auth"); + if (tokens == 0) { revert HorizonStakingInvalidZeroTokens(); } - _fulfillThawRequests(_serviceProvider, _verifier, _tokens); + _fulfillThawRequests(serviceProvider, verifier, tokens); } /** @@ -334,49 +287,49 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp * This function can be called by the service provider or by an operator authorized by the provider * for the two corresponding verifiers. * The provider must have previously provisioned tokens to the new verifier. - * @param _serviceProvider The service provider address - * @param _oldVerifier The verifier address for which the tokens are currently provisioned - * @param _newVerifier The verifier address for which the tokens will be provisioned - * @param _tokens The amount of tokens to move + * @param serviceProvider The service provider address + * @param oldVerifier The verifier address for which the tokens are currently provisioned + * @param newVerifier The verifier address for which the tokens will be provisioned + * @param tokens The amount of tokens to move */ function reprovision( - address _serviceProvider, - address _oldVerifier, - address _newVerifier, - uint256 _tokens + address serviceProvider, + address oldVerifier, + address newVerifier, + uint256 tokens ) external override notPartialPaused - onlyAuthorized(_serviceProvider, _oldVerifier) - onlyAuthorized(_serviceProvider, _newVerifier) + onlyAuthorized(serviceProvider, oldVerifier) + onlyAuthorized(serviceProvider, newVerifier) { - _fulfillThawRequests(_serviceProvider, _oldVerifier, _tokens); - _addToProvision(_serviceProvider, _newVerifier, _tokens); + _fulfillThawRequests(serviceProvider, oldVerifier, tokens); + _addToProvision(serviceProvider, newVerifier, tokens); } /** * @notice Move idle stake back to the owner's account. * If tokens were thawing they must be deprovisioned first. * Stake is removed from the protocol. - * @param _tokens Amount of tokens to unstake + * @param tokens Amount of tokens to unstake */ - function unstake(uint256 _tokens) external override notPaused { + function unstake(uint256 tokens) external override notPaused { address serviceProvider = msg.sender; - if (_tokens == 0) { + if (tokens == 0) { revert HorizonStakingInvalidZeroTokens(); } - if (getIdleStake(serviceProvider) < _tokens) { + if (getIdleStake(serviceProvider) < tokens) { revert HorizonStakingInsufficientCapacity(); } - ServiceProviderInternal storage sp = serviceProviders[serviceProvider]; + ServiceProviderInternal storage sp = _serviceProviders[serviceProvider]; uint256 stakedTokens = sp.tokensStaked; // Check that the indexer's stake minus the tokens to unstake is sufficient // to cover existing allocations // TODO this is only needed until legacy allocations are closed, // so we should remove it after the transition period - if ((stakedTokens - _tokens) < sp.__DEPRECATED_tokensAllocated) { + if ((stakedTokens - tokens) < sp.__DEPRECATED_tokensAllocated) { revert HorizonStakingInsufficientCapacityForLegacyAllocations(); } @@ -386,9 +339,9 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp // and set the thawing period to 0. uint256 lockingPeriod = __DEPRECATED_thawingPeriod; if (lockingPeriod == 0) { - sp.tokensStaked = stakedTokens - _tokens; - TokenUtils.pushTokens(_graphToken(), serviceProvider, _tokens); - emit StakeWithdrawn(serviceProvider, _tokens); + sp.tokensStaked = stakedTokens - tokens; + TokenUtils.pushTokens(_graphToken(), serviceProvider, tokens); + emit StakeWithdrawn(serviceProvider, tokens); } else { // Before locking more tokens, withdraw any unlocked ones if possible if (sp.__DEPRECATED_tokensLockedUntil != 0 && block.number >= sp.__DEPRECATED_tokensLockedUntil) { @@ -401,12 +354,12 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp MathUtils.diffOrZero(sp.__DEPRECATED_tokensLockedUntil, block.number), // Remaining thawing period sp.__DEPRECATED_tokensLocked, // Weighted by remaining unstaked tokens lockingPeriod, // Thawing period - _tokens // Weighted by new tokens to unstake + tokens // Weighted by new tokens to unstake ); } // Update balances - sp.__DEPRECATED_tokensLocked = sp.__DEPRECATED_tokensLocked + _tokens; + sp.__DEPRECATED_tokensLocked = sp.__DEPRECATED_tokensLocked + tokens; sp.__DEPRECATED_tokensLockedUntil = block.number + lockingPeriod; emit StakeLocked(serviceProvider, sp.__DEPRECATED_tokensLocked, sp.__DEPRECATED_tokensLockedUntil); } @@ -417,54 +370,54 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp * the provider has provisioned stake, and up to the amount of tokens they have provisioned. * @dev If delegation slashing is disabled, and the amount of tokens is more than the * provider's provisioned self-stake, the delegation slashing is skipped without reverting. - * @param _serviceProvider The service provider to slash - * @param _tokens The amount of tokens to slash - * @param _verifierCutAmount The amount of tokens to transfer instead of burning - * @param _verifierCutDestination The address to transfer the verifier cut to + * @param serviceProvider The service provider to slash + * @param tokens The amount of tokens to slash + * @param verifierCutAmount The amount of tokens to transfer instead of burning + * @param verifierCutDestination The address to transfer the verifier cut to */ function slash( - address _serviceProvider, - uint256 _tokens, - uint256 _verifierCutAmount, - address _verifierCutDestination + address serviceProvider, + uint256 tokens, + uint256 verifierCutAmount, + address verifierCutDestination ) external override notPartialPaused { address verifier = msg.sender; - Provision storage prov = provisions[_serviceProvider][verifier]; - if (prov.tokens < _tokens) { - revert HorizonStakingInsufficientTokens(_tokens, prov.tokens); + Provision storage prov = _provisions[serviceProvider][verifier]; + if (prov.tokens < tokens) { + revert HorizonStakingInsufficientTokens(tokens, prov.tokens); } - uint256 tokensToSlash = _tokens; + uint256 tokensToSlash = tokens; uint256 providerTokensSlashed = MathUtils.min(prov.tokens, tokensToSlash); if (providerTokensSlashed > 0) { - require((prov.tokens * prov.maxVerifierCut) / MAX_PPM >= _verifierCutAmount, "verifier cut too high"); - if (_verifierCutAmount > 0) { - TokenUtils.pushTokens(_graphToken(), _verifierCutDestination, _verifierCutAmount); - emit VerifierCutSent(_serviceProvider, verifier, _verifierCutDestination, _verifierCutAmount); + require((prov.tokens * prov.maxVerifierCut) / MAX_PPM >= verifierCutAmount, "verifier cut too high"); + if (verifierCutAmount > 0) { + TokenUtils.pushTokens(_graphToken(), verifierCutDestination, verifierCutAmount); + emit VerifierCutSent(serviceProvider, verifier, verifierCutDestination, verifierCutAmount); } - TokenUtils.burnTokens(_graphToken(), providerTokensSlashed - _verifierCutAmount); + TokenUtils.burnTokens(_graphToken(), providerTokensSlashed - verifierCutAmount); uint256 provisionFractionSlashed = (providerTokensSlashed * FIXED_POINT_PRECISION) / prov.tokens; // TODO check for rounding issues prov.tokensThawing = (prov.tokensThawing * (FIXED_POINT_PRECISION - provisionFractionSlashed)) / (FIXED_POINT_PRECISION); prov.tokens = prov.tokens - providerTokensSlashed; - serviceProviders[_serviceProvider].tokensProvisioned = - serviceProviders[_serviceProvider].tokensProvisioned - + _serviceProviders[serviceProvider].tokensProvisioned = + _serviceProviders[serviceProvider].tokensProvisioned - providerTokensSlashed; - serviceProviders[_serviceProvider].tokensStaked = - serviceProviders[_serviceProvider].tokensStaked - + _serviceProviders[serviceProvider].tokensStaked = + _serviceProviders[serviceProvider].tokensStaked - providerTokensSlashed; - emit ProvisionSlashed(_serviceProvider, verifier, providerTokensSlashed); + emit ProvisionSlashed(serviceProvider, verifier, providerTokensSlashed); } tokensToSlash = tokensToSlash - providerTokensSlashed; if (tokensToSlash > 0) { DelegationPoolInternal storage pool; if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - pool = legacyDelegationPools[_serviceProvider]; + pool = _legacyDelegationPools[serviceProvider]; } else { - pool = delegationPools[_serviceProvider][verifier]; + pool = _delegationPools[serviceProvider][verifier]; } if (delegationSlashingEnabled) { require(pool.tokens >= tokensToSlash, "insufficient delegated tokens"); @@ -474,55 +427,32 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp pool.tokensThawing = (pool.tokensThawing * (FIXED_POINT_PRECISION - delegationFractionSlashed)) / FIXED_POINT_PRECISION; - emit DelegationSlashed(_serviceProvider, verifier, tokensToSlash); + emit DelegationSlashed(serviceProvider, verifier, tokensToSlash); } else { - emit DelegationSlashingSkipped(_serviceProvider, verifier, tokensToSlash); + emit DelegationSlashingSkipped(serviceProvider, verifier, tokensToSlash); } } } - /** - * @notice Check if an operator is authorized for the caller on a specific verifier / data service. - * @param _operator The address to check for auth - * @param _serviceProvider The service provider on behalf of whom they're claiming to act - * @param _verifier The verifier / data service on which they're claiming to act - */ - function isAuthorized( - address _operator, - address _serviceProvider, - address _verifier - ) public view override returns (bool) { - if (_operator == _serviceProvider) { - return true; - } - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - return legacyOperatorAuth[_serviceProvider][_operator]; - } else { - return operatorAuth[_serviceProvider][_verifier][_operator]; - } + // For backwards compatibility, delegates to the subgraph data service + // (Note this one doesn't have splippage/rounding protection!) + function delegate(address serviceProvider, uint256 tokens) external { + delegate(serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, tokens, 0); } - // staked tokens that are currently not provisioned, aka idle stake - // `getStake(serviceProvider) - ServiceProvider.tokensProvisioned` - function getIdleStake(address serviceProvider) public view override returns (uint256 tokens) { - return - serviceProviders[serviceProvider].tokensStaked - - serviceProviders[serviceProvider].tokensProvisioned - - serviceProviders[serviceProvider].__DEPRECATED_tokensLocked; + // For backwards compatibility, undelegates from the subgraph data service + function undelegate(address serviceProvider, uint256 shares) external { + undelegate(serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, shares); } - // provisioned tokens from the service provider that are not being thawed - // `Provision.tokens - Provision.tokensThawing` - function getProviderTokensAvailable( - address _serviceProvider, - address _verifier - ) public view override returns (uint256) { - return provisions[_serviceProvider][_verifier].tokens - provisions[_serviceProvider][_verifier].tokensThawing; + // For backwards compatibility, withdraws delegated tokens from the subgraph data service + function withdrawDelegated(address serviceProvider, address newServiceProvider) external { + withdrawDelegated(serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, newServiceProvider, 0); } - function setAllowedLockedVerifier(address _verifier, bool _allowed) external onlyGovernor { - allowedLockedVerifiers[_verifier] = _allowed; - emit AllowedLockedVerifierSet(_verifier, _allowed); + function setAllowedLockedVerifier(address verifier, bool allowed) external onlyGovernor { + _allowedLockedVerifiers[verifier] = allowed; + emit AllowedLockedVerifierSet(verifier, allowed); } /** @@ -534,152 +464,159 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp _withdraw(msg.sender); } - function delegate( - address _serviceProvider, - address _verifier, - uint256 _tokens, - uint256 _minSharesOut - ) public override notPartialPaused { - // Transfer tokens to stake from caller to this contract - TokenUtils.pullTokens(_graphToken(), msg.sender, _tokens); - _delegate(_serviceProvider, _verifier, _tokens, _minSharesOut); + /** + * @notice Add tokens to a delegation pool (without getting shares). + * Used by data services to pay delegation fees/rewards. + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned + * @param tokens The amount of tokens to add to the delegation pool + */ + function addToDelegationPool( + address serviceProvider, + address verifier, + uint256 tokens + ) external override notPartialPaused { + if (tokens == 0) { + revert HorizonStakingInvalidZeroTokens(); + } + DelegationPoolInternal storage pool; + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + pool = _legacyDelegationPools[serviceProvider]; + } else { + pool = _delegationPools[serviceProvider][verifier]; + } + pool.tokens = pool.tokens + tokens; + emit TokensAddedToDelegationPool(serviceProvider, verifier, tokens); } - // For backwards compatibility, delegates to the subgraph data service - // (Note this one doesn't have splippage/rounding protection!) - function delegate(address _serviceProvider, uint256 _tokens) external { - delegate(_serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, _tokens, 0); + function setDelegationSlashingEnabled(bool enabled) external override onlyGovernor { + delegationSlashingEnabled = enabled; + emit DelegationSlashingEnabled(enabled); } - // For backwards compatibility, undelegates from the subgraph data service - function undelegate(address _serviceProvider, uint256 _shares) external { - undelegate(_serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, _shares); + // To be called at the end of the transition period, to set the deprecated thawing period to 0 + function clearThawingPeriod() external onlyGovernor { + __DEPRECATED_thawingPeriod = 0; + emit ParameterUpdated("thawingPeriod"); } - // For backwards compatibility, withdraws delegated tokens from the subgraph data service - function withdrawDelegated(address _serviceProvider, address _newServiceProvider) external { - withdrawDelegated(_serviceProvider, SUBGRAPH_DATA_SERVICE_ADDRESS, _newServiceProvider, 0); + function setMaxThawingPeriod(uint64 maxThawingPeriod) external override onlyGovernor { + maxThawingPeriod = _maxThawingPeriod; + emit ParameterUpdated("maxThawingPeriod"); } - function _delegate(address _serviceProvider, address _verifier, uint256 _tokens, uint256 _minSharesOut) internal { - if (_tokens == 0) { - revert HorizonStakingInvalidZeroTokens(); - } - // TODO: remove this after L2 transfer tool for delegation is removed - if (_tokens < MIN_DELEGATION) { - revert HorizonStakingInsufficientTokens(MIN_DELEGATION, _tokens); - } - if (provisions[_serviceProvider][_verifier].tokens == 0) { - revert HorizonStakingInvalidProvision(_serviceProvider, _verifier); + /** + * @notice Get the amount of service provider's tokens in a provision that have finished thawing + * @param serviceProvider The service provider address + * @param verifier The verifier address for which the tokens are provisioned + */ + function getThawedTokens(address serviceProvider, address verifier) external view returns (uint256) { + Provision storage prov = _provisions[serviceProvider][verifier]; + if (prov.nThawRequests == 0) { + return 0; } - - DelegationPoolInternal storage pool; - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - pool = legacyDelegationPools[_serviceProvider]; - } else { - pool = delegationPools[_serviceProvider][_verifier]; + bytes32 thawRequestId = prov.firstThawRequestId; + uint256 tokens = 0; + while (thawRequestId != bytes32(0)) { + ThawRequest storage thawRequest = _thawRequests[thawRequestId]; + if (thawRequest.thawingUntil <= block.timestamp) { + tokens += (thawRequest.shares * prov.tokensThawing) / prov.sharesThawing; + } else { + break; + } + thawRequestId = thawRequest.next; } - Delegation storage delegation = pool.delegators[msg.sender]; + return tokens; + } - // Calculate shares to issue - uint256 shares = (pool.tokens == 0) ? _tokens : ((_tokens * pool.shares) / (pool.tokens - pool.tokensThawing)); - if (shares == 0 || shares < _minSharesOut) { - revert HorizonStakingSlippageProtection(_minSharesOut, shares); + /** + * @notice Deposit tokens on the service provider stake, on behalf of the service provider. + * @param serviceProvider Address of the indexer + * @param tokens Amount of tokens to stake + */ + function stakeTo(address serviceProvider, uint256 tokens) public override notPartialPaused { + if (tokens == 0) { + revert HorizonStakingInvalidZeroTokens(); } - pool.tokens = pool.tokens + _tokens; - pool.shares = pool.shares + shares; + // Transfer tokens to stake from caller to this contract + TokenUtils.pullTokens(_graphToken(), msg.sender, tokens); - delegation.shares = delegation.shares + shares; + // Stake the transferred tokens + _stake(serviceProvider, tokens); + } - emit TokensDelegated(_serviceProvider, _verifier, msg.sender, _tokens); + function delegate( + address serviceProvider, + address verifier, + uint256 tokens, + uint256 minSharesOut + ) public override notPartialPaused { + // Transfer tokens to stake from caller to this contract + TokenUtils.pullTokens(_graphToken(), msg.sender, tokens); + _delegate(serviceProvider, verifier, tokens, minSharesOut); } // undelegate tokens from a service provider // the shares are burned and replaced with shares in the thawing pool - function undelegate(address _serviceProvider, address _verifier, uint256 _shares) public override notPartialPaused { - require(_shares > 0, "!shares"); + function undelegate(address serviceProvider, address verifier, uint256 shares) public override notPartialPaused { + require(shares > 0, "!shares"); DelegationPoolInternal storage pool; - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - pool = legacyDelegationPools[_serviceProvider]; + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + pool = _legacyDelegationPools[serviceProvider]; } else { - pool = delegationPools[_serviceProvider][_verifier]; + pool = _delegationPools[serviceProvider][verifier]; } Delegation storage delegation = pool.delegators[msg.sender]; - require(delegation.shares >= _shares, "!shares-avail"); + require(delegation.shares >= shares, "!shares-avail"); - uint256 tokens = (_shares * (pool.tokens - pool.tokensThawing)) / pool.shares; + uint256 tokens = (shares * (pool.tokens - pool.tokensThawing)) / pool.shares; uint256 thawingShares = pool.tokensThawing == 0 ? tokens : ((tokens * pool.sharesThawing) / pool.tokensThawing); pool.tokensThawing = pool.tokensThawing + tokens; - pool.shares = pool.shares - _shares; + pool.shares = pool.shares - shares; pool.sharesThawing = pool.sharesThawing + thawingShares; - delegation.shares = delegation.shares - _shares; + delegation.shares = delegation.shares - shares; // TODO: remove this when L2 transfer tools are removed if (delegation.shares != 0) { uint256 remainingTokens = (delegation.shares * (pool.tokens - pool.tokensThawing)) / pool.shares; require(remainingTokens >= MIN_DELEGATION, "!minimum-delegation"); } bytes32 thawRequestId = keccak256( - abi.encodePacked(_serviceProvider, _verifier, msg.sender, delegation.nextThawRequestNonce) + abi.encodePacked(serviceProvider, verifier, msg.sender, delegation.nextThawRequestNonce) ); delegation.nextThawRequestNonce += 1; - ThawRequest storage thawRequest = thawRequests[thawRequestId]; + ThawRequest storage thawRequest = _thawRequests[thawRequestId]; thawRequest.shares = thawingShares; thawRequest.thawingUntil = uint64( - block.timestamp + uint256(provisions[_serviceProvider][_verifier].thawingPeriod) + block.timestamp + uint256(_provisions[serviceProvider][verifier].thawingPeriod) ); require(delegation.nThawRequests < MAX_THAW_REQUESTS, "max thaw requests"); if (delegation.nThawRequests == 0) { delegation.firstThawRequestId = thawRequestId; } else { - thawRequests[delegation.lastThawRequestId].next = thawRequestId; + _thawRequests[delegation.lastThawRequestId].next = thawRequestId; } delegation.lastThawRequestId = thawRequestId; unchecked { delegation.nThawRequests += 1; } - emit TokensUndelegated(_serviceProvider, _verifier, msg.sender, tokens); - } - - /** - * @notice Add tokens to a delegation pool (without getting shares). - * Used by data services to pay delegation fees/rewards. - * @param _serviceProvider The service provider address - * @param _verifier The verifier address for which the tokens are provisioned - * @param _tokens The amount of tokens to add to the delegation pool - */ - function addToDelegationPool( - address _serviceProvider, - address _verifier, - uint256 _tokens - ) external override notPartialPaused { - if (_tokens == 0) { - revert HorizonStakingInvalidZeroTokens(); - } - DelegationPoolInternal storage pool; - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - pool = legacyDelegationPools[_serviceProvider]; - } else { - pool = delegationPools[_serviceProvider][_verifier]; - } - pool.tokens = pool.tokens + _tokens; - emit TokensAddedToDelegationPool(_serviceProvider, _verifier, _tokens); + emit TokensUndelegated(serviceProvider, verifier, msg.sender, tokens); } function withdrawDelegated( - address _serviceProvider, - address _verifier, - address _newServiceProvider, - uint256 _minSharesForNewProvider + address serviceProvider, + address verifier, + address newServiceProvider, + uint256 minSharesForNewProvider ) public override notPartialPaused { DelegationPoolInternal storage pool; - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - pool = legacyDelegationPools[_serviceProvider]; + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + pool = _legacyDelegationPools[serviceProvider]; } else { - pool = delegationPools[_serviceProvider][_verifier]; + pool = _delegationPools[serviceProvider][verifier]; } Delegation storage delegation = pool.delegators[msg.sender]; uint256 thawedTokens = 0; @@ -689,13 +626,13 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp require(delegation.nThawRequests > 0, "no thaw requests"); bytes32 thawRequestId = delegation.firstThawRequestId; while (thawRequestId != bytes32(0)) { - ThawRequest storage thawRequest = thawRequests[thawRequestId]; + ThawRequest storage thawRequest = _thawRequests[thawRequestId]; if (thawRequest.thawingUntil <= block.timestamp) { uint256 tokens = (thawRequest.shares * tokensThawing) / sharesThawing; tokensThawing = tokensThawing - tokens; sharesThawing = sharesThawing - thawRequest.shares; thawedTokens = thawedTokens + tokens; - delete thawRequests[thawRequestId]; + delete _thawRequests[thawRequestId]; delegation.firstThawRequestId = thawRequest.next; delegation.nThawRequests -= 1; if (delegation.nThawRequests == 0) { @@ -711,51 +648,85 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp pool.sharesThawing = sharesThawing; pool.tokensThawing = tokensThawing; - if (_newServiceProvider != address(0)) { - _delegate(_newServiceProvider, _verifier, thawedTokens, _minSharesForNewProvider); + if (newServiceProvider != address(0)) { + _delegate(newServiceProvider, verifier, thawedTokens, minSharesForNewProvider); } else { TokenUtils.pushTokens(_graphToken(), msg.sender, thawedTokens); } - emit DelegatedTokensWithdrawn(_serviceProvider, _verifier, msg.sender, thawedTokens); + emit DelegatedTokensWithdrawn(serviceProvider, verifier, msg.sender, thawedTokens); } - function setDelegationSlashingEnabled(bool _enabled) external override onlyGovernor { - delegationSlashingEnabled = _enabled; - emit DelegationSlashingEnabled(_enabled); + /** + * @notice Check if an operator is authorized for the caller on a specific verifier / data service. + * @param operator The address to check for auth + * @param serviceProvider The service provider on behalf of whom they're claiming to act + * @param verifier The verifier / data service on which they're claiming to act + */ + function isAuthorized( + address operator, + address serviceProvider, + address verifier + ) public view override returns (bool) { + if (operator == serviceProvider) { + return true; + } + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + return _legacyOperatorAuth[serviceProvider][operator]; + } else { + return _operatorAuth[serviceProvider][verifier][operator]; + } } - // To be called at the end of the transition period, to set the deprecated thawing period to 0 - function clearThawingPeriod() external onlyGovernor { - __DEPRECATED_thawingPeriod = 0; - emit ParameterUpdated("thawingPeriod"); + // staked tokens that are currently not provisioned, aka idle stake + // `getStake(serviceProvider) - ServiceProvider.tokensProvisioned` + function getIdleStake(address serviceProvider) public view override returns (uint256 tokens) { + return + _serviceProviders[serviceProvider].tokensStaked - + _serviceProviders[serviceProvider].tokensProvisioned - + _serviceProviders[serviceProvider].__DEPRECATED_tokensLocked; } - function setMaxThawingPeriod(uint64 _maxThawingPeriod) external override onlyGovernor { - maxThawingPeriod = _maxThawingPeriod; - emit ParameterUpdated("maxThawingPeriod"); + // provisioned tokens from the service provider that are not being thawed + // `Provision.tokens - Provision.tokensThawing` + function getProviderTokensAvailable( + address serviceProvider, + address verifier + ) public view override returns (uint256) { + return _provisions[serviceProvider][verifier].tokens - _provisions[serviceProvider][verifier].tokensThawing; } - /** - * @dev Withdraw indexer tokens once the thawing period has passed. - * @param _indexer Address of indexer to withdraw funds from - */ - function _withdraw(address _indexer) private { - // Get tokens available for withdraw and update balance - ServiceProviderInternal storage sp = serviceProviders[_indexer]; - uint256 tokensToWithdraw = sp.__DEPRECATED_tokensLocked; - require(tokensToWithdraw > 0, "!tokens"); - require(block.number >= sp.__DEPRECATED_tokensLockedUntil, "locked"); + function _delegate(address _serviceProvider, address _verifier, uint256 _tokens, uint256 _minSharesOut) internal { + if (_tokens == 0) { + revert HorizonStakingInvalidZeroTokens(); + } + // TODO: remove this after L2 transfer tool for delegation is removed + if (_tokens < MIN_DELEGATION) { + revert HorizonStakingInsufficientTokens(MIN_DELEGATION, _tokens); + } + if (_provisions[_serviceProvider][_verifier].tokens == 0) { + revert HorizonStakingInvalidProvision(_serviceProvider, _verifier); + } - // Reset locked tokens - sp.__DEPRECATED_tokensLocked = 0; - sp.__DEPRECATED_tokensLockedUntil = 0; + DelegationPoolInternal storage pool; + if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + pool = _legacyDelegationPools[_serviceProvider]; + } else { + pool = _delegationPools[_serviceProvider][_verifier]; + } + Delegation storage delegation = pool.delegators[msg.sender]; - sp.tokensStaked = sp.tokensStaked - tokensToWithdraw; + // Calculate shares to issue + uint256 shares = (pool.tokens == 0) ? _tokens : ((_tokens * pool.shares) / (pool.tokens - pool.tokensThawing)); + if (shares == 0 || shares < _minSharesOut) { + revert HorizonStakingSlippageProtection(_minSharesOut, shares); + } - // Return tokens to the indexer - TokenUtils.pushTokens(_graphToken(), _indexer, tokensToWithdraw); + pool.tokens = pool.tokens + _tokens; + pool.shares = pool.shares + shares; - emit StakeWithdrawn(_indexer, tokensToWithdraw); + delegation.shares = delegation.shares + shares; + + emit TokensDelegated(_serviceProvider, _verifier, msg.sender, _tokens); } /** @@ -770,8 +741,8 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp ) internal { require(_tokens >= MIN_PROVISION_SIZE, "!tokens"); require(_maxVerifierCut <= MAX_MAX_VERIFIER_CUT, "maxVerifierCut too high"); - require(_thawingPeriod <= maxThawingPeriod, "thawingPeriod too high"); - provisions[_serviceProvider][_verifier] = Provision({ + require(_thawingPeriod <= _maxThawingPeriod, "thawingPeriod too high"); + _provisions[_serviceProvider][_verifier] = Provision({ tokens: _tokens, tokensThawing: 0, sharesThawing: 0, @@ -785,26 +756,26 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp thawingPeriodPending: _thawingPeriod }); - ServiceProviderInternal storage sp = serviceProviders[_serviceProvider]; + ServiceProviderInternal storage sp = _serviceProviders[_serviceProvider]; sp.tokensProvisioned = sp.tokensProvisioned + _tokens; emit ProvisionCreated(_serviceProvider, _verifier, _tokens, _maxVerifierCut, _thawingPeriod); } function _fulfillThawRequests(address _serviceProvider, address _verifier, uint256 _tokens) internal { - Provision storage prov = provisions[_serviceProvider][_verifier]; + Provision storage prov = _provisions[_serviceProvider][_verifier]; uint256 tokensRemaining = _tokens; uint256 sharesThawing = prov.sharesThawing; uint256 tokensThawing = prov.tokensThawing; while (tokensRemaining > 0) { require(prov.nThawRequests > 0, "not enough thawed tokens"); bytes32 thawRequestId = prov.firstThawRequestId; - ThawRequest storage thawRequest = thawRequests[thawRequestId]; + ThawRequest storage thawRequest = _thawRequests[thawRequestId]; require(thawRequest.thawingUntil <= block.timestamp, "thawing period not over"); uint256 thawRequestTokens = (thawRequest.shares * tokensThawing) / sharesThawing; if (thawRequestTokens <= tokensRemaining) { tokensRemaining = tokensRemaining - thawRequestTokens; - delete thawRequests[thawRequestId]; + delete _thawRequests[thawRequestId]; prov.firstThawRequestId = thawRequest.next; prov.nThawRequests -= 1; tokensThawing = tokensThawing - thawRequestTokens; @@ -829,11 +800,11 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp prov.sharesThawing = sharesThawing; prov.tokensThawing = tokensThawing; prov.tokens = prov.tokens - _tokens; - serviceProviders[_serviceProvider].tokensProvisioned -= _tokens; + _serviceProviders[_serviceProvider].tokensProvisioned -= _tokens; } function _addToProvision(address _serviceProvider, address _verifier, uint256 _tokens) internal { - Provision storage prov = provisions[_serviceProvider][_verifier]; + Provision storage prov = _provisions[_serviceProvider][_verifier]; if (_tokens == 0) { revert HorizonStakingInvalidZeroTokens(); } @@ -845,8 +816,8 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp } prov.tokens = prov.tokens + _tokens; - serviceProviders[_serviceProvider].tokensProvisioned = - serviceProviders[_serviceProvider].tokensProvisioned + + _serviceProviders[_serviceProvider].tokensProvisioned = + _serviceProviders[_serviceProvider].tokensProvisioned + _tokens; emit ProvisionIncreased(_serviceProvider, _verifier, _tokens); } @@ -859,7 +830,7 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp */ function _stake(address _serviceProvider, uint256 _tokens) internal { // Deposit tokens into the indexer stake - serviceProviders[_serviceProvider].tokensStaked = serviceProviders[_serviceProvider].tokensStaked + _tokens; + _serviceProviders[_serviceProvider].tokensStaked = _serviceProviders[_serviceProvider].tokensStaked + _tokens; emit StakeDeposited(_serviceProvider, _tokens); } @@ -867,4 +838,27 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUp function _graphToken() internal view returns (IGraphToken) { return IGraphToken(GRAPH_TOKEN); } + + /** + * @dev Withdraw indexer tokens once the thawing period has passed. + * @param _indexer Address of indexer to withdraw funds from + */ + function _withdraw(address _indexer) private { + // Get tokens available for withdraw and update balance + ServiceProviderInternal storage sp = _serviceProviders[_indexer]; + uint256 tokensToWithdraw = sp.__DEPRECATED_tokensLocked; + require(tokensToWithdraw > 0, "!tokens"); + require(block.number >= sp.__DEPRECATED_tokensLockedUntil, "locked"); + + // Reset locked tokens + sp.__DEPRECATED_tokensLocked = 0; + sp.__DEPRECATED_tokensLockedUntil = 0; + + sp.tokensStaked = sp.tokensStaked - tokensToWithdraw; + + // Return tokens to the indexer + TokenUtils.pushTokens(_graphToken(), _indexer, tokensToWithdraw); + + emit StakeWithdrawn(_indexer, tokensToWithdraw); + } } diff --git a/packages/horizon/contracts/staking/HorizonStakingExtension.sol b/packages/horizon/contracts/staking/HorizonStakingExtension.sol index 0183e73f9..12a685277 100644 --- a/packages/horizon/contracts/staking/HorizonStakingExtension.sol +++ b/packages/horizon/contracts/staking/HorizonStakingExtension.sol @@ -29,9 +29,9 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki } constructor( - address _controller, - address _subgraphDataServiceAddress - ) StakingBackwardsCompatibility(_controller, _subgraphDataServiceAddress) {} + address controller, + address subgraphDataServiceAddress + ) StakingBackwardsCompatibility(controller, subgraphDataServiceAddress) {} /** * @notice Receive ETH into the Staking contract: this will always revert @@ -41,78 +41,28 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki revert("RECEIVE_ETH_NOT_ALLOWED"); } - // total staked tokens to the provider - // `ServiceProvider.tokensStaked - function getStake(address serviceProvider) external view override returns (uint256) { - return serviceProviders[serviceProvider].tokensStaked; - } - - // provisioned tokens from delegators that are not being thawed - // `Provision.delegatedTokens - Provision.delegatedTokensThawing` - function getDelegatedTokensAvailable( - address _serviceProvider, - address _verifier - ) public view override returns (uint256) { - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - return - legacyDelegationPools[_serviceProvider].tokens - - (legacyDelegationPools[_serviceProvider].tokensThawing); - } - return - delegationPools[_serviceProvider][_verifier].tokens - - (delegationPools[_serviceProvider][_verifier].tokensThawing); - } - - // provisioned tokens that are not being thawed (including provider tokens and delegation) - function getTokensAvailable( - address _serviceProvider, - address _verifier, - uint32 _delegationRatio - ) external view override returns (uint256) { - uint256 providerTokens = provisions[_serviceProvider][_verifier].tokens; - uint256 tokensDelegatedMax = providerTokens * (uint256(_delegationRatio)); - uint256 tokensDelegatedCapacity = MathUtils.min( - getDelegatedTokensAvailable(_serviceProvider, _verifier), - tokensDelegatedMax - ); - return providerTokens - provisions[_serviceProvider][_verifier].tokensThawing + tokensDelegatedCapacity; - } - - function getServiceProvider(address serviceProvider) external view override returns (ServiceProvider memory) { - ServiceProvider memory sp; - ServiceProviderInternal storage spInternal = serviceProviders[serviceProvider]; - sp.tokensStaked = spInternal.tokensStaked; - sp.tokensProvisioned = spInternal.tokensProvisioned; - sp.nextThawRequestNonce = spInternal.nextThawRequestNonce; - return sp; - } - /** * @notice Authorize or unauthorize an address to be an operator for the caller on a data service. - * @param _operator Address to authorize or unauthorize - * @param _verifier The verifier / data service on which they'll be allowed to operate - * @param _allowed Whether the operator is authorized or not + * @param operator Address to authorize or unauthorize + * @param verifier The verifier / data service on which they'll be allowed to operate + * @param allowed Whether the operator is authorized or not */ - function setOperator(address _operator, address _verifier, bool _allowed) external override { - require(_operator != msg.sender, "operator == sender"); - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - legacyOperatorAuth[msg.sender][_operator] = _allowed; + function setOperator(address operator, address verifier, bool allowed) external override { + require(operator != msg.sender, "operator == sender"); + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + _legacyOperatorAuth[msg.sender][operator] = allowed; } else { - operatorAuth[msg.sender][_verifier][_operator] = _allowed; + _operatorAuth[msg.sender][verifier][operator] = allowed; } - emit OperatorSet(msg.sender, _operator, _verifier, _allowed); + emit OperatorSet(msg.sender, operator, verifier, allowed); } // for vesting contracts - function setOperatorLocked(address _operator, address _verifier, bool _allowed) external override { - require(_operator != msg.sender, "operator == sender"); - require(allowedLockedVerifiers[_verifier], "VERIFIER_NOT_ALLOWED"); - operatorAuth[msg.sender][_verifier][_operator] = _allowed; - emit OperatorSet(msg.sender, _operator, _verifier, _allowed); - } - - function getMaxThawingPeriod() external view override returns (uint64) { - return maxThawingPeriod; + function setOperatorLocked(address operator, address verifier, bool allowed) external override { + require(operator != msg.sender, "operator == sender"); + require(_allowedLockedVerifiers[verifier], "VERIFIER_NOT_ALLOWED"); + _operatorAuth[msg.sender][verifier][operator] = allowed; + emit OperatorSet(msg.sender, operator, verifier, allowed); } /** @@ -120,45 +70,89 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki * @dev The encoded _data can contain information about an indexer's stake * or a delegator's delegation. * See L1MessageCodes in IL2Staking for the supported messages. - * @param _from Token sender in L1 - * @param _amount Amount of tokens that were transferred - * @param _data ABI-encoded callhook data which must include a uint8 code and either a ReceiveIndexerStakeData or ReceiveDelegationData struct. + * @param from Token sender in L1 + * @param amount Amount of tokens that were transferred + * @param data ABI-encoded callhook data which must include a uint8 code and either a ReceiveIndexerStakeData or ReceiveDelegationData struct. */ function onTokenTransfer( - address _from, - uint256 _amount, - bytes calldata _data + address from, + uint256 amount, + bytes calldata data ) external override notPartialPaused onlyL2Gateway { - require(_from == counterpartStakingAddress, "ONLY_L1_STAKING_THROUGH_BRIDGE"); - (uint8 code, bytes memory functionData) = abi.decode(_data, (uint8, bytes)); + require(from == _counterpartStakingAddress, "ONLY_L1_STAKING_THROUGH_BRIDGE"); + (uint8 code, bytes memory functionData) = abi.decode(data, (uint8, bytes)); if (code == uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_INDEXER_STAKE_CODE)) { IL2StakingTypes.ReceiveIndexerStakeData memory indexerData = abi.decode( functionData, (IL2StakingTypes.ReceiveIndexerStakeData) ); - _receiveIndexerStake(_amount, indexerData); + _receiveIndexerStake(amount, indexerData); } else if (code == uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE)) { IL2StakingTypes.ReceiveDelegationData memory delegationData = abi.decode( functionData, (IL2StakingTypes.ReceiveDelegationData) ); - _receiveDelegation(_amount, delegationData); + _receiveDelegation(amount, delegationData); } else { revert("INVALID_CODE"); } } + function setDelegationFeeCut( + address serviceProvider, + address verifier, + uint256 feeType, + uint256 feeCut + ) external override { + delegationFeeCut[serviceProvider][verifier][feeType] = feeCut; + emit DelegationFeeCutSet(serviceProvider, verifier, feeType, feeCut); + } + + // total staked tokens to the provider + // `ServiceProvider.tokensStaked + function getStake(address serviceProvider) external view override returns (uint256) { + return _serviceProviders[serviceProvider].tokensStaked; + } + + // provisioned tokens that are not being thawed (including provider tokens and delegation) + function getTokensAvailable( + address serviceProvider, + address verifier, + uint32 delegationRatio + ) external view override returns (uint256) { + uint256 providerTokens = _provisions[serviceProvider][verifier].tokens; + uint256 tokensDelegatedMax = providerTokens * (uint256(delegationRatio)); + uint256 tokensDelegatedCapacity = MathUtils.min( + getDelegatedTokensAvailable(serviceProvider, verifier), + tokensDelegatedMax + ); + return providerTokens - _provisions[serviceProvider][verifier].tokensThawing + tokensDelegatedCapacity; + } + + function getServiceProvider(address serviceProvider) external view override returns (ServiceProvider memory) { + ServiceProvider memory sp; + ServiceProviderInternal storage spInternal = _serviceProviders[serviceProvider]; + sp.tokensStaked = spInternal.tokensStaked; + sp.tokensProvisioned = spInternal.tokensProvisioned; + sp.nextThawRequestNonce = spInternal.nextThawRequestNonce; + return sp; + } + + function getMaxThawingPeriod() external view override returns (uint64) { + return _maxThawingPeriod; + } + function getDelegationPool( - address _serviceProvider, - address _verifier + address serviceProvider, + address verifier ) external view override returns (DelegationPool memory) { DelegationPool memory pool; DelegationPoolInternal storage poolInternal; - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - poolInternal = legacyDelegationPools[_serviceProvider]; + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + poolInternal = _legacyDelegationPools[serviceProvider]; } else { - poolInternal = delegationPools[_serviceProvider][_verifier]; + poolInternal = _delegationPools[serviceProvider][verifier]; } pool.tokens = poolInternal.tokens; pool.shares = poolInternal.shares; @@ -168,44 +162,47 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki } function getDelegation( - address _delegator, - address _serviceProvider, - address _verifier + address delegator, + address serviceProvider, + address verifier ) external view override returns (Delegation memory) { - if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { - return legacyDelegationPools[_serviceProvider].delegators[_delegator]; + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + return _legacyDelegationPools[serviceProvider].delegators[delegator]; } else { - return delegationPools[_serviceProvider][_verifier].delegators[_delegator]; + return _delegationPools[serviceProvider][verifier].delegators[delegator]; } } - function getThawRequest(bytes32 _thawRequestId) external view returns (ThawRequest memory) { - return thawRequests[_thawRequestId]; - } - - function getProvision( - address _serviceProvider, - address _verifier - ) external view override returns (Provision memory) { - return provisions[_serviceProvider][_verifier]; + function getThawRequest(bytes32 thawRequestId) external view returns (ThawRequest memory) { + return _thawRequests[thawRequestId]; } - function setDelegationFeeCut( - address _serviceProvider, - address _verifier, - uint256 _feeType, - uint256 _feeCut - ) external override { - delegationFeeCut[_serviceProvider][_verifier][_feeType] = _feeCut; - emit DelegationFeeCutSet(_serviceProvider, _verifier, _feeType, _feeCut); + function getProvision(address serviceProvider, address verifier) external view override returns (Provision memory) { + return _provisions[serviceProvider][verifier]; } function getDelegationFeeCut( - address _serviceProvider, - address _verifier, - uint256 _feeType + address serviceProvider, + address verifier, + uint256 feeType ) external view override returns (uint256) { - return delegationFeeCut[_serviceProvider][_verifier][_feeType]; + return delegationFeeCut[serviceProvider][verifier][feeType]; + } + + // provisioned tokens from delegators that are not being thawed + // `Provision.delegatedTokens - Provision.delegatedTokensThawing` + function getDelegatedTokensAvailable( + address serviceProvider, + address verifier + ) public view override returns (uint256) { + if (verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) { + return + _legacyDelegationPools[serviceProvider].tokens - + (_legacyDelegationPools[serviceProvider].tokensThawing); + } + return + _delegationPools[serviceProvider][verifier].tokens - + (_delegationPools[serviceProvider][verifier].tokensThawing); } /** @@ -219,9 +216,9 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki uint256 _amount, IL2StakingTypes.ReceiveIndexerStakeData memory _indexerData ) internal { - address _indexer = _indexerData.indexer; + address indexer = _indexerData.indexer; // Deposit tokens into the indexer stake - _stake(_indexer, _amount); + _stake(indexer, _amount); } /** @@ -237,7 +234,7 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStaki IL2StakingTypes.ReceiveDelegationData memory _delegationData ) internal { // Get the delegation pool of the indexer - DelegationPoolInternal storage pool = legacyDelegationPools[_delegationData.indexer]; + DelegationPoolInternal storage pool = _legacyDelegationPools[_delegationData.indexer]; Delegation storage delegation = pool.delegators[_delegationData.delegator]; // Calculate shares to issue (without applying any delegation tax) diff --git a/packages/horizon/contracts/staking/HorizonStakingStorage.sol b/packages/horizon/contracts/staking/HorizonStakingStorage.sol index 80e25f6ae..a640568ef 100644 --- a/packages/horizon/contracts/staking/HorizonStakingStorage.sol +++ b/packages/horizon/contracts/staking/HorizonStakingStorage.sol @@ -7,6 +7,9 @@ import { IHorizonStakingTypes } from "../interfaces/IHorizonStakingTypes.sol"; import { Managed } from "./utilities/Managed.sol"; +// TODO: create custom var-name-mixedcase +/* solhint-disable var-name-mixedcase */ + /** * @title HorizonStakingV1Storage * @notice This contract holds all the storage variables for the Staking contract, version 1 @@ -34,7 +37,7 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { uint32 internal __DEPRECATED_protocolPercentage; /// @dev Period for allocation to be finalized - uint32 private __DEPRECATED_channelDisputeEpochs; // solhint-disable-line var-name-mixedcase + uint32 private __DEPRECATED_channelDisputeEpochs; /// @dev Maximum allocation time. Deprecated, allocations now live on the subgraph service contract. uint32 internal __DEPRECATED_maxAllocationEpochs; @@ -50,25 +53,26 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { uint32 internal __DEPRECATED_alphaDenominator; /// @dev Service provider stakes : serviceProviderAddress => ServiceProvider - mapping(address => ServiceProviderInternal) internal serviceProviders; + mapping(address serviceProvider => ServiceProviderInternal details) internal _serviceProviders; /// @dev Allocations : allocationID => Allocation /// Deprecated, now applied on the SubgraphService - mapping(address => IStakingBackwardsCompatibility.Allocation) internal __DEPRECATED_allocations; + mapping(address allocationId => IStakingBackwardsCompatibility.Allocation allocation) + internal __DEPRECATED_allocations; /// @dev Subgraph Allocations: subgraphDeploymentID => tokens /// Deprecated, now applied on the SubgraphService - mapping(bytes32 => uint256) internal __DEPRECATED_subgraphAllocations; + mapping(bytes32 subgraphDeploymentId => uint256 tokens) internal __DEPRECATED_subgraphAllocations; /// @dev Rebate pools : epoch => Pool /// Deprecated. - mapping(uint256 => uint256) private __DEPRECATED_rebates; // solhint-disable-line var-name-mixedcase + mapping(uint256 epoch => uint256 rebates) private __DEPRECATED_rebates; // -- Slashing -- /// @dev List of addresses allowed to slash stakes /// Deprecated, now each verifier can slash the corresponding provision. - mapping(address => bool) internal __DEPRECATED_slashers; + mapping(address slasher => bool allowed) internal __DEPRECATED_slashers; // -- Delegation -- @@ -77,7 +81,7 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { uint32 internal __DEPRECATED_delegationRatio; /// @dev Time in blocks an indexer needs to wait to change delegation parameters (deprecated) - uint32 internal __DEPRECATED_delegationParametersCooldown; // solhint-disable-line var-name-mixedcase + uint32 internal __DEPRECATED_delegationParametersCooldown; /// @dev Time in epochs a delegator needs to wait to withdraw delegated stake /// Deprecated, now only enforced during a transition period @@ -90,24 +94,24 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { /// @dev Delegation pools : serviceProvider => DelegationPoolInternal /// These are for the subgraph data service. - mapping(address => DelegationPoolInternal) internal legacyDelegationPools; + mapping(address serviceProvider => DelegationPoolInternal delegationPool) internal _legacyDelegationPools; // -- Operators -- /// @dev Legacy operator auth : indexer => operator => is authorized - mapping(address => mapping(address => bool)) internal legacyOperatorAuth; + mapping(address legacyOperator => mapping(address serviceProvider => bool authorized)) internal _legacyOperatorAuth; // -- Asset Holders -- /// @dev DEPRECATED: Allowed AssetHolders: assetHolder => is allowed - mapping(address => bool) private __DEPRECATED_assetHolders; // solhint-disable-line var-name-mixedcase + mapping(address assetHolder => bool allowed) private __DEPRECATED_assetHolders; /// @dev Destination of accrued rewards : beneficiary => rewards destination /// Deprecated, defined by each data service as needed - mapping(address => address) internal __DEPRECATED_rewardsDestination; + mapping(address serviceProvider => address rewardsDestination) internal __DEPRECATED_rewardsDestination; /// @dev Address of the counterpart Staking contract on L1/L2 - address internal counterpartStakingAddress; + address internal _counterpartStakingAddress; /// @dev Address of the StakingExtension implementation address internal __DEPRECATED_extensionImpl; @@ -116,11 +120,11 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { uint32 internal __DEPRECATED_lambdaDenominator; /// Maximum thawing period, in seconds, for a provision - uint64 internal maxThawingPeriod; + uint64 internal _maxThawingPeriod; /// @dev Provisions from each service provider for each data service /// ServiceProvider => Verifier => Provision - mapping(address => mapping(address => Provision)) internal provisions; + mapping(address serviceProvider => mapping(address verifier => Provision provision)) internal _provisions; /// @dev Delegation fee cuts for each service provider on each provision, by fee type: /// ServiceProvider => Verifier => Fee Type => Fee Cut. @@ -128,19 +132,22 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes { /// This is in PPM and is the cut taken by the indexer from the fees that correspond to delegators. /// (based on stake vs delegated stake proportion). /// The cuts are applied in GraphPayments so apply to all data services that use it. - mapping(address => mapping(address => mapping(uint256 => uint256))) public delegationFeeCut; + mapping(address serviceProvider => mapping(address verifier => mapping(uint256 feeType => uint256 feeCut))) + public delegationFeeCut; - mapping(bytes32 => ThawRequest) internal thawRequests; + mapping(bytes32 thawRequestId => ThawRequest thawRequest) internal _thawRequests; // indexer => verifier => operator => authorized - mapping(address => mapping(address => mapping(address => bool))) internal operatorAuth; + mapping(address serviceProvider => mapping(address verifier => mapping(address operator => bool authorized))) + internal _operatorAuth; // governance enables or disables delegation slashing with this flag bool public delegationSlashingEnabled; // delegation pools for each service provider and verifier - mapping(address => mapping(address => DelegationPoolInternal)) internal delegationPools; + mapping(address serviceProvider => mapping(address verifier => DelegationPoolInternal delegationPool)) + internal _delegationPools; // allowed verifiers for locked provisions (i.e. from GraphTokenLockWallets) - mapping(address => bool) internal allowedLockedVerifiers; + mapping(address verifier => bool allowed) internal _allowedLockedVerifiers; } diff --git a/packages/horizon/contracts/staking/StakingBackwardsCompatibility.sol b/packages/horizon/contracts/staking/StakingBackwardsCompatibility.sol index 0d8c2749e..ea51f372c 100644 --- a/packages/horizon/contracts/staking/StakingBackwardsCompatibility.sol +++ b/packages/horizon/contracts/staking/StakingBackwardsCompatibility.sol @@ -38,18 +38,18 @@ abstract contract StakingBackwardsCompatibility is address public immutable SUBGRAPH_DATA_SERVICE_ADDRESS; - constructor(address _controller, address _subgraphDataServiceAddress) Managed(_controller) { - SUBGRAPH_DATA_SERVICE_ADDRESS = _subgraphDataServiceAddress; + constructor(address controller, address subgraphDataServiceAddress) Managed(controller) { + SUBGRAPH_DATA_SERVICE_ADDRESS = subgraphDataServiceAddress; } /** * @notice Set the address of the counterpart (L1 or L2) staking contract. * @dev This function can only be called by the governor. * TODO: Remove after L2 transition period - * @param _counterpart Address of the counterpart staking contract in the other chain, without any aliasing. + * @param counterpart Address of the counterpart staking contract in the other chain, without any aliasing. */ - function setCounterpartStakingAddress(address _counterpart) external override onlyGovernor { - counterpartStakingAddress = _counterpart; + function setCounterpartStakingAddress(address counterpart) external override onlyGovernor { + _counterpartStakingAddress = counterpart; emit ParameterUpdated("counterpartStakingAddress"); } @@ -59,11 +59,11 @@ abstract contract StakingBackwardsCompatibility is * Presenting a bad proof is subject to slashable condition. * To opt out of rewards set _poi to 0x0 * @dev TODO: Remove after Horizon transition period - * @param _allocationID The allocation identifier - * @param _poi Proof of indexing submitted for the allocated period + * @param allocationID The allocation identifier + * @param poi Proof of indexing submitted for the allocated period */ - function closeAllocation(address _allocationID, bytes32 _poi) external override notPaused { - _closeAllocation(_allocationID, _poi); + function closeAllocation(address allocationID, bytes32 poi) external override notPaused { + _closeAllocation(allocationID, poi); } /** @@ -73,27 +73,27 @@ abstract contract StakingBackwardsCompatibility is * This implementation allows collecting multiple times on the same allocation, keeping track of the * total amount rebated, the total amount collected and compensating the indexer for the difference. * TODO: Remove after Horizon transition period - * @param _tokens Amount of tokens to collect - * @param _allocationID Allocation where the tokens will be assigned + * @param tokens Amount of tokens to collect + * @param allocationID Allocation where the tokens will be assigned */ - function collect(uint256 _tokens, address _allocationID) external override { + function collect(uint256 tokens, address allocationID) external override { // Allocation identifier validation - require(_allocationID != address(0), "!alloc"); + require(allocationID != address(0), "!alloc"); // Allocation must exist - AllocationState allocState = _getAllocationState(_allocationID); + AllocationState allocState = _getAllocationState(allocationID); require(allocState != AllocationState.Null, "!collect"); // If the query fees are zero, we don't want to revert // but we also don't need to do anything, so just return - if (_tokens == 0) { + if (tokens == 0) { return; } - Allocation storage alloc = __DEPRECATED_allocations[_allocationID]; + Allocation storage alloc = __DEPRECATED_allocations[allocationID]; bytes32 subgraphDeploymentID = alloc.subgraphDeploymentID; - uint256 queryFees = _tokens; // Tokens collected from the channel + uint256 queryFees = tokens; // Tokens collected from the channel uint256 protocolTax = 0; // Tokens burnt as protocol tax uint256 curationFees = 0; // Tokens distributed to curators as curation fees uint256 queryRebates = 0; // Tokens to distribute to indexer @@ -160,9 +160,9 @@ abstract contract StakingBackwardsCompatibility is msg.sender, alloc.indexer, subgraphDeploymentID, - _allocationID, + allocationID, _epochManager().currentEpoch(), - _tokens, + tokens, protocolTax, curationFees, queryFees, @@ -174,69 +174,172 @@ abstract contract StakingBackwardsCompatibility is /** * @notice Return if allocationID is used. * @dev TODO: Remove after Horizon transition period - * @param _allocationID Address used as signer by the indexer for an allocation + * @param allocationID Address used as signer by the indexer for an allocation * @return True if allocationID already used */ - function isAllocation(address _allocationID) external view override returns (bool) { - return _getAllocationState(_allocationID) != AllocationState.Null; + function isAllocation(address allocationID) external view override returns (bool) { + return _getAllocationState(allocationID) != AllocationState.Null; } /** * @notice Return the allocation by ID. * @dev TODO: Remove after Horizon transition period - * @param _allocationID Address used as allocation identifier + * @param allocationID Address used as allocation identifier * @return Allocation data */ - function getAllocation(address _allocationID) external view override returns (Allocation memory) { - return __DEPRECATED_allocations[_allocationID]; + function getAllocation(address allocationID) external view override returns (Allocation memory) { + return __DEPRECATED_allocations[allocationID]; } /** * @notice Return the current state of an allocation * @dev TODO: Remove after Horizon transition period - * @param _allocationID Allocation identifier + * @param allocationID Allocation identifier * @return AllocationState enum with the state of the allocation */ - function getAllocationState(address _allocationID) external view override returns (AllocationState) { - return _getAllocationState(_allocationID); + function getAllocationState(address allocationID) external view override returns (AllocationState) { + return _getAllocationState(allocationID); } /** * @notice Return the total amount of tokens allocated to subgraph. - * @param _subgraphDeploymentID Deployment ID for the subgraph + * @param subgraphDeploymentID Deployment ID for the subgraph * @return Total tokens allocated to subgraph */ - function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentID) external view override returns (uint256) { - return __DEPRECATED_subgraphAllocations[_subgraphDeploymentID]; + function getSubgraphAllocatedTokens(bytes32 subgraphDeploymentID) external view override returns (uint256) { + return __DEPRECATED_subgraphAllocations[subgraphDeploymentID]; + } + + /** + * @notice Get the total amount of tokens staked by the indexer. + * @param indexer Address of the indexer + * @return Amount of tokens staked by the indexer + */ + function getIndexerStakedTokens(address indexer) external view override returns (uint256) { + return _serviceProviders[indexer].tokensStaked; + } + + /** + * @notice Getter that returns if an indexer has any stake. + * @param indexer Address of the indexer + * @return True if indexer has staked tokens + */ + function hasStake(address indexer) external view override returns (bool) { + return _serviceProviders[indexer].tokensStaked > 0; } /** * @notice (Legacy) Return true if operator is allowed for the service provider on the subgraph data service. * @dev TODO: Delete after the transition period - * @param _operator Address of the operator - * @param _serviceProvider Address of the service provider + * @param operator Address of the operator + * @param serviceProvider Address of the service provider * @return True if operator is allowed for indexer, false otherwise */ - function isOperator(address _operator, address _serviceProvider) public view override returns (bool) { - return legacyOperatorAuth[_serviceProvider][_operator]; + function isOperator(address operator, address serviceProvider) public view override returns (bool) { + return _legacyOperatorAuth[serviceProvider][operator]; } /** - * @notice Get the total amount of tokens staked by the indexer. - * @param _indexer Address of the indexer - * @return Amount of tokens staked by the indexer + * @dev Stake tokens on the service provider. + * TODO: Move to HorizonStaking after the transition period + * @param _serviceProvider Address of staking party + * @param _tokens Amount of tokens to stake */ - function getIndexerStakedTokens(address _indexer) external view override returns (uint256) { - return serviceProviders[_indexer].tokensStaked; + function _stake(address _serviceProvider, uint256 _tokens) internal { + // Deposit tokens into the indexer stake + _serviceProviders[_serviceProvider].tokensStaked = _serviceProviders[_serviceProvider].tokensStaked + _tokens; + + emit StakeDeposited(_serviceProvider, _tokens); + } + + function _graphToken() internal view returns (IGraphToken) { + return IGraphToken(GRAPH_TOKEN); + } + + function _curation() internal view returns (ICuration) { + return ICuration(CURATION); + } + + function _rewardsManager() internal view returns (IRewardsManager) { + return IRewardsManager(REWARDS_MANAGER); + } + + function _epochManager() internal view returns (IEpochManager) { + return IEpochManager(EPOCH_MANAGER); } /** - * @notice Getter that returns if an indexer has any stake. - * @param _indexer Address of the indexer - * @return True if indexer has staked tokens + * @dev Collect tax to burn for an amount of tokens. + * @param _tokens Total tokens received used to calculate the amount of tax to collect + * @param _percentage Percentage of tokens to burn as tax + * @return Amount of tax charged + */ + function _collectTax(uint256 _tokens, uint256 _percentage) private returns (uint256) { + // Calculate tokens after tax first, and subtract that, + // to prevent the tax from rounding down to zero + uint256 tokensAfterTax = ((uint256(MAX_PPM) - _percentage) * _tokens) / MAX_PPM; + uint256 tax = _tokens - tokensAfterTax; + TokenUtils.burnTokens(_graphToken(), tax); // Burn tax if any + return tax; + } + + /** + * @dev Triggers an update of rewards due to a change in allocations. + * @param _subgraphDeploymentID Subgraph deployment updated + * @return Accumulated rewards per allocated token for the subgraph deployment */ - function hasStake(address _indexer) external view override returns (bool) { - return serviceProviders[_indexer].tokensStaked > 0; + function _updateRewards(bytes32 _subgraphDeploymentID) private returns (uint256) { + IRewardsManager rewardsManager = _rewardsManager(); + if (address(rewardsManager) == address(0)) { + return 0; + } + return rewardsManager.onSubgraphAllocationUpdate(_subgraphDeploymentID); + } + + /** + * @dev Assign rewards for the closed allocation to indexer and delegators. + * @param _allocationID Allocation + * @param _indexer Address of the indexer that did the allocation + */ + function _distributeRewards(address _allocationID, address _indexer) private { + IRewardsManager rewardsManager = _rewardsManager(); + if (address(rewardsManager) == address(0)) { + return; + } + + // Automatically triggers update of rewards snapshot as allocation will change + // after this call. Take rewards mint tokens for the Staking contract to distribute + // between indexer and delegators + uint256 totalRewards = rewardsManager.takeRewards(_allocationID); + if (totalRewards == 0) { + return; + } + + // Calculate delegation rewards and add them to the delegation pool + uint256 delegationRewards = _collectDelegationIndexingRewards(_indexer, totalRewards); + uint256 indexerRewards = totalRewards - delegationRewards; + + // Send the indexer rewards + _sendRewards(indexerRewards, _indexer, __DEPRECATED_rewardsDestination[_indexer] == address(0)); + } + + /** + * @dev Send rewards to the appropriate destination. + * @param _amount Number of rewards tokens + * @param _beneficiary Address of the beneficiary of rewards + * @param _restake Whether to restake or not + */ + function _sendRewards(uint256 _amount, address _beneficiary, bool _restake) private { + if (_amount == 0) return; + + if (_restake) { + // Restake to place fees into the indexer stake + _stake(_beneficiary, _amount); + } else { + // Transfer funds to the beneficiary's designated rewards destination if set + address destination = __DEPRECATED_rewardsDestination[_beneficiary]; + TokenUtils.pushTokens(_graphToken(), destination == address(0) ? _beneficiary : destination, _amount); + } } /** @@ -280,8 +383,8 @@ abstract contract StakingBackwardsCompatibility is } // Free allocated tokens from use - serviceProviders[alloc.indexer].__DEPRECATED_tokensAllocated = - serviceProviders[alloc.indexer].__DEPRECATED_tokensAllocated - + _serviceProviders[alloc.indexer].__DEPRECATED_tokensAllocated = + _serviceProviders[alloc.indexer].__DEPRECATED_tokensAllocated - alloc.tokens; // Track total allocations per subgraph @@ -312,7 +415,7 @@ abstract contract StakingBackwardsCompatibility is */ function _collectDelegationQueryRewards(address _indexer, uint256 _tokens) private returns (uint256) { uint256 delegationRewards = 0; - DelegationPoolInternal storage pool = legacyDelegationPools[_indexer]; + DelegationPoolInternal storage pool = _legacyDelegationPools[_indexer]; if (pool.tokens > 0 && pool.__DEPRECATED_queryFeeCut < MAX_PPM) { uint256 indexerCut = (uint256(pool.__DEPRECATED_queryFeeCut) * _tokens) / MAX_PPM; delegationRewards = _tokens - indexerCut; @@ -330,7 +433,7 @@ abstract contract StakingBackwardsCompatibility is */ function _collectDelegationIndexingRewards(address _indexer, uint256 _tokens) private returns (uint256) { uint256 delegationRewards = 0; - DelegationPoolInternal storage pool = legacyDelegationPools[_indexer]; + DelegationPoolInternal storage pool = _legacyDelegationPools[_indexer]; if (pool.tokens > 0 && pool.__DEPRECATED_indexingRewardCut < MAX_PPM) { uint256 indexerCut = (uint256(pool.__DEPRECATED_indexingRewardCut) * _tokens) / MAX_PPM; delegationRewards = _tokens - indexerCut; @@ -377,80 +480,6 @@ abstract contract StakingBackwardsCompatibility is return 0; } - /** - * @dev Collect tax to burn for an amount of tokens. - * @param _tokens Total tokens received used to calculate the amount of tax to collect - * @param _percentage Percentage of tokens to burn as tax - * @return Amount of tax charged - */ - function _collectTax(uint256 _tokens, uint256 _percentage) private returns (uint256) { - // Calculate tokens after tax first, and subtract that, - // to prevent the tax from rounding down to zero - uint256 tokensAfterTax = ((uint256(MAX_PPM) - _percentage) * _tokens) / MAX_PPM; - uint256 tax = _tokens - tokensAfterTax; - TokenUtils.burnTokens(_graphToken(), tax); // Burn tax if any - return tax; - } - - /** - * @dev Triggers an update of rewards due to a change in allocations. - * @param _subgraphDeploymentID Subgraph deployment updated - * @return Accumulated rewards per allocated token for the subgraph deployment - */ - function _updateRewards(bytes32 _subgraphDeploymentID) private returns (uint256) { - IRewardsManager rewardsManager = _rewardsManager(); - if (address(rewardsManager) == address(0)) { - return 0; - } - return rewardsManager.onSubgraphAllocationUpdate(_subgraphDeploymentID); - } - - /** - * @dev Assign rewards for the closed allocation to indexer and delegators. - * @param _allocationID Allocation - * @param _indexer Address of the indexer that did the allocation - */ - function _distributeRewards(address _allocationID, address _indexer) private { - IRewardsManager rewardsManager = _rewardsManager(); - if (address(rewardsManager) == address(0)) { - return; - } - - // Automatically triggers update of rewards snapshot as allocation will change - // after this call. Take rewards mint tokens for the Staking contract to distribute - // between indexer and delegators - uint256 totalRewards = rewardsManager.takeRewards(_allocationID); - if (totalRewards == 0) { - return; - } - - // Calculate delegation rewards and add them to the delegation pool - uint256 delegationRewards = _collectDelegationIndexingRewards(_indexer, totalRewards); - uint256 indexerRewards = totalRewards - delegationRewards; - - // Send the indexer rewards - _sendRewards(indexerRewards, _indexer, __DEPRECATED_rewardsDestination[_indexer] == address(0)); - } - - /** - * @dev Send rewards to the appropriate destination. - * @param _amount Number of rewards tokens - * @param _beneficiary Address of the beneficiary of rewards - * @param _restake Whether to restake or not - */ - function _sendRewards(uint256 _amount, address _beneficiary, bool _restake) private { - if (_amount == 0) return; - - if (_restake) { - // Restake to place fees into the indexer stake - _stake(_beneficiary, _amount); - } else { - // Transfer funds to the beneficiary's designated rewards destination if set - address destination = __DEPRECATED_rewardsDestination[_beneficiary]; - TokenUtils.pushTokens(_graphToken(), destination == address(0) ? _beneficiary : destination, _amount); - } - } - /** * @dev Return the current state of an allocation * @param _allocationID Allocation identifier @@ -469,33 +498,4 @@ abstract contract StakingBackwardsCompatibility is return AllocationState.Closed; } - - /** - * @dev Stake tokens on the service provider. - * TODO: Move to HorizonStaking after the transition period - * @param _serviceProvider Address of staking party - * @param _tokens Amount of tokens to stake - */ - function _stake(address _serviceProvider, uint256 _tokens) internal { - // Deposit tokens into the indexer stake - serviceProviders[_serviceProvider].tokensStaked = serviceProviders[_serviceProvider].tokensStaked + _tokens; - - emit StakeDeposited(_serviceProvider, _tokens); - } - - function _graphToken() internal view returns (IGraphToken) { - return IGraphToken(GRAPH_TOKEN); - } - - function _curation() internal view returns (ICuration) { - return ICuration(CURATION); - } - - function _rewardsManager() internal view returns (IRewardsManager) { - return IRewardsManager(REWARDS_MANAGER); - } - - function _epochManager() internal view returns (IEpochManager) { - return IEpochManager(EPOCH_MANAGER); - } } diff --git a/packages/horizon/contracts/staking/utilities/Managed.sol b/packages/horizon/contracts/staking/utilities/Managed.sol index 685c18184..496caca0e 100644 --- a/packages/horizon/contracts/staking/utilities/Managed.sol +++ b/packages/horizon/contracts/staking/utilities/Managed.sol @@ -7,6 +7,9 @@ import { IManaged } from "../../interfaces/IManaged.sol"; import { GraphDirectory } from "../../GraphDirectory.sol"; +// TODO: create custom var-name-mixedcase +/* solhint-disable var-name-mixedcase */ + /** * @title Graph Managed contract * @dev The Managed contract provides an interface to interact with the Controller. @@ -19,7 +22,7 @@ abstract contract Managed is IManaged, GraphDirectory { /// Controller that manages this contract IController private __DEPRECATED_controller; /// @dev Cache for the addresses of the contracts retrieved from the controller - mapping(bytes32 => address) private __DEPRECATED_addressCache; + mapping(bytes32 contractName => address contractAddress) private __DEPRECATED_addressCache; /// @dev Gap for future storage variables uint256[10] private __gap; @@ -35,41 +38,6 @@ abstract contract Managed is IManaged, GraphDirectory { error ManagedSetControllerDeprecated(); - constructor(address _controller) GraphDirectory(_controller) {} - - function controller() public view override returns (IController) { - return IController(CONTROLLER); - } - - /** - * @dev Revert if the controller is paused or partially paused - */ - function _notPartialPaused() internal view { - require(!controller().paused(), "Paused"); - require(!controller().partialPaused(), "Partial-paused"); - } - - /** - * @dev Revert if the controller is paused - */ - function _notPaused() internal view virtual { - require(!controller().paused(), "Paused"); - } - - /** - * @dev Revert if the caller is not the governor - */ - function _onlyGovernor() internal view { - require(msg.sender == controller().getGovernor(), "Only Controller governor"); - } - - /** - * @dev Revert if the caller is not the Controller - */ - function _onlyController() internal view { - require(msg.sender == CONTROLLER, "Caller must be Controller"); - } - /** * @dev Revert if the controller is paused or partially paused */ @@ -102,10 +70,45 @@ abstract contract Managed is IManaged, GraphDirectory { _; } + constructor(address controller_) GraphDirectory(controller_) {} + /** * @notice Set Controller. Deprecated, will revert. */ function setController(address) external view override onlyController { revert ManagedSetControllerDeprecated(); } + + function controller() public view override returns (IController) { + return IController(CONTROLLER); + } + + /** + * @dev Revert if the controller is paused or partially paused + */ + function _notPartialPaused() internal view { + require(!controller().paused(), "Paused"); + require(!controller().partialPaused(), "Partial-paused"); + } + + /** + * @dev Revert if the controller is paused + */ + function _notPaused() internal view virtual { + require(!controller().paused(), "Paused"); + } + + /** + * @dev Revert if the caller is not the governor + */ + function _onlyGovernor() internal view { + require(msg.sender == controller().getGovernor(), "Only Controller governor"); + } + + /** + * @dev Revert if the caller is not the Controller + */ + function _onlyController() internal view { + require(msg.sender == CONTROLLER, "Caller must be Controller"); + } } diff --git a/packages/horizon/package.json b/packages/horizon/package.json index fff88b6d4..a39d4adc6 100644 --- a/packages/horizon/package.json +++ b/packages/horizon/package.json @@ -37,6 +37,7 @@ "prettier-plugin-solidity": "^1.3.1", "solhint": "^4.5.2", "solhint-graph-config": "workspace:^0.0.1", + "solhint-plugin-graph": "workspace:^0.0.1", "solidity-coverage": "^0.8.0", "ts-node": ">=8.0.0", "typechain": "^8.3.0", diff --git a/packages/solhint-graph-config/index.js b/packages/solhint-graph-config/index.js index f7193d748..f05ee0df6 100644 --- a/packages/solhint-graph-config/index.js +++ b/packages/solhint-graph-config/index.js @@ -24,5 +24,7 @@ module.exports = { // graph 'graph/leading-underscore': 'warn', + + 'gas-custom-errors': 'off' }, } diff --git a/packages/solhint-plugin-graph/index.js b/packages/solhint-plugin-graph/index.js index cd16840e8..d21c8840d 100644 --- a/packages/solhint-plugin-graph/index.js +++ b/packages/solhint-plugin-graph/index.js @@ -4,6 +4,8 @@ function hasLeadingUnderscore(text) { class Base { constructor(reporter, config, source, fileName) { + this.ignoreDeprecated = true; + this.deprecatedPrefix = '__DEPRECATED_'; this.reporter = reporter; this.ignored = this.constructor.global; this.ruleId = this.constructor.ruleId; @@ -68,6 +70,10 @@ module.exports = [ } validateName(node, type) { + if (this.ignoreDeprecated && node.name.startsWith(this.deprecatedPrefix)) { + return + } + const isPrivate = node.visibility === 'private' const isInternal = node.visibility === 'internal' || node.visibility === 'default' const isConstant = node.isDeclaredConst diff --git a/yarn.lock b/yarn.lock index c105ba53f..a164666c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2937,6 +2937,7 @@ __metadata: prettier-plugin-solidity: "npm:^1.3.1" solhint: "npm:^4.5.2" solhint-graph-config: "workspace:^0.0.1" + solhint-plugin-graph: "workspace:^0.0.1" solidity-coverage: "npm:^0.8.0" ts-node: "npm:>=8.0.0" typechain: "npm:^8.3.0"