From 056719afdc0c2a79c2833991e6b8cd1fc687b620 Mon Sep 17 00:00:00 2001 From: ayodeko Date: Mon, 6 May 2024 16:00:15 +0300 Subject: [PATCH 1/9] [Feature] Init ERC721 pool --- contracts/pools/ERC721LockUpStakingPool.sol | 168 ++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 contracts/pools/ERC721LockUpStakingPool.sol diff --git a/contracts/pools/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721LockUpStakingPool.sol new file mode 100644 index 0000000..e2d4e27 --- /dev/null +++ b/contracts/pools/ERC721LockUpStakingPool.sol @@ -0,0 +1,168 @@ +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"; +contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + + + + //Error Messages + error ZeroStakingTokens(); + error NotEnoughTokens(); + error NotStaker(); + error UserNotFound(); + error NothingToClaim(); + error TokensInLockup(uint256 currentTime, uint256 unlockTime); + + + //Emit messages + event Staked(address user, uint256[] tokenIds); + event UnStaked(address user, uint256[] tokenIds); + event Claim(address user, uint256 pending); + event UpdatePool( + uint256 totalStaked, + uint256 accumulatedRewardTokenPerShare, + uint256 lastBlockNumber + ); + struct Pool { + IERC721 stakeToken; + IERC20 rewardToken; + uint256 startTime; + uint256 endTime; + 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 + bool isActive; // Flag indicating if the pool is active + address adminWallet; // Address of the admin + mapping(address => User) userInfo; // Mapping to store user-specific staking information + mapping(uint256 => address) stakedTokens; // Mapping token-ids to owner address + } + struct User { + uint256 amount; + uint256 claimed; + uint256 rewardDebt; + uint256 pending; + } + + Pool public pool; + + constructor( + address stakeToken, + address rewardToken, + uint256 startTime, + uint256 endTime, + uint256 unstakeLockupTime, + uint256 claimLockUpTime, uint256 rewardTokenPerSecond + ) Ownable(msg.sender) { + pool.stakeToken = IERC721(stakeToken); + pool.rewardToken = IERC20(rewardToken); + pool.startTime = startTime; + pool.endTime = endTime; + pool.unstakeLockupTime = unstakeLockupTime; + pool.claimLockupTime = claimLockUpTime; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = block.timestamp; + } + + function stake(uint256[] calldata _tokenIds) external { + //update parameters + _updatePool(); + + uint64 len = uint64(_tokenIds.length); + if(len == 0){ + revert ZeroStakingTokens(); + } + + for (uint64 i = 0; i < len; i++) { + pool.stakeToken.safeTransferFrom(msg.sender, address(this), _tokenIds[i]); + pool.stakedTokens[_tokenIds[i]] = msg.sender; + } + pool.userInfo[msg.sender].amount += len; + pool.totalStaked += len; + emit Staked(msg.sender, _tokenIds); + } + + function unstake(uint256[] calldata _tokenIds) external { + uint256 _amount = _tokenIds.length; + if(_amount > pool.userInfo[msg.sender].amount){ + revert NotEnoughTokens(); + } + _updatePool(); + + for(uint256 i = _amount; i < _amount; i++){ + if(pool.stakedTokens[_tokenIds[i]] != msg.sender){ + revert NotStaker(); + } + if(pool.stakedTokens[_tokenIds[i]] == msg.sender){ + revert UserNotFound(); + } + pool.stakeToken.safeTransferFrom(address(this), msg.sender, _tokenIds[i]); + pool.stakedTokens[_tokenIds[1]] = address(0); + } + pool.userInfo[msg.sender].amount -= _amount; + emit UnStaked(msg.sender, _tokenIds); + } + + + /// @notice Function to allow users to claim pending rewards + 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); + + // Update the pool + _updatePool(); + + // Get user information + User storage user = pool.userInfo[msg.sender]; + uint256 amount = user.amount; + uint256 pending = user.pending; + + // Calculate pending rewards + if (amount > 0) { + pending += + (amount * pool.accRewardPerShare) - + user.rewardDebt; + user.rewardDebt = + (user.amount * pool.accRewardPerShare); + } + if (pending == 0) revert NothingToClaim(); + // Transfer pending rewards to the user + user.pending = 0; + unchecked { + user.claimed += pending; + } + pool.totalClaimed += pending; + pool.rewardToken.safeTransfer(msg.sender, pending); + emit Claim(msg.sender, pending); + } + + function _updatePool() internal{ + if(block.timestamp > pool.lastUpdateTimestamp){ + if(pool.totalStaked > 0){ + uint256 elapsedTime = _getMultiplier(pool.lastUpdateTimestamp, block.timestamp); + pool.accRewardPerShare = (elapsedTime * pool.rewardTokenPerSecond) / pool.totalStaked; + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool(pool.totalStaked, pool.accRewardPerShare, pool.lastUpdateTimestamp); + } + } + } + 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; + } + } +} From e6f4a564f29477142349af4e1086066b18ff5bac Mon Sep 17 00:00:00 2001 From: ayodeko Date: Mon, 6 May 2024 16:46:26 +0300 Subject: [PATCH 2/9] [Edit] More erc721 changes --- contracts/pools/ERC721LockUpStakingPool.sol | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/pools/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721LockUpStakingPool.sol index e2d4e27..eb5d734 100644 --- a/contracts/pools/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721LockUpStakingPool.sol @@ -15,6 +15,8 @@ contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { error NotStaker(); error UserNotFound(); error NothingToClaim(); + error PoolNotStarted(); + error PoolNotActive(); error TokensInLockup(uint256 currentTime, uint256 unlockTime); @@ -27,6 +29,13 @@ contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { uint256 accumulatedRewardTokenPerShare, uint256 lastBlockNumber ); + + /// @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 (!pool.isActive) revert PoolNotActive(); + _; + } struct Pool { IERC721 stakeToken; IERC20 rewardToken; @@ -71,7 +80,7 @@ contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { pool.lastUpdateTimestamp = block.timestamp; } - function stake(uint256[] calldata _tokenIds) external { + function stake(uint256[] calldata _tokenIds) external validPool { //update parameters _updatePool(); @@ -100,13 +109,11 @@ contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { if(pool.stakedTokens[_tokenIds[i]] != msg.sender){ revert NotStaker(); } - if(pool.stakedTokens[_tokenIds[i]] == msg.sender){ - revert UserNotFound(); - } pool.stakeToken.safeTransferFrom(address(this), msg.sender, _tokenIds[i]); pool.stakedTokens[_tokenIds[1]] = address(0); } pool.userInfo[msg.sender].amount -= _amount; + pool.totalStaked -= _amount; emit UnStaked(msg.sender, _tokenIds); } From 65805d246d4a1d5dc3ed227549ca16c50ed2e339 Mon Sep 17 00:00:00 2001 From: ayodeko Date: Wed, 8 May 2024 16:05:38 +0300 Subject: [PATCH 3/9] [Update] Refactor ERC721 contract to inherit from interface --- .../interfaces/IERC721/IERC721BasePool.sol | 162 ++++++++++++++++ .../IERC721/IERC721LockUpPoolExtension.sol | 57 ++++++ .../pools/ERC721/ERC721LockUpStakingPool.sol | 177 ++++++++++++++++++ contracts/pools/ERC721LockUpStakingPool.sol | 175 ----------------- 4 files changed, 396 insertions(+), 175 deletions(-) create mode 100644 contracts/interfaces/IERC721/IERC721BasePool.sol create mode 100644 contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol create mode 100644 contracts/pools/ERC721/ERC721LockUpStakingPool.sol delete mode 100644 contracts/pools/ERC721LockUpStakingPool.sol diff --git a/contracts/interfaces/IERC721/IERC721BasePool.sol b/contracts/interfaces/IERC721/IERC721BasePool.sol new file mode 100644 index 0000000..b002a8c --- /dev/null +++ b/contracts/interfaces/IERC721/IERC721BasePool.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IERC721BasePool { + /** + * ERROR MESSAGES + */ + + /** + * @dev Error to indicate an invalid staking period + */ + error InvalidStakingPeriod(); + + /** + * @dev Error to indicate an invalid start time for the staking pool + */ + error InvalidStartTime(); + + /** + * @notice Error emitted when attempting to stake zero tokens. + */ + error ZeroStakingTokens(); + + /** + * @notice Error emitted when attempting to unstake more tokens than the user has staked. + */ + error NotEnoughTokens(); + + /** + * @notice Error emitted when a user other than the owner of a token attempts to unstake it. + */ + error NotStaker(); + + /** + * @notice Error emitted when attempting an operation but no user account is found. + */ + error UserNotFound(); + + /** + * @notice Error emitted when attempting to claim rewards but there are none available. + */ + error NothingToClaim(); + + /** + * @notice Error emitted when attempting an operation before the pool has started. + */ + error PoolNotStarted(); + + /** + * @notice Error emitted when attempting an operation while pool is not active. + */ + error PoolNotActive(); + + // **Events** + + /** + * @notice Event emitted when tokens are staked into the pool. + * @param user The address of the user who staked the tokens. + * @param tokenIds The IDs of the staked tokens. + */ + event Staked(address indexed user, uint256[] indexed tokenIds); + + /** + * @notice Event emitted when tokens are unstaked from the pool. + * @param user The address of the user who unstaked the tokens. + * @param tokenIds The IDs of the unstaked tokens. + */ + event UnStaked(address indexed user, uint256[] indexed tokenIds); + + /** + * @notice Event emitted when a user claims their rewards. + * @param user The address of the user who claimed the rewards. + * @param pending The amount of rewards claimed. + */ + event Claim(address indexed user, uint256 pending); + + /** + * @notice Event emitted when the pool parameters are updated + * @param totalStaked Total number of tokens staked in the pool. + * @param accumulatedRewardTokenPerShare Accumulated reward tokens per staked share. + * @param lastBlockNumber Block number where the update happened. + */ + event UpdatePool( + uint256 indexed totalStaked, + uint256 indexed accumulatedRewardTokenPerShare, + uint256 lastBlockNumber + ); + + // **Structs** + + /** + * @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 unstakeLockupTime The lockup period (in seconds) after unstaking + * @dev claimLockupTime The lockup period (in seconds) before claiming rewards + * @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 + * @dev isActive: Flag indicating active/inactive pool + * @dev adminWallet: Address of the pool admin + * @dev userInfo: Mapping for user staking data + * @dev stakedTokens: Mapping tokenIds to owner addresses + */ + struct BasePoolInfo { + IERC721 stakeToken; + IERC20 rewardToken; + uint256 startTime; + uint256 endTime; + uint256 unstakeLockupTime; + uint256 claimLockupTime; + uint256 rewardTokenPerSecond; + uint256 totalStaked; + uint256 totalClaimed; + uint256 lastUpdateTimestamp; + uint256 accRewardPerShare; + bool isActive; + address adminWallet; + mapping(address => BaseUserInfo) userInfo; + mapping(uint256 => address) stakedTokens; + } + + /** + * @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 BaseUserInfo { + uint256 amount; + uint256 claimed; + uint256 rewardDebt; + uint256 pending; + } + + // **External Functions** + + /** + * @notice Allows users to stake ERC721 tokens into the pool. + * @param _tokenIds An array of token IDs to be staked. + */ + function stake(uint256[] calldata _tokenIds) external; + + /** + * @notice Allows users to unstake their ERC721 tokens from the pool. + * @param _tokenIds An array of token IDs to be unstaked. + */ + function unstake(uint256[] calldata _tokenIds) external; + + /** + * @notice Allows users to claim their pending rewards. + */ + function claim() external; +} diff --git a/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol b/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol new file mode 100644 index 0000000..4d63eae --- /dev/null +++ b/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol @@ -0,0 +1,57 @@ +// 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"; +import {ILockUpPool} from "../ILockUpPool.sol"; + +interface IERC721LockUpPoolExtension 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 startTime The start time of the pool + * @dev endTime The end time of the pool + * @dev unstakeLockupTime The lockup period (in seconds) after unstaking + * @dev claimLockupTime The lockup period (in seconds) before claiming rewards + * @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 + * @dev isActive: Flag indicating active/inactive pool + * @dev adminWallet: Address of the pool admin + * @dev userInfo: Mapping for user staking data + * @dev stakedTokens: Mapping tokenIds to owner addresses + */ + struct Pool { + IERC721 stakeToken; + IERC20 rewardToken; + uint256 startTime; + uint256 endTime; + uint256 rewardTokenPerSecond; + uint256 totalStaked; + uint256 totalClaimed; + uint256 lastUpdateTimestamp; + uint256 accRewardPerShare; + bool isActive; + address adminWallet; + mapping(address => User) userInfo; + mapping(uint256 => address) stakedTokens; + uint256 unstakeLockupTime; // Lockup period for unstaking + uint256 claimLockupTime; // Lockup period for claiming rewards + } + + /** + * 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/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol new file mode 100644 index 0000000..7dc4d23 --- /dev/null +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -0,0 +1,177 @@ +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 {IERC721BasePool} from "../../interfaces/IERC721/IERC721BasePool.sol"; +import {IERC721LockUpPoolExtension} from "../../interfaces/IERC721/IERC721LockUpPoolExtension.sol"; +import {ILockUpPool} from "../../interfaces/ILockUpPool.sol"; +contract ERC721LockUpStakingPool is + ReentrancyGuard, + Ownable, + IERC721LockUpPoolExtension +{ + using SafeERC20 for IERC20; + + + /// @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 (!pool.isActive) revert PoolNotActive(); + _; + } + ///@dev Mapping to store user-specific staking information + mapping(address => BaseUserInfo) public userInfo; + + /// @dev Modifier to allow only the admin to execute certain functions + modifier onlyAdmin() { + if (msg.sender != pool.baseInfo.adminWallet) revert NotAdmin(); + _; + } + + Pool public pool; + + constructor( + address stakeToken, + address rewardToken, + uint256 poolStartTime, + uint256 poolEndTime, + uint256 unstakeLockupTime, + uint256 claimLockUpTime, + uint256 rewardTokenPerSecond + ) 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(); + + pool.stakeToken = IERC721(stakeToken); + pool.rewardToken = IERC20(rewardToken); + pool.startTime = poolStartTime; + pool.endTime = poolEndTime; + pool.unstakeLockupTime = unstakeLockupTime; + pool.claimLockupTime = claimLockUpTime; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = block.timestamp; + } + + /** + * @dev See {IERC721BasePool-stake}. + */ + function stake(uint256[] calldata _tokenIds) external validPool { + //update parameters + _updatePool(); + + uint64 len = uint64(_tokenIds.length); + if (len == 0) { + revert ZeroStakingTokens(); + } + + for (uint64 i = 0; i < len; i++) { + pool.stakeToken.safeTransferFrom( + msg.sender, + address(this), + _tokenIds[i] + ); + pool.stakedTokens[_tokenIds[i]] = msg.sender; + } + pool.userInfo[msg.sender].amount += len; + pool.totalStaked += len; + emit Staked(msg.sender, _tokenIds); + } + + /** + * @dev See {IERC721BasePool-unstake}. + */ + function unstake(uint256[] calldata _tokenIds) external { + uint256 _amount = _tokenIds.length; + if (_amount > pool.userInfo[msg.sender].amount) { + revert NotEnoughTokens(); + } + _updatePool(); + + for (uint256 i = _amount; i < _amount; i++) { + if (pool.stakedTokens[_tokenIds[i]] != msg.sender) { + revert NotStaker(); + } + pool.stakeToken.safeTransferFrom( + address(this), + msg.sender, + _tokenIds[i] + ); + pool.stakedTokens[_tokenIds[1]] = address(0); + } + pool.userInfo[msg.sender].amount -= _amount; + pool.totalStaked -= _amount; + emit UnStaked(msg.sender, _tokenIds); + } + + /** + * @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); + + // Update the pool + _updatePool(); + + // Get user information + User storage user = pool.userInfo[msg.sender]; + uint256 amount = user.amount; + uint256 pending = user.pending; + + // Calculate pending rewards + if (amount > 0) { + pending += (amount * pool.accRewardPerShare) - user.rewardDebt; + user.rewardDebt = (user.amount * pool.accRewardPerShare); + } + if (pending == 0) revert NothingToClaim(); + // Transfer pending rewards to the user + user.pending = 0; + unchecked { + user.claimed += pending; + } + pool.totalClaimed += pending; + pool.rewardToken.safeTransfer(msg.sender, pending); + emit Claim(msg.sender, pending); + } + + function _updatePool() internal { + if (block.timestamp > pool.lastUpdateTimestamp) { + if (pool.totalStaked > 0) { + uint256 elapsedTime = _getMultiplier( + pool.lastUpdateTimestamp, + block.timestamp + ); + pool.accRewardPerShare = + (elapsedTime * pool.rewardTokenPerSecond) / + pool.totalStaked; + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool( + pool.totalStaked, + pool.accRewardPerShare, + pool.lastUpdateTimestamp + ); + } + } + } + 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/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721LockUpStakingPool.sol deleted file mode 100644 index eb5d734..0000000 --- a/contracts/pools/ERC721LockUpStakingPool.sol +++ /dev/null @@ -1,175 +0,0 @@ -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"; -contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable { - using SafeERC20 for IERC20; - - - - //Error Messages - error ZeroStakingTokens(); - error NotEnoughTokens(); - error NotStaker(); - error UserNotFound(); - error NothingToClaim(); - error PoolNotStarted(); - error PoolNotActive(); - error TokensInLockup(uint256 currentTime, uint256 unlockTime); - - - //Emit messages - event Staked(address user, uint256[] tokenIds); - event UnStaked(address user, uint256[] tokenIds); - event Claim(address user, uint256 pending); - event UpdatePool( - uint256 totalStaked, - uint256 accumulatedRewardTokenPerShare, - uint256 lastBlockNumber - ); - - /// @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 (!pool.isActive) revert PoolNotActive(); - _; - } - struct Pool { - IERC721 stakeToken; - IERC20 rewardToken; - uint256 startTime; - uint256 endTime; - 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 - bool isActive; // Flag indicating if the pool is active - address adminWallet; // Address of the admin - mapping(address => User) userInfo; // Mapping to store user-specific staking information - mapping(uint256 => address) stakedTokens; // Mapping token-ids to owner address - } - struct User { - uint256 amount; - uint256 claimed; - uint256 rewardDebt; - uint256 pending; - } - - Pool public pool; - - constructor( - address stakeToken, - address rewardToken, - uint256 startTime, - uint256 endTime, - uint256 unstakeLockupTime, - uint256 claimLockUpTime, uint256 rewardTokenPerSecond - ) Ownable(msg.sender) { - pool.stakeToken = IERC721(stakeToken); - pool.rewardToken = IERC20(rewardToken); - pool.startTime = startTime; - pool.endTime = endTime; - pool.unstakeLockupTime = unstakeLockupTime; - pool.claimLockupTime = claimLockUpTime; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastUpdateTimestamp = block.timestamp; - } - - function stake(uint256[] calldata _tokenIds) external validPool { - //update parameters - _updatePool(); - - uint64 len = uint64(_tokenIds.length); - if(len == 0){ - revert ZeroStakingTokens(); - } - - for (uint64 i = 0; i < len; i++) { - pool.stakeToken.safeTransferFrom(msg.sender, address(this), _tokenIds[i]); - pool.stakedTokens[_tokenIds[i]] = msg.sender; - } - pool.userInfo[msg.sender].amount += len; - pool.totalStaked += len; - emit Staked(msg.sender, _tokenIds); - } - - function unstake(uint256[] calldata _tokenIds) external { - uint256 _amount = _tokenIds.length; - if(_amount > pool.userInfo[msg.sender].amount){ - revert NotEnoughTokens(); - } - _updatePool(); - - for(uint256 i = _amount; i < _amount; i++){ - if(pool.stakedTokens[_tokenIds[i]] != msg.sender){ - revert NotStaker(); - } - pool.stakeToken.safeTransferFrom(address(this), msg.sender, _tokenIds[i]); - pool.stakedTokens[_tokenIds[1]] = address(0); - } - pool.userInfo[msg.sender].amount -= _amount; - pool.totalStaked -= _amount; - emit UnStaked(msg.sender, _tokenIds); - } - - - /// @notice Function to allow users to claim pending rewards - 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); - - // Update the pool - _updatePool(); - - // Get user information - User storage user = pool.userInfo[msg.sender]; - uint256 amount = user.amount; - uint256 pending = user.pending; - - // Calculate pending rewards - if (amount > 0) { - pending += - (amount * pool.accRewardPerShare) - - user.rewardDebt; - user.rewardDebt = - (user.amount * pool.accRewardPerShare); - } - if (pending == 0) revert NothingToClaim(); - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; - } - pool.totalClaimed += pending; - pool.rewardToken.safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); - } - - function _updatePool() internal{ - if(block.timestamp > pool.lastUpdateTimestamp){ - if(pool.totalStaked > 0){ - uint256 elapsedTime = _getMultiplier(pool.lastUpdateTimestamp, block.timestamp); - pool.accRewardPerShare = (elapsedTime * pool.rewardTokenPerSecond) / pool.totalStaked; - pool.lastUpdateTimestamp = block.timestamp; - emit UpdatePool(pool.totalStaked, pool.accRewardPerShare, pool.lastUpdateTimestamp); - } - } - } - 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; - } - } -} From 98ae036090dd4a92bd58e0699a4b156018e5dfbc Mon Sep 17 00:00:00 2001 From: ayodeko Date: Wed, 8 May 2024 16:12:55 +0300 Subject: [PATCH 4/9] [Fix] bug fix in ERC721 --- contracts/interfaces/IERC721/IERC721BasePool.sol | 4 ++++ contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol | 3 +-- contracts/pools/ERC721/ERC721LockUpStakingPool.sol | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/IERC721/IERC721BasePool.sol b/contracts/interfaces/IERC721/IERC721BasePool.sol index b002a8c..3117ecd 100644 --- a/contracts/interfaces/IERC721/IERC721BasePool.sol +++ b/contracts/interfaces/IERC721/IERC721BasePool.sol @@ -53,6 +53,10 @@ interface IERC721BasePool { * @notice Error emitted when attempting an operation while pool is not active. */ error PoolNotActive(); + /** + * @notice Error emitted when attempting an operation while pool is not active. + */ + error NotAdmin(); // **Events** diff --git a/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol b/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol index 4d63eae..d8756ec 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol @@ -4,7 +4,6 @@ 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"; -import {ILockUpPool} from "../ILockUpPool.sol"; interface IERC721LockUpPoolExtension is IERC721BasePool{ /** @@ -37,7 +36,7 @@ interface IERC721LockUpPoolExtension is IERC721BasePool{ uint256 accRewardPerShare; bool isActive; address adminWallet; - mapping(address => User) userInfo; + mapping(address => BaseUserInfo) userInfo; mapping(uint256 => address) stakedTokens; uint256 unstakeLockupTime; // Lockup period for unstaking uint256 claimLockupTime; // Lockup period for claiming rewards diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 7dc4d23..052585c 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -6,7 +6,6 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC721BasePool} from "../../interfaces/IERC721/IERC721BasePool.sol"; import {IERC721LockUpPoolExtension} from "../../interfaces/IERC721/IERC721LockUpPoolExtension.sol"; -import {ILockUpPool} from "../../interfaces/ILockUpPool.sol"; contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable, @@ -26,7 +25,7 @@ contract ERC721LockUpStakingPool is /// @dev Modifier to allow only the admin to execute certain functions modifier onlyAdmin() { - if (msg.sender != pool.baseInfo.adminWallet) revert NotAdmin(); + if (msg.sender != pool.adminWallet) revert NotAdmin(); _; } @@ -122,7 +121,7 @@ contract ERC721LockUpStakingPool is _updatePool(); // Get user information - User storage user = pool.userInfo[msg.sender]; + BaseUserInfo storage user = pool.userInfo[msg.sender]; uint256 amount = user.amount; uint256 pending = user.pending; From 9021954f3c2581b47a4362b764acd9532339b6f4 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 13 May 2024 20:24:08 +0300 Subject: [PATCH 5/9] [Fix] Minor erc721 pool fixes --- .../interfaces/IERC721/IERC721BasePool.sol | 63 +------------ ...oolExtension.sol => IERC721LockUpPool.sol} | 27 ++++-- .../pools/ERC721/ERC721LockUpStakingPool.sol | 91 +++++++++++-------- 3 files changed, 73 insertions(+), 108 deletions(-) rename contracts/interfaces/IERC721/{IERC721LockUpPoolExtension.sol => IERC721LockUpPool.sol} (80%) diff --git a/contracts/interfaces/IERC721/IERC721BasePool.sol b/contracts/interfaces/IERC721/IERC721BasePool.sol index 3117ecd..627f969 100644 --- a/contracts/interfaces/IERC721/IERC721BasePool.sol +++ b/contracts/interfaces/IERC721/IERC721BasePool.sol @@ -22,7 +22,7 @@ interface IERC721BasePool { /** * @notice Error emitted when attempting to stake zero tokens. */ - error ZeroStakingTokens(); + error InvalidAmount(); /** * @notice Error emitted when attempting to unstake more tokens than the user has staked. @@ -34,11 +34,6 @@ interface IERC721BasePool { */ error NotStaker(); - /** - * @notice Error emitted when attempting an operation but no user account is found. - */ - error UserNotFound(); - /** * @notice Error emitted when attempting to claim rewards but there are none available. */ @@ -65,14 +60,14 @@ interface IERC721BasePool { * @param user The address of the user who staked the tokens. * @param tokenIds The IDs of the staked tokens. */ - event Staked(address indexed user, uint256[] indexed tokenIds); + event Stake(address indexed user, uint256[] indexed tokenIds); /** * @notice Event emitted when tokens are unstaked from the pool. * @param user The address of the user who unstaked the tokens. * @param tokenIds The IDs of the unstaked tokens. */ - event UnStaked(address indexed user, uint256[] indexed tokenIds); + event Unstake(address indexed user, uint256[] indexed tokenIds); /** * @notice Event emitted when a user claims their rewards. @@ -93,58 +88,6 @@ interface IERC721BasePool { uint256 lastBlockNumber ); - // **Structs** - - /** - * @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 unstakeLockupTime The lockup period (in seconds) after unstaking - * @dev claimLockupTime The lockup period (in seconds) before claiming rewards - * @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 - * @dev isActive: Flag indicating active/inactive pool - * @dev adminWallet: Address of the pool admin - * @dev userInfo: Mapping for user staking data - * @dev stakedTokens: Mapping tokenIds to owner addresses - */ - struct BasePoolInfo { - IERC721 stakeToken; - IERC20 rewardToken; - uint256 startTime; - uint256 endTime; - uint256 unstakeLockupTime; - uint256 claimLockupTime; - uint256 rewardTokenPerSecond; - uint256 totalStaked; - uint256 totalClaimed; - uint256 lastUpdateTimestamp; - uint256 accRewardPerShare; - bool isActive; - address adminWallet; - mapping(address => BaseUserInfo) userInfo; - mapping(uint256 => address) stakedTokens; - } - - /** - * @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 BaseUserInfo { - uint256 amount; - uint256 claimed; - uint256 rewardDebt; - uint256 pending; - } - // **External Functions** /** diff --git a/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol b/contracts/interfaces/IERC721/IERC721LockUpPool.sol similarity index 80% rename from contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol rename to contracts/interfaces/IERC721/IERC721LockUpPool.sol index d8756ec..be0fdcf 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPoolExtension.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPool.sol @@ -5,7 +5,22 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721BasePool} from "./IERC721BasePool.sol"; -interface IERC721LockUpPoolExtension is IERC721BasePool{ +interface IERC721LockUpPool 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 @@ -24,7 +39,7 @@ interface IERC721LockUpPoolExtension is IERC721BasePool{ * @dev userInfo: Mapping for user staking data * @dev stakedTokens: Mapping tokenIds to owner addresses */ - struct Pool { + struct LockupPool { IERC721 stakeToken; IERC20 rewardToken; uint256 startTime; @@ -34,18 +49,14 @@ interface IERC721LockUpPoolExtension is IERC721BasePool{ uint256 totalClaimed; uint256 lastUpdateTimestamp; uint256 accRewardPerShare; - bool isActive; - address adminWallet; - mapping(address => BaseUserInfo) userInfo; - mapping(uint256 => address) stakedTokens; uint256 unstakeLockupTime; // Lockup period for unstaking uint256 claimLockupTime; // Lockup period for claiming rewards + mapping(uint256 => address) stakedTokens; } - + /** * 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 diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index 052585c..c5ac677 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -1,44 +1,39 @@ +// 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 {IERC721BasePool} from "../../interfaces/IERC721/IERC721BasePool.sol"; -import {IERC721LockUpPoolExtension} from "../../interfaces/IERC721/IERC721LockUpPoolExtension.sol"; +import {IERC721LockUpPool} from "../../interfaces/IERC721/IERC721LockUpPool.sol"; + contract ERC721LockUpStakingPool is ReentrancyGuard, Ownable, - IERC721LockUpPoolExtension + IERC721LockUpPool { using SafeERC20 for IERC20; - /// @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 (!pool.isActive) revert PoolNotActive(); _; } ///@dev Mapping to store user-specific staking information - mapping(address => BaseUserInfo) public userInfo; + mapping(address => UserInfo) public userInfo; - /// @dev Modifier to allow only the admin to execute certain functions - modifier onlyAdmin() { - if (msg.sender != pool.adminWallet) revert NotAdmin(); - _; - } - - Pool public pool; + LockupPool public pool; constructor( address stakeToken, address rewardToken, + uint256 rewardTokenPerSecond, uint256 poolStartTime, uint256 poolEndTime, uint256 unstakeLockupTime, - uint256 claimLockUpTime, - uint256 rewardTokenPerSecond + uint256 claimLockUpTime ) Ownable(msg.sender) { // Ensure the staking period is valid if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); @@ -61,52 +56,67 @@ contract ERC721LockUpStakingPool is /** * @dev See {IERC721BasePool-stake}. */ - function stake(uint256[] calldata _tokenIds) external validPool { - //update parameters - _updatePool(); + function stake(uint256[] calldata tokenIds) external validPool { + uint256 amount = tokenIds.length; + if (amount == 0) revert InvalidAmount(); - uint64 len = uint64(_tokenIds.length); - if (len == 0) { - revert ZeroStakingTokens(); + 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) - user.rewardDebt; } + // Update user data + unchecked { + user.amount = currentAmount + amount; + } + user.rewardDebt = user.amount * share; + pool.totalStaked += amount; - for (uint64 i = 0; i < len; i++) { + for (uint256 i = 0; i < amount; i++) { pool.stakeToken.safeTransferFrom( msg.sender, address(this), - _tokenIds[i] + tokenIds[i] ); - pool.stakedTokens[_tokenIds[i]] = msg.sender; + pool.stakedTokens[tokenIds[i]] = msg.sender; } - pool.userInfo[msg.sender].amount += len; - pool.totalStaked += len; - emit Staked(msg.sender, _tokenIds); + emit Stake(msg.sender, tokenIds); } /** * @dev See {IERC721BasePool-unstake}. */ - function unstake(uint256[] calldata _tokenIds) external { - uint256 _amount = _tokenIds.length; - if (_amount > pool.userInfo[msg.sender].amount) { - revert NotEnoughTokens(); - } + function unstake(uint256[] calldata tokenIds) external { + uint256 amount = tokenIds.length; + if (amount == 0) revert InvalidAmount(); + UserInfo storage user = userInfo[msg.sender]; + uint256 currentAmount = user.amount; + if (amount > currentAmount) revert NotEnoughTokens(); _updatePool(); + uint256 share = pool.accRewardPerShare; - for (uint256 i = _amount; i < _amount; i++) { - if (pool.stakedTokens[_tokenIds[i]] != msg.sender) { + user.pending += (currentAmount * share) - user.rewardDebt; + // Update user data + unchecked { + user.amount -= amount; + } + pool.totalStaked -= amount; + + for (uint256 i = amount; i < amount; i++) { + if (pool.stakedTokens[tokenIds[i]] != msg.sender) revert NotStaker(); - } pool.stakeToken.safeTransferFrom( address(this), msg.sender, - _tokenIds[i] + tokenIds[i] ); - pool.stakedTokens[_tokenIds[1]] = address(0); + pool.stakedTokens[tokenIds[1]] = address(0); } - pool.userInfo[msg.sender].amount -= _amount; - pool.totalStaked -= _amount; - emit UnStaked(msg.sender, _tokenIds); + + emit Unstake(msg.sender, tokenIds); } /** @@ -121,7 +131,7 @@ contract ERC721LockUpStakingPool is _updatePool(); // Get user information - BaseUserInfo storage user = pool.userInfo[msg.sender]; + UserInfo storage user = userInfo[msg.sender]; uint256 amount = user.amount; uint256 pending = user.pending; @@ -160,6 +170,7 @@ contract ERC721LockUpStakingPool is } } } + function _getMultiplier( uint256 _from, uint256 _to From b81f0d75b005e89fa95e979dd6cf6bda720d314c Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 13 May 2024 21:42:21 +0300 Subject: [PATCH 6/9] [CI] Added codespell, fixed slither --- .github/workflows/codespell.yml | 19 +++++++++++++++++++ .github/workflows/slither.yml | 8 +++++++- .github/workflows/tests.yml | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/codespell.yml diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 0000000..f3e3803 --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,19 @@ +name: Codespell checkup + +on: + push: + branches: [ develop ] + pull_request: + branches: [ master, develop ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Run CodeSpell + uses: codespell-project/actions-codespell@v2.0 + with: + check_filenames: true + skip: package-lock.json,*.pdf \ No newline at end of file diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml index 1dcfb03..2313e2b 100644 --- a/.github/workflows/slither.yml +++ b/.github/workflows/slither.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + actions: read security-events: write steps: - name: Checkout repository @@ -25,4 +26,9 @@ jobs: fail-on: high sarif: results.sarif slither-config: slither.config.json - slither-args: --checklist --markdown-root ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/ \ No newline at end of file + slither-args: --checklist --markdown-root ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/ + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.slither.outputs.sarif }} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c4fd142..e741c9f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set .env run: cp .env.example .env From 80fb968ce2262e6780ff576c474ec85c61d52fde Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 13 May 2024 21:47:20 +0300 Subject: [PATCH 7/9] [Fix] Fixed codespell --- contracts/factories/ERC20LockUpFactory.sol | 6 +++--- contracts/factories/ERC20NoLockUpFactory.sol | 6 +++--- contracts/factories/ERC20PenaltyFeeFactory.sol | 6 +++--- .../interfaces/IERC20Factories/IERC20BaseFactory.sol | 2 +- contracts/interfaces/IERC20Pools/IERC20BasePool.sol | 8 ++++---- contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol | 2 +- test/ERC20LockUpStakingPool.test.ts | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index 59a112f..c211948 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -26,7 +26,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; - if (req.requestStatus != Status.APROVED) revert InvalidRequestStatus(); + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC20LockUpStakingPool{ @@ -88,7 +88,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { if (requests.length < id) revert InvalidId(); Request storage req = requests[id]; if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APROVED; + req.requestStatus = Status.APPROVED; emit RequestStatusChanged(id, req.requestStatus); } @@ -106,7 +106,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { if (msg.sender != req.deployer) revert InvalidCaller(); if ( req.requestStatus != Status.CREATED && - req.requestStatus != Status.APROVED + req.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); req.requestStatus = Status.CANCELED; emit RequestStatusChanged(id, req.requestStatus); diff --git a/contracts/factories/ERC20NoLockUpFactory.sol b/contracts/factories/ERC20NoLockUpFactory.sol index cc59930..bd4b9ec 100644 --- a/contracts/factories/ERC20NoLockUpFactory.sol +++ b/contracts/factories/ERC20NoLockUpFactory.sol @@ -26,7 +26,7 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; - if (req.requestStatus != Status.APROVED) revert InvalidRequestStatus(); + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC20NoLockUpStakingPool{ @@ -86,7 +86,7 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { if (requests.length < id) revert InvalidId(); Request storage req = requests[id]; if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APROVED; + req.requestStatus = Status.APPROVED; emit RequestStatusChanged(id, req.requestStatus); } @@ -104,7 +104,7 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { if (msg.sender != req.deployer) revert InvalidCaller(); if ( req.requestStatus != Status.CREATED || - req.requestStatus != Status.APROVED + req.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); req.requestStatus = Status.CANCELED; emit RequestStatusChanged(id, req.requestStatus); diff --git a/contracts/factories/ERC20PenaltyFeeFactory.sol b/contracts/factories/ERC20PenaltyFeeFactory.sol index c238774..8823296 100644 --- a/contracts/factories/ERC20PenaltyFeeFactory.sol +++ b/contracts/factories/ERC20PenaltyFeeFactory.sol @@ -26,7 +26,7 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IERC20PenaltyFeeFactory { function deploy(uint256 id) public returns (address newPoolAddress) { if (requests.length < id) revert InvalidId(); Request memory req = requests[id]; - if (req.requestStatus != Status.APROVED) revert InvalidRequestStatus(); + if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( new ERC20PenaltyFeePool{ @@ -77,7 +77,7 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IERC20PenaltyFeeFactory { if (requests.length < id) revert InvalidId(); Request storage req = requests[id]; if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus(); - req.requestStatus = Status.APROVED; + req.requestStatus = Status.APPROVED; emit RequestStatusChanged(id, req.requestStatus); } @@ -95,7 +95,7 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IERC20PenaltyFeeFactory { if (msg.sender != req.deployer) revert InvalidCaller(); if ( req.requestStatus != Status.CREATED || - req.requestStatus != Status.APROVED + req.requestStatus != Status.APPROVED ) revert InvalidRequestStatus(); req.requestStatus = Status.CANCELED; emit RequestStatusChanged(id, req.requestStatus); diff --git a/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol b/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol index dc20c18..652a5cd 100644 --- a/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol +++ b/contracts/interfaces/IERC20Factories/IERC20BaseFactory.sol @@ -6,7 +6,7 @@ interface IERC20BaseFactory { UNKNOWN, CREATED, DENIED, - APROVED, + APPROVED, DEPLOYED, CANCELED } diff --git a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol index 4abfff2..249a88f 100644 --- a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol @@ -31,7 +31,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user stakes tokens - * @dev Emmited in 'stake' function + * @dev Emited in 'stake' function * @param user The address of the user who stakes tokens * @param amount The amount of tokens staked */ @@ -39,7 +39,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user unstakes tokens - * @dev Emmited in 'unstake' function + * @dev Emited in 'unstake' function * @param user The address of the user who unstakes tokens * @param amount The amount of tokens unstaked */ @@ -47,7 +47,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user claims rewards - * @dev Emmited in 'claim' function + * @dev Emited in 'claim' function * @param user The address of the user who claims rewards * @param amount The amount of rewards claimed */ @@ -55,7 +55,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when the staking pool is updated - * @dev Emmited in '_updatePool' function + * @dev Emited in '_updatePool' function * @param totalStaked The total amount of tokens staked in the pool * @param accumulatedRewardTokenPerShare The accumulated rewards per share * @param lastBlockTimestamp The timestamp of the last block with any user operation diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol index 0d86956..b3696c6 100644 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol @@ -45,7 +45,7 @@ interface IERC20PenaltyPool is IBasePoolERC20 { /** * @notice Event to notify when an admin claims accumulated fees - * @dev Emmited in 'claim' function + * @dev Emited in 'claim' function * @param amount The amount of fees claimed */ event FeeClaim(uint256 amount); diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index 4204ca0..3740a44 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -238,7 +238,7 @@ describe("Contract Deployment", async function () { expect(req.requestStatus).to.be.equal(3); }); - it("Should correctly deploy pool from aproved request", async function () { + it("Should correctly deploy pool from APPROVED request", async function () { await mockRewardToken .connect(ayo) .approve( From 81258c303f93b721979e93d43e7a0dd3f83fd70c Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 13 May 2024 21:58:33 +0300 Subject: [PATCH 8/9] [Fix] Fixed codespell, slither issues --- contracts/interfaces/IERC20Pools/IERC20BasePool.sol | 8 ++++---- .../interfaces/IERC20Pools/IERC20PenaltyPool.sol | 2 +- contracts/pools/ERC721/ERC721LockUpStakingPool.sol | 12 +++++------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol index 249a88f..8238a70 100644 --- a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol @@ -31,7 +31,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user stakes tokens - * @dev Emited in 'stake' function + * @dev Emitted in 'stake' function * @param user The address of the user who stakes tokens * @param amount The amount of tokens staked */ @@ -39,7 +39,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user unstakes tokens - * @dev Emited in 'unstake' function + * @dev Emitted in 'unstake' function * @param user The address of the user who unstakes tokens * @param amount The amount of tokens unstaked */ @@ -47,7 +47,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when a user claims rewards - * @dev Emited in 'claim' function + * @dev Emitted in 'claim' function * @param user The address of the user who claims rewards * @param amount The amount of rewards claimed */ @@ -55,7 +55,7 @@ interface IBasePoolERC20 { /** * @notice Event to notify when the staking pool is updated - * @dev Emited in '_updatePool' function + * @dev Emitted in '_updatePool' function * @param totalStaked The total amount of tokens staked in the pool * @param accumulatedRewardTokenPerShare The accumulated rewards per share * @param lastBlockTimestamp The timestamp of the last block with any user operation diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol index b3696c6..5e3246c 100644 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol @@ -45,7 +45,7 @@ interface IERC20PenaltyPool is IBasePoolERC20 { /** * @notice Event to notify when an admin claims accumulated fees - * @dev Emited in 'claim' function + * @dev Emitted in 'claim' function * @param amount The amount of fees claimed */ event FeeClaim(uint256 amount); diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index c5ac677..ededae2 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -6,7 +6,6 @@ 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 {IERC721BasePool} from "../../interfaces/IERC721/IERC721BasePool.sol"; import {IERC721LockUpPool} from "../../interfaces/IERC721/IERC721LockUpPool.sol"; contract ERC721LockUpStakingPool is @@ -76,12 +75,12 @@ contract ERC721LockUpStakingPool is pool.totalStaked += amount; for (uint256 i = 0; i < amount; i++) { + pool.stakedTokens[tokenIds[i]] = msg.sender; pool.stakeToken.safeTransferFrom( msg.sender, address(this), tokenIds[i] ); - pool.stakedTokens[tokenIds[i]] = msg.sender; } emit Stake(msg.sender, tokenIds); } @@ -89,7 +88,7 @@ contract ERC721LockUpStakingPool is /** * @dev See {IERC721BasePool-unstake}. */ - function unstake(uint256[] calldata tokenIds) external { + function unstake(uint256[] calldata tokenIds) external nonReentrant { uint256 amount = tokenIds.length; if (amount == 0) revert InvalidAmount(); UserInfo storage user = userInfo[msg.sender]; @@ -97,25 +96,24 @@ contract ERC721LockUpStakingPool is if (amount > currentAmount) revert NotEnoughTokens(); _updatePool(); uint256 share = pool.accRewardPerShare; - user.pending += (currentAmount * share) - user.rewardDebt; // Update user data unchecked { user.amount -= amount; } + user.rewardDebt = user.amount * share; pool.totalStaked -= amount; - for (uint256 i = amount; i < amount; i++) { + for (uint256 i = 0; i < amount; i++) { if (pool.stakedTokens[tokenIds[i]] != msg.sender) revert NotStaker(); + pool.stakedTokens[tokenIds[i]] = address(0); pool.stakeToken.safeTransferFrom( address(this), msg.sender, tokenIds[i] ); - pool.stakedTokens[tokenIds[1]] = address(0); } - emit Unstake(msg.sender, tokenIds); } From 0f489a15ca2e25e6fd47da2a254fb6b8bd8d378f Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 14 May 2024 05:42:11 +0300 Subject: [PATCH 9/9] [Update] Added ERC721 lockup and draft-ERC721penaltyFee, updated packages, code refactoring --- .github/workflows/tests.yml | 4 +- .gitignore | 1 + contracts/factories/ERC20LockUpFactory.sol | 10 +- contracts/factories/ERC20NoLockUpFactory.sol | 6 +- .../IERC20Factories/IERC20LockUpFactory.sol | 2 +- .../interfaces/IERC20Pools/IERC20BasePool.sol | 3 + .../IERC20Pools/IERC20LockUpPool.sol | 2 +- .../IERC20Pools/IERC20NoLockupPool.sol | 2 +- .../IERC20Pools/IERC20PenaltyPool.sol | 2 +- .../interfaces/IERC721/IERC721BasePool.sol | 26 +- .../interfaces/IERC721/IERC721LockUpPool.sol | 11 +- .../IERC721/IERC721NoLockupPool.sol | 49 + .../IERC721/IERC721PenaltyFeePool.sol | 77 + contracts/pools/ERC20LockUpStakingPool.sol | 70 +- contracts/pools/ERC20NoLockUpStakingPool.sol | 61 +- contracts/pools/ERC20PenaltyFeePool.sol | 82 +- .../pools/ERC721/ERC721LockUpStakingPool.sol | 130 +- .../ERC721/ERC721NoLockUpStakingPool.sol | 228 ++ .../pools/ERC721/ERC721PenaltyFeePool.sol | 254 +++ hardhat.config.ts | 14 +- package-lock.json | 1925 ++++++----------- package.json | 27 +- test/ERC20LockUpStakingPool.test.ts | 100 +- 23 files changed, 1618 insertions(+), 1468 deletions(-) create mode 100644 contracts/interfaces/IERC721/IERC721NoLockupPool.sol create mode 100644 contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol create mode 100644 contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol create mode 100644 contracts/pools/ERC721/ERC721PenaltyFeePool.sol diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e741c9f..ace2d7d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,4 +24,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm ci - - run: npm run test \ No newline at end of file + - run: npm run test + - name: Markdown report + run: cat "./test/gasReport.md" > $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.gitignore b/.gitignore index e6c7381..3f2158e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules /cache /artifacts /ignition/deployments +/test/gasReport.md # TypeChain files /typechain diff --git a/contracts/factories/ERC20LockUpFactory.sol b/contracts/factories/ERC20LockUpFactory.sol index c211948..166e233 100644 --- a/contracts/factories/ERC20LockUpFactory.sol +++ b/contracts/factories/ERC20LockUpFactory.sol @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20LockUpStakingPool} from "../pools/ERC20LockUpStakingPool.sol"; +import {ERC20LockupPool} from "../pools/ERC20LockUpStakingPool.sol"; import {IERC20LockUpFactory} from "../interfaces/IERC20Factories/IERC20LockUpFactory.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -29,7 +29,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC20LockUpStakingPool{ + new ERC20LockupPool{ salt: keccak256( abi.encode( req.data.stakeToken, @@ -42,11 +42,11 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { }( req.data.stakeToken, req.data.rewardToken, - req.data.rewardPerSecond, req.data.poolStartTime, req.data.poolEndTime, req.data.unstakeLockupTime, - req.data.claimLockupTime + req.data.claimLockupTime, + req.data.rewardPerSecond ) ); stakingPools.push(newPoolAddress); @@ -54,7 +54,7 @@ contract ERC20LockUpStakingFactory is Ownable, IERC20LockUpFactory { poolById[id] = newPoolAddress; uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) * req.data.rewardPerSecond; - ERC20LockUpStakingPool(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 bd4b9ec..9d6e7dd 100644 --- a/contracts/factories/ERC20NoLockUpFactory.sol +++ b/contracts/factories/ERC20NoLockUpFactory.sol @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT */ pragma solidity 0.8.25; -import {ERC20NoLockUpStakingPool} from "../pools/ERC20NoLockUpStakingPool.sol"; +import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol"; import {IERC20NoLockupFactory} from "../interfaces/IERC20Factories/IERC20NoLockupFactory.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -29,7 +29,7 @@ contract ERC20NoLockUpStakingFactory is Ownable, IERC20NoLockupFactory { if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus(); if (msg.sender != req.deployer) revert InvalidCaller(); newPoolAddress = address( - new ERC20NoLockUpStakingPool{ + 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; - ERC20NoLockUpStakingPool(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/interfaces/IERC20Factories/IERC20LockUpFactory.sol b/contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol index e7cd50f..9884dd7 100644 --- a/contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol +++ b/contracts/interfaces/IERC20Factories/IERC20LockUpFactory.sol @@ -7,11 +7,11 @@ interface IERC20LockUpFactory is IERC20BaseFactory { struct DeploymentData { address stakeToken; address rewardToken; - uint256 rewardPerSecond; uint256 poolStartTime; uint256 poolEndTime; uint256 unstakeLockupTime; // Lockup period for unstaking uint256 claimLockupTime; // Lockup period for claiming rewards + uint256 rewardPerSecond; } struct Request { diff --git a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol index 8238a70..7acb8d0 100644 --- a/contracts/interfaces/IERC20Pools/IERC20BasePool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20BasePool.sol @@ -25,6 +25,9 @@ interface IBasePoolERC20 { /// @dev Error to indicate that the staking pool has not started yet error PoolNotStarted(); + /// @dev Error to indicate that the staking pool has already ended + error PoolHasEnded(); + /** * EVENTS */ diff --git a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol index b8b22b1..28201c1 100644 --- a/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20LockUpPool.sol @@ -20,7 +20,7 @@ interface IERC20LockupPool is IBasePoolERC20 { uint256 rewardTokenPerSecond; // Rate of rewards per second uint256 totalStaked; // Total amount of tokens staked uint256 totalClaimed; // Total amount of claimed rewards - uint256 lastRewardTimestamp; // Timestamp of the last reward update + uint256 lastUpdateTimestamp; // Timestamp of the last reward update uint256 accRewardPerShare; // Accumulated rewards per share } diff --git a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol b/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol index f16f7da..0c12dbe 100644 --- a/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20NoLockupPool.sol @@ -17,7 +17,7 @@ interface IERC20NoLockupPool is IBasePoolERC20 { uint256 rewardTokenPerSecond; // Rate of rewards per second uint256 totalStaked; // Total amount of tokens staked uint256 totalClaimed; // Total amount of claimed rewards - uint256 lastRewardTimestamp; // Timestamp of the last reward update + uint256 lastUpdateTimestamp; // Timestamp of the last reward update uint256 accRewardPerShare; // Accumulated rewards per share } } \ No newline at end of file diff --git a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol index 5e3246c..99e79d4 100644 --- a/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol +++ b/contracts/interfaces/IERC20Pools/IERC20PenaltyPool.sol @@ -13,7 +13,7 @@ interface IERC20PenaltyPool is IBasePoolERC20 { uint256 totalStaked; // Total amount of tokens staked uint256 totalClaimed; // Total amount of claimed rewards uint256 totalPenalties; - uint256 lastRewardTimestamp; // Timestamp of the last reward update + uint256 lastUpdateTimestamp; // Timestamp of the last reward update uint256 accRewardPerShare; // Accumulated rewards per share address adminWallet; // Address of the admin } diff --git a/contracts/interfaces/IERC721/IERC721BasePool.sol b/contracts/interfaces/IERC721/IERC721BasePool.sol index 627f969..81cc24b 100644 --- a/contracts/interfaces/IERC721/IERC721BasePool.sol +++ b/contracts/interfaces/IERC721/IERC721BasePool.sol @@ -25,9 +25,11 @@ interface IERC721BasePool { error InvalidAmount(); /** - * @notice Error emitted when attempting to unstake more tokens than the user has staked. + * @dev Error to indicate insufficient amount of tokens + * @param reqAmount The amount of tokens that is required + * @param currentAmount The current amount of tokens */ - error NotEnoughTokens(); + error InsufficientAmount(uint256 reqAmount, uint256 currentAmount); /** * @notice Error emitted when a user other than the owner of a token attempts to unstake it. @@ -45,16 +47,11 @@ interface IERC721BasePool { error PoolNotStarted(); /** - * @notice Error emitted when attempting an operation while pool is not active. + * @notice Error emitted when attempting an operation after the pool has ended. */ - error PoolNotActive(); - /** - * @notice Error emitted when attempting an operation while pool is not active. - */ - error NotAdmin(); - + error PoolHasEnded(); + // **Events** - /** * @notice Event emitted when tokens are staked into the pool. * @param user The address of the user who staked the tokens. @@ -106,4 +103,13 @@ interface IERC721BasePool { * @notice Allows users to claim their pending rewards. */ function claim() external; + + /** + * @notice Function to calculate pending rewards for a user + * @param userAddress Address of the user + * @return pending rewards + */ + function pendingRewards( + address userAddress + ) external view returns (uint256); } diff --git a/contracts/interfaces/IERC721/IERC721LockUpPool.sol b/contracts/interfaces/IERC721/IERC721LockUpPool.sol index be0fdcf..2a56d6a 100644 --- a/contracts/interfaces/IERC721/IERC721LockUpPool.sol +++ b/contracts/interfaces/IERC721/IERC721LockUpPool.sol @@ -27,16 +27,13 @@ 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 period (in seconds) after unstaking - * @dev claimLockupTime The lockup period (in seconds) 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 * @dev lastUpdateTimestamp: The timestamp of the last update * @dev accRewardPerShare: Accumulated rewards per staked token - * @dev isActive: Flag indicating active/inactive pool - * @dev adminWallet: Address of the pool admin - * @dev userInfo: Mapping for user staking data * @dev stakedTokens: Mapping tokenIds to owner addresses */ struct LockupPool { @@ -44,13 +41,13 @@ interface IERC721LockUpPool is IERC721BasePool{ IERC20 rewardToken; uint256 startTime; uint256 endTime; + uint256 unstakeLockupTime; // Lockup period for unstaking + uint256 claimLockupTime; // Lockup period for claiming rewards uint256 rewardTokenPerSecond; uint256 totalStaked; uint256 totalClaimed; uint256 lastUpdateTimestamp; uint256 accRewardPerShare; - uint256 unstakeLockupTime; // Lockup period for unstaking - uint256 claimLockupTime; // Lockup period for claiming rewards mapping(uint256 => address) stakedTokens; } diff --git a/contracts/interfaces/IERC721/IERC721NoLockupPool.sol b/contracts/interfaces/IERC721/IERC721NoLockupPool.sol new file mode 100644 index 0000000..9a71ce6 --- /dev/null +++ b/contracts/interfaces/IERC721/IERC721NoLockupPool.sol @@ -0,0 +1,49 @@ +// 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 + * @dev stakedTokens: Mapping tokenIds to owner addresses + */ + struct Pool { + IERC721 stakeToken; + IERC20 rewardToken; + uint256 startTime; + uint256 endTime; + uint256 rewardTokenPerSecond; + uint256 totalStaked; + uint256 totalClaimed; + uint256 lastUpdateTimestamp; + uint256 accRewardPerShare; + mapping(uint256 => address) stakedTokens; + } +} diff --git a/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol new file mode 100644 index 0000000..fb357fe --- /dev/null +++ b/contracts/interfaces/IERC721/IERC721PenaltyFeePool.sol @@ -0,0 +1,77 @@ +// 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 IERC721PenaltyFeePool 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; + uint256 penaltyEndTime; + bool penalized; + } + + /** + * @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 + * @dev stakedTokens: Mapping tokenIds to owner addresses + */ + struct PenaltyPool { + IERC721 stakeToken; + IERC20 rewardToken; + uint256 startTime; + uint256 endTime; + uint256 penaltyPeriod; + uint256 rewardTokenPerSecond; + uint256 totalStaked; + uint256 totalClaimed; + uint256 totalPenalties; + uint256 lastUpdateTimestamp; + uint256 accRewardPerShare; + address adminWallet; + mapping(uint256 => address) stakedTokens; + } + + /** + * 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/pools/ERC20LockUpStakingPool.sol b/contracts/pools/ERC20LockUpStakingPool.sol index 679a4a9..8a847a0 100644 --- a/contracts/pools/ERC20LockUpStakingPool.sol +++ b/contracts/pools/ERC20LockUpStakingPool.sol @@ -8,13 +8,9 @@ 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 ERC20LockUpStakingPool +/// @title ERC20LockupPool /// @notice A smart contract for staking ERC20 tokens and earning rewards over a specified period. -contract ERC20LockUpStakingPool is - ReentrancyGuard, - Ownable, - IERC20LockupPool -{ +contract ERC20LockupPool is ReentrancyGuard, Ownable, IERC20LockupPool { using SafeERC20 for IERC20; /// @dev Precision factor for calculations @@ -28,25 +24,26 @@ contract ERC20LockUpStakingPool is /// @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(); _; } /// @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 rewardTokenPerSecond Rate of rewards per second /// @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 rewardTokenPerSecond Rate of rewards per second constructor( address stakeToken, address rewardToken, - uint256 rewardTokenPerSecond, uint256 poolStartTime, uint256 poolEndTime, uint256 unstakeLockup, - uint256 claimLockup + uint256 claimLockup, + uint256 rewardTokenPerSecond ) Ownable(msg.sender) { // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); @@ -59,12 +56,12 @@ contract ERC20LockUpStakingPool is // Initialize pool parameters pool.stakeToken = stakeToken; pool.rewardToken = rewardToken; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastRewardTimestamp = poolStartTime; pool.startTime = poolStartTime; pool.endTime = poolEndTime; pool.unstakeLockupTime = unstakeLockup; pool.claimLockupTime = claimLockup; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = poolStartTime; } /** @@ -116,7 +113,7 @@ contract ERC20LockUpStakingPool is uint256 currentAmount = user.amount; // Ensure the user has enough staked tokens if (currentAmount < amount) - revert InsufficientAmount(currentAmount, amount); + revert InsufficientAmount(amount, currentAmount); // Update the pool _updatePool(); // Get accumulated rewards per share @@ -161,15 +158,18 @@ contract ERC20LockUpStakingPool is (user.amount * pool.accRewardPerShare) / PRECISION_FACTOR; } - if (pending == 0) revert NothingToClaim(); - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; + 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(); } - pool.totalClaimed += pending; - IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); } /** @@ -183,18 +183,14 @@ contract ERC20LockUpStakingPool is uint256 share = pool.accRewardPerShare; // Update accumulated rewards per share if necessary if ( - block.timestamp > pool.lastRewardTimestamp && - pool.totalStaked != 0 + block.timestamp > pool.lastUpdateTimestamp && pool.totalStaked != 0 ) { uint256 elapsedPeriod = _getMultiplier( - pool.lastRewardTimestamp, + pool.lastUpdateTimestamp, block.timestamp ); - uint256 totalNewReward = pool.rewardTokenPerSecond * - elapsedPeriod; - share += - (totalNewReward * PRECISION_FACTOR) / - pool.totalStaked; + uint256 totalNewReward = pool.rewardTokenPerSecond * elapsedPeriod; + share += (totalNewReward * PRECISION_FACTOR) / pool.totalStaked; } // Calculate pending rewards return @@ -209,25 +205,23 @@ contract ERC20LockUpStakingPool is * the pool rewadrs are no longer updated (stopped). */ function _updatePool() internal { + uint256 lastTimestamp = pool.lastUpdateTimestamp; + uint256 total = pool.totalStaked; // Update accumulated rewards per share if necessary - if (block.timestamp > pool.lastRewardTimestamp) { - if (pool.totalStaked != 0) { + if (block.timestamp > lastTimestamp) { + if (total > 0) { uint256 elapsedPeriod = _getMultiplier( - pool.lastRewardTimestamp, + lastTimestamp, block.timestamp ); pool.accRewardPerShare += (pool.rewardTokenPerSecond * PRECISION_FACTOR * elapsedPeriod) / - pool.totalStaked; + total; } - pool.lastRewardTimestamp = block.timestamp; - emit UpdatePool( - pool.totalStaked, - pool.accRewardPerShare, - block.timestamp - ); + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); } } diff --git a/contracts/pools/ERC20NoLockUpStakingPool.sol b/contracts/pools/ERC20NoLockUpStakingPool.sol index 35dcdeb..f063051 100644 --- a/contracts/pools/ERC20NoLockUpStakingPool.sol +++ b/contracts/pools/ERC20NoLockUpStakingPool.sol @@ -8,12 +8,17 @@ 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 ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPool { +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 @@ -24,15 +29,15 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo /// @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 rewardTokenPerSecond Rate of rewards per second /// @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 rewardTokenPerSecond, uint256 poolStartTime, - uint256 poolEndTime + uint256 poolEndTime, + uint256 rewardTokenPerSecond ) Ownable(msg.sender) { // Ensure the start time is in the future if (poolStartTime < block.timestamp) revert InvalidStartTime(); @@ -40,10 +45,10 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo if (poolStartTime > poolEndTime) revert InvalidStakingPeriod(); pool.stakeToken = stakeToken; pool.rewardToken = rewardToken; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastRewardTimestamp = poolStartTime; pool.startTime = poolStartTime; pool.endTime = poolEndTime; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = poolStartTime; } /** @@ -82,7 +87,7 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo UserInfo storage user = userInfo[msg.sender]; uint256 currentAmount = user.amount; if (currentAmount < amount) - revert InsufficientAmount(currentAmount, amount); + revert InsufficientAmount(amount, currentAmount); _updatePool(); uint256 share = pool.accRewardPerShare; user.pending += @@ -114,15 +119,18 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo (amount * pool.accRewardPerShare) / PRECISION_FACTOR; } - if (pending == 0) revert NothingToClaim(); - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; + 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(); } - pool.totalClaimed += pending; - IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); } /** @@ -134,10 +142,10 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo UserInfo storage user = userInfo[userAddress]; uint256 share = pool.accRewardPerShare; if ( - block.timestamp > pool.lastRewardTimestamp && pool.totalStaked != 0 + block.timestamp > pool.lastUpdateTimestamp && pool.totalStaked != 0 ) { uint256 elapsedPeriod = _getMultiplier( - pool.lastRewardTimestamp, + pool.lastUpdateTimestamp, block.timestamp ); uint256 totalNewReward = pool.rewardTokenPerSecond * elapsedPeriod; @@ -152,24 +160,23 @@ contract ERC20NoLockUpStakingPool is ReentrancyGuard, Ownable, IERC20NoLockupPoo } function _updatePool() internal { - if (block.timestamp > pool.lastRewardTimestamp) { - if (pool.totalStaked != 0) { + 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( - pool.lastRewardTimestamp, + lastTimestamp, block.timestamp ); pool.accRewardPerShare += (pool.rewardTokenPerSecond * PRECISION_FACTOR * elapsedPeriod) / - pool.totalStaked; + total; } - pool.lastRewardTimestamp = block.timestamp; - emit UpdatePool( - pool.totalStaked, - pool.accRewardPerShare, - block.timestamp - ); + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); } } diff --git a/contracts/pools/ERC20PenaltyFeePool.sol b/contracts/pools/ERC20PenaltyFeePool.sol index 43d4ab6..48716ce 100644 --- a/contracts/pools/ERC20PenaltyFeePool.sol +++ b/contracts/pools/ERC20PenaltyFeePool.sol @@ -9,11 +9,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 ERC20PenaltyFeePool is - ReentrancyGuard, - Ownable, - IERC20PenaltyPool -{ +contract ERC20PenaltyFeePool is ReentrancyGuard, Ownable, IERC20PenaltyPool { using SafeERC20 for IERC20; uint256 public constant PRECISION_FACTOR = 10e18; uint256 public constant PENALTY_FEE = 2500; @@ -30,6 +26,7 @@ contract ERC20PenaltyFeePool is } modifier validPool() { if (block.timestamp < pool.startTime) revert PoolNotStarted(); + if (block.timestamp > pool.endTime) revert PoolHasEnded(); _; } @@ -50,12 +47,12 @@ contract ERC20PenaltyFeePool is revert InvalidPenaltyPeriod(); pool.stakeToken = stakeToken; pool.rewardToken = rewardToken; - pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastRewardTimestamp = poolStartTime; pool.startTime = poolStartTime; pool.endTime = poolEndTime; - pool.adminWallet = adminAddress; pool.penaltyPeriod = penaltyPeriod; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = poolStartTime; + pool.adminWallet = adminAddress; } /** @@ -82,7 +79,11 @@ contract ERC20PenaltyFeePool is : block.timestamp + pool.penaltyPeriod; user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; pool.totalStaked += amount; - IERC20(pool.stakeToken).safeTransferFrom(msg.sender, address(this), amount); + IERC20(pool.stakeToken).safeTransferFrom( + msg.sender, + address(this), + amount + ); emit Stake(msg.sender, amount); } @@ -93,11 +94,14 @@ contract ERC20PenaltyFeePool is if (amount == 0) revert InvalidAmount(); UserInfo storage user = userInfo[msg.sender]; uint256 currentAmount = user.amount; - if (currentAmount < amount) revert InsufficientAmount(currentAmount, amount); + if (currentAmount < amount) + revert InsufficientAmount(amount, currentAmount); _updatePool(); uint256 share = pool.accRewardPerShare; if (block.timestamp <= user.penaltyEndTime) user.penalized = true; - user.pending += ((currentAmount * share) / PRECISION_FACTOR) - user.rewardDebt; + user.pending += + ((currentAmount * share) / PRECISION_FACTOR) - + user.rewardDebt; unchecked { user.amount -= amount; } @@ -126,21 +130,24 @@ contract ERC20PenaltyFeePool is (amount * pool.accRewardPerShare) / PRECISION_FACTOR; } - if (pending == 0) revert NothingToClaim(); - user.pending = 0; - uint256 penalityAmount = _calculatePenalizedAmount( - user.penalized, - pending - ); - pending -= penalityAmount; - if (user.penalized) user.penalized = false; - unchecked { - user.claimed += pending; + if (pending > 0) { + user.pending = 0; + uint256 penalityAmount = _calculatePenalizedAmount( + user.penalized, + pending + ); + pending -= penalityAmount; + if (user.penalized) user.penalized = false; + unchecked { + user.claimed += pending; + } + pool.totalClaimed += pending; + pool.totalPenalties += penalityAmount; + IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); + emit Claim(msg.sender, pending); + } else { + revert NothingToClaim(); } - pool.totalClaimed += pending; - pool.totalPenalties += penalityAmount; - IERC20(pool.rewardToken).safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); } function claimFee() external nonReentrant onlyAdmin { @@ -158,11 +165,11 @@ contract ERC20PenaltyFeePool is uint256 share = pool.accRewardPerShare; uint256 pending = user.pending; if ( - block.timestamp > pool.lastRewardTimestamp && pool.totalStaked > 0 + block.timestamp > pool.lastUpdateTimestamp && pool.totalStaked > 0 ) { uint256 elapsedPeriod = _getMultiplier( block.timestamp, - pool.lastRewardTimestamp + pool.lastUpdateTimestamp ); uint256 totalNewReward = pool.rewardTokenPerSecond * elapsedPeriod; share = @@ -174,24 +181,23 @@ contract ERC20PenaltyFeePool is } function _updatePool() internal { - if (block.timestamp > pool.lastRewardTimestamp) { - if (pool.totalStaked != 0) { + 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( - block.timestamp, - pool.lastRewardTimestamp + lastTimestamp, + block.timestamp ); pool.accRewardPerShare += (pool.rewardTokenPerSecond * PRECISION_FACTOR * elapsedPeriod) / - pool.totalStaked; + total; } - pool.lastRewardTimestamp = block.timestamp; - emit UpdatePool( - pool.totalStaked, - pool.accRewardPerShare, - block.timestamp - ); + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); } } diff --git a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol index ededae2..0cc0d56 100644 --- a/contracts/pools/ERC721/ERC721LockUpStakingPool.sol +++ b/contracts/pools/ERC721/ERC721LockUpStakingPool.sol @@ -8,16 +8,19 @@ 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"; -contract ERC721LockUpStakingPool is +contract ERC721LockUpPool is ReentrancyGuard, Ownable, IERC721LockUpPool { 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 @@ -49,13 +52,15 @@ contract ERC721LockUpStakingPool is pool.unstakeLockupTime = unstakeLockupTime; pool.claimLockupTime = claimLockUpTime; pool.rewardTokenPerSecond = rewardTokenPerSecond; - pool.lastUpdateTimestamp = block.timestamp; + pool.lastUpdateTimestamp = pool.startTime; } /** * @dev See {IERC721BasePool-stake}. */ - function stake(uint256[] calldata tokenIds) external validPool { + function stake( + uint256[] calldata tokenIds + ) external validPool nonReentrant { uint256 amount = tokenIds.length; if (amount == 0) revert InvalidAmount(); @@ -65,17 +70,25 @@ contract ERC721LockUpStakingPool is uint256 currentAmount = user.amount; // Calculate pending rewards if (currentAmount > 0) { - user.pending += (currentAmount * share) - user.rewardDebt; + user.pending += + (currentAmount * share) / + PRECISION_FACTOR - + user.rewardDebt; } // Update user data unchecked { - user.amount = currentAmount + amount; + user.amount += amount; } - user.rewardDebt = user.amount * share; + 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++) { pool.stakedTokens[tokenIds[i]] = msg.sender; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < amount; i++) { pool.stakeToken.safeTransferFrom( msg.sender, address(this), @@ -89,25 +102,33 @@ contract ERC721LockUpStakingPool is * @dev See {IERC721BasePool-unstake}. */ function unstake(uint256[] calldata tokenIds) external nonReentrant { - uint256 amount = tokenIds.length; - if (amount == 0) revert InvalidAmount(); + uint256 length = tokenIds.length; + if (length == 0) revert InvalidAmount(); UserInfo storage user = userInfo[msg.sender]; uint256 currentAmount = user.amount; - if (amount > currentAmount) revert NotEnoughTokens(); + if (length > currentAmount) + revert InsufficientAmount(length, currentAmount); _updatePool(); uint256 share = pool.accRewardPerShare; - user.pending += (currentAmount * share) - user.rewardDebt; + user.pending += + ((currentAmount * share) / PRECISION_FACTOR) - + user.rewardDebt; // Update user data unchecked { - user.amount -= amount; + user.amount -= length; } - user.rewardDebt = user.amount * share; - pool.totalStaked -= amount; + user.rewardDebt = (user.amount * share) / PRECISION_FACTOR; + pool.totalStaked -= length; - for (uint256 i = 0; i < amount; i++) { + // 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) revert NotStaker(); - pool.stakedTokens[tokenIds[i]] = address(0); + delete pool.stakedTokens[tokenIds[i]]; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < length; i++) { pool.stakeToken.safeTransferFrom( address(this), msg.sender, @@ -135,37 +156,70 @@ contract ERC721LockUpStakingPool is // Calculate pending rewards if (amount > 0) { - pending += (amount * pool.accRewardPerShare) - user.rewardDebt; - user.rewardDebt = (user.amount * pool.accRewardPerShare); + pending += + (amount * pool.accRewardPerShare) / + PRECISION_FACTOR - + user.rewardDebt; + user.rewardDebt = + (user.amount * pool.accRewardPerShare) / + PRECISION_FACTOR; } - if (pending == 0) revert NothingToClaim(); - // Transfer pending rewards to the user - user.pending = 0; - unchecked { - user.claimed += pending; + 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(); } - pool.totalClaimed += pending; - pool.rewardToken.safeTransfer(msg.sender, pending); - emit Claim(msg.sender, pending); + } + + 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 { - if (block.timestamp > pool.lastUpdateTimestamp) { - if (pool.totalStaked > 0) { - uint256 elapsedTime = _getMultiplier( - pool.lastUpdateTimestamp, + 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 = - (elapsedTime * pool.rewardTokenPerSecond) / - pool.totalStaked; - pool.lastUpdateTimestamp = block.timestamp; - emit UpdatePool( - pool.totalStaked, - pool.accRewardPerShare, - pool.lastUpdateTimestamp - ); + pool.accRewardPerShare += + (pool.rewardTokenPerSecond * + PRECISION_FACTOR * + elapsedPeriod) / + total; } + pool.lastUpdateTimestamp = block.timestamp; + emit UpdatePool(total, pool.accRewardPerShare, block.timestamp); } } diff --git a/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol new file mode 100644 index 0000000..3b5e1e9 --- /dev/null +++ b/contracts/pools/ERC721/ERC721NoLockUpStakingPool.sol @@ -0,0 +1,228 @@ +// 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 ERC721LockUpPool 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; + + 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 = IERC721(stakeToken); + pool.rewardToken = IERC20(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++) { + pool.stakedTokens[tokenIds[i]] = msg.sender; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < amount; i++) { + 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 (pool.stakedTokens[tokenIds[i]] != msg.sender) + revert NotStaker(); + delete pool.stakedTokens[tokenIds[i]]; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < length; i++) { + 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 new file mode 100644 index 0000000..8f00d3f --- /dev/null +++ b/contracts/pools/ERC721/ERC721PenaltyFeePool.sol @@ -0,0 +1,254 @@ +// 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 {IERC721PenaltyFeePool} from "../../interfaces/IERC721/IERC721PenaltyFeePool.sol"; + +contract draftERC721PenaltyFeepPool is + ReentrancyGuard, + Ownable, + IERC721PenaltyFeePool +{ + using SafeERC20 for IERC20; + /// @dev Precision factor for calculations + uint256 public constant PRECISION_FACTOR = 10e18; + uint256 public constant PENALTY_FEE = 2500; + uint256 public constant COLLECTABLE_FEE = 100; + + /// @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; + + PenaltyPool public pool; + + constructor( + address stakeToken, + address rewardToken, + uint256 rewardTokenPerSecond, + uint256 poolStartTime, + uint256 poolEndTime, + uint256 penaltyPeriod, + address adminAddress + ) 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 (poolEndTime - poolStartTime > penaltyPeriod) + revert InvalidPenaltyPeriod(); + + pool.stakeToken = IERC721(stakeToken); + pool.rewardToken = IERC20(rewardToken); + pool.startTime = poolStartTime; + pool.endTime = poolEndTime; + pool.penaltyPeriod = penaltyPeriod; + pool.rewardTokenPerSecond = rewardTokenPerSecond; + pool.lastUpdateTimestamp = poolStartTime; + pool.adminWallet = adminAddress; + } + + /** + * @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++) { + pool.stakedTokens[tokenIds[i]] = msg.sender; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < amount; i++) { + 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 (pool.stakedTokens[tokenIds[i]] != msg.sender) + revert NotStaker(); + delete pool.stakedTokens[tokenIds[i]]; + } + + // Interactions: Transfer the tokens after state changes + for (uint256 i = 0; i < length; i++) { + pool.stakeToken.safeTransferFrom( + address(this), + msg.sender, + tokenIds[i] + ); + } + emit Unstake(msg.sender, tokenIds); + } + + /** + * @dev See {IERC721BasePool-claim}. + */ + function claim() external nonReentrant { + // Get user information + UserInfo storage user = userInfo[msg.sender]; + // 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 + _updatePool(); + 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) { + user.pending = 0; + uint256 penalityAmount = _calculatePenalizedAmount( + user.penalized, + pending + ); + pending -= penalityAmount; + if (user.penalized) user.penalized = false; + unchecked { + user.claimed += pending; + } + pool.totalClaimed += pending; + pool.totalPenalties += penalityAmount; + 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; + uint256 pending = user.pending; + // 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 + pending += ((user.amount * share) / PRECISION_FACTOR) - user.rewardDebt; + return pending - _calculatePenalizedAmount(user.penalized, pending); + } + + function _calculatePenalizedAmount( + bool penalized, + uint256 _amountToPenalize + ) internal pure returns (uint256) { + if (penalized) { + return (_amountToPenalize * PENALTY_FEE) / 10000; + } + // Flat 1% penalty fee in basis points if the penalty period has already ended + return (_amountToPenalize * COLLECTABLE_FEE) / 10000; + } + + 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/hardhat.config.ts b/hardhat.config.ts index 5bae5c0..f50e02f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,6 +1,11 @@ import * as dotenv from 'dotenv'; import { HardhatUserConfig } from "hardhat/config"; -import "@nomicfoundation/hardhat-toolbox"; +import '@nomicfoundation/hardhat-verify'; +import 'hardhat-gas-reporter'; +import '@typechain/hardhat'; +import '@nomicfoundation/hardhat-network-helpers'; +import '@nomicfoundation/hardhat-ethers'; +import '@nomicfoundation/hardhat-chai-matchers'; dotenv.config(); @@ -41,7 +46,12 @@ const config: HardhatUserConfig = { gasReporter: { enabled: true, currency: 'USD', - gasPrice: 50, + L1: "polygon", + L1Etherscan: process.env.AMOY_API_KEY as string, + reportFormat: "markdown", + outputFile: "./test/gasReport.md", + forceTerminalOutput: true, + forceTerminalOutputFormat: "terminal" }, typechain: { outDir: './typechain', diff --git a/package-lock.json b/package-lock.json index ed8b581..ab2a900 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,28 +5,27 @@ "packages": { "": { "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@nomicfoundation/hardhat-ignition": "^0.15.0", - "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-ignition": "^0.15.3", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.3", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomicfoundation/hardhat-toolbox": "^5.0.0", - "@nomicfoundation/hardhat-verify": "^2.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.6", "@openzeppelin/contracts": "^5.0.2", - "@typechain/ethers-v6": "^0.5.0", - "@typechain/hardhat": "^9.0.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", "@types/node": "^20.12.7", "chai": "^4.2.0", "dotenv": "^16.4.5", - "ethers": "^6.4.0", - "hardhat": "^2.22.2", - "hardhat-gas-reporter": "^1.0.8", - "solidity-coverage": "^0.8.0", + "ethers": "^6.12.1", + "hardhat": "^2.22.3", + "hardhat-gas-reporter": "^2.1.1", + "solidity-coverage": "^0.8.12", "ts-node": ">=8.0.0", - "typechain": "^8.3.0", - "typescript": ">=4.5.0" + "typechain": "^8.3.2", + "typescript": "^5.4.5" } }, "node_modules/@adraffy/ens-normalize": { @@ -35,6 +34,16 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "dev": true }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -97,6 +106,33 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", @@ -226,26 +262,6 @@ "@ethersproject/bytes": "^5.7.0" } }, - "node_modules/@ethersproject/basex": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", - "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, "node_modules/@ethersproject/bignumber": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", @@ -305,34 +321,6 @@ "@ethersproject/bignumber": "^5.7.0" } }, - "node_modules/@ethersproject/contracts": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", - "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0" - } - }, "node_modules/@ethersproject/hash": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", @@ -360,73 +348,6 @@ "@ethersproject/strings": "^5.7.0" } }, - "node_modules/@ethersproject/hdnode": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", - "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "node_modules/@ethersproject/json-wallets": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", - "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "dev": true - }, "node_modules/@ethersproject/keccak256": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", @@ -482,26 +403,6 @@ "@ethersproject/logger": "^5.7.0" } }, - "node_modules/@ethersproject/pbkdf2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", - "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/sha2": "^5.7.0" - } - }, "node_modules/@ethersproject/properties": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", @@ -521,85 +422,6 @@ "@ethersproject/logger": "^5.7.0" } }, - "node_modules/@ethersproject/providers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", - "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0", - "bech32": "1.1.4", - "ws": "7.4.6" - } - }, - "node_modules/@ethersproject/providers/node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@ethersproject/random": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", - "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, "node_modules/@ethersproject/rlp": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", @@ -620,27 +442,6 @@ "@ethersproject/logger": "^5.7.0" } }, - "node_modules/@ethersproject/sha2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", - "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "hash.js": "1.1.7" - } - }, "node_modules/@ethersproject/signing-key": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", @@ -665,30 +466,6 @@ "hash.js": "1.1.7" } }, - "node_modules/@ethersproject/solidity": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", - "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, "node_modules/@ethersproject/strings": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", @@ -758,39 +535,6 @@ "@ethersproject/logger": "^5.7.0" } }, - "node_modules/@ethersproject/wallet": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", - "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/json-wallets": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, "node_modules/@ethersproject/web": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", @@ -814,29 +558,6 @@ "@ethersproject/strings": "^5.7.0" } }, - "node_modules/@ethersproject/wordlists": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", - "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -846,6 +567,102 @@ "node": ">=14" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -989,27 +806,27 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.3.5.tgz", - "integrity": "sha512-dPSM9DuI1sr71gqWUMgLo8MjHQWO4+WNDm3iWaT6P4vUFJReZX5qwA5X+3UwIPBry8GvNY084u7yWUvB3/8rqA==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.3.7.tgz", + "integrity": "sha512-v2JFWnFKRsnOa6PDUrD+sr8amcdhxnG/YbL7LzmgRGU1odWEyOF4/EwNeUajQr4ZNKVWrYnJ6XjydXtUge5OBQ==", "dev": true, "engines": { "node": ">= 18" }, "optionalDependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.3.5", - "@nomicfoundation/edr-darwin-x64": "0.3.5", - "@nomicfoundation/edr-linux-arm64-gnu": "0.3.5", - "@nomicfoundation/edr-linux-arm64-musl": "0.3.5", - "@nomicfoundation/edr-linux-x64-gnu": "0.3.5", - "@nomicfoundation/edr-linux-x64-musl": "0.3.5", - "@nomicfoundation/edr-win32-x64-msvc": "0.3.5" + "@nomicfoundation/edr-darwin-arm64": "0.3.7", + "@nomicfoundation/edr-darwin-x64": "0.3.7", + "@nomicfoundation/edr-linux-arm64-gnu": "0.3.7", + "@nomicfoundation/edr-linux-arm64-musl": "0.3.7", + "@nomicfoundation/edr-linux-x64-gnu": "0.3.7", + "@nomicfoundation/edr-linux-x64-musl": "0.3.7", + "@nomicfoundation/edr-win32-x64-msvc": "0.3.7" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.5.tgz", - "integrity": "sha512-gIXUIiPMUy6roLHpNlxf15DumU7/YhffUf7XIB+WUjMecaySfTGyZsTGnCMJZqrDyiYqWPyPKwCV/2u/jqFAUg==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.7.tgz", + "integrity": "sha512-6tK9Lv/lSfyBvpEQ4nsTfgxyDT1y1Uv/x8Wa+aB+E8qGo3ToexQ1BMVjxJk6PChXCDOWxB3B4KhqaZFjdhl3Ow==", "cpu": [ "arm64" ], @@ -1023,9 +840,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.5.tgz", - "integrity": "sha512-0MrpOCXUK8gmplpYZ2Cy0holHEylvWoNeecFcrP2WJ5DLQzrB23U5JU2MvUzOJ7aL76Za1VXNBWi/UeTWdHM+w==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.7.tgz", + "integrity": "sha512-1RrQ/1JPwxrYO69e0tglFv5H+ggour5Ii3bb727+yBpBShrxtOTQ7fZyfxA5h62LCN+0Z9wYOPeQ7XFcVurMaQ==", "cpu": [ "x64" ], @@ -1039,9 +856,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.5.tgz", - "integrity": "sha512-aw9f7AZMiY1dZFNePJGKho2k+nEgFgzUAyyukiKfSqUIMXoFXMf1U3Ujv848czrSq9c5XGcdDa2xnEf3daU3xg==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.7.tgz", + "integrity": "sha512-ds/CKlBoVXIihjhflhgPn13EdKWed6r5bgvMs/YwRqT5wldQAQJZWAfA2+nYm0Yi2gMGh1RUpBcfkyl4pq7G+g==", "cpu": [ "arm64" ], @@ -1055,9 +872,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.5.tgz", - "integrity": "sha512-cVFRQjyABBlsbDj+XTczYBfrCHprZ6YNzN8gGGSqAh+UGIJkAIRomK6ar27GyJLNx3HkgbuDoi/9kA0zOo/95w==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.7.tgz", + "integrity": "sha512-e29udiRaPujhLkM3+R6ju7QISrcyOqpcaxb2FsDWBkuD7H8uU9JPZEyyUIpEp5uIY0Jh1eEJPKZKIXQmQAEAuw==", "cpu": [ "arm64" ], @@ -1071,9 +888,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.5.tgz", - "integrity": "sha512-CjOg85DfR1Vt0fQWn5U0qi26DATK9tVzo3YOZEyI0JBsnqvk43fUTPv3uUAWBrPIRg5O5kOc9xG13hSpCBBxBg==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.7.tgz", + "integrity": "sha512-/xkjmTyv+bbJ4akBCW0qzFKxPOV4AqLOmqurov+s9umHb16oOv72osSa3SdzJED2gHDaKmpMITT4crxbar4Axg==", "cpu": [ "x64" ], @@ -1087,9 +904,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.5.tgz", - "integrity": "sha512-hvX8bBGpBydAVevzK8jsu2FlqVZK1RrCyTX6wGHnltgMuBaoGLHYtNHiFpteOaJw2byYMiORc2bvj+98LhJ0Ew==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.7.tgz", + "integrity": "sha512-QwBP9xlmsbf/ldZDGLcE4QiAb8Zt46E/+WLpxHBATFhGa7MrpJh6Zse+h2VlrT/SYLPbh2cpHgSmoSlqVxWG9g==", "cpu": [ "x64" ], @@ -1103,9 +920,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.5.tgz", - "integrity": "sha512-IJXjW13DY5UPsx/eG5DGfXtJ7Ydwrvw/BTZ2Y93lRLHzszVpSmeVmlxjZP5IW2afTSgMLaAAsqNw4NhppRGN8A==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.7.tgz", + "integrity": "sha512-j/80DEnkxrF2ewdbk/gQ2EOPvgF0XSsg8D0o4+6cKhUVAW6XwtWKzIphNL6dyD2YaWEPgIrNvqiJK/aln0ww4Q==", "cpu": [ "x64" ], @@ -1216,13 +1033,13 @@ } }, "node_modules/@nomicfoundation/hardhat-ignition": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.1.tgz", - "integrity": "sha512-hWV/W9ZdG9HIqUiQXexrwoBBGP4IrDLghlZPAXXEXETmJ2AVPnBKQG626YmAYgEk2G3vX9ojn16daT+H2i/mFA==", + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.3.tgz", + "integrity": "sha512-gwCSI615nZmHu004L0Wp/J3Md+Pc8ipNzWD1x+UtFNdlf9R2hVhjSgp80tD/PMAJWzp8+DiTyQuX2dSZziJ4pQ==", "dev": true, "dependencies": { - "@nomicfoundation/ignition-core": "^0.15.1", - "@nomicfoundation/ignition-ui": "^0.15.1", + "@nomicfoundation/ignition-core": "^0.15.3", + "@nomicfoundation/ignition-ui": "^0.15.2", "chalk": "^4.0.0", "debug": "^4.3.2", "fs-extra": "^10.0.0", @@ -1234,14 +1051,14 @@ } }, "node_modules/@nomicfoundation/hardhat-ignition-ethers": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.1.tgz", - "integrity": "sha512-FPeE0EbJ+RcBGro9TxODyDffpSPhnG8ra43nJp7/1H2M0S+UkmJUeZlSjAIVfUut1zMwy+57j+PNn07dOr/YmQ==", + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.3.tgz", + "integrity": "sha512-PO3e+CE3N0yQZ+SjXFB5RB4QX5DbiYyDdoNeECiStRcz8qxHPN04qMy4CsVfr/b50hitqGavXSjz9UBKnafYFg==", "dev": true, "peerDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.4", - "@nomicfoundation/hardhat-ignition": "^0.15.1", - "@nomicfoundation/ignition-core": "^0.15.1", + "@nomicfoundation/hardhat-ignition": "^0.15.3", + "@nomicfoundation/ignition-core": "^0.15.3", "ethers": "^6.7.0", "hardhat": "^2.18.0" } @@ -1258,32 +1075,6 @@ "hardhat": "^2.9.5" } }, - "node_modules/@nomicfoundation/hardhat-toolbox": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-5.0.0.tgz", - "integrity": "sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==", - "dev": true, - "peerDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", - "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomicfoundation/hardhat-verify": "^2.0.0", - "@typechain/ethers-v6": "^0.5.0", - "@typechain/hardhat": "^9.0.0", - "@types/chai": "^4.2.0", - "@types/mocha": ">=9.1.0", - "@types/node": ">=18.0.0", - "chai": "^4.2.0", - "ethers": "^6.4.0", - "hardhat": "^2.11.0", - "hardhat-gas-reporter": "^1.0.8", - "solidity-coverage": "^0.8.1", - "ts-node": ">=8.0.0", - "typechain": "^8.3.0", - "typescript": ">=4.5.0" - } - }, "node_modules/@nomicfoundation/hardhat-verify": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.6.tgz", @@ -1376,9 +1167,9 @@ } }, "node_modules/@nomicfoundation/ignition-core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.1.tgz", - "integrity": "sha512-/AZO0YHRv1+yQSOtSSbg4GEH9YhU8EVePSfByU2PZW2bsAK0SA8GdoLYFbVNl140dogem5lrE+bCKtX0eN/n+A==", + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.3.tgz", + "integrity": "sha512-IW2mJVEoVOJYPgD5xRQdsZMi1qoDMOU4Q2bMGEKUm+6tLlIKJOUUvTRVt9tnXeU55rD6mOWbIoqE0OiWizTn/Q==", "dev": true, "dependencies": { "@ethersproject/address": "5.6.1", @@ -1428,9 +1219,9 @@ } }, "node_modules/@nomicfoundation/ignition-ui": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.1.tgz", - "integrity": "sha512-ecx6M9K4IeF7L0XCcHg0E72zlVaGSOlkhb/9XuWrA2ltfB/e4ZsOhVxXtwDf9xIcaq7tUdMSxyj6Ld0bPAhxAw==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.2.tgz", + "integrity": "sha512-NEX2prbfLEm45KbnBS0imvSgQgwLTgmT8zD3rAPmcIFZx+tLG4lKKw99k6EgEwmKwBiaO2zQMmt+FNoF7xGaiQ==", "dev": true }, "node_modules/@nomicfoundation/solidity-analyzer": { @@ -1620,6 +1411,16 @@ "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==", "dev": true }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@scure/base": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", @@ -1629,52 +1430,28 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@scure/bip32": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", - "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", - "dev": true, - "dependencies": { - "@noble/curves": "~1.3.0", - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", - "dev": true, - "dependencies": { - "@noble/hashes": "1.3.3" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", "dev": true, - "engines": { - "node": ">= 16" + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", - "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", "dev": true, "dependencies": { - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1819,13 +1596,10 @@ "dev": true }, "node_modules/@solidity-parser/parser": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", - "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", - "dev": true, - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" - } + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true }, "node_modules/@tsconfig/node10": { "version": "1.0.11", @@ -1906,9 +1680,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", - "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -1920,24 +1694,6 @@ "@types/chai": "*" } }, - "node_modules/@types/concat-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", - "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/form-data": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", - "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -1967,9 +1723,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1990,12 +1746,6 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, "node_modules/@types/secp256k1": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", @@ -2011,6 +1761,27 @@ "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true }, + "node_modules/abitype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.0.tgz", + "integrity": "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -2073,15 +1844,15 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -2155,12 +1926,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/antlr4ts": { - "version": "0.5.0-alpha.4", - "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2204,21 +1969,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2284,12 +2034,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "dev": true - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -2376,6 +2120,12 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, + "node_modules/brotli-wasm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-2.0.1.tgz", + "integrity": "sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww==", + "dev": true + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -2437,25 +2187,6 @@ "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -2468,12 +2199,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, "node_modules/cbor": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", @@ -2505,15 +2230,15 @@ } }, "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { @@ -2615,62 +2340,18 @@ } }, "node_modules/cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^2.1.1" + "string-width": "^4.2.0" }, "engines": { - "node": ">=6" + "node": "10.* || >= 12.*" }, "optionalDependencies": { - "colors": "^1.1.2" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" + "@colors/colors": "1.5.0" } }, "node_modules/cliui": { @@ -2702,15 +2383,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2860,51 +2532,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -2914,12 +2541,6 @@ "node": ">= 0.6" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -2953,6 +2574,35 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", @@ -3024,23 +2674,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3104,6 +2737,12 @@ "url": "https://dotenvx.com" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -3153,27 +2792,6 @@ "node": ">=6" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -3222,164 +2840,30 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eth-gas-reporter": { - "version": "0.2.27", - "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", - "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", - "dev": true, - "dependencies": { - "@solidity-parser/parser": "^0.14.0", - "axios": "^1.5.1", - "cli-table3": "^0.5.0", - "colors": "1.4.0", - "ethereum-cryptography": "^1.0.3", - "ethers": "^5.7.2", - "fs-readdir-recursive": "^1.1.0", - "lodash": "^4.17.14", - "markdown-table": "^1.1.3", - "mocha": "^10.2.0", - "req-cwd": "^2.0.0", - "sha1": "^1.1.1", - "sync-request": "^6.0.0" - }, - "peerDependencies": { - "@codechecks/client": "^0.1.0" - }, - "peerDependenciesMeta": { - "@codechecks/client": { - "optional": true - } - } - }, - "node_modules/eth-gas-reporter/node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/eth-gas-reporter/node_modules/@scure/bip32": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", - "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/eth-gas-reporter/node_modules/@scure/bip39": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", - "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", - "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", "dev": true, - "dependencies": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eth-gas-reporter/node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" + "engines": { + "node": ">=0.10.0" } }, "node_modules/ethereum-bloom-filters": { @@ -3483,9 +2967,9 @@ } }, "node_modules/ethers": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.0.tgz", - "integrity": "sha512-zL5NlOTjML239gIvtVJuaSk0N9GQLi1Hom3ZWUszE5lDTQE/IVB62mrPkQ2W1bGcZwVGSLaetQbWNQSvI4rGDQ==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", "dev": true, "funding": [ { @@ -3662,6 +3146,22 @@ } } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -3696,12 +3196,6 @@ "node": ">=12" } }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3722,15 +3216,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3749,34 +3234,6 @@ "node": "*" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ghost-testrpc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", @@ -3938,18 +3395,6 @@ "node": ">=8" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4053,17 +3498,138 @@ } }, "node_modules/hardhat-gas-reporter": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", - "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.1.1.tgz", + "integrity": "sha512-h3PAU7iDiPNqPA4i7GcKgnIZsN3UIdALM4gdJNoTtfmF9PGVne5nFZRNX1N7Y2Mx+znbBqeV1mS7o/i6T7NkTg==", "dev": true, "dependencies": { - "array-uniq": "1.0.3", - "eth-gas-reporter": "^0.2.25", - "sha1": "^1.1.1" + "@ethersproject/abi": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/units": "^5.7.0", + "@solidity-parser/parser": "^0.18.0", + "axios": "^1.6.7", + "brotli-wasm": "^2.0.1", + "chalk": "4.1.2", + "cli-table3": "^0.6.3", + "ethereum-cryptography": "^2.1.3", + "glob": "^10.3.10", + "jsonschema": "^1.4.1", + "lodash": "^4.17.21", + "markdown-table": "2.0.0", + "sha1": "^1.1.1", + "viem": "2.7.14" }, "peerDependencies": { - "hardhat": "^2.0.2" + "hardhat": "^2.16.0" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "dev": true, + "dependencies": { + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/glob": { + "version": "10.3.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", + "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hardhat-gas-reporter/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/hardhat/node_modules/@noble/hashes": { @@ -4256,42 +3822,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -4316,18 +3846,6 @@ "minimalistic-assert": "^1.0.1" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -4354,21 +3872,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/http-basic": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", - "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", - "dev": true, - "dependencies": { - "caseless": "^0.12.0", - "concat-stream": "^1.6.2", - "http-response-object": "^3.0.1", - "parse-cache-control": "^1.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -4385,21 +3888,6 @@ "node": ">= 0.8" } }, - "node_modules/http-response-object": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", - "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", - "dev": true, - "dependencies": { - "@types/node": "^10.0.3" - } - }, - "node_modules/http-response-object/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "dev": true - }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -4445,9 +3933,9 @@ } }, "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "node_modules/indent-string": { @@ -4581,18 +4069,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isows": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", + "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -4774,15 +4289,12 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/make-error": { @@ -4792,10 +4304,17 @@ "dev": true }, "node_modules/markdown-table": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", - "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, "node_modules/md5.js": { "version": "1.3.5", @@ -4899,6 +4418,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -5169,9 +4697,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -5229,24 +4757,6 @@ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", "dev": true }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/obliterator": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", @@ -5342,12 +4852,6 @@ "node": ">=4" } }, - "node_modules/parse-cache-control": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", - "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", - "dev": true - }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -5366,12 +4870,37 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5451,21 +4980,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dev": true, - "dependencies": { - "asap": "~2.0.6" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5494,21 +5008,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5612,28 +5111,13 @@ "node": ">=6" } }, - "node_modules/req-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", - "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", - "dev": true, - "dependencies": { - "req-from": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/req-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", - "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=0.10" } }, "node_modules/require-directory": { @@ -5666,15 +5150,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5910,23 +5385,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -5965,6 +5423,27 @@ "node": "*" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", @@ -5982,22 +5461,16 @@ "node": ">=4" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/sisteransi": { @@ -6119,12 +5592,6 @@ "hardhat": "^2.11.0" } }, - "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", - "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", - "dev": true - }, "node_modules/solidity-coverage/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -6208,13 +5675,10 @@ } }, "node_modules/solidity-coverage/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6349,6 +5813,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6361,6 +5840,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -6398,29 +5890,6 @@ "node": ">=8" } }, - "node_modules/sync-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", - "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", - "dev": true, - "dependencies": { - "http-response-object": "^3.0.1", - "sync-rpc": "^1.2.1", - "then-request": "^6.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/sync-rpc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", - "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", - "dev": true, - "dependencies": { - "get-port": "^3.1.0" - } - }, "node_modules/table": { "version": "6.8.2", "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", @@ -6470,48 +5939,6 @@ "node": ">=8" } }, - "node_modules/then-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", - "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", - "dev": true, - "dependencies": { - "@types/concat-stream": "^1.6.0", - "@types/form-data": "0.0.33", - "@types/node": "^8.0.0", - "@types/qs": "^6.2.31", - "caseless": "~0.12.0", - "concat-stream": "^1.6.0", - "form-data": "^2.2.0", - "http-basic": "^8.1.1", - "http-response-object": "^3.0.1", - "promise": "^8.0.0", - "qs": "^6.4.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/then-request/node_modules/@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", - "dev": true - }, - "node_modules/then-request/node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", @@ -6775,12 +6202,6 @@ "node": ">= 4.0.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true - }, "node_modules/typescript": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", @@ -6888,6 +6309,63 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/viem": { + "version": "2.7.14", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.14.tgz", + "integrity": "sha512-5b1KB1gXli02GOQHZIUsRluNUwssl2t4hqdFAzyWPwJ744N83jAOBOjOkrGz7K3qMIv9b0GQt3DoZIErSQTPkQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@scure/bip32": "1.3.2", + "@scure/bip39": "1.2.1", + "abitype": "1.0.0", + "isows": "1.0.3", + "ws": "8.13.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==", + "dev": true + }, + "node_modules/viem/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/web3-utils": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", @@ -6931,6 +6409,33 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/web3-utils/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/web3-utils/node_modules/ethereum-cryptography": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", @@ -7027,6 +6532,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7063,12 +6586,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 257c7f9..62b0821 100644 --- a/package.json +++ b/package.json @@ -5,27 +5,26 @@ "coverage": "npx hardhat coverage" }, "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@nomicfoundation/hardhat-ignition": "^0.15.0", - "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-ignition": "^0.15.3", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.3", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomicfoundation/hardhat-toolbox": "^5.0.0", - "@nomicfoundation/hardhat-verify": "^2.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.6", "@openzeppelin/contracts": "^5.0.2", - "@typechain/ethers-v6": "^0.5.0", - "@typechain/hardhat": "^9.0.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", "@types/node": "^20.12.7", "chai": "^4.2.0", "dotenv": "^16.4.5", - "ethers": "^6.4.0", - "hardhat": "^2.22.2", - "hardhat-gas-reporter": "^1.0.8", - "solidity-coverage": "^0.8.0", + "ethers": "^6.12.1", + "hardhat": "^2.22.3", + "hardhat-gas-reporter": "^2.1.1", + "solidity-coverage": "^0.8.12", "ts-node": ">=8.0.0", - "typechain": "^8.3.0", - "typescript": ">=4.5.0" + "typechain": "^8.3.2", + "typescript": "^5.4.5" } } diff --git a/test/ERC20LockUpStakingPool.test.ts b/test/ERC20LockUpStakingPool.test.ts index 3740a44..3d01c5e 100644 --- a/test/ERC20LockUpStakingPool.test.ts +++ b/test/ERC20LockUpStakingPool.test.ts @@ -3,10 +3,9 @@ import { ethers } from "hardhat"; import { loadFixture, time, -} from "@nomicfoundation/hardhat-toolbox/network-helpers"; -import { latest } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; +} from "@nomicfoundation/hardhat-network-helpers"; import { - ERC20LockUpStakingPool, + ERC20LockupPool, ERC20MockToken, ERC20LockUpStakingFactory, ERC20LockUpStakingFactory__factory, @@ -41,7 +40,7 @@ describe("Contract Deployment", async function () { let ayo: HardhatEthersSigner; let alina: HardhatEthersSigner; let vartan: HardhatEthersSigner; - let poolContract: ERC20LockUpStakingPool; + let poolContract: ERC20LockupPool; before(async () => { StakingFactory = await ethers.getContractFactory( @@ -79,59 +78,6 @@ describe("Contract Deployment", async function () { }); describe("ERC20LockUpStakingPool Deployment", async function () { - // it("Deployment fail: start Time less than block time (InvalidStartTime)", async function () { - // await expect( - // ercStakingPoolFactory.deploy( - // await mockStakeToken.getAddress(), - // await mockRewardToken.getAddress(), - // rewardTokenPerSecond, - // poolStartTime, - // poolEndTime + 24 * 60 * 60 * 30, - // unstakeLockup + 24 * 60 * 60, - // claimLockup + 10 * 60 - // ) - // ).to.be.reverted; - // }); - - // it("Deployment fail: end Time less than start time (InvalidStakingPeriod)", async function () { - // await expect( - // ercStakingPoolFactory.deploy( - // await mockStakeToken.getAddress(), - // await mockRewardToken.getAddress(), - // rewardTokenPerSecond, - // poolStartTime + 100, - // poolEndTime, - // unstakeLockup + 100 + 100, - // claimLockup + 100 + 100 - // ) - // ).to.be.reverted; - // }); - // it("Deployment fail: end Time less than unstake lockup time (InvalidLockupTime)", async function () { - // await expect( - // ercStakingPoolFactory.deploy( - // await mockStakeToken.getAddress(), - // await mockRewardToken.getAddress(), - // rewardTokenPerSecond, - // poolStartTime + 100, - // poolEndTime + +100 + 10, - // unstakeLockup + 100 + 10 + 10, - // claimLockup + 100 + 10 - // ) - // ).to.be.reverted; - // }); - // it("Deployment fail: end Time less than claim lockup time (InvalidLockupTime)", async function () { - // await expect( - // ercStakingPoolFactory.deploy( - // await mockStakeToken.getAddress(), - // await mockRewardToken.getAddress(), - // rewardTokenPerSecond, - // poolStartTime + 100, - // poolEndTime + +100 + 10 + 10, - // unstakeLockup + 100 + 10, - // claimLockup + 100 + 10 + 10 + 10 - // ) - // ).to.be.reverted; - // }); it("Request creation failed: invalid staking token address", async function () { poolStartTime += 100; poolEndTime = poolStartTime + 120; @@ -140,11 +86,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: ethers.ZeroAddress, rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime, poolEndTime: poolEndTime, unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup + claimLockupTime: claimLockup, + rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); @@ -160,11 +106,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: ethers.ZeroAddress, - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime, poolEndTime: poolEndTime, unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup + claimLockupTime: claimLockup, + rewardPerSecond: rewardTokenPerSecond } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidTokenAddress"); @@ -180,11 +126,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: ethers.toBigInt(0), poolStartTime: poolStartTime, poolEndTime: poolEndTime, unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup + claimLockupTime: claimLockup, + rewardPerSecond: ethers.toBigInt(0) } let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; await expect(ercStakingPoolFactory.connect(ayo).requestDeployment(data)).to.be.revertedWithCustomError(ercStakingPoolFactory, "InvalidRewardRate"); @@ -200,11 +146,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime, poolEndTime: poolEndTime, unstakeLockupTime: unstakeLockup, - claimLockupTime: claimLockup + claimLockupTime: claimLockup, + rewardPerSecond: rewardTokenPerSecond } let length = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); @@ -252,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( - "ERC20LockUpStakingPool", + "ERC20LockupPool", lastPool ); }); @@ -268,11 +214,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime - 10000, poolEndTime: poolStartTime + 120, unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolStartTime + 10 + claimLockupTime: poolStartTime + 10, + rewardPerSecond: rewardTokenPerSecond, }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); @@ -290,11 +236,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime + 10000, poolEndTime: poolStartTime + 120, unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolStartTime + 10 + claimLockupTime: poolStartTime + 10, + rewardPerSecond: rewardTokenPerSecond }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); @@ -313,11 +259,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime + 100, poolEndTime: poolStartTime + 120, unstakeLockupTime: poolEndTime + 130, - claimLockupTime: poolStartTime + 10 + claimLockupTime: poolStartTime + 10, + rewardPerSecond: rewardTokenPerSecond, }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); @@ -334,11 +280,11 @@ describe("Contract Deployment", async function () { const data = { stakeToken: await mockStakeToken.getAddress(), rewardToken: await mockRewardToken.getAddress(), - rewardPerSecond: rewardTokenPerSecond, poolStartTime: poolStartTime + 100, poolEndTime: poolStartTime + 120, unstakeLockupTime: poolStartTime + 10, - claimLockupTime: poolEndTime + 10 + claimLockupTime: poolEndTime + 10, + rewardPerSecond: rewardTokenPerSecond }; let lengthBefore = (await ercStakingPoolFactory.getRequests()).length; let values = Object.values(data); @@ -469,8 +415,8 @@ describe("Contract Deployment", async function () { let accRewardPerShare = (await poolContract.pool()).accRewardPerShare; let stakingPool = await poolContract.pool() - if (currentTimestamp > stakingPool.lastRewardTimestamp && stakingPool.totalStaked !== BigInt(0)) { - const elapsedPeriod = BigInt(currentTimestamp) - stakingPool.lastRewardTimestamp; + if (currentTimestamp > stakingPool.lastUpdateTimestamp && stakingPool.totalStaked !== BigInt(0)) { + const elapsedPeriod = BigInt(currentTimestamp) - stakingPool.lastUpdateTimestamp; const totalNewReward = stakingPool.rewardTokenPerSecond * elapsedPeriod; accRewardPerShare += (totalNewReward * PRECISION_FACTOR) / stakingPool.totalStaked; } @@ -513,7 +459,7 @@ describe("Contract Deployment", async function () { rewardTokenPerSecond: bigint; totalStaked: bigint; totalClaimed: bigint; - lastRewardTimestamp: bigint; + lastUpdateTimestamp: bigint; accRewardPerShare: bigint; isActive: boolean; adminWallet: string;