From c7b2ff32d990bd5c50844dc743a4103c3141eaeb Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 14 May 2024 14:06:01 +0300 Subject: [PATCH 01/13] [CI] Added coverage workflow --- .github/workflows/coverage.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..f67b188 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,30 @@ +name: Solidity Coverage + +on: + push: + branches: [ develop ] + pull_request: + branches: [ master, develop ] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [ 20.x ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set .env + run: cp .env.example .env + + - name: Run tests + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run coverage + - uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file From 835449f95d6b637968799923fe7984c59fcdbe1c Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 14 May 2024 14:12:48 +0300 Subject: [PATCH 02/13] [Fix] Added coverage to hh config --- hardhat.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index f50e02f..009b79d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -3,6 +3,7 @@ import { HardhatUserConfig } from "hardhat/config"; import '@nomicfoundation/hardhat-verify'; import 'hardhat-gas-reporter'; import '@typechain/hardhat'; +import 'solidity-coverage'; import '@nomicfoundation/hardhat-network-helpers'; import '@nomicfoundation/hardhat-ethers'; import '@nomicfoundation/hardhat-chai-matchers'; From 49d87bf8fc3c4acd6493fae92ccb4ed0a0eca615 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 14 May 2024 15:00:12 +0300 Subject: [PATCH 03/13] [Update] Added ERC721 factories, code refactoring --- contracts/factories/ERC20LockUpFactory.sol | 14 +- contracts/factories/ERC20NoLockUpFactory.sol | 16 +-- .../factories/ERC20PenaltyFeeFactory.sol | 8 +- contracts/factories/ERC721LockupFactory.sol | 122 ++++++++++++++++++ contracts/factories/ERC721NoLockupFactory.sol | 120 +++++++++++++++++ .../factories/ERC721PenaltyFeeFactory.sol | 111 ++++++++++++++++ .../IERC20Pools/IERC20LockUpPool.sol | 6 +- .../IERC20Pools/IERC20PenaltyPool.sol | 2 +- .../interfaces/IERC721/IERC721LockUpPool.sol | 10 +- .../IERC721/IERC721PenaltyFeePool.sol | 2 +- .../IBaseFactory.sol} | 2 +- .../ILockupFactory.sol} | 4 +- .../INoLockupFactory.sol} | 4 +- .../IPenaltyFeeFactory.sol} | 4 +- contracts/pools/ERC20LockUpStakingPool.sol | 10 +- contracts/pools/ERC20NoLockUpStakingPool.sol | 2 +- .../pools/ERC721/ERC721LockUpStakingPool.sol | 16 +-- .../ERC721/ERC721NoLockUpStakingPool.sol | 2 +- .../pools/ERC721/ERC721PenaltyFeePool.sol | 4 +- ignition/modules/factories.ts | 8 +- test/ERC20LockUpStakingPool.test.ts | 16 +-- 21 files changed, 418 insertions(+), 65 deletions(-) create mode 100644 contracts/factories/ERC721LockupFactory.sol create mode 100644 contracts/factories/ERC721NoLockupFactory.sol create mode 100644 contracts/factories/ERC721PenaltyFeeFactory.sol rename contracts/interfaces/{IERC20Factories/IERC20BaseFactory.sol => IFactories/IBaseFactory.sol} (93%) rename contracts/interfaces/{IERC20Factories/IERC20LockUpFactory.sol => IFactories/ILockupFactory.sol} (86%) rename contracts/interfaces/{IERC20Factories/IERC20NoLockupFactory.sol => IFactories/INoLockupFactory.sol} (83%) rename contracts/interfaces/{IERC20Factories/IERC20PenaltyFeeFactory.sol => IFactories/IPenaltyFeeFactory.sol} (83%) diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index 166e233..5997d10 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockUpFactory +ERC20LockupFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20LockupPool} from "../pools/ERC20LockUpStakingPool.sol"; -import {IERC20LockUpFactory} from "../interfaces/IERC20Factories/IERC20LockUpFactory.sol"; +import {ERC20LockupPool} from "../pools/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"; -/// @title ERC20LockUpStakingFactory -/// @notice A smart contract for deploying ERC20 lockup staking pools. +/// @title ERC20LockupStakingFactory +/// @notice A smart contract for deploying ERC20 Lockup staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { +contract ERC20LockupStakingFactory is Ownable, ILockupFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,7 +22,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the lockup staking pool with specified parameters + /// @notice Function allows users to deploy the Lockup staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; diff --git a/contracts/factories/ERC20NoLockUpFactory.sol b/contracts/factories/ERC20NoLockUpFactory.sol index 9d6e7dd..288be53 100644 --- a/contracts/factories/ERC20NoLockUpFactory.sol +++ b/contracts/factories/ERC20NoLockUpFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockUpFactory +ERC20LockupFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol"; -import {IERC20NoLockupFactory} from "../interfaces/IERC20Factories/IERC20NoLockupFactory.sol"; +import {ERC20NoLockupPool} from "../pools/ERC20NoLockupStakingPool.sol"; +import {INoLockupFactory} from "../interfaces/IFactories/INoLockupFactory.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"; -/// @title ERC20LockUpStakingFactory +/// @title ERC20LockupStakingFactory /// @notice A smart contract for deploying ERC20 regular staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { +contract ERC20NoLockupStakingFactory is Ownable, INoLockupFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,14 +22,14 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the lockup staking pool with specified parameters + /// @notice Function allows users to deploy the Lockup staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC20NoLockUpPool{ + new ERC20NoLockupPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -52,7 +52,7 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC20NoLockUpPool(newPoolAddress).transferOwnership(msg.sender); + ERC20NoLockupPool(newPoolAddress).transferOwnership(msg.sender); // Transfer reward tokens from the owner to the contract // slither-disable-next-line arbitrary-send-erc20 IERC20(req.data.rewardToken).safeTransferFrom( diff --git a/contracts/factories/ERC20PenaltyFeeFactory.sol b/contracts/factories/ERC20PenaltyFeeFactory.sol index 8823296..0c616c1 100644 --- a/contracts/factories/ERC20PenaltyFeeFactory.sol +++ b/contracts/factories/ERC20PenaltyFeeFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockUpFactory +ERC20LockupFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; import {ERC20PenaltyFeePool} from "../pools/ERC20PenaltyFeePool.sol"; -import {IERC20PenaltyFeeFactory} from "../interfaces/IERC20Factories/IERC20PenaltyFeeFactory.sol"; +import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.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"; -/// @title ERC20LockUpStakingFactory +/// @title ERC20LockupStakingFactory /// @notice A smart contract for deploying ERC20 staking pools with penalty fees. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20PenaltyFeeStakingFactory is Ownable, IERC20PenaltyFeeFactory { +contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { using SafeERC20 for IERC20; address[] public stakingPools; diff --git a/contracts/factories/ERC721LockupFactory.sol b/contracts/factories/ERC721LockupFactory.sol new file mode 100644 index 0000000..7606877 --- /dev/null +++ b/contracts/factories/ERC721LockupFactory.sol @@ -0,0 +1,122 @@ +/* +ERC20LockupFactory +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 {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 { + using SafeERC20 for IERC20; + + address[] public stakingPools; + Request[] public requests; + mapping(uint256 id => address pool) public poolById; + + constructor() Ownable(msg.sender) {} + + /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + function deploy(uint256 id) public returns (address newPoolAddress) { + if (requests.length < id) revert InvalidId(); + Request memory req = requests[id]; + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); + if (msg.sender != req.deployer) revert InvalidCaller(); + newPoolAddress = address( + new ERC721LockupPool{ + salt: keccak256( + abi.encode( + req.data.stakeToken, + req.data.rewardToken, + req.data.rewardPerSecond, + req.data.poolStartTime, + req.data.poolEndTime + ) + ) + }( + req.data.stakeToken, + req.data.rewardToken, + req.data.poolStartTime, + req.data.poolEndTime, + req.data.unstakeLockupTime, + req.data.claimLockupTime, + req.data.rewardPerSecond + ) + ); + stakingPools.push(newPoolAddress); + requests[id].requestStatus = Status.DEPLOYED; + poolById[id] = newPoolAddress; + uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * + req.data.rewardPerSecond; + ERC721LockupPool(newPoolAddress).transferOwnership(msg.sender); + // Transfer reward tokens from the owner to the contract + // slither-disable-next-line arbitrary-send-erc20 + IERC20(req.data.rewardToken).safeTransferFrom( + msg.sender, + newPoolAddress, + rewardAmount + ); + emit StakingPoolDeployed(newPoolAddress, id); + } + + function requestDeployment(DeploymentData calldata data) external { + if (data.stakeToken == address(0) || data.rewardToken == address(0)) + revert InvalidTokenAddress(); + if (data.rewardPerSecond == 0) revert InvalidRewardRate(); + requests.push( + Request({ + deployer: msg.sender, + requestStatus: Status.CREATED, + data: data + }) + ); + emit RequestSubmitted( + requests.length - 1, + msg.sender, + Status.CREATED, + data + ); + } + + function approveRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function denyRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function cancelRequest(uint256 id) external { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (msg.sender != req.deployer) revert InvalidCaller(); + if ( + req.requestStatus != Status.CREATED && + req.requestStatus != Status.APPROVED + ) revert InvalidRequestStatus(); + req.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function getRequests() external view returns (Request[] memory reqs) { + reqs = requests; + } + + function getPools() external view returns (address[] memory pools) { + pools = stakingPools; + } +} diff --git a/contracts/factories/ERC721NoLockupFactory.sol b/contracts/factories/ERC721NoLockupFactory.sol new file mode 100644 index 0000000..1f33534 --- /dev/null +++ b/contracts/factories/ERC721NoLockupFactory.sol @@ -0,0 +1,120 @@ +/* +ERC20LockupFactory +SPDX-License-Identifier: MIT +*/ + +pragma solidity 0.8.25; +import {ERC721NoLockupPool} from "../pools/ERC721/ERC721NoLockupStakingPool.sol"; +import {INoLockupFactory} from "../interfaces/IFactories/INoLockupFactory.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"; + +/// @title ERC721NoLockupStakingFactory +/// @notice A smart contract for deploying ERC721 regular staking pools. +/// @author Ayooluwa Akindeko, Soramitsu team +contract ERC721NoLockupStakingFactory is Ownable, INoLockupFactory { + using SafeERC20 for IERC20; + + address[] public stakingPools; + Request[] public requests; + mapping(uint256 id => address pool) public poolById; + + constructor() Ownable(msg.sender) {} + + /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + function deploy(uint256 id) public returns (address newPoolAddress) { + if (requests.length < id) revert InvalidId(); + Request memory req = requests[id]; + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); + if (msg.sender != req.deployer) revert InvalidCaller(); + newPoolAddress = address( + new ERC721NoLockupPool{ + salt: keccak256( + abi.encode( + req.data.stakeToken, + req.data.rewardToken, + req.data.rewardPerSecond, + req.data.poolStartTime, + req.data.poolEndTime + ) + ) + }( + req.data.stakeToken, + req.data.rewardToken, + req.data.rewardPerSecond, + req.data.poolStartTime, + req.data.poolEndTime + ) + ); + stakingPools.push(newPoolAddress); + requests[id].requestStatus = Status.DEPLOYED; + poolById[id] = newPoolAddress; + uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * + req.data.rewardPerSecond; + ERC721NoLockupPool(newPoolAddress).transferOwnership(msg.sender); + // Transfer reward tokens from the owner to the contract + // slither-disable-next-line arbitrary-send-erc20 + IERC20(req.data.rewardToken).safeTransferFrom( + msg.sender, + newPoolAddress, + rewardAmount + ); + emit StakingPoolDeployed(newPoolAddress, id); + } + + function requestDeployment(DeploymentData calldata data) external { + if (data.stakeToken == address(0) || data.rewardToken == address(0)) + revert InvalidTokenAddress(); + if (data.rewardPerSecond == 0) revert InvalidRewardRate(); + requests.push( + Request({ + deployer: msg.sender, + requestStatus: Status.CREATED, + data: data + }) + ); + emit RequestSubmitted( + requests.length - 1, + msg.sender, + Status.CREATED, + data + ); + } + + function approveRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function denyRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function cancelRequest(uint256 id) external { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (msg.sender != req.deployer) revert InvalidCaller(); + if ( + req.requestStatus != Status.CREATED || + req.requestStatus != Status.APPROVED + ) revert InvalidRequestStatus(); + req.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function getRequests() external view returns (Request[] memory reqs) { + reqs = requests; + } + + function getPools() external view returns (address[] memory pools) { + pools = stakingPools; + } +} diff --git a/contracts/factories/ERC721PenaltyFeeFactory.sol b/contracts/factories/ERC721PenaltyFeeFactory.sol new file mode 100644 index 0000000..130c023 --- /dev/null +++ b/contracts/factories/ERC721PenaltyFeeFactory.sol @@ -0,0 +1,111 @@ +/* +ERC20LockupFactory +SPDX-License-Identifier: MIT +*/ + +pragma solidity 0.8.25; +import {draftERC721PenaltyFeepPool} from "../pools/ERC721/ERC721PenaltyFeePool.sol"; +import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.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"; + +/// @title ERC721PenaltyFeeStakingFactory +/// @notice A smart contract for deploying ERC721 staking pools with penalty fees. +/// @author Ayooluwa Akindeko, Soramitsu team +contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { + using SafeERC20 for IERC20; + + address[] public stakingPools; + Request[] public requests; + mapping(uint256 id => address pool) public poolById; + + constructor() Ownable(msg.sender) {} + + /// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters + function deploy(uint256 id) public returns (address newPoolAddress) { + if (requests.length < id) revert InvalidId(); + Request memory req = requests[id]; + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); + if (msg.sender != req.deployer) revert InvalidCaller(); + newPoolAddress = address( + new draftERC721PenaltyFeepPool{ + salt: keccak256( + abi.encode( + req.data.stakeToken, + req.data.rewardToken, + req.data.rewardPerSecond, + req.data.poolStartTime, + req.data.poolEndTime + ) + ) + }( + req.data.stakeToken, + req.data.rewardToken, + req.data.rewardPerSecond, + req.data.poolStartTime, + req.data.poolEndTime, + req.data.penaltyPeriod, + owner() + ) + ); + stakingPools.push(newPoolAddress); + draftERC721PenaltyFeepPool(newPoolAddress).transferOwnership(msg.sender); + emit StakingPoolDeployed(newPoolAddress, id); + } + + function requestDeployment(DeploymentData calldata data) external { + if (data.stakeToken == address(0) || data.rewardToken == address(0)) + revert InvalidTokenAddress(); + if (data.rewardPerSecond == 0) revert InvalidRewardRate(); + requests.push( + Request({ + deployer: msg.sender, + requestStatus: Status.CREATED, + data: data + }) + ); + emit RequestSubmitted( + requests.length - 1, + msg.sender, + Status.CREATED, + data + ); + } + + function approveRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function denyRequest(uint256 id) external onlyOwner { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function cancelRequest(uint256 id) external { + if (requests.length < id) revert InvalidId(); + Request storage req = requests[id]; + if (msg.sender != req.deployer) revert InvalidCaller(); + if ( + req.requestStatus != Status.CREATED || + req.requestStatus != Status.APPROVED + ) revert InvalidRequestStatus(); + req.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.requestStatus); + } + + function getRequests() external view returns (Request[] memory reqs) { + reqs = requests; + } + + function getPools() external view returns (address[] memory pools) { + pools = stakingPools; + } +} diff --git a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol index 28201c1..5629929 100644 --- a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol @@ -10,7 +10,7 @@ interface IERC20LockupPool is IBasePoolERC20 { uint256 pending; // Pending rewards } - struct LockUpPool { + struct LockupPool { address stakeToken; // ERC20 token being staked address rewardToken; // ERC20 token used for rewards uint256 startTime; // Start time of the staking pool @@ -28,11 +28,11 @@ interface IERC20LockupPool is IBasePoolERC20 { * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in lockup and cannot be accessed + /// @dev Error to indicate that tokens are still in Lockup and cannot be accessed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked error TokensInLockup(uint256 currentTime, uint256 unlockTime); - /// @dev Error to indicate an invalid lockup time for unstaking or claiming rewards + /// @dev Error to indicate an invalid Lockup time for unstaking or claiming rewards error InvalidLockupTime(); } diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol index 99e79d4..3221ca0 100644 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol @@ -30,7 +30,7 @@ interface IERC20PenaltyPool is IBasePoolERC20 { /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in lockup and cannot be claimed + /// @dev Error to indicate that tokens are still in Lockup and cannot be claimed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked for claim error ClaimInLockup(uint256 currentTime, uint256 unlockTime); diff --git a/contracts/interfaces/IERC721/IERC721LockUpPool.sol b/contracts/interfaces/IERC721/IERC721LockUpPool.sol index 2a56d6a..f14dda8 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPool.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPool.sol @@ -5,7 +5,7 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721LockUpPool is IERC721BasePool{ +interface IERC721LockupPool is IERC721BasePool{ /** * @notice Storage for a user's staking information @@ -27,8 +27,8 @@ interface IERC721LockUpPool is IERC721BasePool{ * @dev rewardToken The address of the ERC20 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 @@ -54,11 +54,11 @@ interface IERC721LockUpPool is IERC721BasePool{ /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in lockup and cannot be accessed + /// @dev Error to indicate that tokens are still in Lockup and cannot be accessed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked error TokensInLockup(uint256 currentTime, uint256 unlockTime); - /// @dev Error to indicate an invalid lockup time for unstaking or claiming rewards + /// @dev Error to indicate an invalid Lockup time for unstaking or claiming rewards error InvalidLockupTime(); } diff --git a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol index fb357fe..8a86316 100644 --- a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol +++ b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol @@ -55,7 +55,7 @@ interface IERC721PenaltyFeePool is IERC721BasePool{ /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in lockup and cannot be claimed + /// @dev Error to indicate that tokens are still in Lockup and cannot be claimed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked for claim error ClaimInLockup(uint256 currentTime, uint256 unlockTime); diff --git a/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol b/contracts/interfaces/IFactories/IBaseFactory.sol similarity index 93% rename from contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol rename to contracts/interfaces/IFactories/IBaseFactory.sol index 652a5cd..da15271 100644 --- a/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol +++ b/contracts/interfaces/IFactories/IBaseFactory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -interface IERC20BaseFactory { +interface IBaseFactory { enum Status { UNKNOWN, CREATED, diff --git a/contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol b/contracts/interfaces/IFactories/ILockupFactory.sol similarity index 86% rename from contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol rename to contracts/interfaces/IFactories/ILockupFactory.sol index 9884dd7..f9d55e8 100644 --- a/contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol +++ b/contracts/interfaces/IFactories/ILockupFactory.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IERC20BaseFactory} from "./IERC20BaseFactory.sol"; +import {IBaseFactory} from "./IBaseFactory.sol"; -interface IERC20LockUpFactory is IERC20BaseFactory { +interface ILockupFactory is IBaseFactory { struct DeploymentData { address stakeToken; diff --git a/contracts/interfaces/IERC20Factories/IERC20NoLockupFactory.sol b/contracts/interfaces/IFactories/INoLockupFactory.sol similarity index 83% rename from contracts/interfaces/IERC20Factories/IERC20NoLockupFactory.sol rename to contracts/interfaces/IFactories/INoLockupFactory.sol index 0215ac4..2218c56 100644 --- a/contracts/interfaces/IERC20Factories/IERC20NoLockupFactory.sol +++ b/contracts/interfaces/IFactories/INoLockupFactory.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IERC20BaseFactory} from "./IERC20BaseFactory.sol"; +import {IBaseFactory} from "./IBaseFactory.sol"; -interface IERC20NoLockupFactory is IERC20BaseFactory { +interface INoLockupFactory is IBaseFactory { struct DeploymentData { address stakeToken; diff --git a/contracts/interfaces/IERC20Factories/IERC20PenaltyFeeFactory.sol b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol similarity index 83% rename from contracts/interfaces/IERC20Factories/IERC20PenaltyFeeFactory.sol rename to contracts/interfaces/IFactories/IPenaltyFeeFactory.sol index 185c91e..ae21237 100644 --- a/contracts/interfaces/IERC20Factories/IERC20PenaltyFeeFactory.sol +++ b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IERC20BaseFactory} from "./IERC20BaseFactory.sol"; +import {IBaseFactory} from "./IBaseFactory.sol"; -interface IERC20PenaltyFeeFactory is IERC20BaseFactory { +interface IPenaltyFeeFactory is IBaseFactory { struct DeploymentData { address stakeToken; diff --git a/contracts/pools/ERC20LockUpStakingPool.sol b/contracts/pools/ERC20LockUpStakingPool.sol index 8a847a0..e339a22 100644 --- a/contracts/pools/ERC20LockUpStakingPool.sol +++ b/contracts/pools/ERC20LockUpStakingPool.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; // Import OpenZeppelin contracts for ERC20 token interaction, reentrancy protection, safe token transfers, and ownership management. import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20LockupPool} from "../interfaces/IERC20Pools/IERC20LockUpPool.sol"; +import {IERC20LockupPool} from "../interfaces/IERC20Pools/IERC20LockupPool.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -17,7 +17,7 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { uint256 public constant PRECISION_FACTOR = 10e18; ///@dev Public pool variable to access pool data - LockUpPool public pool; + LockupPool public pool; ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; @@ -49,7 +49,7 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { if (poolStartTime < block.timestamp) revert InvalidStartTime(); // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); - // Ensure the lockup periods are valid + // Ensure the Lockup periods are valid if (unstakeLockup > poolEndTime || claimLockup > poolEndTime) revert InvalidLockupTime(); @@ -105,7 +105,7 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { */ function unstake(uint256 amount) external nonReentrant { if (amount == 0) revert InvalidAmount(); - // Check if the current timestamp is before the unstake lockup time + // Check if the current timestamp is before the unstake Lockup time if (block.timestamp < pool.unstakeLockupTime) revert TokensInLockup(block.timestamp, pool.unstakeLockupTime); // Get user information @@ -139,7 +139,7 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { * @dev See {IBasePoolERC20-claim}. */ function claim() external nonReentrant { - // Check if the current timestamp is before the claim lockup time + // Check if the current timestamp is before the claim Lockup time if (block.timestamp < pool.claimLockupTime) revert TokensInLockup(block.timestamp, pool.claimLockupTime); // Update the pool diff --git a/contracts/pools/ERC20NoLockUpStakingPool.sol b/contracts/pools/ERC20NoLockUpStakingPool.sol index f063051..8f550df 100644 --- a/contracts/pools/ERC20NoLockUpStakingPool.sol +++ b/contracts/pools/ERC20NoLockUpStakingPool.sol @@ -8,7 +8,7 @@ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -contract ERC20NoLockUpPool is +contract ERC20NoLockupPool is ReentrancyGuard, Ownable, IERC20NoLockupPool diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 0cc0d56..7ae1db6 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -6,12 +6,12 @@ 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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721LockUpPool} from "../../interfaces/IERC721/IERC721LockUpPool.sol"; +import {IERC721LockupPool} from "../../interfaces/IERC721/IERC721LockupPool.sol"; -contract ERC721LockUpPool is +contract ERC721LockupPool is ReentrancyGuard, Ownable, - IERC721LockUpPool + IERC721LockupPool { using SafeERC20 for IERC20; /// @dev Precision factor for calculations @@ -35,14 +35,14 @@ contract ERC721LockUpPool is uint256 poolStartTime, uint256 poolEndTime, uint256 unstakeLockupTime, - uint256 claimLockUpTime + uint256 claimLockupTime ) Ownable(msg.sender) { // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); - // Ensure the lockup periods are valid - if (unstakeLockupTime > poolEndTime || claimLockUpTime > poolEndTime) + // Ensure the Lockup periods are valid + if (unstakeLockupTime > poolEndTime || claimLockupTime > poolEndTime) revert InvalidLockupTime(); pool.stakeToken = IERC721(stakeToken); @@ -50,7 +50,7 @@ contract ERC721LockUpPool is pool.startTime = poolStartTime; pool.endTime = poolEndTime; pool.unstakeLockupTime = unstakeLockupTime; - pool.claimLockupTime = claimLockUpTime; + pool.claimLockupTime = claimLockupTime; pool.rewardTokenPerSecond = rewardTokenPerSecond; pool.lastUpdateTimestamp = pool.startTime; } @@ -142,7 +142,7 @@ contract ERC721LockUpPool is * @dev See {IERC721BasePool-claim}. */ function claim() external nonReentrant { - // Check if the current timestamp is before the claim lockup time + // Check if the current timestamp is before the claim Lockup time if (block.timestamp < pool.claimLockupTime) revert TokensInLockup(block.timestamp, pool.claimLockupTime); diff --git a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol index 3b5e1e9..14af48f 100644 --- a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol @@ -8,7 +8,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC721NoLockupPool} from "../../interfaces/IERC721/IERC721NoLockupPool.sol"; -contract ERC721LockUpPool is +contract ERC721NoLockupPool is ReentrancyGuard, Ownable, IERC721NoLockupPool diff --git a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol index 8f00d3f..9bb2b35 100644 --- a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -43,7 +43,7 @@ contract draftERC721PenaltyFeepPool is if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); - // Ensure the lockup periods are valid + // Ensure the Lockup periods are valid if (poolEndTime - poolStartTime > penaltyPeriod) revert InvalidPenaltyPeriod(); @@ -146,7 +146,7 @@ contract draftERC721PenaltyFeepPool is function claim() external nonReentrant { // Get user information UserInfo storage user = userInfo[msg.sender]; - // Check if the current timestamp is before the claim lockup time + // Check if the current timestamp is before the claim Lockup time if (block.timestamp < user.penaltyEndTime) revert ClaimInLockup(block.timestamp, user.penaltyEndTime); // Update the pool diff --git a/ignition/modules/factories.ts b/ignition/modules/factories.ts index 2eda6fc..1e2e02c 100644 --- a/ignition/modules/factories.ts +++ b/ignition/modules/factories.ts @@ -1,8 +1,8 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -export default buildModule("LockUpFactory", (m) => { - const factory = m.contract("ERC20NoLockUpStakingFactory", []); - const lockUpfactory = m.contract("ERC20LockUpStakingFactory", []); +export default buildModule("LockupFactory", (m) => { + const factory = m.contract("ERC20NoLockupStakingFactory", []); + const Lockupfactory = m.contract("ERC20LockupStakingFactory", []); const feeFactory = m.contract("ERC20PenaltyFeeStakingFactory", []); - return { factory, lockUpfactory, feeFactory }; + return { factory, Lockupfactory, feeFactory }; }); \ No newline at end of file diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index 3d01c5e..9435020 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -7,8 +7,8 @@ import { import { ERC20LockupPool, ERC20MockToken, - ERC20LockUpStakingFactory, - ERC20LockUpStakingFactory__factory, + ERC20LockupStakingFactory, + ERC20LockupStakingFactory__factory, } from "../typechain"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { parseEther } from "ethers"; @@ -27,8 +27,8 @@ interface DeploymentParams { let PRECISION_FACTOR = BigInt(10e18); describe("Contract Deployment", async function () { - let StakingFactory: ERC20LockUpStakingFactory__factory; - let ercStakingPoolFactory: ERC20LockUpStakingFactory; + let StakingFactory: ERC20LockupStakingFactory__factory; + let ercStakingPoolFactory: ERC20LockupStakingFactory; let mockStakeToken: ERC20MockToken; let mockRewardToken: ERC20MockToken; let rewardTokenPerSecond: bigint; @@ -44,7 +44,7 @@ describe("Contract Deployment", async function () { before(async () => { StakingFactory = await ethers.getContractFactory( - "ERC20LockUpStakingFactory" + "ERC20LockupStakingFactory" ); ercStakingPoolFactory = await StakingFactory.deploy(); const blockTimestamp = await time.latest(); @@ -77,7 +77,7 @@ describe("Contract Deployment", async function () { ); }); - describe("ERC20LockUpStakingPool Deployment", async function () { + describe("ERC20LockupStakingPool Deployment", async function () { it("Request creation failed: invalid staking token address", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; @@ -255,7 +255,7 @@ describe("Contract Deployment", async function () { expect(req.requestStatus).to.be.equal(3); }); - it("Another requests created with wrong unstake lockup time", async function () { + it("Another requests created with wrong unstake Lockup time", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), @@ -276,7 +276,7 @@ describe("Contract Deployment", async function () { await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockupTime"); }); - it("Another requests created with wrong claim lockup time", async function () { + it("Another requests created with wrong claim Lockup time", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), From 9847461bec5825359a564eff8e8e9df5b24d2541 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 14 May 2024 15:11:32 +0300 Subject: [PATCH 04/13] [Fix] Fixed case sensitive impotrs --- contracts/factories/ERC20LockUpFactory.sol | 22 ++--- contracts/factories/ERC20NoLockUpFactory.sol | 16 ++-- .../factories/ERC20PenaltyFeeFactory.sol | 4 +- contracts/factories/ERC721LockupFactory.sol | 22 ++--- contracts/factories/ERC721NoLockupFactory.sol | 16 ++-- .../factories/ERC721PenaltyFeeFactory.sol | 2 +- .../IERC20Pools/IERC20LockUpPool.sol | 16 ++-- .../IERC20Pools/IERC20NoLockupPool.sol | 2 +- .../IERC20Pools/IERC20PenaltyPool.sol | 4 +- .../interfaces/IERC721/IERC721LockUpPool.sol | 20 ++--- .../IERC721/IERC721NoLockupPool.sol | 2 +- .../IERC721/IERC721PenaltyFeePool.sol | 4 +- .../interfaces/IFactories/ILockupFactory.sol | 6 +- .../IFactories/INoLockupFactory.sol | 2 +- contracts/pools/ERC20LockUpStakingPool.sol | 38 ++++---- contracts/pools/ERC20NoLockUpStakingPool.sol | 6 +- contracts/pools/ERC20PenaltyFeePool.sol | 2 +- .../pools/ERC721/ERC721LockUpStakingPool.sol | 28 +++--- .../ERC721/ERC721NoLockUpStakingPool.sol | 6 +- .../pools/ERC721/ERC721PenaltyFeePool.sol | 6 +- ignition/modules/factories.ts | 8 +- test/ERC20LockUpStakingPool.test.ts | 90 +++++++++---------- 22 files changed, 161 insertions(+), 161 deletions(-) diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index 5997d10..c272a74 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20LockupPool} from "../pools/ERC20LockupStakingPool.sol"; -import {ILockupFactory} from "../interfaces/IFactories/ILockupFactory.sol"; +import {ERC20LockUpPool} from "../pools/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"; -/// @title ERC20LockupStakingFactory -/// @notice A smart contract for deploying ERC20 Lockup staking pools. +/// @title ERC20LockUpStakingFactory +/// @notice A smart contract for deploying ERC20 LockUp staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20LockupStakingFactory is Ownable, ILockupFactory { +contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,14 +22,14 @@ contract ERC20LockupStakingFactory is Ownable, ILockupFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC20LockupPool{ + new ERC20LockUpPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -44,8 +44,8 @@ contract ERC20LockupStakingFactory is Ownable, ILockupFactory { req.data.rewardToken, req.data.poolStartTime, req.data.poolEndTime, - req.data.unstakeLockupTime, - req.data.claimLockupTime, + req.data.unstakeLockUpTime, + req.data.claimLockUpTime, req.data.rewardPerSecond ) ); @@ -54,7 +54,7 @@ contract ERC20LockupStakingFactory is Ownable, ILockupFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC20LockupPool(newPoolAddress).transferOwnership(msg.sender); + ERC20LockUpPool(newPoolAddress).transferOwnership(msg.sender); // Transfer reward tokens from the owner to the contract // slither-disable-next-line arbitrary-send-erc20 IERC20(req.data.rewardToken).safeTransferFrom( diff --git a/contracts/factories/ERC20NoLockUpFactory.sol b/contracts/factories/ERC20NoLockUpFactory.sol index 288be53..18f95e2 100644 --- a/contracts/factories/ERC20NoLockUpFactory.sol +++ b/contracts/factories/ERC20NoLockUpFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20NoLockupPool} from "../pools/ERC20NoLockupStakingPool.sol"; -import {INoLockupFactory} from "../interfaces/IFactories/INoLockupFactory.sol"; +import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol"; +import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.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"; -/// @title ERC20LockupStakingFactory +/// @title ERC20LockUpStakingFactory /// @notice A smart contract for deploying ERC20 regular staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20NoLockupStakingFactory is Ownable, INoLockupFactory { +contract ERC20NoLockUpStakingFactory is Ownable, INoLockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,14 +22,14 @@ contract ERC20NoLockupStakingFactory is Ownable, INoLockupFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC20NoLockupPool{ + new ERC20NoLockUpPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -52,7 +52,7 @@ contract ERC20NoLockupStakingFactory is Ownable, INoLockupFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC20NoLockupPool(newPoolAddress).transferOwnership(msg.sender); + ERC20NoLockUpPool(newPoolAddress).transferOwnership(msg.sender); // Transfer reward tokens from the owner to the contract // slither-disable-next-line arbitrary-send-erc20 IERC20(req.data.rewardToken).safeTransferFrom( diff --git a/contracts/factories/ERC20PenaltyFeeFactory.sol b/contracts/factories/ERC20PenaltyFeeFactory.sol index 0c616c1..a584534 100644 --- a/contracts/factories/ERC20PenaltyFeeFactory.sol +++ b/contracts/factories/ERC20PenaltyFeeFactory.sol @@ -1,5 +1,5 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ @@ -10,7 +10,7 @@ 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"; -/// @title ERC20LockupStakingFactory +/// @title ERC20PenaltyFeeStakingFactory /// @notice A smart contract for deploying ERC20 staking pools with penalty fees. /// @author Ayooluwa Akindeko, Soramitsu team contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { diff --git a/contracts/factories/ERC721LockupFactory.sol b/contracts/factories/ERC721LockupFactory.sol index 7606877..d76ac2f 100644 --- a/contracts/factories/ERC721LockupFactory.sol +++ b/contracts/factories/ERC721LockupFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC721LockupPool} from "../pools/ERC721/ERC721LockupStakingPool.sol"; -import {ILockupFactory} from "../interfaces/IFactories/ILockupFactory.sol"; +import {ERC721LockUpPool} from "../pools/ERC721/ERC721LockUpStakingPool.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"; -/// @title ERC721LockupStakingFactory -/// @notice A smart contract for deploying ERC721 Lockup staking pools. +/// @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 Ownable, ILockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,14 +22,14 @@ contract ERC721LockupStakingFactory is Ownable, ILockupFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC721LockupPool{ + new ERC721LockUpPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -44,8 +44,8 @@ contract ERC721LockupStakingFactory is Ownable, ILockupFactory { req.data.rewardToken, req.data.poolStartTime, req.data.poolEndTime, - req.data.unstakeLockupTime, - req.data.claimLockupTime, + req.data.unstakeLockUpTime, + req.data.claimLockUpTime, req.data.rewardPerSecond ) ); @@ -54,7 +54,7 @@ contract ERC721LockupStakingFactory is Ownable, ILockupFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC721LockupPool(newPoolAddress).transferOwnership(msg.sender); + ERC721LockUpPool(newPoolAddress).transferOwnership(msg.sender); // Transfer reward tokens from the owner to the contract // slither-disable-next-line arbitrary-send-erc20 IERC20(req.data.rewardToken).safeTransferFrom( diff --git a/contracts/factories/ERC721NoLockupFactory.sol b/contracts/factories/ERC721NoLockupFactory.sol index 1f33534..f778c38 100644 --- a/contracts/factories/ERC721NoLockupFactory.sol +++ b/contracts/factories/ERC721NoLockupFactory.sol @@ -1,19 +1,19 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC721NoLockupPool} from "../pools/ERC721/ERC721NoLockupStakingPool.sol"; -import {INoLockupFactory} from "../interfaces/IFactories/INoLockupFactory.sol"; +import {ERC721NoLockUpPool} from "../pools/ERC721/ERC721NoLockUpStakingPool.sol"; +import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.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"; -/// @title ERC721NoLockupStakingFactory +/// @title ERC721NoLockUpStakingFactory /// @notice A smart contract for deploying ERC721 regular staking pools. /// @author Ayooluwa Akindeko, Soramitsu team -contract ERC721NoLockupStakingFactory is Ownable, INoLockupFactory { +contract ERC721NoLockUpStakingFactory is Ownable, INoLockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; @@ -22,14 +22,14 @@ contract ERC721NoLockupStakingFactory is Ownable, INoLockupFactory { constructor() Ownable(msg.sender) {} - /// @notice Function allows users to deploy the Lockup staking pool with specified parameters + /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC721NoLockupPool{ + new ERC721NoLockUpPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -52,7 +52,7 @@ contract ERC721NoLockupStakingFactory is Ownable, INoLockupFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC721NoLockupPool(newPoolAddress).transferOwnership(msg.sender); + ERC721NoLockUpPool(newPoolAddress).transferOwnership(msg.sender); // Transfer reward tokens from the owner to the contract // slither-disable-next-line arbitrary-send-erc20 IERC20(req.data.rewardToken).safeTransferFrom( diff --git a/contracts/factories/ERC721PenaltyFeeFactory.sol b/contracts/factories/ERC721PenaltyFeeFactory.sol index 130c023..c63b8c5 100644 --- a/contracts/factories/ERC721PenaltyFeeFactory.sol +++ b/contracts/factories/ERC721PenaltyFeeFactory.sol @@ -1,5 +1,5 @@ /* -ERC20LockupFactory +ERC20LockUpFactory SPDX-License-Identifier: MIT */ diff --git a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol index 5629929..5272421 100644 --- a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.25; import {IBasePoolERC20} from "./IERC20BasePool.sol"; -interface IERC20LockupPool is IBasePoolERC20 { +interface IERC20LockUpPool is IBasePoolERC20 { struct UserInfo { uint256 amount; // Amount of tokens staked uint256 claimed; // Amount of claimed rewards @@ -10,13 +10,13 @@ interface IERC20LockupPool is IBasePoolERC20 { uint256 pending; // Pending rewards } - struct LockupPool { + struct LockUpPool { address stakeToken; // ERC20 token being staked address rewardToken; // ERC20 token used for rewards uint256 startTime; // Start time of the staking pool uint256 endTime; // End time of the staking pool - uint256 unstakeLockupTime; // Lockup period for unstaking - uint256 claimLockupTime; // Lockup period for claiming rewards + uint256 unstakeLockUpTime; // LockUp period for unstaking + uint256 claimLockUpTime; // LockUp period for claiming rewards uint256 rewardTokenPerSecond; // Rate of rewards per second uint256 totalStaked; // Total amount of tokens staked uint256 totalClaimed; // Total amount of claimed rewards @@ -28,11 +28,11 @@ interface IERC20LockupPool is IBasePoolERC20 { * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in Lockup and cannot be accessed + /// @dev Error to indicate that tokens are still in LockUp and cannot be accessed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked - error TokensInLockup(uint256 currentTime, uint256 unlockTime); + error TokensInLockUp(uint256 currentTime, uint256 unlockTime); - /// @dev Error to indicate an invalid Lockup time for unstaking or claiming rewards - error InvalidLockupTime(); + /// @dev Error to indicate an invalid LockUp time for unstaking or claiming rewards + error InvalidLockUpTime(); } diff --git a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol b/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol index 0c12dbe..7fcfa1e 100644 --- a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {IBasePoolERC20} from "./IERC20BasePool.sol"; -interface IERC20NoLockupPool is IBasePoolERC20 { +interface IERC20NoLockUpPool is IBasePoolERC20 { struct UserInfo { uint256 amount; // Amount of tokens staked uint256 claimed; // Amount of claimed rewards diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol index 3221ca0..f291263 100644 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol @@ -30,10 +30,10 @@ interface IERC20PenaltyPool is IBasePoolERC20 { /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in Lockup and cannot be claimed + /// @dev Error to indicate that tokens are still in LockUp and cannot be claimed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked for claim - error ClaimInLockup(uint256 currentTime, uint256 unlockTime); + error ClaimInLockUp(uint256 currentTime, uint256 unlockTime); /// @dev Error to indicate an invalid penalty duration for unstaking error InvalidPenaltyPeriod(); /// @dev Error to indicate that the caller is not the admin diff --git a/contracts/interfaces/IERC721/IERC721LockUpPool.sol b/contracts/interfaces/IERC721/IERC721LockUpPool.sol index f14dda8..c744d1a 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPool.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPool.sol @@ -5,7 +5,7 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721LockupPool is IERC721BasePool{ +interface IERC721LockUpPool is IERC721BasePool{ /** * @notice Storage for a user's staking information @@ -27,8 +27,8 @@ interface IERC721LockupPool is IERC721BasePool{ * @dev rewardToken The address of the ERC20 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 @@ -36,13 +36,13 @@ interface IERC721LockupPool is IERC721BasePool{ * @dev accRewardPerShare: Accumulated rewards per staked token * @dev stakedTokens: Mapping tokenIds to owner addresses */ - struct LockupPool { + struct LockUpPool { IERC721 stakeToken; IERC20 rewardToken; uint256 startTime; uint256 endTime; - uint256 unstakeLockupTime; // Lockup period for unstaking - uint256 claimLockupTime; // Lockup period for claiming rewards + uint256 unstakeLockUpTime; // LockUp period for unstaking + uint256 claimLockUpTime; // LockUp period for claiming rewards uint256 rewardTokenPerSecond; uint256 totalStaked; uint256 totalClaimed; @@ -54,11 +54,11 @@ interface IERC721LockupPool is IERC721BasePool{ /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in Lockup and cannot be accessed + /// @dev Error to indicate that tokens are still in LockUp and cannot be accessed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked - error TokensInLockup(uint256 currentTime, uint256 unlockTime); + error TokensInLockUp(uint256 currentTime, uint256 unlockTime); - /// @dev Error to indicate an invalid Lockup time for unstaking or claiming rewards - error InvalidLockupTime(); + /// @dev Error to indicate an invalid LockUp time for unstaking or claiming rewards + error InvalidLockUpTime(); } diff --git a/contracts/interfaces/IERC721/IERC721NoLockupPool.sol b/contracts/interfaces/IERC721/IERC721NoLockupPool.sol index 9a71ce6..18d0589 100644 --- a/contracts/interfaces/IERC721/IERC721NoLockupPool.sol +++ b/contracts/interfaces/IERC721/IERC721NoLockupPool.sol @@ -5,7 +5,7 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721NoLockupPool is IERC721BasePool{ +interface IERC721NoLockUpPool is IERC721BasePool{ /** * @notice Storage for a user's staking information diff --git a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol index 8a86316..5d6c78f 100644 --- a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol +++ b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol @@ -55,10 +55,10 @@ interface IERC721PenaltyFeePool is IERC721BasePool{ /** * ERROR MESSAGES */ - /// @dev Error to indicate that tokens are still in Lockup and cannot be claimed + /// @dev Error to indicate that tokens are still in LockUp and cannot be claimed /// @param currentTime The current timestamp /// @param unlockTime The timestamp when the tokens will be unlocked for claim - error ClaimInLockup(uint256 currentTime, uint256 unlockTime); + error ClaimInLockUp(uint256 currentTime, uint256 unlockTime); /// @dev Error to indicate an invalid penalty duration for unstaking error InvalidPenaltyPeriod(); /// @dev Error to indicate that the caller is not the admin diff --git a/contracts/interfaces/IFactories/ILockupFactory.sol b/contracts/interfaces/IFactories/ILockupFactory.sol index f9d55e8..e6a00a7 100644 --- a/contracts/interfaces/IFactories/ILockupFactory.sol +++ b/contracts/interfaces/IFactories/ILockupFactory.sol @@ -2,15 +2,15 @@ pragma solidity 0.8.25; import {IBaseFactory} from "./IBaseFactory.sol"; -interface ILockupFactory is IBaseFactory { +interface ILockUpFactory is IBaseFactory { struct DeploymentData { address stakeToken; address rewardToken; uint256 poolStartTime; uint256 poolEndTime; - uint256 unstakeLockupTime; // Lockup period for unstaking - uint256 claimLockupTime; // Lockup period for claiming rewards + uint256 unstakeLockUpTime; // LockUp period for unstaking + uint256 claimLockUpTime; // LockUp period for claiming rewards uint256 rewardPerSecond; } diff --git a/contracts/interfaces/IFactories/INoLockupFactory.sol b/contracts/interfaces/IFactories/INoLockupFactory.sol index 2218c56..43dcbe2 100644 --- a/contracts/interfaces/IFactories/INoLockupFactory.sol +++ b/contracts/interfaces/IFactories/INoLockupFactory.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.25; import {IBaseFactory} from "./IBaseFactory.sol"; -interface INoLockupFactory is IBaseFactory { +interface INoLockUpFactory is IBaseFactory { struct DeploymentData { address stakeToken; diff --git a/contracts/pools/ERC20LockUpStakingPool.sol b/contracts/pools/ERC20LockUpStakingPool.sol index e339a22..7cac0f2 100644 --- a/contracts/pools/ERC20LockUpStakingPool.sol +++ b/contracts/pools/ERC20LockUpStakingPool.sol @@ -3,21 +3,21 @@ pragma solidity 0.8.25; // Import OpenZeppelin contracts for ERC20 token interaction, reentrancy protection, safe token transfers, and ownership management. import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20LockupPool} from "../interfaces/IERC20Pools/IERC20LockupPool.sol"; +import {IERC20LockUpPool} from "../interfaces/IERC20Pools/IERC20LockUpPool.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -/// @title ERC20LockupPool +/// @title ERC20LockUpPool /// @notice A smart contract for staking ERC20 tokens and earning rewards over a specified period. -contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { +contract ERC20LockUpPool is ReentrancyGuard, Ownable, IERC20LockUpPool { using SafeERC20 for IERC20; /// @dev Precision factor for calculations uint256 public constant PRECISION_FACTOR = 10e18; ///@dev Public pool variable to access pool data - LockupPool public pool; + LockUpPool public pool; ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; @@ -33,33 +33,33 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { /// @param rewardToken Address of the ERC20 token used for rewards /// @param poolStartTime Start time of the staking pool /// @param poolEndTime End time of the staking pool - /// @param unstakeLockup Lockup period for unstaking - /// @param claimLockup Lockup period for claiming rewards + /// @param unstakeLockUp LockUp period for unstaking + /// @param claimLockUp LockUp period for claiming rewards /// @param rewardTokenPerSecond Rate of rewards per second constructor( address stakeToken, address rewardToken, uint256 poolStartTime, uint256 poolEndTime, - uint256 unstakeLockup, - uint256 claimLockup, + uint256 unstakeLockUp, + uint256 claimLockUp, uint256 rewardTokenPerSecond ) Ownable(msg.sender) { // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); - // Ensure the Lockup periods are valid - if (unstakeLockup > poolEndTime || claimLockup > poolEndTime) - revert InvalidLockupTime(); + // Ensure the LockUp periods are valid + if (unstakeLockUp > poolEndTime || claimLockUp > poolEndTime) + revert InvalidLockUpTime(); // Initialize pool parameters pool.stakeToken = stakeToken; pool.rewardToken = rewardToken; pool.startTime = poolStartTime; pool.endTime = poolEndTime; - pool.unstakeLockupTime = unstakeLockup; - pool.claimLockupTime = claimLockup; + pool.unstakeLockUpTime = unstakeLockUp; + pool.claimLockUpTime = claimLockUp; pool.rewardTokenPerSecond = rewardTokenPerSecond; pool.lastUpdateTimestamp = poolStartTime; } @@ -105,9 +105,9 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { */ function unstake(uint256 amount) external nonReentrant { if (amount == 0) revert InvalidAmount(); - // Check if the current timestamp is before the unstake Lockup time - if (block.timestamp < pool.unstakeLockupTime) - revert TokensInLockup(block.timestamp, pool.unstakeLockupTime); + // Check if the current timestamp is before the unstake LockUp time + if (block.timestamp < pool.unstakeLockUpTime) + revert TokensInLockUp(block.timestamp, pool.unstakeLockUpTime); // Get user information UserInfo storage user = userInfo[msg.sender]; uint256 currentAmount = user.amount; @@ -139,9 +139,9 @@ contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { * @dev See {IBasePoolERC20-claim}. */ function claim() external nonReentrant { - // Check if the current timestamp is before the claim Lockup time - if (block.timestamp < pool.claimLockupTime) - revert TokensInLockup(block.timestamp, pool.claimLockupTime); + // Check if the current timestamp is before the claim LockUp time + if (block.timestamp < pool.claimLockUpTime) + revert TokensInLockUp(block.timestamp, pool.claimLockUpTime); // Update the pool _updatePool(); // Get user information diff --git a/contracts/pools/ERC20NoLockUpStakingPool.sol b/contracts/pools/ERC20NoLockUpStakingPool.sol index 8f550df..8eb3342 100644 --- a/contracts/pools/ERC20NoLockUpStakingPool.sol +++ b/contracts/pools/ERC20NoLockUpStakingPool.sol @@ -3,15 +3,15 @@ SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20NoLockupPool} from "../interfaces/IERC20Pools/IERC20NoLockupPool.sol"; +import {IERC20NoLockUpPool} from "../interfaces/IERC20Pools/IERC20NoLockUpPool.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -contract ERC20NoLockupPool is +contract ERC20NoLockUpPool is ReentrancyGuard, Ownable, - IERC20NoLockupPool + IERC20NoLockUpPool { using SafeERC20 for IERC20; uint256 public constant PRECISION_FACTOR = 10e18; diff --git a/contracts/pools/ERC20PenaltyFeePool.sol b/contracts/pools/ERC20PenaltyFeePool.sol index 48716ce..d22afed 100644 --- a/contracts/pools/ERC20PenaltyFeePool.sol +++ b/contracts/pools/ERC20PenaltyFeePool.sol @@ -117,7 +117,7 @@ contract ERC20PenaltyFeePool is ReentrancyGuard, Ownable, IERC20PenaltyPool { function claim() external nonReentrant { UserInfo storage user = userInfo[msg.sender]; if (block.timestamp < user.penaltyEndTime) - revert ClaimInLockup(block.timestamp, user.penaltyEndTime); + revert ClaimInLockUp(block.timestamp, user.penaltyEndTime); _updatePool(); uint256 amount = user.amount; uint256 pending = user.pending; diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 7ae1db6..0000009 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -6,12 +6,12 @@ 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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721LockupPool} from "../../interfaces/IERC721/IERC721LockupPool.sol"; +import {IERC721LockUpPool} from "../../interfaces/IERC721/IERC721LockUpPool.sol"; -contract ERC721LockupPool is +contract ERC721LockUpPool is ReentrancyGuard, Ownable, - IERC721LockupPool + IERC721LockUpPool { using SafeERC20 for IERC20; /// @dev Precision factor for calculations @@ -26,7 +26,7 @@ contract ERC721LockupPool is ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; - LockupPool public pool; + LockUpPool public pool; constructor( address stakeToken, @@ -34,23 +34,23 @@ contract ERC721LockupPool is uint256 rewardTokenPerSecond, uint256 poolStartTime, uint256 poolEndTime, - uint256 unstakeLockupTime, - uint256 claimLockupTime + uint256 unstakeLockUpTime, + uint256 claimLockUpTime ) Ownable(msg.sender) { // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); - // Ensure the Lockup periods are valid - if (unstakeLockupTime > poolEndTime || claimLockupTime > poolEndTime) - revert InvalidLockupTime(); + // Ensure the LockUp periods are valid + if (unstakeLockUpTime > poolEndTime || claimLockUpTime > poolEndTime) + revert InvalidLockUpTime(); pool.stakeToken = IERC721(stakeToken); pool.rewardToken = IERC20(rewardToken); pool.startTime = poolStartTime; pool.endTime = poolEndTime; - pool.unstakeLockupTime = unstakeLockupTime; - pool.claimLockupTime = claimLockupTime; + pool.unstakeLockUpTime = unstakeLockUpTime; + pool.claimLockUpTime = claimLockUpTime; pool.rewardTokenPerSecond = rewardTokenPerSecond; pool.lastUpdateTimestamp = pool.startTime; } @@ -142,9 +142,9 @@ contract ERC721LockupPool is * @dev See {IERC721BasePool-claim}. */ function claim() external nonReentrant { - // Check if the current timestamp is before the claim Lockup time - if (block.timestamp < pool.claimLockupTime) - revert TokensInLockup(block.timestamp, pool.claimLockupTime); + // Check if the current timestamp is before the claim LockUp time + if (block.timestamp < pool.claimLockUpTime) + revert TokensInLockUp(block.timestamp, pool.claimLockUpTime); // Update the pool _updatePool(); diff --git a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol index 14af48f..b0c8406 100644 --- a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol @@ -6,12 +6,12 @@ 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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721NoLockupPool} from "../../interfaces/IERC721/IERC721NoLockupPool.sol"; +import {IERC721NoLockUpPool} from "../../interfaces/IERC721/IERC721NoLockUpPool.sol"; -contract ERC721NoLockupPool is +contract ERC721NoLockUpPool is ReentrancyGuard, Ownable, - IERC721NoLockupPool + IERC721NoLockUpPool { using SafeERC20 for IERC20; /// @dev Precision factor for calculations diff --git a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol index 9bb2b35..7d05f6f 100644 --- a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -43,7 +43,7 @@ contract draftERC721PenaltyFeepPool is if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); - // Ensure the Lockup periods are valid + // Ensure the LockUp periods are valid if (poolEndTime - poolStartTime > penaltyPeriod) revert InvalidPenaltyPeriod(); @@ -146,9 +146,9 @@ contract draftERC721PenaltyFeepPool is function claim() external nonReentrant { // Get user information UserInfo storage user = userInfo[msg.sender]; - // Check if the current timestamp is before the claim Lockup time + // Check if the current timestamp is before the claim LockUp time if (block.timestamp < user.penaltyEndTime) - revert ClaimInLockup(block.timestamp, user.penaltyEndTime); + revert ClaimInLockUp(block.timestamp, user.penaltyEndTime); // Update the pool _updatePool(); uint256 amount = user.amount; diff --git a/ignition/modules/factories.ts b/ignition/modules/factories.ts index 1e2e02c..62172c6 100644 --- a/ignition/modules/factories.ts +++ b/ignition/modules/factories.ts @@ -1,8 +1,8 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -export default buildModule("LockupFactory", (m) => { - const factory = m.contract("ERC20NoLockupStakingFactory", []); - const Lockupfactory = m.contract("ERC20LockupStakingFactory", []); +export default buildModule("LockUpFactory", (m) => { + const factory = m.contract("ERC20NoLockUpStakingFactory", []); + const LockUpfactory = m.contract("ERC20LockUpStakingFactory", []); const feeFactory = m.contract("ERC20PenaltyFeeStakingFactory", []); - return { factory, Lockupfactory, feeFactory }; + return { factory, LockUpfactory, feeFactory }; }); \ No newline at end of file diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index 9435020..b2839dc 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -5,10 +5,10 @@ import { time, } from "@nomicfoundation/hardhat-network-helpers"; import { - ERC20LockupPool, + ERC20LockUpPool, ERC20MockToken, - ERC20LockupStakingFactory, - ERC20LockupStakingFactory__factory, + ERC20LockUpStakingFactory, + ERC20LockUpStakingFactory__factory, } from "../typechain"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { parseEther } from "ethers"; @@ -18,8 +18,8 @@ interface DeploymentParams { rewardTokenPerSecond: bigint; poolStartTime: number; poolEndTime: number; - unstakeLockup: number; - claimLockup: number; + unstakeLockUp: number; + claimLockUp: number; adminAddress: string; stakeToken: string; rewardToken: string; @@ -27,32 +27,32 @@ interface DeploymentParams { let PRECISION_FACTOR = BigInt(10e18); describe("Contract Deployment", async function () { - let StakingFactory: ERC20LockupStakingFactory__factory; - let ercStakingPoolFactory: ERC20LockupStakingFactory; + let StakingFactory: ERC20LockUpStakingFactory__factory; + let ercStakingPoolFactory: ERC20LockUpStakingFactory; let mockStakeToken: ERC20MockToken; let mockRewardToken: ERC20MockToken; let rewardTokenPerSecond: bigint; let poolStartTime: number; let poolEndTime: number; - let unstakeLockup: number; - let claimLockup: number; + let unstakeLockUp: number; + let claimLockUp: number; let signer: HardhatEthersSigner; let ayo: HardhatEthersSigner; let alina: HardhatEthersSigner; let vartan: HardhatEthersSigner; - let poolContract: ERC20LockupPool; + let poolContract: ERC20LockUpPool; before(async () => { StakingFactory = await ethers.getContractFactory( - "ERC20LockupStakingFactory" + "ERC20LockUpStakingFactory" ); ercStakingPoolFactory = await StakingFactory.deploy(); const blockTimestamp = await time.latest(); rewardTokenPerSecond = ethers.parseEther("1"); poolStartTime = blockTimestamp; poolEndTime = blockTimestamp; - unstakeLockup = blockTimestamp; - claimLockup = blockTimestamp; + unstakeLockUp = blockTimestamp; + claimLockUp = blockTimestamp; [signer, ayo, alina, vartan] = await ethers.getSigners(); mockStakeToken = await ethers.deployContract("ERC20MockToken", [ @@ -77,19 +77,19 @@ describe("Contract Deployment", async function () { ); }); - describe("ERC20LockupStakingPool Deployment", async function () { + describe("ERC20LockUpStakingPool Deployment", async function () { it("Request creation failed: invalid staking token address", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; - unstakeLockup = poolStartTime + 10; - claimLockup = poolStartTime + 10; + unstakeLockUp = poolStartTime + 10; + claimLockUp = poolStartTime + 10; const data = { stakeToken: ethers.ZeroAddress, rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime, poolEndTime: poolEndTime, - unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup, + unstakeLockUpTime: unstakeLockUp, + claimLockUpTime: claimLockUp, rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -101,15 +101,15 @@ describe("Contract Deployment", async function () { it("Request creation failed: invalid reward token addresses", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; - unstakeLockup = poolStartTime + 10; - claimLockup = poolStartTime + 10; + unstakeLockUp = poolStartTime + 10; + claimLockUp = poolStartTime + 10; const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: ethers.ZeroAddress, poolStartTime: poolStartTime, poolEndTime: poolEndTime, - unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup, + unstakeLockUpTime: unstakeLockUp, + claimLockUpTime: claimLockUp, rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -121,15 +121,15 @@ describe("Contract Deployment", async function () { it("Request creation failed: invalid reward token addresses", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; - unstakeLockup = poolStartTime + 10; - claimLockup = poolStartTime + 10; + unstakeLockUp = poolStartTime + 10; + claimLockUp = poolStartTime + 10; const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime, poolEndTime: poolEndTime, - unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup, + unstakeLockUpTime: unstakeLockUp, + claimLockUpTime: claimLockUp, rewardPerSecond: ethers.toBigInt(0) } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -141,15 +141,15 @@ describe("Contract Deployment", async function () { it("Request should be successfully created", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; - unstakeLockup = poolStartTime + 10; - claimLockup = poolStartTime + 10; + unstakeLockUp = poolStartTime + 10; + claimLockUp = poolStartTime + 10; const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime, poolEndTime: poolEndTime, - unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup, + unstakeLockUpTime: unstakeLockUp, + claimLockUpTime: claimLockUp, rewardPerSecond: rewardTokenPerSecond } let length = (await ercStakingPoolFactory.getRequests()).length; @@ -198,7 +198,7 @@ describe("Contract Deployment", async function () { let poolsLength = (await ercStakingPoolFactory.getPools()).length; let lastPool = await ercStakingPoolFactory.stakingPools(poolsLength - 1); poolContract = await ethers.getContractAt( - "ERC20LockupPool", + "ERC20LockUpPool", lastPool ); }); @@ -216,8 +216,8 @@ describe("Contract Deployment", async function () { rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime - 10000, poolEndTime: poolStartTime + 120, - unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolStartTime + 10, + unstakeLockUpTime: poolStartTime + 10, + claimLockUpTime: poolStartTime + 10, rewardPerSecond: rewardTokenPerSecond, }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -238,8 +238,8 @@ describe("Contract Deployment", async function () { rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime + 10000, poolEndTime: poolStartTime + 120, - unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolStartTime + 10, + unstakeLockUpTime: poolStartTime + 10, + claimLockUpTime: poolStartTime + 10, rewardPerSecond: rewardTokenPerSecond }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -255,14 +255,14 @@ describe("Contract Deployment", async function () { expect(req.requestStatus).to.be.equal(3); }); - it("Another requests created with wrong unstake Lockup time", async function () { + it("Another requests created with wrong unstake LockUp time", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime + 100, poolEndTime: poolStartTime + 120, - unstakeLockupTime: poolEndTime + 130, - claimLockupTime: poolStartTime + 10, + unstakeLockUpTime: poolEndTime + 130, + claimLockUpTime: poolStartTime + 10, rewardPerSecond: rewardTokenPerSecond, }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; @@ -273,24 +273,24 @@ describe("Contract Deployment", async function () { expect(length).to.be.equal(lengthBefore + 1); expect(req.requestStatus).to.be.equal(1); await ercStakingPoolFactory.approveRequest(length - 1); - await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockupTime"); + await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockUpTime"); }); - it("Another requests created with wrong claim Lockup time", async function () { + it("Another requests created with wrong claim LockUp time", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), poolStartTime: poolStartTime + 100, poolEndTime: poolStartTime + 120, - unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolEndTime + 10, + unstakeLockUpTime: poolStartTime + 10, + claimLockUpTime: poolEndTime + 10, rewardPerSecond: rewardTokenPerSecond }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); await ercStakingPoolFactory.approveRequest(lengthBefore); - await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockupTime"); + await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockUpTime"); }); it("Cancel last approved request failed: caller is not an owner", async function () { @@ -454,8 +454,8 @@ describe("Contract Deployment", async function () { rewardToken: string; startTime: bigint; endTime: bigint; - unstakeLockupTime: bigint; - claimLockupTime: bigint; + unstakeLockUpTime: bigint; + claimLockUpTime: bigint; rewardTokenPerSecond: bigint; totalStaked: bigint; totalClaimed: bigint; From fe64f163dc0fdc638667aeb7017d30f798ff69ac Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:16:00 +0300 Subject: [PATCH 05/13] Rename ERC721LockupFactory.sol to ERC721LockUpFactory.sol --- .../{ERC721LockupFactory.sol => ERC721LockUpFactory.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/factories/{ERC721LockupFactory.sol => ERC721LockUpFactory.sol} (100%) diff --git a/contracts/factories/ERC721LockupFactory.sol b/contracts/factories/ERC721LockUpFactory.sol similarity index 100% rename from contracts/factories/ERC721LockupFactory.sol rename to contracts/factories/ERC721LockUpFactory.sol From 22d5d166fa803d883ed343e3d2a1e2e46f1f1478 Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:16:27 +0300 Subject: [PATCH 06/13] Rename ERC721NoLockupFactory.sol to ERC721NoLockUpFactory.sol --- .../{ERC721NoLockupFactory.sol => ERC721NoLockUpFactory.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/factories/{ERC721NoLockupFactory.sol => ERC721NoLockUpFactory.sol} (100%) diff --git a/contracts/factories/ERC721NoLockupFactory.sol b/contracts/factories/ERC721NoLockUpFactory.sol similarity index 100% rename from contracts/factories/ERC721NoLockupFactory.sol rename to contracts/factories/ERC721NoLockUpFactory.sol From 7833584b6c524a0cf4db7183f4874acbcdf14a64 Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:16:52 +0300 Subject: [PATCH 07/13] Rename IERC20NoLockupPool.sol to IERC20NoLockUpPool.sol --- .../{IERC20NoLockupPool.sol => IERC20NoLockUpPool.sol} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename contracts/interfaces/IERC20Pools/{IERC20NoLockupPool.sol => IERC20NoLockUpPool.sol} (99%) diff --git a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol b/contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol similarity index 99% rename from contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol rename to contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol index 7fcfa1e..c00f9ae 100644 --- a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol @@ -20,4 +20,4 @@ interface IERC20NoLockUpPool is IBasePoolERC20 { uint256 lastUpdateTimestamp; // Timestamp of the last reward update uint256 accRewardPerShare; // Accumulated rewards per share } -} \ No newline at end of file +} From 5f5bcc062a733b78b6320d6340eb3db90aa74b6e Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:17:13 +0300 Subject: [PATCH 08/13] Rename IERC721NoLockupPool.sol to IERC721NoLockUpPool.sol --- .../IERC721/{IERC721NoLockupPool.sol => IERC721NoLockUpPool.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/interfaces/IERC721/{IERC721NoLockupPool.sol => IERC721NoLockUpPool.sol} (100%) diff --git a/contracts/interfaces/IERC721/IERC721NoLockupPool.sol b/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol similarity index 100% rename from contracts/interfaces/IERC721/IERC721NoLockupPool.sol rename to contracts/interfaces/IERC721/IERC721NoLockUpPool.sol From c656988dea5cfa239e9dbc3aaa7fd41ae0dccf6f Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:17:35 +0300 Subject: [PATCH 09/13] Rename ILockupFactory.sol to ILockUpFactory.sol --- .../IFactories/{ILockupFactory.sol => ILockUpFactory.sol} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename contracts/interfaces/IFactories/{ILockupFactory.sol => ILockUpFactory.sol} (99%) diff --git a/contracts/interfaces/IFactories/ILockupFactory.sol b/contracts/interfaces/IFactories/ILockUpFactory.sol similarity index 99% rename from contracts/interfaces/IFactories/ILockupFactory.sol rename to contracts/interfaces/IFactories/ILockUpFactory.sol index e6a00a7..07eeaa7 100644 --- a/contracts/interfaces/IFactories/ILockupFactory.sol +++ b/contracts/interfaces/IFactories/ILockUpFactory.sol @@ -22,4 +22,4 @@ interface ILockUpFactory is IBaseFactory { event RequestSubmitted(uint256 indexed id, address indexed deployer, Status indexed status, DeploymentData data); event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); -} \ No newline at end of file +} From bcdba86d517171082599678c6a02a2312804a6d8 Mon Sep 17 00:00:00 2001 From: Sergey Poslavskiy <37510737+SergeyPoslavskiy@users.noreply.github.com> Date: Tue, 14 May 2024 15:17:48 +0300 Subject: [PATCH 10/13] Rename INoLockupFactory.sol to INoLockUpFactory.sol --- .../IFactories/{INoLockupFactory.sol => INoLockUpFactory.sol} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename contracts/interfaces/IFactories/{INoLockupFactory.sol => INoLockUpFactory.sol} (99%) diff --git a/contracts/interfaces/IFactories/INoLockupFactory.sol b/contracts/interfaces/IFactories/INoLockUpFactory.sol similarity index 99% rename from contracts/interfaces/IFactories/INoLockupFactory.sol rename to contracts/interfaces/IFactories/INoLockUpFactory.sol index 43dcbe2..be91f63 100644 --- a/contracts/interfaces/IFactories/INoLockupFactory.sol +++ b/contracts/interfaces/IFactories/INoLockUpFactory.sol @@ -20,4 +20,4 @@ interface INoLockUpFactory is IBaseFactory { event RequestSubmitted(uint256 indexed id, address indexed deployer, Status indexed status, DeploymentData data); event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); -} \ No newline at end of file +} From e2c1f4269ff7b753823a68704ef57b419968b041 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 15 May 2024 03:08:12 +0300 Subject: [PATCH 11/13] [Update] Unified interfaces --- .../interfaces/IERC721/IERC721LockUpPool.sol | 6 ++---- .../interfaces/IERC721/IERC721NoLockUpPool.sol | 6 ++---- .../interfaces/IERC721/IERC721PenaltyFeePool.sol | 6 ++---- .../pools/ERC721/ERC721LockUpStakingPool.sol | 16 +++++++++------- .../pools/ERC721/ERC721NoLockUpStakingPool.sol | 16 +++++++++------- contracts/pools/ERC721/ERC721PenaltyFeePool.sol | 16 +++++++++------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/contracts/interfaces/IERC721/IERC721LockUpPool.sol b/contracts/interfaces/IERC721/IERC721LockUpPool.sol index c744d1a..00b76f1 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPool.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPool.sol @@ -34,11 +34,10 @@ interface IERC721LockUpPool is IERC721BasePool{ * @dev totalClaimed: Total rewards claimed * @dev lastUpdateTimestamp: The timestamp of the last update * @dev accRewardPerShare: Accumulated rewards per staked token - * @dev stakedTokens: Mapping tokenIds to owner addresses */ struct LockUpPool { - IERC721 stakeToken; - IERC20 rewardToken; + address stakeToken; + address rewardToken; uint256 startTime; uint256 endTime; uint256 unstakeLockUpTime; // LockUp period for unstaking @@ -48,7 +47,6 @@ interface IERC721LockUpPool is IERC721BasePool{ uint256 totalClaimed; uint256 lastUpdateTimestamp; uint256 accRewardPerShare; - mapping(uint256 => address) stakedTokens; } /** diff --git a/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol b/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol index 18d0589..b2afb75 100644 --- a/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol +++ b/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol @@ -32,11 +32,10 @@ interface IERC721NoLockUpPool is IERC721BasePool{ * @dev totalClaimed: Total rewards claimed * @dev lastUpdateTimestamp: The timestamp of the last update * @dev accRewardPerShare: Accumulated rewards per staked token - * @dev stakedTokens: Mapping tokenIds to owner addresses */ struct Pool { - IERC721 stakeToken; - IERC20 rewardToken; + address stakeToken; + address rewardToken; uint256 startTime; uint256 endTime; uint256 rewardTokenPerSecond; @@ -44,6 +43,5 @@ interface IERC721NoLockUpPool is IERC721BasePool{ uint256 totalClaimed; uint256 lastUpdateTimestamp; uint256 accRewardPerShare; - mapping(uint256 => address) stakedTokens; } } diff --git a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol index 5d6c78f..c5a44c5 100644 --- a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol +++ b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol @@ -34,11 +34,10 @@ interface IERC721PenaltyFeePool is IERC721BasePool{ * @dev totalClaimed: Total rewards claimed * @dev lastUpdateTimestamp: The timestamp of the last update * @dev accRewardPerShare: Accumulated rewards per staked token - * @dev stakedTokens: Mapping tokenIds to owner addresses */ struct PenaltyPool { - IERC721 stakeToken; - IERC20 rewardToken; + address stakeToken; + address rewardToken; uint256 startTime; uint256 endTime; uint256 penaltyPeriod; @@ -49,7 +48,6 @@ interface IERC721PenaltyFeePool is IERC721BasePool{ uint256 lastUpdateTimestamp; uint256 accRewardPerShare; address adminWallet; - mapping(uint256 => address) stakedTokens; } /** diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 0000009..6d2d69b 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -25,6 +25,8 @@ contract ERC721LockUpPool is } ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; + ///@dev stakedTokens: Mapping tokenIds to owner addresses + mapping(uint256 => address) stakedTokens; LockUpPool public pool; @@ -45,8 +47,8 @@ contract ERC721LockUpPool is if (unstakeLockUpTime > poolEndTime || claimLockUpTime > poolEndTime) revert InvalidLockUpTime(); - pool.stakeToken = IERC721(stakeToken); - pool.rewardToken = IERC20(rewardToken); + pool.stakeToken = stakeToken; + pool.rewardToken = rewardToken; pool.startTime = poolStartTime; pool.endTime = poolEndTime; pool.unstakeLockUpTime = unstakeLockUpTime; @@ -84,12 +86,12 @@ contract ERC721LockUpPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < amount; i++) { - pool.stakedTokens[tokenIds[i]] = msg.sender; + stakedTokens[tokenIds[i]] = msg.sender; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < amount; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( msg.sender, address(this), tokenIds[i] @@ -122,14 +124,14 @@ contract ERC721LockUpPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < length; i++) { - if (pool.stakedTokens[tokenIds[i]] != msg.sender) + if (stakedTokens[tokenIds[i]] != msg.sender) revert NotStaker(); - delete pool.stakedTokens[tokenIds[i]]; + delete stakedTokens[tokenIds[i]]; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < length; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( address(this), msg.sender, tokenIds[i] diff --git a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol index b0c8406..0d60af7 100644 --- a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol @@ -25,6 +25,8 @@ contract ERC721NoLockUpPool is } ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; + ///@dev stakedTokens: Mapping tokenIds to owner addresses + mapping(uint256 => address) stakedTokens; Pool public pool; @@ -40,8 +42,8 @@ contract ERC721NoLockUpPool is // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); - pool.stakeToken = IERC721(stakeToken); - pool.rewardToken = IERC20(rewardToken); + pool.stakeToken = stakeToken; + pool.rewardToken = rewardToken; pool.startTime = poolStartTime; pool.endTime = poolEndTime; pool.rewardTokenPerSecond = rewardTokenPerSecond; @@ -77,12 +79,12 @@ contract ERC721NoLockUpPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < amount; i++) { - pool.stakedTokens[tokenIds[i]] = msg.sender; + stakedTokens[tokenIds[i]] = msg.sender; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < amount; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( msg.sender, address(this), tokenIds[i] @@ -115,14 +117,14 @@ contract ERC721NoLockUpPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < length; i++) { - if (pool.stakedTokens[tokenIds[i]] != msg.sender) + if (stakedTokens[tokenIds[i]] != msg.sender) revert NotStaker(); - delete pool.stakedTokens[tokenIds[i]]; + delete stakedTokens[tokenIds[i]]; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < length; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( address(this), msg.sender, tokenIds[i] diff --git a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol index 7d05f6f..eeb7e75 100644 --- a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -27,6 +27,8 @@ contract draftERC721PenaltyFeepPool is } ///@dev Mapping to store user-specific staking information mapping(address => UserInfo) public userInfo; + ///@dev stakedTokens: Mapping tokenIds to owner addresses + mapping(uint256 => address) stakedTokens; PenaltyPool public pool; @@ -47,8 +49,8 @@ contract draftERC721PenaltyFeepPool is if (poolEndTime - poolStartTime > penaltyPeriod) revert InvalidPenaltyPeriod(); - pool.stakeToken = IERC721(stakeToken); - pool.rewardToken = IERC20(rewardToken); + pool.stakeToken = stakeToken; + pool.rewardToken = rewardToken; pool.startTime = poolStartTime; pool.endTime = poolEndTime; pool.penaltyPeriod = penaltyPeriod; @@ -86,12 +88,12 @@ contract draftERC721PenaltyFeepPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < amount; i++) { - pool.stakedTokens[tokenIds[i]] = msg.sender; + stakedTokens[tokenIds[i]] = msg.sender; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < amount; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( msg.sender, address(this), tokenIds[i] @@ -124,14 +126,14 @@ contract draftERC721PenaltyFeepPool is // Update the staked tokens mapping and ensure the state changes are done first for (uint256 i = 0; i < length; i++) { - if (pool.stakedTokens[tokenIds[i]] != msg.sender) + if (stakedTokens[tokenIds[i]] != msg.sender) revert NotStaker(); - delete pool.stakedTokens[tokenIds[i]]; + delete stakedTokens[tokenIds[i]]; } // Interactions: Transfer the tokens after state changes for (uint256 i = 0; i < length; i++) { - pool.stakeToken.safeTransferFrom( + IERC721(pool.stakeToken).safeTransferFrom( address(this), msg.sender, tokenIds[i] From 97cacc8a8e6eb43b651e86a0616d9d0e6243a1ed Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 15 May 2024 13:15:34 +0300 Subject: [PATCH 12/13] [Update] Removed nolockup related contracts --- contracts/factories/ERC20NoLockUpFactory.sol | 120 ----------- contracts/factories/ERC721NoLockUpFactory.sol | 120 ----------- .../IERC20Pools/IERC20NoLockUpPool.sol | 23 -- .../IERC721/IERC721NoLockUpPool.sol | 47 ---- contracts/pools/ERC20NoLockUpStakingPool.sol | 202 ------------------ ignition/modules/factories.ts | 5 +- 6 files changed, 2 insertions(+), 515 deletions(-) delete mode 100644 contracts/factories/ERC20NoLockUpFactory.sol delete mode 100644 contracts/factories/ERC721NoLockUpFactory.sol delete mode 100644 contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol delete mode 100644 contracts/interfaces/IERC721/IERC721NoLockUpPool.sol delete mode 100644 contracts/pools/ERC20NoLockUpStakingPool.sol diff --git a/contracts/factories/ERC20NoLockUpFactory.sol b/contracts/factories/ERC20NoLockUpFactory.sol deleted file mode 100644 index 18f95e2..0000000 --- a/contracts/factories/ERC20NoLockUpFactory.sol +++ /dev/null @@ -1,120 +0,0 @@ -/* -ERC20LockUpFactory -SPDX-License-Identifier: MIT -*/ - -pragma solidity 0.8.25; -import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol"; -import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.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"; - -/// @title ERC20LockUpStakingFactory -/// @notice A smart contract for deploying ERC20 regular staking pools. -/// @author Ayooluwa Akindeko, Soramitsu team -contract ERC20NoLockUpStakingFactory is Ownable, INoLockUpFactory { - using SafeERC20 for IERC20; - - address[] public stakingPools; - Request[] public requests; - mapping(uint256 id => address pool) public poolById; - - constructor() Ownable(msg.sender) {} - - /// @notice Function allows users to deploy the LockUp staking pool with specified parameters - function deploy(uint256 id) public returns (address newPoolAddress) { - if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); - newPoolAddress = address( - new ERC20NoLockUpPool{ - salt: keccak256( - abi.encode( - req.data.stakeToken, - req.data.rewardToken, - req.data.rewardPerSecond, - req.data.poolStartTime, - req.data.poolEndTime - ) - ) - }( - req.data.stakeToken, - req.data.rewardToken, - req.data.rewardPerSecond, - req.data.poolStartTime, - req.data.poolEndTime - ) - ); - stakingPools.push(newPoolAddress); - requests[id].requestStatus = Status.DEPLOYED; - poolById[id] = newPoolAddress; - uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * - req.data.rewardPerSecond; - ERC20NoLockUpPool(newPoolAddress).transferOwnership(msg.sender); - // Transfer reward tokens from the owner to the contract - // slither-disable-next-line arbitrary-send-erc20 - IERC20(req.data.rewardToken).safeTransferFrom( - msg.sender, - newPoolAddress, - rewardAmount - ); - emit StakingPoolDeployed(newPoolAddress, id); - } - - function requestDeployment(DeploymentData calldata data) external { - if (data.stakeToken == address(0) || data.rewardToken == address(0)) - revert InvalidTokenAddress(); - if (data.rewardPerSecond == 0) revert InvalidRewardRate(); - requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, - data: data - }) - ); - emit RequestSubmitted( - requests.length - 1, - msg.sender, - Status.CREATED, - data - ); - } - - function approveRequest(uint256 id) external onlyOwner { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function denyRequest(uint256 id) external onlyOwner { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function cancelRequest(uint256 id) external { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); - if ( - req.requestStatus != Status.CREATED || - req.requestStatus != Status.APPROVED - ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function getRequests() external view returns (Request[] memory reqs) { - reqs = requests; - } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } -} diff --git a/contracts/factories/ERC721NoLockUpFactory.sol b/contracts/factories/ERC721NoLockUpFactory.sol deleted file mode 100644 index f778c38..0000000 --- a/contracts/factories/ERC721NoLockUpFactory.sol +++ /dev/null @@ -1,120 +0,0 @@ -/* -ERC20LockUpFactory -SPDX-License-Identifier: MIT -*/ - -pragma solidity 0.8.25; -import {ERC721NoLockUpPool} from "../pools/ERC721/ERC721NoLockUpStakingPool.sol"; -import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.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"; - -/// @title ERC721NoLockUpStakingFactory -/// @notice A smart contract for deploying ERC721 regular staking pools. -/// @author Ayooluwa Akindeko, Soramitsu team -contract ERC721NoLockUpStakingFactory is Ownable, INoLockUpFactory { - using SafeERC20 for IERC20; - - address[] public stakingPools; - Request[] public requests; - mapping(uint256 id => address pool) public poolById; - - constructor() Ownable(msg.sender) {} - - /// @notice Function allows users to deploy the LockUp staking pool with specified parameters - function deploy(uint256 id) public returns (address newPoolAddress) { - if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); - newPoolAddress = address( - new ERC721NoLockUpPool{ - salt: keccak256( - abi.encode( - req.data.stakeToken, - req.data.rewardToken, - req.data.rewardPerSecond, - req.data.poolStartTime, - req.data.poolEndTime - ) - ) - }( - req.data.stakeToken, - req.data.rewardToken, - req.data.rewardPerSecond, - req.data.poolStartTime, - req.data.poolEndTime - ) - ); - stakingPools.push(newPoolAddress); - requests[id].requestStatus = Status.DEPLOYED; - poolById[id] = newPoolAddress; - uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * - req.data.rewardPerSecond; - ERC721NoLockUpPool(newPoolAddress).transferOwnership(msg.sender); - // Transfer reward tokens from the owner to the contract - // slither-disable-next-line arbitrary-send-erc20 - IERC20(req.data.rewardToken).safeTransferFrom( - msg.sender, - newPoolAddress, - rewardAmount - ); - emit StakingPoolDeployed(newPoolAddress, id); - } - - function requestDeployment(DeploymentData calldata data) external { - if (data.stakeToken == address(0) || data.rewardToken == address(0)) - revert InvalidTokenAddress(); - if (data.rewardPerSecond == 0) revert InvalidRewardRate(); - requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, - data: data - }) - ); - emit RequestSubmitted( - requests.length - 1, - msg.sender, - Status.CREATED, - data - ); - } - - function approveRequest(uint256 id) external onlyOwner { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function denyRequest(uint256 id) external onlyOwner { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function cancelRequest(uint256 id) external { - if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); - if ( - req.requestStatus != Status.CREATED || - req.requestStatus != Status.APPROVED - ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); - } - - function getRequests() external view returns (Request[] memory reqs) { - reqs = requests; - } - - function getPools() external view returns (address[] memory pools) { - pools = stakingPools; - } -} diff --git a/contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol b/contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol deleted file mode 100644 index c00f9ae..0000000 --- a/contracts/interfaces/IERC20Pools/IERC20NoLockUpPool.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; -import {IBasePoolERC20} from "./IERC20BasePool.sol"; -interface IERC20NoLockUpPool is IBasePoolERC20 { - struct UserInfo { - uint256 amount; // Amount of tokens staked - uint256 claimed; // Amount of claimed rewards - uint256 rewardDebt; // Reward debt - uint256 pending; // Pending rewards - } - - struct Pool { - address stakeToken; // ERC20 token being staked - address rewardToken; // ERC20 token used for rewards - uint256 startTime; // Start time of the staking pool - uint256 endTime; // End time of the staking pool - uint256 rewardTokenPerSecond; // Rate of rewards per second - uint256 totalStaked; // Total amount of tokens staked - uint256 totalClaimed; // Total amount of claimed rewards - uint256 lastUpdateTimestamp; // Timestamp of the last reward update - uint256 accRewardPerShare; // Accumulated rewards per share - } -} diff --git a/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol b/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol deleted file mode 100644 index b2afb75..0000000 --- a/contracts/interfaces/IERC721/IERC721NoLockUpPool.sol +++ /dev/null @@ -1,47 +0,0 @@ -// 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"; -import {IERC721BasePool} from "./IERC721BasePool.sol"; - -interface IERC721NoLockUpPool is IERC721BasePool{ - - /** - * @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 pending The amount of rewards pending for the user - */ - struct UserInfo { - uint256 amount; - uint256 claimed; - uint256 rewardDebt; - uint256 pending; - } - - /** - * @notice Defines the pool state and config parameters - * @dev stakeToken The address of the ERC721 staking token - * @dev rewardToken The address of the ERC20 reward token - * @dev startTime The start time of the pool - * @dev endTime The end time of the pool - * @dev rewardTokenPerSecond The reward distribution rate per second - * @dev totalStaked: Total tokens staked - * @dev totalClaimed: Total rewards claimed - * @dev lastUpdateTimestamp: The timestamp of the last update - * @dev accRewardPerShare: Accumulated rewards per staked token - */ - struct Pool { - address stakeToken; - address rewardToken; - uint256 startTime; - uint256 endTime; - uint256 rewardTokenPerSecond; - uint256 totalStaked; - uint256 totalClaimed; - uint256 lastUpdateTimestamp; - uint256 accRewardPerShare; - } -} diff --git a/contracts/pools/ERC20NoLockUpStakingPool.sol b/contracts/pools/ERC20NoLockUpStakingPool.sol deleted file mode 100644 index 8eb3342..0000000 --- a/contracts/pools/ERC20NoLockUpStakingPool.sol +++ /dev/null @@ -1,202 +0,0 @@ -/* -SPDX-License-Identifier: MIT -*/ -pragma solidity 0.8.25; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20NoLockUpPool} from "../interfaces/IERC20Pools/IERC20NoLockUpPool.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -contract ERC20NoLockUpPool is - ReentrancyGuard, - Ownable, - IERC20NoLockUpPool -{ - using SafeERC20 for IERC20; - uint256 public constant PRECISION_FACTOR = 10e18; - - modifier validPool() { - if (block.timestamp < pool.startTime) revert PoolNotStarted(); - if (block.timestamp > pool.endTime) revert PoolHasEnded(); - _; - } - ///@dev Public pool variable to access pool data - Pool public pool; - ///@dev Mapping to store user-specific staking information - mapping(address => UserInfo) public userInfo; - - /// @notice Constructor to initialize the staking pool with specified parameters - /// @param stakeToken Address of the ERC20 token to be staked - /// @param rewardToken Address of the ERC20 token used for rewards - /// @param poolStartTime Start time of the staking pool - /// @param poolEndTime End time of the staking pool - /// @param rewardTokenPerSecond Rate of rewards per second - constructor( - address stakeToken, - address rewardToken, - uint256 poolStartTime, - uint256 poolEndTime, - uint256 rewardTokenPerSecond - ) Ownable(msg.sender) { - // Ensure the start time is in the future - if (poolStartTime < block.timestamp) revert InvalidStartTime(); - // Ensure the staking period is valid - if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); - pool.stakeToken = stakeToken; - pool.rewardToken = rewardToken; - pool.startTime = poolStartTime; - pool.endTime = poolEndTime; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastUpdateTimestamp = poolStartTime; - } - - /** - * @dev See {IBasePoolERC20-stake}. - */ - function stake(uint256 amount) external validPool { - if (amount == 0) revert InvalidAmount(); - _updatePool(); - UserInfo storage user = userInfo[msg.sender]; - uint256 share = pool.accRewardPerShare; - uint256 currentAmount = user.amount; - if (currentAmount > 0) { - user.pending += - (currentAmount * share) / - PRECISION_FACTOR - - user.rewardDebt; - } - unchecked { - user.amount = currentAmount + amount; - } - user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; - pool.totalStaked += amount; - IERC20(pool.stakeToken).safeTransferFrom( - msg.sender, - address(this), - amount - ); - emit Stake(msg.sender, amount); - } - - /** - * @dev See {IBasePoolERC20-unstake}. - */ - function unstake(uint256 amount) external nonReentrant { - if (amount == 0) revert InvalidAmount(); - UserInfo storage user = userInfo[msg.sender]; - uint256 currentAmount = user.amount; - if (currentAmount < amount) - revert InsufficientAmount(amount, currentAmount); - _updatePool(); - uint256 share = pool.accRewardPerShare; - user.pending += - ((currentAmount * share) / PRECISION_FACTOR) - - user.rewardDebt; - unchecked { - user.amount -= amount; - } - user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; - pool.totalStaked -= amount; - IERC20(pool.stakeToken).safeTransfer(msg.sender, amount); - emit Unstake(msg.sender, amount); - } - - /** - * @dev See {IBasePoolERC20-claim}. - */ - function claim() external nonReentrant { - _updatePool(); - UserInfo storage user = userInfo[msg.sender]; - uint256 amount = user.amount; - uint256 pending = user.pending; - if (amount > 0) { - pending += - (amount * pool.accRewardPerShare) / - PRECISION_FACTOR - - user.rewardDebt; - user.rewardDebt = - (amount * pool.accRewardPerShare) / - PRECISION_FACTOR; - } - if (pending > 0) { - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; - } - pool.totalClaimed += pending; - IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); - } else { - revert NothingToClaim(); - } - } - - /** - * @dev See {IBasePoolERC20-pendingRewards}. - */ - function pendingRewards( - address userAddress - ) external view returns (uint256) { - UserInfo storage user = userInfo[userAddress]; - uint256 share = pool.accRewardPerShare; - if ( - block.timestamp > pool.lastUpdateTimestamp && pool.totalStaked != 0 - ) { - uint256 elapsedPeriod = _getMultiplier( - pool.lastUpdateTimestamp, - block.timestamp - ); - uint256 totalNewReward = pool.rewardTokenPerSecond * elapsedPeriod; - share = - share + - ((totalNewReward * PRECISION_FACTOR) / pool.totalStaked); - } - return - user.pending + - ((user.amount * share) / PRECISION_FACTOR) - - user.rewardDebt; - } - - function _updatePool() internal { - uint256 lastTimestamp = pool.lastUpdateTimestamp; - uint256 total = pool.totalStaked; - // Update accumulated rewards per share if necessary - if (block.timestamp > lastTimestamp) { - if (total > 0) { - uint256 elapsedPeriod = _getMultiplier( - lastTimestamp, - block.timestamp - ); - pool.accRewardPerShare += - (pool.rewardTokenPerSecond * - PRECISION_FACTOR * - elapsedPeriod) / - total; - } - pool.lastUpdateTimestamp = block.timestamp; - emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); - } - } - - /** - * @notice Return reward multiplier over the given `_from` to `_to` block. - * If the `from` block is higher than the pool's reward-end block, - * the function returns 0 and therefore rewards are no longer updated. - * @param _from timestamp to start - * @param _to timestamp to finish - */ - function _getMultiplier( - uint256 _from, - uint256 _to - ) internal view returns (uint256) { - if (_to <= pool.endTime) { - return _to - _from; - } else if (_from >= pool.endTime) { - return 0; - } else { - return pool.endTime - _from; - } - } -} diff --git a/ignition/modules/factories.ts b/ignition/modules/factories.ts index 62172c6..4e0be10 100644 --- a/ignition/modules/factories.ts +++ b/ignition/modules/factories.ts @@ -1,8 +1,7 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("LockUpFactory", (m) => { - const factory = m.contract("ERC20NoLockUpStakingFactory", []); - const LockUpfactory = m.contract("ERC20LockUpStakingFactory", []); + const lockUpfactory = m.contract("ERC20LockUpStakingFactory", []); const feeFactory = m.contract("ERC20PenaltyFeeStakingFactory", []); - return { factory, LockUpfactory, feeFactory }; + return { lockUpfactory, feeFactory }; }); \ No newline at end of file From 193e504805515074f9a93431bd16a17e11f8ae17 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 15 May 2024 14:18:27 +0300 Subject: [PATCH 13/13] [Update] Interfaces refactored, removed nolockup remaining contracts --- contracts/factories/ERC20LockUpFactory.sol | 55 +++-- .../factories/ERC20PenaltyFeeFactory.sol | 49 ++-- contracts/factories/ERC721LockUpFactory.sol | 55 +++-- .../factories/ERC721PenaltyFeeFactory.sol | 49 ++-- .../IERC20BasePool.sol => IERC20Pool.sol} | 2 +- .../IERC20Pools/IERC20LockUpPool.sol | 38 --- .../IERC20Pools/IERC20PenaltyPool.sol | 52 ---- .../IERC721BasePool.sol => IERC721Pool.sol} | 2 +- .../interfaces/IFactories/IBaseFactory.sol | 7 + .../interfaces/IFactories/ILockUpFactory.sol | 6 +- .../IFactories/INoLockUpFactory.sol | 23 -- .../IFactories/IPenaltyFeeFactory.sol | 8 +- .../IERC721LockUpPool.sol => ILockUpPool.sol} | 8 +- ...PenaltyFeePool.sol => IPenaltyFeePool.sol} | 3 +- contracts/pools/ERC20LockUpStakingPool.sol | 5 +- contracts/pools/ERC20PenaltyFeePool.sol | 5 +- .../pools/ERC721/ERC721LockUpStakingPool.sol | 6 +- .../ERC721/ERC721NoLockUpStakingPool.sol | 230 ------------------ .../pools/ERC721/ERC721PenaltyFeePool.sol | 6 +- test/ERC20LockUpStakingPool.test.ts | 48 ++-- 20 files changed, 172 insertions(+), 485 deletions(-) rename contracts/interfaces/{IERC20Pools/IERC20BasePool.sol => IERC20Pool.sol} (99%) delete mode 100644 contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol delete mode 100644 contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol rename contracts/interfaces/{IERC721/IERC721BasePool.sol => IERC721Pool.sol} (99%) delete mode 100644 contracts/interfaces/IFactories/INoLockUpFactory.sol rename contracts/interfaces/{IERC721/IERC721LockUpPool.sol => ILockUpPool.sol} (90%) rename contracts/interfaces/{IERC721/IERC721PenaltyFeePool.sol => IPenaltyFeePool.sol} (95%) delete mode 100644 contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index c272a74..0722456 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -17,7 +17,7 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; - Request[] public requests; + LockUpRequest[] public requests; mapping(uint256 id => address pool) public poolById; constructor() Ownable(msg.sender) {} @@ -25,9 +25,10 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); + LockUpRequest memory req = requests[id]; + if (req.info.requestStatus != Status.APPROVED) + revert InvalidRequestStatus(); + if (msg.sender != req.info.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC20LockUpPool{ salt: keccak256( @@ -50,7 +51,7 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { ) ); stakingPools.push(newPoolAddress); - requests[id].requestStatus = Status.DEPLOYED; + requests[id].info.requestStatus = Status.DEPLOYED; poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; @@ -65,14 +66,20 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { emit StakingPoolDeployed(newPoolAddress, id); } - function requestDeployment(DeploymentData calldata data) external { + function requestDeployment( + bytes32 ipfsHash, + DeploymentData calldata data + ) external { if (data.stakeToken == address(0) || data.rewardToken == address(0)) revert InvalidTokenAddress(); if (data.rewardPerSecond == 0) revert InvalidRewardRate(); requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, + LockUpRequest({ + info: RequestInfo({ + ipfsHash: ipfsHash, + deployer: msg.sender, + requestStatus: Status.CREATED + }), data: data }) ); @@ -86,33 +93,33 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory { function approveRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); + LockUpRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.info.requestStatus); } function denyRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); + LockUpRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.info.requestStatus); } function cancelRequest(uint256 id) external { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); + LockUpRequest storage req = requests[id]; + if (msg.sender != req.info.deployer) revert InvalidCaller(); if ( - req.requestStatus != Status.CREATED && - req.requestStatus != Status.APPROVED + req.info.requestStatus != Status.CREATED && + req.info.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); + req.info.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.info.requestStatus); } - function getRequests() external view returns (Request[] memory reqs) { + function getRequests() external view returns (LockUpRequest[] memory reqs) { reqs = requests; } diff --git a/contracts/factories/ERC20PenaltyFeeFactory.sol b/contracts/factories/ERC20PenaltyFeeFactory.sol index a584534..b461be5 100644 --- a/contracts/factories/ERC20PenaltyFeeFactory.sol +++ b/contracts/factories/ERC20PenaltyFeeFactory.sol @@ -17,7 +17,7 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { using SafeERC20 for IERC20; address[] public stakingPools; - Request[] public requests; + PenaltyFeeRequest[] public requests; mapping(uint256 id => address pool) public poolById; constructor() Ownable(msg.sender) {} @@ -25,9 +25,9 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { /// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); + PenaltyFeeRequest memory req = requests[id]; + if (req.info.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); + if (msg.sender != req.info.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC20PenaltyFeePool{ salt: keccak256( @@ -54,14 +54,17 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { emit StakingPoolDeployed(newPoolAddress, id); } - function requestDeployment(DeploymentData calldata data) external { + function requestDeployment(bytes32 ipfsHash, DeploymentData calldata data) external { if (data.stakeToken == address(0) || data.rewardToken == address(0)) revert InvalidTokenAddress(); if (data.rewardPerSecond == 0) revert InvalidRewardRate(); requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, + PenaltyFeeRequest({ + info: RequestInfo({ + ipfsHash: ipfsHash, + deployer: msg.sender, + requestStatus: Status.CREATED + }), data: data }) ); @@ -75,33 +78,33 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { function approveRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); + PenaltyFeeRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.info.requestStatus); } function denyRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); + PenaltyFeeRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.info.requestStatus); } function cancelRequest(uint256 id) external { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); + PenaltyFeeRequest storage req = requests[id]; + if (msg.sender != req.info.deployer) revert InvalidCaller(); if ( - req.requestStatus != Status.CREATED || - req.requestStatus != Status.APPROVED + req.info.requestStatus != Status.CREATED || + req.info.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); + req.info.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.info.requestStatus); } - function getRequests() external view returns (Request[] memory reqs) { + function getRequests() external view returns (PenaltyFeeRequest[] memory reqs) { reqs = requests; } diff --git a/contracts/factories/ERC721LockUpFactory.sol b/contracts/factories/ERC721LockUpFactory.sol index d76ac2f..2cc1d50 100644 --- a/contracts/factories/ERC721LockUpFactory.sol +++ b/contracts/factories/ERC721LockUpFactory.sol @@ -17,7 +17,7 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { using SafeERC20 for IERC20; address[] public stakingPools; - Request[] public requests; + LockUpRequest[] public requests; mapping(uint256 id => address pool) public poolById; constructor() Ownable(msg.sender) {} @@ -25,9 +25,10 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { /// @notice Function allows users to deploy the LockUp staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); + LockUpRequest memory req = requests[id]; + if (req.info.requestStatus != Status.APPROVED) + revert InvalidRequestStatus(); + if (msg.sender != req.info.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC721LockUpPool{ salt: keccak256( @@ -50,7 +51,7 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { ) ); stakingPools.push(newPoolAddress); - requests[id].requestStatus = Status.DEPLOYED; + requests[id].info.requestStatus = Status.DEPLOYED; poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; @@ -65,14 +66,20 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { emit StakingPoolDeployed(newPoolAddress, id); } - function requestDeployment(DeploymentData calldata data) external { + function requestDeployment( + bytes32 ipfsHash, + DeploymentData calldata data + ) external { if (data.stakeToken == address(0) || data.rewardToken == address(0)) revert InvalidTokenAddress(); if (data.rewardPerSecond == 0) revert InvalidRewardRate(); requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, + LockUpRequest({ + info: RequestInfo({ + ipfsHash: ipfsHash, + deployer: msg.sender, + requestStatus: Status.CREATED + }), data: data }) ); @@ -86,33 +93,33 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory { function approveRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); + LockUpRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.info.requestStatus); } function denyRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); + LockUpRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.info.requestStatus); } function cancelRequest(uint256 id) external { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); + LockUpRequest storage req = requests[id]; + if (msg.sender != req.info.deployer) revert InvalidCaller(); if ( - req.requestStatus != Status.CREATED && - req.requestStatus != Status.APPROVED + req.info.requestStatus != Status.CREATED && + req.info.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); + req.info.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.info.requestStatus); } - function getRequests() external view returns (Request[] memory reqs) { + function getRequests() external view returns (LockUpRequest[] memory reqs) { reqs = requests; } diff --git a/contracts/factories/ERC721PenaltyFeeFactory.sol b/contracts/factories/ERC721PenaltyFeeFactory.sol index c63b8c5..e40ef69 100644 --- a/contracts/factories/ERC721PenaltyFeeFactory.sol +++ b/contracts/factories/ERC721PenaltyFeeFactory.sol @@ -17,7 +17,7 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { using SafeERC20 for IERC20; address[] public stakingPools; - Request[] public requests; + PenaltyFeeRequest[] public requests; mapping(uint256 id => address pool) public poolById; constructor() Ownable(msg.sender) {} @@ -25,9 +25,9 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { /// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); - Request memory req = requests[id]; - if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); - if (msg.sender != req.deployer) revert InvalidCaller(); + PenaltyFeeRequest memory req = requests[id]; + if (req.info.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); + if (msg.sender != req.info.deployer) revert InvalidCaller(); newPoolAddress = address( new draftERC721PenaltyFeepPool{ salt: keccak256( @@ -54,14 +54,17 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { emit StakingPoolDeployed(newPoolAddress, id); } - function requestDeployment(DeploymentData calldata data) external { + function requestDeployment(bytes32 ipfsHash, DeploymentData calldata data) external { if (data.stakeToken == address(0) || data.rewardToken == address(0)) revert InvalidTokenAddress(); if (data.rewardPerSecond == 0) revert InvalidRewardRate(); requests.push( - Request({ - deployer: msg.sender, - requestStatus: Status.CREATED, + PenaltyFeeRequest({ + info: RequestInfo({ + ipfsHash: ipfsHash, + deployer: msg.sender, + requestStatus: Status.CREATED + }), data: data }) ); @@ -75,33 +78,33 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory { function approveRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APPROVED; - emit RequestStatusChanged(id, req.requestStatus); + PenaltyFeeRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.APPROVED; + emit RequestStatusChanged(id, req.info.requestStatus); } function denyRequest(uint256 id) external onlyOwner { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.DENIED; - emit RequestStatusChanged(id, req.requestStatus); + PenaltyFeeRequest storage req = requests[id]; + if (req.info.requestStatus != Status.CREATED) revert InvalidRequestStatus(); + req.info.requestStatus = Status.DENIED; + emit RequestStatusChanged(id, req.info.requestStatus); } function cancelRequest(uint256 id) external { if (requests.length < id) revert InvalidId(); - Request storage req = requests[id]; - if (msg.sender != req.deployer) revert InvalidCaller(); + PenaltyFeeRequest storage req = requests[id]; + if (msg.sender != req.info.deployer) revert InvalidCaller(); if ( - req.requestStatus != Status.CREATED || - req.requestStatus != Status.APPROVED + req.info.requestStatus != Status.CREATED || + req.info.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); - req.requestStatus = Status.CANCELED; - emit RequestStatusChanged(id, req.requestStatus); + req.info.requestStatus = Status.CANCELED; + emit RequestStatusChanged(id, req.info.requestStatus); } - function getRequests() external view returns (Request[] memory reqs) { + function getRequests() external view returns (PenaltyFeeRequest[] memory reqs) { reqs = requests; } diff --git a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol b/contracts/interfaces/IERC20Pool.sol similarity index 99% rename from contracts/interfaces/IERC20Pools/IERC20BasePool.sol rename to contracts/interfaces/IERC20Pool.sol index 7acb8d0..814499e 100644 --- a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol +++ b/contracts/interfaces/IERC20Pool.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -interface IBasePoolERC20 { +interface IPoolERC20 { /** * ERROR MESSAGES */ diff --git a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol deleted file mode 100644 index 5272421..0000000 --- a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; -import {IBasePoolERC20} from "./IERC20BasePool.sol"; - -interface IERC20LockUpPool is IBasePoolERC20 { - struct UserInfo { - uint256 amount; // Amount of tokens staked - uint256 claimed; // Amount of claimed rewards - uint256 rewardDebt; // Reward debt - uint256 pending; // Pending rewards - } - - struct LockUpPool { - address stakeToken; // ERC20 token being staked - address rewardToken; // ERC20 token used for rewards - uint256 startTime; // Start time of the staking pool - uint256 endTime; // End time of the staking pool - uint256 unstakeLockUpTime; // LockUp period for unstaking - uint256 claimLockUpTime; // LockUp period for claiming rewards - uint256 rewardTokenPerSecond; // Rate of rewards per second - uint256 totalStaked; // Total amount of tokens staked - uint256 totalClaimed; // Total amount of claimed rewards - uint256 lastUpdateTimestamp; // Timestamp of the last reward update - uint256 accRewardPerShare; // Accumulated rewards per share - } - - /** - * ERROR MESSAGES - */ - - /// @dev Error to indicate that tokens are still in LockUp and cannot be accessed - /// @param currentTime The current timestamp - /// @param unlockTime The timestamp when the tokens will be unlocked - error TokensInLockUp(uint256 currentTime, uint256 unlockTime); - - /// @dev Error to indicate an invalid LockUp time for unstaking or claiming rewards - error InvalidLockUpTime(); -} diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol deleted file mode 100644 index f291263..0000000 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; -import {IBasePoolERC20} from "./IERC20BasePool.sol"; - -interface IERC20PenaltyPool is IBasePoolERC20 { - struct PenaltyPool { - address stakeToken; // ERC20 token being staked - address rewardToken; // ERC20 token used for rewards - uint256 startTime; // Start time of the staking pool - uint256 endTime; // End time of the staking pool - uint256 penaltyPeriod; - uint256 rewardTokenPerSecond; // Rate of rewards per second - uint256 totalStaked; // Total amount of tokens staked - uint256 totalClaimed; // Total amount of claimed rewards - uint256 totalPenalties; - uint256 lastUpdateTimestamp; // Timestamp of the last reward update - uint256 accRewardPerShare; // Accumulated rewards per share - address adminWallet; // Address of the admin - } - - struct UserInfo { - uint256 amount; // Amount of tokens staked - uint256 claimed; // Amount of claimed rewards - uint256 rewardDebt; // Reward debt - uint256 pending; // Pending rewards - uint256 penaltyEndTime; - bool penalized; - } - - /** - * ERROR MESSAGES - */ - /// @dev Error to indicate that tokens are still in LockUp and cannot be claimed - /// @param currentTime The current timestamp - /// @param unlockTime The timestamp when the tokens will be unlocked for claim - error ClaimInLockUp(uint256 currentTime, uint256 unlockTime); - /// @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(); - - /** - * EVENTS - */ - - /** - * @notice Event to notify when an admin claims accumulated fees - * @dev Emitted in 'claim' function - * @param amount The amount of fees claimed - */ - event FeeClaim(uint256 amount); -} diff --git a/contracts/interfaces/IERC721/IERC721BasePool.sol b/contracts/interfaces/IERC721Pool.sol similarity index 99% rename from contracts/interfaces/IERC721/IERC721BasePool.sol rename to contracts/interfaces/IERC721Pool.sol index 81cc24b..e980f3a 100644 --- a/contracts/interfaces/IERC721/IERC721BasePool.sol +++ b/contracts/interfaces/IERC721Pool.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.25; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -interface IERC721BasePool { +interface IPoolERC721 { /** * ERROR MESSAGES */ diff --git a/contracts/interfaces/IFactories/IBaseFactory.sol b/contracts/interfaces/IFactories/IBaseFactory.sol index da15271..df353f7 100644 --- a/contracts/interfaces/IFactories/IBaseFactory.sol +++ b/contracts/interfaces/IFactories/IBaseFactory.sol @@ -10,11 +10,18 @@ interface IBaseFactory { DEPLOYED, CANCELED } + struct RequestInfo { + bytes32 ipfsHash; + address deployer; + Status requestStatus; + } + error InvalidId(); error InvalidRequestStatus(); error InvalidCaller(); error InvalidTokenAddress(); error InvalidRewardRate(); + event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); event RequestStatusChanged(uint256 indexed id, Status indexed status); } \ No newline at end of file diff --git a/contracts/interfaces/IFactories/ILockUpFactory.sol b/contracts/interfaces/IFactories/ILockUpFactory.sol index 07eeaa7..8cf25e8 100644 --- a/contracts/interfaces/IFactories/ILockUpFactory.sol +++ b/contracts/interfaces/IFactories/ILockUpFactory.sol @@ -14,12 +14,10 @@ interface ILockUpFactory is IBaseFactory { uint256 rewardPerSecond; } - struct Request { - address deployer; - Status requestStatus; + struct LockUpRequest { + RequestInfo info; DeploymentData data; } event RequestSubmitted(uint256 indexed id, address indexed deployer, Status indexed status, DeploymentData data); - event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); } diff --git a/contracts/interfaces/IFactories/INoLockUpFactory.sol b/contracts/interfaces/IFactories/INoLockUpFactory.sol deleted file mode 100644 index be91f63..0000000 --- a/contracts/interfaces/IFactories/INoLockUpFactory.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; -import {IBaseFactory} from "./IBaseFactory.sol"; - -interface INoLockUpFactory is IBaseFactory { - - struct DeploymentData { - address stakeToken; - address rewardToken; - uint256 rewardPerSecond; - uint256 poolStartTime; - uint256 poolEndTime; - } - - struct Request { - address deployer; - Status requestStatus; - DeploymentData data; - } - - event RequestSubmitted(uint256 indexed id, address indexed deployer, Status indexed status, DeploymentData data); - event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); -} diff --git a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol index ae21237..3882550 100644 --- a/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol +++ b/contracts/interfaces/IFactories/IPenaltyFeeFactory.sol @@ -7,18 +7,16 @@ interface IPenaltyFeeFactory is IBaseFactory { struct DeploymentData { address stakeToken; address rewardToken; - uint256 rewardPerSecond; uint256 poolStartTime; uint256 poolEndTime; uint256 penaltyPeriod; + uint256 rewardPerSecond; } - struct Request { - address deployer; - Status requestStatus; + struct PenaltyFeeRequest { + RequestInfo info; DeploymentData data; } event RequestSubmitted(uint256 indexed id, address indexed deployer, Status indexed status, DeploymentData data); - event StakingPoolDeployed(address indexed stakingAddress, uint256 indexed id); } \ No newline at end of file diff --git a/contracts/interfaces/IERC721/IERC721LockUpPool.sol b/contracts/interfaces/ILockUpPool.sol similarity index 90% rename from contracts/interfaces/IERC721/IERC721LockUpPool.sol rename to contracts/interfaces/ILockUpPool.sol index 00b76f1..a317093 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPool.sol +++ b/contracts/interfaces/ILockUpPool.sol @@ -3,9 +3,9 @@ pragma solidity 0.8.25; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721LockUpPool is IERC721BasePool{ + +interface ILockUpPoolStorage { /** * @notice Storage for a user's staking information @@ -23,8 +23,8 @@ interface IERC721LockUpPool is IERC721BasePool{ /** * @notice Defines the pool state and config parameters - * @dev stakeToken The address of the ERC721 staking token - * @dev rewardToken The address of the ERC20 reward token + * @dev stakeToken The address of the staking token + * @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 diff --git a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol b/contracts/interfaces/IPenaltyFeePool.sol similarity index 95% rename from contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol rename to contracts/interfaces/IPenaltyFeePool.sol index c5a44c5..d5ff622 100644 --- a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol +++ b/contracts/interfaces/IPenaltyFeePool.sol @@ -3,9 +3,8 @@ pragma solidity 0.8.25; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721PenaltyFeePool is IERC721BasePool{ +interface IPenaltyFeePoolStorage { /** * @notice Storage for a user's staking information diff --git a/contracts/pools/ERC20LockUpStakingPool.sol b/contracts/pools/ERC20LockUpStakingPool.sol index 7cac0f2..d409bc9 100644 --- a/contracts/pools/ERC20LockUpStakingPool.sol +++ b/contracts/pools/ERC20LockUpStakingPool.sol @@ -3,14 +3,15 @@ pragma solidity 0.8.25; // Import OpenZeppelin contracts for ERC20 token interaction, reentrancy protection, safe token transfers, and ownership management. import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20LockUpPool} from "../interfaces/IERC20Pools/IERC20LockUpPool.sol"; +import {IPoolERC20} from "../interfaces/IERC20Pool.sol"; +import {ILockUpPoolStorage} from "../interfaces/ILockUpPool.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; /// @title ERC20LockUpPool /// @notice A smart contract for staking ERC20 tokens and earning rewards over a specified period. -contract ERC20LockUpPool is ReentrancyGuard, Ownable, IERC20LockUpPool { +contract ERC20LockUpPool is ReentrancyGuard, Ownable, IPoolERC20, ILockUpPoolStorage { using SafeERC20 for IERC20; /// @dev Precision factor for calculations diff --git a/contracts/pools/ERC20PenaltyFeePool.sol b/contracts/pools/ERC20PenaltyFeePool.sol index d22afed..2d52b36 100644 --- a/contracts/pools/ERC20PenaltyFeePool.sol +++ b/contracts/pools/ERC20PenaltyFeePool.sol @@ -4,12 +4,13 @@ SPDX-License-Identifier: MIT pragma solidity 0.8.25; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC20PenaltyPool} from "../interfaces/IERC20Pools/IERC20PenaltyPool.sol"; +import {IPoolERC20} from "../interfaces/IERC20Pool.sol"; +import {IPenaltyFeePoolStorage} from "../interfaces/IPenaltyFeePool.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -contract ERC20PenaltyFeePool is ReentrancyGuard, Ownable, IERC20PenaltyPool { +contract ERC20PenaltyFeePool is ReentrancyGuard, Ownable, IPoolERC20, IPenaltyFeePoolStorage { using SafeERC20 for IERC20; uint256 public constant PRECISION_FACTOR = 10e18; uint256 public constant PENALTY_FEE = 2500; diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 6d2d69b..81d2cc3 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -6,12 +6,14 @@ 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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721LockUpPool} from "../../interfaces/IERC721/IERC721LockUpPool.sol"; +import {IPoolERC721} from "../../interfaces/IERC721Pool.sol"; +import {ILockUpPoolStorage} from "../../interfaces/ILockUpPool.sol"; contract ERC721LockUpPool is ReentrancyGuard, Ownable, - IERC721LockUpPool + IPoolERC721, + ILockUpPoolStorage { using SafeERC20 for IERC20; /// @dev Precision factor for calculations diff --git a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol deleted file mode 100644 index 0d60af7..0000000 --- a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721NoLockUpPool} from "../../interfaces/IERC721/IERC721NoLockUpPool.sol"; - -contract ERC721NoLockUpPool is - ReentrancyGuard, - Ownable, - IERC721NoLockUpPool -{ - using SafeERC20 for IERC20; - /// @dev Precision factor for calculations - uint256 public constant PRECISION_FACTOR = 10e18; - - /// @dev Modifier to ensure that functions can only be executed when the pool is active and within the specified time range - modifier validPool() { - if (block.timestamp < pool.startTime) revert PoolNotStarted(); - if (block.timestamp > pool.endTime) revert PoolHasEnded(); - _; - } - ///@dev Mapping to store user-specific staking information - mapping(address => UserInfo) public userInfo; - ///@dev stakedTokens: Mapping tokenIds to owner addresses - mapping(uint256 => address) stakedTokens; - - Pool public pool; - - constructor( - address stakeToken, - address rewardToken, - uint256 rewardTokenPerSecond, - uint256 poolStartTime, - uint256 poolEndTime - ) Ownable(msg.sender) { - // Ensure the staking period is valid - if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); - // Ensure the start time is in the future - if (poolStartTime < block.timestamp) revert InvalidStartTime(); - - pool.stakeToken = stakeToken; - pool.rewardToken = rewardToken; - pool.startTime = poolStartTime; - pool.endTime = poolEndTime; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastUpdateTimestamp = pool.startTime; - } - - /** - * @dev See {IERC721BasePool-stake}. - */ - function stake( - uint256[] calldata tokenIds - ) external validPool nonReentrant { - uint256 amount = tokenIds.length; - if (amount == 0) revert InvalidAmount(); - - UserInfo storage user = userInfo[msg.sender]; - _updatePool(); - uint256 share = pool.accRewardPerShare; - uint256 currentAmount = user.amount; - // Calculate pending rewards - if (currentAmount > 0) { - user.pending += - (currentAmount * share) / - PRECISION_FACTOR - - user.rewardDebt; - } - // Update user data - unchecked { - user.amount += amount; - } - user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; - pool.totalStaked += amount; - - // Update the staked tokens mapping and ensure the state changes are done first - for (uint256 i = 0; i < amount; i++) { - stakedTokens[tokenIds[i]] = msg.sender; - } - - // Interactions: Transfer the tokens after state changes - for (uint256 i = 0; i < amount; i++) { - IERC721(pool.stakeToken).safeTransferFrom( - msg.sender, - address(this), - tokenIds[i] - ); - } - emit Stake(msg.sender, tokenIds); - } - - /** - * @dev See {IERC721BasePool-unstake}. - */ - function unstake(uint256[] calldata tokenIds) external nonReentrant { - uint256 length = tokenIds.length; - if (length == 0) revert InvalidAmount(); - UserInfo storage user = userInfo[msg.sender]; - uint256 currentAmount = user.amount; - if (length > currentAmount) - revert InsufficientAmount(length, currentAmount); - _updatePool(); - uint256 share = pool.accRewardPerShare; - user.pending += - ((currentAmount * share) / PRECISION_FACTOR) - - user.rewardDebt; - // Update user data - unchecked { - user.amount -= length; - } - user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; - pool.totalStaked -= length; - - // Update the staked tokens mapping and ensure the state changes are done first - for (uint256 i = 0; i < length; i++) { - if (stakedTokens[tokenIds[i]] != msg.sender) - revert NotStaker(); - delete stakedTokens[tokenIds[i]]; - } - - // Interactions: Transfer the tokens after state changes - for (uint256 i = 0; i < length; i++) { - IERC721(pool.stakeToken).safeTransferFrom( - address(this), - msg.sender, - tokenIds[i] - ); - } - emit Unstake(msg.sender, tokenIds); - } - - /** - * @dev See {IERC721BasePool-claim}. - */ - function claim() external nonReentrant { - // Update the pool - _updatePool(); - - // Get user information - UserInfo storage user = userInfo[msg.sender]; - uint256 amount = user.amount; - uint256 pending = user.pending; - - // Calculate pending rewards - if (amount > 0) { - pending += - (amount * pool.accRewardPerShare) / - PRECISION_FACTOR - - user.rewardDebt; - user.rewardDebt = - (user.amount * pool.accRewardPerShare) / - PRECISION_FACTOR; - } - if (pending > 0) { - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; - } - pool.totalClaimed += pending; - IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); - } else { - revert NothingToClaim(); - } - } - - function pendingRewards( - address userAddress - ) external view returns (uint256) { - // Get user information - UserInfo storage user = userInfo[userAddress]; - uint256 share = pool.accRewardPerShare; - // Update accumulated rewards per share if necessary - if ( - block.timestamp > pool.lastUpdateTimestamp && pool.totalStaked != 0 - ) { - uint256 elapsedPeriod = _getMultiplier( - pool.lastUpdateTimestamp, - block.timestamp - ); - uint256 totalNewReward = pool.rewardTokenPerSecond * elapsedPeriod; - share += (totalNewReward * PRECISION_FACTOR) / pool.totalStaked; - } - // Calculate pending rewards - return - user.pending + - ((user.amount * share) / PRECISION_FACTOR) - - user.rewardDebt; - } - - function _updatePool() internal { - uint256 lastTimestamp = pool.lastUpdateTimestamp; - uint256 total = pool.totalStaked; - // Update accumulated rewards per share if necessary - if (block.timestamp > lastTimestamp) { - if (total > 0) { - uint256 elapsedPeriod = _getMultiplier( - lastTimestamp, - block.timestamp - ); - pool.accRewardPerShare += - (pool.rewardTokenPerSecond * - PRECISION_FACTOR * - elapsedPeriod) / - total; - } - pool.lastUpdateTimestamp = block.timestamp; - emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); - } - } - - function _getMultiplier( - uint256 _from, - uint256 _to - ) internal view returns (uint256) { - if (_from > pool.endTime) { - return 0; - } - if (_to <= pool.endTime) { - return _to - _from; - } else { - return pool.endTime - _from; - } - } -} diff --git a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol index eeb7e75..d4bf93e 100644 --- a/contracts/pools/ERC721/ERC721PenaltyFeePool.sol +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -6,12 +6,14 @@ 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 {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC721PenaltyFeePool} from "../../interfaces/IERC721/IERC721PenaltyFeePool.sol"; +import {IPoolERC721} from "../../interfaces/IERC721Pool.sol"; +import {IPenaltyFeePoolStorage} from "../../interfaces/IPenaltyFeePool.sol"; contract draftERC721PenaltyFeepPool is ReentrancyGuard, Ownable, - IERC721PenaltyFeePool + IPoolERC721, + IPenaltyFeePoolStorage { using SafeERC20 for IERC20; /// @dev Precision factor for calculations diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index b2839dc..a9760c3 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -11,7 +11,7 @@ import { ERC20LockUpStakingFactory__factory, } from "../typechain"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; -import { parseEther } from "ethers"; +import { BytesLike, parseEther } from "ethers"; interface DeploymentParams { currentTime: number; @@ -36,6 +36,7 @@ describe("Contract Deployment", async function () { let poolEndTime: number; let unstakeLockUp: number; let claimLockUp: number; + let ipfsHash: BytesLike; let signer: HardhatEthersSigner; let ayo: HardhatEthersSigner; let alina: HardhatEthersSigner; @@ -46,6 +47,7 @@ describe("Contract Deployment", async function () { StakingFactory = await ethers.getContractFactory( "ERC20LockUpStakingFactory" ); + ipfsHash = ethers.randomBytes(32); ercStakingPoolFactory = await StakingFactory.deploy(); const blockTimestamp = await time.latest(); rewardTokenPerSecond = ethers.parseEther("1"); @@ -93,7 +95,7 @@ describe("Contract Deployment", async function () { rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); let length = (await ercStakingPoolFactory.getRequests()).length; expect(lengthBefore).to.be.equal(length); }); @@ -113,7 +115,7 @@ describe("Contract Deployment", async function () { rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); let length = (await ercStakingPoolFactory.getRequests()).length; expect(lengthBefore).to.be.equal(length); }); @@ -133,7 +135,7 @@ describe("Contract Deployment", async function () { rewardPerSecond: ethers.toBigInt(0) } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRewardRate"); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRewardRate"); let length = (await ercStakingPoolFactory.getRequests()).length; expect(lengthBefore).to.be.equal(length); }); @@ -154,12 +156,12 @@ describe("Contract Deployment", async function () { } let length = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(length, ayo.address, 1, values); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(length, ayo.address, 1, values); length = (await ercStakingPoolFactory.getRequests()).length; let req = await ercStakingPoolFactory.requests(length - 1); expect(length).to.be.equal(1); - expect(req.requestStatus).to.be.equal(1); - expect(req.deployer).to.be.equal(ayo.address); + expect(req.info.requestStatus).to.be.equal(1); + expect(req.info.deployer).to.be.equal(ayo.address); expect(req.data).to.be.deep.equal(values); }); @@ -167,21 +169,21 @@ describe("Contract Deployment", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.connect(ayo).approveRequest(length - 1)).to.be.revertedWithCustomError(ercStakingPoolFactory, "OwnableUnauthorizedAccount"); let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(1); + expect(req.info.requestStatus).to.be.equal(1); }); it("Should correctly approve request deployment", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; await ercStakingPoolFactory.approveRequest(length - 1); let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); }); it("Request approval failed: already approved", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.approveRequest(length - 1)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRequestStatus"); let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); }); it("Should correctly deploy pool from APPROVED request", async function () { @@ -194,7 +196,7 @@ describe("Contract Deployment", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.connect(ayo).deploy(length - 1)).to.emit(ercStakingPoolFactory, "StakingPoolDeployed"); let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(4); + expect(req.info.requestStatus).to.be.equal(4); let poolsLength = (await ercStakingPoolFactory.getPools()).length; let lastPool = await ercStakingPoolFactory.stakingPools(poolsLength - 1); poolContract = await ethers.getContractAt( @@ -207,7 +209,7 @@ describe("Contract Deployment", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.approveRequest(length - 1)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRequestStatus"); let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(4); + expect(req.info.requestStatus).to.be.equal(4); }); it("Another requests created with wrong start time", async function () { @@ -222,14 +224,14 @@ describe("Contract Deployment", async function () { }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); let length = (await ercStakingPoolFactory.getRequests()).length; expect(length).to.be.equal(lengthBefore + 1); await ercStakingPoolFactory.approveRequest(length - 1); await expect(ercStakingPoolFactory.connect(ayo).deploy(length - 1)).to.be.revertedWithCustomError(poolContract, "InvalidStartTime"); let req = await ercStakingPoolFactory.requests(lengthBefore); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); }); it("Another requests created with wrong staking period", async function () { @@ -244,7 +246,7 @@ describe("Contract Deployment", async function () { }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); let length = (await ercStakingPoolFactory.getRequests()).length; expect(length).to.be.equal(lengthBefore + 1); @@ -252,7 +254,7 @@ describe("Contract Deployment", async function () { await ercStakingPoolFactory.approveRequest(lengthBefore); await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidStakingPeriod"); let req = await ercStakingPoolFactory.requests(lengthBefore); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); }); it("Another requests created with wrong unstake LockUp time", async function () { @@ -267,11 +269,11 @@ describe("Contract Deployment", async function () { }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); let length = (await ercStakingPoolFactory.getRequests()).length; let req = await ercStakingPoolFactory.requests(lengthBefore); expect(length).to.be.equal(lengthBefore + 1); - expect(req.requestStatus).to.be.equal(1); + expect(req.info.requestStatus).to.be.equal(1); await ercStakingPoolFactory.approveRequest(length - 1); await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockUpTime"); }); @@ -288,7 +290,7 @@ describe("Contract Deployment", async function () { }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); - await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); + await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(ipfsHash, data)).to.emit(ercStakingPoolFactory, "RequestSubmitted").withArgs(lengthBefore, ayo.address, 1, values); await ercStakingPoolFactory.approveRequest(lengthBefore); await expect(ercStakingPoolFactory.connect(ayo).deploy(lengthBefore)).to.be.revertedWithCustomError(poolContract, "InvalidLockUpTime"); }); @@ -296,23 +298,23 @@ describe("Contract Deployment", async function () { it("Cancel last approved request failed: caller is not an owner", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); await expect(ercStakingPoolFactory.cancelRequest(length - 1)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidCaller"); }); it("Cancel last approved request", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(3); + expect(req.info.requestStatus).to.be.equal(3); await expect(ercStakingPoolFactory.connect(ayo).cancelRequest(length - 1)).to.be.emit(ercStakingPoolFactory, "RequestStatusChanged"); req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(5); + expect(req.info.requestStatus).to.be.equal(5); }); it("Cancel last approved request failed: already canceled", async function () { let length = (await ercStakingPoolFactory.getRequests()).length; let req = await ercStakingPoolFactory.requests(length - 1); - expect(req.requestStatus).to.be.equal(5); + expect(req.info.requestStatus).to.be.equal(5); await expect(ercStakingPoolFactory.connect(ayo).cancelRequest(length - 1)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRequestStatus"); }); })