diff --git a/.gitignore b/.gitignore index 412b708..a209e7a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ node_modules gasreport.ansi .vscode/ + +# Solidity wake extension +.wake/ diff --git a/.solhint.json b/.solhint.json index aefec5f..a5397f1 100644 --- a/.solhint.json +++ b/.solhint.json @@ -17,6 +17,7 @@ ], "comprehensive-interface": "off", "const-name-snakecase": "off", + "custom-errors": "off", "func-name-mixedcase": "off", "func-visibility": [ "warn", diff --git a/lib/forge-std b/lib/forge-std index 3b20d60..8ba9031 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 +Subproject commit 8ba9031ffcbe25aa0d1224d3ca263a995026e477 diff --git a/lib/oz b/lib/oz index acd4ff7..fda6b85 160000 --- a/lib/oz +++ b/lib/oz @@ -1 +1 @@ -Subproject commit acd4ff74de833399287ed6b31b4debf6b2b35527 +Subproject commit fda6b85f2c65d146b86d513a604554d15abd6679 diff --git a/lib/oz-upgradeable b/lib/oz-upgradeable index 3d5fa5c..36ec707 160000 --- a/lib/oz-upgradeable +++ b/lib/oz-upgradeable @@ -1 +1 @@ -Subproject commit 3d5fa5c24c411112bab47bec25cfa9ad0af0e6e8 +Subproject commit 36ec7079af1a68bd866f6b9f4cf2f4dddee1e7bc diff --git a/src/PayerRegistry.sol b/src/PayerRegistry.sol new file mode 100644 index 0000000..7fd66cc --- /dev/null +++ b/src/PayerRegistry.sol @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { AccessControlUpgradeable } from "../lib/oz-upgradeable/contracts/access/AccessControlUpgradeable.sol"; +import { EnumerableSet } from "../lib/oz/contracts/utils/structs/EnumerableSet.sol"; +import { IERC20 } from "../lib/oz/contracts/token/ERC20/IERC20.sol"; +import { IERC20Metadata } from "../lib/oz/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { IERC165 } from "../lib/oz/contracts/utils/introspection/IERC165.sol"; +import { Initializable } from "../lib/oz-upgradeable/contracts/proxy/utils/Initializable.sol"; +import { PausableUpgradeable } from "../lib/oz-upgradeable/contracts/utils/PausableUpgradeable.sol"; +import { ReentrancyGuardUpgradeable } from "../lib/oz-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { SafeERC20 } from "../lib/oz/contracts/token/ERC20/utils/SafeERC20.sol"; +import { UUPSUpgradeable } from "../lib/oz-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import { IFeeDistributor } from "./interfaces/IFeeDistributor.sol"; +import { INodeRegistry } from "./interfaces/INodeRegistry.sol"; +import { IPayerRegistry } from "./interfaces/IPayerRegistry.sol"; +import { IPayerReportManager } from "./interfaces/IPayerReportManager.sol"; + +/** + * @title PayerRegistry + * @notice Implementation for managing payer USDC deposits, usage settlements, + * and a secure withdrawal process. + */ +contract PayerRegistry is + Initializable, + AccessControlUpgradeable, + UUPSUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable, + IPayerRegistry +{ + using SafeERC20 for IERC20; + using EnumerableSet for EnumerableSet.AddressSet; + + /* ============ Constants ============ */ + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + string internal constant _USDC_SYMBOL = "USDC"; + uint64 private constant _DEFAULT_MINIMUM_REGISTRATION_AMOUNT_MICRO_DOLLARS = 10_000_000; + uint64 private constant _DEFAULT_MINIMUM_DEPOSIT_AMOUNT_MICRO_DOLLARS = 10_000_000; + uint32 private constant _DEFAULT_MINIMUM_TRANSFER_FEES_PERIOD = 6 hours; + uint32 private constant _ABSOLUTE_MINIMUM_TRANSFER_FEES_PERIOD = 1 hours; + uint32 private constant _DEFAULT_WITHDRAWAL_LOCK_PERIOD = 3 days; + uint32 private constant _ABSOLUTE_MINIMUM_WITHDRAWAL_LOCK_PERIOD = 1 days; + + /* ============ UUPS Storage ============ */ + + /// @custom:storage-location erc7201:xmtp.storage.Payer + struct PayerStorage { + uint64 minimumRegistrationAmountMicroDollars; + uint64 minimumDepositAmountMicroDollars; + uint64 pendingFees; + uint64 collectedFees; + uint64 lastFeeTransferTimestamp; + uint32 withdrawalLockPeriod; + uint32 transferFeesPeriod; + address usdcToken; + address feeDistributor; + address nodeRegistry; + address payerReportManager; + mapping(address => Payer) payers; + mapping(address => Withdrawal) withdrawals; + EnumerableSet.AddressSet activePayers; + EnumerableSet.AddressSet debtPayers; + } + + // keccak256(abi.encode(uint256(keccak256("xmtp.storage.Payer")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 internal constant _PAYER_STORAGE_LOCATION = + 0xd0335f337c570f3417b0f0d20340c88da711d60e810b5e9b3ecabe9ccfcdce5a; + + function _getPayerStorage() internal pure returns (PayerStorage storage $) { + // slither-disable-next-line assembly + assembly { + $.slot := _PAYER_STORAGE_LOCATION + } + } + + /* ============ Modifiers ============ */ + + /** + * @dev Modifier to check if caller is the payer report contract. + */ + modifier onlyPayerReport() { + require(msg.sender == _getPayerStorage().payerReportManager, Unauthorized()); + _; + } + + /** + * @dev Modifier to check if address is an active payer. + */ + modifier onlyPayer(address payer) { + require(_payerExists(msg.sender), PayerDoesNotExist()); + _; + } + + /* ============ Initialization ============ */ + + /** + * @notice Initializes the contract with the deployer as admin. + * @param initialAdmin The address of the admin. + * @dev There's a chicken-egg problem here with PayerReport and Distribution contracts. + * We need to deploy these contracts first, then set their addresses + * in the Payer contract. + */ + function initialize(address initialAdmin, address usdcToken, address nodesContract) public initializer { + if (initialAdmin == address(0) || usdcToken == address(0) || nodesContract == address(0)) { + revert InvalidAddress(); + } + + __UUPSUpgradeable_init(); + __AccessControl_init(); + __Pausable_init(); + + PayerStorage storage $ = _getPayerStorage(); + + $.minimumRegistrationAmountMicroDollars = _DEFAULT_MINIMUM_REGISTRATION_AMOUNT_MICRO_DOLLARS; + $.minimumDepositAmountMicroDollars = _DEFAULT_MINIMUM_DEPOSIT_AMOUNT_MICRO_DOLLARS; + $.withdrawalLockPeriod = _DEFAULT_WITHDRAWAL_LOCK_PERIOD; + $.transferFeesPeriod = _DEFAULT_MINIMUM_TRANSFER_FEES_PERIOD; + + _setUsdcToken(usdcToken); + _setNodeRegistry(nodesContract); + + _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin); + _grantRole(ADMIN_ROLE, initialAdmin); + } + + /* ============ Payers Management ============ */ + + /** + * @inheritdoc IPayerRegistry + */ + function register(uint64 amount) external whenNotPaused { + PayerStorage storage $ = _getPayerStorage(); + + require(amount >= $.minimumRegistrationAmountMicroDollars, InsufficientAmount()); + + if (_payerExists(msg.sender)) revert PayerAlreadyRegistered(); + + IERC20($.usdcToken).safeTransferFrom(msg.sender, address(this), amount); + + // New payer registration + $.payers[msg.sender] = Payer({ balance: int64(amount), latestDepositTimestamp: uint64(block.timestamp) }); + + // Add new payer to active and total payers sets + require($.activePayers.add(msg.sender), FailedToRegisterPayer()); + + emit PayerRegistered(msg.sender, amount); + } + + /** + * @inheritdoc IPayerRegistry + */ + function deposit(uint64 amount) external whenNotPaused nonReentrant onlyPayer(msg.sender) { + _validateAndProcessDeposit(msg.sender, msg.sender, amount); + } + + /** + * @inheritdoc IPayerRegistry + */ + function deposit(address payer, uint64 amount) external whenNotPaused { + _revertIfPayerDoesNotExist(payer); + + _validateAndProcessDeposit(msg.sender, payer, amount); + } + + /* ========== Payers Balance Management ========= */ + + /** + * @inheritdoc IPayerRegistry + */ + function requestWithdrawal(uint64 amount) external whenNotPaused onlyPayer(msg.sender) { + if (_withdrawalExists(msg.sender)) revert WithdrawalAlreadyRequested(); + + PayerStorage storage $ = _getPayerStorage(); + + require($.payers[msg.sender].balance >= int64(amount), InsufficientBalance()); + + // Balance to be withdrawn is deducted from the payer's balance, + // it can't be used to settle payments. + $.payers[msg.sender].balance -= int64(amount); + + uint64 withdrawableTimestamp = uint64(block.timestamp) + $.withdrawalLockPeriod; + + $.withdrawals[msg.sender] = Withdrawal({ withdrawableTimestamp: withdrawableTimestamp, amount: amount }); + + emit PayerBalanceUpdated(msg.sender, $.payers[msg.sender].balance); + + emit WithdrawalRequested(msg.sender, withdrawableTimestamp, amount); + } + + /** + * @inheritdoc IPayerRegistry + */ + function cancelWithdrawal() external whenNotPaused onlyPayer(msg.sender) { + _revertIfWithdrawalNotExists(msg.sender); + + PayerStorage storage $ = _getPayerStorage(); + + Withdrawal memory withdrawal = $.withdrawals[msg.sender]; + + delete $.withdrawals[msg.sender]; + + $.payers[msg.sender].balance += int64(withdrawal.amount); + + emit PayerBalanceUpdated(msg.sender, $.payers[msg.sender].balance); + + emit WithdrawalCancelled(msg.sender, withdrawal.withdrawableTimestamp); + } + + /** + * @inheritdoc IPayerRegistry + */ + function finalizeWithdrawal() external whenNotPaused nonReentrant onlyPayer(msg.sender) { + _revertIfWithdrawalNotExists(msg.sender); + + PayerStorage storage $ = _getPayerStorage(); + + Withdrawal memory withdrawal = $.withdrawals[msg.sender]; + + // slither-disable-next-line timestamp + require(block.timestamp >= withdrawal.withdrawableTimestamp, WithdrawalPeriodNotElapsed()); + + delete $.withdrawals[msg.sender]; + + uint64 finalWithdrawalAmount = withdrawal.amount; + + if ($.payers[msg.sender].balance < 0) { + finalWithdrawalAmount = _settleDebtsBeforeWithdrawal(msg.sender, withdrawal.amount); + } + + if (finalWithdrawalAmount > 0) { + IERC20($.usdcToken).safeTransfer(msg.sender, finalWithdrawalAmount); + } + + /// @dev re-emitting the balance update, as it can change due to debt settlement. + emit PayerBalanceUpdated(msg.sender, $.payers[msg.sender].balance); + + emit WithdrawalFinalized(msg.sender, withdrawal.withdrawableTimestamp, finalWithdrawalAmount); + } + + /** + * @inheritdoc IPayerRegistry + */ + function getWithdrawalStatus(address payer) external view returns (Withdrawal memory withdrawal) { + _revertIfPayerDoesNotExist(payer); + + return _getPayerStorage().withdrawals[payer]; + } + + /* ============ Usage Settlement ============ */ + + /** + * @inheritdoc IPayerRegistry + */ + function settleUsage( + uint256 originatorNode, + address[] calldata payerList, + uint64[] calldata usageAmountsList + ) external whenNotPaused nonReentrant onlyPayerReport { + require(payerList.length == usageAmountsList.length, InvalidPayerListLength()); + + PayerStorage storage $ = _getPayerStorage(); + + uint64 settledFees = 0; + uint64 pendingFees = $.pendingFees; + + for (uint256 i = 0; i < payerList.length; i++) { + address payer = payerList[i]; + uint64 usage = usageAmountsList[i]; + + // This should never happen, as PayerReport has already verified the payers and amounts. + // In case it does, skip the payer and process the rest. + if (!_payerExists(payer)) continue; + + Payer memory storedPayer = $.payers[payer]; + + if (storedPayer.balance >= int64(usage)) { + settledFees += usage; + pendingFees += usage; + + storedPayer.balance -= int64(usage); + + $.payers[payer] = storedPayer; + + emit PayerBalanceUpdated(payer, storedPayer.balance); + + continue; + } + + if (storedPayer.balance < 0) { + storedPayer.balance -= int64(usage); + + $.payers[payer] = storedPayer; + + emit PayerBalanceUpdated(payer, storedPayer.balance); + + continue; + } + + // Payer has balance, but not enough to settle the usage. + _addDebtor(payer); + + settledFees += uint64(storedPayer.balance); + pendingFees += uint64(storedPayer.balance); + + storedPayer.balance -= int64(usage); + + $.payers[payer] = storedPayer; + + emit PayerBalanceUpdated(payer, storedPayer.balance); + } + + $.pendingFees = pendingFees; + + emit UsageSettled(originatorNode, uint64(block.timestamp), settledFees); + } + + /** + * @inheritdoc IPayerRegistry + */ + function transferFeesToDistribution() external whenNotPaused nonReentrant { + PayerStorage storage $ = _getPayerStorage(); + + /// @dev slither marks this as a security issue because validators can modify block.timestamp. + /// However, in this scenario it's fine, as we'd just send fees a earlier than expected. + /// It would be a bigger issue if we'd rely on timestamp for randomness or calculations. + // slither-disable-next-line timestamp + require(block.timestamp - $.lastFeeTransferTimestamp >= $.transferFeesPeriod, InsufficientTimePassed()); + + uint64 pendingFeesAmount = $.pendingFees; + + require(pendingFeesAmount > 0, InsufficientAmount()); + + IERC20($.usdcToken).safeTransfer($.feeDistributor, pendingFeesAmount); + + $.lastFeeTransferTimestamp = uint64(block.timestamp); + $.collectedFees += pendingFeesAmount; + $.pendingFees = 0; + + emit FeesTransferred(uint64(block.timestamp), pendingFeesAmount); + } + + /* ========== Administrative Functions ========== */ + + /** + * @inheritdoc IPayerRegistry + */ + function setFeeDistributor(address newFeeDistributor) external onlyRole(ADMIN_ROLE) { + PayerStorage storage $ = _getPayerStorage(); + + try IFeeDistributor(newFeeDistributor).supportsInterface(type(IFeeDistributor).interfaceId) returns ( + bool supported + ) { + require(supported, InvalidFeeDistributor()); + } catch { + revert InvalidFeeDistributor(); + } + + $.feeDistributor = newFeeDistributor; + + emit FeeDistributorSet(newFeeDistributor); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setPayerReportManager(address newPayerReportManager) external onlyRole(ADMIN_ROLE) { + PayerStorage storage $ = _getPayerStorage(); + + try + IPayerReportManager(newPayerReportManager).supportsInterface(type(IPayerReportManager).interfaceId) + returns (bool supported) { + require(supported, InvalidPayerReportManager()); + } catch { + revert InvalidPayerReportManager(); + } + + $.payerReportManager = newPayerReportManager; + + emit PayerReportManagerSet(newPayerReportManager); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setNodeRegistry(address newNodeRegistry) external onlyRole(ADMIN_ROLE) { + _setNodeRegistry(newNodeRegistry); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setUsdcToken(address newUsdcToken) external onlyRole(ADMIN_ROLE) { + _setUsdcToken(newUsdcToken); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setMinimumDeposit(uint64 newMinimumDeposit) external onlyRole(ADMIN_ROLE) { + require(newMinimumDeposit > _DEFAULT_MINIMUM_DEPOSIT_AMOUNT_MICRO_DOLLARS, InvalidMinimumDeposit()); + + PayerStorage storage $ = _getPayerStorage(); + + uint64 oldMinimumDeposit = $.minimumDepositAmountMicroDollars; + $.minimumDepositAmountMicroDollars = newMinimumDeposit; + + emit MinimumDepositSet(oldMinimumDeposit, newMinimumDeposit); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setMinimumRegistrationAmount(uint64 newMinimumRegistrationAmount) external onlyRole(ADMIN_ROLE) { + require( + newMinimumRegistrationAmount > _DEFAULT_MINIMUM_REGISTRATION_AMOUNT_MICRO_DOLLARS, + InvalidMinimumRegistrationAmount() + ); + + PayerStorage storage $ = _getPayerStorage(); + + uint64 oldMinimumRegistrationAmount = $.minimumRegistrationAmountMicroDollars; + $.minimumRegistrationAmountMicroDollars = newMinimumRegistrationAmount; + + emit MinimumRegistrationAmountSet(oldMinimumRegistrationAmount, newMinimumRegistrationAmount); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setWithdrawalLockPeriod(uint32 newWithdrawalLockPeriod) external onlyRole(ADMIN_ROLE) { + require(newWithdrawalLockPeriod >= _ABSOLUTE_MINIMUM_WITHDRAWAL_LOCK_PERIOD, InvalidWithdrawalLockPeriod()); + + PayerStorage storage $ = _getPayerStorage(); + + uint32 oldWithdrawalLockPeriod = $.withdrawalLockPeriod; + $.withdrawalLockPeriod = newWithdrawalLockPeriod; + + emit WithdrawalLockPeriodSet(oldWithdrawalLockPeriod, newWithdrawalLockPeriod); + } + + /** + * @inheritdoc IPayerRegistry + */ + function setTransferFeesPeriod(uint32 newTransferFeesPeriod) external onlyRole(ADMIN_ROLE) { + require(newTransferFeesPeriod >= _ABSOLUTE_MINIMUM_TRANSFER_FEES_PERIOD, InvalidTransferFeesPeriod()); + + PayerStorage storage $ = _getPayerStorage(); + + uint32 oldTransferFeesPeriod = $.transferFeesPeriod; + $.transferFeesPeriod = newTransferFeesPeriod; + + emit TransferFeesPeriodSet(oldTransferFeesPeriod, newTransferFeesPeriod); + } + + /** + * @inheritdoc IPayerRegistry + */ + function pause() external onlyRole(ADMIN_ROLE) { + _pause(); + } + + /** + * @inheritdoc IPayerRegistry + */ + function unpause() external onlyRole(ADMIN_ROLE) { + _unpause(); + } + + /* ============ Getters ============ */ + + /** + * @inheritdoc IPayerRegistry + */ + function getPayer(address payer) external view returns (Payer memory payerInfo) { + _revertIfPayerDoesNotExist(payer); + + return _getPayerStorage().payers[payer]; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getActivePayers(uint32 offset, uint32 limit) external view returns (Payer[] memory payers, bool hasMore) { + PayerStorage storage $ = _getPayerStorage(); + + (address[] memory payerAddresses, bool hasMore_) = _getPaginatedAddresses($.activePayers, offset, limit); + + payers = new Payer[](payerAddresses.length); + for (uint256 i = 0; i < payerAddresses.length; i++) { + payers[i] = $.payers[payerAddresses[i]]; + } + + return (payers, hasMore_); + } + + /** + * @inheritdoc IPayerRegistry + */ + function getIsActivePayer(address payer) public view returns (bool isActive) { + _revertIfPayerDoesNotExist(payer); + + return _getPayerStorage().activePayers.contains(payer); + } + + /** + * @inheritdoc IPayerRegistry + */ + function getPayerBalance(address payer) external view returns (int64 balance) { + _revertIfPayerDoesNotExist(payer); + + return _getPayerStorage().payers[payer].balance; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getPayersInDebt(uint32 offset, uint32 limit) external view returns (Payer[] memory payers, bool hasMore) { + PayerStorage storage $ = _getPayerStorage(); + + (address[] memory payerAddresses, bool hasMore_) = _getPaginatedAddresses($.debtPayers, offset, limit); + + payers = new Payer[](payerAddresses.length); + for (uint256 i = 0; i < payerAddresses.length; i++) { + payers[i] = $.payers[payerAddresses[i]]; + } + + return (payers, hasMore_); + } + + /** + * @inheritdoc IPayerRegistry + */ + function getLastFeeTransferTimestamp() external view returns (uint64 timestamp) { + return _getPayerStorage().lastFeeTransferTimestamp; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getContractBalance() external view returns (uint256 balance) { + return IERC20(_getPayerStorage().usdcToken).balanceOf(address(this)); + } + + /** + * @inheritdoc IPayerRegistry + */ + function getFeeDistributor() external view returns (address feeDistributorAddress) { + return _getPayerStorage().feeDistributor; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getNodeRegistry() external view returns (address nodeRegistryAddress) { + return _getPayerStorage().nodeRegistry; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getPayerReportManager() external view returns (address payerReportManagerAddress) { + return _getPayerStorage().payerReportManager; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getMinimumDeposit() external view returns (uint64 minimumDeposit) { + return _getPayerStorage().minimumDepositAmountMicroDollars; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getMinimumRegistrationAmount() external view returns (uint64 minimumRegistrationAmount) { + return _getPayerStorage().minimumRegistrationAmountMicroDollars; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getWithdrawalLockPeriod() external view returns (uint32 lockPeriod) { + return _getPayerStorage().withdrawalLockPeriod; + } + + /** + * @inheritdoc IPayerRegistry + */ + function getPendingFees() external view returns (uint64 fees) { + return _getPayerStorage().pendingFees; + } + + /* ============ Internal ============ */ + + /** + * @notice Validates and processes a deposit or donation + * @param from The address funds are coming from + * @param to The payer account receiving the deposit + * @param amount The amount to deposit + */ + function _validateAndProcessDeposit(address from, address to, uint64 amount) internal { + PayerStorage storage $ = _getPayerStorage(); + + require(amount >= $.minimumDepositAmountMicroDollars, InsufficientAmount()); + require($.withdrawals[to].withdrawableTimestamp == 0, PayerInWithdrawal()); + + IERC20($.usdcToken).safeTransferFrom(from, address(this), amount); + + $.payers[to].balance += int64(amount); + + emit PayerBalanceUpdated(to, $.payers[to].balance); + } + + /** + * @notice Settles debts for a payer before withdrawal. + * @param payer The address of the payer. + * @param amount The withdrawal amount that can be used to settle debt. + * @return remainingAmount The amount left for withdrawal after settling debt. + */ + function _settleDebtsBeforeWithdrawal(address payer, uint64 amount) internal returns (uint64 remainingAmount) { + Payer storage payerData = _getPayerStorage().payers[payer]; + + if (payerData.balance >= 0) return amount; + + // Balance is always negative, we can safely negate it to get the debt. + uint64 debt = uint64(-payerData.balance); + + // If debt is greater than or equal to the withdrawal amount, + // use the entire amount to reduce debt. + if (debt >= amount) { + payerData.balance += int64(amount); + + // If balance is now 0, remove from debtors + if (payerData.balance == 0) _removeDebtor(payer); + + return 0; + } + + payerData.balance = 0; + + _removeDebtor(payer); + + // Return remaining amount after settling debt + return amount - debt; + } + + /** + * @notice Checks if a payer exists. + * @param payer The address of the payer to check. + * @return exists True if the payer exists, false otherwise. + */ + function _payerExists(address payer) internal view returns (bool exists) { + return _getPayerStorage().payers[payer].latestDepositTimestamp != 0; + } + + /** + * @notice Reverts if a payer does not exist. + * @param payer The address of the payer to check. + */ + function _revertIfPayerDoesNotExist(address payer) internal view { + require(_payerExists(payer), PayerDoesNotExist()); + } + + /** + * @notice Checks if a withdrawal exists. + * @param payer The address of the payer to check. + * @return exists True if the withdrawal exists, false otherwise. + */ + function _withdrawalExists(address payer) internal view returns (bool exists) { + return _getPayerStorage().withdrawals[payer].withdrawableTimestamp != 0; + } + + /** + * @notice Reverts if a withdrawal does not exist. + * @param payer The address of the payer to check. + */ + function _revertIfWithdrawalNotExists(address payer) internal view { + require(_withdrawalExists(payer), WithdrawalNotExists()); + } + + /** + * @notice Removes a payer from the debt payers set. + * @param payer The address of the payer to remove. + */ + function _removeDebtor(address payer) internal { + PayerStorage storage $ = _getPayerStorage(); + + if ($.debtPayers.contains(payer)) { + require($.debtPayers.remove(payer), FailedToRemoveDebtor()); + } + } + + /** + * @notice Adds a payer to the debt payers set. + * @param payer The address of the payer to add. + */ + function _addDebtor(address payer) internal { + PayerStorage storage $ = _getPayerStorage(); + + if (!$.debtPayers.contains(payer)) { + require($.debtPayers.add(payer), FailedToAddDebtor()); + } + } + + /** + * @notice Sets the NodeRegistry contract. + * @param newNodeRegistry The address of the new NodeRegistry contract. + */ + function _setNodeRegistry(address newNodeRegistry) internal { + PayerStorage storage $ = _getPayerStorage(); + + try INodeRegistry(newNodeRegistry).supportsInterface(type(INodeRegistry).interfaceId) returns (bool supported) { + require(supported, InvalidNodeRegistry()); + } catch { + revert InvalidNodeRegistry(); + } + + $.nodeRegistry = newNodeRegistry; + + emit NodeRegistrySet(newNodeRegistry); + } + + /** + * @notice Sets the USDC token contract. + * @param newUsdcToken The address of the new USDC token contract. + */ + function _setUsdcToken(address newUsdcToken) internal { + PayerStorage storage $ = _getPayerStorage(); + + try IERC20Metadata(newUsdcToken).symbol() returns (string memory symbol) { + require(keccak256(bytes(symbol)) == keccak256(bytes(_USDC_SYMBOL)), InvalidUsdcTokenContract()); + } catch { + revert InvalidUsdcTokenContract(); + } + + $.usdcToken = newUsdcToken; + + emit UsdcTokenSet(newUsdcToken); + } + + /** + * @notice Internal helper for paginated access to EnumerableSet.AddressSet. + * @param addressSet The EnumerableSet to paginate. + * @param offset The starting index. + * @param limit Maximum number of items to return. + * @return addresses Array of addresses from the set. + * @return hasMore Whether there are more items after this page. + */ + function _getPaginatedAddresses( + EnumerableSet.AddressSet storage addressSet, + uint256 offset, + uint256 limit + ) internal view returns (address[] memory addresses, bool hasMore) { + uint256 totalCount = addressSet.length(); + + if (offset >= totalCount) revert OutOfBounds(); + + uint256 count = totalCount - offset; + if (count > limit) { + count = limit; + hasMore = true; + } else { + hasMore = false; + } + + addresses = new address[](count); + + for (uint256 i = 0; i < count; i++) { + addresses[i] = addressSet.at(offset + i); + } + + return (addresses, hasMore); + } + + /* ============ Upgradeability ============ */ + + /** + * @dev Authorizes the upgrade of the contract. + * @param newImplementation The address of the new implementation. + */ + function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) { + require(newImplementation != address(0), InvalidAddress()); + emit UpgradeAuthorized(msg.sender, newImplementation); + } + + /* ============ ERC165 ============ */ + + /** + * @dev Override to support IPayerRegistry, IERC165 and AccessControlUpgradeable. + */ + function supportsInterface( + bytes4 interfaceId + ) public view override(IERC165, AccessControlUpgradeable) returns (bool supported) { + return interfaceId == type(IPayerRegistry).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/src/interfaces/IFeeDistributor.sol b/src/interfaces/IFeeDistributor.sol new file mode 100644 index 0000000..8bee78c --- /dev/null +++ b/src/interfaces/IFeeDistributor.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { IERC165 } from "../../lib/oz/contracts/utils/introspection/IERC165.sol"; + +/** + * @title IFeeDistributor + * @notice Interface for distributing rewards. + */ +interface IFeeDistributor is IERC165 { + /* ============ Distribution ============ */ + + /** + * @notice Distributes the rewards. + * @dev Only callable by the Payer contract. When called: + * - The protocol fee is deducted and kept in the treasury. + * - The Node Operators are paid for their operational costs. + * - Any excess is split between: + * - The possibility of minting XMTP rebates to incentivize Payers. + * - Used to buy back and burn XMTP tokens. + */ + function distributeRewards() external; /* onlyPayerContract */ + + /* ============ Administrative Functions ============ */ + + /** + * @notice Refreshes the node operator list. + * @dev Only callable by the admin or active nodes. + * This function is used to refresh the node operator list. + * It is called when a new node is added, disabled or removed. + */ + function refreshNodeOperatorList() external; + + /** + * @notice Sets the address of the nodes contract for operator verification. + * @param nodesContract The address of the new nodes contract. + * + * Emits `NodesContractUpdated`. + */ + function setNodesContract(address nodesContract) external; + + /** + * @notice Sets the address of the payer contract. + * @param payerContract The address of the new payer contract. + * + * Emits `PayerContractUpdated`. + */ + function setPayerContract(address payerContract) external; + + /** + * @notice Sets the protocol fee. + * @param newProtocolFee New protocol fee (in basis points, e.g., 100 = 1%). + */ + function setProtocolFee(uint256 newProtocolFee) external; + + /** + * @notice Sets the rebates percentage which will be minted as XMTP rebates. + * @param newRebatesPercentage New rebates percentage. + * + * Emits `RebatesPercentageUpdated`. + */ + function setRebatesPercentage(uint256 newRebatesPercentage) external; + + /** + * @notice Sets the address of the USDC token contract. + * @param usdcToken The address of the new USDC token contract. + * + * Emits `UsdcTokenUpdated`. + */ + function setUsdcToken(address usdcToken) external; + + /* ============ Getters ============ */ + + /** + * @notice Returns the current address of the nodes contract. + * @return nodesContract The current address of the nodes contract. + */ + function getNodesContract() external view returns (address nodesContract); + + /** + * @notice Returns the current address of the payer contract. + * @return payerContract The current address of the payer contract. + */ + function getPayerContract() external view returns (address payerContract); + + /** + * @notice Returns the current protocol fee (in basis points). + * @return protocolFee The current protocol fee. + */ + function getProtocolFee() external view returns (uint256 protocolFee); + + /** + * @notice Returns the current rebates percentage (in basis points). + * @return rebatesPercentage The current rebates percentage. + */ + function getRebatesPercentage() external view returns (uint256 rebatesPercentage); + + /** + * @notice Returns the current address of the USDC token contract. + * @return usdcToken The current address of the USDC token contract. + */ + function getUsdcToken() external view returns (address usdcToken); +} diff --git a/src/interfaces/IPayerRegistry.sol b/src/interfaces/IPayerRegistry.sol new file mode 100644 index 0000000..12296ac --- /dev/null +++ b/src/interfaces/IPayerRegistry.sol @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { IERC165 } from "../../lib/oz/contracts/utils/introspection/IERC165.sol"; + +/** + * @title IPayerRegistryEvents + * @notice Interface for events emitted by the PayerRegistry contract. + */ +interface IPayerRegistryEvents { + /// @dev Emitted when the fee distributor address is updated. + event FeeDistributorSet(address indexed newFeeDistributor); + + /// @dev Emitted when fees are transferred to the distribution contract. + event FeesTransferred(uint64 indexed timestamp, uint64 amount); + + /// @dev Emitted when the minimum deposit amount is updated. + event MinimumDepositSet(uint64 oldMinimumDeposit, uint64 newMinimumDeposit); + + /// @dev Emitted when the minimum registration amount is updated. + event MinimumRegistrationAmountSet(uint64 oldMinimumRegistrationAmount, uint64 newMinimumRegistrationAmount); + + /// @dev Emitted when the node registry address is updated. + event NodeRegistrySet(address indexed newNodeRegistry); + + /// @dev Emitted when a payer balance is updated. + event PayerBalanceUpdated(address indexed payer, int64 newBalance); + + /// @dev Emitted when a payer is deactivated by an owner. + event PayerDeactivated(uint256 indexed operatorId, address indexed payer); + + /// @dev Emitted when a new payer is registered. + event PayerRegistered(address indexed payer, uint64 amount); + + /// @dev Emitted when the payer report manager address is updated. + event PayerReportManagerSet(address indexed newPayerReportManager); + + /// @dev Emitted when the transfer fees period is updated. + event TransferFeesPeriodSet(uint32 oldTransferFeesPeriod, uint32 newTransferFeesPeriod); + + /// @dev Emitted when the upgrade is authorized. + event UpgradeAuthorized(address indexed upgrader, address indexed newImplementation); + + /// @dev Emitted when usage is settled and fees are calculated. + event UsageSettled(uint256 indexed originatorNode, uint64 timestamp, uint64 collectedFees); + + /// @dev Emitted when the USDC token address is updated. + event UsdcTokenSet(address indexed newUsdcToken); + + /// @dev Emitted when a payer cancels a withdrawal request. + event WithdrawalCancelled(address indexed payer, uint64 indexed withdrawableTimestamp); + + /// @dev Emitted when a payer's withdrawal is finalized. + event WithdrawalFinalized(address indexed payer, uint64 indexed withdrawableTimestamp, uint64 amount); + + /// @dev Emitted when the withdrawal lock period is updated. + event WithdrawalLockPeriodSet(uint32 oldWithdrawalLockPeriod, uint32 newWithdrawalLockPeriod); + + /// @dev Emitted when a payer initiates a withdrawal request. + event WithdrawalRequested(address indexed payer, uint64 indexed withdrawableTimestamp, uint64 amount); +} + +/** + * @title IPayerRegistryErrors + * @notice Interface for errors emitted by the Payer contract. + */ +interface IPayerRegistryErrors { + /// @dev Error thrown when arrays have mismatched lengths. + error ArrayLengthMismatch(); + + /// @notice Error thrown when adding a debtor has failed. + error FailedToAddDebtor(); + + /// @notice Error thrown when deactivating a payer has failed. + error FailedToDeactivatePayer(); + + /// @notice Error thrown when deleting a payer has failed. + error FailedToDeletePayer(); + + /// @notice Error thrown when granting a role has failed. + error FailedToGrantRole(bytes32 role, address account); + + /// @notice Error thrown when registering a payer has failed. + error FailedToRegisterPayer(); + + /// @notice Error thrown when removing a debtor has failed. + error FailedToRemoveDebtor(); + + /// @dev Error thrown when the amount is insufficient. + error InsufficientAmount(); + + /// @dev Error thrown when balance is insufficient. + error InsufficientBalance(); + + /// @dev Error thrown when insufficient time has passed since the last fee transfer. + error InsufficientTimePassed(); + + /// @dev Error thrown when an address is invalid (usually zero address). + error InvalidAddress(); + + /// @dev Error thrown when contract is not the fee distributor. + error InvalidFeeDistributor(); + + /// @dev Error thrown when the minimum deposit is invalid. + error InvalidMinimumDeposit(); + + /// @dev Error thrown when the minimum registration amount is invalid. + error InvalidMinimumRegistrationAmount(); + + /// @dev Error thrown when contract is not the node registry. + error InvalidNodeRegistry(); + + /// @notice Error thrown when the payer list length is invalid. + error InvalidPayerListLength(); + + /// @dev Error thrown when contract is not the payer report manager. + error InvalidPayerReportManager(); + + /// @dev Error thrown when trying to backdate settlement too far. + error InvalidSettlementTime(); + + /// @dev Error thrown when the transfer fees period is invalid. + error InvalidTransferFeesPeriod(); + + /// @dev Error thrown when contract is not the USDC token contract. + error InvalidUsdcTokenContract(); + + /// @dev Error thrown when the withdrawal lock period is invalid. + error InvalidWithdrawalLockPeriod(); + + /// @dev Error thrown when a lock period has not yet elapsed. + error LockPeriodNotElapsed(); + + /// @notice Error thrown when the offset is out of bounds. + error OutOfBounds(); + + /// @dev Error thrown when payer already exists. + error PayerAlreadyRegistered(); + + /// @dev Error thrown when payer does not exist. + error PayerDoesNotExist(); + + /// @dev Error thrown when trying to delete a payer with balance or debt. + error PayerHasBalanceOrDebt(); + + /// @dev Error thrown when payer has debt. + error PayerHasDebt(); + + /// @dev Error thrown when trying to delete a payer in withdrawal state. + error PayerInWithdrawal(); + + /// @dev Error thrown when a payer is not active. + error PayerIsNotActive(); + + /// @dev Error thrown when a call is unauthorized. + error Unauthorized(); + + /// @dev Error thrown when caller is not an authorized node operator. + error UnauthorizedNodeOperator(); + + /// @dev Error thrown when a withdrawal is already in progress. + error WithdrawalAlreadyRequested(); + + /// @dev Error thrown when a withdrawal is not in the requested state. + error WithdrawalNotExists(); + + /// @dev Error thrown when a withdrawal is not in the requested state. + error WithdrawalNotRequested(); + + /// @dev Error thrown when a withdrawal lock period has not yet elapsed. + error WithdrawalPeriodNotElapsed(); +} + +/** + * @title IPayerRegistry + * @notice Interface for managing payer USDC deposits, usage settlements, + * and a secure withdrawal process. + */ +interface IPayerRegistry is IERC165, IPayerRegistryEvents, IPayerRegistryErrors { + /* ============ Structs ============ */ + + /** + * @dev Struct to store payer information. + * @param balance The current USDC balance of the payer. + * @param latestDepositTimestamp The timestamp of the most recent deposit. + */ + struct Payer { + int64 balance; + uint64 latestDepositTimestamp; + } + + /** + * @dev Struct to store withdrawal request information. + * @param withdrawableTimestamp The timestamp when the withdrawal can be finalized. + * @param amount The amount requested for withdrawal. + */ + struct Withdrawal { + uint64 withdrawableTimestamp; + uint64 amount; + } + + /* ============ Payer Management ============ */ + + /** + * @notice Registers the caller as a new payer upon depositing the minimum required USDC. + * The caller must approve this contract to spend USDC beforehand. + * @param amount The amount of USDC to deposit (must be at least the minimum required). + * + * Emits `PayerRegistered`. + */ + function register(uint64 amount) external; + + /** + * @notice Allows the caller to deposit USDC into their own payer account. + * The caller must approve this contract to spend USDC beforehand. + * @param amount The amount of USDC to deposit. + * + * Emits `PayerBalanceUpdated`. + */ + function deposit(uint64 amount) external; + + /** + * @notice Allows anyone to donate USDC to an existing payer's account. + * The sender must approve this contract to spend USDC beforehand. + * @param payer The address of the payer receiving the donation. + * @param amount The amount of USDC to donate. + * + * Emits `PayerBalanceUpdated`. + */ + function deposit(address payer, uint64 amount) external; + + /* ============ Payer Balance Management ============ */ + + /** + * @notice Initiates a withdrawal request for the caller. + * - Sets the payer into withdrawal mode (no further usage allowed). + * - Records a timestamp for the withdrawal lock period. + * @param amount The amount to withdraw (can be less than or equal to current balance). + * + * Emits `WithdrawalRequest`. + * Emits `PayerBalanceUpdated`. + */ + function requestWithdrawal(uint64 amount) external; + + /** + * @notice Cancels a previously requested withdrawal, removing withdrawal mode. + * @dev Only callable by the payer who initiated the withdrawal. + * + * Emits `WithdrawalCancelled`. + * Emits `PayerBalanceUpdated`. + */ + function cancelWithdrawal() external; + + /** + * @notice Finalizes a payer's withdrawal after the lock period has elapsed. + * - Accounts for any pending usage during the lock. + * - Returns the unspent balance to the payer. + * + * Emits `WithdrawalFinalized`. + * Emits `PayerBalanceUpdated`. + */ + function finalizeWithdrawal() external; + + /** + * @notice Checks if a payer is currently in withdrawal mode and the timestamp + * when they initiated the withdrawal. + * @param payer The address to check. + * @return withdrawal The withdrawal status. + */ + function getWithdrawalStatus(address payer) external view returns (Withdrawal memory withdrawal); + + /* ============ Usage Settlement ============ */ + + /** + * @notice Settles usage for a contiguous batch of (payer, amount) entries. + * Assumes that the PayerReport contract has already verified the validity of the payers and amounts. + * + * @param originatorNode The originator node of the usage. + * @param payers A contiguous array of payer addresses. + * @param amounts A contiguous array of usage amounts corresponding to each payer. + * + * Emits `UsageSettled`. + * Emits `PayerBalanceUpdated` for each payer. + */ + function settleUsage( + uint256 originatorNode, + address[] calldata payers, + uint64[] calldata amounts + ) external; /* onlyPayerReport */ + + /** + * @notice Transfers all pending fees to the designated distribution contract. + * @dev Uses a single storage write for updating accumulated fees. + * + * Emits `FeesTransferred`. + */ + function transferFeesToDistribution() external; + + /* ============ Administrative Functions ============ */ + + /** + * @notice Sets the address of the fee distributor. + * @param feeDistributor The address of the new fee distributor. + * + * Emits `FeeDistributorUpdated`. + */ + function setFeeDistributor(address feeDistributor) external; + + /** + * @notice Sets the address of the payer report manager. + * @param payerReportManager The address of the new payer report manager. + * + * Emits `PayerReportManagerUpdated`. + */ + function setPayerReportManager(address payerReportManager) external; + + /** + * @notice Sets the address of the node registry for operator verification. + * @param nodeRegistry The address of the new node registry. + * + * Emits `NodeRegistryUpdated`. + */ + function setNodeRegistry(address nodeRegistry) external; + + /** + * @notice Sets the address of the USDC token contract. + * @param usdcToken The address of the new USDC token contract. + * + * Emits `UsdcTokenUpdated`. + */ + function setUsdcToken(address usdcToken) external; + + /** + * @notice Sets the minimum deposit amount required for registration. + * @param newMinimumDeposit The new minimum deposit amount. + * + * Emits `MinimumDepositUpdated`. + */ + function setMinimumDeposit(uint64 newMinimumDeposit) external; + + /** + * @notice Sets the minimum deposit amount required for registration. + * @param newMinimumRegistrationAmount The new minimum deposit amount. + * + * Emits `MinimumRegistrationAmountUpdated`. + */ + function setMinimumRegistrationAmount(uint64 newMinimumRegistrationAmount) external; + + /** + * @notice Sets the withdrawal lock period. + * @param newWithdrawalLockPeriod The new withdrawal lock period. + * + * Emits `WithdrawalLockPeriodUpdated`. + */ + function setWithdrawalLockPeriod(uint32 newWithdrawalLockPeriod) external; + + /** + * @notice Sets the transfer fees period. + * @param newTransferFeesPeriod The new transfer fees period. + * + * Emits `TransferFeesPeriodUpdated`. + */ + function setTransferFeesPeriod(uint32 newTransferFeesPeriod) external; + + /** + * @notice Pauses the contract functions in case of emergency. + * + * Emits `Paused()`. + */ + function pause() external; + + /** + * @notice Unpauses the contract. + * + * Emits `Unpaused()`. + */ + function unpause() external; + + /* ============ Getters ============ */ + + /** + * @notice Returns the payer information. + * @param payer The address of the payer. + * @return payerInfo The payer information. + */ + function getPayer(address payer) external view returns (Payer memory payerInfo); + + /** + * @notice Returns all active payers in a paginated response. + * @param offset Number of payers to skip before starting to return results. + * @param limit Maximum number of payers to return. + * @return payers The payer information. + * @return hasMore True if there are more payers to retrieve. + */ + function getActivePayers(uint32 offset, uint32 limit) external view returns (Payer[] memory payers, bool hasMore); + + /** + * @notice Checks if a given address is an active payer. + * @param payer The address to check. + * @return isActive True if the address is an active payer, false otherwise. + */ + function getIsActivePayer(address payer) external view returns (bool isActive); + + /** + * @notice Returns a paginated list of payers with outstanding debt. + * @param offset Number of payers to skip before starting to return results. + * @param limit Maximum number of payers to return. + * @return payers Array of payer addresses with debt. + * @return hasMore True if there are more payers to retrieve. + */ + function getPayersInDebt(uint32 offset, uint32 limit) external view returns (Payer[] memory payers, bool hasMore); + + /** + * @notice Returns the timestamp of the last fee transfer to the rewards contract. + * @return timestamp The last fee transfer timestamp. + */ + function getLastFeeTransferTimestamp() external view returns (uint64 timestamp); + + /** + * @notice Returns the actual USDC balance held by the contract. + * @dev This can be used to verify the contract's accounting is accurate. + * @return balance The USDC token balance of the contract. + */ + function getContractBalance() external view returns (uint256 balance); + + /** + * @notice Retrieves the address of the current distribution contract. + * @return feeDistributor The address of the fee distributor. + */ + function getFeeDistributor() external view returns (address feeDistributor); + + /** + * @notice Retrieves the address of the current nodes contract. + * @return nodeRegistry The address of the node registry. + */ + function getNodeRegistry() external view returns (address nodeRegistry); + + /** + * @notice Retrieves the address of the current payer report manager. + * @return payerReportManager The address of the payer report manager. + */ + function getPayerReportManager() external view returns (address payerReportManager); + + /** + * @notice Retrieves the minimum deposit amount required to register as a payer. + * @return minimumDeposit The minimum deposit amount in USDC. + */ + function getMinimumDeposit() external view returns (uint64 minimumDeposit); + + /** + * @notice Retrieves the minimum deposit amount required to register as a payer. + * @return minimumRegistrationAmount The minimum deposit amount in USDC. + */ + function getMinimumRegistrationAmount() external view returns (uint64 minimumRegistrationAmount); + + /** + * @notice Retrieves the current total balance of a given payer. + * @param payer The address of the payer. + * @return balance The current balance of the payer. + */ + function getPayerBalance(address payer) external view returns (int64 balance); + + /** + * @notice Returns the duration of the lock period required before a withdrawal + * can be finalized. + * @return lockPeriod The lock period in seconds. + */ + function getWithdrawalLockPeriod() external view returns (uint32 lockPeriod); + + /** + * @notice Retrieves the total pending fees that have not yet been transferred + * to the distribution contract. + * @return fees The total pending fees in USDC. + */ + function getPendingFees() external view returns (uint64 fees); +} diff --git a/src/interfaces/IPayerReportManager.sol b/src/interfaces/IPayerReportManager.sol new file mode 100644 index 0000000..9857dc4 --- /dev/null +++ b/src/interfaces/IPayerReportManager.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { IERC165 } from "../../lib/oz/contracts/utils/introspection/IERC165.sol"; + +/** + * @title IPayerReportManager + * @notice Interface for the PayerReportManager contract handling usage reports and batch settlements. + */ +interface IPayerReportManager is IERC165 { + /* ============ Structs ============ */ + + /** + * @notice A struct containing the usage report details. + * @param originatorNode The address of the originator node. + * @param startingSequenceID The starting sequence ID of the report. + * @param endingSequenceID The ending sequence ID of the report. + * @param lastMessageTimestamp The timestamp of the last message in the report. + * @param reportTimestamp The timestamp of the report. + * @param reportMerkleRoot The Merkle root of the report. + * @param leafCount The number of leaves in the report. A leaf is a single (payer, amount) pair. + */ + struct PayerReport { + address originatorNode; + uint256 startingSequenceID; + uint256 endingSequenceID; + uint256 lastMessageTimestamp; + uint256 reportTimestamp; + bytes32 reportMerkleRoot; + uint16 leafCount; + } + + /* ============ Events ============ */ + + /** + * @dev Emitted when an originator node submits a usage report. + * The report includes the Merkle root of the detailed (payer, amount) data. + * Note: Payers and amounts are not stored on-chain, only emitted in the event. + * Nodes listen to this event to get full details of the report. + */ + event PayerReportSubmitted( + address indexed originatorNode, + uint256 indexed reportIndex, + bytes32 indexed reportMerkleRoot, + uint256 startingSequenceID, + uint256 endingSequenceID, + uint256 lastMessageTimestamp, + uint256 reportTimestamp, + uint16 leafCount + ); + + /** + * @dev Emitted when a node attests to the correctness of a report. + */ + event PayerReportAttested( + address indexed originatorNode, + uint256 indexed reportIndex, + bytes32 indexed reportMerkleRoot + ); + + /** + * @dev Emitted when a usage report is confirmed. + */ + event PayerReportConfirmed( + address indexed originatorNode, + uint256 indexed reportIndex, + bytes32 indexed reportMerkleRoot + ); + + /** + * @dev Emitted when a batch of usage is settled. + */ + event PayerReportPartiallySettled( + address indexed originatorNode, + uint256 indexed reportIndex, + bytes32 indexed reportMerkleRoot, + address[] payers, + uint256[] amounts, + uint16 offset + ); + + /** + * @dev Emitted when a usage report is fully settled. + */ + event PayerReportFullySettled( + address indexed originatorNode, + uint256 indexed reportIndex, + bytes32 indexed reportMerkleRoot + ); + + /* ============ Usage Report Logic ============ */ + + /** + * @notice Submits a usage report for a node covering messages from startingSequenceID to endingSequenceID. + * @param payerReport A struct containing the usage report details. + * + * Emits a PayerReportSubmitted event. + */ + function submitPayerReport(PayerReport calldata payerReport) external; + + /** + * @notice Allows nodes to attest to the correctness of a submitted usage report. + * @param originatorNode The node that submitted the report. + * @param reportIndex The index of the report. + * + * Emits a PayerReportAttested event. + */ + function attestPayerReport(address originatorNode, uint256 reportIndex) external; + + /** + * @notice Returns a list of all payer reports for a given originator node. + * @param originatorNode The address of the originator node. + * @return startingSequenceIDs The array of starting sequence IDs for each report. + * @return reportsMerkleRoots The array of Merkle roots for each report. + */ + function listPayerReports( + address originatorNode + ) external view returns (uint256[] memory startingSequenceIDs, bytes32[] memory reportsMerkleRoots); + + /** + * @notice Returns summary info about a specific usage report. + * @param originatorNode The node that submitted the report. + * @param reportIndex The index of the report. + * @return payerReport A PayerReport struct with the report details. + */ + function getPayerReport( + address originatorNode, + uint256 reportIndex + ) external view returns (PayerReport memory payerReport); + + /** + * @notice Settles a contiguous batch of usage data from a confirmed report. + * Verifies an aggregated Merkle proof that the provided (payer, amount) batch is included in the report's committed + * Merkle root, then calls the settleUsage function in the Payer contract. + * + * @param originatorNode The node that submitted the report. + * @param reportIndex The index of the report. + * @param offset The index of the batch in the report's data (managed off-chain). + * @param payers A contiguous array of payer addresses. + * @param amounts A contiguous array of usage amounts corresponding to each payer. + * @param proof An aggregated Merkle proof containing branch hashes. + * + * Emits a UsageSettled event. + */ + function settleUsageBatch( + address originatorNode, + uint256 reportIndex, + uint16 offset, + address[] calldata payers, + uint256[] calldata amounts, + bytes32[] calldata proof + ) external; + + /** + * @notice Sets the maximum batch size for usage settlements. + * @param maxBatchSize The new maximum batch size. + */ + function setMaxBatchSize(uint256 maxBatchSize) external; + + /** + * @notice Returns the current maximum batch size. + * @return batchSize The current maximum batch size. + */ + function getMaxBatchSize() external view returns (uint256 batchSize); +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..ce11db0 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1050 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz#2ab05e09c1af0cdf2fcf5035bea1484e222f7983" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== + dependencies: + graceful-fs "4.2.10" + +"@pnpm/npm-conf@^2.1.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz#bb375a571a0bd63ab0a23bece33033c683e9b6b0" + integrity sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw== + dependencies: + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" + +"@prettier/sync@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@prettier/sync/-/sync-0.3.0.tgz#91f2cfc23490a21586d1cf89c6f72157c000ca1e" + integrity sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw== + +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + +"@solidity-parser/parser@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.19.0.tgz#37a8983b2725af9b14ff8c4a475fa0e98d773c3f" + integrity sha512-RV16k/qIxW/wWc+mLzV3ARyKUaMUTBy9tOLMzFhtNSKYeTAanQ3a5MudJKf/8arIFnA2L27SNjarQKmFg0w/jA== + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +ajv@^6.12.6: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" + integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== + dependencies: + environment "^1.0.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.0.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +antlr4@^4.13.1-patch-1: + version "4.13.2" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.2.tgz#0d084ad0e32620482a9c3a0e2470c02e72e4006d" + integrity sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +ast-parents@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== + +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + dependencies: + restore-cursor "^5.0.0" + +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== + +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +cosmiconfig@^8.0.0: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2, fast-diff@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +got@^12.1.0: + version "12.6.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +latest-version@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da" + integrity sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg== + dependencies: + package-json "^8.1.0" + +lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@^15.5.0: + version "15.5.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.5.0.tgz#fa6464cfb06e0faf5bb167f83186e952ff6e569e" + integrity sha512-WyCzSbfYGhK7cU+UuDDkzUiytbfbi0ZdPy2orwtM75P3WTtQBzmG40cCxIa8Ii2+XjfxzLH6Be46tUfWS85Xfg== + dependencies: + chalk "^5.4.1" + commander "^13.1.0" + debug "^4.4.0" + execa "^8.0.1" + lilconfig "^3.1.3" + listr2 "^8.2.5" + micromatch "^4.0.8" + pidtree "^0.6.0" + string-argv "^0.3.2" + yaml "^2.7.0" + +listr2@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.5.tgz#5c9db996e1afeb05db0448196d3d5f64fec2593d" + integrity sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ== + dependencies: + cli-truncate "^4.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.1.0" + rfdc "^1.4.1" + wrap-ansi "^9.0.0" + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-update@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== + dependencies: + ansi-escapes "^7.0.0" + cli-cursor "^5.0.0" + slice-ansi "^7.1.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== + +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + +package-json@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-8.1.1.tgz#3e9948e43df40d1e8e78a85485f1070bf8f03dc8" + integrity sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA== + dependencies: + got "^12.1.0" + registry-auth-token "^5.0.1" + registry-url "^6.0.0" + semver "^7.3.7" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.2.tgz#d4f6173674e73a29731a8c79c45ab6f5246310df" + integrity sha512-VVD/4XlDjSzyPWWCPW8JEleFa8JNKFYac5kNlMjVXemQyQZKfpekPMhFZSePuXB6L+RixlFvWe20iacGjFYrLw== + dependencies: + "@solidity-parser/parser" "^0.19.0" + semver "^7.6.3" + +prettier@^2.8.3: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +rc@1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +registry-auth-token@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.1.0.tgz#3c659047ecd4caebd25bc1570a3aa979ae490eca" + integrity sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw== + dependencies: + "@pnpm/npm-conf" "^2.1.0" + +registry-url@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-6.0.1.tgz#056d9343680f2f64400032b1e199faa692286c58" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== + dependencies: + rc "1.2.8" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + dependencies: + onetime "^7.0.0" + signal-exit "^4.1.0" + +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +semver@^7.3.7, semver@^7.5.2, semver@^7.6.3: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +slice-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + +solhint-plugin-prettier@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.1.0.tgz#2f46999e26d6c6bc80281c22a7a21e381175bef7" + integrity sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw== + dependencies: + "@prettier/sync" "^0.3.0" + prettier-linter-helpers "^1.0.0" + +solhint@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-5.0.5.tgz#43d9c730c2b22e11aa45582580749e0801a049d4" + integrity sha512-WrnG6T+/UduuzSWsSOAbfq1ywLUDwNea3Gd5hg6PS+pLUm8lz2ECNr0beX609clBxmDeZ3676AiA9nPDljmbJQ== + dependencies: + "@solidity-parser/parser" "^0.19.0" + ajv "^6.12.6" + antlr4 "^4.13.1-patch-1" + ast-parents "^0.0.1" + chalk "^4.1.2" + commander "^10.0.0" + cosmiconfig "^8.0.0" + fast-diff "^1.2.0" + glob "^8.0.3" + ignore "^5.2.4" + js-yaml "^4.1.0" + latest-version "^7.0.0" + lodash "^4.17.21" + pluralize "^8.0.0" + semver "^7.5.2" + strip-ansi "^6.0.1" + table "^6.8.1" + text-table "^0.2.0" + optionalDependencies: + prettier "^2.8.3" + +string-argv@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +table@^6.8.1: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==