diff --git a/packages/contracts/contracts/arbitrum/ITokenGateway.sol b/packages/contracts/contracts/arbitrum/ITokenGateway.sol index 96080858c..3b12e578e 100644 --- a/packages/contracts/contracts/arbitrum/ITokenGateway.sol +++ b/packages/contracts/contracts/arbitrum/ITokenGateway.sol @@ -23,7 +23,7 @@ * */ -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface ITokenGateway { /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated diff --git a/packages/contracts/contracts/curation/ICuration.sol b/packages/contracts/contracts/curation/ICuration.sol index 635b45a0c..fe2f0e929 100644 --- a/packages/contracts/contracts/curation/ICuration.sol +++ b/packages/contracts/contracts/curation/ICuration.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; /** * @title Curation Interface diff --git a/packages/contracts/contracts/epochs/IEpochManager.sol b/packages/contracts/contracts/epochs/IEpochManager.sol index 23c55f15b..c65280d59 100644 --- a/packages/contracts/contracts/epochs/IEpochManager.sol +++ b/packages/contracts/contracts/epochs/IEpochManager.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IEpochManager { // -- Configuration -- diff --git a/packages/contracts/contracts/gateway/ICallhookReceiver.sol b/packages/contracts/contracts/gateway/ICallhookReceiver.sol index 0fc27cf23..8d003cb76 100644 --- a/packages/contracts/contracts/gateway/ICallhookReceiver.sol +++ b/packages/contracts/contracts/gateway/ICallhookReceiver.sol @@ -6,7 +6,7 @@ * be allowlisted by the governor, but also implement this interface that contains * the function that will actually be called by the L2GraphTokenGateway. */ -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface ICallhookReceiver { /** diff --git a/packages/contracts/contracts/governance/Controller.sol b/packages/contracts/contracts/governance/Controller.sol index a24b96b4e..2b71fd885 100644 --- a/packages/contracts/contracts/governance/Controller.sol +++ b/packages/contracts/contracts/governance/Controller.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { IController } from "./IController.sol"; import { IManaged } from "./IManaged.sol"; diff --git a/packages/contracts/contracts/governance/Governed.sol b/packages/contracts/contracts/governance/Governed.sol index c9cf940db..76a3247dd 100644 --- a/packages/contracts/contracts/governance/Governed.sol +++ b/packages/contracts/contracts/governance/Governed.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; /** * @title Graph Governance contract diff --git a/packages/contracts/contracts/governance/IController.sol b/packages/contracts/contracts/governance/IController.sol index 093a0303a..6ab72010e 100644 --- a/packages/contracts/contracts/governance/IController.sol +++ b/packages/contracts/contracts/governance/IController.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IController { function getGovernor() external view returns (address); diff --git a/packages/contracts/contracts/governance/IManaged.sol b/packages/contracts/contracts/governance/IManaged.sol index 59f44dd9a..ff6625d81 100644 --- a/packages/contracts/contracts/governance/IManaged.sol +++ b/packages/contracts/contracts/governance/IManaged.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { IController } from "./IController.sol"; diff --git a/packages/contracts/contracts/governance/Pausable.sol b/packages/contracts/contracts/governance/Pausable.sol index 260d658af..6c5d2fd2c 100644 --- a/packages/contracts/contracts/governance/Pausable.sol +++ b/packages/contracts/contracts/governance/Pausable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; abstract contract Pausable { /** diff --git a/packages/contracts/contracts/l2/staking/IL2StakingBase.sol b/packages/contracts/contracts/l2/staking/IL2StakingBase.sol index 8c0b145de..f5c33c2d0 100644 --- a/packages/contracts/contracts/l2/staking/IL2StakingBase.sol +++ b/packages/contracts/contracts/l2/staking/IL2StakingBase.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { ICallhookReceiver } from "../../gateway/ICallhookReceiver.sol"; diff --git a/packages/contracts/contracts/l2/staking/IL2StakingTypes.sol b/packages/contracts/contracts/l2/staking/IL2StakingTypes.sol index 086f88e77..500694e89 100644 --- a/packages/contracts/contracts/l2/staking/IL2StakingTypes.sol +++ b/packages/contracts/contracts/l2/staking/IL2StakingTypes.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IL2StakingTypes { /// @dev Message codes for the L1 -> L2 bridge callhook diff --git a/packages/contracts/contracts/rewards/IRewardsIssuer.sol b/packages/contracts/contracts/rewards/IRewardsIssuer.sol index 4bcfa6ea0..705ce8db8 100644 --- a/packages/contracts/contracts/rewards/IRewardsIssuer.sol +++ b/packages/contracts/contracts/rewards/IRewardsIssuer.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IRewardsIssuer { /** diff --git a/packages/contracts/contracts/rewards/IRewardsManager.sol b/packages/contracts/contracts/rewards/IRewardsManager.sol index 4030ad2e5..3b6bf3ff6 100644 --- a/packages/contracts/contracts/rewards/IRewardsManager.sol +++ b/packages/contracts/contracts/rewards/IRewardsManager.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IRewardsManager { /** diff --git a/packages/contracts/contracts/rewards/RewardsManagerStorage.sol b/packages/contracts/contracts/rewards/RewardsManagerStorage.sol index c40ad38f4..ded325593 100644 --- a/packages/contracts/contracts/rewards/RewardsManagerStorage.sol +++ b/packages/contracts/contracts/rewards/RewardsManagerStorage.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import "./IRewardsManager.sol"; import "../governance/Managed.sol"; diff --git a/packages/contracts/contracts/token/IGraphToken.sol b/packages/contracts/contracts/token/IGraphToken.sol index f6f1c00f6..df3b7643f 100644 --- a/packages/contracts/contracts/token/IGraphToken.sol +++ b/packages/contracts/contracts/token/IGraphToken.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/packages/contracts/contracts/upgrades/GraphProxy.sol b/packages/contracts/contracts/upgrades/GraphProxy.sol index cb52ab1dc..d6fbfac7f 100644 --- a/packages/contracts/contracts/upgrades/GraphProxy.sol +++ b/packages/contracts/contracts/upgrades/GraphProxy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { GraphProxyStorage } from "./GraphProxyStorage.sol"; diff --git a/packages/contracts/contracts/upgrades/GraphProxyAdmin.sol b/packages/contracts/contracts/upgrades/GraphProxyAdmin.sol index 55bbd2176..db8e9dcb3 100644 --- a/packages/contracts/contracts/upgrades/GraphProxyAdmin.sol +++ b/packages/contracts/contracts/upgrades/GraphProxyAdmin.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { Governed } from "../governance/Governed.sol"; diff --git a/packages/contracts/contracts/upgrades/GraphProxyStorage.sol b/packages/contracts/contracts/upgrades/GraphProxyStorage.sol index cd076c118..7871e4996 100644 --- a/packages/contracts/contracts/upgrades/GraphProxyStorage.sol +++ b/packages/contracts/contracts/upgrades/GraphProxyStorage.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; /** * @title Graph Proxy Storage diff --git a/packages/contracts/contracts/upgrades/GraphUpgradeable.sol b/packages/contracts/contracts/upgrades/GraphUpgradeable.sol index ffc47aa40..60dfbe888 100644 --- a/packages/contracts/contracts/upgrades/GraphUpgradeable.sol +++ b/packages/contracts/contracts/upgrades/GraphUpgradeable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import { IGraphProxy } from "./IGraphProxy.sol"; diff --git a/packages/contracts/contracts/upgrades/IGraphProxy.sol b/packages/contracts/contracts/upgrades/IGraphProxy.sol index e81722e3f..4f501ed7c 100644 --- a/packages/contracts/contracts/upgrades/IGraphProxy.sol +++ b/packages/contracts/contracts/upgrades/IGraphProxy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; interface IGraphProxy { function admin() external returns (address); diff --git a/packages/contracts/contracts/utils/TokenUtils.sol b/packages/contracts/contracts/utils/TokenUtils.sol index a34059a1d..fb125613a 100644 --- a/packages/contracts/contracts/utils/TokenUtils.sol +++ b/packages/contracts/contracts/utils/TokenUtils.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.7.6 || 0.8.26; +pragma solidity ^0.7.6 || 0.8.27; import "../token/IGraphToken.sol"; diff --git a/packages/horizon/contracts/data-service/DataService.sol b/packages/horizon/contracts/data-service/DataService.sol index c87cf5e40..8a06ad4ea 100644 --- a/packages/horizon/contracts/data-service/DataService.sol +++ b/packages/horizon/contracts/data-service/DataService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataService } from "./interfaces/IDataService.sol"; diff --git a/packages/horizon/contracts/data-service/DataServiceStorage.sol b/packages/horizon/contracts/data-service/DataServiceStorage.sol index 03b060515..a0271443c 100644 --- a/packages/horizon/contracts/data-service/DataServiceStorage.sol +++ b/packages/horizon/contracts/data-service/DataServiceStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; abstract contract DataServiceV1Storage { /// @dev Gap to allow adding variables in future upgrades diff --git a/packages/horizon/contracts/data-service/extensions/DataServiceFees.sol b/packages/horizon/contracts/data-service/extensions/DataServiceFees.sol index bcec6f828..e0ea587f4 100644 --- a/packages/horizon/contracts/data-service/extensions/DataServiceFees.sol +++ b/packages/horizon/contracts/data-service/extensions/DataServiceFees.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataServiceFees } from "../interfaces/IDataServiceFees.sol"; diff --git a/packages/horizon/contracts/data-service/extensions/DataServiceFeesStorage.sol b/packages/horizon/contracts/data-service/extensions/DataServiceFeesStorage.sol index 483b67b80..cb4f908dc 100644 --- a/packages/horizon/contracts/data-service/extensions/DataServiceFeesStorage.sol +++ b/packages/horizon/contracts/data-service/extensions/DataServiceFeesStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataServiceFees } from "../interfaces/IDataServiceFees.sol"; diff --git a/packages/horizon/contracts/data-service/extensions/DataServicePausable.sol b/packages/horizon/contracts/data-service/extensions/DataServicePausable.sol index 404876dc6..4d88cb72e 100644 --- a/packages/horizon/contracts/data-service/extensions/DataServicePausable.sol +++ b/packages/horizon/contracts/data-service/extensions/DataServicePausable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataServicePausable } from "../interfaces/IDataServicePausable.sol"; diff --git a/packages/horizon/contracts/data-service/extensions/DataServicePausableUpgradeable.sol b/packages/horizon/contracts/data-service/extensions/DataServicePausableUpgradeable.sol index 53003ebfc..52f27d9c4 100644 --- a/packages/horizon/contracts/data-service/extensions/DataServicePausableUpgradeable.sol +++ b/packages/horizon/contracts/data-service/extensions/DataServicePausableUpgradeable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataServicePausable } from "../interfaces/IDataServicePausable.sol"; diff --git a/packages/horizon/contracts/data-service/extensions/DataServiceRescuable.sol b/packages/horizon/contracts/data-service/extensions/DataServiceRescuable.sol index 96fae4c26..081950b8d 100644 --- a/packages/horizon/contracts/data-service/extensions/DataServiceRescuable.sol +++ b/packages/horizon/contracts/data-service/extensions/DataServiceRescuable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/packages/horizon/contracts/data-service/interfaces/IDataService.sol b/packages/horizon/contracts/data-service/interfaces/IDataService.sol index 258fc336c..b7e1028f0 100644 --- a/packages/horizon/contracts/data-service/interfaces/IDataService.sol +++ b/packages/horizon/contracts/data-service/interfaces/IDataService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "../../interfaces/IGraphPayments.sol"; diff --git a/packages/horizon/contracts/data-service/interfaces/IDataServiceFees.sol b/packages/horizon/contracts/data-service/interfaces/IDataServiceFees.sol index 5e84863e6..483f11274 100644 --- a/packages/horizon/contracts/data-service/interfaces/IDataServiceFees.sol +++ b/packages/horizon/contracts/data-service/interfaces/IDataServiceFees.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataService } from "./IDataService.sol"; diff --git a/packages/horizon/contracts/data-service/interfaces/IDataServicePausable.sol b/packages/horizon/contracts/data-service/interfaces/IDataServicePausable.sol index 04e931749..bd27ca848 100644 --- a/packages/horizon/contracts/data-service/interfaces/IDataServicePausable.sol +++ b/packages/horizon/contracts/data-service/interfaces/IDataServicePausable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataService } from "./IDataService.sol"; diff --git a/packages/horizon/contracts/data-service/interfaces/IDataServiceRescuable.sol b/packages/horizon/contracts/data-service/interfaces/IDataServiceRescuable.sol index ba21f3019..811d3b92e 100644 --- a/packages/horizon/contracts/data-service/interfaces/IDataServiceRescuable.sol +++ b/packages/horizon/contracts/data-service/interfaces/IDataServiceRescuable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataService } from "./IDataService.sol"; diff --git a/packages/horizon/contracts/data-service/libraries/ProvisionTracker.sol b/packages/horizon/contracts/data-service/libraries/ProvisionTracker.sol index 08ac970bb..fef392302 100644 --- a/packages/horizon/contracts/data-service/libraries/ProvisionTracker.sol +++ b/packages/horizon/contracts/data-service/libraries/ProvisionTracker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStaking } from "../../interfaces/IHorizonStaking.sol"; diff --git a/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol b/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol index ef7c9bcf1..d1cf94287 100644 --- a/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol +++ b/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStaking } from "../../interfaces/IHorizonStaking.sol"; diff --git a/packages/horizon/contracts/data-service/utilities/ProvisionManagerStorage.sol b/packages/horizon/contracts/data-service/utilities/ProvisionManagerStorage.sol index 8649055db..0a6bed2be 100644 --- a/packages/horizon/contracts/data-service/utilities/ProvisionManagerStorage.sol +++ b/packages/horizon/contracts/data-service/utilities/ProvisionManagerStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title Storage layout for the {ProvisionManager} helper contract. diff --git a/packages/horizon/contracts/interfaces/IGraphPayments.sol b/packages/horizon/contracts/interfaces/IGraphPayments.sol index ab89c94f8..f446d6f52 100644 --- a/packages/horizon/contracts/interfaces/IGraphPayments.sol +++ b/packages/horizon/contracts/interfaces/IGraphPayments.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title Interface for the {GraphPayments} contract diff --git a/packages/horizon/contracts/interfaces/IGraphProxyAdmin.sol b/packages/horizon/contracts/interfaces/IGraphProxyAdmin.sol index 6bc063057..de812fa87 100644 --- a/packages/horizon/contracts/interfaces/IGraphProxyAdmin.sol +++ b/packages/horizon/contracts/interfaces/IGraphProxyAdmin.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title IGraphProxyAdmin diff --git a/packages/horizon/contracts/interfaces/IHorizonStaking.sol b/packages/horizon/contracts/interfaces/IHorizonStaking.sol index 1cf1b6283..e38c3e451 100644 --- a/packages/horizon/contracts/interfaces/IHorizonStaking.sol +++ b/packages/horizon/contracts/interfaces/IHorizonStaking.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStakingTypes } from "./internal/IHorizonStakingTypes.sol"; import { IHorizonStakingMain } from "./internal/IHorizonStakingMain.sol"; diff --git a/packages/horizon/contracts/interfaces/IPaymentsCollector.sol b/packages/horizon/contracts/interfaces/IPaymentsCollector.sol index 176f6284b..bcd67df0a 100644 --- a/packages/horizon/contracts/interfaces/IPaymentsCollector.sol +++ b/packages/horizon/contracts/interfaces/IPaymentsCollector.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "./IGraphPayments.sol"; diff --git a/packages/horizon/contracts/interfaces/IPaymentsEscrow.sol b/packages/horizon/contracts/interfaces/IPaymentsEscrow.sol index 7ab5ff023..4b98cf0a5 100644 --- a/packages/horizon/contracts/interfaces/IPaymentsEscrow.sol +++ b/packages/horizon/contracts/interfaces/IPaymentsEscrow.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "./IGraphPayments.sol"; @@ -84,12 +84,7 @@ interface IPaymentsEscrow { * @param tokens The amount of tokens being thawed * @param thawEndTimestamp The timestamp at which the thawing period ends */ - event Thaw( - address indexed payer, - address indexed receiver, - uint256 tokens, - uint256 thawEndTimestamp - ); + event Thaw(address indexed payer, address indexed receiver, uint256 tokens, uint256 thawEndTimestamp); /** * @notice Emitted when a payer withdraws funds from the escrow for a payer-receiver pair diff --git a/packages/horizon/contracts/interfaces/ITAPCollector.sol b/packages/horizon/contracts/interfaces/ITAPCollector.sol index 28561e2ce..e7b5bc4fd 100644 --- a/packages/horizon/contracts/interfaces/ITAPCollector.sol +++ b/packages/horizon/contracts/interfaces/ITAPCollector.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IPaymentsCollector } from "./IPaymentsCollector.sol"; diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingBase.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingBase.sol index 522ddf44e..e221dc2cf 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingBase.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingBase.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStakingTypes } from "./IHorizonStakingTypes.sol"; import { IGraphPayments } from "../IGraphPayments.sol"; diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol index 2df5cd82a..6e29cb5c9 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IRewardsIssuer } from "@graphprotocol/contracts/contracts/rewards/IRewardsIssuer.sol"; import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol index bbbfa07b6..418459d9f 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "../../interfaces/IGraphPayments.sol"; diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol index 5c2f3f5e7..eef8098e6 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /* solhint-disable var-name-mixedcase */ // TODO: create custom var-name-mixedcase diff --git a/packages/horizon/contracts/libraries/Denominations.sol b/packages/horizon/contracts/libraries/Denominations.sol index fe5cf2d05..675194b1e 100644 --- a/packages/horizon/contracts/libraries/Denominations.sol +++ b/packages/horizon/contracts/libraries/Denominations.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title Denominations library diff --git a/packages/horizon/contracts/libraries/LibFixedMath.sol b/packages/horizon/contracts/libraries/LibFixedMath.sol index 1a941ed11..f23329b5e 100644 --- a/packages/horizon/contracts/libraries/LibFixedMath.sol +++ b/packages/horizon/contracts/libraries/LibFixedMath.sol @@ -18,7 +18,7 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.26; +pragma solidity 0.8.27; // solhint-disable indent /// @dev Signed, fixed-point, 127-bit precision math library. diff --git a/packages/horizon/contracts/libraries/LinkedList.sol b/packages/horizon/contracts/libraries/LinkedList.sol index 13607e257..aa72b3fcf 100644 --- a/packages/horizon/contracts/libraries/LinkedList.sol +++ b/packages/horizon/contracts/libraries/LinkedList.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title LinkedList library diff --git a/packages/horizon/contracts/libraries/MathUtils.sol b/packages/horizon/contracts/libraries/MathUtils.sol index a5ad3f41a..1a5599fb6 100644 --- a/packages/horizon/contracts/libraries/MathUtils.sol +++ b/packages/horizon/contracts/libraries/MathUtils.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title MathUtils Library diff --git a/packages/horizon/contracts/libraries/PPMMath.sol b/packages/horizon/contracts/libraries/PPMMath.sol index b02230628..5bd636add 100644 --- a/packages/horizon/contracts/libraries/PPMMath.sol +++ b/packages/horizon/contracts/libraries/PPMMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title PPMMath library diff --git a/packages/horizon/contracts/libraries/UintRange.sol b/packages/horizon/contracts/libraries/UintRange.sol index 12fe10176..b2caf779c 100644 --- a/packages/horizon/contracts/libraries/UintRange.sol +++ b/packages/horizon/contracts/libraries/UintRange.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title UintRange library diff --git a/packages/horizon/contracts/mocks/ControllerMock.sol b/packages/horizon/contracts/mocks/ControllerMock.sol index 82673dca6..557b1eff6 100644 --- a/packages/horizon/contracts/mocks/ControllerMock.sol +++ b/packages/horizon/contracts/mocks/ControllerMock.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IController } from "@graphprotocol/contracts/contracts/governance/IController.sol"; import { IManaged } from "@graphprotocol/contracts/contracts/governance/IManaged.sol"; diff --git a/packages/horizon/contracts/mocks/CurationMock.sol b/packages/horizon/contracts/mocks/CurationMock.sol index 402a07e4c..996f971b1 100644 --- a/packages/horizon/contracts/mocks/CurationMock.sol +++ b/packages/horizon/contracts/mocks/CurationMock.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { MockGRTToken } from "./MockGRTToken.sol"; diff --git a/packages/horizon/contracts/mocks/EpochManagerMock.sol b/packages/horizon/contracts/mocks/EpochManagerMock.sol index b76714e50..12f694a5e 100644 --- a/packages/horizon/contracts/mocks/EpochManagerMock.sol +++ b/packages/horizon/contracts/mocks/EpochManagerMock.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IEpochManager } from "@graphprotocol/contracts/contracts/epochs/IEpochManager.sol"; diff --git a/packages/horizon/contracts/mocks/MockGRTToken.sol b/packages/horizon/contracts/mocks/MockGRTToken.sol index 1d5649f29..235999ae5 100644 --- a/packages/horizon/contracts/mocks/MockGRTToken.sol +++ b/packages/horizon/contracts/mocks/MockGRTToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; diff --git a/packages/horizon/contracts/mocks/RewardsManagerMock.sol b/packages/horizon/contracts/mocks/RewardsManagerMock.sol index 2388d99b6..272584ca4 100644 --- a/packages/horizon/contracts/mocks/RewardsManagerMock.sol +++ b/packages/horizon/contracts/mocks/RewardsManagerMock.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { MockGRTToken } from "./MockGRTToken.sol"; diff --git a/packages/horizon/contracts/payments/GraphPayments.sol b/packages/horizon/contracts/payments/GraphPayments.sol index 4072c2616..b7cb34db7 100644 --- a/packages/horizon/contracts/payments/GraphPayments.sol +++ b/packages/horizon/contracts/payments/GraphPayments.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IGraphPayments } from "../interfaces/IGraphPayments.sol"; diff --git a/packages/horizon/contracts/payments/PaymentsEscrow.sol b/packages/horizon/contracts/payments/PaymentsEscrow.sol index 5045be86d..5d0694346 100644 --- a/packages/horizon/contracts/payments/PaymentsEscrow.sol +++ b/packages/horizon/contracts/payments/PaymentsEscrow.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IGraphPayments } from "../interfaces/IGraphPayments.sol"; @@ -18,12 +18,7 @@ import { GraphDirectory } from "../utilities/GraphDirectory.sol"; * for payments made through the payments protocol for services provided * via a Graph Horizon data service. */ -contract PaymentsEscrow is - Initializable, - MulticallUpgradeable, - GraphDirectory, - IPaymentsEscrow -{ +contract PaymentsEscrow is Initializable, MulticallUpgradeable, GraphDirectory, IPaymentsEscrow { using TokenUtils for IGraphToken; /// @notice Authorization details for payer-collector pairs @@ -73,7 +68,6 @@ contract PaymentsEscrow is WITHDRAW_ESCROW_THAWING_PERIOD = withdrawEscrowThawingPeriod; } - /** * @notice Initialize the contract */ diff --git a/packages/horizon/contracts/payments/collectors/TAPCollector.sol b/packages/horizon/contracts/payments/collectors/TAPCollector.sol index e385667c7..f3d67e819 100644 --- a/packages/horizon/contracts/payments/collectors/TAPCollector.sol +++ b/packages/horizon/contracts/payments/collectors/TAPCollector.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "../../interfaces/IGraphPayments.sol"; import { ITAPCollector } from "../../interfaces/ITAPCollector.sol"; diff --git a/packages/horizon/contracts/staking/HorizonStaking.sol b/packages/horizon/contracts/staking/HorizonStaking.sol index e6cc98315..74b2d8d09 100644 --- a/packages/horizon/contracts/staking/HorizonStaking.sol +++ b/packages/horizon/contracts/staking/HorizonStaking.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IHorizonStakingMain } from "../interfaces/internal/IHorizonStakingMain.sol"; diff --git a/packages/horizon/contracts/staking/HorizonStakingBase.sol b/packages/horizon/contracts/staking/HorizonStakingBase.sol index 722a2f061..b3e94c7ea 100644 --- a/packages/horizon/contracts/staking/HorizonStakingBase.sol +++ b/packages/horizon/contracts/staking/HorizonStakingBase.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStakingTypes } from "../interfaces/internal/IHorizonStakingTypes.sol"; import { IHorizonStakingBase } from "../interfaces/internal/IHorizonStakingBase.sol"; diff --git a/packages/horizon/contracts/staking/HorizonStakingExtension.sol b/packages/horizon/contracts/staking/HorizonStakingExtension.sol index 1f6843d53..ba7eb3a5b 100644 --- a/packages/horizon/contracts/staking/HorizonStakingExtension.sol +++ b/packages/horizon/contracts/staking/HorizonStakingExtension.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { ICuration } from "@graphprotocol/contracts/contracts/curation/ICuration.sol"; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; diff --git a/packages/horizon/contracts/staking/HorizonStakingStorage.sol b/packages/horizon/contracts/staking/HorizonStakingStorage.sol index 2b58b02c7..ff515962a 100644 --- a/packages/horizon/contracts/staking/HorizonStakingStorage.sol +++ b/packages/horizon/contracts/staking/HorizonStakingStorage.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStakingExtension } from "../interfaces/internal/IHorizonStakingExtension.sol"; import { IHorizonStakingTypes } from "../interfaces/internal/IHorizonStakingTypes.sol"; @@ -105,7 +105,7 @@ abstract contract HorizonStakingV1Storage { /// @dev Operator allow list (legacy) /// Only used when the verifier is the subgraph data service. - mapping(address legacyOperator => mapping(address serviceProvider => bool authorized)) internal _legacyOperatorAuth; + mapping(address serviceProvider => mapping(address legacyOperator => bool authorized)) internal _legacyOperatorAuth; // -- Asset Holders -- diff --git a/packages/horizon/contracts/staking/libraries/ExponentialRebates.sol b/packages/horizon/contracts/staking/libraries/ExponentialRebates.sol index 1f7f0b3d2..cda9bf247 100644 --- a/packages/horizon/contracts/staking/libraries/ExponentialRebates.sol +++ b/packages/horizon/contracts/staking/libraries/ExponentialRebates.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { LibFixedMath } from "../../libraries/LibFixedMath.sol"; diff --git a/packages/horizon/contracts/staking/utilities/Managed.sol b/packages/horizon/contracts/staking/utilities/Managed.sol index 4d8e77b4f..44d0ad81b 100644 --- a/packages/horizon/contracts/staking/utilities/Managed.sol +++ b/packages/horizon/contracts/staking/utilities/Managed.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { GraphDirectory } from "../../utilities/GraphDirectory.sol"; diff --git a/packages/horizon/contracts/utilities/GraphDirectory.sol b/packages/horizon/contracts/utilities/GraphDirectory.sol index b63bcdeab..c24a7984d 100644 --- a/packages/horizon/contracts/utilities/GraphDirectory.sol +++ b/packages/horizon/contracts/utilities/GraphDirectory.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IHorizonStaking } from "../interfaces/IHorizonStaking.sol"; diff --git a/packages/horizon/foundry.toml b/packages/horizon/foundry.toml index a1826d45c..56cb8a0b6 100644 --- a/packages/horizon/foundry.toml +++ b/packages/horizon/foundry.toml @@ -6,12 +6,4 @@ test = 'test' cache_path = 'cache_forge' fs_permissions = [{ access = "read", path = "./"}] optimizer = true -optimizer-runs = 200 -via_ir = true - -[profile.lite] -optimizer = false -optimizer-runs = 1 - -[profile.lite.optimizer_details.yulDetails] -optimizerSteps = ':' \ No newline at end of file +optimizer_runs = 200 \ No newline at end of file diff --git a/packages/horizon/hardhat.config.ts b/packages/horizon/hardhat.config.ts index fa73c9643..de221c723 100644 --- a/packages/horizon/hardhat.config.ts +++ b/packages/horizon/hardhat.config.ts @@ -7,9 +7,8 @@ import { HardhatUserConfig } from 'hardhat/config' const config: HardhatUserConfig = { solidity: { - version: '0.8.26', + version: '0.8.27', settings: { - viaIR: true, optimizer: { enabled: true, runs: 200, diff --git a/packages/horizon/package.json b/packages/horizon/package.json index a214e1d0a..064e7e1db 100644 --- a/packages/horizon/package.json +++ b/packages/horizon/package.json @@ -9,8 +9,8 @@ "lint:sol": "prettier --write contracts/**/*.sol && solhint --noPrompt --fix contracts/**/*.sol --config node_modules/solhint-graph-config/index.js", "lint": "yarn lint:ts && yarn lint:sol", "clean": "rm -rf build cache typechain-types", - "build": "forge build && hardhat compile", - "test": "FOUNDRY_PROFILE=lite forge test -vvv && hardhat test" + "build": "forge build contracts && hardhat compile", + "test": "FOUNDRY_PROFILE=test forge test -vvv && hardhat test" }, "devDependencies": { "@graphprotocol/contracts": "workspace:^7.0.0", diff --git a/packages/horizon/test/GraphBase.t.sol b/packages/horizon/test/GraphBase.t.sol index 6236d00dd..3c2375b8d 100644 --- a/packages/horizon/test/GraphBase.t.sol +++ b/packages/horizon/test/GraphBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -14,6 +14,7 @@ import { GraphPayments } from "contracts/payments/GraphPayments.sol"; import { IHorizonStaking } from "contracts/interfaces/IHorizonStaking.sol"; import { HorizonStaking } from "contracts/staking/HorizonStaking.sol"; import { HorizonStakingExtension } from "contracts/staking/HorizonStakingExtension.sol"; +import { IHorizonStakingTypes } from "contracts/interfaces/internal/IHorizonStakingTypes.sol"; import { MockGRTToken } from "../contracts/mocks/MockGRTToken.sol"; import { EpochManagerMock } from "../contracts/mocks/EpochManagerMock.sol"; import { RewardsManagerMock } from "../contracts/mocks/RewardsManagerMock.sol"; @@ -22,8 +23,7 @@ import { Constants } from "./utils/Constants.sol"; import { Users } from "./utils/Users.sol"; import { Utils } from "./utils/Utils.sol"; -abstract contract GraphBaseTest is Utils, Constants { - +abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants { /* * VARIABLES */ @@ -54,10 +54,6 @@ abstract contract GraphBaseTest is Utils, Constants { Users internal users; - /* Constants */ - - Constants public constants; - /* * SET UP */ diff --git a/packages/horizon/test/data-service/DataService.t.sol b/packages/horizon/test/data-service/DataService.t.sol index 98f7aadb3..c535c6dea 100644 --- a/packages/horizon/test/data-service/DataService.t.sol +++ b/packages/horizon/test/data-service/DataService.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IHorizonStakingMain } from "../../contracts/interfaces/internal/IHorizonStakingMain.sol"; import { HorizonStakingSharedTest } from "../shared/horizon-staking/HorizonStakingShared.t.sol"; @@ -69,7 +69,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataService.setProvisionTokensRange(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); tokens = bound(tokens, dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); - _createProvision(address(dataService), tokens, 0, 0); + _createProvision(users.indexer, address(dataService), tokens, 0, 0); dataService.checkProvisionTokens(users.indexer); } @@ -81,7 +81,7 @@ contract DataServiceTest is HorizonStakingSharedTest { ); // this checker accepts provisions with any amount of tokens - _createProvision(address(dataServiceOverride), tokens, 0, 0); + _createProvision(users.indexer, address(dataServiceOverride), tokens, 0, 0); dataServiceOverride.checkProvisionTokens(users.indexer); } @@ -89,7 +89,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataService.setProvisionTokensRange(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); tokens = bound(tokens, 1, dataService.PROVISION_TOKENS_MIN() - 1); - _createProvision(address(dataService), tokens, 0, 0); + _createProvision(users.indexer, address(dataService), tokens, 0, 0); vm.expectRevert( abi.encodeWithSelector( ProvisionManager.ProvisionManagerInvalidValue.selector, @@ -142,7 +142,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); verifierCut = uint32(bound(verifierCut, dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX())); - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); dataService.checkProvisionParameters(users.indexer, false); } @@ -151,7 +151,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataServiceOverride.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); // this checker accepts provisions with any verifier cut range - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); dataServiceOverride.checkProvisionParameters(users.indexer, false); } @@ -159,7 +159,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); verifierCut = uint32(bound(verifierCut, 0, dataService.VERIFIER_CUT_MIN() - 1)); - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); vm.expectRevert( abi.encodeWithSelector( ProvisionManager.ProvisionManagerInvalidValue.selector, @@ -205,7 +205,7 @@ contract DataServiceTest is HorizonStakingSharedTest { bound(thawingPeriod, dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()) ); - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); dataService.checkProvisionParameters(users.indexer, false); } @@ -214,7 +214,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataServiceOverride.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); // this checker accepts provisions with any verifier cut range - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); dataServiceOverride.checkProvisionParameters(users.indexer, false); } @@ -222,7 +222,7 @@ contract DataServiceTest is HorizonStakingSharedTest { dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); thawingPeriod = uint32(bound(thawingPeriod, 0, dataService.THAWING_PERIOD_MIN() - 1)); - _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + _createProvision(users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); vm.expectRevert( abi.encodeWithSelector( ProvisionManager.ProvisionManagerInvalidValue.selector, @@ -255,12 +255,13 @@ contract DataServiceTest is HorizonStakingSharedTest { // stage provision parameter changes _createProvision( + users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), dataService.VERIFIER_CUT_MIN(), dataService.THAWING_PERIOD_MIN() ); - staking.setProvisionParameters(users.indexer, address(dataService), maxVerifierCut, thawingPeriod); + _setProvisionParameters(users.indexer, address(dataService), maxVerifierCut, thawingPeriod); // accept provision parameters if (maxVerifierCut != dataService.VERIFIER_CUT_MIN() || thawingPeriod != dataService.THAWING_PERIOD_MIN()) { @@ -287,12 +288,13 @@ contract DataServiceTest is HorizonStakingSharedTest { // stage provision parameter changes _createProvision( + users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), dataService.VERIFIER_CUT_MIN(), dataService.THAWING_PERIOD_MIN() ); - staking.setProvisionParameters( + _setProvisionParameters( users.indexer, address(dataService), dataService.VERIFIER_CUT_MIN(), @@ -324,12 +326,13 @@ contract DataServiceTest is HorizonStakingSharedTest { // stage provision parameter changes _createProvision( + users.indexer, address(dataService), dataService.PROVISION_TOKENS_MIN(), dataService.VERIFIER_CUT_MIN(), dataService.THAWING_PERIOD_MIN() ); - staking.setProvisionParameters( + _setProvisionParameters( users.indexer, address(dataService), maxVerifierCut, diff --git a/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol b/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol index c8721260f..be33173f8 100644 --- a/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol +++ b/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { GraphBaseTest } from "../GraphBase.t.sol"; import { DataServiceBaseUpgradeable } from "./implementations/DataServiceBaseUpgradeable.sol"; diff --git a/packages/horizon/test/data-service/extensions/DataServiceFees.t.sol b/packages/horizon/test/data-service/extensions/DataServiceFees.t.sol index 4ab1406ae..f6f31d54c 100644 --- a/packages/horizon/test/data-service/extensions/DataServiceFees.t.sol +++ b/packages/horizon/test/data-service/extensions/DataServiceFees.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { HorizonStakingSharedTest } from "../../shared/horizon-staking/HorizonStakingShared.t.sol"; import { DataServiceImpFees } from "../implementations/DataServiceImpFees.sol"; @@ -126,20 +126,27 @@ contract DataServiceFeesTest is HorizonStakingSharedTest { } // -- Assertion functions -- - + // use struct to avoid 'stack too deep' error + struct CalcValues_LockStake { + uint256 unlockTimestamp; + uint256 stakeToLock; + bytes32 predictedClaimId; + } function _assert_lockStake(address serviceProvider, uint256 tokens) private { // before state (bytes32 beforeHead, , uint256 beforeNonce, uint256 beforeCount) = dataService.claimsLists(serviceProvider); uint256 beforeLockedStake = dataService.feesProvisionTracker(serviceProvider); // calc - uint256 unlockTimestamp = block.timestamp + dataService.LOCK_DURATION(); - uint256 stakeToLock = tokens * dataService.STAKE_TO_FEES_RATIO(); - bytes32 predictedClaimId = keccak256(abi.encodePacked(address(dataService), serviceProvider, beforeNonce)); + CalcValues_LockStake memory calcValues = CalcValues_LockStake({ + unlockTimestamp: block.timestamp + dataService.LOCK_DURATION(), + stakeToLock: tokens * dataService.STAKE_TO_FEES_RATIO(), + predictedClaimId: keccak256(abi.encodePacked(address(dataService), serviceProvider, beforeNonce)) + }); // it should emit a an event vm.expectEmit(); - emit IDataServiceFees.StakeClaimLocked(serviceProvider, predictedClaimId, stakeToLock, unlockTimestamp); + emit IDataServiceFees.StakeClaimLocked(serviceProvider, calcValues.predictedClaimId, calcValues.stakeToLock, calcValues.unlockTimestamp); dataService.lockStake(serviceProvider, tokens); // after state @@ -149,24 +156,30 @@ contract DataServiceFeesTest is HorizonStakingSharedTest { ); // it should lock the tokens - assertEq(beforeLockedStake + stakeToLock, afterLockedStake); + assertEq(beforeLockedStake + calcValues.stakeToLock, afterLockedStake); // it should create a stake claim (uint256 claimTokens, uint256 createdAt, uint256 releaseAt, bytes32 nextClaim) = dataService.claims( - predictedClaimId + calcValues.predictedClaimId ); - assertEq(claimTokens, stakeToLock); + assertEq(claimTokens, calcValues.stakeToLock); assertEq(createdAt, block.timestamp); - assertEq(releaseAt, unlockTimestamp); + assertEq(releaseAt, calcValues.unlockTimestamp); assertEq(nextClaim, bytes32(0)); // it should update the list assertEq(afterCount, beforeCount + 1); assertEq(afterNonce, beforeNonce + 1); - assertEq(afterHead, beforeCount == 0 ? predictedClaimId : beforeHead); - assertEq(afterTail, predictedClaimId); + assertEq(afterHead, beforeCount == 0 ? calcValues.predictedClaimId : beforeHead); + assertEq(afterTail, calcValues.predictedClaimId); } + // use struct to avoid 'stack too deep' error + struct CalcValues_ReleaseStake { + uint256 claimsCount; + uint256 tokensReleased; + bytes32 head; + } function _assert_releaseStake(address serviceProvider, uint256 n) private { // before state (bytes32 beforeHead, bytes32 beforeTail, uint256 beforeNonce, uint256 beforeCount) = dataService.claimsLists( @@ -177,23 +190,25 @@ contract DataServiceFeesTest is HorizonStakingSharedTest { // calc and set events vm.expectEmit(); - uint256 claimsCount = 0; - uint256 tokensReleased = 0; - bytes32 head = beforeHead; - while (head != bytes32(0) && (claimsCount < n || n == 0)) { - (uint256 claimTokens, , uint256 releaseAt, bytes32 nextClaim) = dataService.claims(head); + CalcValues_ReleaseStake memory calcValues = CalcValues_ReleaseStake({ + claimsCount: 0, + tokensReleased: 0, + head: beforeHead + }); + while (calcValues.head != bytes32(0) && (calcValues.claimsCount < n || n == 0)) { + (uint256 claimTokens, , uint256 releaseAt, bytes32 nextClaim) = dataService.claims(calcValues.head); if (releaseAt > block.timestamp) { break; } - emit IDataServiceFees.StakeClaimReleased(serviceProvider, head, claimTokens, releaseAt); - head = nextClaim; - tokensReleased += claimTokens; - claimsCount++; + emit IDataServiceFees.StakeClaimReleased(serviceProvider, calcValues.head, claimTokens, releaseAt); + calcValues.head = nextClaim; + calcValues.tokensReleased += claimTokens; + calcValues.claimsCount++; } // it should emit a an event - emit IDataServiceFees.StakeClaimsReleased(serviceProvider, claimsCount, tokensReleased); + emit IDataServiceFees.StakeClaimsReleased(serviceProvider, calcValues.claimsCount, calcValues.tokensReleased); dataService.releaseStake(n); // after state @@ -203,17 +218,17 @@ contract DataServiceFeesTest is HorizonStakingSharedTest { uint256 afterLockedStake = dataService.feesProvisionTracker(serviceProvider); // it should release the tokens - assertEq(beforeLockedStake - tokensReleased, afterLockedStake); + assertEq(beforeLockedStake - calcValues.tokensReleased, afterLockedStake); // it should remove the processed claims from the list - assertEq(afterCount, beforeCount - claimsCount); + assertEq(afterCount, beforeCount - calcValues.claimsCount); assertEq(afterNonce, beforeNonce); - if (claimsCount != 0) { + if (calcValues.claimsCount != 0) { assertNotEq(afterHead, beforeHead); } else { assertEq(afterHead, beforeHead); } - assertEq(afterHead, head); - assertEq(afterTail, claimsCount == beforeCount ? bytes32(0) : beforeTail); + assertEq(afterHead, calcValues.head); + assertEq(afterTail, calcValues.claimsCount == beforeCount ? bytes32(0) : beforeTail); } } diff --git a/packages/horizon/test/data-service/extensions/DataServicePausable.t.sol b/packages/horizon/test/data-service/extensions/DataServicePausable.t.sol index c71cace11..111838fe4 100644 --- a/packages/horizon/test/data-service/extensions/DataServicePausable.t.sol +++ b/packages/horizon/test/data-service/extensions/DataServicePausable.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { HorizonStakingSharedTest } from "../../shared/horizon-staking/HorizonStakingShared.t.sol"; import { DataServiceImpPausable } from "../implementations/DataServiceImpPausable.sol"; diff --git a/packages/horizon/test/data-service/extensions/DataServicePausableUpgradeable.t.sol b/packages/horizon/test/data-service/extensions/DataServicePausableUpgradeable.t.sol index 9f4a8822b..6e58810c1 100644 --- a/packages/horizon/test/data-service/extensions/DataServicePausableUpgradeable.t.sol +++ b/packages/horizon/test/data-service/extensions/DataServicePausableUpgradeable.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { GraphBaseTest } from "../../GraphBase.t.sol"; import { DataServiceImpPausableUpgradeable } from "../implementations/DataServiceImpPausableUpgradeable.sol"; diff --git a/packages/horizon/test/data-service/implementations/DataServiceBase.sol b/packages/horizon/test/data-service/implementations/DataServiceBase.sol index b8171e649..d98dd2857 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceBase.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataService } from "../../../contracts/data-service/DataService.sol"; import { IGraphPayments } from "./../../../contracts/interfaces/IGraphPayments.sol"; diff --git a/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol b/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol index fbe2fe211..31309c524 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataService } from "../../../contracts/data-service/DataService.sol"; import { IGraphPayments } from "./../../../contracts/interfaces/IGraphPayments.sol"; contract DataServiceBaseUpgradeable is DataService { - constructor(address controller) DataService(controller) { + constructor(address controller_) DataService(controller_) { _disableInitializers(); } diff --git a/packages/horizon/test/data-service/implementations/DataServiceImpFees.sol b/packages/horizon/test/data-service/implementations/DataServiceImpFees.sol index ee071c47d..efabda6ff 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceImpFees.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceImpFees.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataService } from "../../../contracts/data-service/DataService.sol"; import { DataServiceFees } from "../../../contracts/data-service/extensions/DataServiceFees.sol"; @@ -25,6 +25,7 @@ contract DataServiceImpFees is DataServiceFees { uint256 amount = abi.decode(data, (uint256)); _releaseStake(serviceProvider, 0); _lockStake(serviceProvider, amount * STAKE_TO_FEES_RATIO, block.timestamp + LOCK_DURATION); + return amount; } function lockStake(address serviceProvider, uint256 amount) external { diff --git a/packages/horizon/test/data-service/implementations/DataServiceImpPausable.sol b/packages/horizon/test/data-service/implementations/DataServiceImpPausable.sol index 5d49f8873..0ca990ab1 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceImpPausable.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceImpPausable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataService } from "../../../contracts/data-service/DataService.sol"; import { DataServicePausable } from "../../../contracts/data-service/extensions/DataServicePausable.sol"; diff --git a/packages/horizon/test/data-service/implementations/DataServiceImpPausableUpgradeable.sol b/packages/horizon/test/data-service/implementations/DataServiceImpPausableUpgradeable.sol index 5b55d0866..39b2bb26b 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceImpPausableUpgradeable.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceImpPausableUpgradeable.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataService } from "../../../contracts/data-service/DataService.sol"; import { DataServicePausableUpgradeable } from "../../../contracts/data-service/extensions/DataServicePausableUpgradeable.sol"; import { IGraphPayments } from "./../../../contracts/interfaces/IGraphPayments.sol"; contract DataServiceImpPausableUpgradeable is DataServicePausableUpgradeable { - constructor(address controller) DataService(controller) { + constructor(address controller_) DataService(controller_) { _disableInitializers(); } diff --git a/packages/horizon/test/data-service/implementations/DataServiceOverride.sol b/packages/horizon/test/data-service/implementations/DataServiceOverride.sol index 838be68a5..c5d50ca74 100644 --- a/packages/horizon/test/data-service/implementations/DataServiceOverride.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceOverride.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { DataServiceBase } from "./DataServiceBase.sol"; diff --git a/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol b/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol index a7ec2f614..af147fdd6 100644 --- a/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol +++ b/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { HorizonStakingSharedTest } from "../../shared/horizon-staking/HorizonStakingShared.t.sol"; import { ProvisionTrackerImplementation } from "./ProvisionTrackerImplementation.sol"; diff --git a/packages/horizon/test/data-service/libraries/ProvisionTrackerImplementation.sol b/packages/horizon/test/data-service/libraries/ProvisionTrackerImplementation.sol index 0093fba72..5c9cf4a6a 100644 --- a/packages/horizon/test/data-service/libraries/ProvisionTrackerImplementation.sol +++ b/packages/horizon/test/data-service/libraries/ProvisionTrackerImplementation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { ProvisionTracker } from "../../../contracts/data-service/libraries/ProvisionTracker.sol"; diff --git a/packages/horizon/test/escrow/GraphEscrow.t.sol b/packages/horizon/test/escrow/GraphEscrow.t.sol index a510f57bf..2421ea80c 100644 --- a/packages/horizon/test/escrow/GraphEscrow.t.sol +++ b/packages/horizon/test/escrow/GraphEscrow.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/collect.t.sol b/packages/horizon/test/escrow/collect.t.sol index 1ba403a23..67efcc6e7 100644 --- a/packages/horizon/test/escrow/collect.t.sol +++ b/packages/horizon/test/escrow/collect.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/collector.t.sol b/packages/horizon/test/escrow/collector.t.sol index 5023c38ce..3e5b71bc0 100644 --- a/packages/horizon/test/escrow/collector.t.sol +++ b/packages/horizon/test/escrow/collector.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/deposit.t.sol b/packages/horizon/test/escrow/deposit.t.sol index 79b7c3616..3ce341a1a 100644 --- a/packages/horizon/test/escrow/deposit.t.sol +++ b/packages/horizon/test/escrow/deposit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/paused.t.sol b/packages/horizon/test/escrow/paused.t.sol index 2be37a61b..f6b65aa2b 100644 --- a/packages/horizon/test/escrow/paused.t.sol +++ b/packages/horizon/test/escrow/paused.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/thaw.t.sol b/packages/horizon/test/escrow/thaw.t.sol index 35f501ff3..36808602c 100644 --- a/packages/horizon/test/escrow/thaw.t.sol +++ b/packages/horizon/test/escrow/thaw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/escrow/withdraw.t.sol b/packages/horizon/test/escrow/withdraw.t.sol index f7ffdd1c6..0e5670c7a 100644 --- a/packages/horizon/test/escrow/withdraw.t.sol +++ b/packages/horizon/test/escrow/withdraw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/horizon/test/libraries/LinkedList.t.sol b/packages/horizon/test/libraries/LinkedList.t.sol index 5150b6116..1850cdb47 100644 --- a/packages/horizon/test/libraries/LinkedList.t.sol +++ b/packages/horizon/test/libraries/LinkedList.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/console.sol"; import {Test} from "forge-std/Test.sol"; diff --git a/packages/horizon/test/libraries/ListImplementation.sol b/packages/horizon/test/libraries/ListImplementation.sol index 9c8bbfb60..fda762c6d 100644 --- a/packages/horizon/test/libraries/ListImplementation.sol +++ b/packages/horizon/test/libraries/ListImplementation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { LinkedList } from "../../contracts/libraries/LinkedList.sol"; @@ -34,7 +34,7 @@ contract ListImplementation { return items[_id].next; } - function _processItemAddition(bytes32 _id, bytes memory _acc) internal returns (bool, bytes memory) { + function _processItemAddition(bytes32 _id, bytes memory _acc) internal view returns (bool, bytes memory) { uint256 sum = abi.decode(_acc, (uint256)); sum += items[_id].data; return (false, abi.encode(sum)); // dont break, do delete diff --git a/packages/horizon/test/payments/GraphPayments.t.sol b/packages/horizon/test/payments/GraphPayments.t.sol index 9d2ef7495..62e582c26 100644 --- a/packages/horizon/test/payments/GraphPayments.t.sol +++ b/packages/horizon/test/payments/GraphPayments.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -60,7 +60,7 @@ contract GraphPaymentsTest is HorizonStakingSharedTest { uint256 amount, uint256 tokensDataService ) public useIndexer useProvision(amount, 0, 0) useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) { - tokensDataService = bound(tokensDataService, amount + 1, MAX_STAKING_TOKENS); + tokensDataService = bound(tokensDataService, amount + 1, MAX_STAKING_TOKENS + 1); address escrowAddress = address(escrow); mint(escrowAddress, amount); diff --git a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol index faf7b8039..d9f300979 100644 --- a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol +++ b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol @@ -1,12 +1,35 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; import { GraphBaseTest } from "../../GraphBase.t.sol"; 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"; +import { PPMMath } from "../../../contracts/libraries/PPMMath.sol"; +import { ExponentialRebates } from "../../../contracts/staking/libraries/ExponentialRebates.sol"; abstract contract HorizonStakingSharedTest is GraphBaseTest { + using LinkedList for LinkedList.List; + using PPMMath for uint256; + + event Transfer(address indexed from, address indexed to, uint tokens); + + address internal _allocationId = makeAddr("allocationId"); + bytes32 internal constant _subgraphDeploymentID = keccak256("subgraphDeploymentID"); + uint256 internal constant MAX_ALLOCATION_EPOCHS = 28; + + uint32 internal alphaNumerator = 100; + uint32 internal alphaDenominator = 100; + uint32 internal lambdaNumerator = 60; + uint32 internal lambdaDenominator = 100; + /* * MODIFIERS */ @@ -17,9 +40,17 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { vm.stopPrank(); } - modifier assumeProvisionTokens(uint256 tokens) { - vm.assume(tokens > 0); - vm.assume(tokens <= MAX_STAKING_TOKENS); + modifier useOperator() { + vm.startPrank(users.indexer); + _setOperator(users.operator, subgraphDataServiceAddress, true); + vm.startPrank(users.operator); + _; + vm.stopPrank(); + } + + modifier useStake(uint256 amount) { + vm.assume(amount > 0); + _stake(amount); _; } @@ -43,41 +74,2203 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { } modifier useDelegationFeeCut(IGraphPayments.PaymentTypes paymentType, uint256 cut) { - _setDelegationFeeCut(paymentType, cut); + _setDelegationFeeCut(users.indexer, subgraphDataServiceAddress, paymentType, cut); + _; + } + + function _useProvision(address dataService, uint256 tokens, uint32 maxVerifierCut, uint64 thawingPeriod) internal { + // use assume instead of bound to avoid the bounding falling out of scope + vm.assume(tokens > 0); + vm.assume(tokens <= MAX_STAKING_TOKENS); + vm.assume(maxVerifierCut <= MAX_MAX_VERIFIER_CUT); + vm.assume(thawingPeriod <= MAX_THAWING_PERIOD); + + _createProvision(users.indexer, dataService, tokens, maxVerifierCut, thawingPeriod); + } + + modifier useAllocation(uint256 tokens) { + vm.assume(tokens <= MAX_STAKING_TOKENS); + _createAllocation(users.indexer, _allocationId, _subgraphDeploymentID, tokens); + _; + } + + modifier useRebateParameters() { + _setStorage_RebateParameters(alphaNumerator, alphaDenominator, lambdaNumerator, lambdaDenominator); _; } /* - * HELPERS + * HELPERS: these are shortcuts to perform common actions that often involve multiple contract calls */ + function _createProvision( + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) internal { + _stakeTo(serviceProvider, tokens); + _provision(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod); + } - function _useProvision( - address dataService, + // This allows setting up contract state with legacy allocations + function _createAllocation( + address serviceProvider, + address allocationId, + bytes32 subgraphDeploymentID, + uint256 tokens + ) internal { + _setStorage_MaxAllocationEpochs(MAX_ALLOCATION_EPOCHS); + + IHorizonStakingExtension.Allocation memory _allocation = IHorizonStakingExtension.Allocation({ + indexer: serviceProvider, + subgraphDeploymentID: subgraphDeploymentID, + tokens: tokens, + createdAtEpoch: block.timestamp, + closedAtEpoch: 0, + collectedFees: 0, + __DEPRECATED_effectiveAllocation: 0, + accRewardsPerAllocatedToken: 0, + distributedRebates: 0 + }); + _setStorage_allocation(_allocation, allocationId, tokens); + + // delegation pool initialized + _setStorage_DelegationPool(serviceProvider, 0, uint32(PPMMath.MAX_PPM), uint32(PPMMath.MAX_PPM)); + + token.transfer(address(staking), tokens); + } + + /* + * ACTIONS: these are individual contract calls wrapped in assertion blocks to ensure they work as expected + */ + function _stake(uint256 tokens) internal { + (, address msgSender, ) = vm.readCallers(); + _stakeTo(msgSender, tokens); + } + + function _stakeTo(address serviceProvider, uint256 tokens) internal { + (, address msgSender, ) = vm.readCallers(); + + // before + uint256 beforeStakingBalance = token.balanceOf(address(staking)); + uint256 beforeSenderBalance = token.balanceOf(msgSender); + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // stakeTo + token.approve(address(staking), tokens); + vm.expectEmit(); + emit IHorizonStakingBase.StakeDeposited(serviceProvider, tokens); + staking.stakeTo(serviceProvider, tokens); + + // after + uint256 afterStakingBalance = token.balanceOf(address(staking)); + uint256 afterSenderBalance = token.balanceOf(msgSender); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // assert + assertEq(afterStakingBalance, beforeStakingBalance + tokens); + assertEq(afterSenderBalance, beforeSenderBalance - tokens); + assertEq(afterServiceProvider.tokensStaked, beforeServiceProvider.tokensStaked + tokens); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeServiceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeServiceProvider.__DEPRECATED_tokensLockedUntil + ); + } + + function _unstake(uint256 _tokens) internal { + (, address msgSender, ) = vm.readCallers(); + + uint256 deprecatedThawingPeriod = staking.__DEPRECATED_getThawingPeriod(); + + // before + uint256 beforeSenderBalance = token.balanceOf(msgSender); + uint256 beforeStakingBalance = token.balanceOf(address(staking)); + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(msgSender); + + bool withdrawCalled = beforeServiceProvider.__DEPRECATED_tokensLocked != 0 && + block.number >= beforeServiceProvider.__DEPRECATED_tokensLockedUntil; + + if (deprecatedThawingPeriod != 0 && beforeServiceProvider.__DEPRECATED_tokensLocked > 0) { + deprecatedThawingPeriod = MathUtils.weightedAverageRoundingUp( + MathUtils.diffOrZero( + withdrawCalled ? 0 : beforeServiceProvider.__DEPRECATED_tokensLockedUntil, + block.number + ), + withdrawCalled ? 0 : beforeServiceProvider.__DEPRECATED_tokensLocked, + deprecatedThawingPeriod, + _tokens + ); + } + + // unstake + if (deprecatedThawingPeriod == 0) { + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.StakeWithdrawn(msgSender, _tokens); + } else { + if (withdrawCalled) { + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.StakeWithdrawn(msgSender, beforeServiceProvider.__DEPRECATED_tokensLocked); + } + + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.StakeLocked( + msgSender, + withdrawCalled ? _tokens : beforeServiceProvider.__DEPRECATED_tokensLocked + _tokens, + block.number + deprecatedThawingPeriod + ); + } + staking.unstake(_tokens); + + // after + uint256 afterSenderBalance = token.balanceOf(msgSender); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(msgSender); + + // assert + if (deprecatedThawingPeriod == 0) { + assertEq(afterSenderBalance, _tokens + beforeSenderBalance); + assertEq(afterStakingBalance, beforeStakingBalance - _tokens); + assertEq(afterServiceProvider.tokensStaked, beforeServiceProvider.tokensStaked - _tokens); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned); + assertEq( + afterServiceProvider.__DEPRECATED_tokensAllocated, + beforeServiceProvider.__DEPRECATED_tokensAllocated + ); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeServiceProvider.__DEPRECATED_tokensLockedUntil + ); + } else { + assertEq( + afterServiceProvider.tokensStaked, + withdrawCalled + ? beforeServiceProvider.tokensStaked - beforeServiceProvider.__DEPRECATED_tokensLocked + : beforeServiceProvider.tokensStaked + ); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLocked, + _tokens + (withdrawCalled ? 0 : beforeServiceProvider.__DEPRECATED_tokensLocked) + ); + assertEq(afterServiceProvider.__DEPRECATED_tokensLockedUntil, block.number + deprecatedThawingPeriod); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned); + assertEq( + afterServiceProvider.__DEPRECATED_tokensAllocated, + beforeServiceProvider.__DEPRECATED_tokensAllocated + ); + uint256 tokensTransferred = (withdrawCalled ? beforeServiceProvider.__DEPRECATED_tokensLocked : 0); + assertEq(afterSenderBalance, beforeSenderBalance + tokensTransferred); + assertEq(afterStakingBalance, beforeStakingBalance - tokensTransferred); + } + } + + function _withdraw() internal { + (, address msgSender, ) = vm.readCallers(); + + // before + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(msgSender); + uint256 beforeSenderBalance = token.balanceOf(msgSender); + uint256 beforeStakingBalance = token.balanceOf(address(staking)); + + // withdraw + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.StakeWithdrawn(msgSender, beforeServiceProvider.__DEPRECATED_tokensLocked); + staking.withdraw(); + + // after + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(msgSender); + uint256 afterSenderBalance = token.balanceOf(msgSender); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + + // assert + assertEq(afterSenderBalance - beforeSenderBalance, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq(beforeStakingBalance - afterStakingBalance, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.tokensStaked, + beforeServiceProvider.tokensStaked - beforeServiceProvider.__DEPRECATED_tokensLocked + ); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeServiceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, 0); + assertEq(afterServiceProvider.__DEPRECATED_tokensLockedUntil, 0); + } + + function _provision( + address serviceProvider, + address verifier, uint256 tokens, uint32 maxVerifierCut, uint64 thawingPeriod ) internal { - vm.assume(tokens <= MAX_STAKING_TOKENS); - vm.assume(tokens > 0); - vm.assume(maxVerifierCut <= MAX_MAX_VERIFIER_CUT); - vm.assume(thawingPeriod <= MAX_THAWING_PERIOD); - _createProvision(dataService, tokens, maxVerifierCut, thawingPeriod); + __provision(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod, false); } - function _createProvision( - address dataServiceAddress, + function _provisionLocked( + address serviceProvider, + address verifier, uint256 tokens, uint32 maxVerifierCut, uint64 thawingPeriod ) internal { + __provision(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod, true); + } + + function __provision( + address serviceProvider, + address verifier, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod, + bool locked + ) private { + // before + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // provision + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionCreated(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod); + if (locked) { + staking.provisionLocked(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod); + } else { + staking.provision(serviceProvider, verifier, tokens, maxVerifierCut, thawingPeriod); + } + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // assert + assertEq(afterProvision.tokens, tokens); + assertEq(afterProvision.tokensThawing, 0); + assertEq(afterProvision.sharesThawing, 0); + assertEq(afterProvision.maxVerifierCut, maxVerifierCut); + assertEq(afterProvision.thawingPeriod, thawingPeriod); + assertEq(afterProvision.createdAt, uint64(block.timestamp)); + assertEq(afterProvision.maxVerifierCutPending, maxVerifierCut); + assertEq(afterProvision.thawingPeriodPending, thawingPeriod); + assertEq(afterServiceProvider.tokensStaked, beforeServiceProvider.tokensStaked); + assertEq(afterServiceProvider.tokensProvisioned, tokens + beforeServiceProvider.tokensProvisioned); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeServiceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeServiceProvider.__DEPRECATED_tokensLockedUntil + ); + } + + function _addToProvision(address serviceProvider, address verifier, uint256 tokens) internal { + // before + Provision memory beforeProvision = staking.getProvision(serviceProvider, verifier); + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // addToProvision + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionIncreased(serviceProvider, verifier, tokens); + staking.addToProvision(serviceProvider, verifier, tokens); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + + // assert + assertEq(afterProvision.tokens, beforeProvision.tokens + tokens); + assertEq(afterProvision.tokensThawing, beforeProvision.tokensThawing); + assertEq(afterProvision.sharesThawing, beforeProvision.sharesThawing); + assertEq(afterProvision.maxVerifierCut, beforeProvision.maxVerifierCut); + assertEq(afterProvision.thawingPeriod, beforeProvision.thawingPeriod); + assertEq(afterProvision.createdAt, beforeProvision.createdAt); + assertEq(afterProvision.maxVerifierCutPending, beforeProvision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriodPending, beforeProvision.thawingPeriodPending); + assertEq(afterServiceProvider.tokensStaked, beforeServiceProvider.tokensStaked); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned + tokens); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeServiceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeServiceProvider.__DEPRECATED_tokensLockedUntil + ); + } + + function _thaw(address serviceProvider, address verifier, uint256 tokens) internal returns (bytes32) { + // before + Provision memory beforeProvision = staking.getProvision(serviceProvider, verifier); + LinkedList.List memory beforeThawRequestList = staking.getThawRequestList( + serviceProvider, + verifier, + serviceProvider + ); + + bytes32 expectedThawRequestId = keccak256( + abi.encodePacked(users.indexer, verifier, users.indexer, beforeThawRequestList.nonce) + ); + uint256 thawingShares = beforeProvision.sharesThawing == 0 + ? tokens + : (beforeProvision.sharesThawing * tokens) / beforeProvision.tokensThawing; + + // thaw + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestCreated( + serviceProvider, + verifier, + serviceProvider, + thawingShares, + uint64(block.timestamp + beforeProvision.thawingPeriod), + expectedThawRequestId + ); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ProvisionThawed(serviceProvider, verifier, tokens); + bytes32 thawRequestId = staking.thaw(serviceProvider, verifier, tokens); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + ThawRequest memory afterThawRequest = staking.getThawRequest(thawRequestId); + LinkedList.List memory afterThawRequestList = staking.getThawRequestList( + serviceProvider, + verifier, + serviceProvider + ); + ThawRequest memory afterPreviousTailThawRequest = staking.getThawRequest(beforeThawRequestList.tail); + + // assert + assertEq(afterProvision.tokens, beforeProvision.tokens); + assertEq(afterProvision.tokensThawing, beforeProvision.tokensThawing + tokens); + assertEq(afterProvision.sharesThawing, beforeProvision.sharesThawing + thawingShares); + assertEq(afterProvision.maxVerifierCut, beforeProvision.maxVerifierCut); + assertEq(afterProvision.thawingPeriod, beforeProvision.thawingPeriod); + assertEq(afterProvision.createdAt, beforeProvision.createdAt); + assertEq(afterProvision.maxVerifierCutPending, beforeProvision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriodPending, beforeProvision.thawingPeriodPending); + assertEq(thawRequestId, expectedThawRequestId); + assertEq(afterThawRequest.shares, thawingShares); + assertEq(afterThawRequest.thawingUntil, block.timestamp + beforeProvision.thawingPeriod); + assertEq(afterThawRequest.next, bytes32(0)); + assertEq( + afterThawRequestList.head, + beforeThawRequestList.count == 0 ? thawRequestId : beforeThawRequestList.head + ); + assertEq(afterThawRequestList.tail, thawRequestId); + assertEq(afterThawRequestList.count, beforeThawRequestList.count + 1); + assertEq(afterThawRequestList.nonce, beforeThawRequestList.nonce + 1); + if (beforeThawRequestList.count != 0) { + assertEq(afterPreviousTailThawRequest.next, thawRequestId); + } + + return thawRequestId; + } + + function _deprovision(address serviceProvider, address verifier, uint256 nThawRequests) internal { + // before + Provision memory beforeProvision = staking.getProvision(serviceProvider, verifier); + ServiceProviderInternal memory beforeServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + LinkedList.List memory beforeThawRequestList = staking.getThawRequestList( + serviceProvider, + verifier, + serviceProvider + ); + + CalcValues_ThawRequestData memory calcValues = calcThawRequestData(serviceProvider, verifier, serviceProvider, nThawRequests, false); + + // deprovision + for (uint i = 0; i < calcValues.thawRequestsFulfilledList.length; i++) { + ThawRequest memory thawRequest = calcValues.thawRequestsFulfilledList[i]; + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestFulfilled( + calcValues.thawRequestsFulfilledListIds[i], + calcValues.thawRequestsFulfilledListTokens[i], + thawRequest.shares, + thawRequest.thawingUntil + ); + } + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestsFulfilled( + serviceProvider, + verifier, + serviceProvider, + calcValues.thawRequestsFulfilledList.length, + calcValues.tokensThawed + ); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.TokensDeprovisioned(serviceProvider, verifier, calcValues.tokensThawed); + staking.deprovision(serviceProvider, verifier, nThawRequests); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + LinkedList.List memory afterThawRequestList = staking.getThawRequestList( + serviceProvider, + verifier, + serviceProvider + ); + + // assert + assertEq(afterProvision.tokens, beforeProvision.tokens - calcValues.tokensThawed); + assertEq(afterProvision.tokensThawing, calcValues.tokensThawing); + assertEq(afterProvision.sharesThawing, calcValues.sharesThawing); + assertEq(afterProvision.maxVerifierCut, beforeProvision.maxVerifierCut); + assertEq(afterProvision.thawingPeriod, beforeProvision.thawingPeriod); + assertEq(afterProvision.createdAt, beforeProvision.createdAt); + assertEq(afterProvision.maxVerifierCutPending, beforeProvision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriodPending, beforeProvision.thawingPeriodPending); + assertEq(afterServiceProvider.tokensStaked, beforeServiceProvider.tokensStaked); + assertEq(afterServiceProvider.tokensProvisioned, beforeServiceProvider.tokensProvisioned - calcValues.tokensThawed); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeServiceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeServiceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeServiceProvider.__DEPRECATED_tokensLockedUntil + ); + for (uint i = 0; i < calcValues.thawRequestsFulfilledListIds.length; i++) { + ThawRequest memory thawRequest = staking.getThawRequest(calcValues.thawRequestsFulfilledListIds[i]); + assertEq(thawRequest.shares, 0); + assertEq(thawRequest.thawingUntil, 0); + assertEq(thawRequest.next, bytes32(0)); + } + if (calcValues.thawRequestsFulfilledList.length == 0) { + assertEq(afterThawRequestList.head, beforeThawRequestList.head); + } else { + assertEq( + afterThawRequestList.head, + calcValues.thawRequestsFulfilledList.length == beforeThawRequestList.count + ? bytes32(0) + : calcValues.thawRequestsFulfilledList[calcValues.thawRequestsFulfilledList.length - 1].next + ); + } + assertEq( + afterThawRequestList.tail, + calcValues.thawRequestsFulfilledList.length == beforeThawRequestList.count + ? bytes32(0) + : beforeThawRequestList.tail + ); + assertEq(afterThawRequestList.count, beforeThawRequestList.count - calcValues.thawRequestsFulfilledList.length); + assertEq(afterThawRequestList.nonce, beforeThawRequestList.nonce); + } + + struct BeforeValues_Reprovision { + Provision provision; + Provision provisionNewVerifier; + ServiceProviderInternal serviceProvider; + LinkedList.List thawRequestList; + } + function _reprovision( + address serviceProvider, + address verifier, + address newVerifier, + uint256 tokens, + uint256 nThawRequests + ) internal { + // before + BeforeValues_Reprovision memory beforeValues = BeforeValues_Reprovision({ + provision: staking.getProvision(serviceProvider, verifier), + provisionNewVerifier: staking.getProvision(serviceProvider, newVerifier), + serviceProvider: _getStorage_ServiceProviderInternal(serviceProvider), + thawRequestList: staking.getThawRequestList(serviceProvider, verifier, serviceProvider) + }); + + // calc + CalcValues_ThawRequestData memory calcValues = calcThawRequestData(serviceProvider, verifier, serviceProvider, nThawRequests, false); + + // reprovision + for (uint i = 0; i < calcValues.thawRequestsFulfilledList.length; i++) { + ThawRequest memory thawRequest = calcValues.thawRequestsFulfilledList[i]; + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestFulfilled( + calcValues.thawRequestsFulfilledListIds[i], + calcValues.thawRequestsFulfilledListTokens[i], + thawRequest.shares, + thawRequest.thawingUntil + ); + } + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestsFulfilled( + serviceProvider, + verifier, + serviceProvider, + calcValues.thawRequestsFulfilledList.length, + calcValues.tokensThawed + ); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.TokensDeprovisioned(serviceProvider, verifier, calcValues.tokensThawed); + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionIncreased(serviceProvider, newVerifier, tokens); + staking.reprovision(serviceProvider, verifier, newVerifier, tokens, nThawRequests); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + Provision memory afterProvisionNewVerifier = staking.getProvision(serviceProvider, newVerifier); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + LinkedList.List memory afterThawRequestList = staking.getThawRequestList( + serviceProvider, + verifier, + serviceProvider + ); + + // assert: provision old verifier + assertEq(afterProvision.tokens, beforeValues.provision.tokens - calcValues.tokensThawed); + assertEq(afterProvision.tokensThawing, calcValues.tokensThawing); + assertEq(afterProvision.sharesThawing, calcValues.sharesThawing); + assertEq(afterProvision.maxVerifierCut, beforeValues.provision.maxVerifierCut); + assertEq(afterProvision.thawingPeriod, beforeValues.provision.thawingPeriod); + assertEq(afterProvision.createdAt, beforeValues.provision.createdAt); + assertEq(afterProvision.maxVerifierCutPending, beforeValues.provision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriodPending, beforeValues.provision.thawingPeriodPending); + + // assert: provision new verifier + assertEq(afterProvisionNewVerifier.tokens, beforeValues.provisionNewVerifier.tokens + tokens); + assertEq(afterProvisionNewVerifier.tokensThawing, beforeValues.provisionNewVerifier.tokensThawing); + assertEq(afterProvisionNewVerifier.sharesThawing, beforeValues.provisionNewVerifier.sharesThawing); + assertEq(afterProvisionNewVerifier.maxVerifierCut, beforeValues.provisionNewVerifier.maxVerifierCut); + assertEq(afterProvisionNewVerifier.thawingPeriod, beforeValues.provisionNewVerifier.thawingPeriod); + assertEq(afterProvisionNewVerifier.createdAt, beforeValues.provisionNewVerifier.createdAt); + assertEq(afterProvisionNewVerifier.maxVerifierCutPending, beforeValues.provisionNewVerifier.maxVerifierCutPending); + assertEq(afterProvisionNewVerifier.thawingPeriodPending, beforeValues.provisionNewVerifier.thawingPeriodPending); + + // assert: service provider + assertEq(afterServiceProvider.tokensStaked, beforeValues.serviceProvider.tokensStaked); + assertEq( + afterServiceProvider.tokensProvisioned, + beforeValues.serviceProvider.tokensProvisioned + tokens - calcValues.tokensThawed + ); + assertEq(afterServiceProvider.__DEPRECATED_tokensAllocated, beforeValues.serviceProvider.__DEPRECATED_tokensAllocated); + assertEq(afterServiceProvider.__DEPRECATED_tokensLocked, beforeValues.serviceProvider.__DEPRECATED_tokensLocked); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeValues.serviceProvider.__DEPRECATED_tokensLockedUntil + ); + + // assert: thaw request list old verifier + for (uint i = 0; i < calcValues.thawRequestsFulfilledListIds.length; i++) { + ThawRequest memory thawRequest = staking.getThawRequest(calcValues.thawRequestsFulfilledListIds[i]); + assertEq(thawRequest.shares, 0); + assertEq(thawRequest.thawingUntil, 0); + assertEq(thawRequest.next, bytes32(0)); + } + if (calcValues.thawRequestsFulfilledList.length == 0) { + assertEq(afterThawRequestList.head, beforeValues.thawRequestList.head); + } else { + assertEq( + afterThawRequestList.head, + calcValues.thawRequestsFulfilledList.length == beforeValues.thawRequestList.count + ? bytes32(0) + : calcValues.thawRequestsFulfilledList[calcValues.thawRequestsFulfilledList.length - 1].next + ); + } + assertEq( + afterThawRequestList.tail, + calcValues.thawRequestsFulfilledList.length == beforeValues.thawRequestList.count + ? bytes32(0) + : beforeValues.thawRequestList.tail + ); + assertEq(afterThawRequestList.count, beforeValues.thawRequestList.count - calcValues.thawRequestsFulfilledList.length); + assertEq(afterThawRequestList.nonce, beforeValues.thawRequestList.nonce); + } + + function _setProvisionParameters( + address serviceProvider, + address verifier, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) internal { + // before + Provision memory beforeProvision = staking.getProvision(serviceProvider, verifier); + + // setProvisionParameters + if (beforeProvision.maxVerifierCut != maxVerifierCut || beforeProvision.thawingPeriod != thawingPeriod) { + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionParametersStaged( + serviceProvider, + verifier, + maxVerifierCut, + thawingPeriod + ); + } + staking.setProvisionParameters(serviceProvider, verifier, maxVerifierCut, thawingPeriod); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + + // assert + assertEq(afterProvision.tokens, beforeProvision.tokens); + assertEq(afterProvision.tokensThawing, beforeProvision.tokensThawing); + assertEq(afterProvision.sharesThawing, beforeProvision.sharesThawing); + assertEq(afterProvision.maxVerifierCut, beforeProvision.maxVerifierCut); + assertEq(afterProvision.thawingPeriod, beforeProvision.thawingPeriod); + assertEq(afterProvision.createdAt, beforeProvision.createdAt); + assertEq(afterProvision.maxVerifierCutPending, maxVerifierCut); + assertEq(afterProvision.thawingPeriodPending, thawingPeriod); + } + + function _acceptProvisionParameters(address serviceProvider) internal { + (, address msgSender, ) = vm.readCallers(); + + // before + Provision memory beforeProvision = staking.getProvision(serviceProvider, msgSender); + + // acceptProvisionParameters + if ( + beforeProvision.maxVerifierCutPending != beforeProvision.maxVerifierCut || + beforeProvision.thawingPeriodPending != beforeProvision.thawingPeriod + ) { + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionParametersSet( + serviceProvider, + msgSender, + beforeProvision.maxVerifierCutPending, + beforeProvision.thawingPeriodPending + ); + } + staking.acceptProvisionParameters(serviceProvider); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, msgSender); + + // assert + assertEq(afterProvision.tokens, beforeProvision.tokens); + assertEq(afterProvision.tokensThawing, beforeProvision.tokensThawing); + assertEq(afterProvision.sharesThawing, beforeProvision.sharesThawing); + assertEq(afterProvision.maxVerifierCut, beforeProvision.maxVerifierCutPending); + assertEq(afterProvision.maxVerifierCut, afterProvision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriod, beforeProvision.thawingPeriodPending); + assertEq(afterProvision.thawingPeriod, afterProvision.thawingPeriodPending); + assertEq(afterProvision.createdAt, beforeProvision.createdAt); + } + + function _setOperator(address operator, address verifier, bool allow) internal { + __setOperator(operator, verifier, allow, false); + } + + function _setOperatorLocked(address operator, address verifier, bool allow) internal { + __setOperator(operator, verifier, allow, true); + } + + function __setOperator(address operator, address verifier, bool allow, bool locked) private { + (, address msgSender, ) = vm.readCallers(); + + // staking contract knows the address of the legacy subgraph service + // but we cannot read it as it's an immutable, we have to use the global var :/ + bool legacy = verifier == subgraphDataServiceLegacyAddress; + + // before + bool beforeOperatorAllowed = _getStorage_OperatorAuth(msgSender, operator, verifier, legacy); + bool beforeOperatorAllowedGetter = staking.isAuthorized(operator, msgSender, verifier); + assertEq(beforeOperatorAllowed, beforeOperatorAllowedGetter); + + // setOperator + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.OperatorSet(msgSender, operator, verifier, allow); + if (locked) { + staking.setOperatorLocked(operator, verifier, allow); + } else { + staking.setOperator(operator, verifier, allow); + } + + // after + bool afterOperatorAllowed = _getStorage_OperatorAuth(msgSender, operator, verifier, legacy); + bool afterOperatorAllowedGetter = staking.isAuthorized(operator, msgSender, verifier); + assertEq(afterOperatorAllowed, afterOperatorAllowedGetter); + + // assert + assertEq(afterOperatorAllowed, allow); + } + + function _delegate(address serviceProvider, address verifier, uint256 tokens, uint256 minSharesOut) internal { + __delegate(serviceProvider, verifier, tokens, minSharesOut, false); + } + + function _delegate(address serviceProvider, uint256 tokens) internal { + __delegate(serviceProvider, subgraphDataServiceLegacyAddress, tokens, 0, true); + } + + function __delegate( + address serviceProvider, + address verifier, + uint256 tokens, + uint256 minSharesOut, + bool legacy + ) private { + (, address delegator, ) = vm.readCallers(); + + // before + DelegationPoolInternalTest memory beforePool = _getStorage_DelegationPoolInternal( + serviceProvider, + verifier, + legacy + ); + DelegationInternal memory beforeDelegation = _getStorage_Delegation( + serviceProvider, + verifier, + delegator, + legacy + ); + uint256 beforeDelegatorBalance = token.balanceOf(delegator); + uint256 beforeStakingBalance = token.balanceOf(address(staking)); + + uint256 calcShares = (beforePool.tokens == 0 || beforePool.tokens == beforePool.tokensThawing) + ? tokens + : ((tokens * beforePool.shares) / (beforePool.tokens - beforePool.tokensThawing)); + + // delegate token.approve(address(staking), tokens); - staking.stakeTo(users.indexer, tokens); - staking.provision(users.indexer, dataServiceAddress, tokens, maxVerifierCut, thawingPeriod); + vm.expectEmit(); + emit IHorizonStakingMain.TokensDelegated(serviceProvider, verifier, delegator, tokens); + if (legacy) { + staking.delegate(serviceProvider, tokens); + } else { + staking.delegate(serviceProvider, verifier, tokens, minSharesOut); + } + + // after + DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal( + serviceProvider, + verifier, + legacy + ); + DelegationInternal memory afterDelegation = _getStorage_Delegation( + serviceProvider, + verifier, + delegator, + legacy + ); + uint256 afterDelegatorBalance = token.balanceOf(delegator); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + + uint256 deltaShares = afterDelegation.shares - beforeDelegation.shares; + + // assertions + assertEq(beforePool.tokens + tokens, afterPool.tokens); + assertEq(beforePool.shares + calcShares, afterPool.shares); + assertEq(beforePool.tokensThawing, afterPool.tokensThawing); + assertEq(beforePool.sharesThawing, afterPool.sharesThawing); + assertEq(beforeDelegation.shares + calcShares, afterDelegation.shares); + assertEq(beforeDelegation.__DEPRECATED_tokensLocked, afterDelegation.__DEPRECATED_tokensLocked); + assertEq(beforeDelegation.__DEPRECATED_tokensLockedUntil, afterDelegation.__DEPRECATED_tokensLockedUntil); + assertGe(deltaShares, minSharesOut); + assertEq(calcShares, deltaShares); + assertEq(beforeDelegatorBalance - tokens, afterDelegatorBalance); + assertEq(beforeStakingBalance + tokens, afterStakingBalance); + } + + function _undelegate(address serviceProvider, address verifier, uint256 shares) internal { + __undelegate(serviceProvider, verifier, shares, false); + } + + function _undelegate(address serviceProvider, uint256 shares) internal { + __undelegate(serviceProvider, subgraphDataServiceLegacyAddress, shares, true); + } + + struct BeforeValues_Undelegate { + DelegationPoolInternalTest pool; + DelegationInternal delegation; + LinkedList.List thawRequestList; + uint256 delegatedTokens; + } + struct CalcValues_Undelegate { + uint256 tokens; + uint256 thawingShares; + uint64 thawingUntil; + bytes32 thawRequestId; + } + + function __undelegate(address serviceProvider, address verifier, uint256 shares, bool legacy) private { + (, address delegator, ) = vm.readCallers(); + + // before + BeforeValues_Undelegate memory beforeValues; + beforeValues.pool = _getStorage_DelegationPoolInternal(serviceProvider, verifier, legacy); + beforeValues.delegation = _getStorage_Delegation(serviceProvider, verifier, delegator, legacy); + beforeValues.thawRequestList = staking.getThawRequestList(serviceProvider, verifier, delegator); + beforeValues.delegatedTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); + + // calc + CalcValues_Undelegate memory calcValues; + calcValues.tokens = ((beforeValues.pool.tokens - beforeValues.pool.tokensThawing) * shares) / beforeValues.pool.shares; + calcValues.thawingShares = beforeValues.pool.tokensThawing == 0 + ? calcValues.tokens + : (beforeValues.pool.sharesThawing * calcValues.tokens) / beforeValues.pool.tokensThawing; + calcValues.thawingUntil = + staking.getProvision(serviceProvider, verifier).thawingPeriod + + uint64(block.timestamp); + calcValues.thawRequestId = keccak256( + abi.encodePacked(serviceProvider, verifier, delegator, beforeValues.thawRequestList.nonce) + ); + + // undelegate + vm.expectEmit(); + emit IHorizonStakingMain.ThawRequestCreated( + serviceProvider, + verifier, + delegator, + calcValues.thawingShares, + calcValues.thawingUntil, + calcValues.thawRequestId + ); + vm.expectEmit(); + emit IHorizonStakingMain.TokensUndelegated(serviceProvider, verifier, delegator, calcValues.tokens); + if (legacy) { + staking.undelegate(serviceProvider, shares); + } else { + staking.undelegate(serviceProvider, verifier, shares); + } + + // after + DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal( + users.indexer, + verifier, + legacy + ); + DelegationInternal memory afterDelegation = _getStorage_Delegation( + serviceProvider, + verifier, + delegator, + legacy + ); + LinkedList.List memory afterThawRequestList = staking.getThawRequestList(serviceProvider, verifier, delegator); + ThawRequest memory afterThawRequest = staking.getThawRequest(calcValues.thawRequestId); + uint256 afterDelegatedTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); + + // assertions + assertEq(beforeValues.pool.shares, afterPool.shares + shares); + assertEq(beforeValues.pool.tokens, afterPool.tokens); + assertEq(beforeValues.pool.tokensThawing + calcValues.tokens, afterPool.tokensThawing); + assertEq(beforeValues.pool.sharesThawing + calcValues.thawingShares, afterPool.sharesThawing); + assertEq(beforeValues.delegation.shares - shares, afterDelegation.shares); + assertEq(afterThawRequest.shares, calcValues.thawingShares); + assertEq(afterThawRequest.thawingUntil, calcValues.thawingUntil); + assertEq(afterThawRequest.next, bytes32(0)); + assertEq(calcValues.thawRequestId, afterThawRequestList.tail); + assertEq(beforeValues.thawRequestList.nonce + 1, afterThawRequestList.nonce); + assertEq(beforeValues.thawRequestList.count + 1, afterThawRequestList.count); + assertEq(afterDelegatedTokens + calcValues.tokens, beforeValues.delegatedTokens); + } + + function _withdrawDelegated( + address serviceProvider, + address verifier, + address newServiceProvider, + uint256 minSharesForNewProvider, + uint256 nThawRequests + ) internal { + __withdrawDelegated( + serviceProvider, + verifier, + newServiceProvider, + minSharesForNewProvider, + nThawRequests, + false + ); + } + + function _withdrawDelegated(address serviceProvider, address newServiceProvider) internal { + __withdrawDelegated(serviceProvider, subgraphDataServiceLegacyAddress, newServiceProvider, 0, 0, true); + } + + struct BeforeValues_WithdrawDelegated { + DelegationPoolInternalTest pool; + DelegationPoolInternalTest newPool; + DelegationInternal newDelegation; + LinkedList.List thawRequestList; + uint256 senderBalance; + uint256 stakingBalance; + } + struct AfterValues_WithdrawDelegated { + DelegationPoolInternalTest pool; + DelegationPoolInternalTest newPool; + DelegationInternal newDelegation; + LinkedList.List thawRequestList; + uint256 senderBalance; + uint256 stakingBalance; + } + function __withdrawDelegated( + address _serviceProvider, + address _verifier, + address _newServiceProvider, + uint256 _minSharesForNewProvider, + uint256 _nThawRequests, + bool legacy + ) private { + (, address msgSender, ) = vm.readCallers(); + + bool reDelegate = _newServiceProvider != address(0); + + // before + BeforeValues_WithdrawDelegated memory beforeValues; + beforeValues.pool = _getStorage_DelegationPoolInternal(_serviceProvider, _verifier, legacy); + beforeValues.newPool = _getStorage_DelegationPoolInternal(_newServiceProvider, _verifier, legacy); + beforeValues.newDelegation = _getStorage_Delegation(_serviceProvider, _verifier, msgSender, legacy); + beforeValues.thawRequestList = staking.getThawRequestList(_serviceProvider, _verifier, msgSender); + beforeValues.senderBalance = token.balanceOf(msgSender); + beforeValues.stakingBalance = token.balanceOf(address(staking)); + + CalcValues_ThawRequestData memory calcValues = calcThawRequestData( + _serviceProvider, + _verifier, + msgSender, + _nThawRequests, + true + ); + + // withdrawDelegated + for (uint i = 0; i < calcValues.thawRequestsFulfilledList.length; i++) { + ThawRequest memory thawRequest = calcValues.thawRequestsFulfilledList[i]; + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestFulfilled( + calcValues.thawRequestsFulfilledListIds[i], + calcValues.thawRequestsFulfilledListTokens[i], + thawRequest.shares, + thawRequest.thawingUntil + ); + } + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawRequestsFulfilled( + _serviceProvider, + _verifier, + msgSender, + calcValues.thawRequestsFulfilledList.length, + calcValues.tokensThawed + ); + if (calcValues.tokensThawed != 0) { + vm.expectEmit(); + if (reDelegate) { + emit IHorizonStakingMain.TokensDelegated(_newServiceProvider, _verifier, msgSender, calcValues.tokensThawed); + } else { + emit Transfer(address(staking), msgSender, calcValues.tokensThawed); + } + } + vm.expectEmit(); + emit IHorizonStakingMain.DelegatedTokensWithdrawn(_serviceProvider, _verifier, msgSender, calcValues.tokensThawed); + staking.withdrawDelegated( + _serviceProvider, + _verifier, + _newServiceProvider, + _minSharesForNewProvider, + _nThawRequests + ); + + // after + AfterValues_WithdrawDelegated memory afterValues; + afterValues.pool = _getStorage_DelegationPoolInternal(_serviceProvider, _verifier, legacy); + afterValues.newPool = _getStorage_DelegationPoolInternal(_newServiceProvider, _verifier, legacy); + afterValues.newDelegation = _getStorage_Delegation(_newServiceProvider, _verifier, msgSender, legacy); + afterValues.thawRequestList = staking.getThawRequestList(_serviceProvider, _verifier, msgSender); + afterValues.senderBalance = token.balanceOf(msgSender); + afterValues.stakingBalance = token.balanceOf(address(staking)); + + // assert + assertEq(afterValues.pool.tokens, beforeValues.pool.tokens - calcValues.tokensThawed); + assertEq(afterValues.pool.shares, beforeValues.pool.shares); + assertEq(afterValues.pool.tokensThawing, calcValues.tokensThawing); + assertEq(afterValues.pool.sharesThawing, calcValues.sharesThawing); + + for (uint i = 0; i < calcValues.thawRequestsFulfilledListIds.length; i++) { + ThawRequest memory thawRequest = staking.getThawRequest(calcValues.thawRequestsFulfilledListIds[i]); + assertEq(thawRequest.shares, 0); + assertEq(thawRequest.thawingUntil, 0); + assertEq(thawRequest.next, bytes32(0)); + } + if (calcValues.thawRequestsFulfilledList.length == 0) { + assertEq(afterValues.thawRequestList.head, beforeValues.thawRequestList.head); + } else { + assertEq( + afterValues.thawRequestList.head, + calcValues.thawRequestsFulfilledList.length == beforeValues.thawRequestList.count + ? bytes32(0) + : calcValues.thawRequestsFulfilledList[calcValues.thawRequestsFulfilledList.length - 1].next + ); + } + assertEq( + afterValues.thawRequestList.tail, + calcValues.thawRequestsFulfilledList.length == beforeValues.thawRequestList.count + ? bytes32(0) + : beforeValues.thawRequestList.tail + ); + assertEq(afterValues.thawRequestList.count, beforeValues.thawRequestList.count - calcValues.thawRequestsFulfilledList.length); + assertEq(afterValues.thawRequestList.nonce, beforeValues.thawRequestList.nonce); + + if (reDelegate) { + uint256 calcShares = (afterValues.newPool.tokens == 0 || + afterValues.newPool.tokens == afterValues.newPool.tokensThawing) + ? calcValues.tokensThawed + : ((calcValues.tokensThawed * afterValues.newPool.shares) / + (afterValues.newPool.tokens - afterValues.newPool.tokensThawing)); + uint256 deltaShares = afterValues.newDelegation.shares - beforeValues.newDelegation.shares; + + assertEq(afterValues.newPool.tokens, beforeValues.newPool.tokens + calcValues.tokensThawed); + assertEq(afterValues.newPool.shares, beforeValues.newPool.shares + calcShares); + assertEq(afterValues.newPool.tokensThawing, beforeValues.newPool.tokensThawing); + assertEq(afterValues.newPool.sharesThawing, beforeValues.newPool.sharesThawing); + assertEq(afterValues.newDelegation.shares, beforeValues.newDelegation.shares + calcShares); + assertEq(afterValues.newDelegation.__DEPRECATED_tokensLocked, beforeValues.newDelegation.__DEPRECATED_tokensLocked); + assertEq( + afterValues.newDelegation.__DEPRECATED_tokensLockedUntil, + beforeValues.newDelegation.__DEPRECATED_tokensLockedUntil + ); + assertGe(deltaShares, _minSharesForNewProvider); + assertEq(calcShares, deltaShares); + assertEq(afterValues.senderBalance - beforeValues.senderBalance, 0); + assertEq(beforeValues.stakingBalance - afterValues.stakingBalance, 0); + } else { + assertEq(beforeValues.stakingBalance - afterValues.stakingBalance, calcValues.tokensThawed); + assertEq(afterValues.senderBalance - beforeValues.senderBalance, calcValues.tokensThawed); + } + } + + function _addToDelegationPool(address serviceProvider, address verifier, uint256 tokens) internal { + (, address msgSender, ) = vm.readCallers(); + + // staking contract knows the address of the legacy subgraph service + // but we cannot read it as it's an immutable, we have to use the global var :/ + bool legacy = verifier == subgraphDataServiceLegacyAddress; + + // before + DelegationPoolInternalTest memory beforePool = _getStorage_DelegationPoolInternal( + serviceProvider, + verifier, + legacy + ); + uint256 beforeSenderBalance = token.balanceOf(msgSender); + uint256 beforeStakingBalance = token.balanceOf(address(staking)); + + // addToDelegationPool + vm.expectEmit(); + emit Transfer(msgSender, address(staking), tokens); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.TokensToDelegationPoolAdded(serviceProvider, verifier, tokens); + staking.addToDelegationPool(serviceProvider, verifier, tokens); + + // after + DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal( + serviceProvider, + verifier, + legacy + ); + uint256 afterSenderBalance = token.balanceOf(msgSender); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + + // assert + assertEq(beforeSenderBalance - tokens, afterSenderBalance); + assertEq(beforeStakingBalance + tokens, afterStakingBalance); + assertEq(beforePool.tokens + tokens, afterPool.tokens); + assertEq(beforePool.shares, afterPool.shares); + assertEq(beforePool.tokensThawing, afterPool.tokensThawing); + assertEq(beforePool.sharesThawing, afterPool.sharesThawing); } - function _setDelegationFeeCut(IGraphPayments.PaymentTypes paymentType, uint256 cut) internal { - staking.setDelegationFeeCut(users.indexer, subgraphDataServiceAddress, paymentType, cut); - uint256 delegationFeeCut = staking.getDelegationFeeCut(users.indexer, subgraphDataServiceAddress, paymentType); - assertEq(delegationFeeCut, cut); + function _setDelegationFeeCut( + address serviceProvider, + address verifier, + IGraphPayments.PaymentTypes paymentType, + uint256 feeCut + ) internal { + // setDelegationFeeCut + vm.expectEmit(); + emit IHorizonStakingMain.DelegationFeeCutSet(serviceProvider, verifier, paymentType, feeCut); + staking.setDelegationFeeCut(serviceProvider, verifier, paymentType, feeCut); + + // after + uint256 afterDelegationFeeCut = staking.getDelegationFeeCut(serviceProvider, verifier, paymentType); + + // assert + assertEq(afterDelegationFeeCut, feeCut); + } + + function _setAllowedLockedVerifier(address verifier, bool allowed) internal { + // setAllowedLockedVerifier + vm.expectEmit(); + emit IHorizonStakingMain.AllowedLockedVerifierSet(verifier, allowed); + staking.setAllowedLockedVerifier(verifier, allowed); + + // after + bool afterAllowed = staking.isAllowedLockedVerifier(verifier); + + // assert + assertEq(afterAllowed, allowed); + } + + function _setDelegationSlashingEnabled() internal { + // setDelegationSlashingEnabled + vm.expectEmit(); + emit IHorizonStakingMain.DelegationSlashingEnabled(true); + staking.setDelegationSlashingEnabled(); + + // after + bool afterEnabled = staking.isDelegationSlashingEnabled(); + + // assert + assertEq(afterEnabled, true); + } + + function _clearThawingPeriod() internal { + // clearThawingPeriod + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ThawingPeriodCleared(); + staking.clearThawingPeriod(); + + // after + uint64 afterThawingPeriod = staking.__DEPRECATED_getThawingPeriod(); + + // assert + assertEq(afterThawingPeriod, 0); + } + + function _setMaxThawingPeriod(uint64 maxThawingPeriod) internal { + // setMaxThawingPeriod + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.MaxThawingPeriodSet(maxThawingPeriod); + staking.setMaxThawingPeriod(maxThawingPeriod); + + // after + uint64 afterMaxThawingPeriod = staking.getMaxThawingPeriod(); + + // assert + 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; + ServiceProviderInternal serviceProvider; + uint256 stakingBalance; + uint256 verifierBalance; + } + struct CalcValues_Slash { + uint256 tokensToSlash; + uint256 providerTokensSlashed; + uint256 delegationTokensSlashed; + } + + function _slash(address serviceProvider, address verifier, uint256 tokens, uint256 verifierCutAmount) internal { + bool isDelegationSlashingEnabled = staking.isDelegationSlashingEnabled(); + + // staking contract knows the address of the legacy subgraph service + // but we cannot read it as it's an immutable, we have to use the global var :/ + bool legacy = verifier == subgraphDataServiceLegacyAddress; + + // before + BeforeValues_Slash memory before; + before.provision = staking.getProvision(serviceProvider, verifier); + before.pool = _getStorage_DelegationPoolInternal(serviceProvider, verifier, legacy); + before.serviceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + before.stakingBalance = token.balanceOf(address(staking)); + before.verifierBalance = token.balanceOf(verifier); + + // Calculate expected tokens after slashing + CalcValues_Slash memory calcValues; + calcValues.tokensToSlash = MathUtils.min(tokens, before.provision.tokens + before.pool.tokens); + calcValues.providerTokensSlashed = MathUtils.min(before.provision.tokens, calcValues.tokensToSlash); + calcValues.delegationTokensSlashed = calcValues.tokensToSlash - calcValues.providerTokensSlashed; + + if (calcValues.tokensToSlash > 0) { + if (verifierCutAmount > 0) { + vm.expectEmit(address(token)); + emit Transfer(address(staking), verifier, verifierCutAmount); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.VerifierTokensSent(serviceProvider, verifier, verifier, verifierCutAmount); + } + if (calcValues.providerTokensSlashed - verifierCutAmount > 0) { + vm.expectEmit(address(token)); + emit Transfer(address(staking), address(0), calcValues.providerTokensSlashed - verifierCutAmount); + } + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.ProvisionSlashed(serviceProvider, verifier, calcValues.providerTokensSlashed); + } + + if (calcValues.delegationTokensSlashed > 0) { + if (isDelegationSlashingEnabled) { + vm.expectEmit(address(token)); + emit Transfer(address(staking), address(0), calcValues.delegationTokensSlashed); + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.DelegationSlashed( + serviceProvider, + verifier, + calcValues.delegationTokensSlashed + ); + } else { + vm.expectEmit(address(staking)); + emit IHorizonStakingMain.DelegationSlashingSkipped( + serviceProvider, + verifier, + calcValues.delegationTokensSlashed + ); + } + } + staking.slash(serviceProvider, tokens, verifierCutAmount, verifier); + + // after + Provision memory afterProvision = staking.getProvision(serviceProvider, verifier); + DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal( + serviceProvider, + verifier, + legacy + ); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal(serviceProvider); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + uint256 afterVerifierBalance = token.balanceOf(verifier); + + { + uint256 tokensSlashed = calcValues.providerTokensSlashed + + (isDelegationSlashingEnabled ? calcValues.delegationTokensSlashed : 0); + uint256 provisionThawingTokens = (before.provision.tokensThawing * + (1e18 - ((calcValues.providerTokensSlashed * 1e18) / before.provision.tokens))) / (1e18); + + // assert + assertEq(afterProvision.tokens + calcValues.providerTokensSlashed, before.provision.tokens); + assertEq(afterProvision.tokensThawing, provisionThawingTokens); + assertEq(afterProvision.sharesThawing, before.provision.sharesThawing); + assertEq(afterProvision.maxVerifierCut, before.provision.maxVerifierCut); + assertEq(afterProvision.maxVerifierCutPending, before.provision.maxVerifierCutPending); + assertEq(afterProvision.thawingPeriod, before.provision.thawingPeriod); + assertEq(afterProvision.thawingPeriodPending, before.provision.thawingPeriodPending); + + if (isDelegationSlashingEnabled) { + uint256 poolThawingTokens = (before.pool.tokensThawing * + (1e18 - ((calcValues.delegationTokensSlashed * 1e18) / before.pool.tokens))) / (1e18); + assertEq(afterPool.tokens + calcValues.delegationTokensSlashed, before.pool.tokens); + assertEq(afterPool.shares, before.pool.shares); + assertEq(afterPool.tokensThawing, poolThawingTokens); + assertEq(afterPool.sharesThawing, before.pool.sharesThawing); + } + + assertEq(before.stakingBalance - tokensSlashed, afterStakingBalance); + assertEq(before.verifierBalance + verifierCutAmount, afterVerifierBalance); + + assertEq( + afterServiceProvider.tokensStaked + calcValues.providerTokensSlashed, + before.serviceProvider.tokensStaked + ); + assertEq( + afterServiceProvider.tokensProvisioned + calcValues.providerTokensSlashed, + before.serviceProvider.tokensProvisioned + ); + } + } + + // use struct to avoid 'stack too deep' error + struct CalcValues_CloseAllocation { + uint256 rewards; + uint256 delegatorRewards; + uint256 indexerRewards; + } + struct BeforeValues_CloseAllocation { + IHorizonStakingExtension.Allocation allocation; + DelegationPoolInternalTest pool; + ServiceProviderInternal serviceProvider; + uint256 subgraphAllocations; + uint256 stakingBalance; + uint256 indexerBalance; + uint256 beneficiaryBalance; + } + + // Current rewards manager is mocked and assumed to mint fixed rewards + function _closeAllocation(address allocationId, bytes32 poi) internal { + (, address msgSender, ) = vm.readCallers(); + + // before + BeforeValues_CloseAllocation memory beforeValues; + beforeValues.allocation = staking.getAllocation(allocationId); + beforeValues.pool = _getStorage_DelegationPoolInternal( + beforeValues.allocation.indexer, + subgraphDataServiceLegacyAddress, + true + ); + beforeValues.serviceProvider = _getStorage_ServiceProviderInternal(beforeValues.allocation.indexer); + beforeValues.subgraphAllocations = _getStorage_SubgraphAllocations( + beforeValues.allocation.subgraphDeploymentID + ); + beforeValues.stakingBalance = token.balanceOf(address(staking)); + beforeValues.indexerBalance = token.balanceOf(beforeValues.allocation.indexer); + beforeValues.beneficiaryBalance = token.balanceOf( + _getStorage_RewardsDestination(beforeValues.allocation.indexer) + ); + + bool isAuth = staking.isAuthorized( + msgSender, + beforeValues.allocation.indexer, + subgraphDataServiceLegacyAddress + ); + address rewardsDestination = _getStorage_RewardsDestination(beforeValues.allocation.indexer); + + CalcValues_CloseAllocation memory calcValues = CalcValues_CloseAllocation({ + rewards: ALLOCATIONS_REWARD_CUT, + delegatorRewards: ALLOCATIONS_REWARD_CUT - + uint256(beforeValues.pool.__DEPRECATED_indexingRewardCut).mulPPM(ALLOCATIONS_REWARD_CUT), + indexerRewards: 0 + }); + calcValues.indexerRewards = + ALLOCATIONS_REWARD_CUT - + (beforeValues.pool.tokens > 0 ? calcValues.delegatorRewards : 0); + + // closeAllocation + vm.expectEmit(address(staking)); + emit IHorizonStakingExtension.AllocationClosed( + beforeValues.allocation.indexer, + beforeValues.allocation.subgraphDeploymentID, + epochManager.currentEpoch(), + beforeValues.allocation.tokens, + allocationId, + msgSender, + poi, + !isAuth + ); + staking.closeAllocation(allocationId, poi); + + // after + IHorizonStakingExtension.Allocation memory afterAllocation = staking.getAllocation(allocationId); + DelegationPoolInternalTest memory afterPool = _getStorage_DelegationPoolInternal( + beforeValues.allocation.indexer, + subgraphDataServiceLegacyAddress, + true + ); + ServiceProviderInternal memory afterServiceProvider = _getStorage_ServiceProviderInternal( + beforeValues.allocation.indexer + ); + uint256 afterSubgraphAllocations = _getStorage_SubgraphAllocations( + beforeValues.allocation.subgraphDeploymentID + ); + uint256 afterStakingBalance = token.balanceOf(address(staking)); + uint256 afterIndexerBalance = token.balanceOf(beforeValues.allocation.indexer); + uint256 afterBeneficiaryBalance = token.balanceOf(rewardsDestination); + + if (beforeValues.allocation.tokens > 0) { + if (isAuth && poi != 0) { + if (rewardsDestination != address(0)) { + assertEq( + beforeValues.stakingBalance + calcValues.rewards - calcValues.indexerRewards, + afterStakingBalance + ); + assertEq(beforeValues.indexerBalance, afterIndexerBalance); + assertEq(beforeValues.beneficiaryBalance + calcValues.indexerRewards, afterBeneficiaryBalance); + } else { + assertEq(beforeValues.stakingBalance + calcValues.rewards, afterStakingBalance); + assertEq(beforeValues.indexerBalance, afterIndexerBalance); + assertEq(beforeValues.beneficiaryBalance, afterBeneficiaryBalance); + } + } else { + assertEq(beforeValues.stakingBalance, afterStakingBalance); + assertEq(beforeValues.indexerBalance, afterIndexerBalance); + assertEq(beforeValues.beneficiaryBalance, afterBeneficiaryBalance); + } + } else { + assertEq(beforeValues.stakingBalance, afterStakingBalance); + assertEq(beforeValues.indexerBalance, afterIndexerBalance); + assertEq(beforeValues.beneficiaryBalance, afterBeneficiaryBalance); + } + + assertEq(afterAllocation.indexer, beforeValues.allocation.indexer); + assertEq(afterAllocation.subgraphDeploymentID, beforeValues.allocation.subgraphDeploymentID); + assertEq(afterAllocation.tokens, beforeValues.allocation.tokens); + assertEq(afterAllocation.createdAtEpoch, beforeValues.allocation.createdAtEpoch); + assertEq(afterAllocation.closedAtEpoch, epochManager.currentEpoch()); + assertEq(afterAllocation.collectedFees, beforeValues.allocation.collectedFees); + assertEq( + afterAllocation.__DEPRECATED_effectiveAllocation, + beforeValues.allocation.__DEPRECATED_effectiveAllocation + ); + assertEq(afterAllocation.accRewardsPerAllocatedToken, beforeValues.allocation.accRewardsPerAllocatedToken); + assertEq(afterAllocation.distributedRebates, beforeValues.allocation.distributedRebates); + + if (beforeValues.allocation.tokens > 0 && isAuth && poi != 0 && rewardsDestination == address(0)) { + assertEq( + afterServiceProvider.tokensStaked, + beforeValues.serviceProvider.tokensStaked + calcValues.indexerRewards + ); + } else { + assertEq(afterServiceProvider.tokensStaked, beforeValues.serviceProvider.tokensStaked); + } + assertEq(afterServiceProvider.tokensProvisioned, beforeValues.serviceProvider.tokensProvisioned); + assertEq( + afterServiceProvider.__DEPRECATED_tokensAllocated + beforeValues.allocation.tokens, + beforeValues.serviceProvider.__DEPRECATED_tokensAllocated + ); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLocked, + beforeValues.serviceProvider.__DEPRECATED_tokensLocked + ); + assertEq( + afterServiceProvider.__DEPRECATED_tokensLockedUntil, + beforeValues.serviceProvider.__DEPRECATED_tokensLockedUntil + ); + + assertEq(afterSubgraphAllocations + beforeValues.allocation.tokens, beforeValues.subgraphAllocations); + + if (beforeValues.allocation.tokens > 0 && isAuth && poi != 0 && beforeValues.pool.tokens > 0) { + assertEq(afterPool.tokens, beforeValues.pool.tokens + calcValues.delegatorRewards); + } else { + assertEq(afterPool.tokens, beforeValues.pool.tokens); + } + } + + // use struct to avoid 'stack too deep' error + struct BeforeValues_Collect { + IHorizonStakingExtension.Allocation allocation; + DelegationPoolInternalTest pool; + ServiceProviderInternal serviceProvider; + uint256 stakingBalance; + uint256 senderBalance; + uint256 curationBalance; + uint256 beneficiaryBalance; + } + struct CalcValues_Collect { + uint256 protocolTaxTokens; + uint256 queryFees; + uint256 curationCutTokens; + uint256 newRebates; + uint256 payment; + uint256 delegationFeeCut; + } + struct AfterValues_Collect { + IHorizonStakingExtension.Allocation allocation; + DelegationPoolInternalTest pool; + ServiceProviderInternal serviceProvider; + uint256 stakingBalance; + uint256 senderBalance; + uint256 curationBalance; + uint256 beneficiaryBalance; + } + + function _collect(uint256 tokens, address allocationId) internal { + (, address msgSender, ) = vm.readCallers(); + + // before + BeforeValues_Collect memory beforeValues; + beforeValues.allocation = staking.getAllocation(allocationId); + beforeValues.pool = _getStorage_DelegationPoolInternal( + beforeValues.allocation.indexer, + subgraphDataServiceLegacyAddress, + true + ); + beforeValues.serviceProvider = _getStorage_ServiceProviderInternal(beforeValues.allocation.indexer); + + (uint32 curationPercentage, uint32 protocolPercentage) = _getStorage_ProtocolTaxAndCuration(); + address rewardsDestination = _getStorage_RewardsDestination(beforeValues.allocation.indexer); + + beforeValues.stakingBalance = token.balanceOf(address(staking)); + beforeValues.senderBalance = token.balanceOf(msgSender); + beforeValues.curationBalance = token.balanceOf(address(curation)); + beforeValues.beneficiaryBalance = token.balanceOf(rewardsDestination); + + // calc some stuff + CalcValues_Collect memory calcValues; + calcValues.protocolTaxTokens = tokens.mulPPMRoundUp(protocolPercentage); + calcValues.queryFees = tokens - calcValues.protocolTaxTokens; + calcValues.curationCutTokens = 0; + if (curation.isCurated(beforeValues.allocation.subgraphDeploymentID)) { + calcValues.curationCutTokens = calcValues.queryFees.mulPPMRoundUp(curationPercentage); + calcValues.queryFees -= calcValues.curationCutTokens; + } + calcValues.newRebates = ExponentialRebates.exponentialRebates( + calcValues.queryFees + beforeValues.allocation.collectedFees, + beforeValues.allocation.tokens, + alphaNumerator, + alphaDenominator, + lambdaNumerator, + lambdaDenominator + ); + calcValues.payment = calcValues.newRebates > calcValues.queryFees + ? calcValues.queryFees + : calcValues.newRebates; + calcValues.delegationFeeCut = 0; + if (beforeValues.pool.tokens > 0) { + calcValues.delegationFeeCut = + calcValues.payment - + calcValues.payment.mulPPM(beforeValues.pool.__DEPRECATED_queryFeeCut); + calcValues.payment -= calcValues.delegationFeeCut; + } + + // staking.collect() + if (tokens > 0) { + vm.expectEmit(address(staking)); + emit IHorizonStakingExtension.RebateCollected( + msgSender, + beforeValues.allocation.indexer, + beforeValues.allocation.subgraphDeploymentID, + allocationId, + epochManager.currentEpoch(), + tokens, + calcValues.protocolTaxTokens, + calcValues.curationCutTokens, + calcValues.queryFees, + calcValues.payment, + calcValues.delegationFeeCut + ); + } + staking.collect(tokens, allocationId); + + // after + AfterValues_Collect memory afterValues; + afterValues.allocation = staking.getAllocation(allocationId); + afterValues.pool = _getStorage_DelegationPoolInternal( + beforeValues.allocation.indexer, + subgraphDataServiceLegacyAddress, + true + ); + afterValues.serviceProvider = _getStorage_ServiceProviderInternal(beforeValues.allocation.indexer); + afterValues.stakingBalance = token.balanceOf(address(staking)); + afterValues.senderBalance = token.balanceOf(msgSender); + afterValues.curationBalance = token.balanceOf(address(curation)); + afterValues.beneficiaryBalance = token.balanceOf(rewardsDestination); + + // assert + assertEq(afterValues.senderBalance + tokens, beforeValues.senderBalance); + assertEq(afterValues.curationBalance, beforeValues.curationBalance + calcValues.curationCutTokens); + if (rewardsDestination != address(0)) { + assertEq(afterValues.beneficiaryBalance, beforeValues.beneficiaryBalance + calcValues.payment); + assertEq(afterValues.stakingBalance, beforeValues.stakingBalance + calcValues.delegationFeeCut); + } else { + assertEq(afterValues.beneficiaryBalance, beforeValues.beneficiaryBalance); + assertEq( + afterValues.stakingBalance, + beforeValues.stakingBalance + calcValues.delegationFeeCut + calcValues.payment + ); + } + + assertEq( + afterValues.allocation.collectedFees, + beforeValues.allocation.collectedFees + tokens - calcValues.protocolTaxTokens - calcValues.curationCutTokens + ); + assertEq(afterValues.allocation.indexer, beforeValues.allocation.indexer); + assertEq(afterValues.allocation.subgraphDeploymentID, beforeValues.allocation.subgraphDeploymentID); + assertEq(afterValues.allocation.tokens, beforeValues.allocation.tokens); + assertEq(afterValues.allocation.createdAtEpoch, beforeValues.allocation.createdAtEpoch); + assertEq(afterValues.allocation.closedAtEpoch, beforeValues.allocation.closedAtEpoch); + assertEq( + afterValues.allocation.accRewardsPerAllocatedToken, + beforeValues.allocation.accRewardsPerAllocatedToken + ); + assertEq( + afterValues.allocation.distributedRebates, + beforeValues.allocation.distributedRebates + calcValues.newRebates + ); + + assertEq(afterValues.pool.tokens, beforeValues.pool.tokens + calcValues.delegationFeeCut); + assertEq(afterValues.pool.shares, beforeValues.pool.shares); + assertEq(afterValues.pool.tokensThawing, beforeValues.pool.tokensThawing); + assertEq(afterValues.pool.sharesThawing, beforeValues.pool.sharesThawing); + + assertEq(afterValues.serviceProvider.tokensProvisioned, beforeValues.serviceProvider.tokensProvisioned); + if (rewardsDestination != address(0)) { + assertEq(afterValues.serviceProvider.tokensStaked, beforeValues.serviceProvider.tokensStaked); + } else { + assertEq( + afterValues.serviceProvider.tokensStaked, + beforeValues.serviceProvider.tokensStaked + calcValues.payment + ); + } + } + + /* + * STORAGE HELPERS + */ + function _getStorage_ServiceProviderInternal( + address serviceProvider + ) internal view returns (ServiceProviderInternal memory) { + uint256 slotNumber = 14; + uint256 baseSlotUint = uint256(keccak256(abi.encode(serviceProvider, slotNumber))); + + ServiceProviderInternal memory serviceProviderInternal = ServiceProviderInternal({ + tokensStaked: uint256(vm.load(address(staking), bytes32(baseSlotUint))), + __DEPRECATED_tokensAllocated: uint256(vm.load(address(staking), bytes32(baseSlotUint + 1))), + __DEPRECATED_tokensLocked: uint256(vm.load(address(staking), bytes32(baseSlotUint + 2))), + __DEPRECATED_tokensLockedUntil: uint256(vm.load(address(staking), bytes32(baseSlotUint + 3))), + tokensProvisioned: uint256(vm.load(address(staking), bytes32(baseSlotUint + 4))) + }); + + return serviceProviderInternal; + } + + function _getStorage_OperatorAuth( + address serviceProvider, + address operator, + address verifier, + bool legacy + ) internal view returns (bool) { + uint256 slotNumber = legacy ? 21 : 31; + uint256 slot; + + if (legacy) { + slot = uint256(keccak256(abi.encode(operator, keccak256(abi.encode(serviceProvider, slotNumber))))); + } else { + slot = uint256( + keccak256( + abi.encode( + operator, + keccak256(abi.encode(verifier, keccak256(abi.encode(serviceProvider, slotNumber)))) + ) + ) + ); + } + return vm.load(address(staking), bytes32(slot)) == bytes32(uint256(1)); + } + + function _setStorage_DeprecatedThawingPeriod(uint32 _thawingPeriod) internal { + uint256 slot = 13; + + // Read the current value of the slot + uint256 currentSlotValue = uint256(vm.load(address(staking), bytes32(slot))); + + // Create a mask to clear the bits for __DEPRECATED_thawingPeriod (bits 0-31) + uint256 mask = ~(uint256(0xFFFFFFFF)); // Mask to clear the first 32 bits + + // Clear the bits for __DEPRECATED_thawingPeriod and set the new value + uint256 newSlotValue = (currentSlotValue & mask) | uint256(_thawingPeriod); + + // Store the updated value back into the slot + vm.store(address(staking), bytes32(slot), bytes32(newSlotValue)); + } + + function _setStorage_ServiceProvider( + address _indexer, + uint256 _tokensStaked, + uint256 _tokensAllocated, + uint256 _tokensLocked, + uint256 _tokensLockedUntil, + uint256 _tokensProvisioned + ) internal { + uint256 serviceProviderSlot = 14; + bytes32 serviceProviderBaseSlot = keccak256(abi.encode(_indexer, serviceProviderSlot)); + vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot)), bytes32(_tokensStaked)); + vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 1), bytes32(_tokensAllocated)); + vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 2), bytes32(_tokensLocked)); + vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 3), bytes32(_tokensLockedUntil)); + vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 4), bytes32(_tokensProvisioned)); + } + + // DelegationPoolInternal contains a mapping, solidity doesn't allow constructing structs with + // nested mappings on memory: "Struct containing a (nested) mapping cannot be constructed" + // So we use a custom struct here and remove the nested mapping which we don't need anyways + struct DelegationPoolInternalTest { + // (Deprecated) Time, in blocks, an indexer must wait before updating delegation parameters + uint32 __DEPRECATED_cooldownBlocks; + // (Deprecated) Percentage of indexing rewards for the service provider, in PPM + uint32 __DEPRECATED_indexingRewardCut; + // (Deprecated) Percentage of query fees for the service provider, in PPM + uint32 __DEPRECATED_queryFeeCut; + // (Deprecated) Block when the delegation parameters were last updated + uint256 __DEPRECATED_updatedAtBlock; + // Total tokens as pool reserves + uint256 tokens; + // Total shares minted in the pool + uint256 shares; + // Delegation details by delegator + uint256 _gap_delegators_mapping; + // Tokens thawing in the pool + uint256 tokensThawing; + // Shares representing the thawing tokens + uint256 sharesThawing; + } + + function _getStorage_DelegationPoolInternal( + address serviceProvider, + address verifier, + bool legacy + ) internal view returns (DelegationPoolInternalTest memory) { + uint256 slotNumber = legacy ? 20 : 33; + uint256 baseSlot; + if (legacy) { + baseSlot = uint256(keccak256(abi.encode(serviceProvider, slotNumber))); + } else { + baseSlot = uint256(keccak256(abi.encode(verifier, keccak256(abi.encode(serviceProvider, slotNumber))))); + } + + uint256 packedData = uint256(vm.load(address(staking), bytes32(baseSlot))); + + DelegationPoolInternalTest memory delegationPoolInternal = DelegationPoolInternalTest({ + __DEPRECATED_cooldownBlocks: uint32(packedData & 0xFFFFFFFF), + __DEPRECATED_indexingRewardCut: uint32((packedData >> 32) & 0xFFFFFFFF), + __DEPRECATED_queryFeeCut: uint32((packedData >> 64) & 0xFFFFFFFF), + __DEPRECATED_updatedAtBlock: uint256(vm.load(address(staking), bytes32(baseSlot + 1))), + tokens: uint256(vm.load(address(staking), bytes32(baseSlot + 2))), + shares: uint256(vm.load(address(staking), bytes32(baseSlot + 3))), + _gap_delegators_mapping: uint256(vm.load(address(staking), bytes32(baseSlot + 4))), + tokensThawing: uint256(vm.load(address(staking), bytes32(baseSlot + 5))), + sharesThawing: uint256(vm.load(address(staking), bytes32(baseSlot + 6))) + }); + + return delegationPoolInternal; + } + + function _getStorage_Delegation( + address serviceProvider, + address verifier, + address delegator, + bool legacy + ) internal view returns (DelegationInternal memory) { + uint256 slotNumber = legacy ? 20 : 33; + uint256 baseSlot; + + // DelegationPool + if (legacy) { + baseSlot = uint256(keccak256(abi.encode(serviceProvider, slotNumber))); + } else { + baseSlot = uint256(keccak256(abi.encode(verifier, keccak256(abi.encode(serviceProvider, slotNumber))))); + } + + // delegators slot in DelegationPool + baseSlot += 4; + + // Delegation + baseSlot = uint256(keccak256(abi.encode(delegator, baseSlot))); + + DelegationInternal memory delegation = DelegationInternal({ + shares: uint256(vm.load(address(staking), bytes32(baseSlot))), + __DEPRECATED_tokensLocked: uint256(vm.load(address(staking), bytes32(baseSlot + 1))), + __DEPRECATED_tokensLockedUntil: uint256(vm.load(address(staking), bytes32(baseSlot + 2))) + }); + + 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, + uint256 tokens + ) internal { + // __DEPRECATED_allocations + uint256 allocationsSlot = 15; + bytes32 allocationBaseSlot = keccak256(abi.encode(allocationId, allocationsSlot)); + vm.store(address(staking), allocationBaseSlot, bytes32(uint256(uint160(allocation.indexer)))); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 1), allocation.subgraphDeploymentID); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 2), bytes32(tokens)); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 3), bytes32(allocation.createdAtEpoch)); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 4), bytes32(allocation.closedAtEpoch)); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 5), bytes32(allocation.collectedFees)); + vm.store( + address(staking), + bytes32(uint256(allocationBaseSlot) + 6), + bytes32(allocation.__DEPRECATED_effectiveAllocation) + ); + vm.store( + address(staking), + bytes32(uint256(allocationBaseSlot) + 7), + bytes32(allocation.accRewardsPerAllocatedToken) + ); + vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 8), bytes32(allocation.distributedRebates)); + + // _serviceProviders + uint256 serviceProviderSlot = 14; + bytes32 serviceProviderBaseSlot = keccak256(abi.encode(allocation.indexer, serviceProviderSlot)); + uint256 currentTokensStaked = uint256(vm.load(address(staking), serviceProviderBaseSlot)); + uint256 currentTokensProvisioned = uint256( + vm.load(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 1)) + ); + vm.store( + address(staking), + bytes32(uint256(serviceProviderBaseSlot) + 0), + bytes32(currentTokensStaked + tokens) + ); + vm.store( + address(staking), + bytes32(uint256(serviceProviderBaseSlot) + 1), + bytes32(currentTokensProvisioned + tokens) + ); + + // __DEPRECATED_subgraphAllocations + uint256 subgraphsAllocationsSlot = 16; + bytes32 subgraphAllocationsBaseSlot = keccak256( + abi.encode(allocation.subgraphDeploymentID, subgraphsAllocationsSlot) + ); + uint256 currentAllocatedTokens = uint256(vm.load(address(staking), subgraphAllocationsBaseSlot)); + vm.store(address(staking), subgraphAllocationsBaseSlot, bytes32(currentAllocatedTokens + tokens)); + } + + function _getStorage_SubgraphAllocations(bytes32 subgraphDeploymentID) internal view returns (uint256) { + uint256 subgraphsAllocationsSlot = 16; + bytes32 subgraphAllocationsBaseSlot = keccak256(abi.encode(subgraphDeploymentID, subgraphsAllocationsSlot)); + return uint256(vm.load(address(staking), subgraphAllocationsBaseSlot)); + } + + function _setStorage_RewardsDestination(address serviceProvider, address destination) internal { + uint256 rewardsDestinationSlot = 23; + bytes32 rewardsDestinationSlotBaseSlot = keccak256(abi.encode(serviceProvider, rewardsDestinationSlot)); + vm.store(address(staking), rewardsDestinationSlotBaseSlot, bytes32(uint256(uint160(destination)))); + } + + function _getStorage_RewardsDestination(address serviceProvider) internal view returns (address) { + uint256 rewardsDestinationSlot = 23; + bytes32 rewardsDestinationSlotBaseSlot = keccak256(abi.encode(serviceProvider, rewardsDestinationSlot)); + return address(uint160(uint256(vm.load(address(staking), rewardsDestinationSlotBaseSlot)))); + } + + function _setStorage_MaxAllocationEpochs(uint256 maxAllocationEpochs) internal { + uint256 slot = 13; + + // Read the current value of the storage slot + uint256 currentSlotValue = uint256(vm.load(address(staking), bytes32(slot))); + + // Mask to clear the specific bits for __DEPRECATED_maxAllocationEpochs (bits 128-159) + uint256 mask = ~(uint256(0xFFFFFFFF) << 128); + + // Clear the bits and set the new maxAllocationEpochs value + uint256 newSlotValue = (currentSlotValue & mask) | (uint256(maxAllocationEpochs) << 128); + + // Store the updated value back into the slot + vm.store(address(staking), bytes32(slot), bytes32(newSlotValue)); + + uint256 readMaxAllocationEpochs = _getStorage_MaxAllocationEpochs(); + assertEq(readMaxAllocationEpochs, maxAllocationEpochs); + } + + function _getStorage_MaxAllocationEpochs() internal view returns (uint256) { + uint256 slot = 13; + + // Read the current value of the storage slot + uint256 currentSlotValue = uint256(vm.load(address(staking), bytes32(slot))); + + // Mask to isolate bits 128-159 + uint256 mask = uint256(0xFFFFFFFF) << 128; + + // Extract the maxAllocationEpochs by masking and shifting + uint256 maxAllocationEpochs = (currentSlotValue & mask) >> 128; + + return maxAllocationEpochs; + } + + function _setStorage_DelegationPool( + address serviceProvider, + uint256 tokens, + uint32 indexingRewardCut, + uint32 queryFeeCut + ) internal { + bytes32 baseSlot = keccak256(abi.encode(serviceProvider, uint256(20))); + bytes32 feeCutValues = bytes32( + (uint256(indexingRewardCut) << uint256(32)) | (uint256(queryFeeCut) << uint256(64)) + ); + bytes32 tokensSlot = bytes32(uint256(baseSlot) + 2); + vm.store(address(staking), baseSlot, feeCutValues); + vm.store(address(staking), tokensSlot, bytes32(tokens)); + } + + function _setStorage_RebateParameters( + uint32 alphaNumerator_, + uint32 alphaDenominator_, + uint32 lambdaNumerator_, + uint32 lambdaDenominator_ + ) internal { + // Store alpha numerator and denominator in slot 13 + uint256 alphaSlot = 13; + + uint256 newAlphaSlotValue; + { + uint256 alphaNumeratorOffset = 160; // Offset for __DEPRECATED_alphaNumerator (20th byte) + uint256 alphaDenominatorOffset = 192; // Offset for __DEPRECATED_alphaDenominator (24th byte) + + // Read current value of the slot + uint256 currentAlphaSlotValue = uint256(vm.load(address(staking), bytes32(alphaSlot))); + + // Create a mask to clear the bits for alphaNumerator and alphaDenominator + uint256 alphaMask = ~(uint256(0xFFFFFFFF) << alphaNumeratorOffset) & + ~(uint256(0xFFFFFFFF) << alphaDenominatorOffset); + + // Clear and set new values + newAlphaSlotValue = + (currentAlphaSlotValue & alphaMask) | + (uint256(alphaNumerator_) << alphaNumeratorOffset) | + (uint256(alphaDenominator_) << alphaDenominatorOffset); + } + + // Store the updated value back into the slot + vm.store(address(staking), bytes32(alphaSlot), bytes32(newAlphaSlotValue)); + + // Store lambda numerator and denominator in slot 25 + uint256 lambdaSlot = 25; + + uint256 newLambdaSlotValue; + { + uint256 lambdaNumeratorOffset = 160; // Offset for lambdaNumerator (20th byte) + uint256 lambdaDenominatorOffset = 192; // Offset for lambdaDenominator (24th byte) + + // Read current value of the slot + uint256 currentLambdaSlotValue = uint256(vm.load(address(staking), bytes32(lambdaSlot))); + + // Create a mask to clear the bits for lambdaNumerator and lambdaDenominator + uint256 lambdaMask = ~(uint256(0xFFFFFFFF) << lambdaNumeratorOffset) & + ~(uint256(0xFFFFFFFF) << lambdaDenominatorOffset); + + // Clear and set new values + newLambdaSlotValue = + (currentLambdaSlotValue & lambdaMask) | + (uint256(lambdaNumerator_) << lambdaNumeratorOffset) | + (uint256(lambdaDenominator_) << lambdaDenominatorOffset); + } + + // Store the updated value back into the slot + vm.store(address(staking), bytes32(lambdaSlot), bytes32(newLambdaSlotValue)); + + // Verify the storage + ( + uint32 readAlphaNumerator, + uint32 readAlphaDenominator, + uint32 readLambdaNumerator, + uint32 readLambdaDenominator + ) = _getStorage_RebateParameters(); + assertEq(readAlphaNumerator, alphaNumerator_); + assertEq(readAlphaDenominator, alphaDenominator_); + assertEq(readLambdaNumerator, lambdaNumerator_); + assertEq(readLambdaDenominator, lambdaDenominator_); + } + + function _getStorage_RebateParameters() internal view returns (uint32, uint32, uint32, uint32) { + // Read alpha numerator and denominator + uint256 alphaSlot = 13; + uint256 alphaValues = uint256(vm.load(address(staking), bytes32(alphaSlot))); + uint32 alphaNumerator_ = uint32(alphaValues >> 160); + uint32 alphaDenominator_ = uint32(alphaValues >> 192); + + // Read lambda numerator and denominator + uint256 lambdaSlot = 25; + uint256 lambdaValues = uint256(vm.load(address(staking), bytes32(lambdaSlot))); + uint32 lambdaNumerator_ = uint32(lambdaValues >> 160); + uint32 lambdaDenominator_ = uint32(lambdaValues >> 192); + + return (alphaNumerator_, alphaDenominator_, lambdaNumerator_, lambdaDenominator_); + } + + // function _setStorage_ProtocolTaxAndCuration(uint32 curationPercentage, uint32 taxPercentage) private { + // bytes32 slot = bytes32(uint256(13)); + // uint256 curationOffset = 4; + // uint256 protocolTaxOffset = 8; + // bytes32 originalValue = vm.load(address(staking), slot); + + // bytes32 newProtocolTaxValue = bytes32( + // ((uint256(originalValue) & + // ~((0xFFFFFFFF << (8 * curationOffset)) | (0xFFFFFFFF << (8 * protocolTaxOffset)))) | + // (uint256(curationPercentage) << (8 * curationOffset))) | + // (uint256(taxPercentage) << (8 * protocolTaxOffset)) + // ); + // vm.store(address(staking), slot, newProtocolTaxValue); + + // (uint32 readCurationPercentage, uint32 readTaxPercentage) = _getStorage_ProtocolTaxAndCuration(); + // assertEq(readCurationPercentage, curationPercentage); + // } + + function _setStorage_ProtocolTaxAndCuration(uint32 curationPercentage, uint32 taxPercentage) internal { + bytes32 slot = bytes32(uint256(13)); + + // Offsets for the percentages + uint256 curationOffset = 32; // __DEPRECATED_curationPercentage (2nd uint32, bits 32-63) + uint256 protocolTaxOffset = 64; // __DEPRECATED_protocolPercentage (3rd uint32, bits 64-95) + + // Read the current slot value + uint256 originalValue = uint256(vm.load(address(staking), slot)); + + // Create masks to clear the specific bits for the two percentages + uint256 mask = ~(uint256(0xFFFFFFFF) << curationOffset) & ~(uint256(0xFFFFFFFF) << protocolTaxOffset); // Mask for curationPercentage // Mask for protocolTax + + // Clear the existing bits and set the new values + uint256 newSlotValue = (originalValue & mask) | + (uint256(curationPercentage) << curationOffset) | + (uint256(taxPercentage) << protocolTaxOffset); + + // Store the updated slot value + vm.store(address(staking), slot, bytes32(newSlotValue)); + + // Verify the values were set correctly + (uint32 readCurationPercentage, uint32 readTaxPercentage) = _getStorage_ProtocolTaxAndCuration(); + assertEq(readCurationPercentage, curationPercentage); + assertEq(readTaxPercentage, taxPercentage); + } + + function _getStorage_ProtocolTaxAndCuration() internal view returns (uint32, uint32) { + bytes32 slot = bytes32(uint256(13)); + bytes32 value = vm.load(address(staking), slot); + uint32 curationPercentage = uint32(uint256(value) >> 32); + uint32 taxPercentage = uint32(uint256(value) >> 64); + return (curationPercentage, taxPercentage); + } + + /* + * MISC: private functions to help with testing + */ + // use struct to avoid 'stack too deep' error + struct CalcValues_ThawRequestData { + uint256 tokensThawed; + uint256 tokensThawing; + uint256 sharesThawing; + ThawRequest[] thawRequestsFulfilledList; + bytes32[] thawRequestsFulfilledListIds; + uint256[] thawRequestsFulfilledListTokens; + } + + function calcThawRequestData( + address serviceProvider, + address verifier, + address owner, + uint256 iterations, + bool delegation + ) private view returns (CalcValues_ThawRequestData memory) { + LinkedList.List memory thawRequestList = staking.getThawRequestList(serviceProvider, verifier, owner); + if (thawRequestList.count == 0) { + return CalcValues_ThawRequestData(0, 0, 0, new ThawRequest[](0), new bytes32[](0), new uint256[](0)); + } + + Provision memory prov = staking.getProvision(serviceProvider, verifier); + DelegationPool memory pool = staking.getDelegationPool(serviceProvider, verifier); + + uint256 tokensThawed = 0; + uint256 tokensThawing = delegation ? pool.tokensThawing : prov.tokensThawing; + uint256 sharesThawing = delegation ? pool.sharesThawing : prov.sharesThawing; + uint256 thawRequestsFulfilled = 0; + + bytes32 thawRequestId = thawRequestList.head; + while (thawRequestId != bytes32(0) && (iterations == 0 || thawRequestsFulfilled < iterations)) { + ThawRequest memory thawRequest = staking.getThawRequest(thawRequestId); + if (thawRequest.thawingUntil <= block.timestamp) { + thawRequestsFulfilled++; + uint256 tokens = delegation + ? (thawRequest.shares * pool.tokensThawing) / pool.sharesThawing + : (thawRequest.shares * prov.tokensThawing) / prov.sharesThawing; + tokensThawed += tokens; + tokensThawing -= tokens; + sharesThawing -= thawRequest.shares; + } else { + break; + } + thawRequestId = thawRequest.next; + } + + // we need to do a second pass because solidity doesnt allow dynamic arrays on memory + ThawRequest[] memory thawRequestsFulfilledList = new ThawRequest[](thawRequestsFulfilled); + bytes32[] memory thawRequestsFulfilledListIds = new bytes32[](thawRequestsFulfilled); + uint256[] memory thawRequestsFulfilledListTokens = new uint256[](thawRequestsFulfilled); + uint256 i = 0; + thawRequestId = thawRequestList.head; + while (thawRequestId != bytes32(0) && (iterations == 0 || i < iterations)) { + ThawRequest memory thawRequest = staking.getThawRequest(thawRequestId); + if (thawRequest.thawingUntil <= block.timestamp) { + uint256 tokens = delegation + ? (thawRequest.shares * pool.tokensThawing) / pool.sharesThawing + : (thawRequest.shares * prov.tokensThawing) / prov.sharesThawing; + thawRequestsFulfilledListTokens[i] = tokens; + thawRequestsFulfilledListIds[i] = thawRequestId; + thawRequestsFulfilledList[i] = staking.getThawRequest(thawRequestId); + thawRequestId = thawRequestsFulfilledList[i].next; + i++; + } else { + break; + } + thawRequestId = thawRequest.next; + } + + assertEq(thawRequestsFulfilled, thawRequestsFulfilledList.length); + assertEq(thawRequestsFulfilled, thawRequestsFulfilledListIds.length); + assertEq(thawRequestsFulfilled, thawRequestsFulfilledListTokens.length); + + return + CalcValues_ThawRequestData( + tokensThawed, + tokensThawing, + sharesThawing, + thawRequestsFulfilledList, + thawRequestsFulfilledListIds, + thawRequestsFulfilledListTokens + ); } } diff --git a/packages/horizon/test/staking/HorizonStaking.t.sol b/packages/horizon/test/staking/HorizonStaking.t.sol index 1973f8581..d846d1754 100644 --- a/packages/horizon/test/staking/HorizonStaking.t.sol +++ b/packages/horizon/test/staking/HorizonStaking.t.sol @@ -1,17 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; -import { IHorizonStakingMain } from "../../contracts/interfaces/internal/IHorizonStakingMain.sol"; -import { IHorizonStakingTypes } from "../../contracts/interfaces/internal/IHorizonStakingTypes.sol"; -import { LinkedList } from "../../contracts/libraries/LinkedList.sol"; -import { MathUtils } from "../../contracts/libraries/MathUtils.sol"; - import { HorizonStakingSharedTest } from "../shared/horizon-staking/HorizonStakingShared.t.sol"; -contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { +contract HorizonStakingTest is HorizonStakingSharedTest { using stdStorage for StdStorage; /* @@ -25,38 +20,11 @@ contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { _; } - modifier useOperator() { - vm.startPrank(users.indexer); - staking.setOperator(users.operator, subgraphDataServiceAddress, true); - vm.startPrank(users.operator); - _; - vm.stopPrank(); - } - - modifier useStake(uint256 amount) { - vm.assume(amount > 0); - approve(address(staking), amount); - staking.stake(amount); - _; - } - - modifier useStakeTo(address to, uint256 amount) { - vm.assume(amount > 0); - _stakeTo(to, amount); - _; - } - - modifier useThawRequest(uint256 thawAmount) { - vm.assume(thawAmount > 0); - _createThawRequest(thawAmount); - _; - } - modifier useThawAndDeprovision(uint256 amount, uint64 thawingPeriod) { vm.assume(amount > 0); - _createThawRequest(amount); + _thaw(users.indexer, subgraphDataServiceAddress, amount); skip(thawingPeriod + 1); - _deprovision(0); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); _; } @@ -75,7 +43,7 @@ contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { address msgSender; (, msgSender, ) = vm.readCallers(); resetPrank(users.governor); - staking.setAllowedLockedVerifier(verifier, true); + _setAllowedLockedVerifier(verifier, true); resetPrank(msgSender); _; } @@ -88,270 +56,4 @@ contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { resetPrank(msgSender); _; } - - /* - * HELPERS - */ - - function _stakeTo(address to, uint256 amount) internal { - approve(address(staking), amount); - staking.stakeTo(to, amount); - } - - function _createThawRequest(uint256 thawAmount) internal returns (bytes32) { - return staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); - } - - function _deprovision(uint256 nThawRequests) internal { - staking.deprovision(users.indexer, subgraphDataServiceAddress, nThawRequests); - } - - function _delegate(address serviceProvider, address verifier, uint256 tokens, uint256 minSharesOut) internal { - __delegate(serviceProvider, verifier, tokens, minSharesOut, false); - } - - function _delegateLegacy(address serviceProvider, uint256 tokens) internal { - __delegate(serviceProvider, subgraphDataServiceLegacyAddress, tokens, 0, true); - } - - - struct DelegateData { - DelegationPool pool; - Delegation delegation; - uint256 storagePoolTokens; - uint256 delegatedTokens; - uint256 delegatorBalance; - uint256 stakingBalance; - } - - function __delegate( - address serviceProvider, - address verifier, - uint256 tokens, - uint256 minSharesOut, - bool legacy - ) internal { - (, address delegator, ) = vm.readCallers(); - - // before - DelegateData memory beforeData = DelegateData({ - pool: staking.getDelegationPool(serviceProvider, verifier), - delegation: staking.getDelegation(serviceProvider, verifier, delegator), - storagePoolTokens: uint256(vm.load(address(staking), _getSlotPoolTokens(serviceProvider, verifier, legacy))), - delegatedTokens: staking.getDelegatedTokensAvailable(serviceProvider, verifier), - delegatorBalance: token.balanceOf(delegator), - stakingBalance: token.balanceOf(address(staking)) - }); - - uint256 calcShares = (beforeData.pool.tokens == 0 || beforeData.pool.tokens == beforeData.pool.tokensThawing) - ? tokens - : ((tokens * beforeData.pool.shares) / (beforeData.pool.tokens - beforeData.pool.tokensThawing)); - - // delegate - token.approve(address(staking), tokens); - vm.expectEmit(); - emit IHorizonStakingMain.TokensDelegated(serviceProvider, verifier, delegator, tokens); - if (legacy) { - staking.delegate(serviceProvider, tokens); - } else { - staking.delegate(serviceProvider, verifier, tokens, minSharesOut); - } - - // after - DelegateData memory afterData = DelegateData({ - pool: staking.getDelegationPool(serviceProvider, verifier), - delegation: staking.getDelegation(serviceProvider, verifier, delegator), - storagePoolTokens: uint256(vm.load(address(staking), _getSlotPoolTokens(serviceProvider, verifier, legacy))), - delegatedTokens: staking.getDelegatedTokensAvailable(serviceProvider, verifier), - delegatorBalance: token.balanceOf(delegator), - stakingBalance: token.balanceOf(address(staking)) - }); - - uint256 deltaShares = afterData.delegation.shares - beforeData.delegation.shares; - - // assertions - assertEq(beforeData.pool.tokens + tokens, afterData.pool.tokens); - assertEq(beforeData.pool.shares + calcShares, afterData.pool.shares); - assertEq(beforeData.pool.tokensThawing, afterData.pool.tokensThawing); - assertEq(beforeData.pool.sharesThawing, afterData.pool.sharesThawing); - assertGe(deltaShares, minSharesOut); - assertEq(calcShares, deltaShares); - assertEq(beforeData.delegatedTokens + tokens, afterData.delegatedTokens); - // Ensure correct slot is being updated, pools are stored in different storage locations for legacy subgraph data service - assertEq(beforeData.storagePoolTokens + tokens, afterData.storagePoolTokens); - assertEq(beforeData.delegatorBalance - tokens, afterData.delegatorBalance); - assertEq(beforeData.stakingBalance + tokens, afterData.stakingBalance); - } - - function _undelegate(address serviceProvider, address verifier, uint256 shares) internal { - __undelegate(serviceProvider, verifier, shares, false); - } - - function _undelegateLegacy(address serviceProvider, uint256 shares) internal { - __undelegate(serviceProvider, subgraphDataServiceLegacyAddress, shares, true); - } - - function __undelegate(address serviceProvider, address verifier, uint256 shares, bool legacy) internal { - (, address delegator, ) = vm.readCallers(); - - // Delegation pool data is stored in a different storage slot for the legacy subgraph data service - bytes32 slotPoolShares; - if (legacy) { - slotPoolShares = bytes32(uint256(keccak256(abi.encode(serviceProvider, 20))) + 3); - } else { - slotPoolShares = bytes32( - uint256(keccak256(abi.encode(verifier, keccak256(abi.encode(serviceProvider, 33))))) + 3 - ); - } - - // before - DelegationPool memory beforePool = staking.getDelegationPool(serviceProvider, verifier); - Delegation memory beforeDelegation = staking.getDelegation(serviceProvider, verifier, delegator); - LinkedList.List memory beforeThawRequestList = staking.getThawRequestList(serviceProvider, verifier, delegator); - uint256 beforeStoragePoolShares = uint256(vm.load(address(staking), slotPoolShares)); - uint256 beforeDelegatedTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); - - uint256 calcTokens = ((beforePool.tokens - beforePool.tokensThawing) * shares) / beforePool.shares; - uint256 calcThawingShares = beforePool.tokensThawing == 0 - ? calcTokens - : (beforePool.sharesThawing * calcTokens) / beforePool.tokensThawing; - uint64 calcThawingUntil = staking.getProvision(serviceProvider, verifier).thawingPeriod + - uint64(block.timestamp); - bytes32 calcThawRequestId = keccak256( - abi.encodePacked(serviceProvider, verifier, delegator, beforeThawRequestList.nonce) - ); - - // undelegate - vm.expectEmit(); - emit IHorizonStakingMain.ThawRequestCreated( - serviceProvider, - verifier, - delegator, - calcThawingShares, - calcThawingUntil, - calcThawRequestId - ); - vm.expectEmit(); - emit IHorizonStakingMain.TokensUndelegated(serviceProvider, verifier, delegator, calcTokens); - if (legacy) { - staking.undelegate(serviceProvider, shares); - } else { - staking.undelegate(serviceProvider, verifier, shares); - } - - // after - DelegationPool memory afterPool = staking.getDelegationPool(users.indexer, verifier); - Delegation memory afterDelegation = staking.getDelegation(serviceProvider, verifier, delegator); - LinkedList.List memory afterThawRequestList = staking.getThawRequestList(serviceProvider, verifier, delegator); - ThawRequest memory afterThawRequest = staking.getThawRequest(calcThawRequestId); - uint256 afterStoragePoolShares = uint256(vm.load(address(staking), slotPoolShares)); - uint256 afterDelegatedTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); - - // assertions - assertEq(beforePool.shares, afterPool.shares + shares); - assertEq(beforePool.tokens, afterPool.tokens); - assertEq(beforePool.tokensThawing + calcTokens, afterPool.tokensThawing); - assertEq(beforePool.sharesThawing + calcThawingShares, afterPool.sharesThawing); - assertEq(beforeDelegation.shares - shares, afterDelegation.shares); - assertEq(afterThawRequest.shares, calcThawingShares); - assertEq(afterThawRequest.thawingUntil, calcThawingUntil); - assertEq(afterThawRequest.next, bytes32(0)); - assertEq(calcThawRequestId, afterThawRequestList.tail); - assertEq(beforeThawRequestList.nonce + 1, afterThawRequestList.nonce); - assertEq(beforeThawRequestList.count + 1, afterThawRequestList.count); - assertEq(afterDelegatedTokens + calcTokens, beforeDelegatedTokens); - // Ensure correct slot is being updated, pools are stored in different storage locations for legacy subgraph data service - assertEq(beforeStoragePoolShares, afterStoragePoolShares + shares); - } - - // todo remove these - function _getDelegation(address verifier) internal view returns (Delegation memory) { - return staking.getDelegation(users.indexer, verifier, users.delegator); - } - - function _getDelegationPool(address verifier) internal view returns (DelegationPool memory) { - return staking.getDelegationPool(users.indexer, verifier); - } - - function _storeServiceProvider( - address _indexer, - uint256 _tokensStaked, - uint256 _tokensAllocated, - uint256 _tokensLocked, - uint256 _tokensLockedUntil, - uint256 _tokensProvisioned - ) internal { - uint256 serviceProviderSlot = 14; - bytes32 serviceProviderBaseSlot = keccak256(abi.encode(_indexer, serviceProviderSlot)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot)), bytes32(_tokensStaked)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 1), bytes32(_tokensAllocated)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 2), bytes32(_tokensLocked)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 3), bytes32(_tokensLockedUntil)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 4), bytes32(_tokensProvisioned)); - } - - function _slash(address serviceProvider, address verifier, uint256 tokens, uint256 verifierCutAmount) internal { - uint256 beforeProviderTokens = staking.getProviderTokensAvailable(serviceProvider, verifier); - uint256 beforeDelegationTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); - bool isDelegationSlashingEnabled = staking.isDelegationSlashingEnabled(); - - // Calculate expected tokens after slashing - uint256 providerTokensSlashed = MathUtils.min(beforeProviderTokens, tokens); - uint256 expectedProviderTokensAfterSlashing = beforeProviderTokens - providerTokensSlashed; - - uint256 delegationTokensSlashed = MathUtils.min(beforeDelegationTokens, tokens - providerTokensSlashed); - uint256 expectedDelegationTokensAfterSlashing = beforeDelegationTokens - - (isDelegationSlashingEnabled ? delegationTokensSlashed : 0); - - vm.expectEmit(address(staking)); - if (verifierCutAmount > 0) { - emit IHorizonStakingMain.VerifierTokensSent( - serviceProvider, - verifier, - verifier, - verifierCutAmount - ); - } - emit IHorizonStakingMain.ProvisionSlashed(serviceProvider, verifier, providerTokensSlashed); - - if (isDelegationSlashingEnabled) { - emit IHorizonStakingMain.DelegationSlashed( - serviceProvider, - verifier, - delegationTokensSlashed - ); - } else { - emit IHorizonStakingMain.DelegationSlashingSkipped( - serviceProvider, - verifier, - delegationTokensSlashed - ); - } - staking.slash(serviceProvider, tokens, verifierCutAmount, verifier); - - if (!isDelegationSlashingEnabled) { - expectedDelegationTokensAfterSlashing = beforeDelegationTokens; - } - - uint256 provisionTokens = staking.getProviderTokensAvailable(serviceProvider, verifier); - assertEq(provisionTokens, expectedProviderTokensAfterSlashing); - - uint256 delegationTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); - assertEq(delegationTokens, expectedDelegationTokensAfterSlashing); - - uint256 verifierTokens = token.balanceOf(verifier); - assertEq(verifierTokens, verifierCutAmount); - } - - function _getSlotPoolTokens(address serviceProvider, address verifier, bool legacy) private returns (bytes32) { - bytes32 slotPoolTokens; - if (legacy) { - slotPoolTokens = bytes32(uint256(keccak256(abi.encode(serviceProvider, 20))) + 2); - } else { - slotPoolTokens = bytes32( - uint256(keccak256(abi.encode(verifier, keccak256(abi.encode(serviceProvider, 33))))) + 2 - ); - } - return slotPoolTokens; - } } diff --git a/packages/horizon/test/staking/allocation/HorizonStakingExtension.t.sol b/packages/horizon/test/staking/allocation/HorizonStakingExtension.t.sol deleted file mode 100644 index bf9524c43..000000000 --- a/packages/horizon/test/staking/allocation/HorizonStakingExtension.t.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -import "forge-std/Test.sol"; - -import { HorizonStakingTest } from "../HorizonStaking.t.sol"; -import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; - -contract HorizonStakingExtensionTest is HorizonStakingTest { - - /* - * VARIABLES - */ - - address internal _allocationId = makeAddr("allocationId"); - bytes32 internal constant _subgraphDeploymentID = keccak256("subgraphDeploymentID"); - bytes32 internal constant _poi = keccak256("poi"); - uint256 internal constant MAX_ALLOCATION_EPOCHS = 28; - IHorizonStakingExtension.Allocation internal _allocation; - - /* - * MODIFIERS - */ - - modifier useAllocation() { - _storeAllocation(0); - _; - } - - /* - * SET UP - */ - - function setUp() public virtual override { - super.setUp(); - - _allocation = IHorizonStakingExtension.Allocation({ - indexer: users.indexer, - subgraphDeploymentID: _subgraphDeploymentID, - tokens: 0, - createdAtEpoch: block.timestamp, - closedAtEpoch: 0, - collectedFees: 0, - __DEPRECATED_effectiveAllocation: 0, - accRewardsPerAllocatedToken: 0, - distributedRebates: 0 - }); - } - - function _storeDelegationPool(uint256 tokens, uint32 indexingRewardCut, uint32 queryFeeCut) internal { - bytes32 baseSlot = keccak256(abi.encode(users.indexer, uint256(20))); - bytes32 feeCutValues = bytes32( - (uint256(indexingRewardCut) << uint256(32)) | - (uint256(queryFeeCut) << uint256(64)) - ); - bytes32 tokensSlot = bytes32(uint256(baseSlot) + 2); - vm.store(address(staking), baseSlot, feeCutValues); - vm.store(address(staking), tokensSlot, bytes32(tokens)); - } - - /* - * HELPERS - */ - - function _storeAllocation(uint256 tokens) internal { - uint256 allocationsSlot = 15; - bytes32 allocationBaseSlot = keccak256(abi.encode(_allocationId, allocationsSlot)); - vm.store(address(staking), allocationBaseSlot, bytes32(uint256(uint160(_allocation.indexer)))); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 1), _allocation.subgraphDeploymentID); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 2), bytes32(tokens)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 3), bytes32(_allocation.createdAtEpoch)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 4), bytes32(_allocation.closedAtEpoch)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 5), bytes32(_allocation.collectedFees)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 6), bytes32(_allocation.__DEPRECATED_effectiveAllocation)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 7), bytes32(_allocation.accRewardsPerAllocatedToken)); - vm.store(address(staking), bytes32(uint256(allocationBaseSlot) + 8), bytes32(_allocation.distributedRebates)); - - uint256 serviceProviderSlot = 14; - bytes32 serviceProviderBaseSlot = keccak256(abi.encode(_allocation.indexer, serviceProviderSlot)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 0), bytes32(tokens)); - vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 1), bytes32(tokens)); - - uint256 subgraphsAllocationsSlot = 16; - bytes32 subgraphAllocationsBaseSlot = keccak256(abi.encode(_allocation.subgraphDeploymentID, subgraphsAllocationsSlot)); - vm.store(address(staking), subgraphAllocationsBaseSlot, bytes32(tokens)); - } - - function _storeMaxAllocationEpochs() internal { - uint256 slot = 13; - vm.store(address(staking), bytes32(slot), bytes32(MAX_ALLOCATION_EPOCHS) << 128); - } - - function _storeRewardsDestination(address destination) internal { - uint256 rewardsDestinationSlot = 23; - bytes32 rewardsDestinationSlotBaseSlot = keccak256(abi.encode(users.indexer, rewardsDestinationSlot)); - vm.store(address(staking), rewardsDestinationSlotBaseSlot, bytes32(uint256(uint160(destination)))); - } -} \ No newline at end of file diff --git a/packages/horizon/test/staking/allocation/allocation.t.sol b/packages/horizon/test/staking/allocation/allocation.t.sol index 83e23a81f..9e80433b3 100644 --- a/packages/horizon/test/staking/allocation/allocation.t.sol +++ b/packages/horizon/test/staking/allocation/allocation.t.sol @@ -1,41 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; -import { HorizonStakingExtensionTest } from "./HorizonStakingExtension.t.sol"; +import { HorizonStakingTest } from "../HorizonStaking.t.sol"; import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; -contract HorizonStakingAllocationTest is HorizonStakingExtensionTest { +contract HorizonStakingAllocationTest is HorizonStakingTest { /* * TESTS */ - function testAllocation_GetAllocation() public useAllocation { - IHorizonStakingExtension.Allocation memory allocation = staking.getAllocation(_allocationId); - assertEq(allocation.indexer, _allocation.indexer); - assertEq(allocation.subgraphDeploymentID, _allocation.subgraphDeploymentID); - assertEq(allocation.tokens, _allocation.tokens); - assertEq(allocation.createdAtEpoch, _allocation.createdAtEpoch); - assertEq(allocation.closedAtEpoch, _allocation.closedAtEpoch); - assertEq(allocation.collectedFees, _allocation.collectedFees); - assertEq(allocation.__DEPRECATED_effectiveAllocation, _allocation.__DEPRECATED_effectiveAllocation); - assertEq(allocation.accRewardsPerAllocatedToken, _allocation.accRewardsPerAllocatedToken); - assertEq(allocation.distributedRebates, _allocation.distributedRebates); - } - - function testAllocation_GetAllocationData() public useAllocation { - (address indexer, bytes32 subgraphDeploymentID, uint256 tokens, uint256 accRewardsPerAllocatedToken, uint256 accRewardsPending) = - staking.getAllocationData(_allocationId); - assertEq(indexer, _allocation.indexer); - assertEq(subgraphDeploymentID, _allocation.subgraphDeploymentID); - assertEq(tokens, _allocation.tokens); - assertEq(accRewardsPerAllocatedToken, _allocation.accRewardsPerAllocatedToken); - assertEq(accRewardsPending, 0); - } - - function testAllocation_GetAllocationState_Active() public useAllocation { + function testAllocation_GetAllocationState_Active(uint256 tokens) public useIndexer useAllocation(tokens) { IHorizonStakingExtension.AllocationState state = staking.getAllocationState(_allocationId); assertEq(uint16(state), uint16(IHorizonStakingExtension.AllocationState.Active)); } @@ -45,7 +22,7 @@ contract HorizonStakingAllocationTest is HorizonStakingExtensionTest { assertEq(uint16(state), uint16(IHorizonStakingExtension.AllocationState.Null)); } - function testAllocation_IsAllocation() public useAllocation { + function testAllocation_IsAllocation(uint256 tokens) public useIndexer useAllocation(tokens) { bool isAllocation = staking.isAllocation(_allocationId); assertTrue(isAllocation); } diff --git a/packages/horizon/test/staking/allocation/close.t.sol b/packages/horizon/test/staking/allocation/close.t.sol index c7c2aff68..121307056 100644 --- a/packages/horizon/test/staking/allocation/close.t.sol +++ b/packages/horizon/test/staking/allocation/close.t.sol @@ -1,54 +1,42 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; -import { HorizonStakingExtensionTest } from "./HorizonStakingExtension.t.sol"; +import { HorizonStakingTest } from "../HorizonStaking.t.sol"; import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; import { PPMMath } from "../../../contracts/libraries/PPMMath.sol"; -contract HorizonStakingCloseAllocationTest is HorizonStakingExtensionTest { +contract HorizonStakingCloseAllocationTest is HorizonStakingTest { using PPMMath for uint256; + bytes32 internal constant _poi = keccak256("poi"); + /* * TESTS */ - function testCloseAllocation(uint256 tokens) public useIndexer { + function testCloseAllocation(uint256 tokens) public useIndexer useAllocation(1 ether) { tokens = bound(tokens, 1, MAX_STAKING_TOKENS); - _storeAllocation(tokens); - _storeMaxAllocationEpochs(); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, tokens, 0, 0); // Skip 15 epochs vm.roll(15); - staking.closeAllocation(_allocationId, _poi); - IHorizonStakingExtension.Allocation memory allocation = staking.getAllocation(_allocationId); - assertEq(allocation.closedAtEpoch, epochManager.currentEpoch()); - - // Stake should be updated with rewards - assertEq(staking.getStake(address(users.indexer)), tokens * 2 + ALLOCATIONS_REWARD_CUT); + _closeAllocation(_allocationId, _poi); } - function testCloseAllocation_WithBeneficiaryAddress(uint256 tokens) public useIndexer { + function testCloseAllocation_WithBeneficiaryAddress(uint256 tokens) public useIndexer useAllocation(1 ether) { tokens = bound(tokens, 1, MAX_STAKING_TOKENS); - _storeAllocation(tokens); - _storeMaxAllocationEpochs(); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, tokens, 0, 0); address beneficiary = makeAddr("beneficiary"); - _storeRewardsDestination(beneficiary); + _setStorage_RewardsDestination(users.indexer, beneficiary); // Skip 15 epochs vm.roll(15); - staking.closeAllocation(_allocationId, _poi); - IHorizonStakingExtension.Allocation memory allocation = staking.getAllocation(_allocationId); - assertEq(allocation.closedAtEpoch, epochManager.currentEpoch()); - - // Stake should be updated with rewards - assertEq(token.balanceOf(beneficiary), ALLOCATIONS_REWARD_CUT); + _closeAllocation(_allocationId, _poi); } function testCloseAllocation_RevertWhen_NotActive() public { @@ -56,43 +44,32 @@ contract HorizonStakingCloseAllocationTest is HorizonStakingExtensionTest { staking.closeAllocation(_allocationId, _poi); } - function testCloseAllocation_RevertWhen_NotIndexer() public useAllocation { + function testCloseAllocation_RevertWhen_NotIndexer() public useIndexer useAllocation(1 ether) { resetPrank(users.delegator); vm.expectRevert("!auth"); staking.closeAllocation(_allocationId, _poi); } - function testCloseAllocation_AfterMaxEpochs_AnyoneCanClose(uint256 tokens) public useIndexer { + function testCloseAllocation_AfterMaxEpochs_AnyoneCanClose(uint256 tokens) public useIndexer useAllocation(1 ether) { tokens = bound(tokens, 1, MAX_STAKING_TOKENS); - _storeAllocation(tokens); - _storeMaxAllocationEpochs(); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, tokens, 0, 0); // Skip to over the max allocation epochs - vm.roll(MAX_ALLOCATION_EPOCHS + 2); + vm.roll((MAX_ALLOCATION_EPOCHS + 1)* EPOCH_LENGTH + 1); resetPrank(users.delegator); - staking.closeAllocation(_allocationId, 0x0); - IHorizonStakingExtension.Allocation memory allocation = staking.getAllocation(_allocationId); - assertEq(allocation.closedAtEpoch, epochManager.currentEpoch()); - - // No rewards distributed - assertEq(staking.getStake(address(users.indexer)), tokens * 2); + _closeAllocation(_allocationId, 0x0); } - function testCloseAllocation_RevertWhen_ZeroTokensNotAuthorized() public useIndexer { - _storeAllocation(0); - _storeMaxAllocationEpochs(); - - // Skip to over the max allocation epochs - vm.roll(MAX_ALLOCATION_EPOCHS + 2); + function testCloseAllocation_RevertWhen_ZeroTokensNotAuthorized() public useIndexer useAllocation(1 ether){ + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 0); resetPrank(users.delegator); vm.expectRevert("!auth"); staking.closeAllocation(_allocationId, 0x0); } - function testCloseAllocation_WithDelegation(uint256 tokens, uint256 delegationTokens, uint32 indexingRewardCut) public useIndexer { + 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); vm.assume(indexingRewardCut <= MAX_PPM); @@ -100,21 +77,12 @@ contract HorizonStakingCloseAllocationTest is HorizonStakingExtensionTest { uint256 legacyAllocationTokens = tokens / 2; uint256 provisionTokens = tokens - legacyAllocationTokens; - _storeAllocation(legacyAllocationTokens); - _storeMaxAllocationEpochs(); - _createProvision(subgraphDataServiceLegacyAddress, provisionTokens, 0, 0); - _storeDelegationPool(delegationTokens, indexingRewardCut, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, provisionTokens, 0, 0); + _setStorage_DelegationPool(users.indexer, delegationTokens, indexingRewardCut, 0); // Skip 15 epochs vm.roll(15); - staking.closeAllocation(_allocationId, _poi); - IHorizonStakingExtension.Allocation memory allocation = staking.getAllocation(_allocationId); - assertEq(allocation.closedAtEpoch, epochManager.currentEpoch()); - - uint256 indexerRewardCut = ALLOCATIONS_REWARD_CUT.mulPPM(indexingRewardCut); - uint256 delegationFeeCut = ALLOCATIONS_REWARD_CUT - indexerRewardCut; - assertEq(staking.getStake(address(users.indexer)), tokens + indexerRewardCut); - assertEq(staking.getDelegationPool(users.indexer, subgraphDataServiceLegacyAddress).tokens, delegationTokens + delegationFeeCut); + _closeAllocation(_allocationId, _poi); } } \ No newline at end of file diff --git a/packages/horizon/test/staking/allocation/collect.t.sol b/packages/horizon/test/staking/allocation/collect.t.sol index 3ea230a8b..9b8b1d9e5 100644 --- a/packages/horizon/test/staking/allocation/collect.t.sol +++ b/packages/horizon/test/staking/allocation/collect.t.sol @@ -1,75 +1,20 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; -import { HorizonStakingExtensionTest } from "./HorizonStakingExtension.t.sol"; -import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; +import { HorizonStakingTest } from "../HorizonStaking.t.sol"; import { ExponentialRebates } from "../../../contracts/staking/libraries/ExponentialRebates.sol"; import { PPMMath } from "../../../contracts/libraries/PPMMath.sol"; -contract HorizonStakingCollectAllocationTest is HorizonStakingExtensionTest { +contract HorizonStakingCollectAllocationTest is HorizonStakingTest { using PPMMath for uint256; - uint32 private alphaNumerator = 100; - uint32 private alphaDenominator = 100; - uint32 private lambdaNumerator = 60; - uint32 private lambdaDenominator = 100; - - /* - * MODIFIERS - */ - - modifier useRebateParameters() { - _storeRebateParameters(); - _; - } - - /* - * HELPERS - */ - - function _storeRebateParameters() private { - // Store alpha numerator and denominator - uint256 alphaSlot = 13; - uint256 alphaNumeratorOffset = 20; - uint256 alphaDenominatorOffset = 24; - bytes32 alphaValues = bytes32( - (uint256(alphaNumerator) << (8 * alphaNumeratorOffset)) | - (uint256(alphaDenominator) << (8 * alphaDenominatorOffset)) - ); - vm.store(address(staking), bytes32(alphaSlot), alphaValues); - - // Store lambda numerator and denominator - uint256 lambdaSlot = 25; - uint256 lambdaNumeratorOffset = 20; - uint256 lambdaDenominatorOffset = 24; - bytes32 lambdaValues = bytes32( - (uint256(lambdaNumerator) << (8 * lambdaNumeratorOffset)) | - (uint256(lambdaDenominator) << (8 * lambdaDenominatorOffset)) - ); - vm.store(address(staking), bytes32(lambdaSlot), lambdaValues); - } - - function _storeProtocolTaxAndCuration(uint32 curationPercentage, uint32 taxPercentage) private { - bytes32 slot = bytes32(uint256(13)); - uint256 curationOffset = 4; - uint256 protocolTaxOffset = 8; - bytes32 originalValue = vm.load(address(staking), slot); - - bytes32 newProtocolTaxValue = bytes32( - (uint256(originalValue) & ~((0xFFFFFFFF << (8 * curationOffset)) | (0xFFFFFFFF << (8 * protocolTaxOffset))) | - (uint256(curationPercentage) << (8 * curationOffset))) | - (uint256(taxPercentage) << (8 * protocolTaxOffset)) - ); - vm.store(address(staking), slot, newProtocolTaxValue); - } - /* * TESTS */ - function testCollectAllocation_RevertWhen_InvalidAllocationId(uint256 tokens) public useAllocation { + function testCollectAllocation_RevertWhen_InvalidAllocationId(uint256 tokens) public useIndexer useAllocation(1 ether) { vm.expectRevert("!alloc"); staking.collect(tokens, address(0)); } @@ -79,13 +24,7 @@ contract HorizonStakingCollectAllocationTest is HorizonStakingExtensionTest { staking.collect(tokens, _allocationId); } - function testCollectAllocation_ZeroTokens() public useAllocation { - staking.collect(0, _allocationId); - assertEq(staking.getStake(address(users.indexer)), 0); - } - function testCollect_Tokenss( - uint256 provisionTokens, uint256 allocationTokens, uint256 collectTokens, uint256 curationTokens, @@ -93,75 +32,37 @@ contract HorizonStakingCollectAllocationTest is HorizonStakingExtensionTest { uint32 protocolTaxPercentage, uint256 delegationTokens, uint32 queryFeeCut - ) public useRebateParameters { - provisionTokens = bound(provisionTokens, 1, MAX_STAKING_TOKENS); - allocationTokens = bound(allocationTokens, 0, MAX_STAKING_TOKENS); + ) public useIndexer useRebateParameters useAllocation(allocationTokens) { collectTokens = bound(collectTokens, 0, MAX_STAKING_TOKENS); curationTokens = bound(curationTokens, 0, MAX_STAKING_TOKENS); delegationTokens = bound(delegationTokens, 0, MAX_STAKING_TOKENS); vm.assume(curationPercentage <= MAX_PPM); vm.assume(protocolTaxPercentage <= MAX_PPM); vm.assume(queryFeeCut <= MAX_PPM); + resetPrank(users.indexer); - _storeAllocation(allocationTokens); - _storeProtocolTaxAndCuration(curationPercentage, protocolTaxPercentage); - _storeDelegationPool(delegationTokens, 0, queryFeeCut); - _createProvision(subgraphDataServiceLegacyAddress, provisionTokens, 0, 0); + _setStorage_ProtocolTaxAndCuration(curationPercentage, protocolTaxPercentage); + console.log("queryFeeCut", queryFeeCut); + _setStorage_DelegationPool(users.indexer, delegationTokens, 0, queryFeeCut); curation.signal(_subgraphDeploymentID, curationTokens); resetPrank(users.gateway); approve(address(staking), collectTokens); - staking.collect(collectTokens, _allocationId); - - uint256 protocolTaxTokens = collectTokens.mulPPMRoundUp(protocolTaxPercentage); - uint256 queryFees = collectTokens - protocolTaxTokens; - - uint256 curationCutTokens = 0; - if (curationTokens > 0) { - curationCutTokens = queryFees.mulPPMRoundUp(curationPercentage); - queryFees -= curationCutTokens; - } - - uint256 newRebates = ExponentialRebates.exponentialRebates( - queryFees, - allocationTokens, - alphaNumerator, - alphaDenominator, - lambdaNumerator, - lambdaDenominator - ); - uint256 payment = newRebates > queryFees ? queryFees : newRebates; - - uint256 delegationFeeCut = 0; - if (delegationTokens > 0) { - delegationFeeCut = payment - payment.mulPPM(queryFeeCut); - payment -= delegationFeeCut; - } - - assertEq(staking.getStake(address(users.indexer)), allocationTokens + provisionTokens + payment); - assertEq(curation.curation(_subgraphDeploymentID), curationTokens + curationCutTokens); - assertEq(staking.getDelegationPool(users.indexer, subgraphDataServiceLegacyAddress).tokens, delegationTokens + delegationFeeCut); - assertEq(token.balanceOf(address(payments)), 0); + _collect(collectTokens, _allocationId); } function testCollect_WithBeneficiaryAddress( - uint256 provisionTokens, uint256 allocationTokens, uint256 collectTokens - ) public useIndexer useRebateParameters { - provisionTokens = bound(provisionTokens, 1, MAX_STAKING_TOKENS); - allocationTokens = bound(allocationTokens, 0, MAX_STAKING_TOKENS); + ) public useIndexer useRebateParameters useAllocation(allocationTokens) { collectTokens = bound(collectTokens, 0, MAX_STAKING_TOKENS); - _createProvision(subgraphDataServiceLegacyAddress, provisionTokens, 0, 0); - _storeAllocation(allocationTokens); - address beneficiary = makeAddr("beneficiary"); - _storeRewardsDestination(beneficiary); + _setStorage_RewardsDestination(users.indexer, beneficiary); resetPrank(users.gateway); approve(address(staking), collectTokens); - staking.collect(collectTokens, _allocationId); + _collect(collectTokens, _allocationId); uint256 newRebates = ExponentialRebates.exponentialRebates( collectTokens, @@ -175,4 +76,4 @@ contract HorizonStakingCollectAllocationTest is HorizonStakingExtensionTest { assertEq(token.balanceOf(beneficiary), payment); } -} \ No newline at end of file +} diff --git a/packages/horizon/test/staking/delegation/addToPool.t.sol b/packages/horizon/test/staking/delegation/addToPool.t.sol index 4ef0c480a..e88becbbf 100644 --- a/packages/horizon/test/staking/delegation/addToPool.t.sol +++ b/packages/horizon/test/staking/delegation/addToPool.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -27,9 +27,7 @@ contract HorizonStakingDelegationAddToPoolTest is HorizonStakingTest { resetPrank(subgraphDataServiceAddress); mint(subgraphDataServiceAddress, delegationAmount); token.approve(address(staking), delegationAmount); - vm.expectEmit(address(staking)); - emit IHorizonStakingMain.TokensToDelegationPoolAdded(users.indexer, subgraphDataServiceAddress, delegationAmount); - staking.addToDelegationPool(users.indexer, subgraphDataServiceAddress, delegationAmount); + _addToDelegationPool(users.indexer, subgraphDataServiceAddress, delegationAmount); uint256 delegatedTokens = staking.getDelegatedTokensAvailable(users.indexer, subgraphDataServiceAddress); assertEq(delegatedTokens, delegationAmount); @@ -46,7 +44,7 @@ contract HorizonStakingDelegationAddToPoolTest is HorizonStakingTest { resetPrank(address(payments)); mint(address(payments), delegationAmount); token.approve(address(staking), delegationAmount); - staking.addToDelegationPool(users.indexer, subgraphDataServiceAddress, delegationAmount); + _addToDelegationPool(users.indexer, subgraphDataServiceAddress, delegationAmount); uint256 delegatedTokens = staking.getDelegatedTokensAvailable(users.indexer, subgraphDataServiceAddress); assertEq(delegatedTokens, delegationAmount); diff --git a/packages/horizon/test/staking/delegation/delegate.t.sol b/packages/horizon/test/staking/delegation/delegate.t.sol index 87416daf5..d5e101bca 100644 --- a/packages/horizon/test/staking/delegation/delegate.t.sol +++ b/packages/horizon/test/staking/delegation/delegate.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -30,7 +30,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { vm.startPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); undelegateAmount = bound(undelegateAmount, 1 wei, delegation.shares - 1 ether); _undelegate(users.indexer, subgraphDataServiceAddress, undelegateAmount); @@ -46,7 +46,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { vm.startPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); _undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); @@ -78,10 +78,10 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { 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); - _createProvision(subgraphDataServiceLegacyAddress, amount, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, amount, 0, 0); resetPrank(users.delegator); - _delegateLegacy(users.indexer, delegationAmount); + _delegate(users.indexer, delegationAmount); } function testDelegate_RevertWhen_InvalidPool( @@ -112,7 +112,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); _undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares); resetPrank(subgraphDataServiceAddress); diff --git a/packages/horizon/test/staking/delegation/undelegate.t.sol b/packages/horizon/test/staking/delegation/undelegate.t.sol index d256d8502..cc2492ba8 100644 --- a/packages/horizon/test/staking/delegation/undelegate.t.sol +++ b/packages/horizon/test/staking/delegation/undelegate.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -17,7 +17,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { uint256 delegationAmount ) public useIndexer useProvision(amount, 0, 0) useDelegation(delegationAmount) { resetPrank(users.delegator); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); _undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares); } @@ -31,7 +31,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationAmount, 0); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + 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; @@ -73,7 +73,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { uint256 overDelegationShares ) public useIndexer useProvision(amount, 0, 0) useDelegation(delegationAmount) { resetPrank(users.delegator); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); overDelegationShares = bound(overDelegationShares, delegation.shares + 1, MAX_STAKING_TOKENS + 1); bytes memory expectedError = abi.encodeWithSignature( @@ -104,13 +104,13 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { function testUndelegate_LegacySubgraphService(uint256 amount, uint256 delegationAmount) public useIndexer { amount = bound(amount, 1, MAX_STAKING_TOKENS); delegationAmount = bound(delegationAmount, MIN_DELEGATION, MAX_STAKING_TOKENS); - _createProvision(subgraphDataServiceLegacyAddress, amount, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, amount, 0, 0); resetPrank(users.delegator); - _delegateLegacy(users.indexer, delegationAmount); + _delegate(users.indexer, delegationAmount); - Delegation memory delegation = _getDelegation(subgraphDataServiceLegacyAddress); - _undelegateLegacy(users.indexer, delegation.shares); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, true); + _undelegate(users.indexer, delegation.shares); } function testUndelegate_RevertWhen_InvalidPool( @@ -125,7 +125,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { _slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0); resetPrank(users.delegator); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); vm.expectRevert(abi.encodeWithSelector( IHorizonStakingMain.HorizonStakingInvalidDelegationPoolState.selector, users.indexer, diff --git a/packages/horizon/test/staking/delegation/withdraw.t.sol b/packages/horizon/test/staking/delegation/withdraw.t.sol index 7eb6db57e..fc9072898 100644 --- a/packages/horizon/test/staking/delegation/withdraw.t.sol +++ b/packages/horizon/test/staking/delegation/withdraw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -17,11 +17,11 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { modifier useUndelegate(uint256 shares) { vm.stopPrank(); vm.startPrank(users.delegator); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); shares = bound(shares, 1, delegation.shares); if (shares != delegation.shares) { - DelegationPool memory pool = _getDelegationPool(subgraphDataServiceAddress); + 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; @@ -35,37 +35,13 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { /* * HELPERS */ - - function _withdrawDelegated(address _verifier, address _newIndexer) private { - LinkedList.List memory thawingRequests = staking.getThawRequestList(users.indexer, _verifier, users.delegator); - ThawRequest memory thawRequest = staking.getThawRequest(thawingRequests.tail); - - uint256 previousBalance = token.balanceOf(users.delegator); - uint256 expectedTokens = _expectedTokensFromThawRequest(thawRequest, _verifier); - staking.withdrawDelegated(users.indexer, _verifier, _newIndexer, 0, 0); - - if (_newIndexer != address(0)) { - uint256 delegatedTokens = staking.getDelegatedTokensAvailable(_newIndexer, _verifier); - assertEq(delegatedTokens, expectedTokens); - } else { - uint256 newBalance = token.balanceOf(users.delegator); - assertEq(newBalance - previousBalance, expectedTokens); - } - } - - function _expectedTokensFromThawRequest(ThawRequest memory thawRequest, address verifier) private view returns (uint256) { - DelegationPool memory pool = _getDelegationPool(verifier); - return (thawRequest.shares * pool.tokensThawing) / pool.sharesThawing; - } - function _setupNewIndexer(uint256 tokens) private returns(address) { - address msgSender; - (, msgSender,) = vm.readCallers(); + (, address msgSender,) = vm.readCallers(); + address newIndexer = createUser("newIndexer"); vm.startPrank(newIndexer); - token.approve(address(staking), tokens); - staking.stakeTo(newIndexer, tokens); - staking.provision(newIndexer,subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); + _createProvision(newIndexer, subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); + vm.startPrank(msgSender); return newIndexer; } @@ -89,7 +65,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { skip(thawRequest.thawingUntil + 1); - _withdrawDelegated(subgraphDataServiceAddress, address(0)); + _withdrawDelegated(users.indexer, subgraphDataServiceAddress, address(0), 0, 0); } function testWithdrawDelegation_RevertWhen_NotThawing( @@ -118,7 +94,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { // Setup new service provider address newIndexer = _setupNewIndexer(10_000_000 ether); - _withdrawDelegated(subgraphDataServiceAddress, newIndexer); + _withdrawDelegated(users.indexer, subgraphDataServiceAddress, newIndexer, 0, 0); } function testWithdrawDelegation_ZeroTokens( @@ -131,7 +107,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { useUndelegate(delegationAmount) { uint256 previousBalance = token.balanceOf(users.delegator); - staking.withdrawDelegated(users.indexer, subgraphDataServiceAddress, address(0), 0, 0); + _withdrawDelegated(users.indexer, subgraphDataServiceAddress, address(0), 0, 0); // Nothing changed since thawing period haven't finished uint256 newBalance = token.balanceOf(users.delegator); @@ -151,7 +127,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { address newIndexer = _setupNewIndexer(10_000_000 ether); uint256 previousBalance = token.balanceOf(users.delegator); - staking.withdrawDelegated(users.indexer, subgraphDataServiceAddress, newIndexer, 0, 0); + _withdrawDelegated(users.indexer, subgraphDataServiceAddress, newIndexer, 0, 0); uint256 newBalance = token.balanceOf(users.delegator); assertEq(newBalance, previousBalance); @@ -162,19 +138,19 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { function testWithdrawDelegation_LegacySubgraphService(uint256 delegationAmount) public useIndexer { delegationAmount = bound(delegationAmount, MIN_DELEGATION, MAX_STAKING_TOKENS); - _createProvision(subgraphDataServiceLegacyAddress, 10_000_000 ether, 0, MAX_THAWING_PERIOD); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 10_000_000 ether, 0, MAX_THAWING_PERIOD); resetPrank(users.delegator); - _delegateLegacy(users.indexer, delegationAmount); - Delegation memory delegation = _getDelegation(subgraphDataServiceLegacyAddress); - _undelegateLegacy(users.indexer, delegation.shares); + _delegate(users.indexer, delegationAmount); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, true); + _undelegate(users.indexer, delegation.shares); LinkedList.List memory thawingRequests = staking.getThawRequestList(users.indexer, subgraphDataServiceLegacyAddress, users.delegator); ThawRequest memory thawRequest = staking.getThawRequest(thawingRequests.tail); skip(thawRequest.thawingUntil + 1); - _withdrawDelegated(subgraphDataServiceLegacyAddress, address(0)); + _withdrawDelegated(users.indexer, address(0)); } function testWithdrawDelegation_RevertWhen_InvalidPool( @@ -184,7 +160,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS); resetPrank(users.delegator); _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); - Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); + DelegationInternal memory delegation = _getStorage_Delegation(users.indexer, subgraphDataServiceAddress, users.delegator, false); _undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares); skip(MAX_THAWING_PERIOD + 1); diff --git a/packages/horizon/test/staking/governance/governance.t.sol b/packages/horizon/test/staking/governance/governance.t.sol index 932e053c4..401828a3e 100644 --- a/packages/horizon/test/staking/governance/governance.t.sol +++ b/packages/horizon/test/staking/governance/governance.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -21,8 +21,7 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { */ function testGovernance_SetAllowedLockedVerifier() public useGovernor { - staking.setAllowedLockedVerifier(subgraphDataServiceAddress, true); - assertTrue(staking.isAllowedLockedVerifier(subgraphDataServiceAddress)); + _setAllowedLockedVerifier(subgraphDataServiceAddress, true); } function testGovernance_RevertWhen_SetAllowedLockedVerifier_NotGovernor() public useIndexer { @@ -32,8 +31,7 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { } function testGovernance_SetDelgationSlashingEnabled() public useGovernor { - staking.setDelegationSlashingEnabled(); - assertTrue(staking.isDelegationSlashingEnabled()); + _setDelegationSlashingEnabled(); } function testGovernance_SetDelgationSlashing_NotGovernor() public useIndexer { @@ -43,11 +41,10 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { } function testGovernance_ClearThawingPeriod(uint32 thawingPeriod) public useGovernor { - vm.store(address(staking), bytes32(uint256(13)), bytes32(uint256(thawingPeriod))); - assertEq(staking.__DEPRECATED_getThawingPeriod(), thawingPeriod); + // simulate previous thawing period + _setStorage_DeprecatedThawingPeriod(thawingPeriod); - staking.clearThawingPeriod(); - assertEq(staking.__DEPRECATED_getThawingPeriod(), 0); + _clearThawingPeriod(); } function testGovernance_ClearThawingPeriod_NotGovernor() public useIndexer { @@ -57,8 +54,7 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { } function testGovernance__SetMaxThawingPeriod(uint64 maxThawingPeriod) public useGovernor { - staking.setMaxThawingPeriod(maxThawingPeriod); - assertEq(staking.getMaxThawingPeriod(), maxThawingPeriod); + _setMaxThawingPeriod(maxThawingPeriod); } function testGovernance__SetMaxThawingPeriod_NotGovernor() public useIndexer { @@ -68,10 +64,7 @@ contract HorizonStakingGovernanceTest is HorizonStakingTest { } function testGovernance_SetCounterpartStakingAddress(address counterpartStakingAddress) public useGovernor { - staking.setCounterpartStakingAddress(counterpartStakingAddress); - bytes32 storedValue = vm.load(address(staking), bytes32(uint256(24))); - address storedCounterpartStakingAddress = address(uint160(uint256(storedValue))); - assertEq(storedCounterpartStakingAddress, counterpartStakingAddress); + _setCounterpartStakingAddress(counterpartStakingAddress); } function testGovernance_RevertWhen_SetCounterpartStakingAddress_NotGovernor( diff --git a/packages/horizon/test/staking/operator/locked.t.sol b/packages/horizon/test/staking/operator/locked.t.sol index e6eb8038d..706b47a07 100644 --- a/packages/horizon/test/staking/operator/locked.t.sol +++ b/packages/horizon/test/staking/operator/locked.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -12,8 +12,7 @@ contract HorizonStakingOperatorLockedTest is HorizonStakingTest { */ function testOperatorLocked_Set() public useIndexer useLockedVerifier(subgraphDataServiceAddress) { - staking.setOperatorLocked(users.operator, subgraphDataServiceAddress, true); - assertTrue(staking.isAuthorized(users.operator, users.indexer, subgraphDataServiceAddress)); + _setOperatorLocked(users.operator, subgraphDataServiceAddress, true); } function testOperatorLocked_RevertWhen_VerifierNotAllowed() public useIndexer { @@ -29,7 +28,6 @@ contract HorizonStakingOperatorLockedTest is HorizonStakingTest { } function testOperatorLocked_SetLegacySubgraphService() public useIndexer useLockedVerifier(subgraphDataServiceLegacyAddress) { - staking.setOperatorLocked(users.operator, subgraphDataServiceLegacyAddress, true); - assertTrue(staking.isAuthorized(users.operator, users.indexer, subgraphDataServiceLegacyAddress)); + _setOperatorLocked(users.operator, subgraphDataServiceLegacyAddress, true); } } \ No newline at end of file diff --git a/packages/horizon/test/staking/operator/operator.t.sol b/packages/horizon/test/staking/operator/operator.t.sol index af06e0ef5..5d509ba68 100644 --- a/packages/horizon/test/staking/operator/operator.t.sol +++ b/packages/horizon/test/staking/operator/operator.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -11,8 +11,8 @@ contract HorizonStakingOperatorTest is HorizonStakingTest { * TESTS */ - function testOperator_SetOperator() public useOperator { - assertTrue(staking.isAuthorized(users.operator, users.indexer, subgraphDataServiceAddress)); + function testOperator_SetOperator() public useIndexer { + _setOperator(users.operator, subgraphDataServiceAddress, true); } function testOperator_RevertWhen_CallerIsServiceProvider() public useIndexer { @@ -22,10 +22,7 @@ contract HorizonStakingOperatorTest is HorizonStakingTest { } function testOperator_RemoveOperator() public useIndexer { - staking.setOperator(users.operator, subgraphDataServiceAddress, true); - assertTrue(staking.isAuthorized(users.operator, users.indexer, subgraphDataServiceAddress)); - - staking.setOperator(users.operator, subgraphDataServiceAddress, false); - assertFalse(staking.isAuthorized(users.operator, users.indexer, subgraphDataServiceAddress)); + _setOperator(users.operator, subgraphDataServiceAddress, true); + _setOperator(users.operator, subgraphDataServiceAddress, false); } } \ No newline at end of file diff --git a/packages/horizon/test/staking/provision/deprovision.t.sol b/packages/horizon/test/staking/provision/deprovision.t.sol index fbb9d0fc4..6d03c3f6e 100644 --- a/packages/horizon/test/staking/provision/deprovision.t.sol +++ b/packages/horizon/test/staking/provision/deprovision.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingDeprovisionTest is HorizonStakingTest { - /* * TESTS */ @@ -14,52 +13,65 @@ contract HorizonStakingDeprovisionTest is HorizonStakingTest { function testDeprovision_AllRequests( uint256 amount, uint32 maxVerifierCut, - uint64 thawingPeriod - ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) useThawRequest(amount) { + uint64 thawingPeriod, + uint256 thawCount, + uint256 deprovisionCount + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { + thawCount = bound(thawCount, 1, MAX_THAW_REQUESTS); + deprovisionCount = bound(deprovisionCount, 0, thawCount); + vm.assume(amount >= thawCount); // ensure the provision has at least 1 token for each thaw step + uint256 individualThawAmount = amount / thawCount; + + for (uint i = 0; i < thawCount; i++) { + _thaw(users.indexer, subgraphDataServiceAddress, individualThawAmount); + } + skip(thawingPeriod + 1); - // nThawRequests == 0 removes all thaw requests - _deprovision(0); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, amount); + _deprovision(users.indexer, subgraphDataServiceAddress, deprovisionCount); } - function testDeprovision_FirstRequestOnly( + function testDeprovision_ThawedRequests( uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod, - uint256 thawAmount + uint256 thawCount ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { - vm.assume(amount > 1); - thawAmount = bound(thawAmount, 2, amount); - uint256 thawAmount1 = thawAmount / 2; - _createThawRequest(thawAmount1); - _createThawRequest(thawAmount - thawAmount1); + thawCount = bound(thawCount, 2, MAX_THAW_REQUESTS); + vm.assume(amount >= thawCount); // ensure the provision has at least 1 token for each thaw step + uint256 individualThawAmount = amount / thawCount; + + for (uint i = 0; i < thawCount / 2; i++) { + _thaw(users.indexer, subgraphDataServiceAddress, individualThawAmount); + } skip(thawingPeriod + 1); - _deprovision(1); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, thawAmount1); + for (uint i = 0; i < thawCount / 2; i++) { + _thaw(users.indexer, subgraphDataServiceAddress, individualThawAmount); + } + + _deprovision(users.indexer, subgraphDataServiceAddress, 0); } function testDeprovision_OperatorMovingTokens( uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod - ) public useOperator useProvision(amount, maxVerifierCut, thawingPeriod) useThawRequest(amount) { + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) useOperator { + _thaw(users.indexer, subgraphDataServiceAddress, amount); skip(thawingPeriod + 1); - _deprovision(0); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, amount); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); } function testDeprovision_RevertWhen_OperatorNotAuthorized( uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod - ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) useThawRequest(amount) { + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { + _thaw(users.indexer, subgraphDataServiceAddress, amount); + vm.startPrank(users.operator); bytes memory expectedError = abi.encodeWithSignature( "HorizonStakingNotAuthorized(address,address,address)", @@ -68,8 +80,9 @@ contract HorizonStakingDeprovisionTest is HorizonStakingTest { subgraphDataServiceAddress ); vm.expectRevert(expectedError); - _deprovision(0); + staking.deprovision(users.indexer, subgraphDataServiceAddress, 0); } + function testDeprovision_RevertWhen_NoThawingTokens( uint256 amount, uint32 maxVerifierCut, @@ -77,17 +90,18 @@ contract HorizonStakingDeprovisionTest is HorizonStakingTest { ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { bytes memory expectedError = abi.encodeWithSignature("HorizonStakingNothingThawing()"); vm.expectRevert(expectedError); - _deprovision(0); + staking.deprovision(users.indexer, subgraphDataServiceAddress, 0); } function testDeprovision_StillThawing( uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod - ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) useThawRequest(amount) { + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { vm.assume(thawingPeriod > 0); - _deprovision(0); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, 0); + + _thaw(users.indexer, subgraphDataServiceAddress, amount); + + _deprovision(users.indexer, subgraphDataServiceAddress, 0); } -} \ No newline at end of file +} diff --git a/packages/horizon/test/staking/provision/locked.t.sol b/packages/horizon/test/staking/provision/locked.t.sol index 642ba9a10..a8adfc774 100644 --- a/packages/horizon/test/staking/provision/locked.t.sol +++ b/packages/horizon/test/staking/provision/locked.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -17,10 +17,10 @@ contract HorizonStakingProvisionLockedTest is HorizonStakingTest { uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, subgraphDataServiceAddress); assertEq(provisionTokens, 0); - staking.setOperatorLocked(users.operator, subgraphDataServiceAddress, true); + _setOperatorLocked(users.operator, subgraphDataServiceAddress, true); vm.startPrank(users.operator); - staking.provisionLocked( + _provisionLocked( users.indexer, subgraphDataServiceAddress, amount, @@ -39,11 +39,11 @@ contract HorizonStakingProvisionLockedTest is HorizonStakingTest { assertEq(provisionTokens, 0); // Set operator - staking.setOperatorLocked(users.operator, subgraphDataServiceAddress, true); + _setOperatorLocked(users.operator, subgraphDataServiceAddress, true); // Disable locked verifier vm.startPrank(users.governor); - staking.setAllowedLockedVerifier(subgraphDataServiceAddress, false); + _setAllowedLockedVerifier(subgraphDataServiceAddress, false); vm.startPrank(users.operator); bytes memory expectedError = abi.encodeWithSignature("HorizonStakingVerifierNotAllowed(address)", subgraphDataServiceAddress); diff --git a/packages/horizon/test/staking/provision/parameters.t.sol b/packages/horizon/test/staking/provision/parameters.t.sol index 298059a7a..8171f95b4 100644 --- a/packages/horizon/test/staking/provision/parameters.t.sol +++ b/packages/horizon/test/staking/provision/parameters.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -16,20 +16,7 @@ contract HorizonStakingProvisionParametersTest is HorizonStakingTest { uint32 maxVerifierCut, uint64 thawingPeriod ) public useIndexer useProvision(amount, 0, 0) { - vm.assume(maxVerifierCut != 0); - vm.assume(thawingPeriod != 0); - vm.expectEmit(); - emit IHorizonStakingMain.ProvisionParametersStaged( - users.indexer, - subgraphDataServiceAddress, - maxVerifierCut, - thawingPeriod - ); - staking.setProvisionParameters(users.indexer, subgraphDataServiceAddress, maxVerifierCut, thawingPeriod); - - Provision memory prov = staking.getProvision(users.indexer, subgraphDataServiceAddress); - assertEq(prov.maxVerifierCutPending, maxVerifierCut); - assertEq(prov.thawingPeriodPending, thawingPeriod); + _setProvisionParameters(users.indexer, subgraphDataServiceAddress, maxVerifierCut, thawingPeriod); } function test_ProvisionParametersSet_RevertWhen_ProvisionNotExists( @@ -78,27 +65,10 @@ contract HorizonStakingProvisionParametersTest is HorizonStakingTest { uint32 maxVerifierCut, uint64 thawingPeriod ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { - Provision memory prov = staking.getProvision(users.indexer, subgraphDataServiceAddress); - - staking.setProvisionParameters(users.indexer, subgraphDataServiceAddress, maxVerifierCut, thawingPeriod); + _setProvisionParameters(users.indexer, subgraphDataServiceAddress, maxVerifierCut, thawingPeriod); vm.startPrank(subgraphDataServiceAddress); - - if (maxVerifierCut != prov.maxVerifierCut || thawingPeriod != prov.thawingPeriod) { - vm.expectEmit(); - emit IHorizonStakingMain.ProvisionParametersSet( - users.indexer, - subgraphDataServiceAddress, - maxVerifierCut, - thawingPeriod - ); - } - staking.acceptProvisionParameters(users.indexer); + _acceptProvisionParameters(users.indexer); vm.stopPrank(); - - assertEq(prov.maxVerifierCut, maxVerifierCut); - assertEq(prov.maxVerifierCutPending, maxVerifierCut); - assertEq(prov.thawingPeriod, thawingPeriod); - assertEq(prov.thawingPeriodPending, thawingPeriod); } } diff --git a/packages/horizon/test/staking/provision/provision.t.sol b/packages/horizon/test/staking/provision/provision.t.sol index 3394460fb..50dc12057 100644 --- a/packages/horizon/test/staking/provision/provision.t.sol +++ b/packages/horizon/test/staking/provision/provision.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -10,13 +10,12 @@ contract HorizonStakingProvisionTest is HorizonStakingTest { * TESTS */ - function testProvision_Create( - uint256 amount, - uint32 maxVerifierCut, - uint64 thawingPeriod - ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { - uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, subgraphDataServiceAddress); - assertEq(provisionTokens, amount); + function testProvision_Create(uint256 tokens, uint32 maxVerifierCut, uint64 thawingPeriod) public useIndexer { + tokens = bound(tokens, 1, MAX_STAKING_TOKENS); + maxVerifierCut = uint32(bound(maxVerifierCut, 0, MAX_MAX_VERIFIER_CUT)); + thawingPeriod = uint32(bound(thawingPeriod, 0, MAX_THAWING_PERIOD)); + + _createProvision(users.indexer, subgraphDataServiceAddress, tokens, maxVerifierCut, thawingPeriod); } function testProvision_RevertWhen_ZeroTokens() public useIndexer useStake(1000 ether) { @@ -75,7 +74,7 @@ contract HorizonStakingProvisionTest is HorizonStakingTest { resetPrank(users.indexer); token.approve(address(staking), amount / 2); - staking.stake(amount / 2); + _stake(amount / 2); bytes memory expectedError = abi.encodeWithSignature("HorizonStakingProvisionAlreadyExists()"); vm.expectRevert(expectedError); @@ -87,18 +86,12 @@ contract HorizonStakingProvisionTest is HorizonStakingTest { uint32 maxVerifierCut, uint64 thawingPeriod, uint256 tokensToAdd - ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { - tokensToAdd = bound(tokensToAdd, 1, type(uint256).max - amount); - // Set operator - staking.setOperator(users.operator, subgraphDataServiceAddress, true); + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) useOperator { + tokensToAdd = bound(tokensToAdd, 1, MAX_STAKING_TOKENS); // Add more tokens to the provision - vm.startPrank(users.operator); _stakeTo(users.indexer, tokensToAdd); - staking.addToProvision(users.indexer, subgraphDataServiceAddress, tokensToAdd); - - uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, subgraphDataServiceAddress); - assertEq(provisionTokens, amount + tokensToAdd); + _addToProvision(users.indexer, subgraphDataServiceAddress, tokensToAdd); } function testProvision_RevertWhen_OperatorNotAuthorized( diff --git a/packages/horizon/test/staking/provision/reprovision.t.sol b/packages/horizon/test/staking/provision/reprovision.t.sol index 5d7ed8068..6fd170e4b 100644 --- a/packages/horizon/test/staking/provision/reprovision.t.sol +++ b/packages/horizon/test/staking/provision/reprovision.t.sol @@ -1,26 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingReprovisionTest is HorizonStakingTest { - /* * VARIABLES */ address private newDataService = makeAddr("newDataService"); - /* - * HELPERS - */ - - function _reprovision(uint256 tokens, uint256 nThawRequests) private { - staking.reprovision(users.indexer, subgraphDataServiceAddress, newDataService, tokens, nThawRequests); - } - /* * TESTS */ @@ -28,62 +19,62 @@ contract HorizonStakingReprovisionTest is HorizonStakingTest { function testReprovision_MovingTokens( uint64 thawingPeriod, uint256 provisionAmount - ) - public - useIndexer - useProvision(provisionAmount, 0, thawingPeriod) - useThawRequest(provisionAmount) - { + ) public useIndexer useProvision(provisionAmount, 0, thawingPeriod) { + _thaw(users.indexer, subgraphDataServiceAddress, provisionAmount); skip(thawingPeriod + 1); - _createProvision(newDataService, 1 ether, 0, thawingPeriod); + _createProvision(users.indexer, newDataService, 1 ether, 0, thawingPeriod); + + _reprovision(users.indexer, subgraphDataServiceAddress, newDataService, provisionAmount, 0); + } - // nThawRequests == 0 reprovisions all thaw requests - _reprovision(provisionAmount, 0); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, 0 ether); + function testReprovision_TokensOverThawingTokens() public useIndexer { + uint64 thawingPeriod = 1 days; - uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, newDataService); - assertEq(provisionTokens, provisionAmount + 1 ether); + // create provision A, thaw 10 ether, skip time so they are fully thawed + _createProvision(users.indexer, subgraphDataServiceAddress, 100 ether, 0, thawingPeriod); + _thaw(users.indexer, subgraphDataServiceAddress, 10 ether); + skip(thawingPeriod + 1); + + // create provision B + _createProvision(users.indexer, newDataService, 1 ether, 0, thawingPeriod); + + // reprovision 100 ether from A to B + // this should revert because there are only 10 ether that thawed and the service provider + // doesn't have additional idle stake to cover the difference + vm.expectRevert(); + staking.reprovision(users.indexer, subgraphDataServiceAddress, newDataService, 100 ether, 0); + + // now add some idle stake and try again, it should not revert + _stake(100 ether); + _reprovision(users.indexer, subgraphDataServiceAddress, newDataService, 100 ether, 0); } function testReprovision_OperatorMovingTokens( uint64 thawingPeriod, uint256 provisionAmount - ) - public - useOperator - useProvision(provisionAmount, 0, thawingPeriod) - useThawRequest(provisionAmount) - { + ) public useOperator useProvision(provisionAmount, 0, thawingPeriod) { + _thaw(users.indexer, subgraphDataServiceAddress, provisionAmount); skip(thawingPeriod + 1); // Switch to indexer to set operator for new data service vm.startPrank(users.indexer); - staking.setOperator(users.operator, newDataService, true); - + _setOperator(users.operator, newDataService, true); + // Switch back to operator vm.startPrank(users.operator); - _createProvision(newDataService, 1 ether, 0, thawingPeriod); - _reprovision(provisionAmount, 0); - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, 0 ether); - - uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, newDataService); - assertEq(provisionTokens, provisionAmount + 1 ether); + _createProvision(users.indexer, newDataService, 1 ether, 0, thawingPeriod); + _reprovision(users.indexer, subgraphDataServiceAddress, newDataService, provisionAmount, 0); } function testReprovision_RevertWhen_OperatorNotAuthorizedForNewDataService( uint256 provisionAmount - ) - public - useOperator - useProvision(provisionAmount, 0, 0) - useThawRequest(provisionAmount) - { + ) public useOperator useProvision(provisionAmount, 0, 0) { + _thaw(users.indexer, subgraphDataServiceAddress, provisionAmount); + // Switch to indexer to create new provision vm.startPrank(users.indexer); - _createProvision(newDataService, 1 ether, 0, 0); + _createProvision(users.indexer, newDataService, 1 ether, 0, 0); // Switch back to operator vm.startPrank(users.operator); @@ -94,29 +85,23 @@ contract HorizonStakingReprovisionTest is HorizonStakingTest { newDataService ); vm.expectRevert(expectedError); - _reprovision(provisionAmount, 0); + staking.reprovision(users.indexer, subgraphDataServiceAddress, newDataService, provisionAmount, 0); } - function testReprovision_RevertWhen_NoThawingTokens( - uint256 amount - ) public useIndexer useProvision(amount, 0, 0) { + function testReprovision_RevertWhen_NoThawingTokens(uint256 amount) public useIndexer useProvision(amount, 0, 0) { bytes memory expectedError = abi.encodeWithSignature("HorizonStakingNothingThawing()"); vm.expectRevert(expectedError); - _reprovision(amount, 0); + staking.reprovision(users.indexer, subgraphDataServiceAddress, newDataService, amount, 0); } function testReprovision_RevertWhen_StillThawing( uint64 thawingPeriod, uint256 provisionAmount - ) - public - useIndexer - useProvision(provisionAmount, 0, thawingPeriod) - useThawRequest(provisionAmount) - { + ) public useIndexer useProvision(provisionAmount, 0, thawingPeriod) { vm.assume(thawingPeriod > 0); + _thaw(users.indexer, subgraphDataServiceAddress, provisionAmount); - _createProvision(newDataService, 1 ether, 0, thawingPeriod); + _createProvision(users.indexer, newDataService, 1 ether, 0, thawingPeriod); bytes memory expectedError = abi.encodeWithSignature( "HorizonStakingInsufficientIdleStake(uint256,uint256)", @@ -124,6 +109,6 @@ contract HorizonStakingReprovisionTest is HorizonStakingTest { 0 ); vm.expectRevert(expectedError); - _reprovision(provisionAmount, 0); + staking.reprovision(users.indexer, subgraphDataServiceAddress, newDataService, provisionAmount, 0); } -} \ No newline at end of file +} diff --git a/packages/horizon/test/staking/thaw/thaw.t.sol b/packages/horizon/test/staking/provision/thaw.t.sol similarity index 58% rename from packages/horizon/test/staking/thaw/thaw.t.sol rename to packages/horizon/test/staking/provision/thaw.t.sol index 2ed99682f..bb0b3409d 100644 --- a/packages/horizon/test/staking/thaw/thaw.t.sol +++ b/packages/horizon/test/staking/provision/thaw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -7,7 +7,6 @@ import { IHorizonStakingTypes } from "../../../contracts/interfaces/internal/IHo import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingThawTest is HorizonStakingTest { - /* * TESTS */ @@ -18,48 +17,29 @@ contract HorizonStakingThawTest is HorizonStakingTest { uint256 thawAmount ) public useIndexer useProvision(amount, 0, thawingPeriod) { thawAmount = bound(thawAmount, 1, amount); - bytes32 expectedThawRequestId = keccak256( - abi.encodePacked(users.indexer, subgraphDataServiceAddress, users.indexer, uint256(0)) - ); - bytes32 thawRequestId = _createThawRequest(thawAmount); - assertEq(thawRequestId, expectedThawRequestId); - ThawRequest memory thawRequest = staking.getThawRequest(expectedThawRequestId); - assertEq(thawRequest.shares, thawAmount); - assertEq(thawRequest.thawingUntil, block.timestamp + thawingPeriod); + _thaw(users.indexer, subgraphDataServiceAddress, thawAmount); } function testThaw_MultipleRequests( uint256 amount, uint64 thawingPeriod, - uint256 thawAmount, - uint256 thawAmount2 + uint256 thawCount ) public useIndexer useProvision(amount, 0, thawingPeriod) { - vm.assume(amount > 1); - thawAmount = bound(thawAmount, 1, amount - 1); - thawAmount2 = bound(thawAmount2, 1, amount - thawAmount); - bytes32 thawRequestId = _createThawRequest(thawAmount); - bytes32 thawRequestId2 = _createThawRequest(thawAmount2); - - ThawRequest memory thawRequest = staking.getThawRequest(thawRequestId); - assertEq(thawRequest.shares, thawAmount); - assertEq(thawRequest.thawingUntil, block.timestamp + thawingPeriod); - assertEq(thawRequest.next, thawRequestId2); + thawCount = bound(thawCount, 1, MAX_THAW_REQUESTS); + vm.assume(amount >= thawCount); // ensure the provision has at least 1 token for each thaw step + uint256 individualThawAmount = amount / thawCount; - ThawRequest memory thawRequest2 = staking.getThawRequest(thawRequestId2); - assertEq(thawRequest2.shares, thawAmount2); - assertEq(thawRequest2.thawingUntil, block.timestamp + thawingPeriod); + for (uint i = 0; i < thawCount; i++) { + _thaw(users.indexer, subgraphDataServiceAddress, individualThawAmount); + } } function testThaw_OperatorCanStartThawing( uint256 amount, uint64 thawingPeriod - ) public useOperator useProvision(amount, 0, thawingPeriod) { - bytes32 thawRequestId = _createThawRequest(amount); - - ThawRequest memory thawRequest = staking.getThawRequest(thawRequestId); - assertEq(thawRequest.shares, amount); - assertEq(thawRequest.thawingUntil, block.timestamp + thawingPeriod); + ) public useIndexer useProvision(amount, 0, thawingPeriod) useOperator { + _thaw(users.indexer, subgraphDataServiceAddress, amount); } function testThaw_RevertWhen_OperatorNotAuthorized( @@ -74,7 +54,7 @@ contract HorizonStakingThawTest is HorizonStakingTest { subgraphDataServiceAddress ); vm.expectRevert(expectedError); - _createThawRequest(amount); + staking.thaw(users.indexer, subgraphDataServiceAddress, amount); } function testThaw_RevertWhen_InsufficientTokensAvailable( @@ -89,7 +69,7 @@ contract HorizonStakingThawTest is HorizonStakingTest { thawAmount ); vm.expectRevert(expectedError); - _createThawRequest(thawAmount); + staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); } function testThaw_RevertWhen_OverMaxThawRequests( @@ -100,13 +80,13 @@ contract HorizonStakingThawTest is HorizonStakingTest { vm.assume(amount >= MAX_THAW_REQUESTS + 1); thawAmount = bound(thawAmount, 1, amount / (MAX_THAW_REQUESTS + 1)); - for (uint256 i = 0; i < 100; i++) { - _createThawRequest(thawAmount); + for (uint256 i = 0; i < MAX_THAW_REQUESTS; i++) { + _thaw(users.indexer, subgraphDataServiceAddress, thawAmount); } bytes memory expectedError = abi.encodeWithSignature("HorizonStakingTooManyThawRequests()"); vm.expectRevert(expectedError); - _createThawRequest(thawAmount); + staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); } function testThaw_RevertWhen_ThawingZeroTokens( @@ -116,6 +96,6 @@ contract HorizonStakingThawTest is HorizonStakingTest { uint256 thawAmount = 0 ether; bytes memory expectedError = abi.encodeWithSignature("HorizonStakingInvalidZeroTokens()"); vm.expectRevert(expectedError); - _createThawRequest(thawAmount); + staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); } } diff --git a/packages/horizon/test/staking/serviceProvider/serviceProvider.t.sol b/packages/horizon/test/staking/serviceProvider/serviceProvider.t.sol index 2d32fa9e2..63578791a 100644 --- a/packages/horizon/test/staking/serviceProvider/serviceProvider.t.sol +++ b/packages/horizon/test/staking/serviceProvider/serviceProvider.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -25,7 +25,7 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { assertEq(sp.tokensStaked, amount); assertEq(sp.tokensProvisioned, amount); - staking.setOperator(users.operator, subgraphDataServiceAddress, true); + _setOperator(users.operator, subgraphDataServiceAddress, true); resetPrank(users.operator); _stakeTo(users.indexer, operatorAmount); sp = staking.getServiceProvider(users.indexer); @@ -40,7 +40,7 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { vm.assume(paymentTypeInput < 3); IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes(paymentTypeInput); feeCut = bound(feeCut, 0, MAX_PPM); - _setDelegationFeeCut(paymentType, feeCut); + _setDelegationFeeCut(users.indexer, subgraphDataServiceAddress, paymentType, feeCut); } function testServiceProvider_GetProvision( @@ -60,7 +60,7 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { assertEq(p.maxVerifierCutPending, maxVerifierCut); assertEq(p.thawingPeriodPending, thawingPeriod); - staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); + _thaw(users.indexer, subgraphDataServiceAddress, thawAmount); p = staking.getProvision(users.indexer, subgraphDataServiceAddress); assertEq(p.tokensThawing, thawAmount); } @@ -75,7 +75,7 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { uint256 tokensAvailable = staking.getTokensAvailable(users.indexer, subgraphDataServiceAddress, 0); assertEq(tokensAvailable, amount); - staking.thaw(users.indexer, subgraphDataServiceAddress, thawAmount); + _thaw(users.indexer, subgraphDataServiceAddress, thawAmount); tokensAvailable = staking.getTokensAvailable(users.indexer, subgraphDataServiceAddress, 0); assertEq(tokensAvailable, amount - thawAmount); } @@ -106,9 +106,9 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { ) public useIndexer useProvision(amount, MAX_MAX_VERIFIER_CUT, MAX_THAWING_PERIOD) { assertTrue(staking.hasStake(users.indexer)); - _createThawRequest(amount); + _thaw(users.indexer, subgraphDataServiceAddress, amount); skip(MAX_THAWING_PERIOD + 1); - _deprovision(0); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); staking.unstake(amount); assertFalse(staking.hasStake(users.indexer)); @@ -119,12 +119,12 @@ contract HorizonStakingServiceProviderTest is HorizonStakingTest { ) public useIndexer useProvision(amount, MAX_MAX_VERIFIER_CUT, MAX_THAWING_PERIOD) { assertEq(staking.getIndexerStakedTokens(users.indexer), amount); - _createThawRequest(amount); + _thaw(users.indexer, subgraphDataServiceAddress, amount); // Does not discount thawing tokens assertEq(staking.getIndexerStakedTokens(users.indexer), amount); skip(MAX_THAWING_PERIOD + 1); - _deprovision(0); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); // Does not discount thawing tokens assertEq(staking.getIndexerStakedTokens(users.indexer), amount); diff --git a/packages/horizon/test/staking/slash/slash.t.sol b/packages/horizon/test/staking/slash/slash.t.sol index b68794604..bf1f2fb1e 100644 --- a/packages/horizon/test/staking/slash/slash.t.sol +++ b/packages/horizon/test/staking/slash/slash.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -8,15 +8,6 @@ import { IHorizonStakingMain } from "../../../contracts/interfaces/internal/IHor import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingSlashTest is HorizonStakingTest { - - /* - * MODIFIERS - */ - - /* - * HELPERS - */ - /* * TESTS */ diff --git a/packages/horizon/test/staking/stake/stake.t.sol b/packages/horizon/test/staking/stake/stake.t.sol index 9c1c69ad9..55474245c 100644 --- a/packages/horizon/test/staking/stake/stake.t.sol +++ b/packages/horizon/test/staking/stake/stake.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -11,8 +11,9 @@ contract HorizonStakingStakeTest is HorizonStakingTest { * TESTS */ - function testStake_Tokens(uint256 amount) public useIndexer useStake(amount) { - assertTrue(staking.getStake(address(users.indexer)) == amount); + function testStake_Tokens(uint256 amount) public useIndexer { + amount = bound(amount, 1, MAX_STAKING_TOKENS); + _stake(amount); } function testStake_RevertWhen_ZeroTokens() public useIndexer { @@ -21,8 +22,9 @@ contract HorizonStakingStakeTest is HorizonStakingTest { staking.stake(0); } - function testStakeTo_Tokens(uint256 amount) public useOperator useStakeTo(users.indexer, amount) { - assertTrue(staking.getStake(address(users.indexer)) == amount); + function testStakeTo_Tokens(uint256 amount) public useOperator { + amount = bound(amount, 1, MAX_STAKING_TOKENS); + _stakeTo(users.indexer, amount); } function testStakeTo_RevertWhen_ZeroTokens() public useOperator { diff --git a/packages/horizon/test/staking/stake/unstake.t.sol b/packages/horizon/test/staking/stake/unstake.t.sol index c5d44a3fb..13ea92e00 100644 --- a/packages/horizon/test/staking/stake/unstake.t.sol +++ b/packages/horizon/test/staking/stake/unstake.t.sol @@ -1,65 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; -import { IHorizonStakingMain } from "../../../contracts/interfaces/internal/IHorizonStakingMain.sol"; -import { MathUtils } from "../../../contracts/libraries/MathUtils.sol"; - import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingUnstakeTest is HorizonStakingTest { - - function _unstakeTokens(uint256 _tokens) private { - uint256 previousIndexerTokens = token.balanceOf(users.indexer); - uint256 previousIndexerIdleStake = staking.getIdleStake(users.indexer); - - vm.expectEmit(address(staking)); - emit IHorizonStakingMain.StakeWithdrawn(users.indexer, _tokens); - staking.unstake(_tokens); - - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, previousIndexerIdleStake - _tokens); - - uint256 newIndexerBalance = token.balanceOf(users.indexer); - assertEq(newIndexerBalance - previousIndexerTokens, _tokens); - } - - function _unstakeDuringLockingPeriod( - uint256 _tokens, - uint256 _tokensStillThawing, - uint256 _tokensToWithdraw, - uint32 _oldLockingPeriod - ) private { - uint256 previousIndexerTokens = token.balanceOf(users.indexer); - uint256 previousIndexerIdleStake = staking.getIdleStake(users.indexer); - - vm.expectEmit(address(staking)); - uint256 lockingPeriod = block.number + THAWING_PERIOD_IN_BLOCKS; - if (_tokensStillThawing > 0) { - lockingPeriod = block.number + MathUtils.weightedAverageRoundingUp( - MathUtils.diffOrZero(_oldLockingPeriod, block.number), - _tokensStillThawing, - THAWING_PERIOD_IN_BLOCKS, - _tokens - ); - } - emit IHorizonStakingMain.StakeLocked(users.indexer, _tokens + _tokensStillThawing, lockingPeriod); - staking.unstake(_tokens); - - uint256 idleStake = staking.getIdleStake(users.indexer); - assertEq(idleStake, previousIndexerIdleStake - _tokens); - - uint256 newIndexerBalance = token.balanceOf(users.indexer); - assertEq(newIndexerBalance - previousIndexerTokens, _tokensToWithdraw); - } - - function _storeDeprecatedThawingPeriod(uint32 _thawingPeriod) private { - uint256 slot = 13; - bytes32 value = bytes32(uint256(_thawingPeriod)); - vm.store(address(staking), bytes32(slot), value); - } - /* * TESTS */ @@ -69,44 +15,60 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { uint256 tokensToUnstake, uint32 maxVerifierCut, uint64 thawingPeriod - ) - public - useIndexer - useProvision(tokens, maxVerifierCut, thawingPeriod) - { + ) public useIndexer useProvision(tokens, maxVerifierCut, thawingPeriod) { tokensToUnstake = bound(tokensToUnstake, 1, tokens); - _createThawRequest(tokens); + + // thaw, wait and deprovision + _thaw(users.indexer, subgraphDataServiceAddress, tokens); skip(thawingPeriod + 1); - _deprovision(0); - _unstakeTokens(tokensToUnstake); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); + + _unstake(tokensToUnstake); + } + + function testUnstake_LockingPeriodGreaterThanZero_NoThawing( + uint256 tokens, + uint256 tokensToUnstake, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) public useIndexer useProvision(tokens, maxVerifierCut, thawingPeriod) { + tokensToUnstake = bound(tokensToUnstake, 1, tokens); + + // simulate transition period + _setStorage_DeprecatedThawingPeriod(THAWING_PERIOD_IN_BLOCKS); + + // thaw, wait and deprovision + _thaw(users.indexer, subgraphDataServiceAddress, tokens); + skip(thawingPeriod + 1); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); + + // unstake + _unstake(tokensToUnstake); } function testUnstake_LockingPeriodGreaterThanZero_TokensDoneThawing( uint256 tokens, uint256 tokensToUnstake, uint256 tokensLocked - ) - public - useIndexer - { + ) public useIndexer { // bounds tokens = bound(tokens, 1, MAX_STAKING_TOKENS); tokensToUnstake = bound(tokensToUnstake, 1, tokens); tokensLocked = bound(tokensLocked, 1, MAX_STAKING_TOKENS); - // vm.store to simulate locked tokens with past locking period - _storeDeprecatedThawingPeriod(THAWING_PERIOD_IN_BLOCKS); + // simulate locked tokens with past locking period + _setStorage_DeprecatedThawingPeriod(THAWING_PERIOD_IN_BLOCKS); token.transfer(address(staking), tokensLocked); - _storeServiceProvider(users.indexer, tokensLocked, 0, tokensLocked, block.number, 0); + _setStorage_ServiceProvider(users.indexer, tokensLocked, 0, tokensLocked, block.number, 0); - // create provision, thaw request and deprovision - _createProvision(subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); - _createThawRequest(tokens); + // create provision, thaw and deprovision + _createProvision(users.indexer, subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); + _thaw(users.indexer, subgraphDataServiceAddress, tokens); skip(MAX_THAWING_PERIOD + 1); - _deprovision(0); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); // unstake - _unstakeDuringLockingPeriod(tokensToUnstake, 0, tokensLocked, 0); + _unstake(tokensToUnstake); } function testUnstake_LockingPeriodGreaterThanZero_TokensStillThawing( @@ -114,10 +76,7 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { uint256 tokensToUnstake, uint256 tokensThawing, uint32 tokensThawingUntilBlock - ) - public - useIndexer - { + ) public useIndexer { // bounds tokens = bound(tokens, 1, MAX_STAKING_TOKENS); tokensToUnstake = bound(tokensToUnstake, 1, tokens); @@ -125,19 +84,19 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { vm.assume(tokensThawingUntilBlock > block.number); vm.assume(tokensThawingUntilBlock < block.number + THAWING_PERIOD_IN_BLOCKS); - // vm.store to simulate locked tokens still thawing - _storeDeprecatedThawingPeriod(THAWING_PERIOD_IN_BLOCKS); + // simulate locked tokens still thawing + _setStorage_DeprecatedThawingPeriod(THAWING_PERIOD_IN_BLOCKS); token.transfer(address(staking), tokensThawing); - _storeServiceProvider(users.indexer, tokensThawing, 0, tokensThawing, tokensThawingUntilBlock, 0); + _setStorage_ServiceProvider(users.indexer, tokensThawing, 0, tokensThawing, tokensThawingUntilBlock, 0); - // create provision, thaw request and deprovision - _createProvision(subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); - _createThawRequest(tokens); + // create provision, thaw and deprovision + _createProvision(users.indexer, subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); + _thaw(users.indexer, subgraphDataServiceAddress, tokens); skip(MAX_THAWING_PERIOD + 1); - _deprovision(0); + _deprovision(users.indexer, subgraphDataServiceAddress, 0); // unstake - _unstakeDuringLockingPeriod(tokensToUnstake, tokensThawing, 0, tokensThawingUntilBlock); + _unstake(tokensToUnstake); } function testUnstake_RevertWhen_ZeroTokens( @@ -159,11 +118,7 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod - ) - public - useIndexer - useProvision(amount, maxVerifierCut, thawingPeriod) - { + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { bytes memory expectedError = abi.encodeWithSignature( "HorizonStakingInsufficientIdleStake(uint256,uint256)", amount, @@ -177,12 +132,8 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { uint256 amount, uint32 maxVerifierCut, uint64 thawingPeriod - ) - public - useIndexer - useProvision(amount, maxVerifierCut, thawingPeriod) - useThawRequest(amount) - { + ) public useIndexer useProvision(amount, maxVerifierCut, thawingPeriod) { + _thaw(users.indexer, subgraphDataServiceAddress, amount); skip(thawingPeriod + 1); bytes memory expectedError = abi.encodeWithSignature( @@ -193,4 +144,4 @@ contract HorizonStakingUnstakeTest is HorizonStakingTest { vm.expectRevert(expectedError); staking.unstake(amount); } -} \ No newline at end of file +} diff --git a/packages/horizon/test/staking/stake/withdraw.t.sol b/packages/horizon/test/staking/stake/withdraw.t.sol index 73a96b542..b28dea022 100644 --- a/packages/horizon/test/staking/stake/withdraw.t.sol +++ b/packages/horizon/test/staking/stake/withdraw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -8,52 +8,48 @@ import { IHorizonStakingMain } from "../../../contracts/interfaces/internal/IHor import { HorizonStakingTest } from "../HorizonStaking.t.sol"; contract HorizonStakingWithdrawTest is HorizonStakingTest { - - /* - * HELPERS - */ - - function _withdrawLockedTokens(uint256 tokens) private { - uint256 previousIndexerTokens = token.balanceOf(users.indexer); - vm.expectEmit(address(staking)); - emit IHorizonStakingMain.StakeWithdrawn(users.indexer, tokens); - staking.withdraw(); - uint256 newIndexerBalance = token.balanceOf(users.indexer); - assertEq(newIndexerBalance - previousIndexerTokens, tokens); - } - /* * TESTS */ function testWithdraw_Tokens(uint256 tokens, uint256 tokensLocked) public useIndexer { - vm.assume(tokens > 0); + tokens = bound(tokens, 1, MAX_STAKING_TOKENS); tokensLocked = bound(tokensLocked, 1, tokens); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, MAX_THAWING_PERIOD); - _storeServiceProvider(users.indexer, tokens, 0, tokensLocked, block.timestamp, 0); - _withdrawLockedTokens(tokensLocked); + + // simulate locked tokens ready to withdraw + token.transfer(address(staking), tokens); + _setStorage_ServiceProvider(users.indexer, tokens, 0, tokensLocked, block.number, 0); + + _createProvision(users.indexer, subgraphDataServiceAddress, tokens, 0, MAX_THAWING_PERIOD); + + _withdraw(); } function testWithdraw_RevertWhen_ZeroTokens(uint256 tokens) public useIndexer { - vm.assume(tokens > 0); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, MAX_THAWING_PERIOD); - _storeServiceProvider(users.indexer, tokens, 0, 0, block.timestamp, 0); - vm.expectRevert(abi.encodeWithSelector( - IHorizonStakingMain.HorizonStakingInvalidZeroTokens.selector - )); + tokens = bound(tokens, 1, MAX_STAKING_TOKENS); + + // simulate zero locked tokens + token.transfer(address(staking), tokens); + _setStorage_ServiceProvider(users.indexer, tokens, 0, 0, 0, 0); + + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, tokens, 0, MAX_THAWING_PERIOD); + + vm.expectRevert(abi.encodeWithSelector(IHorizonStakingMain.HorizonStakingInvalidZeroTokens.selector)); staking.withdraw(); } function testWithdraw_RevertWhen_StillThawing(uint256 tokens, uint256 tokensLocked) public useIndexer { - vm.assume(tokens > 0); + tokens = bound(tokens, 1, MAX_STAKING_TOKENS); tokensLocked = bound(tokensLocked, 1, tokens); - _createProvision(subgraphDataServiceLegacyAddress, tokens, 0, MAX_THAWING_PERIOD); + + // simulate locked tokens still thawing uint256 thawUntil = block.timestamp + 1; - _storeServiceProvider(users.indexer, tokens, 0, tokensLocked, thawUntil, 0); - vm.expectRevert(abi.encodeWithSelector( - IHorizonStakingMain.HorizonStakingStillThawing.selector, - thawUntil - )); + token.transfer(address(staking), tokens); + _setStorage_ServiceProvider(users.indexer, tokens, 0, tokensLocked, thawUntil, 0); + + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, tokens, 0, MAX_THAWING_PERIOD); + + vm.expectRevert(abi.encodeWithSelector(IHorizonStakingMain.HorizonStakingStillThawing.selector, thawUntil)); staking.withdraw(); } -} \ No newline at end of file +} diff --git a/packages/horizon/test/staking/transfer-tools/ttools.t.sol b/packages/horizon/test/staking/transfer-tools/ttools.t.sol index c41731e10..11bb6e62d 100644 --- a/packages/horizon/test/staking/transfer-tools/ttools.t.sol +++ b/packages/horizon/test/staking/transfer-tools/ttools.t.sol @@ -1,16 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +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"; -import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; -import { IHorizonStakingExtension } from "../../../contracts/interfaces/internal/IHorizonStakingExtension.sol"; contract HorizonStakingTransferToolsTest is HorizonStakingTest { - event Transfer(address indexed from, address indexed to, uint tokens); - /* * TESTS */ @@ -69,10 +65,10 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 resetPrank(users.indexer); - _createProvision(subgraphDataServiceLegacyAddress, 100 ether, 0, 0); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 0); resetPrank(users.delegator); - _delegateLegacy(users.indexer, 1 ether); + _delegate(users.indexer, 1 ether); // send amount to staking contract - this should be done by the bridge resetPrank(users.delegator); @@ -92,10 +88,10 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 resetPrank(users.indexer); - _createProvision(subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); resetPrank(users.delegator); - _delegateLegacy(users.indexer, originalDelegationAmount); + _delegate(users.indexer, originalDelegationAmount); // send amount to staking contract - this should be done by the bridge resetPrank(users.delegator); @@ -103,7 +99,7 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // thaw some delegation before receiving new delegation from L1 resetPrank(users.delegator); - _undelegateLegacy(users.indexer, originalDelegationAmount / 10); + _undelegate(users.indexer, originalDelegationAmount / 10); resetPrank(graphTokenGatewayAddress); bytes memory data = abi.encode( @@ -120,11 +116,11 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 resetPrank(users.indexer); - _createProvision(subgraphDataServiceLegacyAddress, provisionSize, 0, 1 days); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, provisionSize, 0, 1 days); // initialize the delegation pool resetPrank(users.delegator); - _delegateLegacy(users.indexer, originalDelegationAmount); + _delegate(users.indexer, originalDelegationAmount); // slash the entire provision resetPrank(subgraphDataServiceLegacyAddress); @@ -147,10 +143,10 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 resetPrank(users.indexer); - _createProvision(subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); + _createProvision(users.indexer, subgraphDataServiceLegacyAddress, 100 ether, 0, 1 days); resetPrank(users.delegator); - _delegateLegacy(users.indexer, amountDelegated); + _delegate(users.indexer, amountDelegated); // send amount to staking contract - this should be done by the bridge resetPrank(users.delegator); @@ -158,7 +154,7 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { // thaw all delegation before receiving new delegation from L1 resetPrank(users.delegator); - _undelegateLegacy(users.indexer, amountDelegated); + _undelegate(users.indexer, amountDelegated); resetPrank(graphTokenGatewayAddress); bytes memory data = abi.encode( @@ -166,89 +162,5 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { abi.encode(users.indexer, users.delegator) ); _onTokenTransfer_ReceiveDelegation(counterpartStaking, amountReceived, data); - } - - /** - * HELPERS - */ - - function _onTokenTransfer_ReceiveDelegation(address from, uint256 tokens, bytes memory data) internal { - (, bytes memory fnData) = abi.decode(data, (uint8, bytes)); - (address serviceProvider, address delegator) = abi.decode(fnData, (address, address)); - bytes32 slotPoolTokens = bytes32(uint256(keccak256(abi.encode(serviceProvider, 20))) + 2); - - // before - DelegationPool memory beforePool = staking.getDelegationPool(serviceProvider, subgraphDataServiceLegacyAddress); - Delegation memory beforeDelegation = staking.getDelegation( - serviceProvider, - subgraphDataServiceLegacyAddress, - delegator - ); - uint256 beforeStoragePoolTokens = uint256(vm.load(address(staking), slotPoolTokens)); - uint256 beforeDelegatedTokens = staking.getDelegatedTokensAvailable( - serviceProvider, - subgraphDataServiceLegacyAddress - ); - uint256 beforeDelegatorBalance = token.balanceOf(delegator); - uint256 beforeStakingBalance = token.balanceOf(address(staking)); - uint256 calcShares = (beforePool.tokens == 0 || beforePool.tokens == beforePool.tokensThawing) - ? tokens - : ((tokens * beforePool.shares) / (beforePool.tokens - beforePool.tokensThawing)); - - bool earlyExit = (calcShares == 0 || tokens < 1 ether) || - (beforePool.tokens == 0 && (beforePool.shares != 0 || beforePool.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 - DelegationPool memory afterPool = staking.getDelegationPool(serviceProvider, subgraphDataServiceLegacyAddress); - Delegation memory afterDelegation = staking.getDelegation( - serviceProvider, - subgraphDataServiceLegacyAddress, - delegator - ); - uint256 afterStoragePoolTokens = uint256(vm.load(address(staking), slotPoolTokens)); - uint256 afterDelegatedTokens = staking.getDelegatedTokensAvailable( - serviceProvider, - subgraphDataServiceLegacyAddress - ); - uint256 afterDelegatorBalance = token.balanceOf(delegator); - uint256 afterStakingBalance = token.balanceOf(address(staking)); - - uint256 deltaShares = afterDelegation.shares - beforeDelegation.shares; - - // assertions - if (earlyExit) { - assertEq(beforePool.tokens, afterPool.tokens); - assertEq(beforePool.shares, afterPool.shares); - assertEq(beforePool.tokensThawing, afterPool.tokensThawing); - assertEq(beforePool.sharesThawing, afterPool.sharesThawing); - assertEq(0, deltaShares); - assertEq(beforeDelegatedTokens, afterDelegatedTokens); - assertEq(beforeStoragePoolTokens, afterStoragePoolTokens); - assertEq(beforeDelegatorBalance + tokens, afterDelegatorBalance); - assertEq(beforeStakingBalance - tokens, afterStakingBalance); - } else { - assertEq(beforePool.tokens + tokens, afterPool.tokens); - assertEq(beforePool.shares + calcShares, afterPool.shares); - assertEq(beforePool.tokensThawing, afterPool.tokensThawing); - assertEq(beforePool.sharesThawing, afterPool.sharesThawing); - assertEq(calcShares, deltaShares); - assertEq(beforeDelegatedTokens + tokens, afterDelegatedTokens); - // Ensure correct slot is being updated, pools are stored in different storage locations for legacy subgraph data service - assertEq(beforeStoragePoolTokens + tokens, afterStoragePoolTokens); - assertEq(beforeDelegatorBalance, afterDelegatorBalance); - assertEq(beforeStakingBalance, afterStakingBalance); - } - } + } } diff --git a/packages/horizon/test/utilities/GraphDirectory.t.sol b/packages/horizon/test/utilities/GraphDirectory.t.sol index 35ab2a291..ece4afe65 100644 --- a/packages/horizon/test/utilities/GraphDirectory.t.sol +++ b/packages/horizon/test/utilities/GraphDirectory.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; diff --git a/packages/horizon/test/utilities/GraphDirectoryImplementation.sol b/packages/horizon/test/utilities/GraphDirectoryImplementation.sol index 57780f450..1095adc3a 100644 --- a/packages/horizon/test/utilities/GraphDirectoryImplementation.sol +++ b/packages/horizon/test/utilities/GraphDirectoryImplementation.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IHorizonStaking } from "../../contracts/interfaces/IHorizonStaking.sol"; diff --git a/packages/horizon/test/utils/Constants.sol b/packages/horizon/test/utils/Constants.sol index 29daaaa96..f08fa4ec1 100644 --- a/packages/horizon/test/utils/Constants.sol +++ b/packages/horizon/test/utils/Constants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; abstract contract Constants { uint256 internal constant MAX_PPM = 1000000; // 100% in parts per million diff --git a/packages/horizon/test/utils/Users.sol b/packages/horizon/test/utils/Users.sol index 3329d69da..b26329f91 100644 --- a/packages/horizon/test/utils/Users.sol +++ b/packages/horizon/test/utils/Users.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; struct Users { address governor; diff --git a/packages/horizon/test/utils/Utils.sol b/packages/horizon/test/utils/Utils.sol index 47e4e68df..5aae4de3f 100644 --- a/packages/horizon/test/utils/Utils.sol +++ b/packages/horizon/test/utils/Utils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/contracts/DisputeManager.sol b/packages/subgraph-service/contracts/DisputeManager.sol index a9afcff28..f48c20463 100644 --- a/packages/subgraph-service/contracts/DisputeManager.sol +++ b/packages/subgraph-service/contracts/DisputeManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; import { IHorizonStaking } from "@graphprotocol/horizon/contracts/interfaces/IHorizonStaking.sol"; diff --git a/packages/subgraph-service/contracts/DisputeManagerStorage.sol b/packages/subgraph-service/contracts/DisputeManagerStorage.sol index 8ff0685bf..a8790230d 100644 --- a/packages/subgraph-service/contracts/DisputeManagerStorage.sol +++ b/packages/subgraph-service/contracts/DisputeManagerStorage.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDisputeManager } from "./interfaces/IDisputeManager.sol"; import { ISubgraphService } from "./interfaces/ISubgraphService.sol"; diff --git a/packages/subgraph-service/contracts/SubgraphService.sol b/packages/subgraph-service/contracts/SubgraphService.sol index 51cb889f7..5023e6160 100644 --- a/packages/subgraph-service/contracts/SubgraphService.sol +++ b/packages/subgraph-service/contracts/SubgraphService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol"; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; diff --git a/packages/subgraph-service/contracts/SubgraphServiceStorage.sol b/packages/subgraph-service/contracts/SubgraphServiceStorage.sol index 54de429f1..e953dfba0 100644 --- a/packages/subgraph-service/contracts/SubgraphServiceStorage.sol +++ b/packages/subgraph-service/contracts/SubgraphServiceStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { ISubgraphService } from "./interfaces/ISubgraphService.sol"; diff --git a/packages/subgraph-service/contracts/interfaces/IDisputeManager.sol b/packages/subgraph-service/contracts/interfaces/IDisputeManager.sol index 73f7e56d6..95511ecc2 100644 --- a/packages/subgraph-service/contracts/interfaces/IDisputeManager.sol +++ b/packages/subgraph-service/contracts/interfaces/IDisputeManager.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { Attestation } from "../libraries/Attestation.sol"; diff --git a/packages/subgraph-service/contracts/interfaces/ISubgraphService.sol b/packages/subgraph-service/contracts/interfaces/ISubgraphService.sol index e993c5159..e12554303 100644 --- a/packages/subgraph-service/contracts/interfaces/ISubgraphService.sol +++ b/packages/subgraph-service/contracts/interfaces/ISubgraphService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDataServiceFees } from "@graphprotocol/horizon/contracts/data-service/interfaces/IDataServiceFees.sol"; import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol"; diff --git a/packages/subgraph-service/contracts/libraries/Allocation.sol b/packages/subgraph-service/contracts/libraries/Allocation.sol index e039b75bd..a55034f17 100644 --- a/packages/subgraph-service/contracts/libraries/Allocation.sol +++ b/packages/subgraph-service/contracts/libraries/Allocation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; diff --git a/packages/subgraph-service/contracts/libraries/Attestation.sol b/packages/subgraph-service/contracts/libraries/Attestation.sol index 0211db674..66a3cb5fb 100644 --- a/packages/subgraph-service/contracts/libraries/Attestation.sol +++ b/packages/subgraph-service/contracts/libraries/Attestation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title Attestation library diff --git a/packages/subgraph-service/contracts/libraries/LegacyAllocation.sol b/packages/subgraph-service/contracts/libraries/LegacyAllocation.sol index 81e6bcdcb..83bef8037 100644 --- a/packages/subgraph-service/contracts/libraries/LegacyAllocation.sol +++ b/packages/subgraph-service/contracts/libraries/LegacyAllocation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; /** * @title LegacyAllocation library diff --git a/packages/subgraph-service/contracts/utilities/AllocationManager.sol b/packages/subgraph-service/contracts/utilities/AllocationManager.sol index 1a9c86928..2e40f28d3 100644 --- a/packages/subgraph-service/contracts/utilities/AllocationManager.sol +++ b/packages/subgraph-service/contracts/utilities/AllocationManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol"; import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; diff --git a/packages/subgraph-service/contracts/utilities/AllocationManagerStorage.sol b/packages/subgraph-service/contracts/utilities/AllocationManagerStorage.sol index da812f5c1..f11e18d1b 100644 --- a/packages/subgraph-service/contracts/utilities/AllocationManagerStorage.sol +++ b/packages/subgraph-service/contracts/utilities/AllocationManagerStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { Allocation } from "../libraries/Allocation.sol"; import { LegacyAllocation } from "../libraries/LegacyAllocation.sol"; diff --git a/packages/subgraph-service/contracts/utilities/AttestationManager.sol b/packages/subgraph-service/contracts/utilities/AttestationManager.sol index 0d8405917..97c55ab8b 100644 --- a/packages/subgraph-service/contracts/utilities/AttestationManager.sol +++ b/packages/subgraph-service/contracts/utilities/AttestationManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { AttestationManagerV1Storage } from "./AttestationManagerStorage.sol"; diff --git a/packages/subgraph-service/contracts/utilities/AttestationManagerStorage.sol b/packages/subgraph-service/contracts/utilities/AttestationManagerStorage.sol index 5bf57af65..5f86a19f3 100644 --- a/packages/subgraph-service/contracts/utilities/AttestationManagerStorage.sol +++ b/packages/subgraph-service/contracts/utilities/AttestationManagerStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; abstract contract AttestationManagerV1Storage { /// @dev EIP712 domain separator diff --git a/packages/subgraph-service/contracts/utilities/Directory.sol b/packages/subgraph-service/contracts/utilities/Directory.sol index 920a0ae5e..53de7c6e7 100644 --- a/packages/subgraph-service/contracts/utilities/Directory.sol +++ b/packages/subgraph-service/contracts/utilities/Directory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; import { IDisputeManager } from "../interfaces/IDisputeManager.sol"; import { ISubgraphService } from "../interfaces/ISubgraphService.sol"; diff --git a/packages/subgraph-service/foundry.toml b/packages/subgraph-service/foundry.toml index 235a58eb9..9f5c3a92a 100644 --- a/packages/subgraph-service/foundry.toml +++ b/packages/subgraph-service/foundry.toml @@ -4,14 +4,6 @@ out = 'build' libs = ['node_modules', 'lib'] test = 'test' cache_path = 'cache_forge' +fs_permissions = [{ access = "read", path = "./"}] optimizer = true optimizer-runs = 200 -via_ir = true -fs_permissions = [{ access = "read", path = "./"}] - -[profile.lite] -optimizer = false -optimizer-runs = 1 - -[profile.lite.optimizer_details.yulDetails] -optimizerSteps = ':' \ No newline at end of file diff --git a/packages/subgraph-service/hardhat.config.ts b/packages/subgraph-service/hardhat.config.ts index a8e05c779..17ebc2053 100644 --- a/packages/subgraph-service/hardhat.config.ts +++ b/packages/subgraph-service/hardhat.config.ts @@ -8,7 +8,7 @@ import { HardhatUserConfig } from 'hardhat/config' const config: HardhatUserConfig = { solidity: { - version: '0.8.26', + version: '0.8.27', settings: { viaIR: true, optimizer: { diff --git a/packages/subgraph-service/package.json b/packages/subgraph-service/package.json index da7b33a3d..bb5dd9ea9 100644 --- a/packages/subgraph-service/package.json +++ b/packages/subgraph-service/package.json @@ -10,7 +10,7 @@ "lint": "yarn lint:ts && yarn lint:sol", "clean": "rm -rf build cache typechain-types", "build": "forge build && hardhat compile", - "test": "FOUNDRY_PROFILE=lite forge test -vvvv && hardhat test" + "test": "forge test && hardhat test" }, "devDependencies": { "@graphprotocol/contracts": "workspace:^7.0.0", diff --git a/packages/subgraph-service/test/SubgraphBaseTest.t.sol b/packages/subgraph-service/test/SubgraphBaseTest.t.sol index 2fe5ccf64..d42b66e29 100644 --- a/packages/subgraph-service/test/SubgraphBaseTest.t.sol +++ b/packages/subgraph-service/test/SubgraphBaseTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/DisputeManager.t.sol b/packages/subgraph-service/test/disputeManager/DisputeManager.t.sol index 1077c1435..0d99aec0b 100644 --- a/packages/subgraph-service/test/disputeManager/DisputeManager.t.sol +++ b/packages/subgraph-service/test/disputeManager/DisputeManager.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -166,54 +166,67 @@ contract DisputeManagerTest is SubgraphServiceSharedTest { return _disputeID; } + struct BeforeValues_CreateQueryDisputeConflict { + Attestation.State attestation1; + Attestation.State attestation2; + address indexer1; + address indexer2; + uint256 stakeSnapshot1; + uint256 stakeSnapshot2; + } function _createQueryDisputeConflict( bytes memory attestationData1, bytes memory attestationData2 ) internal returns (bytes32, bytes32) { (, address fisherman,) = vm.readCallers(); - Attestation.State memory attestation1 = Attestation.parse(attestationData1); - Attestation.State memory attestation2 = Attestation.parse(attestationData2); - address indexer1 = disputeManager.getAttestationIndexer(attestation1); - address indexer2 = disputeManager.getAttestationIndexer(attestation2); + + BeforeValues_CreateQueryDisputeConflict memory beforeValues; + beforeValues.attestation1 = Attestation.parse(attestationData1); + beforeValues.attestation2 = Attestation.parse(attestationData2); + beforeValues.indexer1 = disputeManager.getAttestationIndexer(beforeValues.attestation1); + beforeValues.indexer2 = disputeManager.getAttestationIndexer(beforeValues.attestation2); + beforeValues.stakeSnapshot1 = disputeManager.getStakeSnapshot(beforeValues.indexer1); + beforeValues.stakeSnapshot2 = disputeManager.getStakeSnapshot(beforeValues.indexer2); + bytes32 expectedDisputeId1 = keccak256( abi.encodePacked( - attestation1.requestCID, - attestation1.responseCID, - attestation1.subgraphDeploymentId, - indexer1, + beforeValues.attestation1.requestCID, + beforeValues.attestation1.responseCID, + beforeValues.attestation1.subgraphDeploymentId, + beforeValues.indexer1, fisherman ) ); bytes32 expectedDisputeId2 = keccak256( abi.encodePacked( - attestation2.requestCID, - attestation2.responseCID, - attestation2.subgraphDeploymentId, - indexer2, + beforeValues.attestation2.requestCID, + beforeValues.attestation2.responseCID, + beforeValues.attestation2.subgraphDeploymentId, + beforeValues.indexer2, fisherman ) ); - uint256 stakeSnapshot1 = disputeManager.getStakeSnapshot(indexer1); - uint256 stakeSnapshot2 = disputeManager.getStakeSnapshot(indexer2); + // createQueryDisputeConflict vm.expectEmit(address(disputeManager)); emit IDisputeManager.QueryDisputeCreated( expectedDisputeId1, - indexer1, + beforeValues.indexer1, fisherman, 0, - attestation1.subgraphDeploymentId, + beforeValues.attestation1.subgraphDeploymentId, attestationData1, - stakeSnapshot1 + beforeValues.stakeSnapshot1 ); + vm.expectEmit(address(disputeManager)); emit IDisputeManager.QueryDisputeCreated( expectedDisputeId2, - indexer2, + beforeValues.indexer2, fisherman, 0, - attestation2.subgraphDeploymentId, + beforeValues.attestation2.subgraphDeploymentId, attestationData2, - stakeSnapshot2 + beforeValues.stakeSnapshot2 ); (bytes32 _disputeId1, bytes32 _disputeId2) = disputeManager.createQueryDisputeConflict(attestationData1, attestationData2); @@ -226,24 +239,24 @@ contract DisputeManagerTest is SubgraphServiceSharedTest { // Check dispute values IDisputeManager.Dispute memory dispute1 = _getDispute(_disputeId1); - assertEq(dispute1.indexer, indexer1, "Indexer 1 should match"); + assertEq(dispute1.indexer, beforeValues.indexer1, "Indexer 1 should match"); assertEq(dispute1.fisherman, fisherman, "Fisherman 1 should match"); assertEq(dispute1.deposit, 0, "Deposit 1 should match"); assertEq(dispute1.relatedDisputeId, _disputeId2, "Related dispute ID 1 should be the id of the other dispute"); assertEq(uint8(dispute1.disputeType), uint8(IDisputeManager.DisputeType.QueryDispute), "Dispute type 1 should be query"); assertEq(uint8(dispute1.status), uint8(IDisputeManager.DisputeStatus.Pending), "Dispute status 1 should be pending"); assertEq(dispute1.createdAt, block.timestamp, "Created at 1 should match"); - assertEq(dispute1.stakeSnapshot, stakeSnapshot1, "Stake snapshot 1 should match"); + assertEq(dispute1.stakeSnapshot, beforeValues.stakeSnapshot1, "Stake snapshot 1 should match"); IDisputeManager.Dispute memory dispute2 = _getDispute(_disputeId2); - assertEq(dispute2.indexer, indexer2, "Indexer 2 should match"); + assertEq(dispute2.indexer, beforeValues.indexer2, "Indexer 2 should match"); assertEq(dispute2.fisherman, fisherman, "Fisherman 2 should match"); assertEq(dispute2.deposit, 0, "Deposit 2 should match"); assertEq(dispute2.relatedDisputeId, _disputeId1, "Related dispute ID 2 should be the id of the other dispute"); assertEq(uint8(dispute2.disputeType), uint8(IDisputeManager.DisputeType.QueryDispute), "Dispute type 2 should be query"); assertEq(uint8(dispute2.status), uint8(IDisputeManager.DisputeStatus.Pending), "Dispute status 2 should be pending"); assertEq(dispute2.createdAt, block.timestamp, "Created at 2 should match"); - assertEq(dispute2.stakeSnapshot, stakeSnapshot2, "Stake snapshot 2 should match"); + assertEq(dispute2.stakeSnapshot, beforeValues.stakeSnapshot2, "Stake snapshot 2 should match"); return (_disputeId1, _disputeId2); } diff --git a/packages/subgraph-service/test/disputeManager/constructor/constructor.t.sol b/packages/subgraph-service/test/disputeManager/constructor/constructor.t.sol index 76f90481a..c9166719f 100644 --- a/packages/subgraph-service/test/disputeManager/constructor/constructor.t.sol +++ b/packages/subgraph-service/test/disputeManager/constructor/constructor.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/disputes.t.sol b/packages/subgraph-service/test/disputeManager/disputes/disputes.t.sol index cd229dbc4..b6b4a6890 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/disputes.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/disputes.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/indexing/accept.t.sol b/packages/subgraph-service/test/disputeManager/disputes/indexing/accept.t.sol index 5e0d36132..49bee9e26 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/indexing/accept.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/indexing/accept.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/indexing/cancel.t.sol b/packages/subgraph-service/test/disputeManager/disputes/indexing/cancel.t.sol index 0a09ad7f4..1639f2cad 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/indexing/cancel.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/indexing/cancel.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/indexing/create.t.sol b/packages/subgraph-service/test/disputeManager/disputes/indexing/create.t.sol index 0174315a2..8d1b75c21 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/indexing/create.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/indexing/create.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/indexing/draw.t.sol b/packages/subgraph-service/test/disputeManager/disputes/indexing/draw.t.sol index 3df590f74..7ce70d962 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/indexing/draw.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/indexing/draw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/indexing/reject.t.sol b/packages/subgraph-service/test/disputeManager/disputes/indexing/reject.t.sol index 5401c0afe..41b5adabc 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/indexing/reject.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/indexing/reject.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/query/accept.t.sol b/packages/subgraph-service/test/disputeManager/disputes/query/accept.t.sol index d0f00234d..7670d381e 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/query/accept.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/query/accept.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/query/cancel.t.sol b/packages/subgraph-service/test/disputeManager/disputes/query/cancel.t.sol index a61db6ad9..5a70e2f70 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/query/cancel.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/query/cancel.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/query/create.t.sol b/packages/subgraph-service/test/disputeManager/disputes/query/create.t.sol index 01f27cf66..4eba11744 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/query/create.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/query/create.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/query/draw.t.sol b/packages/subgraph-service/test/disputeManager/disputes/query/draw.t.sol index 1b9c96ad2..2ff182378 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/query/draw.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/query/draw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/query/reject.t.sol b/packages/subgraph-service/test/disputeManager/disputes/query/reject.t.sol index 07c738a90..fff210ea6 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/query/reject.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/query/reject.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/accept.t.sol b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/accept.t.sol index 1cfde0a8c..46a6d4bdd 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/accept.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/accept.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/cancel.t.sol b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/cancel.t.sol index 4de2b3155..36d14fab2 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/cancel.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/cancel.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/create.t.sol b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/create.t.sol index 2f54983ae..edd2d545b 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/create.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/create.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/draw.t.sol b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/draw.t.sol index 5127b56a5..5444936cc 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/draw.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/draw.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/reject.t.sol b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/reject.t.sol index 078969267..ff347f7d2 100644 --- a/packages/subgraph-service/test/disputeManager/disputes/queryConflict/reject.t.sol +++ b/packages/subgraph-service/test/disputeManager/disputes/queryConflict/reject.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/governance/arbitrator.t.sol b/packages/subgraph-service/test/disputeManager/governance/arbitrator.t.sol index 53e36a497..1277235fd 100644 --- a/packages/subgraph-service/test/disputeManager/governance/arbitrator.t.sol +++ b/packages/subgraph-service/test/disputeManager/governance/arbitrator.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/governance/disputeDeposit.t.sol b/packages/subgraph-service/test/disputeManager/governance/disputeDeposit.t.sol index 163157886..1df13acb2 100644 --- a/packages/subgraph-service/test/disputeManager/governance/disputeDeposit.t.sol +++ b/packages/subgraph-service/test/disputeManager/governance/disputeDeposit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/governance/fishermanRewardCut.t.sol b/packages/subgraph-service/test/disputeManager/governance/fishermanRewardCut.t.sol index 5a09bdd03..3eddddc37 100644 --- a/packages/subgraph-service/test/disputeManager/governance/fishermanRewardCut.t.sol +++ b/packages/subgraph-service/test/disputeManager/governance/fishermanRewardCut.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/disputeManager/governance/maxSlashingCut.t.sol b/packages/subgraph-service/test/disputeManager/governance/maxSlashingCut.t.sol index 343e514bc..a554f774c 100644 --- a/packages/subgraph-service/test/disputeManager/governance/maxSlashingCut.t.sol +++ b/packages/subgraph-service/test/disputeManager/governance/maxSlashingCut.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/mocks/MockCuration.sol b/packages/subgraph-service/test/mocks/MockCuration.sol index 68b4a8ce9..e0158a99e 100644 --- a/packages/subgraph-service/test/mocks/MockCuration.sol +++ b/packages/subgraph-service/test/mocks/MockCuration.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.26; +pragma solidity 0.8.27; contract MockCuration { function isCurated(bytes32) public pure returns (bool) { diff --git a/packages/subgraph-service/test/mocks/MockGRTToken.sol b/packages/subgraph-service/test/mocks/MockGRTToken.sol index d581f8299..7d21fd00a 100644 --- a/packages/subgraph-service/test/mocks/MockGRTToken.sol +++ b/packages/subgraph-service/test/mocks/MockGRTToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; diff --git a/packages/subgraph-service/test/mocks/MockRewardsManager.sol b/packages/subgraph-service/test/mocks/MockRewardsManager.sol index 59ca5577e..363fbf902 100644 --- a/packages/subgraph-service/test/mocks/MockRewardsManager.sol +++ b/packages/subgraph-service/test/mocks/MockRewardsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/shared/HorizonStakingShared.t.sol b/packages/subgraph-service/test/shared/HorizonStakingShared.t.sol index 28a22e1a2..e07516903 100644 --- a/packages/subgraph-service/test/shared/HorizonStakingShared.t.sol +++ b/packages/subgraph-service/test/shared/HorizonStakingShared.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol b/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol index 1067acf14..089fdd9a9 100644 --- a/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol +++ b/packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/SubgraphService.t.sol b/packages/subgraph-service/test/subgraphService/SubgraphService.t.sol index 12d20581f..05c038680 100644 --- a/packages/subgraph-service/test/subgraphService/SubgraphService.t.sol +++ b/packages/subgraph-service/test/subgraphService/SubgraphService.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; @@ -30,7 +30,7 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { * MODIFIERS */ - modifier useOperator { + modifier useOperator() { resetPrank(users.indexer); staking.setOperator(users.operator, address(subgraphService), true); resetPrank(users.operator); @@ -38,7 +38,7 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { vm.stopPrank(); } - modifier useRewardsDestination { + modifier useRewardsDestination() { _setRewardsDestination(users.rewardsDestination); _; } @@ -74,10 +74,10 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { uint64 thawingPeriod = provision.thawingPeriod; uint32 maxVerifierCutPending = provision.maxVerifierCutPending; uint64 thawingPeriodPending = provision.thawingPeriodPending; - + vm.expectEmit(address(subgraphService)); emit IDataService.ProvisionAccepted(_indexer); - + // Accept provision subgraphService.acceptProvision(_indexer, _data); @@ -110,7 +110,13 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { } vm.expectEmit(address(subgraphService)); - emit AllocationManager.AllocationResized(_indexer, _allocationId, subgraphDeploymentId, _tokens, beforeSubgraphAllocatedTokens); + emit AllocationManager.AllocationResized( + _indexer, + _allocationId, + subgraphDeploymentId, + _tokens, + beforeSubgraphAllocatedTokens + ); // resize allocation subgraphService.resizeAllocation(_indexer, _allocationId, _tokens); @@ -119,8 +125,12 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { uint256 afterSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(subgraphDeploymentId); uint256 afterAllocatedTokens = subgraphService.allocationProvisionTracker(_indexer); Allocation.State memory afterAllocation = subgraphService.getAllocation(_allocationId); - uint256 accRewardsPerAllocatedTokenDelta = afterAllocation.accRewardsPerAllocatedToken - beforeAllocation.accRewardsPerAllocatedToken; - uint256 afterAccRewardsPending = rewardsManager.calcRewards(beforeAllocation.tokens, accRewardsPerAllocatedTokenDelta); + uint256 accRewardsPerAllocatedTokenDelta = afterAllocation.accRewardsPerAllocatedToken - + beforeAllocation.accRewardsPerAllocatedToken; + uint256 afterAccRewardsPending = rewardsManager.calcRewards( + beforeAllocation.tokens, + accRewardsPerAllocatedTokenDelta + ); // check state if (_tokens > beforeAllocation.tokens) { @@ -138,10 +148,17 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { assertTrue(subgraphService.isActiveAllocation(_allocationId)); Allocation.State memory allocation = subgraphService.getAllocation(_allocationId); - uint256 previousSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(allocation.subgraphDeploymentId); - + uint256 previousSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens( + allocation.subgraphDeploymentId + ); + vm.expectEmit(address(subgraphService)); - emit AllocationManager.AllocationClosed(allocation.indexer, _allocationId, allocation.subgraphDeploymentId, allocation.tokens); + emit AllocationManager.AllocationClosed( + allocation.indexer, + _allocationId, + allocation.subgraphDeploymentId, + allocation.tokens + ); // close stale allocation subgraphService.closeStaleAllocation(_allocationId); @@ -182,15 +199,20 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { uint256 paymentCollected = 0; Allocation.State memory allocation; CollectPaymentData memory collectPaymentDataBefore; - + // PaymentType.IndexingRewards variables IndexingRewardsData memory indexingRewardsData; - uint32 delegationRatio = subgraphService.delegationRatio(); address rewardsDestination = subgraphService.rewardsDestination(_indexer); collectPaymentDataBefore.rewardsDestinationBalance = token.balanceOf(rewardsDestination); - collectPaymentDataBefore.indexerProvisionBalance = staking.getProviderTokensAvailable(_indexer, address(subgraphService)); - collectPaymentDataBefore.delegationPoolBalance = staking.getDelegatedTokensAvailable(_indexer, address(subgraphService)); - + collectPaymentDataBefore.indexerProvisionBalance = staking.getProviderTokensAvailable( + _indexer, + address(subgraphService) + ); + collectPaymentDataBefore.delegationPoolBalance = staking.getDelegatedTokensAvailable( + _indexer, + address(subgraphService) + ); + // PaymentType.QueryFee variables QueryFeeData memory queryFeeData; queryFeeData.protocolPaymentCut = graphPayments.PROTOCOL_PAYMENT_CUT(); @@ -257,25 +279,38 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { // Collect payment data after CollectPaymentData memory collectPaymentDataAfter; collectPaymentDataAfter.rewardsDestinationBalance = token.balanceOf(rewardsDestination); - collectPaymentDataAfter.indexerProvisionBalance = staking.getProviderTokensAvailable(_indexer, address(subgraphService)); - collectPaymentDataAfter.delegationPoolBalance = staking.getDelegatedTokensAvailable(_indexer, address(subgraphService)); + collectPaymentDataAfter.indexerProvisionBalance = staking.getProviderTokensAvailable( + _indexer, + address(subgraphService) + ); + collectPaymentDataAfter.delegationPoolBalance = staking.getDelegatedTokensAvailable( + _indexer, + address(subgraphService) + ); collectPaymentDataAfter.indexerBalance = token.balanceOf(_indexer); collectPaymentDataAfter.curationBalance = token.balanceOf(address(curation)); collectPaymentDataAfter.lockedTokens = subgraphService.feesProvisionTracker(_indexer); if (_paymentType == IGraphPayments.PaymentTypes.QueryFee) { // Check indexer got paid the correct amount - uint256 tokensProtocol = paymentCollected.mulPPM(protocolPaymentCut); - uint256 curationTokens = paymentCollected.mulPPM(queryFeeData.curationCut); - uint256 expectedIndexerTokensPayment = paymentCollected - tokensProtocol - curationTokens; - assertEq(collectPaymentDataAfter.indexerBalance, collectPaymentDataBefore.indexerBalance + expectedIndexerTokensPayment); + { + uint256 tokensProtocol = paymentCollected.mulPPM(protocolPaymentCut); + uint256 curationTokens = paymentCollected.mulPPM(queryFeeData.curationCut); + uint256 expectedIndexerTokensPayment = paymentCollected - tokensProtocol - curationTokens; + assertEq( + collectPaymentDataAfter.indexerBalance, + collectPaymentDataBefore.indexerBalance + expectedIndexerTokensPayment + ); - // Check curation got paid the correct amount - assertEq(collectPaymentDataAfter.curationBalance, collectPaymentDataBefore.curationBalance + curationTokens); + // Check curation got paid the correct amount + assertEq( + collectPaymentDataAfter.curationBalance, + collectPaymentDataBefore.curationBalance + curationTokens + ); + } // Check locked tokens - uint256 stakeToFeesRatio = subgraphService.stakeToFeesRatio(); - uint256 tokensToLock = paymentCollected * stakeToFeesRatio; + uint256 tokensToLock = paymentCollected * subgraphService.stakeToFeesRatio(); assertEq(collectPaymentDataAfter.lockedTokens, collectPaymentDataBefore.lockedTokens + tokensToLock); // Check the stake claim @@ -290,10 +325,12 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { } else { // Update allocation after collecting rewards allocation = subgraphService.getAllocation(allocationId); - + // Check allocation state assertEq(allocation.accRewardsPending, 0); - uint256 accRewardsPerAllocatedToken = rewardsManager.onSubgraphAllocationUpdate(allocation.subgraphDeploymentId); + uint256 accRewardsPerAllocatedToken = rewardsManager.onSubgraphAllocationUpdate( + allocation.subgraphDeploymentId + ); assertEq(allocation.accRewardsPerAllocatedToken, accRewardsPerAllocatedToken); assertEq(allocation.lastPOIPresentedAt, block.timestamp); @@ -301,7 +338,7 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { if (rewardsDestination == address(0)) { // If rewards destination is address zero indexer should get paid to their provision balance assertEq( - collectPaymentDataAfter.indexerProvisionBalance, + collectPaymentDataAfter.indexerProvisionBalance, collectPaymentDataBefore.indexerProvisionBalance + indexingRewardsData.tokensIndexerRewards ); } else { @@ -314,12 +351,16 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest { // Check delegation pool got paid the correct amount assertEq( - collectPaymentDataAfter.delegationPoolBalance, + collectPaymentDataAfter.delegationPoolBalance, collectPaymentDataBefore.delegationPoolBalance + indexingRewardsData.tokensDelegationRewards ); // If after collecting indexing rewards the indexer is over allocated the allcation should close - uint256 tokensAvailable = staking.getTokensAvailable(_indexer, address(subgraphService), delegationRatio); + uint256 tokensAvailable = staking.getTokensAvailable( + _indexer, + address(subgraphService), + subgraphService.delegationRatio() + ); if (allocation.tokens <= tokensAvailable) { // Indexer isn't over allocated so allocation should still be open assertTrue(allocation.isOpen()); diff --git a/packages/subgraph-service/test/subgraphService/allocation/closeStale.t.sol b/packages/subgraph-service/test/subgraphService/allocation/closeStale.t.sol index 3e6672dd7..68d736261 100644 --- a/packages/subgraph-service/test/subgraphService/allocation/closeStale.t.sol +++ b/packages/subgraph-service/test/subgraphService/allocation/closeStale.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/allocation/resize.t.sol b/packages/subgraph-service/test/subgraphService/allocation/resize.t.sol index 3da7c5a0c..0df994929 100644 --- a/packages/subgraph-service/test/subgraphService/allocation/resize.t.sol +++ b/packages/subgraph-service/test/subgraphService/allocation/resize.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/allocation/start.t.sol b/packages/subgraph-service/test/subgraphService/allocation/start.t.sol index 6db5e73db..9157444fb 100644 --- a/packages/subgraph-service/test/subgraphService/allocation/start.t.sol +++ b/packages/subgraph-service/test/subgraphService/allocation/start.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/allocation/stop.t.sol b/packages/subgraph-service/test/subgraphService/allocation/stop.t.sol index 84b9da27e..ee5958115 100644 --- a/packages/subgraph-service/test/subgraphService/allocation/stop.t.sol +++ b/packages/subgraph-service/test/subgraphService/allocation/stop.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/collect/collect.t.sol b/packages/subgraph-service/test/subgraphService/collect/collect.t.sol index 4891a4a75..9df747b4f 100644 --- a/packages/subgraph-service/test/subgraphService/collect/collect.t.sol +++ b/packages/subgraph-service/test/subgraphService/collect/collect.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/collect/indexing/indexing.t.sol b/packages/subgraph-service/test/subgraphService/collect/indexing/indexing.t.sol index d51f9ea4b..8b29ec830 100644 --- a/packages/subgraph-service/test/subgraphService/collect/indexing/indexing.t.sol +++ b/packages/subgraph-service/test/subgraphService/collect/indexing/indexing.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/collect/query/query.t.sol b/packages/subgraph-service/test/subgraphService/collect/query/query.t.sol index 9e20ad352..6ad7b5347 100644 --- a/packages/subgraph-service/test/subgraphService/collect/query/query.t.sol +++ b/packages/subgraph-service/test/subgraphService/collect/query/query.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/provider/register.t.sol b/packages/subgraph-service/test/subgraphService/provider/register.t.sol index 8e3d0577e..1df1f5571 100644 --- a/packages/subgraph-service/test/subgraphService/provider/register.t.sol +++ b/packages/subgraph-service/test/subgraphService/provider/register.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/provider/rewardsDestination.t.sol b/packages/subgraph-service/test/subgraphService/provider/rewardsDestination.t.sol index 3926bc362..0b4c805b2 100644 --- a/packages/subgraph-service/test/subgraphService/provider/rewardsDestination.t.sol +++ b/packages/subgraph-service/test/subgraphService/provider/rewardsDestination.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/subgraphService/provision/accept.t.sol b/packages/subgraph-service/test/subgraphService/provision/accept.t.sol index 9fdb0b76f..1c8191275 100644 --- a/packages/subgraph-service/test/subgraphService/provision/accept.t.sol +++ b/packages/subgraph-service/test/subgraphService/provision/accept.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol"; diff --git a/packages/subgraph-service/test/utils/Constants.sol b/packages/subgraph-service/test/utils/Constants.sol index e20d1fc72..1dbfd082e 100644 --- a/packages/subgraph-service/test/utils/Constants.sol +++ b/packages/subgraph-service/test/utils/Constants.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; abstract contract Constants { uint256 internal constant MAX_TOKENS = 10_000_000_000 ether; diff --git a/packages/subgraph-service/test/utils/Users.sol b/packages/subgraph-service/test/utils/Users.sol index b2976f98a..69c3e411e 100644 --- a/packages/subgraph-service/test/utils/Users.sol +++ b/packages/subgraph-service/test/utils/Users.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; struct Users { address governor; diff --git a/packages/subgraph-service/test/utils/Utils.sol b/packages/subgraph-service/test/utils/Utils.sol index 47e4e68df..5aae4de3f 100644 --- a/packages/subgraph-service/test/utils/Utils.sol +++ b/packages/subgraph-service/test/utils/Utils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity 0.8.27; import "forge-std/Test.sol";