diff --git a/.gitignore b/.gitignore index e0190bd0..c37ffe42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules - +bin # dependencies, yarn, etc # yarn / eslint .yarn/* diff --git a/packages/foundry/.env.example b/packages/foundry/.env.example deleted file mode 100644 index 05cf662f..00000000 --- a/packages/foundry/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -DEPLOYER_PRIVATE_KEY= -SEPOLIA_RPC_URL= \ No newline at end of file diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaign.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaign.sol new file mode 100644 index 00000000..145b377c --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaign.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; + +import { IDiscountCampaign } from "./Interfaces/IDiscountCampaign.sol"; +import { ISwapDiscountHook } from "./Interfaces/ISwapDiscountHook.sol"; +import { TransferHelper } from "./libraries/TransferHelper.sol"; + +/** + * @title DiscountCampaign + * @notice This contract is used to manage discount campaigns, allowing users to earn rewards through swaps. + * @dev Implements IDiscountCampaign interface. Includes reward distribution, user discount data updates, and campaign management. + */ +contract DiscountCampaign is IDiscountCampaign, Ownable, ReentrancyGuard { + /// @notice Maps token IDs to user-specific swap data. + mapping(uint256 => UserSwapData) public override userDiscountMapping; + + /// @notice Holds the details of the current discount campaign. + CampaignDetails public campaignDetails; + + /// @notice Total amount of reward tokens distributed so far. + uint256 public tokenRewardDistributed; + + /// @notice Address of the discount campaign factory that can manage campaign updates. + address public discountCampaignFactory; + + /// @notice Address of the swap discount hook for tracking user swaps. + ISwapDiscountHook private _swapHook; + + /// @notice Maximum buyable reward amount during the campaign. + uint256 private _maxBuy; + + /// @notice Maximum discount rate available in the campaign. + uint256 private _maxDiscountRate; + + /** + * @notice Initializes the discount campaign contract with the provided details. + * @dev Sets the campaign details, owner, and swap hook address during contract deployment. + * @param _campaignDetails A struct containing reward amount, expiration time, cooldown period, discount rate, and reward token. + * @param _owner The owner address of the discount campaign contract. + * @param _hook The address of the swap hook for tracking user discounts. + * @param _discountCampaignFactory The address of the discount campaign factory. + */ + constructor( + CampaignDetails memory _campaignDetails, + address _owner, + address _hook, + address _discountCampaignFactory + ) Ownable(_owner) { + campaignDetails = _campaignDetails; + _swapHook = ISwapDiscountHook(_hook); + _maxBuy = _campaignDetails.rewardAmount; + _maxDiscountRate = _campaignDetails.discountRate; + discountCampaignFactory = _discountCampaignFactory; + } + + /** + * @notice Modifier to restrict access to the factory contract. + * @dev Reverts with `NOT_AUTHORIZED` if the caller is not the factory. + */ + modifier onlyFactory() { + if (msg.sender != discountCampaignFactory) { + revert NOT_AUTHORIZED(); + } + _; + } + + /** + * @notice Modifier to check and authorize a token ID before processing claims. + * @dev Ensures the token is valid, the campaign has not expired, and the reward has not been claimed. + * @param tokenID The ID of the token to be validated. + */ + modifier checkAndAuthorizeTokenId(uint256 tokenID) { + UserSwapData memory userSwapData = userDiscountMapping[tokenID]; + + // Ensure the campaign address matches the current contract address + if (userSwapData.campaignAddress != address(this)) { + revert InvalidTokenID(); + } + + // Ensure the campaign ID matches the current campaign ID + if (userSwapData.campaignID != campaignDetails.campaignID) { + revert CampaignExpired(); + } + + // Check if the swap happened before the campaign expiration + if (block.timestamp > campaignDetails.expirationTime) { + revert DiscountExpired(); + } + + // Ensure the reward hasn't already been claimed + if (userSwapData.hasClaimed) { + revert RewardAlreadyClaimed(); + } + + // Ensure the cooldown period has passed + if (userSwapData.timeOfSwap + campaignDetails.coolDownPeriod > block.timestamp) { + revert CoolDownPeriodNotPassed(); + } + _; + } + + /** + * @notice Updates the campaign details. + * @dev Can only be called by the factory contract. This will replace the existing campaign parameters. + * @param _newCampaignDetails A struct containing updated reward amount, expiration time, cooldown period, discount rate, and reward token. + */ + function updateCampaignDetails(CampaignDetails calldata _newCampaignDetails) external onlyFactory { + campaignDetails = _newCampaignDetails; + emit CampaignDetailsUpdated(_newCampaignDetails); + } + + /** + * @notice Allows the SwapDiscountHook to update the user discount mapping after a swap. + * @dev Can only be called by the SwapDiscountHook contract. + * @param campaignID The ID of the campaign associated with the user. + * @param tokenId The token ID for which the discount data is being updated. + * @param user The address of the user receiving the discount. + * @param swappedAmount The amount that was swapped. + * @param timeOfSwap The timestamp of when the swap occurred. + */ + function updateUserDiscountMapping( + bytes32 campaignID, + uint256 tokenId, + address user, + uint256 swappedAmount, + uint256 timeOfSwap + ) external override { + require(msg.sender == address(_swapHook), "Unauthorized"); + + userDiscountMapping[tokenId] = UserSwapData({ + campaignID: campaignID, + userAddress: user, + campaignAddress: address(this), + swappedAmount: swappedAmount, + timeOfSwap: timeOfSwap, + hasClaimed: false + }); + } + + /** + * @notice Claims rewards for a specific token ID. + * @dev Transfers the reward to the user associated with the token and marks the token as claimed. + * Reverts if the reward amount is zero or if the total rewards have been distributed. + * @param tokenID The ID of the token for which the claim is made. + */ + function claim(uint256 tokenID) public checkAndAuthorizeTokenId(tokenID) nonReentrant { + UserSwapData memory userSwapData = userDiscountMapping[tokenID]; + uint256 reward = _getClaimableRewards(userSwapData); + + if (reward == 0) { + revert RewardAmountExpired(); + } + + tokenRewardDistributed += reward; + _maxBuy -= reward; + userDiscountMapping[tokenID].hasClaimed = true; + TransferHelper.safeTransfer(campaignDetails.rewardToken, userSwapData.userAddress, reward); + _updateDiscount(); + } + + /** + * @notice Internal function to calculate the claimable reward for a given token ID. + * @dev The reward is calculated based on the swapped amount and discount rate. + * @param tokenID The ID of the token for which to calculate the reward. + * @return claimableReward The amount of reward that can be claimed. + */ + function getClaimableReward(uint256 tokenID) external view returns (uint256 claimableReward) { + UserSwapData memory userSwapData = userDiscountMapping[tokenID]; + return _getClaimableRewards(userSwapData); + } + + /** + * @notice Overloaded version of _getClaimableRewards that takes UserSwapData as input. + * @param userSwapData The UserSwapData struct containing the necessary information for calculating rewards. + * @return claimableReward The amount of reward that can be claimed. + */ + function _getClaimableRewards(UserSwapData memory userSwapData) private view returns (uint256 claimableReward) { + uint256 swappedAmount = userSwapData.swappedAmount; + + // Calculate claimable reward based on the swapped amount and discount rate + if (swappedAmount <= _maxBuy) { + claimableReward = (swappedAmount * campaignDetails.discountRate) / 100e18; + } else { + claimableReward = (_maxBuy * campaignDetails.discountRate) / 100e18; + } + } + + /** + * @notice Updates the discount rate based on the distributed rewards. + * @dev The discount rate decreases proportionally as more rewards are distributed. + */ + function _updateDiscount() private { + campaignDetails.discountRate = + (_maxDiscountRate * (campaignDetails.rewardAmount - tokenRewardDistributed)) / + campaignDetails.rewardAmount; + } + + /** + * @notice Recovers any ERC20 tokens that are mistakenly sent to the contract. + * @dev Can only be called by the contract owner. + * @param tokenAddress Address of the ERC20 token to recover. + * @param tokenAmount Amount of tokens to recover. + */ + function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner { + TransferHelper.safeTransfer(tokenAddress, owner(), tokenAmount); + } +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol new file mode 100644 index 00000000..a16994db --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import { IPoolInfo } from "@balancer-labs/v3-interfaces/contracts/pool-utils/IPoolInfo.sol"; +import { TransferHelper } from "./libraries/TransferHelper.sol"; +import { IDiscountCampaignFactory } from "./Interfaces/IDiscountCampaignFactory.sol"; +import { ISwapDiscountHook } from "./Interfaces/ISwapDiscountHook.sol"; +import { IDiscountCampaign } from "./Interfaces/IDiscountCampaign.sol"; +import { DiscountCampaign } from "./DiscountCampaign.sol"; + +/** + * @title DiscountCampaignFactory + * @notice Factory contract for creating and managing discount campaigns. + * @dev Allows for the creation and updating of discount campaigns associated with liquidity pools. + */ +contract DiscountCampaignFactory is ReentrancyGuard, IDiscountCampaignFactory, Ownable { + /// @notice Mapping to store discount campaigns associated with pools. + mapping(address => CampaignData) public override discountCampaigns; + + /// @notice Address of the SwapDiscountHook contract used to track user swaps. + address public swapDiscountHook; + + /** + * @notice Initializes the DiscountCampaignFactory contract. + * @dev Sets the owner of the contract during deployment. + */ + constructor() Ownable(msg.sender) {} + + /** + * @notice Modifier to allow only the campaign owner to execute the function. + * @param pool The address of the liquidity pool for which the campaign is managed. + */ + modifier onlyCampaignOwner(address pool) { + if (msg.sender != discountCampaigns[pool].owner) revert NOT_AUTHORIZED(); + _; + } + + /** + * @notice Modifier to ensure the campaign for a given pool exists. + * @param pool The address of the liquidity pool. + */ + modifier campaignExists(address pool) { + address campaignAddress = discountCampaigns[pool].campaignAddress; + if (campaignAddress == address(0)) revert PoolCampaignDoesnotExist(); + (, , , , , , address poolAddress, ) = IDiscountCampaign(campaignAddress).campaignDetails(); + if (pool != poolAddress) { + revert PoolAddressCannotBeChanged(); + } + _; + } + + /** + * @notice Modifier to ensure that the campaign for a given pool has not expired. + * @param pool The address of the liquidity pool. + */ + modifier campaignNotExpired(address pool) { + (, , uint256 expirationTime, , , , , ) = IDiscountCampaign(discountCampaigns[pool].campaignAddress) + .campaignDetails(); + if (expirationTime > block.timestamp) { + revert PoolCampaignHasnotExpired(); + } + _; + } + + /** + * @notice Modifier to verify that a valid hook address is provided. + * @param _hookAddress The address of the hook contract. + */ + modifier verifyHook(address _hookAddress) { + if (_hookAddress == address(0)) { + revert InvalidHookAddress(); + } + _; + } + + /** + * @notice Modifier to ensure that the swap discount hook is set. + */ + modifier isHookUpdated() { + if (swapDiscountHook == address(0)) { + revert InvalidHookAddress(); + } + _; + } + + /** + * @notice Sets the address of the SwapDiscountHook contract. + * @dev Can only be called by the contract owner. + * @param _hookAddress The address of the SwapDiscountHook contract. + */ + function setSwapDiscountHook(address _hookAddress) external onlyOwner verifyHook(_hookAddress) { + swapDiscountHook = _hookAddress; + } + + /** + * @notice Create a new discount campaign for a specific liquidity pool. + * @dev Creates a new `DiscountCampaign` contract and stores its details. + * Reverts if a campaign for the specified pool already exists or if the reward token is invalid. + * @param params The parameters for creating the discount campaign. + * @return The address of the newly created `DiscountCampaign` contract. + */ + function createCampaign(CampaignParams memory params) external nonReentrant isHookUpdated returns (address) { + CampaignData storage campaignData = discountCampaigns[params.pool]; + + if (campaignData.campaignAddress != address(0)) revert PoolCampaignAlreadyExist(); + validateTokenAndPool(params.pool, params.rewardToken); + + // Prepare campaign details + IDiscountCampaign.CampaignDetails memory campaignDetails = _prepareCampaignDetails(params); + + // Deploy the DiscountCampaign contract with the struct + DiscountCampaign discountCampaign = new DiscountCampaign( + campaignDetails, + params.owner, + swapDiscountHook, + address(this) + ); + + // Store campaign details + campaignData.campaignAddress = address(discountCampaign); + campaignData.owner = msg.sender; + + TransferHelper.safeTransferFrom(params.rewardToken, msg.sender, address(discountCampaign), params.rewardAmount); + + emit CampaignCreated( + address(discountCampaign), + params.rewardAmount, + params.expirationTime, + params.coolDownPeriod, + params.discountAmount, + params.pool, + params.owner, + params.rewardToken + ); + + return address(discountCampaign); + } + + /** + * @notice Update the campaign details for a specific pool. + * @dev Updates the campaign details if the campaign has expired and belongs to the caller. + * @param params The parameters for updating the discount campaign. + */ + function updateCampaign( + CampaignParams memory params + ) external campaignExists(params.pool) onlyCampaignOwner(params.pool) campaignNotExpired(params.pool) { + validateTokenAndPool(params.pool, params.rewardToken); + + address campaignAddress = discountCampaigns[params.pool].campaignAddress; + + IDiscountCampaign.CampaignDetails memory campaignDetails = _prepareCampaignDetails(params); + + IDiscountCampaign(campaignAddress).updateCampaignDetails(campaignDetails); + + uint256 rewardAmount = params.rewardAmount; + uint256 expirationTime = params.expirationTime; + uint256 coolDownPeriod = params.coolDownPeriod; + uint256 discountAmount = params.discountAmount; + address pool = params.pool; + address owner = params.owner; + address rewardToken = params.rewardToken; + + TransferHelper.safeTransferFrom(rewardToken, msg.sender, campaignAddress, rewardAmount); + + emit CampaignUpdated( + campaignAddress, + rewardAmount, + expirationTime, + coolDownPeriod, + discountAmount, + pool, + owner, + rewardToken + ); + } + + /** + * @notice Prepares the campaign details for creating or updating a campaign. + * @dev Creates a CampaignDetails struct with the specified parameters. + * @param params The parameters for preparing the campaign details. + * @return A `CampaignDetails` struct containing the prepared campaign details. + */ + function _prepareCampaignDetails( + CampaignParams memory params + ) internal view returns (IDiscountCampaign.CampaignDetails memory) { + return + IDiscountCampaign.CampaignDetails({ + campaignID: keccak256(abi.encode(block.timestamp, params.expirationTime)), + rewardAmount: params.rewardAmount, + expirationTime: block.timestamp + params.expirationTime, + coolDownPeriod: params.coolDownPeriod, + discountRate: params.discountAmount, + rewardToken: params.rewardToken, + poolAddress: params.pool, + owner: params.owner + }); + } + + /** + * @notice Recovers ERC20 tokens mistakenly sent to the contract or unwanted and no longer needed. + * @dev Can only be called by the contract owner. + * @param token Address of the ERC20 token to recover. + * @param amount Amount of tokens to recover. + */ + function recoverERC20(address token, uint256 amount) external onlyOwner { + TransferHelper.safeTransfer(token, owner(), amount); + } + + /** + * @notice Checks if the given reward token is valid for the specified pool. + * @dev Iterates over the tokens in the pool to check if the reward token matches any of them. + * @param pool The address of the liquidity pool. + * @param rewardToken The address of the reward token to validate. + * @return True if the reward token is valid for the pool, false otherwise. + */ + function _checkToken(address pool, address rewardToken) internal view returns (bool) { + IERC20[] memory tokens = IPoolInfo(pool).getTokens(); + uint256 tokensLength = tokens.length; + + for (uint256 i = 0; i < tokensLength; i++) { + if (rewardToken == address(tokens[i])) { + return true; + } + } + return false; + } + + /** + * @notice Validates the token and pool for a campaign. + * @dev Ensures that the reward token is one of the pool tokens. + * @param pool The address of the liquidity pool. + * @param rewardToken The address of the reward token to validate. + */ + function validateTokenAndPool(address pool, address rewardToken) internal view { + if (!_checkToken(pool, rewardToken)) revert InvalidRewardToken(); + } +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaign.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaign.sol new file mode 100644 index 00000000..a519c88b --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaign.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IDiscountCampaign { + /// @notice Reverts when the provided token ID does not match the campaign. + error InvalidTokenID(); + + /// @notice Reverts when attempting to claim a discount for a token after the campaign has expired. + error DiscountExpired(); + + /// @notice Reverts when attempting to claim a reward for a token that has already been claimed. + error RewardAlreadyClaimed(); + + /// @notice Reverts when the total reward amount has already been distributed, and no further rewards can be claimed. + error RewardAmountExpired(); + + /// @notice Reverts when attempting to claim a reward without waiting for the required cooldown period. + error CoolDownPeriodNotPassed(); + + /// @notice Reverts when attempting to interact with an expired campaign. + error CampaignExpired(); + + /// @notice Reverts when the caller is not authorized to perform the action. + error NOT_AUTHORIZED(); + + /** + * @notice Contains parameters related to a discount campaign. + * @param rewardAmount Total rewards available for the campaign. + * @param expirationTime Campaign expiration timestamp. + * @param coolDownPeriod Time a user must wait between swaps to be eligible for rewards. + * @param discountRate Percentage rate for discounts based on swaps. + * @param rewardToken Token used to reward users. + * @param poolAddress Address of the associated liquidity pool. + * @param owner Owner of the campaign. + */ + struct CampaignDetails { + bytes32 campaignID; + uint256 rewardAmount; + uint256 expirationTime; + uint256 coolDownPeriod; + uint256 discountRate; + address rewardToken; + address poolAddress; + address owner; + } + + /** + * @notice Contains user-specific data related to a swap. + * @param userAddress Address of the participating user. + * @param campaignAddress Address of the discount campaign contract. + * @param swappedAmount Amount swapped during the campaign. + * @param timeOfSwap Timestamp of the swap. + * @param hasClaimed Indicates if the reward for this swap has been claimed. + */ + struct UserSwapData { + bytes32 campaignID; + address userAddress; + address campaignAddress; + uint256 swappedAmount; + uint256 timeOfSwap; + bool hasClaimed; + } + + /** + * @notice Updates user discount mapping for a given token ID. + * @dev Can only be called by authorized contracts. + * @param tokenId ID of the token being updated. + * @param user Address of the user whose discount is being recorded. + * @param swappedAmount Amount of tokens swapped. + * @param timeOfSwap Timestamp of the swap. + */ + function updateUserDiscountMapping( + bytes32 campaignID, + uint256 tokenId, + address user, + uint256 swappedAmount, + uint256 timeOfSwap + ) external; + + /** + * @notice Updates campaign details. + * @param _newCampaignDetails Struct containing the new campaign details. + */ + function updateCampaignDetails(CampaignDetails calldata _newCampaignDetails) external; + + /** + * @notice Retrieves swap data for a specific token ID. + * @param tokenId ID of the token. + * @return campaignID Campaign ID related to the token. + * @return userAddress User who made the swap. + * @return campaignAddress Campaign address. + * @return swappedAmount Amount swapped. + * @return timeOfSwap Timestamp of the swap. + * @return hasClaimed Indicates if the reward was claimed. + */ + function userDiscountMapping( + uint256 tokenId + ) + external + view + returns ( + bytes32 campaignID, + address userAddress, + address campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ); + + /** + * @notice Retrieves details of the discount campaign. + * @return campaignID Campaign ID. + * @return rewardAmount Total rewards available. + * @return expirationTime Expiration time of the campaign. + * @return coolDownPeriod Cooldown time between swaps for eligibility. + * @return discountRate Discount rate applied to swaps. + * @return rewardToken Reward token address. + * @return poolAddress Address of the liquidity pool. + * @return owner Campaign owner. + */ + function campaignDetails() + external + view + returns ( + bytes32 campaignID, + uint256 rewardAmount, + uint256 expirationTime, + uint256 coolDownPeriod, + uint256 discountRate, + address rewardToken, + address poolAddress, + address owner + ); + + /** + * @notice Emitted when the campaign details are updated. + * @param _newCampaignDetails Updated campaign details. + */ + event CampaignDetailsUpdated(CampaignDetails _newCampaignDetails); +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol new file mode 100644 index 00000000..596e63a1 --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/** + * @title IDiscountCampaignFactory + * @notice Interface for the Discount Campaign Factory, responsible for creating and managing discount campaigns. + */ +interface IDiscountCampaignFactory { + /// @notice Reverts if a campaign for the specified pool already exists. + error PoolCampaignAlreadyExist(); + + /// @notice Reverts if a campaign for the specified pool does not exist. + error PoolCampaignDoesnotExist(); + + /// @notice Reverts if attempting to update a campaign that has not expired. + error PoolCampaignHasnotExpired(); + + /// @notice Reverts if the provided reward token is invalid for the campaign. + error InvalidRewardToken(); + + /// @notice Reverts if the provided hook address is invalid. + error InvalidHookAddress(); + + /// @notice Reverts if the reward amount balance is insufficient for the campaign. + error InsufficientRewardAmountBalance(); + + /// @notice Reverts if the pool address associated with a campaign is changed. + error PoolAddressCannotBeChanged(); + + /// @notice Reverts if the caller is not authorized to perform the action. + error NOT_AUTHORIZED(); + + /** + * @notice Emitted when a campaign is updated with new parameters. + * @param campaign Address of the updated discount campaign. + * @param rewardAmount Updated reward amount for the campaign. + * @param expirationTime Updated expiration time for the campaign. + * @param coolDownPeriod Updated cooldown period for the campaign. + * @param discountAmount Updated discount rate for the campaign. + * @param pool Address of the associated liquidity pool. + * @param owner Address of the campaign owner. + * @param rewardToken Address of the reward token used for the campaign. + */ + event CampaignUpdated( + address indexed campaign, + uint256 rewardAmount, + uint256 expirationTime, + uint256 coolDownPeriod, + uint256 discountAmount, + address pool, + address owner, + address rewardToken + ); + + /** + * @notice Emitted when a new campaign is created. + * @param campaign Address of the newly created discount campaign. + * @param rewardAmount Total reward amount for the campaign. + * @param expirationTime Expiration time for the campaign. + * @param coolDownPeriod Cooldown period for the campaign. + * @param discountAmount Discount rate for the campaign. + * @param pool Address of the associated liquidity pool. + * @param owner Address of the campaign owner. + * @param rewardToken Address of the reward token used for the campaign. + */ + event CampaignCreated( + address indexed campaign, + uint256 rewardAmount, + uint256 expirationTime, + uint256 coolDownPeriod, + uint256 discountAmount, + address pool, + address owner, + address rewardToken + ); + + /** + * @notice Struct representing campaign data. + * @param campaignAddress Address of the discount campaign contract. + * @param owner Address of the campaign owner. + */ + struct CampaignData { + address campaignAddress; + address owner; + } + + /** + * @notice Struct representing the parameters required to create or update a discount campaign. + * @param rewardAmount Total reward amount for the campaign. + * @param expirationTime Campaign expiration timestamp. + * @param coolDownPeriod Cooldown period required between swaps to be eligible for rewards. + * @param discountAmount Discount rate for the campaign. + * @param pool Address of the associated liquidity pool. + * @param owner Address of the campaign owner. + * @param rewardToken Address of the reward token for the campaign. + */ + struct CampaignParams { + uint256 rewardAmount; + uint256 expirationTime; + uint256 coolDownPeriod; + uint256 discountAmount; + address pool; + address owner; + address rewardToken; + } + + /** + * @notice Retrieves the discount campaign data for a specific pool address. + * @param poolAddress The address of the liquidity pool. + * @return campaignAddress The address of the discount campaign contract. + * @return owner The address of the campaign owner. + */ + function discountCampaigns(address poolAddress) external view returns (address campaignAddress, address owner); +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/ISwapDiscountHook.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/ISwapDiscountHook.sol new file mode 100644 index 00000000..79dd073e --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/Interfaces/ISwapDiscountHook.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/** + * @title ISwapDiscountHook + * @notice Interface for the Swap Discount Hook, which manages the granting of discounts during swaps. + */ +interface ISwapDiscountHook { + /// @notice Reverts if the provided campaign address is invalid. + error InvalidCampaignAddress(); +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/README.md b/packages/foundry/contracts/hooks/SwapDiscountHook/README.md new file mode 100644 index 00000000..97e36519 --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/README.md @@ -0,0 +1,98 @@ +# Swap Bond Hook + +![Architecture](images/Architecture.png) + +Swap bond hook is a swap discount utility based hook that equips the pools on balancer v3 to create native incentive campaigns on after swap callback. Projects launching their pools can provide incentives to people buying their token through an incentive campaign in which the token buyers will get a bond nft that represents their buy value. The buyers can redeem reward tokens from the discount campaign based on the value of the token bought. + +This contraption will help project attract volume without relying on the 3rd party incentive products and within the safe boundaries of Balancer v3. + +# Pool Life Cycle Points + +This hook primarily uses onRegister and onAfterSwap callback: + +- onRegister: During the onRegister callback, the hook gets connected with a pool created. +- onAfterSwap : When a user buys the project token, the hook catches the state of the swap in onAfterSwap callback and writes this state against the bond nft id in the discount campaing contract, Finally it mints the bond nft to the user. + +# Example Use Cases + +Suppose Project X is launching XT token on Balancer v3, the project is relatively new and wants to bring volume to the pool. Instead of doing the airdrops or relying on a 3rd party incentive protocol, they integrate the swap bond hook with XT/ETH pool. They create a a campaign of 5000 XT tokens as a reward on 50% initial dynamically decreasing discount on the max trade of 200 XT tokens with a cooldown period of 3 days on reward claim. + +This way, the project can essentially create an incentive opportunity for their users while also bringing volume to the project which can kickstart the tokenomics if done right. + +This hook is not only for the new pools but the projects can utilise this layer at any stage of their project to invite the influx of token holders. + +# Code And Architecture Review + +[![Watch the video](https://img.youtube.com/vi/eEBEvTei99s/maxresdefault.jpg)](https://youtu.be/eEBEvTei99s) + +# Local Test Demo + +[![Watch the video](https://img.youtube.com/vi/y_DcclWcuHk/maxresdefault.jpg)](https://youtu.be/y_DcclWcuHk) + +# Discount Formula + +For the campaign discount we have opt for a dynamically decreasing discount formula. The projects set an initial discount rate with a deterministic quantity of rewards. The discount rate decreases as the users start redeaming the rewards. Whoever avails the discount first gets a better % as compared to the next user. The update formula demostrate it below. + +``` +function _updateDiscount() private { + campaignDetails.discountRate = + (_maxDiscountRate * (campaignDetails.rewardAmount - tokenRewardDistributed)) / + campaignDetails.rewardAmount; + } +``` + +# Calculations of user claimable reward + +``` +function _getClaimableRewards(UserSwapData memory userSwapData) private view returns (uint256 claimableReward) { + uint256 swappedAmount = userSwapData.swappedAmount; + + // Calculate claimable reward based on the swapped amount and discount rate + if (swappedAmount <= _maxBuy) { + claimableReward = (swappedAmount * campaignDetails.discountRate) / 100e18; + } else { + claimableReward = (_maxBuy * campaignDetails.discountRate) / 100e18; + } + } + +``` + +# Intended Project Interaction + +### For Projects + +- Create a pool on balancer v3. +- Register `SwapDiscountHook` with the pool +- Create a campaign by interacting with `DiscountCampaignFactory` contract and transfer the reward tokens to the campaign. + +### For Users + +- Buy Project token on Balancer v3 and get the Bond nft as early as possible to avail the maximum discount. +- Wait for the cooldown period to end. +- Visit the campaign interface and redeem the reward against their bond nft. + +# Challenges + +Since Hook based architecture among the Dexes is a fairly new and untested idea as there is no practical example on mainnet. It's uncertain to know from the user experience perspective if it will be adopted widely and how easy it will be for the people to integrate the applications or hooks built on top of the dexes in their daily crypto usage. + +While building the swap bond hook, we also had some questions related to how much control should a a project must have on the emission of rewards while making a campaign. It both depends on the tokenomics of that individual project and how much control they would like to have on their emissions. + +There is ofcourse a room of improvement as the hook goes into the hands of people and projects. + +# Running Tests + +Run the Foundry tests + +``` +yarn test + +``` + +# DevX Feedback + +The online hookathon is well organized through Dorahacks, Kudos on that. The only feedback we have for the team is that they need to figure out more ways for attracting the hackers to contribute to the eco-system either through long term incentives or inclusion with the team. + +# Authors + +- [@anassohail99](https://www.github.com/anassohail99) +- [@Mubashir-ali-baig](https://github.com/Mubashir-ali-baig) diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol new file mode 100644 index 00000000..9185d470 --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; + +import { IHooks } from "@balancer-labs/v3-interfaces/contracts/vault/IHooks.sol"; +import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; +import { IPoolInfo } from "@balancer-labs/v3-interfaces/contracts/pool-utils/IPoolInfo.sol"; + +import { + LiquidityManagement, + AfterSwapParams, + SwapKind, + TokenConfig, + HookFlags +} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol"; +import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol"; +import { IRouterCommon } from "@balancer-labs/v3-interfaces/contracts/vault/IRouterCommon.sol"; +import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol"; +import { BaseHooks } from "@balancer-labs/v3-vault/contracts/BaseHooks.sol"; +import { VaultGuard } from "@balancer-labs/v3-vault/contracts/VaultGuard.sol"; + +import { ISwapDiscountHook } from "./Interfaces/ISwapDiscountHook.sol"; +import { IDiscountCampaignFactory } from "./Interfaces/IDiscountCampaignFactory.sol"; +import { IDiscountCampaign } from "./Interfaces/IDiscountCampaign.sol"; +import { DiscountCampaign } from "./DiscountCampaign.sol"; + +contract SwapDiscountHook is ISwapDiscountHook, BaseHooks, ERC721, Ownable, VaultGuard, ReentrancyGuard { + using FixedPoint for uint256; + + // Immutable addresses for factory and router + IDiscountCampaignFactory public discountCampaignFactory; + address private immutable allowedFactoryAddress; + address private immutable trustedRouterAddress; + uint256 private _shareTokenId = 1; + + constructor( + IVault vaultInstance, + address _factoryAddress, + address _routerAddress, + address _campaignFactory, + string memory name, + string memory symbol + ) VaultGuard(vaultInstance) ERC721(name, symbol) Ownable(msg.sender) { + allowedFactoryAddress = _factoryAddress; + trustedRouterAddress = _routerAddress; + discountCampaignFactory = IDiscountCampaignFactory(_campaignFactory); + } + + /// @inheritdoc IHooks + function onRegister( + address factory, + address pool, + TokenConfig[] memory, + LiquidityManagement calldata + ) public view override returns (bool) { + return factory == allowedFactoryAddress && IBasePoolFactory(factory).isPoolFromFactory(pool); + } + + /// @inheritdoc IHooks + function getHookFlags() public pure override returns (HookFlags memory hookFlags) { + hookFlags.shouldCallAfterSwap = true; + return hookFlags; + } + + /// @inheritdoc IHooks + function onAfterSwap( + AfterSwapParams calldata params + ) public override onlyVault returns (bool success, uint256 discountedAmount) { + (address campaignAddress, ) = discountCampaignFactory.discountCampaigns(params.pool); + if (campaignAddress != address(0)) { + IDiscountCampaign campaign = IDiscountCampaign(campaignAddress); + (bytes32 campaignID, , , , , address rewardToken, address poolAddress, ) = campaign.campaignDetails(); + if ( + params.kind == SwapKind.EXACT_IN && + address(params.tokenOut) == rewardToken && + poolAddress == params.pool + ) { + mint(params, campaign, campaignID); + } + } + return (true, params.amountCalculatedRaw); + } + + /** + * @notice Apply discount and mint a new token for the user. + * @dev This function mints a token for the user after applying the discount and storing relevant swap data. + * The function uses memory for `UserSwapData` to minimize storage operations until the final write to storage. + * @param params The parameters of the swap after it has been executed. + * - params.router: The address of the router handling the swap. + * - params.amountCalculatedRaw: The amount swapped by the user. + * - params.pool: The liquidity pool involved in the swap. + */ + function mint( + AfterSwapParams calldata params, + IDiscountCampaign _campaign, + bytes32 _campaignID + ) internal nonReentrant { + uint256 newTokenId = _shareTokenId++; + address user = IRouterCommon(params.router).getSender(); + _campaign.updateUserDiscountMapping(_campaignID, newTokenId, user, params.amountCalculatedRaw, block.timestamp); + _mint(user, newTokenId); + } + + /** + * @notice Updates the address of the discount campaign factory. + * @dev Can only be called by the contract owner. Reverts if the new factory address is invalid. + * @param newFactory The address of the new discount campaign factory. + */ + function updateCampaignFactory(address newFactory) external onlyOwner { + if (newFactory == address(0)) { + revert InvalidCampaignAddress(); + } + discountCampaignFactory = IDiscountCampaignFactory(newFactory); + } +} diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/images/Architecture.png b/packages/foundry/contracts/hooks/SwapDiscountHook/images/Architecture.png new file mode 100644 index 00000000..ebbb561a Binary files /dev/null and b/packages/foundry/contracts/hooks/SwapDiscountHook/images/Architecture.png differ diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/images/SwapBondTest.png b/packages/foundry/contracts/hooks/SwapDiscountHook/images/SwapBondTest.png new file mode 100644 index 00000000..eee898fc Binary files /dev/null and b/packages/foundry/contracts/hooks/SwapDiscountHook/images/SwapBondTest.png differ diff --git a/packages/foundry/contracts/hooks/SwapDiscountHook/libraries/TransferHelper.sol b/packages/foundry/contracts/hooks/SwapDiscountHook/libraries/TransferHelper.sol new file mode 100644 index 00000000..44312205 --- /dev/null +++ b/packages/foundry/contracts/hooks/SwapDiscountHook/libraries/TransferHelper.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +library TransferHelper { + /// @notice Transfers tokens from the targeted address to the given destination + /// @notice Errors with 'STF' if transfer fails + /// @param token The contract address of the token to be transferred + /// @param from The originating address from which the tokens will be transferred + /// @param to The destination address of the transfer + /// @param value The amount to be transferred + function safeTransferFrom(address token, address from, address to, uint256 value) external { + (bool success, bytes memory data) = token.call( + abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value) + ); + require(success && (data.length == 0 || abi.decode(data, (bool))), "STF"); + } + + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Errors with ST if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer(address token, address to, uint256 value) external { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), "ST"); + } + + /// @notice Approves the stipulated contract to spend the given allowance in the given token + /// @dev Errors with 'SA' if transfer fails + /// @param token The contract address of the token to be approved + /// @param to The target of the approval + /// @param value The amount of the given token the target will be allowed to spend + function safeApprove(address token, address to, uint256 value) external { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), "SA"); + } + + /// @notice Transfers ETH to the recipient address + /// @dev Fails with `STE` + /// @param to The destination of the transfer + /// @param value The value to be transferred + function safeTransferETH(address to, uint256 value) external { + (bool success, ) = to.call{ value: value }(new bytes(0)); + require(success, "STE"); + } +} diff --git a/packages/foundry/package.json b/packages/foundry/package.json index 4245ab76..712a6f24 100644 --- a/packages/foundry/package.json +++ b/packages/foundry/package.json @@ -19,12 +19,12 @@ "envfile": "~6.18.0", "ethers": "~5.7.1", "qrcode": "~1.5.3", + "solc": "^0.8.27", "toml": "~3.0.0" }, "devDependencies": { "@types/prettier": "2", "@types/qrcode": "1", - "forge-gas-snapshot": "https://github.com/ylv-io/forge-gas-snapshot", "prettier": "~2.8.8", "prettier-plugin-solidity": "^1.3.1" } diff --git a/packages/foundry/remappings.txt b/packages/foundry/remappings.txt index 8abd1432..ad1b8d11 100644 --- a/packages/foundry/remappings.txt +++ b/packages/foundry/remappings.txt @@ -5,5 +5,5 @@ @balancer-labs/v3-pool-weighted/=lib/balancer-v3-monorepo/pkg/pool-weighted/ @balancer-labs/v3-vault/=lib/balancer-v3-monorepo/pkg/vault/ permit2/=lib/permit2/ -forge-gas-snapshot/=node_modules/forge-gas-snapshot/src/ +forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src @openzeppelin/=lib/openzeppelin-contracts/ \ No newline at end of file diff --git a/packages/foundry/test/SwapDiscountHookTests/DiscountCampaign.t.sol b/packages/foundry/test/SwapDiscountHookTests/DiscountCampaign.t.sol new file mode 100644 index 00000000..42f61361 --- /dev/null +++ b/packages/foundry/test/SwapDiscountHookTests/DiscountCampaign.t.sol @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import "forge-std/StdUtils.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; +import { IVaultAdmin } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultAdmin.sol"; +import { IVaultErrors } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultErrors.sol"; +import { + HooksConfig, + LiquidityManagement, + PoolRoleAccounts, + TokenConfig +} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol"; + +import { CastingHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/CastingHelpers.sol"; +import { ArrayHelpers } from "@balancer-labs/v3-solidity-utils/contracts/test/ArrayHelpers.sol"; +import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol"; + +import { BaseVaultTest } from "@balancer-labs/v3-vault/test/foundry/utils/BaseVaultTest.sol"; +import { PoolMock } from "@balancer-labs/v3-vault/contracts/test/PoolMock.sol"; +import { PoolFactoryMock } from "@balancer-labs/v3-vault/contracts/test/PoolFactoryMock.sol"; +import { RouterMock } from "@balancer-labs/v3-vault/contracts/test/RouterMock.sol"; + +import { DiscountCampaignFactory } from "../../contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol"; +import { DiscountCampaign } from "../../contracts/hooks/SwapDiscountHook/DiscountCampaign.sol"; +import { SwapDiscountHook } from "../../contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol"; +import { + IDiscountCampaignFactory +} from "../../contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol"; + +import { IDiscountCampaign } from "../../contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaign.sol"; +import { console } from "forge-std/console.sol"; + +contract DiscountCampaignTest is BaseVaultTest { + using CastingHelpers for address[]; + using FixedPoint for uint256; + using ArrayHelpers for *; + + DiscountCampaignFactory discountCampaignFactory; + DiscountCampaign discountCampaign; + SwapDiscountHook discountHook; + uint256 internal daiIdx; + uint256 internal usdcIdx; + + address payable internal trustedRouter; + + function setUp() public override { + super.setUp(); + + (daiIdx, usdcIdx) = getSortedIndexes(address(dai), address(usdc)); + + discountHook = SwapDiscountHook(poolHooksContract); + discountCampaignFactory.setSwapDiscountHook(address(discountHook)); + } + + function testSuccessfulNFTMint() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + discountCampaign = DiscountCampaign(campaignAddress); + + _doSwapAndCheckBalances(trustedRouter); + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 1); + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + (, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + uint256 swapTime = block.timestamp; + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, false, "Invalid claimed status"); + + vm.warp(block.timestamp + 1 days); + + uint256 bobRewardTokenBalalnceBefore = IERC20(usdc).balanceOf(bob); + + discountCampaign.claim(1); + + (campaignID, userAddress, _campaignAddress, swappedAmount, timeOfSwap, hasClaimed) = discountCampaign + .userDiscountMapping(1); + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, true, "Invalid claimed status"); + + uint256 bobRewardTokenBalalnceAfter = bobRewardTokenBalalnceBefore + ((swappedAmount * discountRate) / 100e18); + + assertEq(bobRewardTokenBalalnceAfter, IERC20(usdc).balanceOf(bob)); + + // if bob tries to reclaim + vm.expectRevert(IDiscountCampaign.RewardAlreadyClaimed.selector); + discountCampaign.claim(1); + + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + console.log(discountRate); + } + + function test_NotAbleToClaimRewardBeforeCoolDownPeriod() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 1 days, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + discountCampaign = DiscountCampaign(campaignAddress); + + _doSwapAndCheckBalances(trustedRouter); + // becasue no campaign Has been created + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 1); + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + (bytes32 campaignId, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + uint256 swapTime = block.timestamp; + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, false, "Invalid claimed status"); + assertEq(campaignID, keccak256(abi.encode(block.timestamp, 2 days))); + assertEq(campaignID, campaignId); + + vm.expectRevert(IDiscountCampaign.CoolDownPeriodNotPassed.selector); + discountCampaign.claim(1); + + vm.warp(block.timestamp + 1 days + 3600); + + uint256 bobRewardTokenBalalnceBefore = IERC20(usdc).balanceOf(bob); + + discountCampaign.claim(1); + + (campaignID, userAddress, _campaignAddress, swappedAmount, timeOfSwap, hasClaimed) = discountCampaign + .userDiscountMapping(1); + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, true, "Invalid claimed status"); + assertEq(campaignID, keccak256(abi.encode(swapTime, 2 days))); + + uint256 bobRewardTokenBalalnceAfter = bobRewardTokenBalalnceBefore + ((swappedAmount * discountRate) / 100e18); + + assertEq(bobRewardTokenBalalnceAfter, IERC20(usdc).balanceOf(bob)); + } + + function test_NotAbleToClaimRewardAfterExpiration() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + discountCampaign = DiscountCampaign(campaignAddress); + + _doSwapAndCheckBalances(trustedRouter); + // becasue no campaign Has been created + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 1); + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + (bytes32 campaignId, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + uint256 swapTime = block.timestamp; + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, false, "Invalid claimed status"); + assertEq(campaignID, keccak256(abi.encode(block.timestamp, 2 days))); + assertEq(campaignID, campaignId); + + vm.warp(block.timestamp + 2 days + 3600); + + // uint256 bobRewardTokenBalalnceBefore = IERC20(usdc).balanceOf(bob); + + // user wont be able to claim because discount is expired + vm.expectRevert(IDiscountCampaign.DiscountExpired.selector); + discountCampaign.claim(1); + + // Now updating the campaign + deal(address(usdc), address(this), 50e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 50e18); + + IDiscountCampaignFactory.CampaignParams memory updateParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 50e18, + expirationTime: 5 days, + coolDownPeriod: 0, + discountAmount: 20e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + discountCampaignFactory.updateCampaign(updateParams); + + // user wont be able to claim because campaign has changed + vm.expectRevert(IDiscountCampaign.CampaignExpired.selector); + discountCampaign.claim(1); + } + + function test_NotAbleToClaimRewardFromAnotherCampaign() public { + deal(address(usdc), address(this), 200e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 200e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 1 days, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + discountCampaign = DiscountCampaign(campaignAddress); + + _doSwapAndCheckBalances(trustedRouter); + + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 1); + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + (, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + uint256 swapTime = block.timestamp; + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, false, "Invalid claimed status"); + + vm.warp(block.timestamp + 1 days + 1); + + // if a wrong nft id or id from a different campaign is provided + vm.expectRevert(IDiscountCampaign.InvalidTokenID.selector); + discountCampaign.claim(2); + } + + function test_DiscountRate() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + discountCampaign = DiscountCampaign(discountCampaignFactory.createCampaign(createParams)); + console.log(poolInitAmount); + _doSwapAndCheckBalancesWithAmount(trustedRouter, poolInitAmount); + + (, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + assertEq(discountRate, 50e18); + + uint256 swapTime = block.timestamp; + + vm.warp(block.timestamp + 2 days); + + uint256 bobRewardTokenBalalnceBefore = IERC20(usdc).balanceOf(bob); + + discountCampaign.claim(1); + + uint256 bobRewardTokenBalalnceAfter = bobRewardTokenBalalnceBefore + ((100e18 * discountRate) / 100e18); + + assertEq(bobRewardTokenBalalnceAfter, IERC20(usdc).balanceOf(bob)); + + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + + assertEq(discountRate, 25e18); + } + + // function test_fuzz_discountRateUntilZero2() public { + // // Initial setup + // deal(address(usdc), address(this), 100e18); + // IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + // IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + // rewardAmount: 100e18, + // expirationTime: 10 days, // Extended expiration time to ensure the loop has enough time to run. + // coolDownPeriod: 0, + // discountAmount: 50e18, + // pool: address(pool), + // owner: address(this), + // rewardToken: address(usdc) + // }); + + // discountCampaign = DiscountCampaign(discountCampaignFactory.createCampaign(createParams)); + + // uint256 tokenId = 1; + // uint256 discountRate; + // uint256 maxIterations = 1000; // Increased max limit to ensure the loop has enough iterations to reach zero. + // uint256 iterationCount = 0; + + // // Initial discount rate before starting + // (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + // console.log("Initial Discount Rate:", discountRate); + + // // Loop until discountRate becomes zero or reaches max iterations + // while (iterationCount < maxIterations) { + // // Bound the amount to swap to avoid out of bounds values and ensure consistency + // uint256 amountToSwap = bound( + // uint256(keccak256(abi.encodePacked(block.timestamp, iterationCount))), + // 90e18, + // 100e18 // Setting upper bound to 5e18 to manage rewards correctly + // ); + + // // Log the amount being swapped + // console.log("Iteration:", iterationCount); + // console.log("Bound Result:", amountToSwap); + // console.log("Current Discount Rate:", discountRate); + + // // Swap by Alice + // vm.prank(alice); + // _doSwapAndCheckBalancesWithAmount(trustedRouter, amountToSwap); + + // // Get updated discount rate after swap + // (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + // console.log("Discount Rate After Swap:", discountRate); + + // // Ensure block time passes for the claim process + // vm.warp(block.timestamp + 1); + + // // Fetch claimable reward directly from the contract for verification + // uint256 claimableReward = discountCampaign.getClaimableReward(tokenId); + // console.log("Claimable Reward from Contract:", claimableReward); + + // // Claim rewards for Alice + // discountCampaign.claim(tokenId); + + // // Log claim and check discount rate + // console.log("Token ID Claimed:", tokenId); + // console.log("Discount Rate After Claim:", discountRate); + + // // Increment tokenId to ensure next iteration swaps with a different token ID + // tokenId++; + + // // Get the updated discount rate + // address rewardToken; + // (, , , , discountRate, rewardToken, , ) = discountCampaign.campaignDetails(); + // console.log("Discount Rate After Claim:", discountRate); + + // console.log("Reward Amount Left in the contract", IERC20(rewardToken).balanceOf(address(discountCampaign))); + + // // If discount rate is zero, break the loop + // if (discountRate == 0) { + // console.log("Discount rate reached zero after iteration:", iterationCount); + // break; + // } + + // iterationCount++; + // } + + // // Ensure the loop terminated because the discount rate is zero + // assertEq(discountRate, 0, "Discount rate did not reach zero within max iterations"); + // } + + function test_fuzz_discountRate() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + discountCampaign = DiscountCampaign(discountCampaignFactory.createCampaign(createParams)); + + vm.prank(alice); + _doSwapAndCheckBalancesWithAmount(trustedRouter, poolInitAmount / 100); + + vm.prank(bob); + _doSwapAndCheckBalancesWithAmount(trustedRouter, poolInitAmount / 80); + + vm.prank(alice); + _doSwapAndCheckBalancesWithAmount(trustedRouter, poolInitAmount / 30); + + vm.prank(bob); + _doSwapAndCheckBalancesWithAmount(trustedRouter, poolInitAmount / 150); + + (, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + assertEq(discountRate, 50e18); + + uint256 swapTime = block.timestamp; + + vm.warp(block.timestamp + 2 days); + + assertEq(discountCampaign.getClaimableReward(1), ((poolInitAmount / 100) * discountRate) / 100e18); //5000000000000000000 + discountCampaign.claim(1); + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + assertEq(discountRate, 47500000000000000000); + + assertEq(discountCampaign.getClaimableReward(2), ((poolInitAmount / 80) * 47500000000000000000) / 100e18); //5937500000000000000 + discountCampaign.claim(2); + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + assertEq(discountRate, 44531250000000000000); + + assertEq(discountCampaign.getClaimableReward(3), ((poolInitAmount / 30) * 44531250000000000000) / 100e18); //14843750000000000000 + discountCampaign.claim(3); + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + assertEq(discountRate, 37109375000000000000); + + assertEq(discountCampaign.getClaimableReward(4), ((poolInitAmount / 150) * 37109375000000000000) / 100e18); //2473958333333333333 + discountCampaign.claim(4); + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + assertEq(discountRate, 35872395833333333334); + } + + // =============================================================================== + + function createAnotherPool() public returns (address pool2) { + pool2 = factoryMock.createPool("Test Pool", "TEST"); + + if (pool2 != address(0)) { + approveForPool(IERC20(pool2)); + } + // Add initial liquidity + vm.startPrank(lp); + _initPool(pool2, [poolInitAmount, poolInitAmount].toMemoryArray(), 0); + vm.stopPrank(); + + trustedRouter = payable(router); + TokenConfig[] memory tokenConfig = vault.buildTokenConfig( + [address(dai), address(usdc)].toMemoryArray().asIERC20() + ); + + _registerPoolWithHook(pool2, tokenConfig, address(factoryMock)); + } + + function _doSwapAndCheckBalancesWithAmount(address payable routerToUse, uint256 amountToSwap) private { + uint256 exactAmountIn = amountToSwap; + // PoolMock uses linear math with a rate of 1, so amountIn == amountOut when no fees are applied. + uint256 expectedAmountOut = exactAmountIn; + + BaseVaultTest.Balances memory balancesBefore = getBalances(bob); + + vm.prank(bob); + RouterMock(routerToUse).swapSingleTokenExactIn( + pool, + dai, + usdc, + exactAmountIn, + expectedAmountOut, + MAX_UINT256, + false, + bytes("") + ); + + BaseVaultTest.Balances memory balancesAfter = getBalances(bob); + + // Bob's balance of DAI is supposed to decrease, since DAI is the token in + assertEq( + balancesBefore.userTokens[daiIdx] - balancesAfter.userTokens[daiIdx], + exactAmountIn, + "Bob's DAI balance is wrong" + ); + // Bob's balance of USDC is supposed to increase, since USDC is the token out + assertEq( + balancesAfter.userTokens[usdcIdx] - balancesBefore.userTokens[usdcIdx], + expectedAmountOut, + "Bob's USDC balance is wrong" + ); + + // Vault's balance of DAI is supposed to increase, since DAI was added by Bob + assertEq( + balancesAfter.vaultTokens[daiIdx] - balancesBefore.vaultTokens[daiIdx], + exactAmountIn, + "Vault's DAI balance is wrong" + ); + // Vault's balance of USDC is supposed to decrease, since USDC was given to Bob + assertEq( + balancesBefore.vaultTokens[usdcIdx] - balancesAfter.vaultTokens[usdcIdx], + expectedAmountOut, + "Vault's USDC balance is wrong" + ); + + // Pool deltas should equal vault's deltas + assertEq( + balancesAfter.poolTokens[daiIdx] - balancesBefore.poolTokens[daiIdx], + exactAmountIn, + "Pool's DAI balance is wrong" + ); + assertEq( + balancesBefore.poolTokens[usdcIdx] - balancesAfter.poolTokens[usdcIdx], + expectedAmountOut, + "Pool's USDC balance is wrong" + ); + } + + function _doSwapAndCheckBalances(address payable routerToUse) private { + uint256 exactAmountIn = poolInitAmount / 100; + // PoolMock uses linear math with a rate of 1, so amountIn == amountOut when no fees are applied. + uint256 expectedAmountOut = exactAmountIn; + + BaseVaultTest.Balances memory balancesBefore = getBalances(bob); + + vm.prank(bob); + RouterMock(routerToUse).swapSingleTokenExactIn( + pool, + dai, + usdc, + exactAmountIn, + expectedAmountOut, + MAX_UINT256, + false, + bytes("") + ); + + BaseVaultTest.Balances memory balancesAfter = getBalances(bob); + + // Bob's balance of DAI is supposed to decrease, since DAI is the token in + assertEq( + balancesBefore.userTokens[daiIdx] - balancesAfter.userTokens[daiIdx], + exactAmountIn, + "Bob's DAI balance is wrong" + ); + // Bob's balance of USDC is supposed to increase, since USDC is the token out + assertEq( + balancesAfter.userTokens[usdcIdx] - balancesBefore.userTokens[usdcIdx], + expectedAmountOut, + "Bob's USDC balance is wrong" + ); + + // Vault's balance of DAI is supposed to increase, since DAI was added by Bob + assertEq( + balancesAfter.vaultTokens[daiIdx] - balancesBefore.vaultTokens[daiIdx], + exactAmountIn, + "Vault's DAI balance is wrong" + ); + // Vault's balance of USDC is supposed to decrease, since USDC was given to Bob + assertEq( + balancesBefore.vaultTokens[usdcIdx] - balancesAfter.vaultTokens[usdcIdx], + expectedAmountOut, + "Vault's USDC balance is wrong" + ); + + // Pool deltas should equal vault's deltas + assertEq( + balancesAfter.poolTokens[daiIdx] - balancesBefore.poolTokens[daiIdx], + exactAmountIn, + "Pool's DAI balance is wrong" + ); + assertEq( + balancesBefore.poolTokens[usdcIdx] - balancesAfter.poolTokens[usdcIdx], + expectedAmountOut, + "Pool's USDC balance is wrong" + ); + } + + function createCampaign(IDiscountCampaignFactory.CampaignParams memory params) public returns (address) { + return discountCampaignFactory.createCampaign(params); + } + + function createHook() internal override returns (address) { + trustedRouter = payable(router); + discountCampaignFactory = new DiscountCampaignFactory(); + // lp will be the owner of the hook. Only LP is able to set hook fee percentages. + vm.prank(lp); + address swapDiscountHook = address( + new SwapDiscountHook( + IVault(address(vault)), + address(factoryMock), + trustedRouter, + address(discountCampaignFactory), + "SwapDiscountNFT", + "SDN" + ) + ); + vm.label(swapDiscountHook, "Swap Discount Hook"); + return swapDiscountHook; + } + + function _registerPoolWithHook(address swapDhookPool, TokenConfig[] memory tokenConfig, address factory) private { + PoolRoleAccounts memory roleAccounts; + LiquidityManagement memory liquidityManagement; + + PoolFactoryMock(factory).registerPool( + swapDhookPool, + tokenConfig, + roleAccounts, + poolHooksContract, + liquidityManagement + ); + } +} diff --git a/packages/foundry/test/SwapDiscountHookTests/DiscountCampaignFactory.t.sol b/packages/foundry/test/SwapDiscountHookTests/DiscountCampaignFactory.t.sol new file mode 100644 index 00000000..9482cdc6 --- /dev/null +++ b/packages/foundry/test/SwapDiscountHookTests/DiscountCampaignFactory.t.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import "forge-std/StdUtils.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; +import { IVaultAdmin } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultAdmin.sol"; +import { IVaultErrors } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultErrors.sol"; +import { + HooksConfig, + LiquidityManagement, + PoolRoleAccounts, + TokenConfig +} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol"; + +import { CastingHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/CastingHelpers.sol"; +import { ArrayHelpers } from "@balancer-labs/v3-solidity-utils/contracts/test/ArrayHelpers.sol"; +import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol"; + +import { BaseVaultTest } from "@balancer-labs/v3-vault/test/foundry/utils/BaseVaultTest.sol"; +import { PoolMock } from "@balancer-labs/v3-vault/contracts/test/PoolMock.sol"; +import { PoolFactoryMock } from "@balancer-labs/v3-vault/contracts/test/PoolFactoryMock.sol"; +import { RouterMock } from "@balancer-labs/v3-vault/contracts/test/RouterMock.sol"; + +import { DiscountCampaignFactory } from "../../contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol"; +import { SwapDiscountHook } from "../../contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol"; +import { + IDiscountCampaignFactory +} from "../../contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol"; +import { console } from "forge-std/console.sol"; + +contract DiscountCampaignFactoryTest is BaseVaultTest { + using CastingHelpers for address[]; + using FixedPoint for uint256; + using ArrayHelpers for *; + + DiscountCampaignFactory discountCampaignFactory; + SwapDiscountHook discountHook; + uint256 internal daiIdx; + uint256 internal usdcIdx; + + address payable internal trustedRouter; + + function setUp() public override { + super.setUp(); + + (daiIdx, usdcIdx) = getSortedIndexes(address(dai), address(usdc)); + + discountHook = SwapDiscountHook(poolHooksContract); + discountCampaignFactory.setSwapDiscountHook(address(discountHook)); + } + + function testSuccessfullDeploymentOfCampaignContract() public { + deal(address(dai), address(this), 100e18); + IERC20(address(dai)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory params = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(params); + + // test the struct + (address _campaignAddress, address _owner) = discountCampaignFactory.discountCampaigns(address(pool)); + + assertEq(_campaignAddress, campaignAddress, "Invalid campaign Address"); + assertEq(_owner, address(this), "Invalid owner Address"); + } + + function testUnsuccessfulDeploymentsOfCampaignContract() public { + vm.expectRevert(); + IDiscountCampaignFactory.CampaignParams memory params = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + discountCampaignFactory.createCampaign(params); + + deal(address(dai), address(this), 100e18); + IERC20(address(dai)).approve(address(discountCampaignFactory), 100e18); + + discountCampaignFactory.createCampaign(params); + + vm.expectRevert(IDiscountCampaignFactory.PoolCampaignAlreadyExist.selector); + discountCampaignFactory.createCampaign(params); + } + + function testUnsuccessfulCampaignUpdate() public { + deal(address(dai), address(this), 100e18); + IERC20(address(dai)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + IDiscountCampaignFactory.CampaignParams memory updateParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 5 days, + coolDownPeriod: 0, + discountAmount: 20e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + vm.expectRevert(IDiscountCampaignFactory.PoolCampaignHasnotExpired.selector); + discountCampaignFactory.updateCampaign(updateParams); + + vm.expectRevert(IDiscountCampaignFactory.PoolCampaignDoesnotExist.selector); + updateParams.pool = address(bob); + discountCampaignFactory.updateCampaign(updateParams); + + vm.prank(address(bob)); + vm.expectRevert(IDiscountCampaignFactory.NOT_AUTHORIZED.selector); + updateParams.pool = address(pool); + discountCampaignFactory.updateCampaign(updateParams); + + // campaign hasn't expired yet + vm.warp(block.timestamp + 7 days); + vm.expectRevert(); + discountCampaignFactory.updateCampaign(updateParams); + } + + function testSuccessfulCampaignUpdate() public { + deal(address(dai), address(this), 100e18); + IERC20(address(dai)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + vm.warp(block.timestamp + 7 days); + deal(address(dai), address(this), 1000e18); + IERC20(address(dai)).approve(address(discountCampaignFactory), 1000e18); + + IDiscountCampaignFactory.CampaignParams memory updateParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 1000e18, + expirationTime: 5 days, + coolDownPeriod: 0, + discountAmount: 20e18, + pool: address(pool), + owner: address(this), + rewardToken: address(dai) + }); + + discountCampaignFactory.updateCampaign(updateParams); + } + + // =============================================================================== + + function createHook() internal override returns (address) { + trustedRouter = payable(router); + discountCampaignFactory = new DiscountCampaignFactory(); + // lp will be the owner of the hook. Only LP is able to set hook fee percentages. + vm.prank(lp); + address swapDiscountHook = address( + new SwapDiscountHook( + IVault(address(vault)), + address(factoryMock), + trustedRouter, + address(discountCampaignFactory), + "SwapDiscountNFT", + "SDN" + ) + ); + vm.label(swapDiscountHook, "Swap Discount Hook"); + return swapDiscountHook; + } + + function _registerPoolWithHook(address swapDhookPool, TokenConfig[] memory tokenConfig, address factory) private { + PoolRoleAccounts memory roleAccounts; + LiquidityManagement memory liquidityManagement; + + PoolFactoryMock(factory).registerPool( + swapDhookPool, + tokenConfig, + roleAccounts, + poolHooksContract, + liquidityManagement + ); + } +} diff --git a/packages/foundry/test/SwapDiscountHookTests/SwapDiscountHook.t.sol b/packages/foundry/test/SwapDiscountHookTests/SwapDiscountHook.t.sol new file mode 100644 index 00000000..338709ea --- /dev/null +++ b/packages/foundry/test/SwapDiscountHookTests/SwapDiscountHook.t.sol @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; +import { IVaultAdmin } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultAdmin.sol"; +import { IVaultErrors } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultErrors.sol"; +import { + HooksConfig, + LiquidityManagement, + PoolRoleAccounts, + TokenConfig +} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol"; + +import { CastingHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/CastingHelpers.sol"; +import { ArrayHelpers } from "@balancer-labs/v3-solidity-utils/contracts/test/ArrayHelpers.sol"; +import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol"; + +import { BaseVaultTest } from "@balancer-labs/v3-vault/test/foundry/utils/BaseVaultTest.sol"; +import { PoolMock } from "@balancer-labs/v3-vault/contracts/test/PoolMock.sol"; +import { PoolFactoryMock } from "@balancer-labs/v3-vault/contracts/test/PoolFactoryMock.sol"; +import { RouterMock } from "@balancer-labs/v3-vault/contracts/test/RouterMock.sol"; + +import { SwapDiscountHook } from "../../contracts/hooks/SwapDiscountHook/SwapDiscountHook.sol"; +import { DiscountCampaignFactory } from "../../contracts/hooks/SwapDiscountHook/DiscountCampaignFactory.sol"; +import { + IDiscountCampaignFactory +} from "../../contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaignFactory.sol"; +import { DiscountCampaign } from "../../contracts/hooks/SwapDiscountHook/DiscountCampaign.sol"; +import { IDiscountCampaign } from "../../contracts/hooks/SwapDiscountHook/Interfaces/IDiscountCampaign.sol"; + +import { console } from "forge-std/console.sol"; + +contract SwapDiscountHookTest is BaseVaultTest { + using CastingHelpers for address[]; + using FixedPoint for uint256; + using ArrayHelpers for *; + + uint256 internal daiIdx; + uint256 internal usdcIdx; + SwapDiscountHook discountHook; + DiscountCampaignFactory discountCampaignFactory; + DiscountCampaign discountCampaign; + + address payable internal trustedRouter; + + function setUp() public override { + super.setUp(); + + (daiIdx, usdcIdx) = getSortedIndexes(address(dai), address(usdc)); + + discountHook = SwapDiscountHook(poolHooksContract); + discountCampaignFactory.setSwapDiscountHook(address(discountHook)); + } + + function createHook() internal override returns (address) { + trustedRouter = payable(router); + pool = factoryMock.createPool("Test Pool", "TEST"); + discountCampaignFactory = new DiscountCampaignFactory(); + TokenConfig[] memory tokenConfig = vault.buildTokenConfig( + [address(dai), address(usdc)].toMemoryArray().asIERC20() + ); + + _registerPoolWithHook(pool, tokenConfig, address(factoryMock)); + + // lp will be the owner of the hook. Only LP is able to set hook fee percentages. + vm.prank(lp); + address swapDiscountHook = address( + new SwapDiscountHook( + IVault(address(vault)), + address(factoryMock), + trustedRouter, + address(discountCampaignFactory), + "SwapDiscountNFT", + "SDN" + ) + ); + vm.label(swapDiscountHook, "Swap Discount Hook"); + return swapDiscountHook; + } + + function _registerPoolWithHook(address swapDhookPool, TokenConfig[] memory tokenConfig, address factory) private { + PoolRoleAccounts memory roleAccounts; + LiquidityManagement memory liquidityManagement; + + PoolFactoryMock(factory).registerPool( + swapDhookPool, + tokenConfig, + roleAccounts, + poolHooksContract, + liquidityManagement + ); + } + + // =================================================================================== + + function testUnSuccessfulNFTMint() public { + _doSwapAndCheckBalances(trustedRouter); + // becasue no campaign Has been created + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 0); + } + + function testSuccessfulNFTMint() public { + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + + discountCampaign = DiscountCampaign(campaignAddress); + + _doSwapAndCheckBalances(trustedRouter); + // becasue no campaign Has been created + assertEq(IERC721(address(discountHook)).balanceOf(address(bob)), 1); + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + (, , , , uint256 discountRate, , , ) = discountCampaign.campaignDetails(); + + uint256 swapTime = block.timestamp; + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, false, "Invalid claimed status"); + + vm.warp(block.timestamp + 1 days); + + uint256 bobRewardTokenBalalnceBefore = IERC20(usdc).balanceOf(bob); + + discountCampaign.claim(1); + + (campaignID, userAddress, _campaignAddress, swappedAmount, timeOfSwap, hasClaimed) = discountCampaign + .userDiscountMapping(1); + + assertEq(userAddress, bob, "Invalid user"); + assertEq(_campaignAddress, campaignAddress, "Invalid campaignAddress"); + assertEq(swappedAmount, poolInitAmount / 100, "Invalid swappedAmount"); + assertEq(timeOfSwap, swapTime, "Invalid timeOfSwap"); + assertEq(hasClaimed, true, "Invalid claimed status"); + + uint256 bobRewardTokenBalalnceAfter = bobRewardTokenBalalnceBefore + ((swappedAmount * discountRate) / 100e18); + + assertEq(bobRewardTokenBalalnceAfter, IERC20(usdc).balanceOf(bob)); + + // if bob tries to reclaim + vm.expectRevert(IDiscountCampaign.RewardAlreadyClaimed.selector); + discountCampaign.claim(1); + + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + console.log(discountRate); + } + + function testLoggingSuccessfulMint() public { + // Step 1: Deploy discountCampaignFactory + console.log("DiscountCampaignFactory deployed at:", address(discountCampaignFactory)); + + // Step 2: Set the Swap Discount Hook address + discountHook = SwapDiscountHook(poolHooksContract); + discountCampaignFactory.setSwapDiscountHook(address(discountHook)); + console.log("Discount Hook address set to:", address(discountHook)); + + // Step 3: Create a discount campaign and log its details + deal(address(usdc), address(this), 100e18); + IERC20(address(usdc)).approve(address(discountCampaignFactory), 100e18); + + IDiscountCampaignFactory.CampaignParams memory createParams = IDiscountCampaignFactory.CampaignParams({ + rewardAmount: 100e18, + expirationTime: 2 days, + coolDownPeriod: 0, + discountAmount: 50e18, + pool: address(pool), + owner: address(this), + rewardToken: address(usdc) + }); + + address campaignAddress = discountCampaignFactory.createCampaign(createParams); + discountCampaign = DiscountCampaign(campaignAddress); + + console.log("DiscountCampaign created at:", campaignAddress); + console.log("Campaign Reward Amount:", createParams.rewardAmount); + console.log("Campaign Expiration Time:", createParams.expirationTime); + console.log("Campaign Discount Amount:", createParams.discountAmount); + + // Step 4: Perform a swap and verify NFT mint + _doSwapAndCheckBalances2(trustedRouter); + uint256 nftBalance = IERC721(address(discountHook)).balanceOf(address(bob)); + console.log("NFT balance of Bob after swap:", nftBalance); + assertEq(nftBalance, 1); + + // Fetch user discount details and log them + ( + bytes32 campaignID, + address userAddress, + address _campaignAddress, + uint256 swappedAmount, + uint256 timeOfSwap, + bool hasClaimed + ) = discountCampaign.userDiscountMapping(1); + + console.log("User Address after swap:", userAddress); + console.log("Campaign Address after swap:", _campaignAddress); + console.log("Swapped Amount:", swappedAmount); + console.log("Time of Swap:", timeOfSwap); + console.log("Has Claimed Reward:", hasClaimed); + + // Step 5: Claim the reward and verify balances + vm.warp(block.timestamp + 1 days); + uint256 bobRewardTokenBalanceBefore = IERC20(usdc).balanceOf(bob); + + uint256 discountRate; + (, , , , discountRate, , , ) = discountCampaign.campaignDetails(); + + discountCampaign.claim(1); + console.log("Reward claimed by Bob."); + + // Fetch updated user discount details and log them + (campaignID, userAddress, _campaignAddress, swappedAmount, timeOfSwap, hasClaimed) = discountCampaign + .userDiscountMapping(1); + console.log("Has Claimed Reward after claiming:", hasClaimed); + + // Verify that Bob's reward balance increased + + uint256 bobRewardTokenBalanceAfter = bobRewardTokenBalanceBefore + ((swappedAmount * discountRate) / 100e18); + assertEq(bobRewardTokenBalanceAfter, IERC20(usdc).balanceOf(bob)); + console.log("Bob's Reward Token Balance after claim:", bobRewardTokenBalanceAfter); + + // Step 6: Attempt to reclaim the reward and expect revert + vm.expectRevert(IDiscountCampaign.RewardAlreadyClaimed.selector); + discountCampaign.claim(1); + + console.log("Reward reclaim attempt reverted as expected."); + } + + function _doSwapAndCheckBalances2(address payable routerToUse) private { + uint256 exactAmountIn = poolInitAmount / 100; + uint256 expectedAmountOut = exactAmountIn; + + BaseVaultTest.Balances memory balancesBefore = getBalances(bob); + + vm.prank(bob); + RouterMock(routerToUse).swapSingleTokenExactIn( + pool, + dai, + usdc, + exactAmountIn, + expectedAmountOut, + MAX_UINT256, + false, + bytes("") + ); + + BaseVaultTest.Balances memory balancesAfter = getBalances(bob); + + // Log swap details for verification + console.log("Swap executed:"); + console.log("Bob's DAI Balance Before:", balancesBefore.userTokens[daiIdx]); + console.log("Bob's DAI Balance After:", balancesAfter.userTokens[daiIdx]); + console.log("Bob's USDC Balance Before:", balancesBefore.userTokens[usdcIdx]); + console.log("Bob's USDC Balance After:", balancesAfter.userTokens[usdcIdx]); + + // Verify balances as per swap expectations + assertEq( + balancesBefore.userTokens[daiIdx] - balancesAfter.userTokens[daiIdx], + exactAmountIn, + "Bob's DAI balance is wrong" + ); + assertEq( + balancesAfter.userTokens[usdcIdx] - balancesBefore.userTokens[usdcIdx], + expectedAmountOut, + "Bob's USDC balance is wrong" + ); + + assertEq( + balancesAfter.vaultTokens[daiIdx] - balancesBefore.vaultTokens[daiIdx], + exactAmountIn, + "Vault's DAI balance is wrong" + ); + assertEq( + balancesBefore.vaultTokens[usdcIdx] - balancesAfter.vaultTokens[usdcIdx], + expectedAmountOut, + "Vault's USDC balance is wrong" + ); + + assertEq( + balancesAfter.poolTokens[daiIdx] - balancesBefore.poolTokens[daiIdx], + exactAmountIn, + "Pool's DAI balance is wrong" + ); + assertEq( + balancesBefore.poolTokens[usdcIdx] - balancesAfter.poolTokens[usdcIdx], + expectedAmountOut, + "Pool's USDC balance is wrong" + ); + } + + function _doSwapAndCheckBalances(address payable routerToUse) private { + uint256 exactAmountIn = poolInitAmount / 100; + // PoolMock uses linear math with a rate of 1, so amountIn == amountOut when no fees are applied. + uint256 expectedAmountOut = exactAmountIn; + + BaseVaultTest.Balances memory balancesBefore = getBalances(bob); + + vm.prank(bob); + RouterMock(routerToUse).swapSingleTokenExactIn( + pool, + dai, + usdc, + exactAmountIn, + expectedAmountOut, + MAX_UINT256, + false, + bytes("") + ); + + BaseVaultTest.Balances memory balancesAfter = getBalances(bob); + + // Bob's balance of DAI is supposed to decrease, since DAI is the token in + assertEq( + balancesBefore.userTokens[daiIdx] - balancesAfter.userTokens[daiIdx], + exactAmountIn, + "Bob's DAI balance is wrong" + ); + // Bob's balance of USDC is supposed to increase, since USDC is the token out + assertEq( + balancesAfter.userTokens[usdcIdx] - balancesBefore.userTokens[usdcIdx], + expectedAmountOut, + "Bob's USDC balance is wrong" + ); + + // Vault's balance of DAI is supposed to increase, since DAI was added by Bob + assertEq( + balancesAfter.vaultTokens[daiIdx] - balancesBefore.vaultTokens[daiIdx], + exactAmountIn, + "Vault's DAI balance is wrong" + ); + // Vault's balance of USDC is supposed to decrease, since USDC was given to Bob + assertEq( + balancesBefore.vaultTokens[usdcIdx] - balancesAfter.vaultTokens[usdcIdx], + expectedAmountOut, + "Vault's USDC balance is wrong" + ); + + // Pool deltas should equal vault's deltas + assertEq( + balancesAfter.poolTokens[daiIdx] - balancesBefore.poolTokens[daiIdx], + exactAmountIn, + "Pool's DAI balance is wrong" + ); + assertEq( + balancesBefore.poolTokens[usdcIdx] - balancesAfter.poolTokens[usdcIdx], + expectedAmountOut, + "Pool's USDC balance is wrong" + ); + } +} diff --git a/yarn.lock b/yarn.lock index 84396280..d9f29e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,14 +41,14 @@ __metadata: linkType: hard "@babel/generator@npm:^7.23.0": - version: 7.24.7 - resolution: "@babel/generator@npm:7.24.7" + version: 7.25.6 + resolution: "@babel/generator@npm:7.25.6" dependencies: - "@babel/types": ^7.24.7 + "@babel/types": ^7.25.6 "@jridgewell/gen-mapping": ^0.3.5 "@jridgewell/trace-mapping": ^0.3.25 jsesc: ^2.5.1 - checksum: 0ff31a73b15429f1287e4d57b439bba4a266f8c673bb445fe313b82f6d110f586776997eb723a777cd7adad9d340edd162aea4973a90112c5d0cfcaf6686844b + checksum: b55975cd664f5602304d868bb34f4ee3bed6f5c7ce8132cd92ff27a46a53a119def28a182d91992e86f75db904f63094a81247703c4dc96e4db0c03fd04bcd68 languageName: node linkType: hard @@ -89,10 +89,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-string-parser@npm:7.24.7" - checksum: 09568193044a578743dd44bf7397940c27ea693f9812d24acb700890636b376847a611cdd0393a928544e79d7ad5b8b916bd8e6e772bc8a10c48a647a96e7b1a +"@babel/helper-string-parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-string-parser@npm:7.24.8" + checksum: 39b03c5119216883878655b149148dc4d2e284791e969b19467a9411fccaa33f7a713add98f4db5ed519535f70ad273cdadfd2eb54d47ebbdeac5083351328ce languageName: node linkType: hard @@ -115,32 +115,34 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/parser@npm:7.24.7" +"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.25.0": + version: 7.25.6 + resolution: "@babel/parser@npm:7.25.6" + dependencies: + "@babel/types": ^7.25.6 bin: parser: ./bin/babel-parser.js - checksum: fc9d2c4c8712f89672edc55c0dc5cf640dcec715b56480f111f85c2bc1d507e251596e4110d65796690a96ac37a4b60432af90b3e97bb47e69d4ef83872dbbd6 + checksum: 85b237ded09ee43cc984493c35f3b1ff8a83e8dbbb8026b8132e692db6567acc5a1659ec928e4baa25499ddd840d7dae9dee3062be7108fe23ec5f94a8066b1e languageName: node linkType: hard "@babel/runtime@npm:^7.12.5": - version: 7.24.7 - resolution: "@babel/runtime@npm:7.24.7" + version: 7.25.6 + resolution: "@babel/runtime@npm:7.25.6" dependencies: regenerator-runtime: ^0.14.0 - checksum: d17f29eed6f848ac15cdf4202a910b741facfb0419a9d79e5c7fa37df6362fc3227f1cc2e248cc6db5e53ddffb4caa6686c488e6e80ce3d29c36a4e74c8734ea + checksum: ee1a69d3ac7802803f5ee6a96e652b78b8addc28c6a38c725a4ad7d61a059d9e6cb9f6550ed2f63cce67a1bd82e0b1ef66a1079d895be6bfb536a5cfbd9ccc32 languageName: node linkType: hard "@babel/template@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/template@npm:7.24.7" + version: 7.25.0 + resolution: "@babel/template@npm:7.25.0" dependencies: "@babel/code-frame": ^7.24.7 - "@babel/parser": ^7.24.7 - "@babel/types": ^7.24.7 - checksum: ea90792fae708ddf1632e54c25fe1a86643d8c0132311f81265d2bdbdd42f9f4fac65457056c1b6ca87f7aa0d6a795b549566774bba064bdcea2034ab3960ee9 + "@babel/parser": ^7.25.0 + "@babel/types": ^7.25.0 + checksum: 3f2db568718756d0daf2a16927b78f00c425046b654cd30b450006f2e84bdccaf0cbe6dc04994aa1f5f6a4398da2f11f3640a4d3ee31722e43539c4c919c817b languageName: node linkType: hard @@ -172,14 +174,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.17.0, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.8.3": - version: 7.24.7 - resolution: "@babel/types@npm:7.24.7" +"@babel/types@npm:^7.17.0, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.6": + version: 7.25.6 + resolution: "@babel/types@npm:7.25.6" dependencies: - "@babel/helper-string-parser": ^7.24.7 + "@babel/helper-string-parser": ^7.24.8 "@babel/helper-validator-identifier": ^7.24.7 to-fast-properties: ^2.0.0 - checksum: 3e4437fced97e02982972ce5bebd318c47d42c9be2152c0fd28c6f786cc74086cc0a8fb83b602b846e41df37f22c36254338eada1a47ef9d8a1ec92332ca3ea8 + checksum: 9b2f84ff3f874ad05b0b9bf06862c56f478b65781801f82296b4cc01bee39e79c20a7c0a06959fed0ee582c8267e1cb21638318655c5e070b0287242a844d1c9 languageName: node linkType: hard @@ -212,13 +214,13 @@ __metadata: linkType: hard "@emotion/hash@npm:^0.9.0": - version: 0.9.1 - resolution: "@emotion/hash@npm:0.9.1" - checksum: 716e17e48bf9047bf9383982c071de49f2615310fb4e986738931776f5a823bc1f29c84501abe0d3df91a3803c80122d24e28b57351bca9e01356ebb33d89876 + version: 0.9.2 + resolution: "@emotion/hash@npm:0.9.2" + checksum: 379bde2830ccb0328c2617ec009642321c0e009a46aa383dfbe75b679c6aea977ca698c832d225a893901f29d7b3eef0e38cf341f560f6b2b56f1ff23c172387 languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0": +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" dependencies: @@ -229,10 +231,10 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.6.1": - version: 4.11.0 - resolution: "@eslint-community/regexpp@npm:4.11.0" - checksum: 97d2fe46690b69417a551bd19a3dc53b6d9590d2295c43cc4c4e44e64131af541e2f4a44d5c12e87de990403654d3dae9d33600081f3a2f0386b368abc9111ec +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.6.1": + version: 4.11.1 + resolution: "@eslint-community/regexpp@npm:4.11.1" + checksum: 6986685529d30e33c2640973c3d8e7ddd31bef3cc8cb10ad54ddc1dea12680779a2c23a45562aa1462c488137a3570e672d122fac7da22d82294382d915cec70 languageName: node linkType: hard @@ -253,10 +255,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.57.0": - version: 8.57.0 - resolution: "@eslint/js@npm:8.57.0" - checksum: 315dc65b0e9893e2bff139bddace7ea601ad77ed47b4550e73da8c9c2d2766c7a575c3cddf17ef85b8fd6a36ff34f91729d0dcca56e73ca887c10df91a41b0bb +"@eslint/js@npm:8.57.1": + version: 8.57.1 + resolution: "@eslint/js@npm:8.57.1" + checksum: 2afb77454c06e8316793d2e8e79a0154854d35e6782a1217da274ca60b5044d2c69d6091155234ed0551a1e408f86f09dd4ece02752c59568fa403e60611e880 languageName: node linkType: hard @@ -705,22 +707,22 @@ __metadata: linkType: hard "@heroicons/react@npm:^2.0.11": - version: 2.1.4 - resolution: "@heroicons/react@npm:2.1.4" + version: 2.1.5 + resolution: "@heroicons/react@npm:2.1.5" peerDependencies: react: ">= 16" - checksum: 4bdfc8934a855321e2e572dbad9bb5198e56d2cfa8d9c70b068d425e1f57028a1d3a114d27a01bd41948c6cc05b84632fc60355c34ad904c6be9671dd029f9ce + checksum: 1c793f96443580416eccdb60d72c19f640ce068a95f8568bfe8176c6e8ef1318f3d4d4caa8656b571002fe3a7f2d989d6812429fd6bca4bdff641e4b8998c0cd languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.14": - version: 0.11.14 - resolution: "@humanwhocodes/config-array@npm:0.11.14" +"@humanwhocodes/config-array@npm:^0.13.0": + version: 0.13.0 + resolution: "@humanwhocodes/config-array@npm:0.13.0" dependencies: - "@humanwhocodes/object-schema": ^2.0.2 + "@humanwhocodes/object-schema": ^2.0.3 debug: ^4.3.1 minimatch: ^3.0.5 - checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 + checksum: eae69ff9134025dd2924f0b430eb324981494be26f0fddd267a33c28711c4db643242cf9fddf7dadb9d16c96b54b2d2c073e60a56477df86e0173149313bd5d6 languageName: node linkType: hard @@ -731,7 +733,7 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.2": +"@humanwhocodes/object-schema@npm:^2.0.3": version: 2.0.3 resolution: "@humanwhocodes/object-schema@npm:2.0.3" checksum: d3b78f6c5831888c6ecc899df0d03bcc25d46f3ad26a11d7ea52944dc36a35ef543fad965322174238d677a43d5c694434f6607532cff7077062513ad7022631 @@ -778,9 +780,9 @@ __metadata: linkType: hard "@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": - version: 1.4.15 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" - checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 + version: 1.5.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" + checksum: 05df4f2538b3b0f998ea4c1cd34574d0feba216fa5d4ccaef0187d12abf82eafe6021cec8b49f9bb4d90f2ba4582ccc581e72986a5fcf4176ae0cfeb04cf52ec languageName: node linkType: hard @@ -795,9 +797,9 @@ __metadata: linkType: hard "@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": - version: 1.2.0 - resolution: "@lit-labs/ssr-dom-shim@npm:1.2.0" - checksum: 704621c28df8d651e54a1b93f6ede8103db2dd3e7a1f02463fe5492bd28aa22de813314c7833260204fed5c8491a6bbd763f6051abc25690df537d812a508c35 + version: 1.2.1 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.1" + checksum: 5667c44f58e16edaa257fc3ae7f752250d5250d4eb1d071b65df0f1fce0b90b42e8528787cc2673998d76d993440143a2a20c3358ce125c62df4cd193784de8d languageName: node linkType: hard @@ -894,8 +896,8 @@ __metadata: linkType: hard "@metamask/utils@npm:^9.0.0": - version: 9.0.0 - resolution: "@metamask/utils@npm:9.0.0" + version: 9.2.1 + resolution: "@metamask/utils@npm:9.2.1" dependencies: "@ethereumjs/tx": ^4.2.0 "@metamask/superstruct": ^3.1.0 @@ -906,7 +908,7 @@ __metadata: pony-cause: ^2.1.10 semver: ^7.5.4 uuid: ^9.0.1 - checksum: 5dcb9d47c4768c33d451cc74c83207726c68b1340be1d091ca44105564f0ba0703026d357de7996de4459ac41cd420a0eb1f06db32f5ebbeeee581393f45fd44 + checksum: 1a0c842d6fe490bb068c74c6c0684a3e5fabdadcf653b1c83bc69832e9e515fbae5809be20ddfc5c31715cd3d90cf18b73088d9c81f99028ff7b2c7160358c4e languageName: node linkType: hard @@ -995,81 +997,81 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:14.2.4": - version: 14.2.4 - resolution: "@next/env@npm:14.2.4" - checksum: ff47297f959c4f4a45393fc84eb2cdef0e92fb07903e1240e061ff71c2319d90d3faf23aa6f8e5747451a26527ab20b483a200845ac9c72629647d67407b15c2 +"@next/env@npm:14.2.12": + version: 14.2.12 + resolution: "@next/env@npm:14.2.12" + checksum: 90141a984bc36c2a1a281392e2821744f442406a453107bf96d89c3e425f9ce414e4996b4e51c00cbf9817c652b4f34b1c310747a7fdf3bb62ea3526ef92ae8b languageName: node linkType: hard -"@next/eslint-plugin-next@npm:14.2.4": - version: 14.2.4 - resolution: "@next/eslint-plugin-next@npm:14.2.4" +"@next/eslint-plugin-next@npm:14.2.12": + version: 14.2.12 + resolution: "@next/eslint-plugin-next@npm:14.2.12" dependencies: glob: 10.3.10 - checksum: 65929cea46c252490ff2deb9bc57e2e1fab9baf02ece1af043dfd3b3ac58cb12ea7c3adbed8687d2a6c2938c1175338a389a695fe9d072712c77a21b55ed29f6 + checksum: f181190d7761e41bd311fb2ec18539f5655ca878a3bfb3b5f6fd950b2143859502b84d87e22189546673d3c4624b0a4be16a4fbd5c56826599a8bb862ed1d2df languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-darwin-arm64@npm:14.2.4" +"@next/swc-darwin-arm64@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-darwin-arm64@npm:14.2.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-darwin-x64@npm:14.2.4" +"@next/swc-darwin-x64@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-darwin-x64@npm:14.2.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-linux-arm64-gnu@npm:14.2.4" +"@next/swc-linux-arm64-gnu@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.12" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-linux-arm64-musl@npm:14.2.4" +"@next/swc-linux-arm64-musl@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.12" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-linux-x64-gnu@npm:14.2.4" +"@next/swc-linux-x64-gnu@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.12" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-linux-x64-musl@npm:14.2.4" +"@next/swc-linux-x64-musl@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-linux-x64-musl@npm:14.2.12" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-win32-arm64-msvc@npm:14.2.4" +"@next/swc-win32-arm64-msvc@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-win32-ia32-msvc@npm:14.2.4" +"@next/swc-win32-ia32-msvc@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.12" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:14.2.4": - version: 14.2.4 - resolution: "@next/swc-win32-x64-msvc@npm:14.2.4" +"@next/swc-win32-x64-msvc@npm:14.2.12": + version: 14.2.12 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1101,6 +1103,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.4.0": + version: 1.6.0 + resolution: "@noble/curves@npm:1.6.0" + dependencies: + "@noble/hashes": 1.5.0 + checksum: 258f3feb2a6098cf35521562ecb7d452fd728e8a008ff9f1ef435184f9d0c782ceb8f7b7fa8df3317c3be7a19f53995ee124cd05c8080b130bd42e3cb072f24d + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -1108,13 +1119,20 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.4.0": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342 languageName: node linkType: hard +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.5.0": + version: 1.5.0 + resolution: "@noble/hashes@npm:1.5.0" + checksum: 9cc031d5c888c455bfeef76af649b87f75380a4511405baea633c1e4912fd84aff7b61e99716f0231d244c9cfeda1fafd7d718963e6a0c674ed705e9b1b4f76b + languageName: node + linkType: hard + "@noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" @@ -1149,6 +1167,13 @@ __metadata: languageName: node linkType: hard +"@nolyfill/is-core-module@npm:1.0.39": + version: 1.0.39 + resolution: "@nolyfill/is-core-module@npm:1.0.39" + checksum: 0d6e098b871eca71d875651288e1f0fa770a63478b0b50479c99dc760c64175a56b5b04f58d5581bbcc6b552b8191ab415eada093d8df9597ab3423c8cac1815 + languageName: node + linkType: hard + "@npmcli/agent@npm:^2.0.0": version: 2.2.2 resolution: "@npmcli/agent@npm:2.2.2" @@ -1343,10 +1368,17 @@ __metadata: languageName: node linkType: hard +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 17d04adf404e04c1e61391ed97bca5117d4c2767a76ae3e879390d6dec7b317fcae68afbf9e98badee075d0b64fa60f287729c4942021b4d19cd01db77385c01 + languageName: node + linkType: hard + "@rushstack/eslint-patch@npm:^1.3.3": - version: 1.10.3 - resolution: "@rushstack/eslint-patch@npm:1.10.3" - checksum: 1042779367ee102576a3c132f052d718d7111fee9f815758a72b21e8145620f7d3403c14fcde3b4cfa1cbc14b08b8519151ff77d0f353bf647f0a0a16eafdef5 + version: 1.10.4 + resolution: "@rushstack/eslint-patch@npm:1.10.4" + checksum: ec17ac954ed01e9c714e29ae00da29099234a71615d6f61f2da5c7beeef283f5619132114faf9481cb1ca7b4417aed74c05a54d416e4d8facc189bb216d49066 languageName: node linkType: hard @@ -1381,16 +1413,16 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.21.8 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.21.8" - checksum: 67b3c993fc6fbb47b8d2f76719fbe7aa9fbf2d3b6eff26848724f79ae78ccd4d3a5b04fa0de9efc6262afd31b3e6fb1b1cddc0d026f3b0dc886b1a1c4b1e03c9 + version: 3.22.2 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.2" + checksum: 75131db9db3c91a7d64773d793e1e8555adfffec4138be71a4f2a5c3daacfa461bf16fd8f8b3324aa8d3d9091c1f6315c41e7decdecd1604bfa4318c6b87d354 languageName: node linkType: hard -"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2, @scure/base@npm:~1.1.6": - version: 1.1.7 - resolution: "@scure/base@npm:1.1.7" - checksum: d9084be9a2f27971df1684af9e40bb750e86f549345e1bb3227fb61673c0c83569c92c1cb0a4ddccb32650b39d3cd3c145603b926ba751c9bc60c27317549b20 +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2, @scure/base@npm:~1.1.6, @scure/base@npm:~1.1.8": + version: 1.1.9 + resolution: "@scure/base@npm:1.1.9" + checksum: 120820a37dfe9dfe4cab2b7b7460552d08e67dee8057ed5354eb68d8e3440890ae983ce3bee957d2b45684950b454a2b6d71d5ee77c1fd3fddc022e2a510337f languageName: node linkType: hard @@ -1436,6 +1468,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip39@npm:1.4.0" + dependencies: + "@noble/hashes": ~1.5.0 + "@scure/base": ~1.1.8 + checksum: 211f2c01361993bfe54c0e4949f290224381457c7f76d7cd51d6a983f3f4b6b9f85adfd0e623977d777ed80417a5fe729eb19dd34e657147810a0e58a8e7b9e0 + languageName: node + linkType: hard + "@se-2/foundry@workspace:packages/foundry": version: 0.0.0-use.local resolution: "@se-2/foundry@workspace:packages/foundry" @@ -1445,10 +1487,10 @@ __metadata: dotenv: ~16.3.1 envfile: ~6.18.0 ethers: ~5.7.1 - forge-gas-snapshot: "https://github.com/ylv-io/forge-gas-snapshot" prettier: ~2.8.8 prettier-plugin-solidity: ^1.3.1 qrcode: ~1.5.3 + solc: ^0.8.27 toml: ~3.0.0 languageName: unknown linkType: soft @@ -1501,10 +1543,10 @@ __metadata: languageName: unknown linkType: soft -"@solidity-parser/parser@npm:^0.17.0": - version: 0.17.0 - resolution: "@solidity-parser/parser@npm:0.17.0" - checksum: 2f47732c9a4f6b264ce6c8a0544bd5a0805f824d3c40a8a253e59d5dbe9a98163f55c06460232f57a6b389bb5235c18d0563f94425202ec2f859d88f2378e0ac +"@solidity-parser/parser@npm:^0.18.0": + version: 0.18.0 + resolution: "@solidity-parser/parser@npm:0.18.0" + checksum: 970d991529d632862fa88e107531339d84df35bf0374e31e8215ce301b19a01ede33fccf4d374402649814263f8bc278a8e6d62a0129bb877539fbdd16a604cc languageName: node linkType: hard @@ -1702,10 +1744,10 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:5.53.2": - version: 5.53.2 - resolution: "@tanstack/query-core@npm:5.53.2" - checksum: cac7dce3c1b31101e6caf33f6d75cdfa1a580193a6fa130e5402d21647ca623a0608cea529b0695df73f8ff141ce0d430010c651704d5103db69bd9666fc25f4 +"@tanstack/query-core@npm:5.56.2": + version: 5.56.2 + resolution: "@tanstack/query-core@npm:5.56.2" + checksum: e78430464de89fd2543155449391415983c7cdcd1ff2bb86da019414204b6800e2405e341db88000b36a101d3619ee7e94e90da1fa5497b2e021c5765924a64c languageName: node linkType: hard @@ -1758,13 +1800,13 @@ __metadata: linkType: hard "@tanstack/react-query@npm:^5.28.6": - version: 5.53.2 - resolution: "@tanstack/react-query@npm:5.53.2" + version: 5.56.2 + resolution: "@tanstack/react-query@npm:5.56.2" dependencies: - "@tanstack/query-core": 5.53.2 + "@tanstack/query-core": 5.56.2 peerDependencies: react: ^18 || ^19 - checksum: 8dab90fe514c4768f97a9a60cd0dacc671e0c4fda1b2a8f275a5200fc65b51ed80e10fe16d38b3dbb10659d80d21241c16a75b220a0fc2ee34cbfa11cd358081 + checksum: 7819a3a316c95df41844bd6e3435511594c3762320c21ee1ef2205ede18a87faae4556ff84d52b36ba17c99540b9554e72a5acf0c225481bdaf107ea79ace815 languageName: node linkType: hard @@ -1835,11 +1877,11 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.14.10 - resolution: "@types/node@npm:20.14.10" + version: 22.5.5 + resolution: "@types/node@npm:22.5.5" dependencies: - undici-types: ~5.26.4 - checksum: 2f397d393de8cddb126e0b7999402ea450215ac69d49666ddef4f730a73325054499ce7345f86095e7b935c55b2e02139f3b8b9afc72fb978ed29edf6bb956b0 + undici-types: ~6.19.2 + checksum: 1f788966ff7df07add0af3481fb68c7fe5091cc72a265c671432abb443788ddacca4ca6378af64fe100c20f857c4d80170d358e66c070171fcea0d4adb1b45b1 languageName: node linkType: hard @@ -1865,9 +1907,9 @@ __metadata: linkType: hard "@types/prop-types@npm:*": - version: 15.7.12 - resolution: "@types/prop-types@npm:15.7.12" - checksum: ac16cc3d0a84431ffa5cfdf89579ad1e2269549f32ce0c769321fdd078f84db4fbe1b461ed5a1a496caf09e637c0e367d600c541435716a55b1d9713f5035dfe + version: 15.7.13 + resolution: "@types/prop-types@npm:15.7.13" + checksum: 8935cad87c683c665d09a055919d617fe951cb3b2d5c00544e3a913f861a2bd8d2145b51c9aa6d2457d19f3107ab40784c40205e757232f6a80cc8b1c815513c languageName: node linkType: hard @@ -1890,12 +1932,12 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.0.9": - version: 18.3.3 - resolution: "@types/react@npm:18.3.3" + version: 18.3.7 + resolution: "@types/react@npm:18.3.7" dependencies: "@types/prop-types": "*" csstype: ^3.0.2 - checksum: c63d6a78163244e2022b01ef79b0baec4fe4da3475dc4a90bb8accefad35ef0c43560fd0312e5974f92a0f1108aa4d669ac72d73d66396aa060ea03b5d2e3873 + checksum: 027cf84d8309c4d0a9b16ec26f71de0950e2d748293bbc4dac42519f77d0bec099aeb5fb1c0bcb891725973e53085c1aedea5c3a16bca215c2fc2ecf68c7ec6e languageName: node linkType: hard @@ -1937,21 +1979,44 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/parser@npm:7.2.0" +"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": + version: 8.6.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.6.0" + dependencies: + "@eslint-community/regexpp": ^4.10.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/type-utils": 8.6.0 + "@typescript-eslint/utils": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 + graphemer: ^1.4.0 + ignore: ^5.3.1 + natural-compare: ^1.4.0 + ts-api-utils: ^1.3.0 + peerDependencies: + "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 6acab71c3066b86ba19b081c44b7060df4468d932813a94ad3b60f0f88b78b97f3555a0605814e32f8399737c0789e72cb509a6cf6d70e4823a7cc8769d06fa4 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": + version: 8.6.0 + resolution: "@typescript-eslint/parser@npm:8.6.0" dependencies: - "@typescript-eslint/scope-manager": 7.2.0 - "@typescript-eslint/types": 7.2.0 - "@typescript-eslint/typescript-estree": 7.2.0 - "@typescript-eslint/visitor-keys": 7.2.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 debug: ^4.3.4 peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 21deb2e7ad1fc730f637af08f5c549f30ef5b50f424639f57f5bc01274e648db47c696bb994bb24e87424b593d4084e306447c9431a0c0e4807952996db306f4 + checksum: d2e1c1ef4b908d2c028b6e1c72b42c0ae0d9f4dab0dea4ea8e0a36a194ec2171833e7bed36e55e0feadad3e06eef1c6da16168a3687d0e2182b80229dc994e2d languageName: node linkType: hard @@ -1965,13 +2030,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/scope-manager@npm:7.2.0" +"@typescript-eslint/scope-manager@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/scope-manager@npm:8.6.0" dependencies: - "@typescript-eslint/types": 7.2.0 - "@typescript-eslint/visitor-keys": 7.2.0 - checksum: b4ef8e35a56f590fa56cf769e111907828abb4793f482bf57e3fc8c987294ec119acb96359aa4b0150eea7416816e0b2d8635dccd1e4a5c2b02678b0f74def94 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 + checksum: d0a305c659eab02ad36265e77a1e30574a72a3e251b24c503537abd5b1dbe45a1db7d63dc73bdcc7fb4951f671cb5cbaedca1130490c764dd05f91e90c5cbbf9 languageName: node linkType: hard @@ -1992,6 +2057,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/type-utils@npm:8.6.0" + dependencies: + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/utils": 8.6.0 + debug: ^4.3.4 + ts-api-utils: ^1.3.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: d395745176cc13d96759e4ad7b698058f4fc24b62d0bd3fe603f49546f369cbf3e46fefbcc6069c33b1b0d825e033e0a5a972fd0b1a05f7ce9e8588154a02b93 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/types@npm:5.62.0" @@ -1999,10 +2079,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/types@npm:7.2.0" - checksum: 237acd24aa55b762ee98904e4f422ba86579325200dcd058b3cbfe70775926e7f00ee0295788d81eb728f3a6326fe4401c648aee9eb1480d9030a441c17520e8 +"@typescript-eslint/types@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/types@npm:8.6.0" + checksum: 5bf0078735b5d2804e1019ff17e9f221af3735fe7b9f4a77a41cba0998e77eebb2c152575bd45a264cb35d7a9db899799c1a10faa29f536c28a804420fb9f870 languageName: node linkType: hard @@ -2024,22 +2104,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.2.0" +"@typescript-eslint/typescript-estree@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.6.0" dependencies: - "@typescript-eslint/types": 7.2.0 - "@typescript-eslint/visitor-keys": 7.2.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 debug: ^4.3.4 - globby: ^11.1.0 + fast-glob: ^3.3.2 is-glob: ^4.0.3 - minimatch: 9.0.3 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + minimatch: ^9.0.4 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 peerDependenciesMeta: typescript: optional: true - checksum: fe882195cad45bb67e7e127efa9c31977348d0ca923ef26bb9fbd03a2ab64e6772e6e60954ba07a437684fae8e35897d71f0e6a1ef8fbf3f0025cd314960cd9d + checksum: 7a0e817b5c381f8937a8e4bf17df5ce43e1269ee150ee635cc8bb8867cb899fcca630eb8f6f1dfdd74ddd296741ac7e1e26ef6c9dc4f99cdcf49311956fbb385 languageName: node linkType: hard @@ -2061,6 +2141,20 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/utils@npm:8.6.0" + dependencies: + "@eslint-community/eslint-utils": ^4.4.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: dbb2efe47c291d36d5ec147f8c8fe62d27e9db2a3368aefd9019fd1e118bd1a54c8b13b990bb0941c9510bc4e2049b336e9a26d6414a6239c020e36baa8797e2 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" @@ -2071,13 +2165,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.2.0": - version: 7.2.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.2.0" +"@typescript-eslint/visitor-keys@npm:8.6.0": + version: 8.6.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.6.0" dependencies: - "@typescript-eslint/types": 7.2.0 - eslint-visitor-keys: ^3.4.1 - checksum: d9b11b52737450f213cea5c6e07e4672684da48325905c096ee09302b6b261c0bb226e1e350011bdf127c0cbbdd9e6474c905befdfa0a2118fc89ece16770f2b + "@typescript-eslint/types": 8.6.0 + eslint-visitor-keys: ^3.4.3 + checksum: de60bb42674818af46b85a94f668e93dc0432e8d7d94f0508dadab41181192fad2c2701ec3533d404e9bd40c8e92384fd7bcdc82fc45584b7323195ceaf32caf languageName: node linkType: hard @@ -2144,9 +2238,9 @@ __metadata: linkType: hard "@vanilla-extract/private@npm:^1.0.3": - version: 1.0.5 - resolution: "@vanilla-extract/private@npm:1.0.5" - checksum: 147acf9b1795f0681372db92e483bc27eeddad050b7d517e9ab87c5e9bcbdce69c0be300c4948f42e3bdeb81b8dd16b8243f3404ce74e6bc9acbb31112429ff4 + version: 1.0.6 + resolution: "@vanilla-extract/private@npm:1.0.6" + checksum: 2265b02af29d8cd40f6ddeeed197fb2df1a7695f5a9821d5e3597677179be8b83bcd8fe4df4a6178544f89123d745a3c6a13599d4fe4e5873b065a8ad329f690 languageName: node linkType: hard @@ -2514,11 +2608,11 @@ __metadata: linkType: hard "@walletconnect/relay-api@npm:^1.0.9": - version: 1.0.10 - resolution: "@walletconnect/relay-api@npm:1.0.10" + version: 1.0.11 + resolution: "@walletconnect/relay-api@npm:1.0.11" dependencies: "@walletconnect/jsonrpc-types": ^1.0.2 - checksum: a332cbfdf0d3bad7046b0559653a5121a4b5a540f029cc01eeb8ef466681b10626a5a24d55668405e7c635535f35b8038d4aa5a2f0d16c8b512c41fecff2448c + checksum: 9fcddf055de01c04b9fa59035e8c6e31d523743c848d266f528009048aeadaa1b4d9b544bdcb6928e7a69f738d5f0352d1cdebbaa34b1346b937942cb5f6f144 languageName: node linkType: hard @@ -2809,9 +2903,9 @@ __metadata: linkType: hard "ansi-regex@npm:^6.0.1": - version: 6.0.1 - resolution: "ansi-regex@npm:6.0.1" - checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + version: 6.1.0 + resolution: "ansi-regex@npm:6.1.0" + checksum: 495834a53b0856c02acd40446f7130cb0f8284f4a39afdab20d5dc42b2e198b1196119fe887beed8f9055c4ff2055e3b2f6d4641d0be018cdfb64fedf6fc1aac languageName: node linkType: hard @@ -2890,7 +2984,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.6, array-includes@npm:^3.1.7, array-includes@npm:^3.1.8": +"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8": version: 3.1.8 resolution: "array-includes@npm:3.1.8" dependencies: @@ -2925,7 +3019,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.3": +"array.prototype.findlastindex@npm:^1.2.5": version: 1.2.5 resolution: "array.prototype.findlastindex@npm:1.2.5" dependencies: @@ -2963,18 +3057,6 @@ __metadata: languageName: node linkType: hard -"array.prototype.toreversed@npm:^1.1.2": - version: 1.1.2 - resolution: "array.prototype.toreversed@npm:1.1.2" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - es-shim-unscopables: ^1.0.0 - checksum: 58598193426282155297bedf950dc8d464624a0d81659822fb73124286688644cb7e0e4927a07f3ab2daaeb6617b647736cc3a5e6ca7ade5bb8e573b284e6240 - languageName: node - linkType: hard - "array.prototype.tosorted@npm:^1.1.4": version: 1.1.4 resolution: "array.prototype.tosorted@npm:1.1.4" @@ -3028,20 +3110,20 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.12": - version: 10.4.19 - resolution: "autoprefixer@npm:10.4.19" + version: 10.4.20 + resolution: "autoprefixer@npm:10.4.20" dependencies: - browserslist: ^4.23.0 - caniuse-lite: ^1.0.30001599 + browserslist: ^4.23.3 + caniuse-lite: ^1.0.30001646 fraction.js: ^4.3.7 normalize-range: ^0.1.2 - picocolors: ^1.0.0 + picocolors: ^1.0.1 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 3a4bc5bace05e057396dca2b306503efc175e90e8f2abf5472d3130b72da1d54d97c0ee05df21bf04fe66a7df93fd8c8ec0f1aca72a165f4701a02531abcbf11 + checksum: 187cec2ec356631932b212f76dc64f4419c117fdb2fb9eeeb40867d38ba5ca5ba734e6ceefc9e3af4eec8258e60accdf5cbf2b7708798598fde35cdc3de562d6 languageName: node linkType: hard @@ -3054,19 +3136,17 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:^4.9.1": - version: 4.9.1 - resolution: "axe-core@npm:4.9.1" - checksum: 41d9227871781f96c2952e2a777fca73624959dd0e98864f6d82806a77602f82b4fc490852082a7e524d8cd864e50d8b4d9931819b4a150112981d8c932110c5 +"axe-core@npm:^4.10.0": + version: 4.10.0 + resolution: "axe-core@npm:4.10.0" + checksum: 7eca827fd8d98d7e4b561df65437be608155c613d8f262ae9e4a6ade02c156c7362dcbc3f71b4b526edce686f7c686280236bcff1d6725e2ef8327def72a8c41 languageName: node linkType: hard -"axobject-query@npm:~3.1.1": - version: 3.1.1 - resolution: "axobject-query@npm:3.1.1" - dependencies: - deep-equal: ^2.0.5 - checksum: c12a5da10dc7bab75e1cda9b6a3b5fcf10eba426ddf1a17b71ef65a434ed707ede7d1c4f013ba1609e970bc8c0cddac01365080d376204314e9b294719acd8a5 +"axobject-query@npm:^4.1.0": + version: 4.1.0 + resolution: "axobject-query@npm:4.1.0" + checksum: 7d1e87bf0aa7ae7a76cd39ab627b7c48fda3dc40181303d9adce4ba1d5b5ce73b5e5403ee6626ec8e91090448c887294d6144e24b6741a976f5be9347e3ae1df languageName: node linkType: hard @@ -3161,17 +3241,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.0": - version: 4.23.1 - resolution: "browserslist@npm:4.23.1" +"browserslist@npm:^4.23.3": + version: 4.23.3 + resolution: "browserslist@npm:4.23.3" dependencies: - caniuse-lite: ^1.0.30001629 - electron-to-chromium: ^1.4.796 - node-releases: ^2.0.14 - update-browserslist-db: ^1.0.16 + caniuse-lite: ^1.0.30001646 + electron-to-chromium: ^1.5.4 + node-releases: ^2.0.18 + update-browserslist-db: ^1.1.0 bin: browserslist: cli.js - checksum: 06189e2d6666a203ce097cc0e713a40477d08420927b79af139211e5712f3cf676fdc4dd6af3aa493d47c09206a344b3420a8315577dbe88c58903132de9b0f5 + checksum: 7906064f9970aeb941310b2fcb8b4ace4a1b50aa657c986677c6f1553a8cabcc94ee9c5922f715baffbedaa0e6cf0831b6fed7b059dde6873a4bfadcbe069c7e languageName: node linkType: hard @@ -3195,8 +3275,8 @@ __metadata: linkType: hard "cacache@npm:^18.0.0": - version: 18.0.3 - resolution: "cacache@npm:18.0.3" + version: 18.0.4 + resolution: "cacache@npm:18.0.4" dependencies: "@npmcli/fs": ^3.1.0 fs-minipass: ^3.0.0 @@ -3210,7 +3290,7 @@ __metadata: ssri: ^10.0.0 tar: ^6.1.11 unique-filename: ^3.0.0 - checksum: b717fd9b36e9c3279bfde4545c3a8f6d5a539b084ee26a9504d48f83694beb724057d26e090b97540f9cc62bea18b9f6cf671c50e18fb7dac60eda9db691714f + checksum: b7422c113b4ec750f33beeca0f426a0024c28e3172f332218f48f963e5b970647fa1ac05679fe5bb448832c51efea9fda4456b9a95c3a1af1105fe6c1833cde2 languageName: node linkType: hard @@ -3248,10 +3328,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001629": - version: 1.0.30001640 - resolution: "caniuse-lite@npm:1.0.30001640" - checksum: ec492d8d1e11d1c55e0f5c0f218229369dc0a4bd1b5d0a579a6435865fe8f4c84bde7e816a844cce1b9cdd97f5a85b6dac5599639fabcdb0c4c5bd039e46cbfd +"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001646": + version: 1.0.30001662 + resolution: "caniuse-lite@npm:1.0.30001662" + checksum: 7a6a0c0d9f7c4a1c51de02838eb47f41f36fff57a77b846c8faed35ba9afba17b9399bc00bd637e5c1663cbc132534085d91151de48edca2ad8932a5d87e23af languageName: node linkType: hard @@ -3426,6 +3506,13 @@ __metadata: languageName: node linkType: hard +"command-exists@npm:^1.2.8": + version: 1.2.9 + resolution: "command-exists@npm:1.2.9" + checksum: 729ae3d88a2058c93c58840f30341b7f82688a573019535d198b57a4d8cb0135ced0ad7f52b591e5b28a90feb2c675080ce916e56254a0f7c15cb2395277cac3 + languageName: node + linkType: hard + "commander@npm:11.0.0": version: 11.0.0 resolution: "commander@npm:11.0.0" @@ -3440,6 +3527,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^8.1.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -3462,9 +3556,9 @@ __metadata: linkType: hard "cookie-es@npm:^1.1.0": - version: 1.1.0 - resolution: "cookie-es@npm:1.1.0" - checksum: 953ee436e9daeb8f93e36f726e4ad15fd20fa8181c4085198db9e617a5dbd200326376d84c2dac7364c4395bcfb2b314017822bfba3fef44d24258b0ac90e639 + version: 1.2.2 + resolution: "cookie-es@npm:1.2.2" + checksum: 099050c30c967c89aa72d1d7984e87b3395f3e709cf148d297f436828ebfcc39033f5374d2efdc46d9b5e3eee50b1d59635432c252e57329fea7f09afeb4d055 languageName: node linkType: hard @@ -3610,15 +3704,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": - version: 4.3.5 - resolution: "debug@npm:4.3.5" +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5": + version: 4.3.7 + resolution: "debug@npm:4.3.7" dependencies: - ms: 2.1.2 + ms: ^2.1.3 peerDependenciesMeta: supports-color: optional: true - checksum: 7c002b51e256257f936dda09eb37167df952758c57badf6bf44bdc40b89a4bcb8e5a0a2e4c7b53f97c69e2970dd5272d33a757378a12c8f8e64ea7bf99e8e86e + checksum: 822d74e209cd910ef0802d261b150314bbcf36c582ccdbb3e70f0894823c17e49a50d3e66d96b633524263975ca16b6a833f3e3b7e030c157169a5fabac63160 languageName: node linkType: hard @@ -3733,7 +3827,7 @@ __metadata: languageName: node linkType: hard -"defu@npm:^6.1.3, defu@npm:^6.1.4": +"defu@npm:^6.1.4": version: 6.1.4 resolution: "defu@npm:6.1.4" checksum: 40e3af6338f195ac1564f53d1887fa2d0429ac7e8c081204bc4d29191180059d3952b5f4e08fe5df8d59eb873aa26e9c88b56d4fac699673d4a372c93620b229 @@ -3844,10 +3938,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.796": - version: 1.4.818 - resolution: "electron-to-chromium@npm:1.4.818" - checksum: 7ff9b1f0a17c013ff020530d35a002a6e42f6cb8207be26d25796615d7099da409351a8ee2fde0e796369251906bdbb7d32eec5dbe413547037ea0436d6287d7 +"electron-to-chromium@npm:^1.5.4": + version: 1.5.25 + resolution: "electron-to-chromium@npm:1.5.25" + checksum: 3aef974d586815e9b1fd3221be3e2e124d59a9b992dbd59dbce618ac165feddebda9238be8f93b504f3cc067821e94810bf8a877be8a23a125d6c8f401aeb27e languageName: node linkType: hard @@ -3905,13 +3999,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.12.0": - version: 5.17.0 - resolution: "enhanced-resolve@npm:5.17.0" +"enhanced-resolve@npm:^5.15.0": + version: 5.17.1 + resolution: "enhanced-resolve@npm:5.17.1" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 1066000454da6a7aeabdbe1f433d912d1e39e6892142a78a37b6577aab27e0436091fa1399d857ad87085b1c3b73a0f811c8874da3dbdc40fbd5ebe89a5568e6 + checksum: 4bc38cf1cea96456f97503db7280394177d1bc46f8f87c267297d04f795ac5efa81e48115a2f5b6273c781027b5b6bfc5f62b54df629e4d25fa7001a86624f59 languageName: node linkType: hard @@ -4088,9 +4182,9 @@ __metadata: linkType: hard "escalade@npm:^3.1.2": - version: 3.1.2 - resolution: "escalade@npm:3.1.2" - checksum: 1ec0977aa2772075493002bdbd549d595ff6e9393b1cb0d7d6fcaf78c750da0c158f180938365486f75cb69fba20294351caddfce1b46552a7b6c3cde52eaa02 + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 47b029c83de01b0d17ad99ed766347b974b0d628e848de404018f3abee728e987da0d2d370ad4574aa3d5b5bfc368754fd085d69a30f8e75903486ec4b5b709e languageName: node linkType: hard @@ -4109,12 +4203,13 @@ __metadata: linkType: hard "eslint-config-next@npm:^14.0.4": - version: 14.2.4 - resolution: "eslint-config-next@npm:14.2.4" + version: 14.2.12 + resolution: "eslint-config-next@npm:14.2.12" dependencies: - "@next/eslint-plugin-next": 14.2.4 + "@next/eslint-plugin-next": 14.2.12 "@rushstack/eslint-patch": ^1.3.3 - "@typescript-eslint/parser": ^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0 + "@typescript-eslint/eslint-plugin": ^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0 + "@typescript-eslint/parser": ^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0 eslint-import-resolver-node: ^0.3.6 eslint-import-resolver-typescript: ^3.5.2 eslint-plugin-import: ^2.28.1 @@ -4127,7 +4222,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 408ab113ecfdfa2de376ef9d23445cb80b66090ef3ab69b5ff4e5d14e83e85f9671a2edb05c8fced9555f6f3602ab0095579facef67ea50dba066a2156d201da + checksum: 7cf61314b0f58b0098c151ca428c527e1ab75bcc557a3b85cbb7b90d5a5518697efd2b367cfa858a5161874785879ff7060f3dd39af724fbf100c3c2e01f9b1c languageName: node linkType: hard @@ -4154,72 +4249,80 @@ __metadata: linkType: hard "eslint-import-resolver-typescript@npm:^3.5.2": - version: 3.6.1 - resolution: "eslint-import-resolver-typescript@npm:3.6.1" - dependencies: - debug: ^4.3.4 - enhanced-resolve: ^5.12.0 - eslint-module-utils: ^2.7.4 - fast-glob: ^3.3.1 - get-tsconfig: ^4.5.0 - is-core-module: ^2.11.0 + version: 3.6.3 + resolution: "eslint-import-resolver-typescript@npm:3.6.3" + dependencies: + "@nolyfill/is-core-module": 1.0.39 + debug: ^4.3.5 + enhanced-resolve: ^5.15.0 + eslint-module-utils: ^2.8.1 + fast-glob: ^3.3.2 + get-tsconfig: ^4.7.5 + is-bun-module: ^1.0.2 is-glob: ^4.0.3 peerDependencies: eslint: "*" eslint-plugin-import: "*" - checksum: 454fa0646533050fb57f13d27daf8c71f51b0bb9156d6a461290ccb8576d892209fcc6702a89553f3f5ea8e5b407395ca2e5de169a952c953685f1f7c46b4496 + eslint-plugin-import-x: "*" + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + checksum: 1ed0cab4f3852de1b14ea6978e76c27694b253a289c2030a35847ba8ab6ac4258d513877f83ea7bc265f746d570240a6348b11d77cc9cd77589749ad86a32234 languageName: node linkType: hard -"eslint-module-utils@npm:^2.7.4, eslint-module-utils@npm:^2.8.0": - version: 2.8.1 - resolution: "eslint-module-utils@npm:2.8.1" +"eslint-module-utils@npm:^2.8.1, eslint-module-utils@npm:^2.9.0": + version: 2.11.0 + resolution: "eslint-module-utils@npm:2.11.0" dependencies: debug: ^3.2.7 peerDependenciesMeta: eslint: optional: true - checksum: 3cecd99b6baf45ffc269167da0f95dcb75e5aa67b93d73a3bab63e2a7eedd9cdd6f188eed048e2f57c1b77db82c9cbf2adac20b512fa70e597d863dd3720170d + checksum: 8c2ecff3484835e031c8f1aa44119be65a058d195cce7b3ac827ad7ccc8bb5f9bcdd85230e2e3398981d07789bf4d90f3b81d106e67faf3cd26e0b34d73093af languageName: node linkType: hard "eslint-plugin-import@npm:^2.28.1": - version: 2.29.1 - resolution: "eslint-plugin-import@npm:2.29.1" + version: 2.30.0 + resolution: "eslint-plugin-import@npm:2.30.0" dependencies: - array-includes: ^3.1.7 - array.prototype.findlastindex: ^1.2.3 + "@rtsao/scc": ^1.1.0 + array-includes: ^3.1.8 + array.prototype.findlastindex: ^1.2.5 array.prototype.flat: ^1.3.2 array.prototype.flatmap: ^1.3.2 debug: ^3.2.7 doctrine: ^2.1.0 eslint-import-resolver-node: ^0.3.9 - eslint-module-utils: ^2.8.0 - hasown: ^2.0.0 - is-core-module: ^2.13.1 + eslint-module-utils: ^2.9.0 + hasown: ^2.0.2 + is-core-module: ^2.15.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.7 - object.groupby: ^1.0.1 - object.values: ^1.1.7 + object.fromentries: ^2.0.8 + object.groupby: ^1.0.3 + object.values: ^1.2.0 semver: ^6.3.1 tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c + checksum: 0ec1ad69c0d22f15bc4a49ee97ae757e4adfc3181996f2c4a1ed4d5028bd99bab38e7623e58ef4477ba1db8425f441e4e986367125273efa4c5f7ad2c4467a9a languageName: node linkType: hard "eslint-plugin-jsx-a11y@npm:^6.7.1": - version: 6.9.0 - resolution: "eslint-plugin-jsx-a11y@npm:6.9.0" + version: 6.10.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.10.0" dependencies: aria-query: ~5.1.3 array-includes: ^3.1.8 array.prototype.flatmap: ^1.3.2 ast-types-flow: ^0.0.8 - axe-core: ^4.9.1 - axobject-query: ~3.1.1 + axe-core: ^4.10.0 + axobject-query: ^4.1.0 damerau-levenshtein: ^1.0.8 emoji-regex: ^9.2.2 es-iterator-helpers: ^1.0.19 @@ -4231,8 +4334,8 @@ __metadata: safe-regex-test: ^1.0.3 string.prototype.includes: ^2.0.0 peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 122cbd22bbd8c3e4a37f386ec183ada63a4ecfa7af7d40cd8a110777ac5ad5ff542f60644596a9e2582ed138a1cc6d96c5d5ca934105e29d5245d6c951ebc3ef + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + checksum: 1009deca12ddbe3624586bc5fc3534ca98d00a5841a2563cb6abd9339b984f0a99075dc2a703a517f4087eb84d659c87e60beda17645883de2ba1d86f2b20c96 languageName: node linkType: hard @@ -4261,30 +4364,30 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.33.2": - version: 7.34.3 - resolution: "eslint-plugin-react@npm:7.34.3" + version: 7.36.1 + resolution: "eslint-plugin-react@npm:7.36.1" dependencies: array-includes: ^3.1.8 array.prototype.findlast: ^1.2.5 array.prototype.flatmap: ^1.3.2 - array.prototype.toreversed: ^1.1.2 array.prototype.tosorted: ^1.1.4 doctrine: ^2.1.0 es-iterator-helpers: ^1.0.19 estraverse: ^5.3.0 + hasown: ^2.0.2 jsx-ast-utils: ^2.4.1 || ^3.0.0 minimatch: ^3.1.2 object.entries: ^1.1.8 object.fromentries: ^2.0.8 - object.hasown: ^1.1.4 object.values: ^1.2.0 prop-types: ^15.8.1 resolve: ^2.0.0-next.5 semver: ^6.3.1 string.prototype.matchall: ^4.0.11 + string.prototype.repeat: ^1.0.0 peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 1a519b9792ab9392a5157f2543ce98ab1218c62f4a31c4c3ceb5dd3e7997def4aa07ab39f7276af0fe116ef002db29d97216a15b7aa3b200e55b641cf77d6292 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + checksum: bf3be414f3d639200a7d91feeaa6beec3397feed93ab22eaecef44dda37ecbd01812ed1720c72a9861fb276d3543cea69a834a66f64de3d878796fef4f4bf129 languageName: node linkType: hard @@ -4316,14 +4419,14 @@ __metadata: linkType: hard "eslint@npm:^8.15.0": - version: 8.57.0 - resolution: "eslint@npm:8.57.0" + version: 8.57.1 + resolution: "eslint@npm:8.57.1" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 "@eslint/eslintrc": ^2.1.4 - "@eslint/js": 8.57.0 - "@humanwhocodes/config-array": ^0.11.14 + "@eslint/js": 8.57.1 + "@humanwhocodes/config-array": ^0.13.0 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 "@ungap/structured-clone": ^1.2.0 @@ -4359,7 +4462,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 3a48d7ff85ab420a8447e9810d8087aea5b1df9ef68c9151732b478de698389ee656fd895635b5f2871c89ee5a2652b3f343d11e9db6f8486880374ebc74a2d9 + checksum: e2489bb7f86dd2011967759a09164e65744ef7688c310bc990612fc26953f34cc391872807486b15c06833bdff737726a23e9b4cdba5de144c311377dc41d91b languageName: node linkType: hard @@ -4375,11 +4478,11 @@ __metadata: linkType: hard "esquery@npm:^1.4.2": - version: 1.5.0 - resolution: "esquery@npm:1.5.0" + version: 1.6.0 + resolution: "esquery@npm:1.6.0" dependencies: estraverse: ^5.1.0 - checksum: aefb0d2596c230118656cd4ec7532d447333a410a48834d80ea648b1e7b5c9bc9ed8b5e33a89cb04e487b60d622f44cf5713bf4abed7c97343edefdc84a35900 + checksum: 08ec4fe446d9ab27186da274d979558557fbdbbd10968fa9758552482720c54152a5640e08b9009e5a30706b66aba510692054d4129d32d0e12e05bbc0b96fb2 languageName: node linkType: hard @@ -4584,7 +4687,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.1": +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -4704,6 +4807,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.12.1": + version: 1.15.9 + resolution: "follow-redirects@npm:1.15.9" + peerDependenciesMeta: + debug: + optional: true + checksum: 859e2bacc7a54506f2bf9aacb10d165df78c8c1b0ceb8023f966621b233717dab56e8d08baadc3ad3b9db58af290413d585c999694b7c146aaf2616340c3d2a6 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -4714,19 +4827,12 @@ __metadata: linkType: hard "foreground-child@npm:^3.1.0": - version: 3.2.1 - resolution: "foreground-child@npm:3.2.1" + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" dependencies: cross-spawn: ^7.0.0 signal-exit: ^4.0.1 - checksum: 3e2e844d6003c96d70affe8ae98d7eaaba269a868c14d997620c088340a8775cd5d2d9043e6ceebae1928d8d9a874911c4d664b9a267e8995945df20337aebc0 - languageName: node - linkType: hard - -"forge-gas-snapshot@https://github.com/ylv-io/forge-gas-snapshot": - version: 0.1.4 - resolution: "forge-gas-snapshot@https://github.com/ylv-io/forge-gas-snapshot.git#commit=ee8e1f02009785ab81a5058a18c75d5d3b7a894d" - checksum: 3be1581ae11f159e86cacd8f156813f3fadb4d341e97bee1cbfd3c0caef058cc023e376cfc91353aafedb1fa4397c9e0d957428ab82bc7ab3bd90e1d41f83724 + checksum: 1989698488f725b05b26bc9afc8a08f08ec41807cd7b92ad85d96004ddf8243fd3e79486b8348c64a3011ae5cc2c9f0936af989e1f28339805d8bc178a75b451 languageName: node linkType: hard @@ -4788,7 +4894,7 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.5, function.prototype.name@npm:^1.1.6": +"function.prototype.name@npm:^1.1.6": version: 1.1.6 resolution: "function.prototype.name@npm:1.1.6" dependencies: @@ -4866,12 +4972,12 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.5.0": - version: 4.7.5 - resolution: "get-tsconfig@npm:4.7.5" +"get-tsconfig@npm:^4.7.5": + version: 4.8.1 + resolution: "get-tsconfig@npm:4.8.1" dependencies: resolve-pkg-maps: ^1.0.0 - checksum: e5b271fae2b4cd1869bbfc58db56983026cc4a08fdba988725a6edd55d04101507de154722503a22ee35920898ff9bdcba71f99d93b17df35dddb8e8a2ad91be + checksum: 12df01672e691d2ff6db8cf7fed1ddfef90ed94a5f3d822c63c147a26742026d582acd86afcd6f65db67d809625d17dd7f9d34f4d3f38f69bc2f48e19b2bdd5b languageName: node linkType: hard @@ -4909,8 +5015,8 @@ __metadata: linkType: hard "glob@npm:^10.2.2, glob@npm:^10.3.10": - version: 10.4.3 - resolution: "glob@npm:10.4.3" + version: 10.4.5 + resolution: "glob@npm:10.4.5" dependencies: foreground-child: ^3.1.0 jackspeak: ^3.1.2 @@ -4920,7 +5026,7 @@ __metadata: path-scurry: ^1.11.1 bin: glob: dist/esm/bin.mjs - checksum: a1daeb570b841480fe95b3dd9492a98a58759186d14cf2ebe81057c3f308b47980d5e757b533422dbc5288ebf7b06b169960ce9f71198d7dbe320bc5068f89f0 + checksum: 0bc725de5e4862f9f387fd0f2b274baf16850dcd2714502ccf471ee401803997983e2c05590cb65f9675a3c6f2a58e7a53f9e365704108c6ad3cbf1d60934c4a languageName: node linkType: hard @@ -5017,7 +5123,7 @@ __metadata: languageName: node linkType: hard -"h3@npm:^1.10.2, h3@npm:^1.11.1": +"h3@npm:^1.10.2, h3@npm:^1.12.0": version: 1.12.0 resolution: "h3@npm:1.12.0" dependencies: @@ -5205,10 +5311,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0": - version: 5.3.1 - resolution: "ignore@npm:5.3.1" - checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 +"ignore@npm:^5.2.0, ignore@npm:^5.3.1": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be languageName: node linkType: hard @@ -5347,6 +5453,15 @@ __metadata: languageName: node linkType: hard +"is-bun-module@npm:^1.0.2": + version: 1.2.1 + resolution: "is-bun-module@npm:1.2.1" + dependencies: + semver: ^7.6.3 + checksum: 1c2cbcf1a76991add1b640d2d7fe09848e8697a76f96e1289dff44133a48c97f5dc601d4a66d3f3a86217a77178d72d33d10d0c9e14194e58e70ec8df3eae41a + languageName: node + linkType: hard + "is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": version: 1.2.7 resolution: "is-callable@npm:1.2.7" @@ -5354,12 +5469,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": - version: 2.14.0 - resolution: "is-core-module@npm:2.14.0" +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1": + version: 2.15.1 + resolution: "is-core-module@npm:2.15.1" dependencies: hasown: ^2.0.2 - checksum: 6bba6c8dc99d88d6f3b2746709d82caddcd9565cafd5870e28ab320720e27e6d9d2bb953ba0839ed4d2ee264bfdd14a9fa1bbc242a916f7dacc8aa95f0322256 + checksum: df134c168115690724b62018c37b2f5bba0d5745fa16960b329c5a00883a8bea6a5632fdb1e3efcce237c201826ba09f93197b7cd95577ea56b0df335be23633 languageName: node linkType: hard @@ -5670,15 +5785,15 @@ __metadata: linkType: hard "jackspeak@npm:^3.1.2": - version: 3.4.1 - resolution: "jackspeak@npm:3.4.1" + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" dependencies: "@isaacs/cliui": ^8.0.2 "@pkgjs/parseargs": ^0.11.0 dependenciesMeta: "@pkgjs/parseargs": optional: true - checksum: 772483c6835f01b4e06eaaf3cf7fa65a0d5e0f2fd3b7290f657df4602b013c60efa53bebd64489862bc7a290167e7671bb41a990512c91e25d2e89c82b2628b8 + checksum: be31027fc72e7cc726206b9f560395604b82e0fddb46c4cbf9f97d049bcef607491a5afc0699612eaa4213ca5be8fd3e1e7cd187b3040988b65c9489838a7c00 languageName: node linkType: hard @@ -6058,10 +6173,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": - version: 10.3.1 - resolution: "lru-cache@npm:10.3.1" - checksum: b7953350f61587102e27cbd298a6576fb32da510407f9ee92070f796f8c026c509cd41efc47cbf42f8f3986712b7a3f7f5fa6337e52ee3d7a8089b859da78523 +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.4.3": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 6476138d2125387a6d20f100608c2583d415a4f64a0fecf30c9e2dda976614f09cad4baa0842447bd37dd459a7bd27f57d9d8f8ce558805abd487c583f3d774a languageName: node linkType: hard @@ -6094,6 +6209,13 @@ __metadata: languageName: node linkType: hard +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: f18b42440d24d09516d01466c06adf797df7873f0d40aa7db02e5fb9ed83074e5e65412d0720901d7069363465f82dc4f8bcb44f0cde271567a61426ce6ca2e9 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -6126,12 +6248,12 @@ __metadata: linkType: hard "micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": - version: 4.0.7 - resolution: "micromatch@npm:4.0.7" + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: braces: ^3.0.3 picomatch: ^2.3.1 - checksum: 3cde047d70ad80cf60c787b77198d680db3b8c25b23feb01de5e2652205d9c19f43bd81882f69a0fd1f0cde6a7a122d774998aad3271ddb1b8accf8a0f480cf7 + checksum: 79920eb634e6f400b464a954fcfa589c4e7c7143209488e44baf627f9affc8b1e306f41f4f0deedde97e69cb725920879462d3e750ab3bd3c1aed675bb3a8966 languageName: node linkType: hard @@ -6172,15 +6294,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: ^2.0.1 - checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 - languageName: node - linkType: hard - "minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -6346,7 +6459,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.1.1": +"ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -6381,9 +6494,9 @@ __metadata: linkType: hard "napi-wasm@npm:^1.1.0": - version: 1.1.0 - resolution: "napi-wasm@npm:1.1.0" - checksum: 649a5d03477b89ee75cd8d7be5404daa5c889915640fd4ab042f2d38d265e961f86933e83982388d72c8b0a3952f36f099b96598ea88210205519ec2adc41d8d + version: 1.1.3 + resolution: "napi-wasm@npm:1.1.3" + checksum: c02424b9e26f152ea1224bdf950d09292ab5f2069644d878c96aa416316f05ba58ae9a6f39f664c592b523e6f39b6b0b831a5987b10e26ce2154da3b4f2b7859 languageName: node linkType: hard @@ -6420,19 +6533,19 @@ __metadata: linkType: hard "next@npm:^14.0.4": - version: 14.2.4 - resolution: "next@npm:14.2.4" - dependencies: - "@next/env": 14.2.4 - "@next/swc-darwin-arm64": 14.2.4 - "@next/swc-darwin-x64": 14.2.4 - "@next/swc-linux-arm64-gnu": 14.2.4 - "@next/swc-linux-arm64-musl": 14.2.4 - "@next/swc-linux-x64-gnu": 14.2.4 - "@next/swc-linux-x64-musl": 14.2.4 - "@next/swc-win32-arm64-msvc": 14.2.4 - "@next/swc-win32-ia32-msvc": 14.2.4 - "@next/swc-win32-x64-msvc": 14.2.4 + version: 14.2.12 + resolution: "next@npm:14.2.12" + dependencies: + "@next/env": 14.2.12 + "@next/swc-darwin-arm64": 14.2.12 + "@next/swc-darwin-x64": 14.2.12 + "@next/swc-linux-arm64-gnu": 14.2.12 + "@next/swc-linux-arm64-musl": 14.2.12 + "@next/swc-linux-x64-gnu": 14.2.12 + "@next/swc-linux-x64-musl": 14.2.12 + "@next/swc-win32-arm64-msvc": 14.2.12 + "@next/swc-win32-ia32-msvc": 14.2.12 + "@next/swc-win32-x64-msvc": 14.2.12 "@swc/helpers": 0.5.5 busboy: 1.6.0 caniuse-lite: ^1.0.30001579 @@ -6473,7 +6586,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 3b858cfec2e061d811811921361855659b09424ea4178cf0f4a0bbe5d3978b45da6f04575fe213d76e47f626439db61591b79932f37ff984b7b7de87dd1ccce0 + checksum: 59845b824e36a97ca8aeaed81fc035643eaa85a3851fc04c277030fa197ecf4ad00a41136db4a41ae4f608b7ae907200ce8b03310050b9128f5aea452b818224 languageName: node linkType: hard @@ -6487,15 +6600,15 @@ __metadata: linkType: hard "node-addon-api@npm:^7.0.0": - version: 7.1.0 - resolution: "node-addon-api@npm:7.1.0" + version: 7.1.1 + resolution: "node-addon-api@npm:7.1.1" dependencies: node-gyp: latest - checksum: 26640c8d2ed7e2059e2ed65ee79e2a195306b3f1fc27ad11448943ba91d37767bd717a9a0453cc97e83a1109194dced8336a55f8650000458ef625c0b8b5e3df + checksum: 46051999e3289f205799dfaf6bcb017055d7569090f0004811110312e2db94cb4f8654602c7eb77a60a1a05142cc2b96e1b5c56ca4622c41a5c6370787faaf30 languageName: node linkType: hard -"node-fetch-native@npm:^1.6.1, node-fetch-native@npm:^1.6.2, node-fetch-native@npm:^1.6.3": +"node-fetch-native@npm:^1.6.3, node-fetch-native@npm:^1.6.4": version: 1.6.4 resolution: "node-fetch-native@npm:1.6.4" checksum: 7b159f610e037e8813750096a6616ec6771e9abf868aa6e75e5b790bfc2ba2d92cf2abcce33c18fd01f2e5e5cc72de09c78bd4381e7f8c0887f7de21bd96f045 @@ -6524,19 +6637,19 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0": - version: 4.8.1 - resolution: "node-gyp-build@npm:4.8.1" + version: 4.8.2 + resolution: "node-gyp-build@npm:4.8.2" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: fe6e95da6f4608c1a98655f6bf2fe4e8dd9c877cd13256056a8acaf585cc7f98718823fe9366be11b78c2f332d5a184b00cf07a4af96c9d8fea45f640c019f98 + checksum: 1a57bba8c4c193f808bd8ad1484d4ebdd8106dd9f04a3e82554dc716e3a2d87d7e369e9503c145e0e6a7e2c663fec0d8aaf52bd8156342ec7fc388195f37824e languageName: node linkType: hard "node-gyp@npm:latest": - version: 10.1.0 - resolution: "node-gyp@npm:10.1.0" + version: 10.2.0 + resolution: "node-gyp@npm:10.2.0" dependencies: env-paths: ^2.2.0 exponential-backoff: ^3.1.1 @@ -6544,20 +6657,20 @@ __metadata: graceful-fs: ^4.2.6 make-fetch-happen: ^13.0.0 nopt: ^7.0.0 - proc-log: ^3.0.0 + proc-log: ^4.1.0 semver: ^7.3.5 - tar: ^6.1.2 + tar: ^6.2.1 which: ^4.0.0 bin: node-gyp: bin/node-gyp.js - checksum: 72e2ab4b23fc32007a763da94018f58069fc0694bf36115d49a2b195c8831e12cf5dd1e7a3718fa85c06969aedf8fc126722d3b672ec1cb27e06ed33caee3c60 + checksum: 0233759d8c19765f7fdc259a35eb046ad86c3d09e22f7384613ae2b89647dd27fcf833fdf5293d9335041e91f9b1c539494225959cdb312a5c8080b7534b926f languageName: node linkType: hard -"node-releases@npm:^2.0.14": - version: 2.0.14 - resolution: "node-releases@npm:2.0.14" - checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 +"node-releases@npm:^2.0.18": + version: 2.0.18 + resolution: "node-releases@npm:2.0.18" + checksum: ef55a3d853e1269a6d6279b7692cd6ff3e40bc74947945101138745bfdc9a5edabfe72cb19a31a8e45752e1910c4c65c77d931866af6357f242b172b7283f5b3 languageName: node linkType: hard @@ -6663,7 +6776,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.7, object.fromentries@npm:^2.0.8": +"object.fromentries@npm:^2.0.8": version: 2.0.8 resolution: "object.fromentries@npm:2.0.8" dependencies: @@ -6675,7 +6788,7 @@ __metadata: languageName: node linkType: hard -"object.groupby@npm:^1.0.1": +"object.groupby@npm:^1.0.3": version: 1.0.3 resolution: "object.groupby@npm:1.0.3" dependencies: @@ -6686,18 +6799,7 @@ __metadata: languageName: node linkType: hard -"object.hasown@npm:^1.1.4": - version: 1.1.4 - resolution: "object.hasown@npm:1.1.4" - dependencies: - define-properties: ^1.2.1 - es-abstract: ^1.23.2 - es-object-atoms: ^1.0.0 - checksum: bc46eb5ca22106fcd07aab1411508c2c68b7565fe8fb272f166fb9bf203972e8b5c86a5a4b2c86204beead0626a7a4119d32cefbaf7c5dd57b400bf9e6363cb6 - languageName: node - linkType: hard - -"object.values@npm:^1.1.6, object.values@npm:^1.1.7, object.values@npm:^1.2.0": +"object.values@npm:^1.1.6, object.values@npm:^1.2.0": version: 1.2.0 resolution: "object.values@npm:1.2.0" dependencies: @@ -6708,7 +6810,7 @@ __metadata: languageName: node linkType: hard -"ofetch@npm:^1.3.3": +"ofetch@npm:^1.3.4": version: 1.3.4 resolution: "ofetch@npm:1.3.4" dependencies: @@ -6720,9 +6822,9 @@ __metadata: linkType: hard "ohash@npm:^1.1.3": - version: 1.1.3 - resolution: "ohash@npm:1.1.3" - checksum: 44c7321cb950ce6e87d46584fd5cc8dd3dd15fcd4ade0ac2995d0497dc6b6b1ae9bd844c59af185d63923da5cfe9b37ae37a9dbd9ac455f3ad0cdfb5a73d5ef6 + version: 1.1.4 + resolution: "ohash@npm:1.1.4" + checksum: 8c63897941e67129ac81a15cfc2bb66a7b122200c9ee244e86d3d6b7aa7f5d9f7cb98d33dfc38b169c83b77c9babcc6f66ccbc90864d1f862f10ac8b72d80d66 languageName: node linkType: hard @@ -6774,6 +6876,13 @@ __metadata: languageName: node linkType: hard +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d + languageName: node + linkType: hard + "outdent@npm:^0.8.0": version: 0.8.0 resolution: "outdent@npm:0.8.0" @@ -6908,10 +7017,10 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1, picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": - version: 1.0.1 - resolution: "picocolors@npm:1.0.1" - checksum: fa68166d1f56009fc02a34cdfd112b0dd3cf1ef57667ac57281f714065558c01828cdf4f18600ad6851cbe0093952ed0660b1e0156bddf2184b6aaf5817553a5 +"picocolors@npm:^1, picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0": + version: 1.1.0 + resolution: "picocolors@npm:1.1.0" + checksum: a64d653d3a188119ff45781dfcdaeedd7625583f45280aea33fcb032c7a0d3959f2368f9b192ad5e8aade75b74dbd954ffe3106c158509a45e4c18ab379a2acd languageName: node linkType: hard @@ -6998,13 +7107,13 @@ __metadata: linkType: hard "pkg-types@npm:^1.1.1": - version: 1.1.3 - resolution: "pkg-types@npm:1.1.3" + version: 1.2.0 + resolution: "pkg-types@npm:1.2.0" dependencies: confbox: ^0.1.7 mlly: ^1.7.1 pathe: ^1.1.2 - checksum: 1085f1ed650db71d62ec9201d0ad4dc9455962b0e40d309e26bb8c01bb5b1560087e44d49e8e034497668c7cdde7cb5397995afa79c9fa1e2b35af9c9abafa82 + checksum: c9ea31be8c7bf0b760c075d5e39f71d90fcebee316e49688345e9095d520ed766f3bfd560227e3f3c28639399a0641a27193eef60c4802d89cb414e21240bbb5 languageName: node linkType: hard @@ -7072,23 +7181,23 @@ __metadata: linkType: hard "postcss-nested@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-nested@npm:6.0.1" + version: 6.2.0 + resolution: "postcss-nested@npm:6.2.0" dependencies: - postcss-selector-parser: ^6.0.11 + postcss-selector-parser: ^6.1.1 peerDependencies: postcss: ^8.2.14 - checksum: 7ddb0364cd797de01e38f644879189e0caeb7ea3f78628c933d91cc24f327c56d31269384454fc02ecaf503b44bfa8e08870a7c4cc56b23bc15640e1894523fa + checksum: 2c86ecf2d0ce68f27c87c7e24ae22dc6dd5515a89fcaf372b2627906e11f5c1f36e4a09e4c15c20fd4a23d628b3d945c35839f44496fbee9a25866258006671b languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.11": - version: 6.1.0 - resolution: "postcss-selector-parser@npm:6.1.0" +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.1.1": + version: 6.1.2 + resolution: "postcss-selector-parser@npm:6.1.2" dependencies: cssesc: ^3.0.0 util-deprecate: ^1.0.2 - checksum: 449f614e6706421be307d8638183c61ba45bc3b460fe3815df8971dbb4d59c4087181940d879daee4a7a2daf3d86e915db1cce0c006dd68ca75b4087079273bd + checksum: ce9440fc42a5419d103f4c7c1847cb75488f3ac9cbe81093b408ee9701193a509f664b4d10a2b4d82c694ee7495e022f8f482d254f92b7ffd9ed9dea696c6f84 languageName: node linkType: hard @@ -7111,20 +7220,20 @@ __metadata: linkType: hard "postcss@npm:^8.4.16, postcss@npm:^8.4.23": - version: 8.4.39 - resolution: "postcss@npm:8.4.39" + version: 8.4.47 + resolution: "postcss@npm:8.4.47" dependencies: nanoid: ^3.3.7 - picocolors: ^1.0.1 - source-map-js: ^1.2.0 - checksum: 14b130c90f165961772bdaf99c67f907f3d16494adf0868e57ef68baa67e0d1f6762db9d41ab0f4d09bab6fb7888588dba3596afd1a235fd5c2d43fba7006ac6 + picocolors: ^1.1.0 + source-map-js: ^1.2.1 + checksum: f78440a9d8f97431dd2ab1ab8e1de64f12f3eff38a3d8d4a33919b96c381046a314658d2de213a5fa5eb296b656de76a3ec269fdea27f16d5ab465b916a0f52c languageName: node linkType: hard "preact@npm:^10.12.0, preact@npm:^10.16.0": - version: 10.22.1 - resolution: "preact@npm:10.22.1" - checksum: 8762645766b1c057eaf9a58ff904b6659ffa7a7d33eb94dc2b96ff1ba08743855c3ade913dfbe9e8da994777aa9f4f5e9f23d39886340f23eaebcd98f5e107e5 + version: 10.24.0 + resolution: "preact@npm:10.24.0" + checksum: bcc55910a159e534b1a1d07abb332854d31adfdab3236c81e5ec17e9650038735ccde36b5830b443c1a7b68a41918ba922c9711bf14430e7562e34b76f3a8ea0 languageName: node linkType: hard @@ -7145,15 +7254,14 @@ __metadata: linkType: hard "prettier-plugin-solidity@npm:^1.3.1": - version: 1.3.1 - resolution: "prettier-plugin-solidity@npm:1.3.1" + version: 1.4.1 + resolution: "prettier-plugin-solidity@npm:1.4.1" dependencies: - "@solidity-parser/parser": ^0.17.0 + "@solidity-parser/parser": ^0.18.0 semver: ^7.5.4 - solidity-comments-extractor: ^0.0.8 peerDependencies: prettier: ">=2.3.0" - checksum: 286bf3b5899d7fad66e49c78ebac164bacfbf419f874a932ed99e491d97d77e91fa03ca068197939d3696ba7991db9e5258390dd42dee8d2184fa8c2e11921e4 + checksum: ac9f3cc525553a45e70f60898da5d4a7b733128cdd9893686364790ff688c56dd6eb0234620759dc6fabad4dc354a27097927b29ea7761c5814c64613c07222f languageName: node linkType: hard @@ -7166,14 +7274,7 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^3.0.0": - version: 3.0.0 - resolution: "proc-log@npm:3.0.0" - checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 - languageName: node - linkType: hard - -"proc-log@npm:^4.2.0": +"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" checksum: 98f6cd012d54b5334144c5255ecb941ee171744f45fca8b43b58ae5a0c1af07352475f481cadd9848e7f0250376ee584f6aa0951a856ff8f021bdfbff4eb33fc @@ -7223,15 +7324,15 @@ __metadata: linkType: hard "qrcode.react@npm:^3.1.0": - version: 3.1.0 - resolution: "qrcode.react@npm:3.1.0" + version: 3.2.0 + resolution: "qrcode.react@npm:3.2.0" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 94a2942ecf83f461d869adb20305ae663c6d1abe93ef2c72442b07d756ce70cf6deb6fd588dc5b382b48c6991cfde1dfd5ac9b814c1461e71d5edb2d945e67fc + checksum: 55d020ca482d57e8d73ee9e2e18f152184fd3d7d2d0742ae54ec58c5a3bab08b242a648585178d7fc91877fc75d6fbad7a35fb51bc4bddd4374e1de450ca78e7 languageName: node linkType: hard -"qrcode@npm:1.5.3, qrcode@npm:^1.5.1, qrcode@npm:~1.5.3": +"qrcode@npm:1.5.3": version: 1.5.3 resolution: "qrcode@npm:1.5.3" dependencies: @@ -7245,6 +7346,19 @@ __metadata: languageName: node linkType: hard +"qrcode@npm:^1.5.1, qrcode@npm:~1.5.3": + version: 1.5.4 + resolution: "qrcode@npm:1.5.4" + dependencies: + dijkstrajs: ^1.0.1 + pngjs: ^5.0.0 + yargs: ^15.3.1 + bin: + qrcode: bin/qrcode + checksum: 0a162822e12c02b0333315462fd4ccad22255002130f86806773be7592aec5ef295efaffa3eb148cbf00e290839c7b610f63b0d62a0c5efc5bc52a68f4189684 + languageName: node + linkType: hard + "query-string@npm:7.1.3": version: 7.1.3 resolution: "query-string@npm:7.1.3" @@ -7635,9 +7749,9 @@ __metadata: linkType: hard "safe-stable-stringify@npm:^2.1.0": - version: 2.4.3 - resolution: "safe-stable-stringify@npm:2.4.3" - checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 + version: 2.5.0 + resolution: "safe-stable-stringify@npm:2.5.0" + checksum: d3ce103ed43c6c2f523e39607208bfb1c73aa48179fc5be53c3aa97c118390bffd4d55e012f5393b982b65eb3e0ee954dd57b547930d3f242b0053dcdb923d17 languageName: node linkType: hard @@ -7673,6 +7787,15 @@ __metadata: languageName: unknown linkType: soft +"semver@npm:^5.5.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" + bin: + semver: bin/semver + checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686 + languageName: node + linkType: hard + "semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -7682,12 +7805,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.4": - version: 7.6.2 - resolution: "semver@npm:7.6.2" +"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" bin: semver: bin/semver.js - checksum: 40f6a95101e8d854357a644da1b8dd9d93ce786d5c6a77227bc69dbb17bea83d0d1d1d7c4cd5920a6df909f48e8bd8a5909869535007f90278289f2451d0292d + checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8 languageName: node linkType: hard @@ -7823,10 +7946,20 @@ __metadata: languageName: node linkType: hard -"solidity-comments-extractor@npm:^0.0.8": - version: 0.0.8 - resolution: "solidity-comments-extractor@npm:0.0.8" - checksum: ad025fc968e2d744b4270710c2f7f55b43d8046ab3f155fd880a7768d6fd163a93ea98f62be3b1115a29ba815bd8b5736bb5ffd1feff79083eca1bf273108d07 +"solc@npm:^0.8.27": + version: 0.8.27 + resolution: "solc@npm:0.8.27" + dependencies: + command-exists: ^1.2.8 + commander: ^8.1.0 + follow-redirects: ^1.12.1 + js-sha3: 0.8.0 + memorystream: ^0.3.1 + semver: ^5.5.0 + tmp: 0.0.33 + bin: + solcjs: solc.js + checksum: 33a81256445b7999672db8b03f94a7a767c28b9be88f60030fbc1b43b5ef9c3fa44591193abe8dda166df333c5fa4370bfcfb67acf01f8ca828ad7fb2f8ce54e languageName: node linkType: hard @@ -7839,10 +7972,10 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0": - version: 1.2.0 - resolution: "source-map-js@npm:1.2.0" - checksum: 791a43306d9223792e84293b00458bf102a8946e7188f3db0e4e22d8d530b5f80a4ce468eb5ec0bf585443ad55ebbd630bf379c98db0b1f317fd902500217f97 +"source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 4eb0cd997cdf228bc253bcaff9340afeb706176e64868ecd20efbe6efea931465f43955612346d6b7318789e5265bdc419bc7669c1cebe3db0eb255f57efa76b languageName: node linkType: hard @@ -7979,6 +8112,16 @@ __metadata: languageName: node linkType: hard +"string.prototype.repeat@npm:^1.0.0": + version: 1.0.0 + resolution: "string.prototype.repeat@npm:1.0.0" + dependencies: + define-properties: ^1.1.3 + es-abstract: ^1.17.5 + checksum: 95dfc514ed7f328d80a066dabbfbbb1615c3e51490351085409db2eb7cbfed7ea29fdadaf277647fbf9f4a1e10e6dd9e95e78c0fd2c4e6bb6723ea6e59401004 + languageName: node + linkType: hard + "string.prototype.trim@npm:^1.2.9": version: 1.2.9 resolution: "string.prototype.trim@npm:1.2.9" @@ -8135,8 +8278,8 @@ __metadata: linkType: hard "tailwindcss@npm:^3.3.3": - version: 3.4.4 - resolution: "tailwindcss@npm:3.4.4" + version: 3.4.12 + resolution: "tailwindcss@npm:3.4.12" dependencies: "@alloc/quick-lru": ^5.2.0 arg: ^5.0.2 @@ -8163,7 +8306,7 @@ __metadata: bin: tailwind: lib/cli.js tailwindcss: lib/cli.js - checksum: 743639b6a5c827b6f91ad8cff22ad296e25f4478202200a6f41ae49fbb28c4c6f8120e742a85e09987e33352fbc52c6a34c4ed33ce000b3810d4edf632142bac + checksum: 1f113cb8fef36088757d15efd29d9309f31a6106b276448bc8b54fd80815ebfb5bdab004a288f7268195410f9019b6d4f4ef2a9950d065e23e08941e3f13b8d1 languageName: node linkType: hard @@ -8174,7 +8317,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.1.11, tar@npm:^6.1.2": +"tar@npm:^6.1.11, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" dependencies: @@ -8236,6 +8379,15 @@ __metadata: languageName: node linkType: hard +"tmp@npm:0.0.33": + version: 0.0.33 + resolution: "tmp@npm:0.0.33" + dependencies: + os-tmpdir: ~1.0.2 + checksum: 902d7aceb74453ea02abbf58c203f4a8fc1cead89b60b31e354f74ed5b3fb09ea817f94fb310f884a5d16987dd9fa5a735412a7c2dd088dd3d415aa819ae3a28 + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -8280,7 +8432,7 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.0.1": +"ts-api-utils@npm:^1.3.0": version: 1.3.0 resolution: "ts-api-utils@npm:1.3.0" peerDependencies: @@ -8316,9 +8468,9 @@ __metadata: linkType: hard "tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": - version: 2.6.3 - resolution: "tslib@npm:2.6.3" - checksum: 74fce0e100f1ebd95b8995fbbd0e6c91bdd8f4c35c00d4da62e285a3363aaa534de40a80db30ecfd388ed7c313c42d930ee0eaf108e8114214b180eec3dbe6f5 + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 1606d5c89f88d466889def78653f3aab0f88692e80bb2066d090ca6112ae250ec1cfa9dbfaab0d17b60da15a4186e8ec4d893801c67896b277c17374e36e1d28 languageName: node linkType: hard @@ -8357,9 +8509,9 @@ __metadata: linkType: hard "type-fest@npm:^4.6.0": - version: 4.21.0 - resolution: "type-fest@npm:4.21.0" - checksum: 32d3536acac388cc32a3c0e31966d36e44124ffd6cb7d6f6c846602ffdeda68b723f5fdcd13d136f9d855b166e5c1d529bcdfac9d5d0ed4e96cff4867710adae + version: 4.26.1 + resolution: "type-fest@npm:4.26.1" + checksum: 7188db3bca82afa62c69a8043fb7c5eb74e63c45e7e28efb986da1629d844286f7181bc5a8185f38989fffff0d6c96be66fd13529b01932d1b6ebe725181d31a languageName: node linkType: hard @@ -8425,36 +8577,38 @@ __metadata: linkType: hard "typescript@npm:^5.1.6": - version: 5.5.3 - resolution: "typescript@npm:5.5.3" + version: 5.6.2 + resolution: "typescript@npm:5.6.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 4b4f14313484d5c86064d04ba892544801fa551f5cf72719b540b498056fec7fc192d0bbdb2ba1448e759b1548769956da9e43e7c16781e8d8856787b0575004 + checksum: 48777e1dabd9044519f56cd012b0296e3b72bafe12b7e8e34222751d45c67e0eba5387ecdaa6c14a53871a29361127798df6dc8d1d35643a0a47cb0b1c65a33a languageName: node linkType: hard "typescript@patch:typescript@^5.1.6#~builtin": - version: 5.5.3 - resolution: "typescript@patch:typescript@npm%3A5.5.3#~builtin::version=5.5.3&hash=a1c5e5" + version: 5.6.2 + resolution: "typescript@patch:typescript@npm%3A5.6.2#~builtin::version=5.6.2&hash=a1c5e5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 6853be4607706cc1ad2f16047cf1cd72d39f79acd5f9716e1d23bc0e462c7f59be7458fe58a21665e7657a05433d7ab8419d093a5a4bd5f3a33f879b35d2769b + checksum: c084ee1ab865f108c787e6233a5f63c126c482c0c8e87ec998ac5288a2ad54b603e1ea8b8b272355823b833eb31b9fabb99e8c6152283e1cb47e3a76bd6faf6c languageName: node linkType: hard "ua-parser-js@npm:^1.0.37": - version: 1.0.38 - resolution: "ua-parser-js@npm:1.0.38" - checksum: d0772b22b027338d806ab17d1ac2896ee7485bdf9217c526028159f3cd6bb10272bb18f6196d2f94dde83e3b36dc9d2533daf08a414764f6f4f1844842383838 + version: 1.0.39 + resolution: "ua-parser-js@npm:1.0.39" + bin: + ua-parser-js: script/cli.js + checksum: 19455df8c2348ef53f2e150e7406d3a025a619c2fd69722a1e63363d5ba8d91731ef7585f2dce7d8f14c8782734b4d704c05f246dca5f7565b5ae7d318084f2a languageName: node linkType: hard -"ufo@npm:^1.4.0, ufo@npm:^1.5.3": - version: 1.5.3 - resolution: "ufo@npm:1.5.3" - checksum: 2f54fa543b2e689cc4ab341fe2194937afe37c5ee43cd782e6ecc184e36859e84d4197a43ae4cd6e9a56f793ca7c5b950dfff3f16fadaeef9b6b88b05c88c8ef +"ufo@npm:^1.4.0, ufo@npm:^1.5.3, ufo@npm:^1.5.4": + version: 1.5.4 + resolution: "ufo@npm:1.5.4" + checksum: f244703b7d4f9f0df4f9af23921241ab73410b591f4e5b39c23e3147f3159b139a4b1fb5903189c306129f7a16b55995dac0008e0fbae88a37c3e58cbc34d833 languageName: node linkType: hard @@ -8486,23 +8640,23 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 +"undici-types@npm:~6.19.2": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: de51f1b447d22571cf155dfe14ff6d12c5bdaec237c765085b439c38ca8518fc360e88c70f99469162bf2e14188a7b0bcb06e1ed2dc031042b984b0bb9544017 languageName: node linkType: hard "unenv@npm:^1.9.0": - version: 1.9.0 - resolution: "unenv@npm:1.9.0" + version: 1.10.0 + resolution: "unenv@npm:1.10.0" dependencies: consola: ^3.2.3 - defu: ^6.1.3 + defu: ^6.1.4 mime: ^3.0.0 - node-fetch-native: ^1.6.1 - pathe: ^1.1.1 - checksum: 4cfbeedee1436e7f417d655c521e4c6220228f5b96afff90b5253d4504282c6de5acdd982aa51c977ce38d21d7692a33d10fc857166b3488655ff29c3bb754a2 + node-fetch-native: ^1.6.4 + pathe: ^1.1.2 + checksum: 4510b20adb2d4481d5ea9996aa37f452add8085fbee76838088c57750014a5a6d6b05f9599333fdc32e7fcb52064ffbd39ee47d9d1c5d634109651ed260819d5 languageName: node linkType: hard @@ -8525,33 +8679,33 @@ __metadata: linkType: hard "unstorage@npm:^1.9.0": - version: 1.10.2 - resolution: "unstorage@npm:1.10.2" + version: 1.12.0 + resolution: "unstorage@npm:1.12.0" dependencies: anymatch: ^3.1.3 chokidar: ^3.6.0 destr: ^2.0.3 - h3: ^1.11.1 + h3: ^1.12.0 listhen: ^1.7.2 - lru-cache: ^10.2.0 + lru-cache: ^10.4.3 mri: ^1.2.0 - node-fetch-native: ^1.6.2 - ofetch: ^1.3.3 - ufo: ^1.4.0 + node-fetch-native: ^1.6.4 + ofetch: ^1.3.4 + ufo: ^1.5.4 peerDependencies: - "@azure/app-configuration": ^1.5.0 - "@azure/cosmos": ^4.0.0 + "@azure/app-configuration": ^1.7.0 + "@azure/cosmos": ^4.1.1 "@azure/data-tables": ^13.2.2 - "@azure/identity": ^4.0.1 + "@azure/identity": ^4.4.1 "@azure/keyvault-secrets": ^4.8.0 - "@azure/storage-blob": ^12.17.0 - "@capacitor/preferences": ^5.0.7 + "@azure/storage-blob": ^12.24.0 + "@capacitor/preferences": ^6.0.2 "@netlify/blobs": ^6.5.0 || ^7.0.0 - "@planetscale/database": ^1.16.0 - "@upstash/redis": ^1.28.4 + "@planetscale/database": ^1.19.0 + "@upstash/redis": ^1.34.0 "@vercel/kv": ^1.0.1 idb-keyval: ^6.2.1 - ioredis: ^5.3.2 + ioredis: ^5.4.1 peerDependenciesMeta: "@azure/app-configuration": optional: true @@ -8579,7 +8733,7 @@ __metadata: optional: true ioredis: optional: true - checksum: dd3dc881fb2724b0e1af069b919682cc8cfe539e9c8fa50cd3fe448744c9608f97c47b092f48c615e4d17736e206e880b76d7479a4520177bc3e197159d49718 + checksum: 894d0009b5336d256fbea63c3c902df4b85084e5670e2fdc6359c0f18aa7de4479eabbdced9ff0e16d7c21cd8b00c1ac657ca002627f29f4838e1a60362c2567 languageName: node linkType: hard @@ -8596,7 +8750,7 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.16": +"update-browserslist-db@npm:^1.1.0": version: 1.1.0 resolution: "update-browserslist-db@npm:1.1.0" dependencies: @@ -8675,7 +8829,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.2.0": +"use-sync-external-store@npm:1.2.2, use-sync-external-store@npm:^1.2.0": version: 1.2.2 resolution: "use-sync-external-store@npm:1.2.2" peerDependencies: @@ -8772,23 +8926,24 @@ __metadata: linkType: hard "viem@npm:^2.1.1, viem@npm:^2.12.1": - version: 2.17.2 - resolution: "viem@npm:2.17.2" + version: 2.21.9 + resolution: "viem@npm:2.21.9" dependencies: "@adraffy/ens-normalize": 1.10.0 "@noble/curves": 1.4.0 "@noble/hashes": 1.4.0 "@scure/bip32": 1.4.0 - "@scure/bip39": 1.3.0 + "@scure/bip39": 1.4.0 abitype: 1.0.5 isows: 1.0.4 + webauthn-p256: 0.0.5 ws: 8.17.1 peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 5e2ab5120b587cd9959056d88cc9d0cd811f3eb710f7f635503349c4146fb57dfdd679d45ac0a7f83bae64f07d88eec89f951f6f03e0d29cca4594477142de19 + checksum: de6b9789a86cd217ce62833db80bcf3b5a6fbca3a8ef29d74083a5822c8a9c5fabcfecd050a5c5f0aa476eac02164b57a7aef3334030f93b649d4b52040f9332 languageName: node linkType: hard @@ -8813,6 +8968,16 @@ __metadata: languageName: node linkType: hard +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" + dependencies: + "@noble/curves": ^1.4.0 + "@noble/hashes": ^1.4.0 + checksum: 2837188d1e6d947c87c5728374fb6aec96387cb766f78e7a04d5903774264feb278d68a639748f09997f591e5278796c662bb5c4e8b8286b0f22254694023584 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -8844,11 +9009,11 @@ __metadata: linkType: hard "which-builtin-type@npm:^1.1.3": - version: 1.1.3 - resolution: "which-builtin-type@npm:1.1.3" + version: 1.1.4 + resolution: "which-builtin-type@npm:1.1.4" dependencies: - function.prototype.name: ^1.1.5 - has-tostringtag: ^1.0.0 + function.prototype.name: ^1.1.6 + has-tostringtag: ^1.0.2 is-async-function: ^2.0.0 is-date-object: ^1.0.5 is-finalizationregistry: ^1.0.2 @@ -8857,13 +9022,13 @@ __metadata: is-weakref: ^1.0.2 isarray: ^2.0.5 which-boxed-primitive: ^1.0.2 - which-collection: ^1.0.1 - which-typed-array: ^1.1.9 - checksum: 43730f7d8660ff9e33d1d3f9f9451c4784265ee7bf222babc35e61674a11a08e1c2925019d6c03154fcaaca4541df43abe35d2720843b9b4cbcebdcc31408f36 + which-collection: ^1.0.2 + which-typed-array: ^1.1.15 + checksum: 1f413025250072534de2a2ee25139a24d477512b532b05c85fb9aa05aef04c6e1ca8e2668acf971b777e602721dbdec4b9d6a4f37c6b9ff8f026ad030352707f languageName: node linkType: hard -"which-collection@npm:^1.0.1": +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -8882,7 +9047,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.9": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: @@ -9053,11 +9218,11 @@ __metadata: linkType: hard "yaml@npm:^2.3.4": - version: 2.4.5 - resolution: "yaml@npm:2.4.5" + version: 2.5.1 + resolution: "yaml@npm:2.5.1" bin: yaml: bin.mjs - checksum: f8efd407c07e095f00f3031108c9960b2b12971d10162b1ec19007200f6c987d2e28f73283f4731119aa610f177a3ea03d4a8fcf640600a25de1b74d00c69b3d + checksum: 31275223863fbd0b47ba9d2b248fbdf085db8d899e4ca43fff8a3a009497c5741084da6871d11f40e555d61360951c4c910b98216c1325d2c94753c0036d8172 languageName: node linkType: hard @@ -9098,10 +9263,10 @@ __metadata: linkType: hard "zustand@npm:^4.1.2, zustand@npm:^4.3.1": - version: 4.5.4 - resolution: "zustand@npm:4.5.4" + version: 4.5.5 + resolution: "zustand@npm:4.5.5" dependencies: - use-sync-external-store: 1.2.0 + use-sync-external-store: 1.2.2 peerDependencies: "@types/react": ">=16.8" immer: ">=9.0.6" @@ -9113,6 +9278,6 @@ __metadata: optional: true react: optional: true - checksum: 8e824aea8b5232f9a719c6d33e016272a0dae8c69a0980a3e2aefbcf2a89379b343af1f2dace340f45f788ec8d8a55a56d037c53498b3b05b91e985d26cae047 + checksum: 654e47959970bc66bbf2ae80fced7e556dd488e9ee54eb678330cb036ecc7184f4b8c2cae273be28022533622c54ab6339bf3fe30d19236367c5c251b6c6679a languageName: node linkType: hard