diff --git a/contracts/staking/NativeTokenStakingManager.sol b/contracts/staking/NativeTokenStakingManager.sol index 020d96c0c..2b3f72ab2 100644 --- a/contracts/staking/NativeTokenStakingManager.sol +++ b/contracts/staking/NativeTokenStakingManager.sol @@ -8,21 +8,27 @@ pragma solidity 0.8.25; import {INativeTokenStakingManager} from "./interfaces/INativeTokenStakingManager.sol"; import {Address} from "@openzeppelin/contracts@5.0.2/utils/Address.sol"; import {StakingManager} from "./StakingManager.sol"; +import {Initializable} from + "@openzeppelin/contracts-upgradeable@5.0.2/proxy/utils/Initializable.sol"; -contract NativeTokenStakingManager is StakingManager, INativeTokenStakingManager { +contract NativeTokenStakingManager is Initializable, StakingManager, INativeTokenStakingManager { using Address for address payable; + + constructor() { + _disableInitializers(); + } + /** * @notice Begins the validator registration process. Locks the provided native asset in the contract as the stake. * @param nodeID The node ID of the validator being registered. - * @param registrationExpiry The time at which the reigistration is no longer valid on the P-Chain. + * @param registrationExpiry The time at which the registration is no longer valid on the P-Chain. * @param signature The raw bytes of the Ed25519 signature over the concatenated bytes of * [subnetID]+[nodeID]+[blsPublicKey]+[weight]+[balance]+[expiry]. This signature must correspond to the Ed25519 - * public key that is used for the nodeID. This approach prevents NodeIDs from being unwillingly added to Subnets. + * public key that is used for the nodeID. This approach prevents nodeIDs from being unwillingly added to Subnets. * balance is the minimum initial $nAVAX balance that must be attached to the validator serialized as a uint64. * The signature field will be validated by the P-Chain. Implementations may choose to validate that the signature * field is well-formed but it is not required. */ - function initializeValidatorRegistration( bytes32 nodeID, uint64 registrationExpiry, diff --git a/contracts/staking/StakingManager.sol b/contracts/staking/StakingManager.sol index a70d7ad91..86148a415 100644 --- a/contracts/staking/StakingManager.sol +++ b/contracts/staking/StakingManager.sol @@ -10,12 +10,21 @@ import { WarpMessage, IWarpMessenger } from "@avalabs/subnet-evm-contracts@1.2.0/contracts/interfaces/IWarpMessenger.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts@5.0.2/utils/ReentrancyGuard.sol"; +import {ReentrancyGuardUpgradeable} from + "@openzeppelin/contracts-upgradeable@5.0.2/utils/ReentrancyGuardUpgradeable.sol"; import {StakingMessages} from "./StakingMessages.sol"; import {IRewardCalculator} from "./interfaces/IRewardCalculator.sol"; -import {Context} from "@openzeppelin/contracts@5.0.2/utils/Context.sol"; - -abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { +import {ContextUpgradeable} from + "@openzeppelin/contracts-upgradeable@5.0.2/utils/ContextUpgradeable.sol"; +import {Initializable} from + "@openzeppelin/contracts-upgradeable@5.0.2/proxy/utils/Initializable.sol"; + +abstract contract StakingManager is + Initializable, + ContextUpgradeable, + ReentrancyGuardUpgradeable, + IStakingManager +{ enum ValidatorStatus { Unknown, PendingAdded, @@ -46,7 +55,6 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { // solhint-disable private-vars-leading-underscore /// @custom:storage-location erc7201:avalanche-icm.storage.StakingManager struct StakingManagerStorage { - IWarpMessenger _warpMessenger; bytes32 _pChainBlockchainID; bytes32 _subnetID; uint256 _minimumStakeAmount; @@ -66,9 +74,9 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { // solhint-enable private-vars-leading-underscore // keccak256(abi.encode(uint256(keccak256("avalanche-icm.storage.StakingManager")) - 1)) & ~bytes32(uint256(0xff)); - // TODO: Update to correct storage slot + // TODO: Unit test for storage slot bytes32 private constant _STAKING_MANAGER_STORAGE_LOCATION = - 0x8568826440873e37a96cb0aab773b28d8154d963d2f0e41bd9b5c15f63625f91; + 0xafe6c4731b852fc2be89a0896ae43d22d8b24989064d841b2a1586b4d39ab600; // solhint-disable ordering function _getStakingManagerStorage() private pure returns (StakingManagerStorage storage $) { @@ -94,9 +102,32 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { IRewardCalculator rewardCalculator; } - function initialize(StakingManagerSettings calldata settings) public { + /** + * @notice Warp precompile used for sending and receiving Warp messages. + */ + IWarpMessenger public constant WARP_MESSENGER = + IWarpMessenger(0x0200000000000000000000000000000000000005); + + function initialize(StakingManagerSettings calldata settings) public initializer { + __StakingManager_init(settings); + } + + // solhint-disable-next-line func-name-mixedcase + function __StakingManager_init(StakingManagerSettings calldata settings) + internal + onlyInitializing + { + __ReentrancyGuard_init(); + __Context_init(); + __StakingManager_init_unchained(settings); + } + + // solhint-disable-next-line func-name-mixedcase + function __StakingManager_init_unchained(StakingManagerSettings calldata settings) + internal + onlyInitializing + { StakingManagerStorage storage $ = _getStakingManagerStorage(); - $._warpMessenger = IWarpMessenger(0x0200000000000000000000000000000000000005); $._pChainBlockchainID = settings.pChainBlockchainID; $._subnetID = settings.subnetID; $._minimumStakeAmount = settings.minimumStakeAmount; @@ -208,7 +239,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { $._pendingRegisterValidationMessages[validationID] = registerSubnetValidatorMessage; // Submit the message to the Warp precompile. - bytes32 messageID = $._warpMessenger.sendWarpMessage(registerSubnetValidatorMessage); + bytes32 messageID = WARP_MESSENGER.sendWarpMessage(registerSubnetValidatorMessage); $._validationPeriods[validationID] = Validator({ status: ValidatorStatus.PendingAdded, @@ -239,7 +270,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { ); // Submit the message to the Warp precompile. - $._warpMessenger.sendWarpMessage($._pendingRegisterValidationMessages[validationID]); + WARP_MESSENGER.sendWarpMessage($._pendingRegisterValidationMessages[validationID]); } /** @@ -250,7 +281,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { function completeValidatorRegistration(uint32 messageIndex) external { StakingManagerStorage storage $ = _getStakingManagerStorage(); (WarpMessage memory warpMessage, bool valid) = - $._warpMessenger.getVerifiedWarpMessage(messageIndex); + WARP_MESSENGER.getVerifiedWarpMessage(messageIndex); require(valid, "StakingManager: Invalid warp message"); require( @@ -312,11 +343,11 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { uint64 uptimeSeconds; if (includeUptimeProof) { (WarpMessage memory warpMessage, bool valid) = - $._warpMessenger.getVerifiedWarpMessage(messageIndex); + WARP_MESSENGER.getVerifiedWarpMessage(messageIndex); require(valid, "StakingManager: Invalid warp message"); require( - warpMessage.sourceChainID == $._warpMessenger.getBlockchainID(), + warpMessage.sourceChainID == WARP_MESSENGER.getBlockchainID(), "StakingManager: Invalid source chain ID" ); require( @@ -341,7 +372,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { bytes memory setValidatorWeightPayload = StakingMessages.packSetSubnetValidatorWeightMessage( validationID, validator.messageNonce, 0 ); - bytes32 messageID = $._warpMessenger.sendWarpMessage(setValidatorWeightPayload); + bytes32 messageID = WARP_MESSENGER.sendWarpMessage(setValidatorWeightPayload); // Emit the event to signal the start of the validator removal process. emit ValidatorRemovalInitialized( @@ -366,7 +397,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { bytes memory setValidatorWeightPayload = StakingMessages.packSetSubnetValidatorWeightMessage( validationID, validator.messageNonce, 0 ); - $._warpMessenger.sendWarpMessage(setValidatorWeightPayload); + WARP_MESSENGER.sendWarpMessage(setValidatorWeightPayload); } /** @@ -384,7 +415,7 @@ abstract contract StakingManager is Context, ReentrancyGuard, IStakingManager { // Get the Warp message. (WarpMessage memory warpMessage, bool valid) = - $._warpMessenger.getVerifiedWarpMessage(messageIndex); + WARP_MESSENGER.getVerifiedWarpMessage(messageIndex); require(valid, "StakingManager: Invalid warp message"); bytes32 validationID;