From e8c0cd17f08aa0e244b4f27f0714b80c9a1793a1 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 10 Jul 2024 18:11:17 +0300 Subject: [PATCH 1/4] [Update] Minor code refactoring --- contracts/RequestManager.sol | 38 ++++++++++++------- contracts/factories/ERC20LockUpFactory.sol | 24 +++++------- .../factories/ERC20PenaltyFeeFactory.sol | 25 +++++------- contracts/factories/ERC721LockUpFactory.sol | 27 ++++++------- .../factories/ERC721PenaltyFeeFactory.sol | 21 ++++------ contracts/factories/FactoryGeneric.sol | 21 ---------- contracts/factories/GenericFactory.sol | 31 +++++++++++++++ .../{IBaseFactory.sol => IGenericFactory.sol} | 2 +- .../interfaces/IFactories/ILockUpFactory.sol | 3 +- .../IFactories/IPenaltyFeeFactory.sol | 3 +- contracts/interfaces/IPools/ILockUpPool.sol | 8 ++-- .../interfaces/IPools/IPenaltyFeePool.sol | 9 ++--- contracts/interfaces/IPools/IPoolErrors.sol | 2 +- contracts/interfaces/IRequestManager.sol | 2 +- 14 files changed, 105 insertions(+), 111 deletions(-) delete mode 100644 contracts/factories/FactoryGeneric.sol create mode 100644 contracts/factories/GenericFactory.sol rename contracts/interfaces/IFactories/{IBaseFactory.sol => IGenericFactory.sol} (91%) diff --git a/contracts/RequestManager.sol b/contracts/RequestManager.sol index 512646c..8f17538 100644 --- a/contracts/RequestManager.sol +++ b/contracts/RequestManager.sol @@ -1,12 +1,12 @@ /* -ERC20LockUpFactory +RequestManager SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; import {IRequestManager} from "./interfaces/IRequestManager.sol"; -import {IBaseFactory} from "./interfaces/IFactories/IBaseFactory.sol"; +import {IGenericFactory} from "./interfaces/IFactories/IGenericFactory.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -22,26 +22,28 @@ contract RequestManager is Ownable, IRequestManager { constructor() Ownable(msg.sender) {} - function addFactory(address factory) onlyOwner external { + function addFactory(address factory) external onlyOwner { if (factory == address(0)) revert InvalidAddress(); + if (whitelistFactory[factory]) revert AlreadyRegisteredFactory(); whitelistFactory[factory] = true; emit FactoryRegistered(factory); } - function removeFactory(address factory) onlyOwner external { - if (whitelistFactory[factory] != true) revert UnregisteredFactory(); + function removeFactory(address factory) external onlyOwner { + if (!whitelistFactory[factory]) revert UnregisteredFactory(); whitelistFactory[factory] = false; emit FactoryUnregistered(factory); } - /// @notice Function allows users to deploy the staking pool with specified parameters - function deploy(uint256 id) external returns (address newPoolAddress) { + /// @notice Deploys the staking pool with parameters from an approved request. + /// @param id ID of the request to be deployed. + function deploy(uint256 id) external { if (requests.length <= id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.data.deployer) revert InvalidCaller(); requests[id].requestStatus = Status.DEPLOYED; - newPoolAddress = IBaseFactory(req.data.factory).deploy( + address newPoolAddress = IGenericFactory(req.data.factory).deploy( req.data.deployer, req.data.stakingData ); @@ -50,20 +52,20 @@ contract RequestManager is Ownable, IRequestManager { emit RequestFullfilled(id, newPoolAddress); } + /// @notice Requests deployment of a new staking pool. + /// @param data Request data for the staking pool. function requestDeployment(RequestPayload calldata data) external { if (data.deployer == address(0) || data.factory == address(0)) revert InvalidAddress(); if (data.ipfsHash == bytes32(0)) revert InvalidIpfsHash(); if (data.stakingData.length == 0) revert InvalidPayload(); - if (whitelistFactory[data.factory] != true) - revert UnregisteredFactory(); + if (!whitelistFactory[data.factory]) revert UnregisteredFactory(); requests.push(Request({requestStatus: Status.CREATED, data: data})); - emit RequestSubmitted( - requests.length - 1, - data - ); + emit RequestSubmitted(requests.length - 1, data); } + /// @notice Approves a deployment request. + /// @param id ID of the request to be approved. function approveRequest(uint256 id) external onlyOwner { if (requests.length <= id) revert InvalidId(); Request storage req = requests[id]; @@ -72,6 +74,8 @@ contract RequestManager is Ownable, IRequestManager { emit RequestStatusChanged(id, req.requestStatus); } + /// @notice Denies a deployment request. + /// @param id ID of the request to be denied. function denyRequest(uint256 id) external onlyOwner { if (requests.length <= id) revert InvalidId(); Request storage req = requests[id]; @@ -80,6 +84,8 @@ contract RequestManager is Ownable, IRequestManager { emit RequestStatusChanged(id, req.requestStatus); } + /// @notice Cancels a deployment request. + /// @param id ID of the request to be canceled. function cancelRequest(uint256 id) external { if (requests.length <= id) revert InvalidId(); Request storage req = requests[id]; @@ -92,10 +98,14 @@ contract RequestManager is Ownable, IRequestManager { emit RequestStatusChanged(id, req.requestStatus); } + /// @notice Returns all deployment requests. + /// @return reqs Array of all PenaltyFee requests. function getRequests() external view returns (Request[] memory reqs) { reqs = requests; } + /// @notice Returns all deployed staking pools. + /// @return pools Array of all staking pool addresses. function getPools() external view returns (address[] memory pools) { pools = stakingPools; } diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index c02a514..554c107 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -6,26 +6,26 @@ SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {ERC20LockUpPool} from "../pools/ERC20/ERC20LockUpStakingPool.sol"; import {ILockUpFactory} from "../interfaces/IFactories/ILockUpFactory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {GenericFactory} from "./GenericFactory.sol"; /// @title ERC20LockUpStakingFactory /// @notice A smart contract for deploying ERC20 LockUp staking pools. -contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { +contract ERC20LockUpStakingFactory is GenericFactory, ILockUpFactory { using SafeERC20 for IERC20; - address public requestManager; - address[] public stakingPools; - constructor(address managerContract) Ownable(msg.sender) { - if (managerContract == address(0)) revert InvalidManagerAddress(); - requestManager = managerContract; - } + constructor(address managerContract) GenericFactory(managerContract) {} /// @notice Function allows users to deploy the LockUp staking pool with specified parameters - function deploy(address deployer, bytes calldata payload) external returns (address newPoolAddress) { - if (payload.length != 224) revert InvalidPayloadLength(); + /// @param deployer Address of the deployer + /// @param payload Encoded staking pool deployment parameters + function deploy( + address deployer, + bytes calldata payload + ) external returns (address newPoolAddress) { if (msg.sender != requestManager) revert InvalidCaller(); + if (payload.length != 224) revert InvalidPayloadLength(); DeploymentData memory data = abi.decode(payload, (DeploymentData)); newPoolAddress = address( new ERC20LockUpPool{ @@ -61,8 +61,4 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { ); emit StakingPoolDeployed(newPoolAddress); } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } } diff --git a/contracts/factories/ERC20PenaltyFeeFactory.sol b/contracts/factories/ERC20PenaltyFeeFactory.sol index c55c7db..ca1f395 100644 --- a/contracts/factories/ERC20PenaltyFeeFactory.sol +++ b/contracts/factories/ERC20PenaltyFeeFactory.sol @@ -6,27 +6,26 @@ SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {ERC20PenaltyFeePool} from "../pools/ERC20/ERC20PenaltyFeePool.sol"; import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {GenericFactory} from "./GenericFactory.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title ERC20PenaltyFeeStakingFactory /// @notice A smart contract for deploying ERC20 staking pools with penalty fees. -/// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { +contract ERC20PenaltyFeeStakingFactory is GenericFactory, IPenaltyFeeFactory { using SafeERC20 for IERC20; - address public requestManager; - address[] public stakingPools; - constructor(address managerContract) Ownable(msg.sender) { - if (managerContract == address(0)) revert InvalidManagerAddress(); - requestManager = managerContract; - } + constructor(address managerContract) GenericFactory(managerContract) {} /// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters - function deploy(address deployer, bytes calldata payload) external returns (address newPoolAddress) { - if (payload.length != 192) revert InvalidPayloadLength(); + /// @param deployer Address of the deployer + /// @param payload Encoded staking pool deployment parameters + function deploy( + address deployer, + bytes calldata payload + ) external returns (address newPoolAddress) { if (msg.sender != requestManager) revert InvalidCaller(); + if (payload.length != 192) revert InvalidPayloadLength(); DeploymentData memory data = abi.decode(payload, (DeploymentData)); newPoolAddress = address( new ERC20PenaltyFeePool{ @@ -62,8 +61,4 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { ); emit StakingPoolDeployed(newPoolAddress); } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } } diff --git a/contracts/factories/ERC721LockUpFactory.sol b/contracts/factories/ERC721LockUpFactory.sol index 420dbea..ef5b54c 100644 --- a/contracts/factories/ERC721LockUpFactory.sol +++ b/contracts/factories/ERC721LockUpFactory.sol @@ -6,28 +6,27 @@ SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {ERC721LockUpPool} from "../pools/ERC721/ERC721LockUpStakingPool.sol"; import {ILockUpFactory} from "../interfaces/IFactories/ILockUpFactory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {GenericFactory} from "./GenericFactory.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title ERC721LockUpStakingFactory /// @notice A smart contract for deploying ERC721 LockUp staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { +contract ERC721LockUpStakingFactory is GenericFactory, ILockUpFactory { using SafeERC20 for IERC20; - address public requestManager; - address[] public stakingPools; + constructor(address managerContract) GenericFactory(managerContract) {} - constructor(address managerContract) Ownable(msg.sender) { - if (managerContract == address(0)) revert InvalidManagerAddress(); - requestManager = managerContract; - } - - /// @notice Function allows users to deploy the LockUp staking pool with specified parameters - function deploy(address deployer, bytes calldata payload) public returns (address newPoolAddress) { - if (payload.length != 224) revert InvalidPayloadLength(); + /// @notice Function allows users to deploy the ERC721 LockUp staking pool with specified parameters + /// @param deployer Address of the deployer + /// @param payload Encoded staking pool deployment parameters + function deploy( + address deployer, + bytes calldata payload + ) public returns (address newPoolAddress) { if (msg.sender != requestManager) revert InvalidCaller(); + if (payload.length != 224) revert InvalidPayloadLength(); DeploymentData memory data = abi.decode(payload, (DeploymentData)); newPoolAddress = address( new ERC721LockUpPool{ @@ -63,8 +62,4 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { ); emit StakingPoolDeployed(newPoolAddress); } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } } diff --git a/contracts/factories/ERC721PenaltyFeeFactory.sol b/contracts/factories/ERC721PenaltyFeeFactory.sol index cbdacde..708944d 100644 --- a/contracts/factories/ERC721PenaltyFeeFactory.sol +++ b/contracts/factories/ERC721PenaltyFeeFactory.sol @@ -6,26 +6,23 @@ SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {ERC721PenaltyFeePool} from "../pools/ERC721/ERC721PenaltyFeePool.sol"; import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {GenericFactory} from "./GenericFactory.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title ERC721PenaltyFeeStakingFactory /// @notice A smart contract for deploying ERC721 staking pools with penalty fees. -contract ERC721PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { +contract ERC721PenaltyFeeStakingFactory is GenericFactory, IPenaltyFeeFactory { using SafeERC20 for IERC20; - address public requestManager; - address[] public stakingPools; - constructor(address managerContract) Ownable(msg.sender) { - if (managerContract == address(0)) revert InvalidManagerAddress(); - requestManager = managerContract; - } + constructor(address managerContract) GenericFactory(managerContract) {} - /// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters + /// @notice Function allows users to deploy the ERC721 penaltyFee staking pool with specified parameters + /// @param deployer Address of the deployer + /// @param payload Encoded staking pool deployment parameters function deploy(address deployer, bytes calldata payload) public returns (address newPoolAddress) { - if (payload.length != 192) revert InvalidPayloadLength(); if (msg.sender != requestManager) revert InvalidCaller(); + if (payload.length != 192) revert InvalidPayloadLength(); DeploymentData memory data = abi.decode(payload, (DeploymentData)); newPoolAddress = address( new ERC721PenaltyFeePool{ @@ -62,8 +59,4 @@ contract ERC721PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { emit StakingPoolDeployed(newPoolAddress); } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } } \ No newline at end of file diff --git a/contracts/factories/FactoryGeneric.sol b/contracts/factories/FactoryGeneric.sol deleted file mode 100644 index a28d470..0000000 --- a/contracts/factories/FactoryGeneric.sol +++ /dev/null @@ -1,21 +0,0 @@ -// /* -// Factory Generic -// SPDX-License-Identifier: MIT -// */ - -// pragma solidity 0.8.25; -// import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -// contract GenericFactory is Ownable { - -// address public requestManager; -// address[] public stakingPools; - -// constructor(address managerContract) Ownable(msg.sender) { -// requestManager = managerContract; -// } - -// function getPools() external view returns (address[] memory pools) { -// pools = stakingPools; -// } -// } \ No newline at end of file diff --git a/contracts/factories/GenericFactory.sol b/contracts/factories/GenericFactory.sol new file mode 100644 index 0000000..84b3930 --- /dev/null +++ b/contracts/factories/GenericFactory.sol @@ -0,0 +1,31 @@ +/* +Factory Generic +SPDX-License-Identifier: MIT +*/ + +pragma solidity 0.8.25; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IGenericFactory} from "../interfaces/IFactories/IGenericFactory.sol"; + +abstract contract GenericFactory is Ownable, IGenericFactory { + address public requestManager; + address[] public stakingPools; + + constructor(address managerContract) Ownable(msg.sender) { + if (managerContract == address(0)) revert InvalidManagerAddress(); + requestManager = managerContract; + } + + function updateManagerContract( + address newManagerContract + ) external onlyOwner { + if (newManagerContract == address(0) || newManagerContract == address(this)) revert InvalidManagerAddress(); + requestManager = newManagerContract; + } + + /// @notice Returns all staking pools deployed by this factory. + /// @return pools Array of all staking pool addresses. + function getPools() external view returns (address[] memory pools) { + pools = stakingPools; + } +} diff --git a/contracts/interfaces/IFactories/IBaseFactory.sol b/contracts/interfaces/IFactories/IGenericFactory.sol similarity index 91% rename from contracts/interfaces/IFactories/IBaseFactory.sol rename to contracts/interfaces/IFactories/IGenericFactory.sol index b6c8e99..7eb12b9 100644 --- a/contracts/interfaces/IFactories/IBaseFactory.sol +++ b/contracts/interfaces/IFactories/IGenericFactory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -interface IBaseFactory { +interface IGenericFactory { error InvalidManagerAddress(); error InvalidCaller(); diff --git a/contracts/interfaces/IFactories/ILockUpFactory.sol b/contracts/interfaces/IFactories/ILockUpFactory.sol index 9ac6471..b77add8 100644 --- a/contracts/interfaces/IFactories/ILockUpFactory.sol +++ b/contracts/interfaces/IFactories/ILockUpFactory.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IBaseFactory} from "./IBaseFactory.sol"; -interface ILockUpFactory is IBaseFactory { +interface ILockUpFactory { struct DeploymentData { address stakeToken; address rewardToken; diff --git a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol index dc38170..59a8aee 100644 --- a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol +++ b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IBaseFactory} from "./IBaseFactory.sol"; -interface IPenaltyFeeFactory is IBaseFactory { +interface IPenaltyFeeFactory { struct DeploymentData { address stakeToken; address rewardToken; diff --git a/contracts/interfaces/IPools/ILockUpPool.sol b/contracts/interfaces/IPools/ILockUpPool.sol index 49bedcb..da72ba7 100644 --- a/contracts/interfaces/IPools/ILockUpPool.sol +++ b/contracts/interfaces/IPools/ILockUpPool.sol @@ -22,8 +22,8 @@ interface ILockUpPoolStorage { * @dev rewardToken The address of the reward token * @dev startTime The start time of the pool * @dev endTime The end time of the pool - * @dev unstakeLockUpTime The LockUp time (in unixtimestamp) before unstaking - * @dev claimLockUpTime The LockUp time (in unixtimestamp) before claiming rewards + * @dev unstakeLockUpTime The lockup time (in unixtimestamp) before unstaking + * @dev claimLockUpTime The lockup time (in unixtimestamp) before claiming rewards * @dev rewardTokenPerSecond The reward distribution rate per second * @dev totalStaked: Total tokens staked * @dev totalClaimed: Total rewards claimed @@ -35,8 +35,8 @@ interface ILockUpPoolStorage { address rewardToken; uint256 startTime; uint256 endTime; - uint256 unstakeLockUpTime; // LockUp period for unstaking - uint256 claimLockUpTime; // LockUp period for claiming rewards + uint256 unstakeLockUpTime; + uint256 claimLockUpTime; uint256 rewardTokenPerSecond; uint256 totalStaked; uint256 totalClaimed; diff --git a/contracts/interfaces/IPools/IPenaltyFeePool.sol b/contracts/interfaces/IPools/IPenaltyFeePool.sol index 6409849..89bdfe8 100644 --- a/contracts/interfaces/IPools/IPenaltyFeePool.sol +++ b/contracts/interfaces/IPools/IPenaltyFeePool.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - interface IPenaltyFeePoolStorage { /** * @notice Storage for a user's staking information * @dev amount Number of tokens staked by the user. * @dev claimed The amount of rewards already claimed by the user - * @dev rewardDebt Used to calculate rewards efficiently + * @dev rewardDebt Used for rewards calculation * @dev pending The amount of rewards pending for the user */ struct UserInfo { @@ -54,8 +51,8 @@ interface IPenaltyFeePoolStorage { /** * @notice Event to notify when an admin claims accumulated fees - * @dev Emitted in 'claim' function - * @param amount The amount of fees claimed + * @dev Emitted in 'FeeClaim' function + * @param amount The amount of fees to be claimed */ event FeeClaim(uint256 amount); } diff --git a/contracts/interfaces/IPools/IPoolErrors.sol b/contracts/interfaces/IPools/IPoolErrors.sol index d624e3f..fed3bb0 100644 --- a/contracts/interfaces/IPools/IPoolErrors.sol +++ b/contracts/interfaces/IPools/IPoolErrors.sol @@ -40,7 +40,7 @@ interface IPoolErrors { error InvalidLockUpTime(); /** - * @notice Error emitted when a user other than the owner of a token attempts to unstake it. + * @dev Error emitted when a user other than the owner of a token attempts to unstake it. */ error NotStaker(); diff --git a/contracts/interfaces/IRequestManager.sol b/contracts/interfaces/IRequestManager.sol index a287fff..18f0284 100644 --- a/contracts/interfaces/IRequestManager.sol +++ b/contracts/interfaces/IRequestManager.sol @@ -30,7 +30,7 @@ interface IRequestManager { error InvalidAddress(); error InvalidPayload(); error UnregisteredFactory(); - error FactoryAlreadyRegistered(); + error AlreadyRegisteredFactory(); event RequestStatusChanged(uint256 indexed id, Status indexed status); event RequestFullfilled(uint256 indexed id, address indexed poolAddress); From 4b6d97eabcdf95963179a752167677ceb6eee8ce Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 10 Jul 2024 19:45:22 +0300 Subject: [PATCH 2/4] [Update] Merged comments from Ayo's branch, minor refactor --- contracts/RequestManager.sol | 10 +- .../interfaces/IFactories/IGenericFactory.sol | 7 +- .../interfaces/IFactories/ILockUpFactory.sol | 10 + .../IFactories/IPenaltyFeeFactory.sol | 9 + contracts/interfaces/IPools/IERC721Pool.sol | 4 +- contracts/interfaces/IPools/ILockUpPool.sol | 2 +- contracts/interfaces/IPools/IPoolErrors.sol | 9 +- contracts/interfaces/IRequestManager.sol | 6 +- .../pools/ERC20/ERC20LockUpStakingPool.sol | 2 + contracts/pools/ERC20/ERC20PenaltyFeePool.sol | 2 + .../pools/ERC721/ERC721LockUpStakingPool.sol | 2 + .../pools/ERC721/ERC721PenaltyFeePool.sol | 2 + hardhat.config.ts | 1 + ignition/modules/factories.ts | 7 - ignition/modules/stakepad.ts | 20 ++ package-lock.json | 195 +++++------------- package.json | 2 +- 17 files changed, 125 insertions(+), 165 deletions(-) delete mode 100644 ignition/modules/factories.ts create mode 100644 ignition/modules/stakepad.ts diff --git a/contracts/RequestManager.sol b/contracts/RequestManager.sol index 8f17538..fa97f9a 100644 --- a/contracts/RequestManager.sol +++ b/contracts/RequestManager.sol @@ -23,7 +23,7 @@ contract RequestManager is Ownable, IRequestManager { constructor() Ownable(msg.sender) {} function addFactory(address factory) external onlyOwner { - if (factory == address(0)) revert InvalidAddress(); + if (factory == address(0) || factory == address(this)) revert InvalidAddress(); if (whitelistFactory[factory]) revert AlreadyRegisteredFactory(); whitelistFactory[factory] = true; emit FactoryRegistered(factory); @@ -41,7 +41,7 @@ contract RequestManager is Ownable, IRequestManager { if (requests.length <= id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.data.deployer) revert InvalidCaller(); + if (msg.sender != req.data.deployer) revert InvalidDeployer(); requests[id].requestStatus = Status.DEPLOYED; address newPoolAddress = IGenericFactory(req.data.factory).deploy( req.data.deployer, @@ -57,8 +57,8 @@ contract RequestManager is Ownable, IRequestManager { function requestDeployment(RequestPayload calldata data) external { if (data.deployer == address(0) || data.factory == address(0)) revert InvalidAddress(); - if (data.ipfsHash == bytes32(0)) revert InvalidIpfsHash(); - if (data.stakingData.length == 0) revert InvalidPayload(); + if (data.ipfsHash == bytes32(0)) revert IpfsZeroHash(); + if (data.stakingData.length == 0) revert EmptyPayload(); if (!whitelistFactory[data.factory]) revert UnregisteredFactory(); requests.push(Request({requestStatus: Status.CREATED, data: data})); emit RequestSubmitted(requests.length - 1, data); @@ -89,7 +89,7 @@ contract RequestManager is Ownable, IRequestManager { function cancelRequest(uint256 id) external { if (requests.length <= id) revert InvalidId(); Request storage req = requests[id]; - if (msg.sender != req.data.deployer) revert InvalidCaller(); + if (msg.sender != req.data.deployer) revert InvalidDeployer(); if ( req.requestStatus != Status.CREATED && req.requestStatus != Status.APPROVED diff --git a/contracts/interfaces/IFactories/IGenericFactory.sol b/contracts/interfaces/IFactories/IGenericFactory.sol index 7eb12b9..3ee81bc 100644 --- a/contracts/interfaces/IFactories/IGenericFactory.sol +++ b/contracts/interfaces/IFactories/IGenericFactory.sol @@ -1,10 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +/// @title Generic Factory Interface +/// @notice Defines the main errors and events used in all inhereted factories from the generic one. +/// @dev This interface is used in a GenericFactory contract which is inherited by the other specific factory contracts. interface IGenericFactory { - + /// @notice Error to indicate when an invalid request manager contract's address was submitted. error InvalidManagerAddress(); + /// @notice Error to indicate when the caller is not authorized. error InvalidCaller(); + /// @notice Error to indicate when an invalid payload was submitted. error InvalidPayloadLength(); function deploy(address deployer, bytes calldata payload) external returns (address); diff --git a/contracts/interfaces/IFactories/ILockUpFactory.sol b/contracts/interfaces/IFactories/ILockUpFactory.sol index b77add8..efd4ad7 100644 --- a/contracts/interfaces/IFactories/ILockUpFactory.sol +++ b/contracts/interfaces/IFactories/ILockUpFactory.sol @@ -1,7 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +/// @title LockUp Factory Interface +/// @notice Defines the structures used in the LockUp type staking factories. interface ILockUpFactory { + /// @notice Deployment data for creating a new LockUp staking pool. + /// @param stakeToken Address of the token to be staked. + /// @param rewardToken Address of the token to be rewarded. + /// @param poolStartTime Start time of the staking pool. + /// @param poolEndTime End time of the staking pool. + /// @param rewardPerSecond Rewards distributed per second. + /// @param unstakeLockUpTime LockUp period for unstaking. + /// @param claimLockUpTime LockUp period for claiming rewards. struct DeploymentData { address stakeToken; address rewardToken; diff --git a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol index 59a8aee..19eaf39 100644 --- a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol +++ b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol @@ -1,7 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +/// @title Penalty Fee Factory Interface +/// @notice Defines the structures used in the Penalty Fee type staking factories. interface IPenaltyFeeFactory { + /// @notice Deployment data for creating a new Penalty Fee staking pool. + /// @param stakeToken Address of the token to be staked. + /// @param rewardToken Address of the token to be rewarded. + /// @param poolStartTime Start time of the staking pool. + /// @param poolEndTime End time of the staking pool. + /// @param rewardPerSecond Rewards distributed per second. + /// @param penaltyPeriod Penalty period for early unstaking. struct DeploymentData { address stakeToken; address rewardToken; diff --git a/contracts/interfaces/IPools/IERC721Pool.sol b/contracts/interfaces/IPools/IERC721Pool.sol index 644401b..b5874d6 100644 --- a/contracts/interfaces/IPools/IERC721Pool.sol +++ b/contracts/interfaces/IPools/IERC721Pool.sol @@ -37,7 +37,9 @@ interface IPoolERC721 { uint256 lastBlockNumber ); - // **External Functions** + /** + * FUNCTIONS + */ /** * @notice Allows users to stake ERC721 tokens into the pool. diff --git a/contracts/interfaces/IPools/ILockUpPool.sol b/contracts/interfaces/IPools/ILockUpPool.sol index da72ba7..d803a28 100644 --- a/contracts/interfaces/IPools/ILockUpPool.sol +++ b/contracts/interfaces/IPools/ILockUpPool.sol @@ -6,7 +6,7 @@ interface ILockUpPoolStorage { * @notice Storage for a user's staking information * @dev amount Number of tokens staked by the user. * @dev claimed The amount of rewards already claimed by the user - * @dev rewardDebt Used to calculate rewards efficiently + * @dev rewardDebt Used for rewards calculation * @dev pending The amount of rewards pending for the user */ struct UserInfo { diff --git a/contracts/interfaces/IPools/IPoolErrors.sol b/contracts/interfaces/IPools/IPoolErrors.sol index fed3bb0..36c7780 100644 --- a/contracts/interfaces/IPools/IPoolErrors.sol +++ b/contracts/interfaces/IPools/IPoolErrors.sol @@ -5,6 +5,10 @@ interface IPoolErrors { /** * ERROR MESSAGES */ + + /// @dev Error to indicate an invalid token address + error InvalidTokenAddress(); + /// @dev Error to indicate an invalid staking period error InvalidStakingPeriod(); @@ -39,13 +43,12 @@ interface IPoolErrors { /// @dev Error to indicate an invalid LockUp time for unstaking or claiming rewards error InvalidLockUpTime(); - /** - * @dev Error emitted when a user other than the owner of a token attempts to unstake it. - */ + /// @dev Error emitted when a user other than the owner of a token attempts to unstake it. error NotStaker(); /// @dev Error to indicate an invalid penalty duration for unstaking error InvalidPenaltyPeriod(); + /// @dev Error to indicate that the caller is not the admin error NotAdmin(); } diff --git a/contracts/interfaces/IRequestManager.sol b/contracts/interfaces/IRequestManager.sol index 18f0284..2f81bcf 100644 --- a/contracts/interfaces/IRequestManager.sol +++ b/contracts/interfaces/IRequestManager.sol @@ -25,10 +25,10 @@ interface IRequestManager { error InvalidId(); error InvalidRequestStatus(); - error InvalidCaller(); - error InvalidIpfsHash(); + error InvalidDeployer(); + error IpfsZeroHash(); error InvalidAddress(); - error InvalidPayload(); + error EmptyPayload(); error UnregisteredFactory(); error AlreadyRegisteredFactory(); diff --git a/contracts/pools/ERC20/ERC20LockUpStakingPool.sol b/contracts/pools/ERC20/ERC20LockUpStakingPool.sol index 5672f5b..6aa02a5 100644 --- a/contracts/pools/ERC20/ERC20LockUpStakingPool.sol +++ b/contracts/pools/ERC20/ERC20LockUpStakingPool.sol @@ -53,6 +53,8 @@ contract ERC20LockUpPool is uint256 unstakeLockUp, uint256 claimLockUp ) Ownable(msg.sender) { + // Ensure that stakeToken and rewardToken addresses are valid + if (stakeToken == address(0) || rewardToken == address(0)) revert InvalidTokenAddress(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); // Ensure the staking period is valid diff --git a/contracts/pools/ERC20/ERC20PenaltyFeePool.sol b/contracts/pools/ERC20/ERC20PenaltyFeePool.sol index eec1789..91552cc 100644 --- a/contracts/pools/ERC20/ERC20PenaltyFeePool.sol +++ b/contracts/pools/ERC20/ERC20PenaltyFeePool.sol @@ -47,6 +47,8 @@ contract ERC20PenaltyFeePool is uint256 penaltyPeriod, address adminAddress ) Ownable(msg.sender) { + // Ensure that stakeToken and rewardToken addresses are valid + if (stakeToken == address(0) || rewardToken == address(0)) revert InvalidTokenAddress(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); // Ensure the staking period is valid diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index f768dba..2ebbf77 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -45,6 +45,8 @@ contract ERC721LockUpPool is uint256 unstakeLockUpTime, uint256 claimLockUpTime ) Ownable(msg.sender) { + // Ensure that stakeToken and rewardToken addresses are valid + if (stakeToken == address(0) || rewardToken == address(0)) revert InvalidTokenAddress(); // Ensure the staking period is valid if (poolStartTime >= poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future diff --git a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol index d23bf62..f3aeefb 100644 --- a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -52,6 +52,8 @@ contract ERC721PenaltyFeePool is uint256 penaltyPeriod, address adminAddress ) Ownable(msg.sender) { + // Ensure that stakeToken and rewardToken addresses are valid + if (stakeToken == address(0) || rewardToken == address(0)) revert InvalidTokenAddress(); // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future diff --git a/hardhat.config.ts b/hardhat.config.ts index 009b79d..bef15fc 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -7,6 +7,7 @@ import 'solidity-coverage'; import '@nomicfoundation/hardhat-network-helpers'; import '@nomicfoundation/hardhat-ethers'; import '@nomicfoundation/hardhat-chai-matchers'; +import "@nomicfoundation/hardhat-ignition"; dotenv.config(); diff --git a/ignition/modules/factories.ts b/ignition/modules/factories.ts deleted file mode 100644 index 4e0be10..0000000 --- a/ignition/modules/factories.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; - -export default buildModule("LockUpFactory", (m) => { - const lockUpfactory = m.contract("ERC20LockUpStakingFactory", []); - const feeFactory = m.contract("ERC20PenaltyFeeStakingFactory", []); - return { lockUpfactory, feeFactory }; -}); \ No newline at end of file diff --git a/ignition/modules/stakepad.ts b/ignition/modules/stakepad.ts new file mode 100644 index 0000000..00cb342 --- /dev/null +++ b/ignition/modules/stakepad.ts @@ -0,0 +1,20 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("Stakepad", (m) => { + // Deploying Stakepad's RequestManager contract first + const requestManager = m.contract("RequestManager", []); + // Deploying Stakepad's ERC20LockUpStakingFactory contract, passing RequestManager's contract address as an input parameter + const lockUpFactoryERC20 = m.contract("ERC20LockUpStakingFactory", [requestManager]); + m.call(requestManager, "addFactory", [lockUpFactoryERC20], { id: "lockUpFactoryERC20"}); + // Deploying Stakepad's ERC20PenaltyFeeStakingFactory contract, passing RequestManager's contract address as an input parameter + const penaltyFeeFactoryERC20 = m.contract("ERC20PenaltyFeeStakingFactory", [requestManager]); + m.call(requestManager, "addFactory", [penaltyFeeFactoryERC20], { id: "penaltyFeeFactoryERC20"}); + // Deploying Stakepad's ERC721LockUpStakingFactory contract, passing RequestManager's contract address as an input parameter + const lockUpFactoryERC721 = m.contract("ERC721LockUpStakingFactory", [requestManager]); + m.call(requestManager, "addFactory", [lockUpFactoryERC721], { id: "lockUpFactoryERC721"}); + // Deploying Stakepad's ERC721PenaltyFeeStakingFactory contract, passing RequestManager's contract address as an input parameter + const penaltyFeeFactoryERC721 = m.contract("ERC721PenaltyFeeStakingFactory", [requestManager]); + m.call(requestManager, "addFactory", [penaltyFeeFactoryERC721], { id: "penaltyFeeFactoryERC721"}); + + return { requestManager, lockUpFactoryERC20, penaltyFeeFactoryERC20, lockUpFactoryERC721, penaltyFeeFactoryERC721 }; +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ab2a900..baeff92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "chai": "^4.2.0", "dotenv": "^16.4.5", "ethers": "^6.12.1", - "hardhat": "^2.22.3", + "hardhat": "^2.22.6", "hardhat-gas-reporter": "^2.1.1", "solidity-coverage": "^0.8.12", "ts-node": ">=8.0.0", @@ -806,131 +806,82 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.3.7.tgz", - "integrity": "sha512-v2JFWnFKRsnOa6PDUrD+sr8amcdhxnG/YbL7LzmgRGU1odWEyOF4/EwNeUajQr4ZNKVWrYnJ6XjydXtUge5OBQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.4.1.tgz", + "integrity": "sha512-NgrMo2rI9r28uidumvd+K2/AJLdxtXsUlJr3hj/pM6S1FCd/HiWaLeLa/cjCVPcE2u1rYAa3W6UFxLCB7S5Dhw==", "dev": true, + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.4.1", + "@nomicfoundation/edr-darwin-x64": "0.4.1", + "@nomicfoundation/edr-linux-arm64-gnu": "0.4.1", + "@nomicfoundation/edr-linux-arm64-musl": "0.4.1", + "@nomicfoundation/edr-linux-x64-gnu": "0.4.1", + "@nomicfoundation/edr-linux-x64-musl": "0.4.1", + "@nomicfoundation/edr-win32-x64-msvc": "0.4.1" + }, "engines": { "node": ">= 18" - }, - "optionalDependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.3.7", - "@nomicfoundation/edr-darwin-x64": "0.3.7", - "@nomicfoundation/edr-linux-arm64-gnu": "0.3.7", - "@nomicfoundation/edr-linux-arm64-musl": "0.3.7", - "@nomicfoundation/edr-linux-x64-gnu": "0.3.7", - "@nomicfoundation/edr-linux-x64-musl": "0.3.7", - "@nomicfoundation/edr-win32-x64-msvc": "0.3.7" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.7.tgz", - "integrity": "sha512-6tK9Lv/lSfyBvpEQ4nsTfgxyDT1y1Uv/x8Wa+aB+E8qGo3ToexQ1BMVjxJk6PChXCDOWxB3B4KhqaZFjdhl3Ow==", - "cpu": [ - "arm64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.1.tgz", + "integrity": "sha512-XuiUUnWAVNw7JYv7nRqDWfpBm21HOxCRBQ8lQnRnmiets9Ss2X5Ul9mvBheIPh/D0wBzwJ8TRtsSrorpwE79cA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.7.tgz", - "integrity": "sha512-1RrQ/1JPwxrYO69e0tglFv5H+ggour5Ii3bb727+yBpBShrxtOTQ7fZyfxA5h62LCN+0Z9wYOPeQ7XFcVurMaQ==", - "cpu": [ - "x64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.1.tgz", + "integrity": "sha512-N1MfJqEX5ixaXlyyrHnaYxzwIT27Nc/jUgLI7ts4/9kRvPTvyZRYmXS1ciKhmUFr/WvFckTCix2RJbZoGGtX7g==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.7.tgz", - "integrity": "sha512-ds/CKlBoVXIihjhflhgPn13EdKWed6r5bgvMs/YwRqT5wldQAQJZWAfA2+nYm0Yi2gMGh1RUpBcfkyl4pq7G+g==", - "cpu": [ - "arm64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.1.tgz", + "integrity": "sha512-bSPOfmcFjJwDgWOV5kgZHeqg2OWu1cINrHSGjig0aVHehjcoX4Sgayrj6fyAxcOV5NQKA6WcyTFll6NrCxzWRA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.7.tgz", - "integrity": "sha512-e29udiRaPujhLkM3+R6ju7QISrcyOqpcaxb2FsDWBkuD7H8uU9JPZEyyUIpEp5uIY0Jh1eEJPKZKIXQmQAEAuw==", - "cpu": [ - "arm64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.1.tgz", + "integrity": "sha512-F/+DgOdeBFQDrk+SX4aFffJFBgJfd75ZtE2mjcWNAh/qWiS7NfUxdQX/5OvNo/H6EY4a+3bZH6Bgzqg4mEWvMw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.7.tgz", - "integrity": "sha512-/xkjmTyv+bbJ4akBCW0qzFKxPOV4AqLOmqurov+s9umHb16oOv72osSa3SdzJED2gHDaKmpMITT4crxbar4Axg==", - "cpu": [ - "x64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.1.tgz", + "integrity": "sha512-POHhTWczIXCPhzKtY0Vt/l+VCqqCx5gNR5ErwSrNnLz/arfQobZFAU+nc61BX3Jch82TW8b3AbfGI73Kh7gO0w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.7.tgz", - "integrity": "sha512-QwBP9xlmsbf/ldZDGLcE4QiAb8Zt46E/+WLpxHBATFhGa7MrpJh6Zse+h2VlrT/SYLPbh2cpHgSmoSlqVxWG9g==", - "cpu": [ - "x64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.1.tgz", + "integrity": "sha512-uu8oNp4Ozg3H1x1We0FF+rwXfFiAvsOm5GQ+OBx9YYOXnfDPWqguQfGIkhrti9GD0iYhfQ/WOG5wvp0IzzgGSg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.7.tgz", - "integrity": "sha512-j/80DEnkxrF2ewdbk/gQ2EOPvgF0XSsg8D0o4+6cKhUVAW6XwtWKzIphNL6dyD2YaWEPgIrNvqiJK/aln0ww4Q==", - "cpu": [ - "x64" - ], + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.1.tgz", + "integrity": "sha512-PaZHFw455z89ZiKYNTnKu+/TiVZVRI+mRJsbRTe2N0VlYfUBS1o2gdXBM12oP1t198HR7xQwEPPAslTFxGBqHA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], "engines": { "node": ">= 18" } @@ -2521,10 +2472,13 @@ } }, "node_modules/commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -3432,14 +3386,14 @@ } }, "node_modules/hardhat": { - "version": "2.22.3", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.3.tgz", - "integrity": "sha512-k8JV2ECWNchD6ahkg2BR5wKVxY0OiKot7fuxiIpRK0frRqyOljcR2vKwgWSLw6YIeDcNNA4xybj7Og7NSxr2hA==", + "version": "2.22.6", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.6.tgz", + "integrity": "sha512-abFEnd9QACwEtSvZZGSmzvw7N3zhQN1cDKz5SLHAupfG24qTHofCjqvD5kT5Wwsq5XOL0ON1Mq5rr4v0XX5ciw==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.3.5", + "@nomicfoundation/edr": "^0.4.1", "@nomicfoundation/ethereumjs-common": "4.0.4", "@nomicfoundation/ethereumjs-tx": "5.0.4", "@nomicfoundation/ethereumjs-util": "9.0.4", @@ -3473,7 +3427,7 @@ "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", - "solc": "0.7.3", + "solc": "0.8.26", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", "tsort": "0.0.1", @@ -4183,15 +4137,6 @@ "node": ">=0.10.0" } }, - "node_modules/klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -5160,18 +5105,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -5506,48 +5439,24 @@ } }, "node_modules/solc": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", - "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", "dev": true, "dependencies": { "command-exists": "^1.2.8", - "commander": "3.0.2", + "commander": "^8.1.0", "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", "js-sha3": "0.8.0", "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", "semver": "^5.5.0", "tmp": "0.0.33" }, "bin": { - "solcjs": "solcjs" + "solcjs": "solc.js" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/solc/node_modules/fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "node_modules/solc/node_modules/jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": ">=10.0.0" } }, "node_modules/solc/node_modules/semver": { diff --git a/package.json b/package.json index 62b0821..e9f9b27 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "chai": "^4.2.0", "dotenv": "^16.4.5", "ethers": "^6.12.1", - "hardhat": "^2.22.3", + "hardhat": "^2.22.6", "hardhat-gas-reporter": "^2.1.1", "solidity-coverage": "^0.8.12", "ts-node": ">=8.0.0", From 942d9329472878e0f33f8c21bf67a103c44be286 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 10 Jul 2024 20:00:49 +0300 Subject: [PATCH 3/4] [Update] Merged and edited readme --- README.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0ded7b..0af538f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,84 @@ -# stakepad-contracts -Designing a Better World Through Decentralized Technologies + +# StakePad Project + +This repository contains the smart contracts and tests for the Stakepad project. + +## Setup + +### Installation + +Install the required dependencies by running: + +``` +npm install +``` + +### Environment Variables + +Before running the project, you need to set up the environment variables. Create a `.env` file in the root directory of the project and add the following variables: + +``` +AMOY_API_KEY="Your_Amoy_API_Key" +PRIVATE_KEY="0xYour_Wallet_Private_Key" +``` + +To get your AMOY API key, please refer to the [AMOY Documentation](https://docs.polygonscan.com/getting-started/viewing-api-usage-statistics#creating-an-api-key). + +## Running Tests + +To run all unit tests, use the following command: + +``` +npx hardhat test +``` + +Make sure you have provided the necessary environment variables in the `.env` file before running the tests. + +## Deployment + +Before deploying the smart contracts, ensure you have set up all required parameters in your `.env` file as described above. + +### Deploying the Contracts + +To deploy the main Stakepad smart contracts, run the following command: + +``` +npx hardhat ignition deploy ignition/modules/stakepad.ts --network `network` +``` + +Replace `network` with the desired network name. The network should be configured in your `hardhat.config.ts` file under the `networks` section. Please refer to the [Hardhat Networks Configuration](https://hardhat.org/hardhat-runner/docs/config#networks-configuration) guide for more information. + +Currently, the following networks are already configured: + +- hardhat (default localhost) +- amoy (also known as polygon-amoy, Polygon PoS testnet) + + +Example for deploying to the amoy network: + +```bash +npx hardhat ignition deploy ignition/modules/stakepad.ts -network 'amoy' +``` + +Example for deploying to the default hardhat network: + +In the first terminal run the hardhat node: + +```bash +npx hardhat node +``` + +In the second terminal run the deployment module: + +```bash +npx hardhat ignition deploy ignition/modules/stakepad.ts -network 'localhost' +``` + +## Documentation + +This repository contains the specifications and audit reports for the ERC20 Lockup Staking Pool smart contracts. For more detailed information, please refer to the [Documentation](./docs/README.md). + +## Support + +If you have any questions or need further assistance, feel free to open an issue on the repository or contact the maintainers. + From ad622a18cd72bc767f2079d412c6e545f66fc4c1 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 10 Jul 2024 20:11:00 +0300 Subject: [PATCH 4/4] [Fix] Minor fixes --- contracts/interfaces/IFactories/IGenericFactory.sol | 2 +- test/ERC20LockUpStakingPool.test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/IFactories/IGenericFactory.sol b/contracts/interfaces/IFactories/IGenericFactory.sol index 3ee81bc..0d3f9b2 100644 --- a/contracts/interfaces/IFactories/IGenericFactory.sol +++ b/contracts/interfaces/IFactories/IGenericFactory.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.25; /// @title Generic Factory Interface -/// @notice Defines the main errors and events used in all inhereted factories from the generic one. +/// @notice Defines the main errors and events used in all inherited factories from the generic one. /// @dev This interface is used in a GenericFactory contract which is inherited by the other specific factory contracts. interface IGenericFactory { /// @notice Error to indicate when an invalid request manager contract's address was submitted. diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index 7fdb3e3..2926aa6 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -153,7 +153,7 @@ describe("Contract Deployment", async function () { } let lengthBefore = (await requestManager.getRequests()).length; - await expect(requestManager.connect(user_A).requestDeployment(requestPayload)).to.be.revertedWithCustomError(requestManager, "InvalidIpfsHash"); + await expect(requestManager.connect(user_A).requestDeployment(requestPayload)).to.be.revertedWithCustomError(requestManager, "IpfsZeroHash"); let length = (await requestManager.getRequests()).length; expect(lengthBefore).to.be.equal(length); }); @@ -167,7 +167,7 @@ describe("Contract Deployment", async function () { } let lengthBefore = (await requestManager.getRequests()).length; - await expect(requestManager.connect(user_A).requestDeployment(requestPayload)).to.be.revertedWithCustomError(requestManager, "InvalidPayload"); + await expect(requestManager.connect(user_A).requestDeployment(requestPayload)).to.be.revertedWithCustomError(requestManager, "EmptyPayload"); let length = (await requestManager.getRequests()).length; expect(lengthBefore).to.be.equal(length); }); @@ -258,11 +258,11 @@ describe("Contract Deployment", async function () { expect(req.requestStatus).to.be.equal(3); }); - it("Deploy failed: invalid caller", async function () { + it("Deploy failed: invalid deployer", async function () { let length = (await requestManager.getRequests()).length; let req = await requestManager.requests(length - 1); expect(req.requestStatus).to.be.equal(3); - await expect(requestManager.connect(user_B).deploy(length - 1)).to.be.revertedWithCustomError(requestManager, "InvalidCaller"); + await expect(requestManager.connect(user_B).deploy(length - 1)).to.be.revertedWithCustomError(requestManager, "InvalidDeployer"); }); it("Deploy failed: invalid id", async function () {