Skip to content

Commit

Permalink
chore: somehow it fits in 24kB now
Browse files Browse the repository at this point in the history
  • Loading branch information
pcarranzav committed Apr 26, 2024
1 parent e4eb030 commit 2053f65
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 338 deletions.
105 changes: 21 additions & 84 deletions packages/horizon/contracts/HorizonStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@ pragma solidity 0.8.24;

import { GraphUpgradeable } from "@graphprotocol/contracts/contracts/upgrades/GraphUpgradeable.sol";

import { IHorizonStaking } from "./IHorizonStaking.sol";
import { IHorizonStakingBase } from "./IHorizonStakingBase.sol";
import { TokenUtils } from "./utils/TokenUtils.sol";
import { MathUtils } from "./utils/MathUtils.sol";
import { Managed } from "./Managed.sol";
import { IGraphToken } from "./IGraphToken.sol";
import { HorizonStakingV1Storage } from "./HorizonStakingStorage.sol";

contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgradeable {
contract HorizonStaking is HorizonStakingV1Storage, IHorizonStakingBase, GraphUpgradeable {
/// Maximum value that can be set as the maxVerifierCut in a provision.
/// It is equivalent to 50% in parts-per-million, to protect delegators from
/// service providers using a malicious verifier.
uint32 public constant MAX_MAX_VERIFIER_CUT = 500000; // 50%
uint32 private constant MAX_MAX_VERIFIER_CUT = 500000; // 50%

/// Minimum size of a provision
uint256 public constant MIN_PROVISION_SIZE = 1e18;
uint256 private constant MIN_PROVISION_SIZE = 1e18;

/// Maximum number of simultaneous stake thaw requests or undelegations
uint256 public constant MAX_THAW_REQUESTS = 100;
uint256 private constant MAX_THAW_REQUESTS = 100;

uint256 public constant FIXED_POINT_PRECISION = 1e18;
uint256 private constant FIXED_POINT_PRECISION = 1e18;

/// Minimum delegation size
uint256 public constant MINIMUM_DELEGATION = 1e18;
uint256 private constant MINIMUM_DELEGATION = 1e18;

address public immutable L2_STAKING_BACKWARDS_COMPATIBILITY;
address public immutable SUBGRAPH_DATA_SERVICE_ADDRESS;
address private immutable STAKING_EXTENSION_ADDRESS;
address private immutable SUBGRAPH_DATA_SERVICE_ADDRESS;

error HorizonStakingInvalidVerifier(address verifier);
error HorizonStakingVerifierAlreadyAllowed(address verifier);
Expand All @@ -42,12 +42,12 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad

constructor(
address _controller,
address _l2StakingBackwardsCompatibility,
address _stakingExtensionAddress,
address _subgraphDataServiceAddress
) Managed(_controller) {
L2_STAKING_BACKWARDS_COMPATIBILITY = _l2StakingBackwardsCompatibility;
) Managed(_controller) {
STAKING_EXTENSION_ADDRESS = _stakingExtensionAddress;
SUBGRAPH_DATA_SERVICE_ADDRESS = _subgraphDataServiceAddress;
}
}

/**
* @notice Delegates the current call to the StakingExtension implementation.
Expand All @@ -56,8 +56,8 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad
*/
// solhint-disable-next-line payable-fallback, no-complex-fallback
fallback() external {
require(_implementation() != address(0), "only through proxy");
address extensionImpl = L2_STAKING_BACKWARDS_COMPATIBILITY;
//require(_implementation() != address(0), "only through proxy");
address extensionImpl = STAKING_EXTENSION_ADDRESS;
// solhint-disable-next-line no-inline-assembly
assembly {
// (a) get free memory pointer
Expand All @@ -84,38 +84,6 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad
}
}

/**
* @notice Allow verifier for stake provisions.
* After calling this, and a timelock period, the service provider will
* be allowed to provision stake that is slashable by the verifier.
* @param _verifier The address of the contract that can slash the provision
*/
function allowVerifier(address _verifier) external override {
if (_verifier == address(0)) {
revert HorizonStakingInvalidVerifier(_verifier);
}
if (verifierAllowlist[msg.sender][_verifier]) {
revert HorizonStakingVerifierAlreadyAllowed(_verifier);
}
verifierAllowlist[msg.sender][_verifier] = true;
emit VerifierAllowed(msg.sender, _verifier);
}

/**
* @notice Deny a verifier for stake provisions.
* After calling this, the service provider will immediately
* be unable to provision any stake to the verifier.
* Any existing provisions will be unaffected.
* @param _verifier The address of the contract that can slash the provision
*/
function denyVerifier(address _verifier) external override {
if (!verifierAllowlist[msg.sender][_verifier]) {
revert HorizonStakingVerifierNotAllowed(_verifier);
}
verifierAllowlist[msg.sender][_verifier] = false;
emit VerifierDenied(msg.sender, _verifier);
}

/**
* @notice Deposit tokens on the caller's stake.
* @param _tokens Amount of tokens to stake
Expand Down Expand Up @@ -389,11 +357,7 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad
* @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
) private view returns (bool) {
function isAuthorized(address _operator, address _serviceProvider, address _verifier) private view returns (bool) {
if (_operator == _serviceProvider) {
return true;
}
Expand All @@ -416,40 +380,10 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad

// provisioned tokens from the service provider that are not being thawed
// `Provision.tokens - Provision.tokensThawing`
function getProviderTokensAvailable(
address _serviceProvider,
address _verifier
) public view returns (uint256) {
function getProviderTokensAvailable(address _serviceProvider, address _verifier) public view returns (uint256) {
return provisions[_serviceProvider][_verifier].tokens - provisions[_serviceProvider][_verifier].tokensThawing;
}

/**
* @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 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;
}
emit OperatorSet(msg.sender, _operator, _verifier, _allowed);
}

/**
* @notice Authorize or unauthorize an address to be an operator for the caller on all data services.
* @param _operator Address to authorize or unauthorize
* @param _allowed Whether the operator is authorized or not
*/
function setGlobalOperator(address _operator, bool _allowed) external override {
require(_operator != msg.sender, "operator == sender");
globalOperatorAuth[msg.sender][_operator] = _allowed;
emit GlobalOperatorSet(msg.sender, _operator, _allowed);
}

/**
* @notice Check if an operator is authorized for the caller on all their allowlisted verifiers and global stake.
* @param _operator The address to check for auth
Expand Down Expand Up @@ -536,7 +470,10 @@ contract HorizonStaking is HorizonStakingV1Storage, IHorizonStaking, GraphUpgrad

pool.shares = pool.shares - _shares;
delegation.shares = delegation.shares - _shares;

if (delegation.shares != 0) {
uint256 remainingTokens = (delegation.shares * (pool.tokens - pool.tokensThawing)) / pool.shares;
require(remainingTokens >= MINIMUM_DELEGATION, "!minimum-delegation");
}
bytes32 thawRequestId = keccak256(
abi.encodePacked(_serviceProvider, _verifier, msg.sender, delegation.nextThawRequestNonce)
);
Expand Down
103 changes: 85 additions & 18 deletions packages/horizon/contracts/HorizonStakingExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ pragma solidity 0.8.24;
import { StakingBackwardsCompatibility } from "./StakingBackwardsCompatibility.sol";
import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol";
import { IL2StakingTypes } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingTypes.sol";
import { IHorizonStaking } from "./IHorizonStaking.sol";
import { IHorizonStakingExtension } from "./IHorizonStakingExtension.sol";

/**
* @title L2Staking contract
* @dev This contract is the L2 variant of the Staking contract. It adds a function
* to receive an indexer's stake or delegation from L1. Note that this contract inherits Staking,
* which uses a StakingExtension contract to implement the full IStaking interface through delegatecalls.
*/
contract HorizonStakingExtension is StakingBackwardsCompatibility, IL2StakingBase {
contract HorizonStakingExtension is StakingBackwardsCompatibility, IHorizonStakingExtension, IL2StakingBase {
/// @dev Minimum amount of tokens that can be delegated
uint256 private constant MINIMUM_DELEGATION = 1e18;

Expand All @@ -25,10 +25,18 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IL2StakingBas
_;
}

constructor(address _controller, address _subgraphDataServiceAddress) StakingBackwardsCompatibility(_controller, _subgraphDataServiceAddress) {}
error HorizonStakingInvalidVerifier(address verifier);
error HorizonStakingVerifierAlreadyAllowed(address verifier);
error HorizonStakingVerifierNotAllowed(address verifier);

constructor(
address _controller,
address _subgraphDataServiceAddress,
address _exponentialRebates
) StakingBackwardsCompatibility(_controller, _subgraphDataServiceAddress, _exponentialRebates) {}

/**
* @notice Receive ETH into the L2Staking contract: this will always revert
* @notice Receive ETH into the Staking contract: this will always revert
* @dev This function is only here to prevent ETH from being sent to the contract
*/
receive() external payable {
Expand All @@ -37,25 +45,16 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IL2StakingBas

// total staked tokens to the provider
// `ServiceProvider.tokensStaked
function getStake(address serviceProvider) external view returns (uint256 tokens) {
function getStake(address serviceProvider) external view override returns (uint256) {
return serviceProviders[serviceProvider].tokensStaked;
}

// provisioned tokens from the service provider that are not being thawed
// `Provision.tokens - Provision.tokensThawing`
function _getProviderTokensAvailable(
address _serviceProvider,
address _verifier
) private view returns (uint256) {
return provisions[_serviceProvider][_verifier].tokens - provisions[_serviceProvider][_verifier].tokensThawing;
}

// provisioned tokens from delegators that are not being thawed
// `Provision.delegatedTokens - Provision.delegatedTokensThawing`
function getDelegatedTokensAvailable(
address _serviceProvider,
address _verifier
) public view returns (uint256) {
) public view override returns (uint256) {
if (_verifier == SUBGRAPH_DATA_SERVICE_ADDRESS) {
return
legacyDelegationPools[_serviceProvider].tokens -
Expand All @@ -67,13 +66,14 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IL2StakingBas
}

// provisioned tokens that are not being thawed (including provider tokens and delegation)
function getTokensAvailable(address _serviceProvider, address _verifier) external view returns (uint256) {
function getTokensAvailable(address _serviceProvider, address _verifier) external view override returns (uint256) {
return
_getProviderTokensAvailable(_serviceProvider, _verifier) +
provisions[_serviceProvider][_verifier].tokens -
provisions[_serviceProvider][_verifier].tokensThawing +
getDelegatedTokensAvailable(_serviceProvider, _verifier);
}

function getServiceProvider(address serviceProvider) external view returns (ServiceProvider memory) {
function getServiceProvider(address serviceProvider) external view override returns (ServiceProvider memory) {
ServiceProvider memory sp;
ServiceProviderInternal storage spInternal = serviceProviders[serviceProvider];
sp.tokensStaked = spInternal.tokensStaked;
Expand All @@ -82,6 +82,73 @@ contract HorizonStakingExtension is StakingBackwardsCompatibility, IL2StakingBas
return sp;
}

/**
* @notice Allow verifier for stake provisions.
* After calling this, and a timelock period, the service provider will
* be allowed to provision stake that is slashable by the verifier.
* @param _verifier The address of the contract that can slash the provision
*/
function allowVerifier(address _verifier) external override {
if (_verifier == address(0)) {
revert HorizonStakingInvalidVerifier(_verifier);
}
if (verifierAllowlist[msg.sender][_verifier]) {
revert HorizonStakingVerifierAlreadyAllowed(_verifier);
}
verifierAllowlist[msg.sender][_verifier] = true;
emit VerifierAllowed(msg.sender, _verifier);
}

/**
* @notice Deny a verifier for stake provisions.
* After calling this, the service provider will immediately
* be unable to provision any stake to the verifier.
* Any existing provisions will be unaffected.
* @param _verifier The address of the contract that can slash the provision
*/
function denyVerifier(address _verifier) external {
if (!verifierAllowlist[msg.sender][_verifier]) {
revert HorizonStakingVerifierNotAllowed(_verifier);
}
verifierAllowlist[msg.sender][_verifier] = false;
emit VerifierDenied(msg.sender, _verifier);
}

/**
* @notice Authorize or unauthorize an address to be an operator for the caller on all data services.
* @param _operator Address to authorize or unauthorize
* @param _allowed Whether the operator is authorized or not
*/
function setGlobalOperator(address _operator, bool _allowed) external override {
require(_operator != msg.sender, "operator == sender");
globalOperatorAuth[msg.sender][_operator] = _allowed;
emit GlobalOperatorSet(msg.sender, _operator, _allowed);
}

function isAllowedVerifier(address _serviceProvider, address _verifier) external view override returns (bool) {
return _verifier == SUBGRAPH_DATA_SERVICE_ADDRESS || verifierAllowlist[_serviceProvider][_verifier];
}

/**
* @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 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;
}
emit OperatorSet(msg.sender, _operator, _verifier, _allowed);
}

function getMaxThawingPeriod() external view override returns (uint64) {
return maxThawingPeriod;
}

/**
* @notice Receive tokens with a callhook from the bridge.
* @dev The encoded _data can contain information about an indexer's stake
Expand Down
6 changes: 2 additions & 4 deletions packages/horizon/contracts/HorizonStakingStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { IHorizonStakingTypes } from "./IHorizonStakingTypes.sol";
/**
* @title HorizonStakingV1Storage
* @notice This contract holds all the storage variables for the Staking contract, version 1
* @dev Note that we use a double underscore prefix for variable names; this prefix identifies
* variables that used to be public but are now internal, getters can be found on StakingExtension.sol.
*/
// solhint-disable-next-line max-states-count
abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes {
Expand Down Expand Up @@ -119,10 +117,10 @@ abstract contract HorizonStakingV1Storage is Managed, IHorizonStakingTypes {

/// Verifier allowlist by service provider
/// serviceProvider => verifier => allowed
mapping(address => mapping(address => bool)) public verifierAllowlist;
mapping(address => mapping(address => bool)) internal verifierAllowlist;

/// Maximum thawing period, in seconds, for a provision
uint64 public maxThawingPeriod;
uint64 internal maxThawingPeriod;

/// @dev Provisions from each service provider for each data service
/// ServiceProvider => Verifier => Provision
Expand Down
Loading

0 comments on commit 2053f65

Please sign in to comment.