diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol index fcf70cc32..a0b2dc1af 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol @@ -3,13 +3,12 @@ pragma solidity 0.8.27; import { IRewardsIssuer } from "@graphprotocol/contracts/contracts/rewards/IRewardsIssuer.sol"; -import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; /** * @title Interface for {HorizonStakingExtension} contract. - * @notice Provides functions for managing legacy allocations and transfer tools. + * @notice Provides functions for managing legacy allocations. */ -interface IHorizonStakingExtension is IRewardsIssuer, IL2StakingBase { +interface IHorizonStakingExtension is IRewardsIssuer { /** * @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() @@ -39,15 +38,6 @@ interface IHorizonStakingExtension is IRewardsIssuer, IL2StakingBase { Closed } - /** - * @notice Emitted when a delegator delegates through the Graph Token Gateway using the transfer tools. - * @dev TODO(after transfer tools): delete - * @param serviceProvider The address of the service provider. - * @param delegator The address of the delegator. - * @param tokens The amount of tokens delegated. - */ - event StakeDelegated(address indexed serviceProvider, address indexed delegator, uint256 tokens, uint256 shares); - /** * @dev Emitted when `indexer` close an allocation in `epoch` for `allocationID`. * An amount of `tokens` get unallocated from `subgraphDeploymentID`. @@ -87,15 +77,6 @@ interface IHorizonStakingExtension is IRewardsIssuer, IL2StakingBase { uint256 delegationRewards ); - event CounterpartStakingAddressSet(address indexed counterpart); - - /** - * @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. - */ - 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. diff --git a/packages/horizon/contracts/staking/HorizonStaking.sol b/packages/horizon/contracts/staking/HorizonStaking.sol index fa566563d..325952ddd 100644 --- a/packages/horizon/contracts/staking/HorizonStaking.sol +++ b/packages/horizon/contracts/staking/HorizonStaking.sol @@ -22,8 +22,7 @@ import { HorizonStakingBase } from "./HorizonStakingBase.sol"; * It is designed to be deployed as an upgrade to the L2Staking contract from the legacy contracts package. * @dev It uses a {HorizonStakingExtension} contract to implement the full {IHorizonStaking} interface through delegatecalls. * This is due to the contract size limit on Arbitrum (24kB). The extension contract implements functionality to support - * the legacy staking functions and the transfer tools. Both can be eventually removed without affecting the main - * staking contract. + * the legacy staking functions. It can be eventually removed without affecting the main staking contract. * @custom:security-contact Please email security+contracts@thegraph.com if you find any * bugs. We may have an active bug bounty program. */ @@ -42,11 +41,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { /// @dev Maximum number of simultaneous stake thaw requests (per provision) or undelegations (per delegation) uint256 private constant MAX_THAW_REQUESTS = 100; - /// @dev Minimum amount of delegation to prevent rounding attacks. - /// TODO: remove this after L2 transfer tool for delegation is removed - /// (delegation on L2 has its own slippage protection) - uint256 private constant MIN_DELEGATION = 1e18; - /// @dev Address of the staking extension contract address private immutable STAKING_EXTENSION_ADDRESS; @@ -741,8 +735,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { * have been done before calling this function. */ function _delegate(address _serviceProvider, address _verifier, uint256 _tokens, uint256 _minSharesOut) private { - // TODO: remove this after L2 transfer tool for delegation is removed - require(_tokens >= MIN_DELEGATION, HorizonStakingInsufficientTokens(_tokens, MIN_DELEGATION)); require( _provisions[_serviceProvider][_verifier].createdAt != 0, HorizonStakingInvalidProvision(_serviceProvider, _verifier) @@ -795,14 +787,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { pool.sharesThawing = pool.sharesThawing + thawingShares; 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, - HorizonStakingInsufficientTokens(remainingTokens, MIN_DELEGATION) - ); - } bytes32 thawRequestId = _createThawRequest( _serviceProvider, diff --git a/packages/horizon/contracts/staking/HorizonStakingExtension.sol b/packages/horizon/contracts/staking/HorizonStakingExtension.sol index d1f08234b..bc878a4f5 100644 --- a/packages/horizon/contracts/staking/HorizonStakingExtension.sol +++ b/packages/horizon/contracts/staking/HorizonStakingExtension.sol @@ -5,9 +5,6 @@ pragma solidity 0.8.27; import { ICuration } from "@graphprotocol/contracts/contracts/curation/ICuration.sol"; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IHorizonStakingExtension } from "../interfaces/internal/IHorizonStakingExtension.sol"; -import { IHorizonStakingTypes } from "../interfaces/internal/IHorizonStakingTypes.sol"; -import { IL2StakingTypes } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingTypes.sol"; -import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; import { TokenUtils } from "@graphprotocol/contracts/contracts/utils/TokenUtils.sol"; import { MathUtils } from "../libraries/MathUtils.sol"; @@ -22,18 +19,15 @@ import { HorizonStakingBase } from "./HorizonStakingBase.sol"; * to the Horizon Staking contract. It allows indexers to close allocations and collect pending query fees, but it * does not allow for the creation of new allocations. This should allow indexers to migrate to a subgraph data service * without losing rewards or having service interruptions. - * @dev TODO: Once the transition period and the transfer tools are deemed not necessary this contract - * can be removed. It's expected the transition period to last for a full allocation cycle (28 epochs). + * @dev TODO: Once the transition period passes this contract can be removed. It's expected the transition period to + * last for a full allocation cycle (28 epochs). * @custom:security-contact Please email security+contracts@thegraph.com if you find any * bugs. We may have an active bug bounty program. */ -contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizonStakingExtension { +contract HorizonStakingExtension is HorizonStakingBase, IHorizonStakingExtension { using TokenUtils for IGraphToken; using PPMMath for uint256; - /// @dev Minimum amount of tokens that can be delegated - uint256 private constant MINIMUM_DELEGATION = 1e18; - /** * @dev Checks that the sender is the L2GraphTokenGateway as configured on the Controller. */ @@ -53,52 +47,6 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon address subgraphDataServiceAddress ) HorizonStakingBase(controller, subgraphDataServiceAddress) {} - /** - * @notice Receive tokens with a callhook from the bridge. - * @dev The encoded _data can contain information about an service provider's stake - * or a delegator's delegation. - * See L1MessageCodes in IL2Staking for the supported messages. - * @dev "indexer" in this context refers to a service provider (legacy terminology for the bridge) - * @param from Token sender in L1 - * @param tokens 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 tokens, - bytes calldata data - ) external override notPaused onlyL2Gateway { - 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(tokens, indexerData); - } else if (code == uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE)) { - IL2StakingTypes.ReceiveDelegationData memory delegationData = abi.decode( - functionData, - (IL2StakingTypes.ReceiveDelegationData) - ); - _receiveDelegation(tokens, delegationData); - } else { - revert("INVALID_CODE"); - } - } - - /** - * @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. - */ - function setCounterpartStakingAddress(address counterpart) external override onlyGovernor { - _counterpartStakingAddress = counterpart; - emit CounterpartStakingAddressSet(counterpart); - } - /** * @notice Close an allocation and free the staked tokens. * To be eligible for rewards a proof of indexing must be presented. @@ -318,71 +266,6 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon return _legacyOperatorAuth[serviceProvider][operator]; } - /** - * @dev Receive an Indexer's stake from L1. - * The specified amount is added to the indexer's stake; the indexer's - * address is specified in the _indexerData struct. - * @param _tokens Amount of tokens that were transferred - * @param _indexerData struct containing the indexer's address - */ - function _receiveIndexerStake( - uint256 _tokens, - IL2StakingTypes.ReceiveIndexerStakeData memory _indexerData - ) private { - address indexer = _indexerData.indexer; - // Deposit tokens into the indexer stake - _stake(indexer, _tokens); - } - - /** - * @dev Receive a Delegator's delegation from L1. - * The specified amount is added to the delegator's delegation; the delegator's - * address and the indexer's address are specified in the _delegationData struct. - * Note that no delegation tax is applied here. - * @dev Note that L1 staking contract only allows delegation transfer if the indexer has already transferred, - * this means the corresponding delegation pool exists. - * @param _tokens Amount of tokens that were transferred - * @param _delegationData struct containing the delegator's address and the indexer's address - */ - function _receiveDelegation( - uint256 _tokens, - IL2StakingTypes.ReceiveDelegationData memory _delegationData - ) private { - require(_provisions[_delegationData.indexer][SUBGRAPH_DATA_SERVICE_ADDRESS].createdAt != 0, "!provision"); - // Get the delegation pool of the indexer - DelegationPoolInternal storage pool = _legacyDelegationPools[_delegationData.indexer]; - IHorizonStakingTypes.DelegationInternal storage delegation = pool.delegators[_delegationData.delegator]; - - // If pool is in an invalid state, return the tokens to the delegator - if (pool.tokens == 0 && (pool.shares != 0 || pool.sharesThawing != 0)) { - _graphToken().transfer(_delegationData.delegator, _tokens); - emit TransferredDelegationReturnedToDelegator(_delegationData.indexer, _delegationData.delegator, _tokens); - return; - } - - // Calculate shares to issue (without applying any delegation tax) - uint256 shares = (pool.tokens == 0 || pool.tokens == pool.tokensThawing) - ? _tokens - : ((_tokens * pool.shares) / (pool.tokens - pool.tokensThawing)); - - if (shares == 0 || _tokens < MINIMUM_DELEGATION) { - // If no shares would be issued (probably a rounding issue or attack), - // or if the amount is under the minimum delegation (which could be part of a rounding attack), - // return the tokens to the delegator - _graphToken().pushTokens(_delegationData.delegator, _tokens); - emit TransferredDelegationReturnedToDelegator(_delegationData.indexer, _delegationData.delegator, _tokens); - } else { - // Update the delegation pool - pool.tokens = pool.tokens + _tokens; - pool.shares = pool.shares + shares; - - // Update the individual delegation - delegation.shares = delegation.shares + shares; - - emit StakeDelegated(_delegationData.indexer, _delegationData.delegator, _tokens, shares); - } - } - /** * @dev Collect tax to burn for an amount of tokens. * @param _tokens Total tokens received used to calculate the amount of tax to collect diff --git a/packages/horizon/contracts/staking/HorizonStakingStorage.sol b/packages/horizon/contracts/staking/HorizonStakingStorage.sol index ff515962a..ce2755468 100644 --- a/packages/horizon/contracts/staking/HorizonStakingStorage.sol +++ b/packages/horizon/contracts/staking/HorizonStakingStorage.sol @@ -118,8 +118,8 @@ abstract contract HorizonStakingV1Storage { mapping(address serviceProvider => address rewardsDestination) internal __DEPRECATED_rewardsDestination; /// @dev Address of the counterpart Staking contract on L1/L2 - /// Used for the transfer tools. - address internal _counterpartStakingAddress; + /// Deprecated, transfer tools no longer enabled. + address internal __DEPRECATED_counterpartStakingAddress; /// @dev Address of the StakingExtension implementation /// This is now an immutable variable to save some gas. diff --git a/packages/horizon/test/GraphBase.t.sol b/packages/horizon/test/GraphBase.t.sol index 3c2375b8d..f02733525 100644 --- a/packages/horizon/test/GraphBase.t.sol +++ b/packages/horizon/test/GraphBase.t.sol @@ -45,10 +45,8 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants { address subgraphDataServiceLegacyAddress = makeAddr("subgraphDataServiceLegacyAddress"); address subgraphDataServiceAddress = makeAddr("subgraphDataServiceAddress"); - - // We use these addresses to mock calls from the counterpart staking contract + address graphTokenGatewayAddress = makeAddr("GraphTokenGateway"); - address counterpartStaking = makeAddr("counterpartStaking"); /* Users */ @@ -188,8 +186,6 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants { proxyAdmin.upgrade(stakingProxy, address(stakingBase)); proxyAdmin.acceptProxy(stakingBase, stakingProxy); staking = IHorizonStaking(address(stakingProxy)); - - staking.setCounterpartStakingAddress(address(counterpartStaking)); } function setupProtocol() private { diff --git a/packages/horizon/test/escrow/collect.t.sol b/packages/horizon/test/escrow/collect.t.sol index cbf945515..d9b775b93 100644 --- a/packages/horizon/test/escrow/collect.t.sol +++ b/packages/horizon/test/escrow/collect.t.sol @@ -101,8 +101,7 @@ contract GraphEscrowCollectTest is GraphEscrowTest { uint256 tokensDelegatoion = tokens * delegationFeeCut / MAX_PPM; vm.assume(tokensDataService < tokens - tokensProtocol - tokensDelegatoion); - vm.assume(delegationTokens > MIN_DELEGATION); - vm.assume(delegationTokens <= MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); diff --git a/packages/horizon/test/payments/GraphPayments.t.sol b/packages/horizon/test/payments/GraphPayments.t.sol index 8b76678b4..19028bde1 100644 --- a/packages/horizon/test/payments/GraphPayments.t.sol +++ b/packages/horizon/test/payments/GraphPayments.t.sol @@ -119,8 +119,7 @@ contract GraphPaymentsTest is HorizonStakingSharedTest { address escrowAddress = address(escrow); // Delegate tokens - vm.assume(tokensDelegate > MIN_DELEGATION); - vm.assume(tokensDelegate <= MAX_STAKING_TOKENS); + tokensDelegate = bound(tokensDelegate, 1, MAX_STAKING_TOKENS); vm.startPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, tokensDelegate, 0); diff --git a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol index fab804e67..c0026c5f6 100644 --- a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol +++ b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol @@ -8,7 +8,6 @@ import { IGraphPayments } from "../../../contracts/interfaces/IGraphPayments.sol import { IHorizonStakingBase } from "../../../contracts/interfaces/internal/IHorizonStakingBase.sol"; import { IHorizonStakingMain } from "../../../contracts/interfaces/internal/IHorizonStakingMain.sol"; import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; -import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; import { LinkedList } from "../../../contracts/libraries/LinkedList.sol"; import { MathUtils } from "../../../contracts/libraries/MathUtils.sol"; @@ -1233,107 +1232,6 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { assertEq(afterMaxThawingPeriod, maxThawingPeriod); } - function _setCounterpartStakingAddress(address counterpartStakingAddress) internal { - // setCounterpartStakingAddress - vm.expectEmit(address(staking)); - emit IHorizonStakingExtension.CounterpartStakingAddressSet(counterpartStakingAddress); - staking.setCounterpartStakingAddress(counterpartStakingAddress); - - // after - address afterCounterpartStakingAddress = _getStorage_CounterpartStakingAddress(); - - // assert - assertEq(afterCounterpartStakingAddress, counterpartStakingAddress); - } - - struct BeforeValues_ReceiveDelegation { - DelegationPoolInternalTest pool; - DelegationInternal delegation; - uint256 delegatedTokens; - uint256 stakingBalance; - uint256 delegatorBalance; - } - function _onTokenTransfer_ReceiveDelegation(address from, uint256 tokens, bytes memory data) internal { - address serviceProvider; - address delegator; - { - (, bytes memory fnData) = abi.decode(data, (uint8, bytes)); - (serviceProvider, delegator) = abi.decode(fnData, (address, address)); - } - - // before - BeforeValues_ReceiveDelegation memory beforeValues; - beforeValues.pool = _getStorage_DelegationPoolInternal(serviceProvider, subgraphDataServiceLegacyAddress, true); - beforeValues.delegation = _getStorage_Delegation( - serviceProvider, - subgraphDataServiceLegacyAddress, - delegator, - true - ); - beforeValues.stakingBalance = token.balanceOf(address(staking)); - beforeValues.delegatorBalance = token.balanceOf(delegator); - beforeValues.delegatedTokens = staking.getDelegatedTokensAvailable( - serviceProvider, - subgraphDataServiceLegacyAddress - ); - - // calc - uint256 calcShares = (beforeValues.pool.tokens == 0 || beforeValues.pool.tokens == beforeValues.pool.tokensThawing) - ? tokens - : ((tokens * beforeValues.pool.shares) / (beforeValues.pool.tokens - beforeValues.pool.tokensThawing)); - - bool earlyExit = (calcShares == 0 || tokens < 1 ether) || - (beforeValues.pool.tokens == 0 && (beforeValues.pool.shares != 0 || beforeValues.pool.sharesThawing != 0)); - - // onTokenTransfer - if (earlyExit) { - vm.expectEmit(); - emit Transfer(address(staking), delegator, tokens); - vm.expectEmit(); - emit IL2StakingBase.TransferredDelegationReturnedToDelegator(serviceProvider, delegator, tokens); - } else { - vm.expectEmit(); - emit IHorizonStakingExtension.StakeDelegated(serviceProvider, delegator, tokens, calcShares); - } - staking.onTokenTransfer(from, tokens, data); - - // after - DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal(serviceProvider, subgraphDataServiceLegacyAddress, true); - DelegationInternal memory afterDelegation = _getStorage_Delegation( - serviceProvider, - subgraphDataServiceLegacyAddress, - delegator, - true - ); - uint256 afterDelegatedTokens = staking.getDelegatedTokensAvailable( - serviceProvider, - subgraphDataServiceLegacyAddress - ); - uint256 afterDelegatorBalance = token.balanceOf(delegator); - uint256 afterStakingBalance = token.balanceOf(address(staking)); - - // assertions - if (earlyExit) { - assertEq(beforeValues.pool.tokens, afterPool.tokens); - assertEq(beforeValues.pool.shares, afterPool.shares); - assertEq(beforeValues.pool.tokensThawing, afterPool.tokensThawing); - assertEq(beforeValues.pool.sharesThawing, afterPool.sharesThawing); - assertEq(0, afterDelegation.shares - beforeValues.delegation.shares); - assertEq(beforeValues.delegatedTokens, afterDelegatedTokens); - assertEq(beforeValues.delegatorBalance + tokens, afterDelegatorBalance); - assertEq(beforeValues.stakingBalance - tokens, afterStakingBalance); - } else { - assertEq(beforeValues.pool.tokens + tokens, afterPool.tokens); - assertEq(beforeValues.pool.shares + calcShares, afterPool.shares); - assertEq(beforeValues.pool.tokensThawing, afterPool.tokensThawing); - assertEq(beforeValues.pool.sharesThawing, afterPool.sharesThawing); - assertEq(calcShares, afterDelegation.shares - beforeValues.delegation.shares); - assertEq(beforeValues.delegatedTokens + tokens, afterDelegatedTokens); - assertEq(beforeValues.delegatorBalance, afterDelegatorBalance); - assertEq(beforeValues.stakingBalance, afterStakingBalance); - } - } - struct BeforeValues_Slash { Provision provision; DelegationPoolInternalTest pool; @@ -1928,11 +1826,6 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { return delegation; } - function _getStorage_CounterpartStakingAddress() internal view returns (address) { - uint256 slot = 24; - return address(uint160(uint256(vm.load(address(staking), bytes32(slot))))); - } - function _setStorage_allocation( IHorizonStakingExtension.Allocation memory allocation, address allocationId, diff --git a/packages/horizon/test/staking/HorizonStaking.t.sol b/packages/horizon/test/staking/HorizonStaking.t.sol index d846d1754..b1b45d118 100644 --- a/packages/horizon/test/staking/HorizonStaking.t.sol +++ b/packages/horizon/test/staking/HorizonStaking.t.sol @@ -31,7 +31,7 @@ contract HorizonStakingTest is HorizonStakingSharedTest { modifier useDelegation(uint256 delegationAmount) { address msgSender; (, msgSender, ) = vm.readCallers(); - vm.assume(delegationAmount > MIN_DELEGATION); + vm.assume(delegationAmount > 1); vm.assume(delegationAmount <= MAX_STAKING_TOKENS); vm.startPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); diff --git a/packages/horizon/test/staking/allocation/close.t.sol b/packages/horizon/test/staking/allocation/close.t.sol index 121307056..ce3cab273 100644 --- a/packages/horizon/test/staking/allocation/close.t.sol +++ b/packages/horizon/test/staking/allocation/close.t.sol @@ -71,7 +71,7 @@ contract HorizonStakingCloseAllocationTest is HorizonStakingTest { function testCloseAllocation_WithDelegation(uint256 tokens, uint256 delegationTokens, uint32 indexingRewardCut) public useIndexer useAllocation(1 ether) { tokens = bound(tokens, 2, MAX_STAKING_TOKENS); - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 0, MAX_STAKING_TOKENS); vm.assume(indexingRewardCut <= MAX_PPM); uint256 legacyAllocationTokens = tokens / 2; diff --git a/packages/horizon/test/staking/delegation/addToPool.t.sol b/packages/horizon/test/staking/delegation/addToPool.t.sol index b9a583ee8..de2647dfc 100644 --- a/packages/horizon/test/staking/delegation/addToPool.t.sol +++ b/packages/horizon/test/staking/delegation/addToPool.t.sol @@ -9,7 +9,6 @@ import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingDelegationAddToPoolTest is HorizonStakingTest { modifier useValidDelegationAmount(uint256 tokens) { - vm.assume(tokens > MIN_DELEGATION); vm.assume(tokens <= MAX_STAKING_TOKENS); _; } @@ -29,6 +28,8 @@ contract HorizonStakingDelegationAddToPoolTest is HorizonStakingTest { uint256 delegationAmount, uint256 addToPoolAmount ) public useIndexer useProvision(amount, 0, 0) useValidDelegationAmount(delegationAmount) useValidAddToPoolAmount(addToPoolAmount) { + delegationAmount = bound(delegationAmount, 1, MAX_STAKING_TOKENS); + // Initialize delegation pool resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); diff --git a/packages/horizon/test/staking/delegation/delegate.t.sol b/packages/horizon/test/staking/delegation/delegate.t.sol index d5e101bca..5deb9b495 100644 --- a/packages/horizon/test/staking/delegation/delegate.t.sol +++ b/packages/horizon/test/staking/delegation/delegate.t.sol @@ -59,25 +59,9 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { staking.delegate(users.indexer, subgraphDataServiceAddress, 0, 0); } - function testDelegate_RevertWhen_BelowMinimum( - uint256 amount, - uint256 delegationAmount - ) public useIndexer useProvision(amount, 0, 0) { - vm.startPrank(users.delegator); - delegationAmount = bound(delegationAmount, 1, MIN_DELEGATION - 1); - token.approve(address(staking), delegationAmount); - bytes memory expectedError = abi.encodeWithSignature( - "HorizonStakingInsufficientTokens(uint256,uint256)", - delegationAmount, - MIN_DELEGATION - ); - vm.expectRevert(expectedError); - staking.delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); - } - function testDelegate_LegacySubgraphService(uint256 amount, uint256 delegationAmount) public useIndexer { amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); - delegationAmount = bound(delegationAmount, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationAmount = bound(delegationAmount, 1, MAX_STAKING_TOKENS); _createProvision(users.indexer, subgraphDataServiceLegacyAddress, amount, 0, 0); resetPrank(users.delegator); @@ -88,7 +72,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { uint256 tokens, uint256 delegationTokens ) public useIndexer useProvision(tokens, 0, 0) useDelegationSlashing() { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); @@ -109,7 +93,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { uint256 tokens, uint256 delegationTokens ) public useIndexer useProvision(tokens, 0, 0) useDelegationSlashing() { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); diff --git a/packages/horizon/test/staking/delegation/undelegate.t.sol b/packages/horizon/test/staking/delegation/undelegate.t.sol index 1ed6469b5..c5d61b6e5 100644 --- a/packages/horizon/test/staking/delegation/undelegate.t.sol +++ b/packages/horizon/test/staking/delegation/undelegate.t.sol @@ -27,14 +27,13 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { uint256 undelegateSteps ) public useIndexer useProvision(amount, 0, 0) { undelegateSteps = bound(undelegateSteps, 1, 10); - delegationAmount = bound(delegationAmount, MIN_DELEGATION + 10 wei, MAX_STAKING_TOKENS); + delegationAmount = bound(delegationAmount, 10 wei, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); - // there is a min delegation amount of 1 ether after undelegating - uint256 undelegateAmount = (delegation.shares - 1 ether) / undelegateSteps; + uint256 undelegateAmount = delegation.shares / undelegateSteps; for (uint i = 0; i < undelegateSteps; i++) { _undelegate(users.indexer, subgraphDataServiceAddress, undelegateAmount); } @@ -96,25 +95,9 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { staking.undelegate(users.indexer, subgraphDataServiceAddress, overDelegationShares); } - function testUndelegate_RevertWhen_UndelegateLeavesInsufficientTokens( - uint256 delegationAmount, - uint256 withdrawShares - ) public useIndexer useProvision(10_000_000 ether, 0, 0) useDelegation(delegationAmount) { - resetPrank(users.delegator); - uint256 minShares = delegationAmount - MIN_DELEGATION + 1; - withdrawShares = bound(withdrawShares, minShares, delegationAmount - 1); - bytes memory expectedError = abi.encodeWithSignature( - "HorizonStakingInsufficientTokens(uint256,uint256)", - delegationAmount - withdrawShares, - MIN_DELEGATION - ); - vm.expectRevert(expectedError); - staking.undelegate(users.indexer, subgraphDataServiceAddress, withdrawShares); - } - function testUndelegate_LegacySubgraphService(uint256 amount, uint256 delegationAmount) public useIndexer { amount = bound(amount, 1, MAX_STAKING_TOKENS); - delegationAmount = bound(delegationAmount, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationAmount = bound(delegationAmount, 1, MAX_STAKING_TOKENS); _createProvision(users.indexer, subgraphDataServiceLegacyAddress, amount, 0, 0); resetPrank(users.delegator); @@ -128,7 +111,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { uint256 tokens, uint256 delegationTokens ) public useIndexer useProvision(tokens, 0, 0) useDelegationSlashing() { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); diff --git a/packages/horizon/test/staking/delegation/withdraw.t.sol b/packages/horizon/test/staking/delegation/withdraw.t.sol index 0419c85fc..866f49942 100644 --- a/packages/horizon/test/staking/delegation/withdraw.t.sol +++ b/packages/horizon/test/staking/delegation/withdraw.t.sol @@ -19,15 +19,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { vm.startPrank(users.delegator); DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); shares = bound(shares, 1, delegation.shares); - - if (shares != delegation.shares) { - DelegationPoolInternalTest memory pool = _getStorage_DelegationPoolInternal(users.indexer, subgraphDataServiceAddress, false); - uint256 tokens = (shares * (pool.tokens - pool.tokensThawing)) / pool.shares; - uint256 newTokensThawing = pool.tokensThawing + tokens; - uint256 remainingTokens = (delegation.shares * (pool.tokens - newTokensThawing)) / pool.shares; - vm.assume(remainingTokens >= MIN_DELEGATION); - } - + _undelegate(users.indexer, subgraphDataServiceAddress, shares); _; } @@ -137,7 +129,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { } function testWithdrawDelegation_LegacySubgraphService(uint256 delegationAmount) public useIndexer { - delegationAmount = bound(delegationAmount, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationAmount = bound(delegationAmount, 1, MAX_STAKING_TOKENS); _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 10_000_000 ether, 0, MAX_THAWING_PERIOD); resetPrank(users.delegator); @@ -157,7 +149,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { uint256 tokens, uint256 delegationTokens ) public useIndexer useProvision(tokens, 0, MAX_THAWING_PERIOD) useDelegationSlashing() { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); diff --git a/packages/horizon/test/staking/governance/governance.t.sol b/packages/horizon/test/staking/governance/governance.t.sol index 401828a3e..434364a80 100644 --- a/packages/horizon/test/staking/governance/governance.t.sol +++ b/packages/horizon/test/staking/governance/governance.t.sol @@ -62,16 +62,4 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { vm.expectRevert(expectedError); staking.setMaxThawingPeriod(MAX_THAWING_PERIOD); } - - function testGovernance_SetCounterpartStakingAddress(address counterpartStakingAddress) public useGovernor { - _setCounterpartStakingAddress(counterpartStakingAddress); - } - - function testGovernance_RevertWhen_SetCounterpartStakingAddress_NotGovernor( - address counterpartStakingAddress - ) public useIndexer { - bytes memory expectedError = abi.encodeWithSignature("ManagedOnlyGovernor()"); - vm.expectRevert(expectedError); - staking.setCounterpartStakingAddress(counterpartStakingAddress); - } } \ No newline at end of file diff --git a/packages/horizon/test/staking/slash/slash.t.sol b/packages/horizon/test/staking/slash/slash.t.sol index bf1f2fb1e..aea8db986 100644 --- a/packages/horizon/test/staking/slash/slash.t.sol +++ b/packages/horizon/test/staking/slash/slash.t.sol @@ -54,7 +54,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { uint256 delegationTokens ) public useIndexer useProvision(tokens, MAX_MAX_VERIFIER_CUT, 0) { vm.assume(slashTokens > tokens); - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); verifierCutAmount = bound(verifierCutAmount, 0, MAX_MAX_VERIFIER_CUT); uint256 maxVerifierTokens = (tokens * MAX_MAX_VERIFIER_CUT) / MAX_PPM; vm.assume(verifierCutAmount <= maxVerifierTokens); @@ -72,8 +72,8 @@ contract HorizonStakingSlashTest is HorizonStakingTest { uint256 verifierCutAmount, uint256 delegationTokens ) public useIndexer useProvision(tokens, MAX_MAX_VERIFIER_CUT, 0) useDelegationSlashing() { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); - slashTokens = bound(slashTokens, tokens + 1, tokens + delegationTokens); + delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS); + slashTokens = bound(slashTokens, tokens + 1, tokens + 1 + delegationTokens); verifierCutAmount = bound(verifierCutAmount, 0, MAX_MAX_VERIFIER_CUT); uint256 maxVerifierTokens = (tokens * MAX_MAX_VERIFIER_CUT) / MAX_PPM; vm.assume(verifierCutAmount <= maxVerifierTokens); @@ -90,7 +90,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { uint256 slashTokens, uint256 delegationTokens ) public useIndexer useProvision(tokens, MAX_MAX_VERIFIER_CUT, 0) { - delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); + delegationTokens = bound(delegationTokens, 0, MAX_STAKING_TOKENS); vm.assume(slashTokens > tokens + delegationTokens); vm.startPrank(subgraphDataServiceAddress); diff --git a/packages/horizon/test/staking/transfer-tools/ttools.t.sol b/packages/horizon/test/staking/transfer-tools/ttools.t.sol deleted file mode 100644 index 11bb6e62d..000000000 --- a/packages/horizon/test/staking/transfer-tools/ttools.t.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.27; - -import "forge-std/Test.sol"; - -import { HorizonStakingTest } from "../HorizonStaking.t.sol"; -import { IL2StakingTypes } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingTypes.sol"; - -contract HorizonStakingTransferToolsTest is HorizonStakingTest { - /* - * TESTS - */ - - function testOnTransfer_RevertWhen_InvalidCaller() public { - bytes memory data = abi.encode(uint8(0), new bytes(0)); // Valid codes are 0 and 1 - vm.expectRevert(bytes("ONLY_GATEWAY")); - staking.onTokenTransfer(counterpartStaking, 0, data); - } - - function testOnTransfer_RevertWhen_InvalidCounterpart() public { - resetPrank(graphTokenGatewayAddress); - - bytes memory data = abi.encode(uint8(0), new bytes(0)); // Valid codes are 0 and 1 - vm.expectRevert(bytes("ONLY_L1_STAKING_THROUGH_BRIDGE")); - staking.onTokenTransfer(address(staking), 0, data); - } - - function testOnTransfer_RevertWhen_InvalidData() public { - resetPrank(graphTokenGatewayAddress); - - vm.expectRevert(); - staking.onTokenTransfer(counterpartStaking, 0, new bytes(0)); - } - - function testOnTransfer_RevertWhen_InvalidCode() public { - resetPrank(graphTokenGatewayAddress); - - bytes memory data = abi.encode(uint8(2), new bytes(0)); // Valid codes are 0 and 1 - vm.expectRevert(bytes("INVALID_CODE")); - staking.onTokenTransfer(counterpartStaking, 0, data); - } - - function testOnTransfer_RevertWhen_ProvisionNotFound(uint256 amount) public { - amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); - - resetPrank(graphTokenGatewayAddress); - bytes memory data = abi.encode( - uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), - abi.encode(users.indexer, users.delegator) - ); - vm.expectRevert(bytes("!provision")); - staking.onTokenTransfer(counterpartStaking, amount, data); - } - - function testOnTransfer_ReceiveDelegation_RevertWhen_InvalidData() public { - resetPrank(graphTokenGatewayAddress); - - bytes memory data = abi.encode(uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), new bytes(0)); - vm.expectRevert(); - staking.onTokenTransfer(counterpartStaking, 0, data); - } - - function testOnTransfer_ReceiveDelegation(uint256 amount) public { - amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); - - // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 - resetPrank(users.indexer); - _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 0); - - resetPrank(users.delegator); - _delegate(users.indexer, 1 ether); - - // send amount to staking contract - this should be done by the bridge - resetPrank(users.delegator); - token.transfer(address(staking), amount); - - resetPrank(graphTokenGatewayAddress); - bytes memory data = abi.encode( - uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), - abi.encode(users.indexer, users.delegator) - ); - _onTokenTransfer_ReceiveDelegation(counterpartStaking, amount, data); - } - - function testOnTransfer_ReceiveDelegation_WhenThawing(uint256 amount) public { - amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); - uint256 originalDelegationAmount = 10 ether; - - // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 - resetPrank(users.indexer); - _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); - - resetPrank(users.delegator); - _delegate(users.indexer, originalDelegationAmount); - - // send amount to staking contract - this should be done by the bridge - resetPrank(users.delegator); - token.transfer(address(staking), amount); - - // thaw some delegation before receiving new delegation from L1 - resetPrank(users.delegator); - _undelegate(users.indexer, originalDelegationAmount / 10); - - resetPrank(graphTokenGatewayAddress); - bytes memory data = abi.encode( - uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), - abi.encode(users.indexer, users.delegator) - ); - _onTokenTransfer_ReceiveDelegation(counterpartStaking, amount, data); - } - - function testOnTransfer_ReceiveDelegation_WhenInvalidPool(uint256 amount) public useDelegationSlashing() { - amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); - uint256 originalDelegationAmount = 10 ether; - uint256 provisionSize = 100 ether; - - // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 - resetPrank(users.indexer); - _createProvision(users.indexer, subgraphDataServiceLegacyAddress, provisionSize, 0, 1 days); - - // initialize the delegation pool - resetPrank(users.delegator); - _delegate(users.indexer, originalDelegationAmount); - - // slash the entire provision - resetPrank(subgraphDataServiceLegacyAddress); - _slash(users.indexer, subgraphDataServiceLegacyAddress, provisionSize + originalDelegationAmount, 0); - - // send amount to staking contract - this should be done by the bridge - resetPrank(users.delegator); - token.transfer(address(staking), amount); - - resetPrank(graphTokenGatewayAddress); - bytes memory data = abi.encode( - uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), - abi.encode(users.indexer, users.delegator) - ); - _onTokenTransfer_ReceiveDelegation(counterpartStaking, amount, data); - } - function testOnTransfer_ReceiveDelegation_WhenAllThawing(uint256 amountReceived, uint256 amountDelegated) public { - amountReceived = bound(amountReceived, 1 ether, MAX_STAKING_TOKENS); - amountDelegated = bound(amountDelegated, 1 ether, MAX_STAKING_TOKENS); - - // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 - resetPrank(users.indexer); - _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); - - resetPrank(users.delegator); - _delegate(users.indexer, amountDelegated); - - // send amount to staking contract - this should be done by the bridge - resetPrank(users.delegator); - token.transfer(address(staking), amountReceived); - - // thaw all delegation before receiving new delegation from L1 - resetPrank(users.delegator); - _undelegate(users.indexer, amountDelegated); - - resetPrank(graphTokenGatewayAddress); - bytes memory data = abi.encode( - uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), - abi.encode(users.indexer, users.delegator) - ); - _onTokenTransfer_ReceiveDelegation(counterpartStaking, amountReceived, data); - } -} diff --git a/packages/horizon/test/utils/Constants.sol b/packages/horizon/test/utils/Constants.sol index f08fa4ec1..270bb1e43 100644 --- a/packages/horizon/test/utils/Constants.sol +++ b/packages/horizon/test/utils/Constants.sol @@ -14,7 +14,6 @@ abstract contract Constants { uint256 internal constant MAX_THAW_REQUESTS = 100; uint32 internal constant MAX_MAX_VERIFIER_CUT = 1000000; // 100% in parts per million uint64 internal constant MAX_THAWING_PERIOD = 28 days; - uint256 internal constant MIN_DELEGATION = 1 ether; uint32 internal constant THAWING_PERIOD_IN_BLOCKS = 300; // Epoch manager uint256 internal constant EPOCH_LENGTH = 1; diff --git a/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol b/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol index 089fdd9a9..d0f28fcec 100644 --- a/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol +++ b/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol @@ -41,8 +41,7 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest { } modifier useDelegation(uint256 tokens) { - // 1e18 is the minimum delegation amount until L2 transfers are removed - vm.assume(tokens > 1e18); + vm.assume(tokens > 1); vm.assume(tokens < 10_000_000_000 ether); (, address msgSender,) = vm.readCallers(); resetPrank(users.delegator);