diff --git a/.github/workflows/publish-prerelease.yml b/.github/workflows/publish-prerelease.yml index c0f558c7..69f28f4d 100644 --- a/.github/workflows/publish-prerelease.yml +++ b/.github/workflows/publish-prerelease.yml @@ -6,13 +6,13 @@ on: jobs: build: - if: 'github.event.release.prerelease' + if: "github.event.release.prerelease" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - run: npm ci - run: npm run build - name: Run test coverage @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: https://registry.npmjs.org/ - run: npm ci - run: npm run build @@ -47,9 +47,9 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: https://npm.pkg.github.com/ - scope: '@tokenysolutions' + scope: "@tokenysolutions" - run: npm ci - run: npm run build - run: npm publish --tag beta diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index fe60abe1..8d511ac9 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -6,13 +6,13 @@ on: jobs: build: - if: '!github.event.release.prerelease' + if: "!github.event.release.prerelease" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - run: npm ci - run: npm run build - name: Run test coverage @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: https://registry.npmjs.org/ - run: npm ci - run: npm run build @@ -47,9 +47,9 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: https://npm.pkg.github.com/ - scope: '@tokenysolutions' + scope: "@tokenysolutions" - run: npm ci - run: npm run build - run: npm publish diff --git a/.github/workflows/push_checking.yml b/.github/workflows/push_checking.yml index f21a4134..9e66d322 100644 --- a/.github/workflows/push_checking.yml +++ b/.github/workflows/push_checking.yml @@ -6,11 +6,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - name: Checkout - uses: 'actions/checkout@master' + uses: "actions/checkout@master" - name: Set Node.js uses: actions/setup-node@v3 @@ -20,6 +20,8 @@ jobs: run: npm ci - name: Lint Solidity sources run: npm run lint:sol + - name: Build contracts + run: npm run build - name: Lint TypeScript sources run: npm run lint:ts @@ -28,11 +30,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - name: Checkout - uses: 'actions/checkout@master' + uses: "actions/checkout@master" - name: Set Node.js uses: actions/setup-node@v3 diff --git a/.husky/commit-msg b/.husky/commit-msg index 5059dcbc..e2674005 100644 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - node ./scripts/commit-msg.js $1 && npx --no-install commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit index d24fdfc6..e69de29b 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx lint-staged diff --git a/.solcover.js b/.solcover.js index 30b33cac..63069152 100644 --- a/.solcover.js +++ b/.solcover.js @@ -4,5 +4,6 @@ module.exports = { "_testContracts", "roles/permissioning/owner/", "roles/permissioning/agent/", + "utils", ], }; diff --git a/.solhint.json b/.solhint.json index aa649c29..9e7603d4 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,7 +1,7 @@ { "extends": "solhint:recommended", "rules": { - "compiler-version": ["error", "^0.8.17"], + "compiler-version": ["error", "^0.8.26"], "func-visibility": ["warn", { "ignoreConstructors": true }], "reentrancy": "error", "state-visibility": "error", @@ -21,14 +21,15 @@ "code-complexity": ["error", 7], "function-max-lines": ["error", 50], "max-line-length": ["error", 130], - "max-states-count": ["error", 15], + "max-states-count": ["warn", 15], "no-empty-blocks": "error", "no-unused-vars": "error", "payable-fallback": "error", "constructor-syntax": "error", "not-rely-on-time": "off", "reason-string": "off", - "no-global-import": "off" + "no-global-import": "off", + "gas-custom-errors": "off" }, "plugins": ["prettier"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 074ef788..e089cbc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,105 @@ # Change Log All notable changes to this project will be documented in this file. +## [4.2.0] + +### Added + +- Introduced **Token Listing Restrictions Module**: Investors can determine which tokens they can receive by whitelisting or blacklisting them + Token issuers can choose the listing type they prefer for their tokens: + WHITELISTING: investors must whitelist/allow the token address in order to receive it. + BLACKLISTING: investors can receive the token by default. If they do not want to receive it, they need to blacklist/disallow it. + +- Introduced **Investor Country Cap Module**: to limit the number of identities per country. + - The module allows the token owner to set a maximum number of identities per country. + +- **Default Allowance Mechanism**: + - Introduced a new feature allowing the contract owner to set certain addresses as trusted external smart contracts, enabling them to use `transferFrom` without requiring an explicit allowance from users. By default, users are opted in, allowing these contracts to have an "infinite allowance". Users can opt-out if they prefer to control allowances manually. + - Added custom errors and events to provide better feedback and traceability: + - Custom errors: `DefaultAllowanceAlreadyEnabled`, `DefaultAllowanceAlreadyDisabled`, `DefaultAllowanceAlreadySet`. + - Events: `DefaultAllowance`, `DefaultAllowanceDisabled`, `DefaultAllowanceEnabled`. + - Enhanced the `allowance` function to return `type(uint256).max` for addresses with default allowance enabled, unless the user has opted out. + +- **Eligibility Checks Toggle**: + - Added the ability for the token owner to enable or disable eligibility checks on the `IdentityRegistry` contract. + - **`disableEligibilityChecks`**: Allows the token owner to disable eligibility checks, making all addresses automatically verified by the `isVerified` function. + - **`enableEligibilityChecks`**: Allows the token owner to re-enable eligibility checks, restoring the full verification process. + - Introduced custom errors and events: + - Custom errors: `EligibilityChecksDisabledAlready`, `EligibilityChecksEnabledAlready`. + - Events: `EligibilityChecksDisabled`, `EligibilityChecksEnabled`. + - This feature provides flexibility for issuers to launch tokens with or without eligibility checks, based on their needs. + +- **ERC-165 Interface Implementation**: + - Implemented ERC-165 support across all major contracts in the suite, allowing them to explicitly declare the interfaces they support. This enhancement improves interoperability and makes it easier for external contracts and tools to interact with T-REX contracts. + - Each contract now implements the `supportsInterface` function to identify the supported interfaces, ensuring compliance with ERC-165 standards. + - In addition to implementing ERC-165, the ERC-3643 interfaces were organized into a new directory, with each interface inheriting from the original ERC-3643 standard interface. This setup allows for future expansion and maintains uniformity across the suite: + - `IERC3643`, `IERC3643IdentityRegistry`, `IERC3643Compliance`, `IERC3643ClaimTopicsRegistry`, `IERC3643TrustedIssuersRegistry`, `IERC3643IdentityRegistryStorage` + - For contracts that extend the ERC-3643 standard with new functions, `supportsInterface` now checks for both the new interface ID and the original ERC-3643 interface ID. + - For interfaces that did not change, `supportsInterface` checks for the ERC-3643 interface ID directly, omitting the empty version-specific interface to avoid redundancy. + +- **Add and Set Module in a Single Transaction**: + - Introduced the `addAndSetModule` function in the `ModularCompliance` contract, allowing the contract owner to add a new compliance module and interact with it in a single transaction. + - This function supports adding a module and performing up to 5 interactions with the module in one call, streamlining the setup process for compliance modules. + +- **Granular Agent Permissioning**: + - Introduced a new system to provide more granular control over agent roles on the token contract. + - **Default Behavior**: By default, agents can perform all actions (mint, burn, freeze tokens, freeze wallets, recover tokens, pause, unpause). + - **Restricting Permissions**: Token owners can now restrict specific roles for agents using the `setAgentRestrictions` function. + - Permissions can be restricted on actions like minting, burning, freezing, recovering, etc. + - Custom restrictions are stored in the `TokenRoles` struct. + - The `AgentRestrictionsSet` event is emitted whenever restrictions are updated for an agent. + - All agent-scoped functions (e.g., mint, burn) now check the agent’s permissions before executing the transaction, ensuring proper enforcement of these restrictions. + +- **Ethers.js Version Upgrade**: + - The project was upgraded from **Ethers v5** to **Ethers v6**, introducing several changes to the API and syntax. + - **Key Changes**: + - Adjusted syntax for contract deployments, interactions, and function calls to comply with Ethers v6. + - Replaced deprecated features from v5 with the new Ethers v6 equivalents, ensuring compatibility and future-proofing the project. + - Updated test suites and helper functions to use the Ethers v6 `Interface` and `Contract` classes, ensuring smooth testing of the updated code. + - This upgrade ensures better performance, enhanced security, and improved developer experience moving forward. + +- **Solidity Version Upgrade to 0.8.27**: + - Upgraded Solidity version from **0.8.17** to **0.8.27** across all contracts, bringing in multiple new features and improvements: + - **File-Level Event Declaration**: Events are now declared at the file level, simplifying code structure and improving readability. + - **Custom Errors for Require Statements**: All `require` clauses that previously returned a string reason for failure have been updated to use **custom errors**, making debugging easier and more efficient. + - This upgrade provides a cleaner, more efficient error handling process and improves overall code structure without affecting backward compatibility. + +- **Utility checker**: + - Add a new utility contract to check for freeze status, eligibility and compliance of an address for a token: + - Test if the transfer of tokens will fail + - Test the eligibility of an address for a token + - Test the compliance of an address for a token + - Test the freeze status of an address for a token + +### Updated + +- **Token Recovery Function**: + - The `recoveryAddress` function was significantly improved to handle more complex scenarios and prevent potential bugs: + - Removed the requirement for the `newWallet` to be a key on the `onchainID`, simplifying the process and reducing potential errors. + - Enhanced compatibility with shared identity registry storage, ensuring the function works correctly even when multiple tokens share the same identity registry storage. + - Added logic to handle recovery to an existing wallet that is already linked to the investor's identity, addressing scenarios where the `newWallet` is an existing wallet. + - The function now accurately updates the identity registry, preventing errors related to attempting to add or remove identities that are already present or absent. + - Improved overall logic to ensure smooth and error-free recovery operations, with events (`RecoverySuccess`, `TokensFrozen`, `TokensUnfrozen`, etc.) emitted to provide detailed feedback. + +- **Ownership Transfer**: + - Implemented a two-step ownership transfer process across all relevant contracts in the suite, using OpenZeppelin's implementation `Ownable2StepUpgradeable`. + - This change enhances security by requiring the new owner to accept ownership explicitly, preventing accidental transfers to incorrect or inaccessible addresses. + - Added new functions: + - `transferOwnership(address newOwner)`: Initiates the ownership transfer process. + - `acceptOwnership()`: Allows the pending owner to accept and finalize the ownership transfer. + - Introduced a `_pendingOwner` state variable to track the address of the pending new owner. + - Updated relevant events to reflect the two-step process: + - `OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)` + - `OwnershipTransferred(address indexed previousOwner, address indexed newOwner)` + - This change affects all contracts that previously used a single-step ownership transfer, ensuring consistent and secure ownership management across the entire suite. + +## [4.1.5] + +### Update +- DvA Transfer Manager contract proxified +- The DvA manager contract freezes the tokens to be transferred instead of being a vault (so it must be a token agent) +- Only the token owner (rather than agents) can call setApprovalCriteria, as it is part of the main token settings. + ## [4.1.4] ### Added @@ -210,4 +309,4 @@ Version 4.0.0 of TREX has been successfully audited by Hacken [more details here - changed required key for roles on AgentManager contract (MANAGEMENT key -> EXECUTION key) - changed required key for roles on OwnerManager contract (MANAGEMENT key -> EXECUTION key) - import interfaces from openzeppelin instead of local copy -- contract name : **Storage** -> **TokenStorage** +- contract name : **Storage** -> **TokenStorage** \ No newline at end of file diff --git a/contracts/DVA/DVATransferManager.sol b/contracts/DVA/DVATransferManager.sol index 0cf98a88..0a317d62 100644 --- a/contracts/DVA/DVATransferManager.sol +++ b/contracts/DVA/DVATransferManager.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,25 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../roles/AgentRole.sol"; import "../token/IToken.sol"; import "./IDVATransferManager.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "../utils/OwnableOnceNext2StepUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../roles/IERC173.sol"; -contract DVATransferManager is IDVATransferManager { +contract DVATransferManager is IDVATransferManager, Initializable, OwnableOnceNext2StepUpgradeable, UUPSUpgradeable, IERC165 { // Mapping for token approval criteria mapping(address => ApprovalCriteria) private _approvalCriteria; @@ -77,7 +88,8 @@ contract DVATransferManager is IDVATransferManager { // nonce of the transaction allowing the creation of unique transferID uint256 private _txNonce; - constructor(){ + function initialize() external initializer { + __Ownable_init(); _txNonce = 0; } @@ -91,13 +103,8 @@ contract DVATransferManager is IDVATransferManager { bool sequentialApproval, address[] memory additionalApprovers ) external { - if (!AgentRole(tokenAddress).isAgent(msg.sender)) { - revert OnlyTokenAgentCanCall(tokenAddress); - } - - if (!IToken(tokenAddress).identityRegistry().isVerified(address(this))) { - revert DVAManagerIsNotVerifiedForTheToken(tokenAddress); - } + require(AgentRole(tokenAddress).owner() == msg.sender, OnlyTokenOwnerCanCall(tokenAddress)); + require(AgentRole(tokenAddress).isAgent(address(this)), DVAManagerIsNotAnAgentOfTheToken(tokenAddress)); bytes32 hash = keccak256( abi.encode( @@ -130,16 +137,12 @@ contract DVATransferManager is IDVATransferManager { */ function initiateTransfer(address tokenAddress, address recipient, uint256 amount) external { ApprovalCriteria memory approvalCriteria = _approvalCriteria[tokenAddress]; - if (approvalCriteria.hash == bytes32(0)) { - revert TokenIsNotRegistered(tokenAddress); - } + require(approvalCriteria.hash != bytes32(0), TokenIsNotRegistered(tokenAddress)); IToken token = IToken(tokenAddress); - if (!token.identityRegistry().isVerified(recipient)) { - revert RecipientIsNotVerified(tokenAddress, recipient); - } + require(token.identityRegistry().isVerified(recipient), RecipientIsNotVerified(tokenAddress, recipient)); - token.transferFrom(msg.sender, address(this), amount); + token.freezePartialTokens(msg.sender, amount); uint256 nonce = _txNonce++; bytes32 transferID = calculateTransferID(nonce, msg.sender, recipient, amount); @@ -182,9 +185,7 @@ contract DVATransferManager is IDVATransferManager { * @dev See {IDVATransferManager-delegateApproveTransfer} */ function delegateApproveTransfer(bytes32 transferID, Signature[] memory signatures) external { - if (signatures.length == 0) { - revert SignaturesCanNotBeEmpty(transferID); - } + require(signatures.length > 0, SignaturesCanNotBeEmpty(transferID)); Transfer storage transfer = _getPendingTransfer(transferID); if (_approvalCriteriaChanged(transferID, transfer)) { @@ -209,12 +210,10 @@ contract DVATransferManager is IDVATransferManager { */ function cancelTransfer(bytes32 transferID) external { Transfer storage transfer = _getPendingTransfer(transferID); - if (msg.sender != transfer.sender) { - revert OnlyTransferSenderCanCall(transferID); - } + require(msg.sender == transfer.sender, OnlyTransferSenderCanCall(transferID)); transfer.status = TransferStatus.CANCELLED; - _transferTokensTo(transfer, transfer.sender); + IToken(transfer.tokenAddress).unfreezePartialTokens(transfer.sender, transfer.amount); emit TransferCancelled(transferID); } @@ -240,17 +239,13 @@ contract DVATransferManager is IDVATransferManager { break; } - if (approvalCriteria.sequentialApproval) { - revert ApprovalsMustBeSequential(transferID); - } + require(!approvalCriteria.sequentialApproval, ApprovalsMustBeSequential(transferID)); } - if (!rejected) { - revert ApproverNotFound(transferID, msg.sender); - } + require(rejected, ApproverNotFound(transferID, msg.sender)); transfer.status = TransferStatus.REJECTED; - _transferTokensTo(transfer, transfer.sender); + IToken(transfer.tokenAddress).unfreezePartialTokens(transfer.sender, transfer.amount); emit TransferRejected(transferID, msg.sender); } @@ -259,9 +254,7 @@ contract DVATransferManager is IDVATransferManager { */ function getApprovalCriteria(address tokenAddress) external view returns (ApprovalCriteria memory) { ApprovalCriteria memory approvalCriteria = _approvalCriteria[tokenAddress]; - if (approvalCriteria.hash == bytes32(0)) { - revert TokenIsNotRegistered(tokenAddress); - } + require(approvalCriteria.hash != bytes32(0), TokenIsNotRegistered(tokenAddress)); return approvalCriteria; } @@ -273,9 +266,7 @@ contract DVATransferManager is IDVATransferManager { */ function getTransfer(bytes32 transferID) external view returns (Transfer memory) { Transfer memory transfer = _transfers[transferID]; - if (transfer.tokenAddress == address(0)) { - revert InvalidTransferID(transferID); - } + require(transfer.tokenAddress != address(0), InvalidTransferID(transferID)); return transfer; } @@ -312,6 +303,16 @@ contract DVATransferManager is IDVATransferManager { return "DVATransferManager"; } + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IDVATransferManager).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + /** * @dev See {IDVATransferManager-calculateTransferID} */ @@ -355,23 +356,22 @@ contract DVATransferManager is IDVATransferManager { continue; } - if (approvalCriteria.sequentialApproval) { - revert ApprovalsMustBeSequential(transferID); - } + require(!approvalCriteria.sequentialApproval, ApprovalsMustBeSequential(transferID)); pendingApproverCount++; } - if (!approved) { - revert ApproverNotFound(transferID, caller); - } + require(approved, ApproverNotFound(transferID, caller)); return pendingApproverCount == 0; } function _completeTransfer(bytes32 transferID, Transfer storage transfer) internal { transfer.status = TransferStatus.COMPLETED; - _transferTokensTo(transfer, transfer.recipient); + IToken token = IToken(transfer.tokenAddress); + token.unfreezePartialTokens(transfer.sender, transfer.amount); + token.transferFrom(transfer.sender, address(this), transfer.amount); + token.transfer(transfer.recipient, transfer.amount); emit TransferCompleted( transferID, transfer.tokenAddress, @@ -412,10 +412,6 @@ contract DVATransferManager is IDVATransferManager { } } - function _transferTokensTo(Transfer memory transfer, address to) internal { - IToken(transfer.tokenAddress).transfer(to, transfer.amount); - } - function _canApprove(Transfer memory transfer, Approver memory approver, address caller) internal view returns (bool) { return approver.wallet == caller || (approver.anyTokenAgent && approver.wallet == address(0) && AgentRole(transfer.tokenAddress).isAgent(caller)); @@ -423,17 +419,15 @@ contract DVATransferManager is IDVATransferManager { function _getPendingTransfer(bytes32 transferID) internal view returns (Transfer storage) { Transfer storage transfer = _transfers[transferID]; - if (transfer.tokenAddress == address(0)) { - revert InvalidTransferID(transferID); - } - - if (transfer.status != TransferStatus.PENDING) { - revert TransferIsNotInPendingStatus(transferID); - } + require(transfer.tokenAddress != address(0), InvalidTransferID(transferID)); + require(transfer.status == TransferStatus.PENDING, TransferIsNotInPendingStatus(transferID)); return transfer; } + // solhint-disable-next-line no-empty-blocks + function _authorizeUpgrade(address /*newImplementation*/) internal view override virtual onlyOwner { } + function _generateTransferSignatureHash(bytes32 transferID) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", transferID)); } diff --git a/contracts/DVA/DVATransferManagerProxy.sol b/contracts/DVA/DVATransferManagerProxy.sol new file mode 100644 index 00000000..1e93ee57 --- /dev/null +++ b/contracts/DVA/DVATransferManagerProxy.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DVATransferManagerProxy is ERC1967Proxy { + // solhint-disable-next-line no-empty-blocks + constructor(address implementation, bytes memory _data) ERC1967Proxy(implementation, _data) { } +} \ No newline at end of file diff --git a/contracts/DVA/IDVATransferManager.sol b/contracts/DVA/IDVATransferManager.sol index 4e9b26e8..69eab01b 100644 --- a/contracts/DVA/IDVATransferManager.sol +++ b/contracts/DVA/IDVATransferManager.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,13 +59,106 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../roles/AgentRole.sol"; import "../token/IToken.sol"; +/// Events + +/// @dev This event is emitted whenever an approval criteria of a token is modified. +/// @param _tokenAddress is the token address. +/// @param _includeRecipientApprover determines whether the recipient is included in the approver list. +/// @param _includeAgentApprover determines whether the agent is included in the approver list. +/// @param _sequentialApproval determines whether approvals must be sequential. +/// @param _additionalApprovers are the addresses of additional approvers to be added to the approver list. +/// @param _hash is the approval criteria hash +event ApprovalCriteriaSet( + address _tokenAddress, + bool _includeRecipientApprover, + bool _includeAgentApprover, + bool _sequentialApproval, + address[] _additionalApprovers, + bytes32 _hash); + +/// @dev This event is emitted whenever a transfer is initiated. +/// @param _transferID is the unique ID of the transfer. +/// @param _tokenAddress is the token address. +/// @param _sender is the address of the sender. +/// @param _recipient is the address of the recipient. +/// @param _amount is the amount of the transfer. +/// @param _approvalCriteriaHash is the approval criteria hash. +event TransferInitiated( + bytes32 _transferID, + address _tokenAddress, + address _sender, + address _recipient, + uint256 _amount, + bytes32 _approvalCriteriaHash); + +/// @dev This event is emitted whenever a transfer is approved by an approver. +/// @param _transferID is the unique ID of the transfer. +/// @param _approver is the approver address. +event TransferApproved(bytes32 _transferID, address _approver); + +/// @dev This event is emitted whenever a transfer is rejected by an approver. +/// @param _transferID is the unique ID of the transfer. +/// @param _rejectedBy is the approver address. +event TransferRejected(bytes32 _transferID, address _rejectedBy); + +/// @dev This event is emitted whenever a transfer is cancelled by the sender. +/// @param _transferID is the unique ID of the transfer. +event TransferCancelled(bytes32 _transferID); + +/// @dev This event is emitted whenever all approvers approve a transfer. +/// @param _transferID is the unique ID of the transfer. +/// @param _tokenAddress is the token address. +/// @param _sender is the address of the sender. +/// @param _recipient is the address of the recipient. +/// @param _amount is the amount of the transfer. +event TransferCompleted( + bytes32 _transferID, + address _tokenAddress, + address _sender, + address _recipient, + uint256 _amount); + +/// @dev This event is emitted whenever a transfer approval criteria are reset. +/// @param _transferID is the unique ID of the transfer. +/// @param _approvalCriteriaHash is the approval criteria hash. +event TransferApprovalStateReset(bytes32 _transferID, bytes32 _approvalCriteriaHash); + + +// Errors + +error OnlyTokenOwnerCanCall(address _tokenAddress); + +error OnlyTransferSenderCanCall(bytes32 _transferID); + +error TokenIsNotRegistered(address _tokenAddress); + +error RecipientIsNotVerified(address _tokenAddress, address _recipient); + +error DVAManagerIsNotAnAgentOfTheToken(address _tokenAddress); + +error InvalidTransferID(bytes32 _transferID); + +error TransferIsNotInPendingStatus(bytes32 _transferID); + +error ApprovalsMustBeSequential(bytes32 _transferID); + +error ApproverNotFound(bytes32 _transferID, address _approver); + +error SignaturesCanNotBeEmpty(bytes32 _transferID); + + interface IDVATransferManager { enum TransferStatus { PENDING, @@ -103,124 +197,7 @@ interface IDVATransferManager { bytes32 s; } - /** - * this event is emitted whenever an approval criteria of a token is modified. - * the event is emitted by 'setApprovalCriteria' function. - * `tokenAddress` is the token address. - * `includeRecipientApprover` determines whether the recipient is included in the approver list - * `includeAgentApprover` determines whether the agent is included in the approver list - * `sequentialApproval` determines whether approvals must be sequential - * `additionalApprovers` are the addresses of additional approvers to be added to the approver list - * `hash` is the approval criteria hash - */ - event ApprovalCriteriaSet( - address tokenAddress, - bool includeRecipientApprover, - bool includeAgentApprover, - bool sequentialApproval, - address[] additionalApprovers, - bytes32 hash - ); - - /** - * this event is emitted whenever a transfer is initiated - * the event is emitted by 'initiateTransfer' function. - * `transferID` is the unique ID of the transfer - * `tokenAddress` is the token address - * `sender` is the address of the sender - * `recipient` is the address of the recipient - * `amount` is the amount of the transfer - * `approvers` is the list of approvers - * `approvalCriteriaHash` is the approval criteria hash - */ - event TransferInitiated( - bytes32 transferID, - address tokenAddress, - address sender, - address recipient, - uint256 amount, - bytes32 approvalCriteriaHash - ); - - /** - * this event is emitted whenever a transfer is approved by an approver - * the event is emitted by 'approveTransfer' function. - * `transferID` is the unique ID of the transfer - * `approver` is the approver address - */ - event TransferApproved( - bytes32 transferID, - address approver - ); - - /** - * this event is emitted whenever a transfer is rejected by an approver - * the event is emitted by 'rejectTransfer' function. - * `transferID` is the unique ID of the transfer - * `rejectedBy` is the approver address - */ - event TransferRejected( - bytes32 transferID, - address rejectedBy - ); - - /** - * this event is emitted whenever a transfer is cancelled by the sender - * the event is emitted by 'cancelTransfer' function. - * `transferID` is the unique ID of the transfer - */ - event TransferCancelled( - bytes32 transferID - ); - - /** - * this event is emitted whenever all approvers approve a transfer - * the event is emitted by 'approveTransfer' function. - * `transferID` is the unique ID of the transfer - * `tokenAddress` is the token address - * `sender` is the address of the sender - * `recipient` is the address of the recipient - * `amount` is the amount of the transfer - */ - event TransferCompleted( - bytes32 transferID, - address tokenAddress, - address sender, - address recipient, - uint256 amount - ); - - /** - * this event is emitted whenever a transfer approval criteria are reset - * the event is emitted by 'approveTransfer' and 'rejectTransfer' functions. - * `transferID` is the unique ID of the transfer - * `approvers` is the list of approvers - * `approvalCriteriaHash` is the approval criteria hash - */ - event TransferApprovalStateReset( - bytes32 transferID, - bytes32 approvalCriteriaHash - ); - - error OnlyTokenAgentCanCall(address _tokenAddress); - - error OnlyTransferSenderCanCall(bytes32 _transferID); - - error TokenIsNotRegistered(address _tokenAddress); - - error RecipientIsNotVerified(address _tokenAddress, address _recipient); - - error DVAManagerIsNotVerifiedForTheToken(address _tokenAddress); - - error InvalidTransferID(bytes32 _transferID); - - error TransferIsNotInPendingStatus(bytes32 _transferID); - - error ApprovalsMustBeSequential(bytes32 _transferID); - - error ApproverNotFound(bytes32 _transferID, address _approver); - error SignaturesCanNotBeEmpty(bytes32 _transferID); /** * @dev modify the approval criteria of a token diff --git a/contracts/DVD/DVDTransferManager.sol b/contracts/DVD/DVDTransferManager.sol index cbc76331..9c06741c 100644 --- a/contracts/DVD/DVDTransferManager.sol +++ b/contracts/DVD/DVDTransferManager.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,12 +59,80 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../roles/AgentRole.sol"; import "../token/IToken.sol"; +import "../errors/CommonErrors.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + + +/// events + +/** +* @dev Emitted when a DVD transfer is initiated by `maker` to swap `token1Amount` tokens `token1` (TREX or not) +* for `token2Amount` tokens `token2` with `taker` +* this event is emitted by the `initiateDVDTransfer` function +*/ +event DVDTransferInitiated( + bytes32 indexed transferID, + address maker, + address indexed token1, + uint256 token1Amount, + address taker, + address indexed token2, + uint256 token2Amount); + +/** +* @dev Emitted when a DVD transfer is validated by `taker` and +* executed either by `taker` either by the agent of the TREX token +* if the TREX token is subject to conditional transfers +* this event is emitted by the `takeDVDTransfer` function +*/ +event DVDTransferExecuted(bytes32 indexed transferID); + +/** +* @dev Emitted when a DVD transfer is cancelled +* this event is emitted by the `cancelDVDTransfer` function +*/ +event DVDTransferCancelled(bytes32 indexed transferID); + +/** +* @dev Emitted when a DVD transfer is cancelled +* this event is emitted by the `cancelDVDTransfer` function +*/ +event FeeModified( + bytes32 indexed parity, + address token1, + address token2, + uint fee1, + uint fee2, + uint feeBase, + address fee1Wallet, + address fee2Wallet); + + +/// Errors + +// @dev Thrown when the fee settings are invalid. +error InvalidFeeSettings(); + +// @dev Thrown when the transfer ID doesn't exist. +error TransferIDDoesNotExist(); + +// @dev Thrown when the transfer is not done by counterpart or owner. +error TransferOnlyByCounterpartOrOwner(); + +// @dev Thrown when the cancel is not done by counterpart, owner or agent. +error CancelOnlyByCounterpartOrOwnerOrAgent(); contract DVDTransferManager is Ownable { @@ -105,50 +174,6 @@ contract DVDTransferManager is Ownable { // nonce of the transaction allowing the creation of unique transferID uint256 public txNonce; - /// events - - /** - * @dev Emitted when a DVD transfer is initiated by `maker` to swap `token1Amount` tokens `token1` (TREX or not) - * for `token2Amount` tokens `token2` with `taker` - * this event is emitted by the `initiateDVDTransfer` function - */ - event DVDTransferInitiated( - bytes32 indexed transferID, - address maker, - address indexed token1, - uint256 token1Amount, - address taker, - address indexed token2, - uint256 token2Amount); - - /** - * @dev Emitted when a DVD transfer is validated by `taker` and - * executed either by `taker` either by the agent of the TREX token - * if the TREX token is subject to conditional transfers - * this event is emitted by the `takeDVDTransfer` function - */ - event DVDTransferExecuted(bytes32 indexed transferID); - - /** - * @dev Emitted when a DVD transfer is cancelled - * this event is emitted by the `cancelDVDTransfer` function - */ - event DVDTransferCancelled(bytes32 indexed transferID); - - /** - * @dev Emitted when a DVD transfer is cancelled - * this event is emitted by the `cancelDVDTransfer` function - */ - event FeeModified( - bytes32 indexed parity, - address token1, - address token2, - uint fee1, - uint fee2, - uint feeBase, - address fee1Wallet, - address fee2Wallet); - /// functions // initiates the nonce at 0 @@ -187,23 +212,20 @@ contract DVDTransferManager is Ownable { msg.sender == owner() || isTREXOwner(_token1, msg.sender) || isTREXOwner(_token2, msg.sender) - , "Ownable: only owner can call"); + , OwnableUnauthorizedAccount(msg.sender)); require( IERC20(_token1).totalSupply() != 0 && IERC20(_token2).totalSupply() != 0 - , "invalid address : address is not an ERC20"); + , AddressNotERC20(IERC20(_token1).totalSupply() != 0 ? _token2 : _token1)); require( - _fee1 <= 10**_feeBase && _fee1 >= 0 && - _fee2 <= 10**_feeBase && _fee2 >= 0 && + _fee1 <= 10**_feeBase && + _fee2 <= 10**_feeBase && _feeBase <= 5 && _feeBase >= 2 - , "invalid fee settings"); - if (_fee1 > 0) { - require(_fee1Wallet != address(0), "fee wallet 1 cannot be zero address"); - } - if (_fee2 > 0) { - require(_fee2Wallet != address(0), "fee wallet 2 cannot be zero address"); - } + , InvalidFeeSettings()); + require(_fee1 == 0 || _fee1Wallet != address(0), ZeroAddress()); + require(_fee2 == 0 || _fee2Wallet != address(0), ZeroAddress()); + bytes32 _parity = calculateParity(_token1, _token2); Fee memory parityFee; parityFee.token1Fee = _fee1; @@ -244,12 +266,14 @@ contract DVDTransferManager is Ownable { address _counterpart, address _token2, uint256 _token2Amount) external { - require(IERC20(_token1).balanceOf(msg.sender) >= _token1Amount, "Not enough tokens in balance"); - require( - IERC20(_token1).allowance(msg.sender, address(this)) >= _token1Amount - , "not enough allowance to initiate transfer"); - require (_counterpart != address(0), "counterpart cannot be null"); - require(IERC20(_token2).totalSupply() != 0, "invalid address : address is not an ERC20"); + uint256 balance = IERC20(_token1).balanceOf(msg.sender); + require(balance >= _token1Amount, ERC20InsufficientBalance(msg.sender, balance, _token1Amount)); + + uint256 allowance = IERC20(_token1).allowance(msg.sender, address(this)); + require(allowance >= _token1Amount, ERC20InsufficientAllowance(msg.sender, allowance, _token1Amount)); + + require (_counterpart != address(0), ZeroAddress()); + require(IERC20(_token2).totalSupply() != 0, AddressNotERC20(_token2)); Delivery memory token1; token1.counterpart = msg.sender; token1.token = _token1; @@ -308,20 +332,16 @@ contract DVDTransferManager is Ownable { Delivery memory token2 = token2ToDeliver[_transferID]; require( token1.counterpart != address(0) && token2.counterpart != address(0) - , "transfer ID does not exist"); + , TransferIDDoesNotExist()); IERC20 token1Contract = IERC20(token1.token); IERC20 token2Contract = IERC20(token2.token); require ( msg.sender == token2.counterpart || isTREXAgent(token1.token, msg.sender) || isTREXAgent(token2.token, msg.sender) - , "transfer has to be done by the counterpart or by owner"); - require( - token2Contract.balanceOf(token2.counterpart) >= token2.amount - , "Not enough tokens in balance"); - require( - token2Contract.allowance(token2.counterpart, address(this)) >= token2.amount - , "not enough allowance to transfer"); + , TransferOnlyByCounterpartOrOwner()); + // @dev balance and allowance not checked here, as it is done in transferFrom + TxFees memory fees = calculateFee(_transferID); if (fees.txFee1 != 0) { token1Contract.transferFrom(token1.counterpart, token2.counterpart, (token1.amount - fees.txFee1)); @@ -356,34 +376,28 @@ contract DVDTransferManager is Ownable { function cancelDVDTransfer(bytes32 _transferID) external { Delivery memory token1 = token1ToDeliver[_transferID]; Delivery memory token2 = token2ToDeliver[_transferID]; - require(token1.counterpart != address(0) && token2.counterpart != address(0), "transfer ID does not exist"); + require(token1.counterpart != address(0) && token2.counterpart != address(0), TransferIDDoesNotExist()); require ( msg.sender == token2.counterpart || msg.sender == token1.counterpart || msg.sender == owner() || isTREXAgent(token1.token, msg.sender) || isTREXAgent(token2.token, msg.sender) - , "you are not allowed to cancel this transfer"); + , CancelOnlyByCounterpartOrOwnerOrAgent()); delete token1ToDeliver[_transferID]; delete token2ToDeliver[_transferID]; emit DVDTransferCancelled(_transferID); } /** - * @dev check if `_token` corresponds to a functional TREX token (with identity registry initiated) + * @dev check if `_token` corresponds to a TREX token * @param _token the address token to check - * the function will try to call `identityRegistry()` on - * the address, which is a getter specific to TREX tokens - * if the call pass and returns an address it means that - * the token is a TREX, otherwise it's not a TREX + * the function will check if the address implements IERC3643 * return `true` if the token is a TREX, `false` otherwise */ function isTREX(address _token) public view returns (bool) { - try IToken(_token).identityRegistry() returns (IIdentityRegistry _ir) { - if (address(_ir) != address(0)) { - return true; - } - return false; + try IERC165(_token).supportsInterface(type(IERC3643).interfaceId) returns (bool _trex) { + return _trex; } catch { return false; @@ -430,9 +444,7 @@ contract DVDTransferManager is Ownable { TxFees memory fees; Delivery memory token1 = token1ToDeliver[_transferID]; Delivery memory token2 = token2ToDeliver[_transferID]; - require( - token1.counterpart != address(0) && token2.counterpart != address(0) - , "transfer ID does not exist"); + require(token1.counterpart != address(0) && token2.counterpart != address(0), TransferIDDoesNotExist()); bytes32 parity = calculateParity(token1.token, token2.token); Fee memory feeDetails = fee[parity]; if (feeDetails.token1Fee != 0 || feeDetails.token2Fee != 0 ){ diff --git a/contracts/ERC-3643/IERC3643.sol b/contracts/ERC-3643/IERC3643.sol new file mode 100644 index 00000000..d309121e --- /dev/null +++ b/contracts/ERC-3643/IERC3643.sol @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +pragma solidity 0.8.27; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import "./IERC3643IdentityRegistry.sol"; +import "./IERC3643Compliance.sol"; + +/// Events + +/// @dev This event is emitted when the token information is updated. +/// @param _newName is the name of the token. +/// @param _newSymbol is the symbol of the token. +/// @param _newDecimals is the decimals of the token. +/// @param _newVersion is the version of the token. +/// @param _newOnchainID is the address of the onchainID of the token. +event UpdatedTokenInformation(string indexed _newName, string indexed _newSymbol, uint8 _newDecimals, + string _newVersion, address indexed _newOnchainID); + +/// @dev This event is emitted when the IdentityRegistry has been set for the token. +/// @param _identityRegistry is the address of the Identity Registry of the token. +event IdentityRegistryAdded(address indexed _identityRegistry); + +/// @dev This event is emitted when the Compliance has been set for the token. +/// @param _compliance is the address of the Compliance contract of the token. +event ComplianceAdded(address indexed _compliance); + +/// @dev This event is emitted when an investor successfully recovers his tokens. +/// @param _lostWallet is the address of the wallet that the investor lost access to. +/// @param _newWallet is the address of the wallet that the investor provided for the recovery. +/// @param _investorOnchainID is the address of the onchainID of the investor who asked for a recovery. +event RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID); + +/// @dev This event is emitted when the wallet of an investor is frozen or unfrozen. +/// @param _userAddress is the wallet of the investor that is concerned by the freezing status. +/// @param _isFrozen is the freezing status of the wallet. +/// @param _isFrozen equals `true` the wallet is frozen after emission of the event. +/// @param _isFrozen equals `false` the wallet is unfrozen after emission of the event. +/// @param _owner is the address of the agent who called the function to freeze the wallet. +event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); + +/// @dev This event is emitted when a certain amount of tokens is frozen on a wallet. +/// @param _userAddress is the wallet of the investor that is concerned by the freezing status. +/// @param _amount is the amount of tokens that are frozen. +event TokensFrozen(address indexed _userAddress, uint256 _amount); + +/// @dev This event is emitted when a certain amount of tokens is unfrozen on a wallet. +/// @param _userAddress is the wallet of the investor that is concerned by the freezing status. +/// @param _amount is the amount of tokens that are unfrozen. +event TokensUnfrozen(address indexed _userAddress, uint256 _amount); + +/// @dev This event is emitted when the token is paused. +/// @param _userAddress is the address of the wallet that called the pause function +event Paused(address _userAddress); + +/// @dev This event is emitted when the token is unpaused. +/// @param _userAddress is the address of the wallet that called the unpause function. +event Unpaused(address _userAddress); + + +interface IERC3643 is IERC20, IERC20Metadata { + + /// Functions + + /// Setters + + /** + * @dev sets the token name + * @param _name the name of token to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setName(string calldata _name) external; + + /** + * @dev sets the token symbol + * @param _symbol the token symbol to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setSymbol(string calldata _symbol) external; + + /** + * @dev sets the onchain ID of the token + * @param _onchainID the address of the onchain ID to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setOnchainID(address _onchainID) external; + + /** + * @dev Pauses the token contract. When the contract is paused, investors cannot transfer tokens anymore. + * This function can only be called by an agent of the token, provided the agent is not restricted from pausing the token. + * emits a `Paused` event upon successful execution. + * To pause token transfers, the calling agent must have pausing capabilities enabled. + * If the agent is disabled from pausing, the function call will fail. + * The function can be called only when the contract is not already paused. + * error AgentNotAuthorized - Thrown if the agent is disabled from pausing the token, + * indicating they do not have the necessary permissions to execute this function. + */ + function pause() external; + + /** + * @dev Unpauses the token contract, allowing investors to resume token transfers under normal conditions + * This function can only be called by an agent of the token, provided the agent is not restricted from pausing the token. + * emits an `Unpaused` event upon successful execution. + * To unpause token transfers, the calling agent must have pausing capabilities enabled. + * If the agent is disabled from pausing, the function call will fail. + * The function can be called only when the contract is currently paused. + * error AgentNotAuthorized - Thrown if the agent is disabled from pausing the token, + * indicating they do not have the necessary permissions to execute this function. + */ + function unpause() external; + + /** + * @dev Sets an address's frozen status for this token, + * either freezing or unfreezing the address based on the provided boolean value. + * This function can be called by an agent of the token, assuming the agent is not restricted from freezing addresses. + * emits an `AddressFrozen` event upon successful execution. + * @param _userAddress The address for which to update the frozen status. + * @param _freeze The frozen status to be applied: `true` to freeze, `false` to unfreeze. + * @notice To change an address's frozen status, the calling agent must have the capability to freeze addresses enabled. + * If the agent is disabled from freezing addresses, the function call will fail. + * error AgentNotAuthorized - Thrown if the agent is disabled from freezing addresses, + * indicating they do not have the necessary permissions to execute this function. + */ + function setAddressFrozen(address _userAddress, bool _freeze) external; + + /** + * @dev Freezes a specified token amount for a given address, preventing those tokens from being transferred. + * This function can be called by an agent of the token, provided the agent is not restricted from freezing tokens. + * emits a `TokensFrozen` event upon successful execution. + * @param _userAddress The address for which to freeze tokens. + * @param _amount The amount of tokens to be frozen. + * @notice To freeze tokens for an address, the calling agent must have the capability to freeze tokens enabled. + * If the agent is disabled from freezing tokens, the function call will fail. + * error AgentNotAuthorized - Thrown if the agent is disabled from freezing tokens, + * indicating they do not have the necessary permissions to execute this function. + */ + function freezePartialTokens(address _userAddress, uint256 _amount) external; + + /** + * @dev Unfreezes a specified token amount for a given address, allowing those tokens to be transferred again. + * This function can be called by an agent of the token, assuming the agent is not restricted from unfreezing tokens. + * emits a `TokensUnfrozen` event upon successful execution. + * @param _userAddress The address for which to unfreeze tokens. + * @param _amount The amount of tokens to be unfrozen. + * @notice To unfreeze tokens for an address, the calling agent must have the capability to unfreeze tokens enabled. + * If the agent is disabled from unfreezing tokens, the function call will fail. + * error AgentNotAuthorized - Thrown if the agent is disabled from unfreezing tokens, + * indicating they do not have the necessary permissions to execute this function. + */ + function unfreezePartialTokens(address _userAddress, uint256 _amount) external; + + /** + * @dev sets the Identity Registry for the token + * @param _identityRegistry the address of the Identity Registry to set + * Only the owner of the token smart contract can call this function + * emits an `IdentityRegistryAdded` event + */ + function setIdentityRegistry(address _identityRegistry) external; + + /** + * @dev sets the compliance contract of the token + * @param _compliance the address of the compliance contract to set + * Only the owner of the token smart contract can call this function + * calls bindToken on the compliance contract + * emits a `ComplianceAdded` event + */ + function setCompliance(address _compliance) external; + + /// Transfer Actions + + /** + * @dev Initiates a forced transfer of tokens between two whitelisted wallets. + * If the `from` address does not have sufficient free tokens (unfrozen tokens) + * but possesses a total balance equal to or greater than the specified `amount`, + * the frozen token amount is reduced to ensure enough free tokens for the transfer. + * In such cases, the remaining balance in the `from` account consists entirely of frozen tokens post-transfer. + * It is imperative that the `to` address is a verified and whitelisted address. + * @param _from The address of the sender. + * @param _to The address of the receiver. + * @param _amount The number of tokens to be transferred. + * This function can only be invoked by a wallet designated as an agent of the token, + * provided the agent is not restricted from initiating forced transfers of the token. + * Emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_from`. + * Also emits a `Transfer` event. + * To execute this function, the calling agent must not be restricted from initiating forced transfers of the token. + * If the agent is restricted from this capability, the function call will fail. + * The function can only be called when the contract is not already paused. + * error `AgentNotAuthorized` - Thrown if the agent is restricted from initiating forced transfers of the token, + * indicating they do not have the necessary permissions to execute this function. + */ + function forcedTransfer( + address _from, + address _to, + uint256 _amount + ) external returns (bool); + + /** + * @dev Mints tokens to a specified address. + * This enhanced version of the default mint method allows tokens to be minted + * to an address only if it is a verified and whitelisted address according to the security token. + * @param _to Address to mint the tokens to. + * @param _amount Amount of tokens to mint. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from minting tokens. + * Emits a `Transfer` event upon successful minting. + * To execute this function, the calling agent must not be restricted from minting tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function mint(address _to, uint256 _amount) external; + + /** + * @dev Burns tokens from a specified address. + * If the account address does not have sufficient free tokens (unfrozen tokens) + * but possesses a total balance equal to or greater than the specified value, + * the frozen token amount is reduced to ensure enough free tokens for the burn. + * In such cases, the remaining balance in the account consists entirely of frozen tokens post-transaction. + * @param _userAddress Address to burn the tokens from. + * @param _amount Amount of tokens to burn. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from burning tokens. + * Emits a `TokensUnfrozen` event if `_amount` exceeds the free balance of `_userAddress`. + * Also emits a `Transfer` event. + * To execute this function, the calling agent must not be restricted from burning tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function burn(address _userAddress, uint256 _amount) external; + + /** + * @dev Initiates a recovery process to transfer tokens and associated states + * from a lost wallet to a new wallet for an investor. + * + * This function allows an authorized agent to recover tokens from a lost wallet, + * transferring them to a new wallet while preserving the investor's + * identity and status within the token ecosystem. The function ensures that all relevant data, + * including frozen tokens and address freezing status, is accurately transferred to the new wallet. + * + * @param _lostWallet The wallet that the investor lost, containing the tokens to be recovered. + * @param _newWallet The newly provided wallet to which tokens and associated statuses must be transferred. + * @param _investorOnchainID The ONCHAINID of the investor whose tokens are being recovered. + * + * Requirements: + * - The caller must be an agent authorized to perform recovery operations, with no restrictions on this capability. + * - The `_lostWallet` must have a non-zero token balance; otherwise, the recovery is unnecessary and will revert. + * - Either the `_lostWallet` or the `_newWallet` must be present in the identity registry; + * if neither is present, the function will revert. + * + * Operations: + * - Transfers the entire token balance from `_lostWallet` to `_newWallet`. + * - Transfers any frozen tokens from `_lostWallet` to `_newWallet`, updating the frozen token count accordingly. + * - Transfers the address freeze status from `_lostWallet` to `_newWallet`, + * ensuring the new wallet retains any restrictions if applicable. + * - Updates the identity registry: + * - If `_lostWallet` is listed in the identity registry, it will be removed, + * and `_newWallet` will be registered unless already present. + * + * Emits the following events: + * - `TokensUnfrozen` if there are frozen tokens on `_lostWallet` that are transferred. + * - `TokensFrozen` if frozen tokens are added to `_newWallet`. + * - `AddressFrozen` if the freeze status of either wallet changes. + * - `Transfer` to reflect the movement of tokens from `_lostWallet` to `_newWallet`. + * - `RecoverySuccess` upon successful completion of the recovery process. + * + * Reverts if: + * - The agent calling the function does not have the necessary permissions to perform recovery (`AgentNotAuthorized`). + * - The `_lostWallet` has no tokens to recover (`NoTokenToRecover`). + * - Neither `_lostWallet` nor `_newWallet` is present in the identity registry (`RecoveryNotPossible`). + * + * @return A boolean value indicating whether the recovery process was successful. + */ + function recoveryAddress( + address _lostWallet, + address _newWallet, + address _investorOnchainID + ) external returns (bool); + + /// Batch Actions + + /** + * @dev function allowing to issue transfers in batch + * Require that the msg.sender and `to` addresses are not frozen. + * Require that the total value should not exceed available balance. + * Require that the `to` addresses are all verified addresses, + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _toList The addresses of the receivers + * @param _amounts The number of tokens to transfer to the corresponding receiver + * emits _toList.length `Transfer` events + */ + function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external; + + /** + * @dev Initiates forced transfers in batch. + * Requires that each _amounts[i] does not exceed the available balance of _fromList[i]. + * Requires that the _toList addresses are all verified and whitelisted addresses. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF _fromList.length IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _fromList The addresses of the senders. + * @param _toList The addresses of the receivers. + * @param _amounts The number of tokens to transfer to the corresponding receiver. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from initiating forced transfers in batch. + * Emits `TokensUnfrozen` events for each `_amounts[i]` that exceeds the free balance of `_fromList[i]`. + * Also emits _fromList.length `Transfer` events upon successful batch transfer. + * To execute this function, the calling agent must not be restricted from initiating forced transfer. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchForcedTransfer( + address[] calldata _fromList, + address[] calldata _toList, + uint256[] calldata _amounts + ) external; + + /** + * @dev Initiates minting of tokens in batch. + * Requires that the `_toList` addresses are all verified and whitelisted addresses. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _toList The addresses of the receivers. + * @param _amounts The number of tokens to mint to the corresponding receiver. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from minting tokens. + * Emits _toList.length `Transfer` events upon successful batch minting. + * To execute this function, the calling agent must not be restricted from minting tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external; + + /** + * @dev Initiates burning of tokens in batch. + * Requires that the `_userAddresses` addresses are all verified and whitelisted addresses. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _userAddresses The addresses of the wallets concerned by the burn. + * @param _amounts The number of tokens to burn from the corresponding wallets. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from burning tokens. + * Emits _userAddresses.length `Transfer` events upon successful batch burn. + * To execute this function, the calling agent must not be restricted from burning tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + + /** + * @dev Initiates setting of frozen status for addresses in batch. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _userAddresses The addresses for which to update frozen status. + * @param _freeze Frozen status of the corresponding address. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from setting frozen addresses. + * Emits _userAddresses.length `AddressFrozen` events upon successful batch update of frozen status. + * To execute this function, the calling agent must not be restricted from setting frozen addresses. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; + + /** + * @dev Initiates partial freezing of tokens in batch. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _userAddresses The addresses on which tokens need to be partially frozen. + * @param _amounts The amount of tokens to freeze on the corresponding address. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from partially freezing tokens. + * Emits _userAddresses.length `TokensFrozen` events upon successful batch partial freezing. + * To execute this function, the calling agent must not be restricted from partially freezing tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + + /** + * @dev Initiates partial unfreezing of tokens in batch. + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH. + * USE WITH CARE TO AVOID "OUT OF GAS" TRANSACTIONS AND POTENTIAL LOSS OF TX FEES. + * @param _userAddresses The addresses on which tokens need to be partially unfrozen. + * @param _amounts The amount of tokens to unfreeze on the corresponding address. + * This function can only be called by a wallet designated as an agent of the token, + * provided the agent is not restricted from partially freezing tokens. + * Emits _userAddresses.length `TokensUnfrozen` events upon successful batch partial unfreezing. + * To execute this function, the calling agent must not be restricted from partially freezing tokens. + * If the agent is restricted from this capability, the function call will fail. + */ + function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + + /// Getters + + /** + * @dev Returns the address of the onchainID of the token. + * the onchainID of the token gives all the information available + * about the token and is managed by the token issuer or his agent. + */ + function onchainID() external view returns (address); + /** + * @dev Returns the TREX version of the token. + */ + function version() external view returns (string memory); + + /** + * @dev Returns the Identity Registry linked to the token + */ + function identityRegistry() external view returns (IERC3643IdentityRegistry); + + /** + * @dev Returns the Compliance contract linked to the token + */ + function compliance() external view returns (IERC3643Compliance); + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() external view returns (bool); + + /** + * @dev Returns the freezing status of a wallet + * if isFrozen returns `true` the wallet is frozen + * if isFrozen returns `false` the wallet is not frozen + * isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by + * a partial freeze or the whole token could be blocked by pause + * @param _userAddress the address of the wallet on which isFrozen is called + */ + function isFrozen(address _userAddress) external view returns (bool); + + /** + * @dev Returns the amount of tokens that are partially frozen on a wallet + * the amount of frozen tokens is always <= to the total balance of the wallet + * @param _userAddress the address of the wallet on which getFrozenTokens is called + */ + function getFrozenTokens(address _userAddress) external view returns (uint256); +} diff --git a/contracts/ERC-3643/IERC3643ClaimTopicsRegistry.sol b/contracts/ERC-3643/IERC3643ClaimTopicsRegistry.sol new file mode 100644 index 00000000..6644070a --- /dev/null +++ b/contracts/ERC-3643/IERC3643ClaimTopicsRegistry.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +pragma solidity 0.8.27; + +/// Events + +/// @dev This event is emitted when a claim topic has been added to the ClaimTopicsRegistry. +/// @param _claimTopic is the required claim added to the Claim Topics Registry. +event ClaimTopicAdded(uint256 indexed _claimTopic); + +/// @dev This event is emitted when a claim topic has been removed from the ClaimTopicsRegistry. +/// @param _claimTopic is the required claim removed from the Claim Topics Registry. +event ClaimTopicRemoved(uint256 indexed _claimTopic); + +interface IERC3643ClaimTopicsRegistry { + /** + * @dev Add a trusted claim topic (For example: KYC=1, AML=2). + * Only owner can call. + * emits `ClaimTopicAdded` event + * cannot add more than 15 topics for 1 token as adding more could create gas issues + * @param _claimTopic The claim topic index + */ + function addClaimTopic(uint256 _claimTopic) external; + + /** + * @dev Remove a trusted claim topic (For example: KYC=1, AML=2). + * Only owner can call. + * emits `ClaimTopicRemoved` event + * @param _claimTopic The claim topic index + */ + function removeClaimTopic(uint256 _claimTopic) external; + + /** + * @dev Get the trusted claim topics for the security token + * @return Array of trusted claim topics + */ + function getClaimTopics() external view returns (uint256[] memory); +} diff --git a/contracts/ERC-3643/IERC3643Compliance.sol b/contracts/ERC-3643/IERC3643Compliance.sol new file mode 100644 index 00000000..2969dad5 --- /dev/null +++ b/contracts/ERC-3643/IERC3643Compliance.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +pragma solidity 0.8.27; + +/// Events + +/// @dev This event is emitted when a token has been bound to the compliance contract. +/// @param _token is the address of the token to bind. +event TokenBound(address _token); + +/// @dev This event is emitted when a token has been unbound from the compliance contract. +/// @param _token is the address of the token to unbind. +event TokenUnbound(address _token); + +interface IERC3643Compliance { + + + /// Functions + + /// initialization of the compliance contract + + /** + * @dev binds a token to the compliance contract + * @param _token address of the token to bind + * This function can be called ONLY by the owner of the compliance contract + * Emits a TokenBound event + */ + function bindToken(address _token) external; + + /** + * @dev unbinds a token from the compliance contract + * @param _token address of the token to unbind + * This function can be called ONLY by the owner of the compliance contract + * Emits a TokenUnbound event + */ + function unbindToken(address _token) external; + + + + // compliance check and state update + /** + * @dev function called whenever tokens are transferred + * from one wallet to another + * this function can update state variables in the modules bound to the compliance + * these state variables being used by the module checks to decide if a transfer + * is compliant or not depending on the values stored in these state variables and on + * the parameters of the modules + * This function can be called ONLY by the token contract bound to the compliance + * @param _from The address of the sender + * @param _to The address of the receiver + * @param _amount The amount of tokens involved in the transfer + * This function calls moduleTransferAction() on each module bound to the compliance contract + */ + function transferred( + address _from, + address _to, + uint256 _amount + ) external; + + /** + * @dev function called whenever tokens are created on a wallet + * this function can update state variables in the modules bound to the compliance + * these state variables being used by the module checks to decide if a transfer + * is compliant or not depending on the values stored in these state variables and on + * the parameters of the modules + * This function can be called ONLY by the token contract bound to the compliance + * @param _to The address of the receiver + * @param _amount The amount of tokens involved in the minting + * This function calls moduleMintAction() on each module bound to the compliance contract + */ + function created(address _to, uint256 _amount) external; + + /** + * @dev function called whenever tokens are destroyed from a wallet + * this function can update state variables in the modules bound to the compliance + * these state variables being used by the module checks to decide if a transfer + * is compliant or not depending on the values stored in these state variables and on + * the parameters of the modules + * This function can be called ONLY by the token contract bound to the compliance + * @param _from The address on which tokens are burnt + * @param _amount The amount of tokens involved in the burn + * This function calls moduleBurnAction() on each module bound to the compliance contract + */ + function destroyed(address _from, uint256 _amount) external; + + /** + * @dev checks that the transfer is compliant. + * default compliance always returns true + * READ ONLY FUNCTION, this function cannot be used to increment + * counters, emit events, ... + * @param _from The address of the sender + * @param _to The address of the receiver + * @param _amount The amount of tokens involved in the transfer + * This function will call moduleCheck() on every module bound to the compliance + * If each of the module checks return TRUE, this function will return TRUE as well + * returns FALSE otherwise + */ + function canTransfer( + address _from, + address _to, + uint256 _amount + ) external view returns (bool); + + /// check the parameters of the compliance contract + + function isTokenBound(address _token) external view returns (bool); + + /** + * @dev getter for the address of the token bound + * returns the address of the token + */ + function getTokenBound() external view returns (address); + +} diff --git a/contracts/ERC-3643/IERC3643IdentityRegistry.sol b/contracts/ERC-3643/IERC3643IdentityRegistry.sol new file mode 100644 index 00000000..52ebc793 --- /dev/null +++ b/contracts/ERC-3643/IERC3643IdentityRegistry.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +pragma solidity 0.8.27; + +import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; +import "./IERC3643IdentityRegistryStorage.sol"; +import "./IERC3643TrustedIssuersRegistry.sol"; +import "./IERC3643ClaimTopicsRegistry.sol"; + +/// Events + +/// @dev This event is emitted when the ClaimTopicsRegistry has been set for the IdentityRegistry. +/// @param _claimTopicsRegistry is the address of the Claim Topics Registry contract. +event ClaimTopicsRegistrySet(address indexed _claimTopicsRegistry); + +/// @dev This event is emitted when the IdentityRegistryStorage has been set for the IdentityRegistry. +/// @param _identityStorage is the address of the Identity Registry Storage contract. +event IdentityStorageSet(address indexed _identityStorage); + +/// @dev This event is emitted when the TrustedIssuersRegistry has been set for the IdentityRegistry. +/// @param _trustedIssuersRegistry is the address of the Trusted Issuers Registry contract. +event TrustedIssuersRegistrySet(address indexed _trustedIssuersRegistry); + +/// @dev This event is emitted when an Identity is registered into the Identity Registry. +/// @param _investorAddress is the address of the investor's wallet. +/// @param _identity is the address of the Identity smart contract (onchainID). +event IdentityRegistered(address indexed _investorAddress, IIdentity indexed _identity); + +/// @dev This event is emitted when an Identity is removed from the Identity Registry. +/// @param _investorAddress is the address of the investor's wallet. +/// @param _identity is the address of the Identity smart contract (onchainID). +event IdentityRemoved(address indexed _investorAddress, IIdentity indexed _identity); + +/// @dev This event is emitted when an Identity has been updated. +/// @param _oldIdentity is the old Identity contract's address to update. +/// @param _newIdentity is the new Identity contract's. +event IdentityUpdated(IIdentity indexed _oldIdentity, IIdentity indexed _newIdentity); + +/// @dev This event is emitted when an Identity's country has been updated. +/// @param _investorAddress is the address on which the country has been updated +/// @param _country is the numeric code (ISO 3166-1) of the new country +event CountryUpdated(address indexed _investorAddress, uint16 indexed _country); + +interface IERC3643IdentityRegistry { + + /// Functions + + /// Identity Registry Setters + + /** + * @dev Replace the actual identityRegistryStorage contract with a new one. + * This function can only be called by the wallet set as owner of the smart contract + * @param _identityRegistryStorage The address of the new Identity Registry Storage + * emits `IdentityStorageSet` event + */ + function setIdentityRegistryStorage(address _identityRegistryStorage) external; + + /** + * @dev Replace the actual claimTopicsRegistry contract with a new one. + * This function can only be called by the wallet set as owner of the smart contract + * @param _claimTopicsRegistry The address of the new claim Topics Registry + * emits `ClaimTopicsRegistrySet` event + */ + function setClaimTopicsRegistry(address _claimTopicsRegistry) external; + + /** + * @dev Replace the actual trustedIssuersRegistry contract with a new one. + * This function can only be called by the wallet set as owner of the smart contract + * @param _trustedIssuersRegistry The address of the new Trusted Issuers Registry + * emits `TrustedIssuersRegistrySet` event + */ + function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external; + + /// Registry Actions + /** + * @dev Register an identity contract corresponding to a user address. + * Requires that the user doesn't have an identity contract already registered. + * This function can only be called by a wallet set as agent of the smart contract + * @param _userAddress The address of the user + * @param _identity The address of the user's identity contract + * @param _country The country of the investor + * emits `IdentityRegistered` event + */ + function registerIdentity( + address _userAddress, + IIdentity _identity, + uint16 _country + ) external; + + /** + * @dev Removes an user from the identity registry. + * Requires that the user have an identity contract already deployed that will be deleted. + * This function can only be called by a wallet set as agent of the smart contract + * @param _userAddress The address of the user to be removed + * emits `IdentityRemoved` event + */ + function deleteIdentity(address _userAddress) external; + + /** + * @dev Updates the country corresponding to a user address. + * Requires that the user should have an identity contract already deployed that will be replaced. + * This function can only be called by a wallet set as agent of the smart contract + * @param _userAddress The address of the user + * @param _country The new country of the user + * emits `CountryUpdated` event + */ + function updateCountry(address _userAddress, uint16 _country) external; + + /** + * @dev Updates an identity contract corresponding to a user address. + * Requires that the user address should be the owner of the identity contract. + * Requires that the user should have an identity contract already deployed that will be replaced. + * This function can only be called by a wallet set as agent of the smart contract + * @param _userAddress The address of the user + * @param _identity The address of the user's new identity contract + * emits `IdentityUpdated` event + */ + function updateIdentity(address _userAddress, IIdentity _identity) external; + + /** + * @dev function allowing to register identities in batch + * This function can only be called by a wallet set as agent of the smart contract + * Requires that none of the users has an identity contract already registered. + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _userAddresses The addresses of the users + * @param _identities The addresses of the corresponding identity contracts + * @param _countries The countries of the corresponding investors + * emits _userAddresses.length `IdentityRegistered` events + */ + function batchRegisterIdentity( + address[] calldata _userAddresses, + IIdentity[] calldata _identities, + uint16[] calldata _countries + ) external; + + /// Registry Consultation + + /** + * @dev This functions checks whether a wallet has its Identity registered or not + * in the Identity Registry. + * @param _userAddress The address of the user to be checked. + * @return 'True' if the address is contained in the Identity Registry, 'false' if not. + */ + function contains(address _userAddress) external view returns (bool); + + /** + * @dev This functions checks whether an identity contract + * corresponding to the provided user address has the required claims or not based + * on the data fetched from trusted issuers registry and from the claim topics registry + * @param _userAddress The address of the user to be verified. + * @return 'True' if the address is verified, 'false' if not. + */ + function isVerified(address _userAddress) external view returns (bool); + + /** + * @dev Returns the onchainID of an investor. + * @param _userAddress The wallet of the investor + */ + function identity(address _userAddress) external view returns (IIdentity); + + /** + * @dev Returns the country code of an investor. + * @param _userAddress The wallet of the investor + */ + function investorCountry(address _userAddress) external view returns (uint16); + + // identity registry getters + /** + * @dev Returns the IdentityRegistryStorage linked to the current IdentityRegistry. + */ + function identityStorage() external view returns (IERC3643IdentityRegistryStorage); + + /** + * @dev Returns the TrustedIssuersRegistry linked to the current IdentityRegistry. + */ + function issuersRegistry() external view returns (IERC3643TrustedIssuersRegistry); + + /** + * @dev Returns the ClaimTopicsRegistry linked to the current IdentityRegistry. + */ + function topicsRegistry() external view returns (IERC3643ClaimTopicsRegistry); + +} diff --git a/contracts/ERC-3643/IERC3643IdentityRegistryStorage.sol b/contracts/ERC-3643/IERC3643IdentityRegistryStorage.sol new file mode 100644 index 00000000..2583a4be --- /dev/null +++ b/contracts/ERC-3643/IERC3643IdentityRegistryStorage.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +pragma solidity 0.8.27; +import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; + +/// events + +/// @dev This event is emitted when an Identity is registered into the storage contract. +/// @param _investorAddress` is the address of the investor's wallet. +/// @param _identity` is the address of the Identity smart contract (onchainID). +event IdentityStored(address indexed _investorAddress, IIdentity indexed _identity); + +/// @dev This event is emitted when an Identity is removed from the storage contract. +/// @param _investorAddress is the address of the investor's wallet. +/// @param _identity is the address of the Identity smart contract (onchainID). +event IdentityUnstored(address indexed _investorAddress, IIdentity indexed _identity); + +/// @dev This event is emitted when an Identity has been updated. +/// @param _oldIdentity is the old Identity contract's address to update. +/// @param _newIdentity is the new Identity contract's. +event IdentityModified(IIdentity indexed _oldIdentity, IIdentity indexed _newIdentity); + +/// @dev This event is emitted when an Identity's country has been updated. +/// @param _investorAddress is the address on which the country has been updated. +/// @param _country is the numeric code (ISO 3166-1) of the new country. +event CountryModified(address indexed _investorAddress, uint16 indexed _country); + +/// @dev This event is emitted when an Identity Registry is bound to the storage contract. +/// @param _identityRegistry is the address of the identity registry added. +event IdentityRegistryBound(address indexed _identityRegistry); + +/// @dev This event is emitted when an Identity Registry is unbound from the storage contract. +/// @param _identityRegistry is the address of the identity registry removed. +event IdentityRegistryUnbound(address indexed _identityRegistry); + +interface IERC3643IdentityRegistryStorage { + + /// functions + + /** + * @dev adds an identity contract corresponding to a user address in the storage. + * Requires that the user doesn't have an identity contract already registered. + * This function can only be called by an address set as agent of the smart contract + * @param _userAddress The address of the user + * @param _identity The address of the user's identity contract + * @param _country The country of the investor + * emits `IdentityStored` event + */ + function addIdentityToStorage( + address _userAddress, + IIdentity _identity, + uint16 _country + ) external; + + /** + * @dev Removes an user from the storage. + * Requires that the user have an identity contract already deployed that will be deleted. + * This function can only be called by an address set as agent of the smart contract + * @param _userAddress The address of the user to be removed + * emits `IdentityUnstored` event + */ + function removeIdentityFromStorage(address _userAddress) external; + + /** + * @dev Updates the country corresponding to a user address. + * Requires that the user should have an identity contract already deployed that will be replaced. + * This function can only be called by an address set as agent of the smart contract + * @param _userAddress The address of the user + * @param _country The new country of the user + * emits `CountryModified` event + */ + function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external; + + /** + * @dev Updates an identity contract corresponding to a user address. + * Requires that the user address should be the owner of the identity contract. + * Requires that the user should have an identity contract already deployed that will be replaced. + * This function can only be called by an address set as agent of the smart contract + * @param _userAddress The address of the user + * @param _identity The address of the user's new identity contract + * emits `IdentityModified` event + */ + function modifyStoredIdentity(address _userAddress, IIdentity _identity) external; + + /** + * @notice Adds an identity registry as agent of the Identity Registry Storage Contract. + * This function can only be called by the wallet set as owner of the smart contract + * This function adds the identity registry to the list of identityRegistries linked to the storage contract + * cannot bind more than 300 IR to 1 IRS + * @param _identityRegistry The identity registry address to add. + */ + function bindIdentityRegistry(address _identityRegistry) external; + + /** + * @notice Removes an identity registry from being agent of the Identity Registry Storage Contract. + * This function can only be called by the wallet set as owner of the smart contract + * This function removes the identity registry from the list of identityRegistries linked to the storage contract + * @param _identityRegistry The identity registry address to remove. + */ + function unbindIdentityRegistry(address _identityRegistry) external; + + /** + * @dev Returns the identity registries linked to the storage contract + */ + function linkedIdentityRegistries() external view returns (address[] memory); + + /** + * @dev Returns the onchainID of an investor. + * @param _userAddress The wallet of the investor + */ + function storedIdentity(address _userAddress) external view returns (IIdentity); + + /** + * @dev Returns the country code of an investor. + * @param _userAddress The wallet of the investor + */ + function storedInvestorCountry(address _userAddress) external view returns (uint16); +} diff --git a/contracts/ERC-3643/IERC3643TrustedIssuersRegistry.sol b/contracts/ERC-3643/IERC3643TrustedIssuersRegistry.sol new file mode 100644 index 00000000..cf1057ff --- /dev/null +++ b/contracts/ERC-3643/IERC3643TrustedIssuersRegistry.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +pragma solidity 0.8.27; +import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; + +/// Events + +/// @dev This event is emitted when a trusted issuer is added in the registry. +/// @param _trustedIssuer is the address of the trusted issuer's ClaimIssuer contract. +/// @param _claimTopics is the set of claims that the trusted issuer is allowed to emit. +event TrustedIssuerAdded(IClaimIssuer indexed _trustedIssuer, uint256[] _claimTopics); + +/// @dev This event is emitted when a trusted issuer is removed from the registry. +/// @param _trustedIssuer is the address of the trusted issuer's ClaimIssuer contract. +event TrustedIssuerRemoved(IClaimIssuer indexed _trustedIssuer); + +/// &dev This event is emitted when the set of claim topics is changed for a given trusted issuer. +/// @param _trustedIssuer is the address of the trusted issuer's ClaimIssuer contract +/// @param _claimTopics is the set of claims that the trusted issuer is allowed to emit. +event ClaimTopicsUpdated(IClaimIssuer indexed _trustedIssuer, uint256[] _claimTopics); + + +interface IERC3643TrustedIssuersRegistry { + + // Functions + + // Setters + /** + * @dev registers a ClaimIssuer contract as trusted claim issuer. + * Requires that a ClaimIssuer contract doesn't already exist + * Requires that the claimTopics set is not empty + * Requires that there is no more than 15 claimTopics + * Requires that there is no more than 50 Trusted issuers + * @param _trustedIssuer The ClaimIssuer contract address of the trusted claim issuer. + * @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit + * This function can only be called by the owner of the Trusted Issuers Registry contract + * emits a `TrustedIssuerAdded` event + */ + function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external; + + /** + * @dev Removes the ClaimIssuer contract of a trusted claim issuer. + * Requires that the claim issuer contract to be registered first + * @param _trustedIssuer the claim issuer to remove. + * This function can only be called by the owner of the Trusted Issuers Registry contract + * emits a `TrustedIssuerRemoved` event + */ + function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external; + + /** + * @dev Updates the set of claim topics that a trusted issuer is allowed to emit. + * Requires that this ClaimIssuer contract already exists in the registry + * Requires that the provided claimTopics set is not empty + * Requires that there is no more than 15 claimTopics + * @param _trustedIssuer the claim issuer to update. + * @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit + * This function can only be called by the owner of the Trusted Issuers Registry contract + * emits a `ClaimTopicsUpdated` event + */ + function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external; + + /// Getters + + /** + * @dev Function for getting all the trusted claim issuers stored. + * @return array of all claim issuers registered. + */ + function getTrustedIssuers() external view returns (IClaimIssuer[] memory); + + /** + * @dev Function for getting all the trusted issuer allowed for a given claim topic. + * @param claimTopic the claim topic to get the trusted issuers for. + * @return array of all claim issuer addresses that are allowed for the given claim topic. + */ + function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory); + + /** + * @dev Checks if the ClaimIssuer contract is trusted + * @param _issuer the address of the ClaimIssuer contract + * @return true if the issuer is trusted, false otherwise. + */ + function isTrustedIssuer(address _issuer) external view returns (bool); + + /** + * @dev Function for getting all the claim topic of trusted claim issuer + * Requires the provided ClaimIssuer contract to be registered in the trusted issuers registry. + * @param _trustedIssuer the trusted issuer concerned. + * @return The set of claim topics that the trusted issuer is allowed to emit + */ + function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns (uint256[] memory); + + /** + * @dev Function for checking if the trusted claim issuer is allowed + * to emit a certain claim topic + * @param _issuer the address of the trusted issuer's ClaimIssuer contract + * @param _claimTopic the Claim Topic that has to be checked to know if the `issuer` is allowed to emit it + * @return true if the issuer is trusted for this claim topic. + */ + function hasClaimTopic(address _issuer, uint256 _claimTopic) external view returns (bool); +} diff --git a/contracts/_testContracts/MockContract.sol b/contracts/_testContracts/MockContract.sol index eaa50c41..6e0568f9 100644 --- a/contracts/_testContracts/MockContract.sol +++ b/contracts/_testContracts/MockContract.sol @@ -1,8 +1,9 @@ -pragma solidity 0.8.17; +pragma solidity 0.8.27; contract MockContract { address _irRegistry; uint16 _investorCountry; + address _compliance; function identityRegistry() public view returns (address identityRegistry) { if (_irRegistry != address(0)) { @@ -19,4 +20,12 @@ contract MockContract { function setInvestorCountry(uint16 country) public { _investorCountry = country; } + + function setCompliance(address compliance) public { + _compliance = compliance; + } + + function compliance() public view returns (address) { + return _compliance; + } } diff --git a/contracts/_testContracts/OIDImports.sol b/contracts/_testContracts/OIDImports.sol deleted file mode 100644 index f2e9b295..00000000 --- a/contracts/_testContracts/OIDImports.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.17; - -import "@onchain-id/solidity/contracts/ClaimIssuer.sol"; -import "@onchain-id/solidity/contracts/Identity.sol"; -import "@onchain-id/solidity/contracts/proxy/ImplementationAuthority.sol"; - -contract OIDImports { - -} diff --git a/contracts/_testContracts/TestERC20.sol b/contracts/_testContracts/TestERC20.sol index c8a28d7c..dbe266ef 100644 --- a/contracts/_testContracts/TestERC20.sol +++ b/contracts/_testContracts/TestERC20.sol @@ -58,7 +58,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/contracts/_testContracts/TestUpgradedCountryAllowModule.sol b/contracts/_testContracts/TestUpgradedCountryAllowModule.sol index 3a3316a7..81ab029e 100644 --- a/contracts/_testContracts/TestUpgradedCountryAllowModule.sol +++ b/contracts/_testContracts/TestUpgradedCountryAllowModule.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../compliance/modular/modules/CountryAllowModule.sol"; diff --git a/contracts/_testContracts/v_3_5_2/LegacyIA.sol b/contracts/_testContracts/v_3_5_2/LegacyIA.sol index 6a9c74ee..db920477 100644 --- a/contracts/_testContracts/v_3_5_2/LegacyIA.sol +++ b/contracts/_testContracts/v_3_5_2/LegacyIA.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; +pragma solidity 0.8.27; abstract contract ContextLegacy { function _msgSender() internal view virtual returns (address) { diff --git a/contracts/_testContracts/v_3_5_2/LegacyProxy.sol b/contracts/_testContracts/v_3_5_2/LegacyProxy.sol index 2ed5fcbf..ba5dcfed 100644 --- a/contracts/_testContracts/v_3_5_2/LegacyProxy.sol +++ b/contracts/_testContracts/v_3_5_2/LegacyProxy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; +pragma solidity 0.8.27; interface IImplementationAuthorityLegacy { function getImplementation() external view returns (address); diff --git a/contracts/_testContracts/v_3_5_2/LegacyToken_3_5_2.sol b/contracts/_testContracts/v_3_5_2/LegacyToken_3_5_2.sol index 9cffad8c..fa154e01 100644 --- a/contracts/_testContracts/v_3_5_2/LegacyToken_3_5_2.sol +++ b/contracts/_testContracts/v_3_5_2/LegacyToken_3_5_2.sol @@ -5,7 +5,7 @@ // File: @onchain-id/solidity/contracts/interface/IERC734Legacy.sol // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; +pragma solidity 0.8.27; /** * @dev interface of the ERC734 (Key Holder) standard as defined in the EIP. diff --git a/contracts/compliance/legacy/BasicCompliance.sol b/contracts/compliance/legacy/BasicCompliance.sol index ef54234f..9b77bdb6 100644 --- a/contracts/compliance/legacy/BasicCompliance.sol +++ b/contracts/compliance/legacy/BasicCompliance.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../../roles/AgentRole.sol"; import "./ICompliance.sol"; diff --git a/contracts/compliance/legacy/DefaultCompliance.sol b/contracts/compliance/legacy/DefaultCompliance.sol index 5ab3a70d..c2fc523a 100644 --- a/contracts/compliance/legacy/DefaultCompliance.sol +++ b/contracts/compliance/legacy/DefaultCompliance.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/ICompliance.sol b/contracts/compliance/legacy/ICompliance.sol index cb99ae87..f9a8070e 100644 --- a/contracts/compliance/legacy/ICompliance.sol +++ b/contracts/compliance/legacy/ICompliance.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; interface ICompliance { /** diff --git a/contracts/compliance/legacy/features/ApproveTransfer.sol b/contracts/compliance/legacy/features/ApproveTransfer.sol index dbdf2f0d..d5076852 100644 --- a/contracts/compliance/legacy/features/ApproveTransfer.sol +++ b/contracts/compliance/legacy/features/ApproveTransfer.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/CountryRestrictions.sol b/contracts/compliance/legacy/features/CountryRestrictions.sol index 368f9356..a2862330 100644 --- a/contracts/compliance/legacy/features/CountryRestrictions.sol +++ b/contracts/compliance/legacy/features/CountryRestrictions.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/CountryWhitelisting.sol b/contracts/compliance/legacy/features/CountryWhitelisting.sol index 56e67889..4adc4c85 100644 --- a/contracts/compliance/legacy/features/CountryWhitelisting.sol +++ b/contracts/compliance/legacy/features/CountryWhitelisting.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/DayMonthLimits.sol b/contracts/compliance/legacy/features/DayMonthLimits.sol index 78264e94..fd7381e2 100644 --- a/contracts/compliance/legacy/features/DayMonthLimits.sol +++ b/contracts/compliance/legacy/features/DayMonthLimits.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/ExchangeMonthlyLimits.sol b/contracts/compliance/legacy/features/ExchangeMonthlyLimits.sol index 36bce9bb..47201e99 100644 --- a/contracts/compliance/legacy/features/ExchangeMonthlyLimits.sol +++ b/contracts/compliance/legacy/features/ExchangeMonthlyLimits.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/MaxBalance.sol b/contracts/compliance/legacy/features/MaxBalance.sol index 35f54b35..93434159 100644 --- a/contracts/compliance/legacy/features/MaxBalance.sol +++ b/contracts/compliance/legacy/features/MaxBalance.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/features/SupplyLimit.sol b/contracts/compliance/legacy/features/SupplyLimit.sol index e815fef6..26d5a956 100644 --- a/contracts/compliance/legacy/features/SupplyLimit.sol +++ b/contracts/compliance/legacy/features/SupplyLimit.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../BasicCompliance.sol"; diff --git a/contracts/compliance/legacy/test/ApproveTransferTest.sol b/contracts/compliance/legacy/test/ApproveTransferTest.sol index 26912450..0d73c1c4 100644 --- a/contracts/compliance/legacy/test/ApproveTransferTest.sol +++ b/contracts/compliance/legacy/test/ApproveTransferTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/ApproveTransfer.sol"; diff --git a/contracts/compliance/legacy/test/CountryRestrictionsTest.sol b/contracts/compliance/legacy/test/CountryRestrictionsTest.sol index 68e3daf0..d9b6519f 100644 --- a/contracts/compliance/legacy/test/CountryRestrictionsTest.sol +++ b/contracts/compliance/legacy/test/CountryRestrictionsTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/CountryRestrictions.sol"; diff --git a/contracts/compliance/legacy/test/CountryWhitelistingTest.sol b/contracts/compliance/legacy/test/CountryWhitelistingTest.sol index 13653942..49739725 100644 --- a/contracts/compliance/legacy/test/CountryWhitelistingTest.sol +++ b/contracts/compliance/legacy/test/CountryWhitelistingTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/CountryWhitelisting.sol"; diff --git a/contracts/compliance/legacy/test/DayMonthLimitsTest.sol b/contracts/compliance/legacy/test/DayMonthLimitsTest.sol index 08f8a0b6..500c3060 100644 --- a/contracts/compliance/legacy/test/DayMonthLimitsTest.sol +++ b/contracts/compliance/legacy/test/DayMonthLimitsTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/DayMonthLimits.sol"; diff --git a/contracts/compliance/legacy/test/ExchangeMonthlyLimitsTest.sol b/contracts/compliance/legacy/test/ExchangeMonthlyLimitsTest.sol index e6699cda..498ec129 100644 --- a/contracts/compliance/legacy/test/ExchangeMonthlyLimitsTest.sol +++ b/contracts/compliance/legacy/test/ExchangeMonthlyLimitsTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/ExchangeMonthlyLimits.sol"; diff --git a/contracts/compliance/legacy/test/MaxBalanceTest.sol b/contracts/compliance/legacy/test/MaxBalanceTest.sol index 3d1b6516..816931f4 100644 --- a/contracts/compliance/legacy/test/MaxBalanceTest.sol +++ b/contracts/compliance/legacy/test/MaxBalanceTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/MaxBalance.sol"; diff --git a/contracts/compliance/legacy/test/SupplyLimitTest.sol b/contracts/compliance/legacy/test/SupplyLimitTest.sol index ef63372e..94797030 100644 --- a/contracts/compliance/legacy/test/SupplyLimitTest.sol +++ b/contracts/compliance/legacy/test/SupplyLimitTest.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../features/SupplyLimit.sol"; diff --git a/contracts/compliance/modular/IModularCompliance.sol b/contracts/compliance/modular/IModularCompliance.sol index bc5769ef..63dd38b5 100644 --- a/contracts/compliance/modular/IModularCompliance.sol +++ b/contracts/compliance/modular/IModularCompliance.sol @@ -60,65 +60,33 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -interface IModularCompliance { +import "../../ERC-3643/IERC3643Compliance.sol"; - /// events +/// events - /** - * @dev Event emitted for each executed interaction with a module contract. - * For gas efficiency, only the interaction calldata selector (first 4 - * bytes) is included in the event. For interactions without calldata or - * whose calldata is shorter than 4 bytes, the selector will be `0`. - */ - event ModuleInteraction(address indexed target, bytes4 selector); +/// @dev Event emitted for each executed interaction with a module contract. +/// For gas efficiency, only the interaction calldata selector (first 4 bytes) is included in the event. +/// For interactions without calldata or whose calldata is shorter than 4 bytes, the selector will be `0`. +/// @param _target Address of the module. +/// @param _selector See above comments. +event ModuleInteraction(address indexed _target, bytes4 _selector); - /** - * this event is emitted when a token has been bound to the compliance contract - * the event is emitted by the bindToken function - * `_token` is the address of the token to bind - */ - event TokenBound(address _token); - /** - * this event is emitted when a token has been unbound from the compliance contract - * the event is emitted by the unbindToken function - * `_token` is the address of the token to unbind - */ - event TokenUnbound(address _token); +/// @dev This event is emitted when a module has been added to the list of modules bound to the compliance contract. +/// @param _module The address of the compliance module. +event ModuleAdded(address indexed _module); - /** - * this event is emitted when a module has been added to the list of modules bound to the compliance contract - * the event is emitted by the addModule function - * `_module` is the address of the compliance module - */ - event ModuleAdded(address indexed _module); - /** - * this event is emitted when a module has been removed from the list of modules bound to the compliance contract - * the event is emitted by the removeModule function - * `_module` is the address of the compliance module - */ - event ModuleRemoved(address indexed _module); +/// @dev This event is emitted when a module has been removed from the list of modules bound to the compliance contract. +/// @param _module is the address of the compliance module +event ModuleRemoved(address indexed _module); - /// functions - /** - * @dev binds a token to the compliance contract - * @param _token address of the token to bind - * This function can be called ONLY by the owner of the compliance contract - * Emits a TokenBound event - */ - function bindToken(address _token) external; +interface IModularCompliance is IERC3643Compliance { - /** - * @dev unbinds a token from the compliance contract - * @param _token address of the token to unbind - * This function can be called ONLY by the owner of the compliance contract - * Emits a TokenUnbound event - */ - function unbindToken(address _token) external; + /// functions /** * @dev adds a module to the list of compliance modules @@ -148,67 +116,38 @@ interface IModularCompliance { function callModuleFunction(bytes calldata callData, address _module) external; /** - * @dev function called whenever tokens are transferred - * from one wallet to another - * this function can update state variables in the modules bound to the compliance - * these state variables being used by the module checks to decide if a transfer - * is compliant or not depending on the values stored in these state variables and on - * the parameters of the modules - * This function can be called ONLY by the token contract bound to the compliance - * @param _from The address of the sender - * @param _to The address of the receiver - * @param _amount The amount of tokens involved in the transfer - * This function calls moduleTransferAction() on each module bound to the compliance contract - */ - function transferred( - address _from, - address _to, - uint256 _amount - ) external; - - /** - * @dev function called whenever tokens are created on a wallet - * this function can update state variables in the modules bound to the compliance - * these state variables being used by the module checks to decide if a transfer - * is compliant or not depending on the values stored in these state variables and on - * the parameters of the modules - * This function can be called ONLY by the token contract bound to the compliance - * @param _to The address of the receiver - * @param _amount The amount of tokens involved in the minting - * This function calls moduleMintAction() on each module bound to the compliance contract - */ - function created(address _to, uint256 _amount) external; - - /** - * @dev function called whenever tokens are destroyed from a wallet - * this function can update state variables in the modules bound to the compliance - * these state variables being used by the module checks to decide if a transfer - * is compliant or not depending on the values stored in these state variables and on - * the parameters of the modules - * This function can be called ONLY by the token contract bound to the compliance - * @param _from The address on which tokens are burnt - * @param _amount The amount of tokens involved in the burn - * This function calls moduleBurnAction() on each module bound to the compliance contract - */ - function destroyed(address _from, uint256 _amount) external; - - /** - * @dev checks that the transfer is compliant. - * default compliance always returns true - * READ ONLY FUNCTION, this function cannot be used to increment - * counters, emit events, ... - * @param _from The address of the sender - * @param _to The address of the receiver - * @param _amount The amount of tokens involved in the transfer - * This function will call moduleCheck() on every module bound to the compliance - * If each of the module checks return TRUE, this function will return TRUE as well - * returns FALSE otherwise - */ - function canTransfer( - address _from, - address _to, - uint256 _amount - ) external view returns (bool); + * @dev Adds a module to the modular compliance contract and performs multiple interactions with it in a single transaction. + * + * This function allows the contract owner to add a new compliance module and immediately configure it by calling + * specified functions on the module. This can be useful for setting up the module with initial parameters or configurations + * right after it is added. + * + * @param _module The address of the module to add. The module must either be "plug and play" + * or be able to bind with the compliance contract. + * @param _interactions An array of bytecode representing function calls to be made on the module. + * These interactions are performed after the module is added. + * + * Requirements: + * - The caller must be the owner of the `ModularCompliance` contract. + * - The `_module` address must not be a zero address. + * - The `_module` must not already be bound to the contract. + * - The total number of modules must not exceed 25 after adding the new module. + * - The `_interactions` array must contain no more than 5 elements to prevent out-of-gas errors. + * + * Operations: + * - The function first adds the module using the `addModule` function. + * - Then, it iterates over the `_interactions` array, performing each + * interaction on the module using the `callModuleFunction`. + * + * Emits: + * - A `ModuleAdded` event upon successful addition of the module. + * - A `ModuleInteraction` event for each function call made to the module during the interaction phase. + * + * Reverts if: + * - Any of the above requirements are not met. + * - Any of the module interactions fail during execution. + */ + function addAndSetModule(address _module, bytes[] calldata _interactions) external; /** * @dev getter for the modules bound to the compliance contract @@ -216,12 +155,6 @@ interface IModularCompliance { */ function getModules() external view returns (address[] memory); - /** - * @dev getter for the address of the token bound - * returns the address of the token - */ - function getTokenBound() external view returns (address); - /** * @dev checks if a module is bound to the compliance contract * returns true if module is bound, false otherwise diff --git a/contracts/compliance/modular/MCStorage.sol b/contracts/compliance/modular/MCStorage.sol index df160809..f50d9e92 100644 --- a/contracts/compliance/modular/MCStorage.sol +++ b/contracts/compliance/modular/MCStorage.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; contract MCStorage { /// token linked to the compliance contract diff --git a/contracts/compliance/modular/ModularCompliance.sol b/contracts/compliance/modular/ModularCompliance.sol index 7dcca9ae..e1d9f5dc 100644 --- a/contracts/compliance/modular/ModularCompliance.sol +++ b/contracts/compliance/modular/ModularCompliance.sol @@ -60,16 +60,39 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "../../token/IToken.sol"; import "./IModularCompliance.sol"; import "./MCStorage.sol"; import "./modules/IModule.sol"; +import "../../errors/ComplianceErrors.sol"; +import "../../errors/CommonErrors.sol"; +import "../../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; +/// errors -contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage { +/// @dev Thrown when trying to add more than max modules. +/// @param maxValue maximum number of modules. +error MaxModulesReached(uint256 maxValue); + +/// @dev Thrown when module is already bound. +error ModuleAlreadyBound(); + +/// @dev Thrown when module is not bound. +error ModuleNotBound(); + +/// @dev Thrown when called by other than owner or token. +error OnlyOwnerOrTokenCanCall(); + +/// @dev Thrown when token is not bound. +error TokenNotBound(); + + +contract ModularCompliance is IModularCompliance, OwnableOnceNext2StepUpgradeable, MCStorage, IERC165 { /// modifiers @@ -77,7 +100,7 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage * @dev Throws if called by any address that is not a token bound to the compliance. */ modifier onlyToken() { - require(msg.sender == _tokenBound, "error : this address is not a token bound to the compliance contract"); + require(msg.sender == _tokenBound, AddressNotATokenBoundToComplianceContract()); _; } @@ -86,51 +109,32 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev See {IModularCompliance-bindToken}. + * @dev See {IERC3643Compliance-bindToken}. */ function bindToken(address _token) external override { - require(owner() == msg.sender || (_tokenBound == address(0) && msg.sender == _token), - "only owner or token can call"); - require(_token != address(0), "invalid argument - zero address"); + require(owner() == msg.sender || (_tokenBound == address(0) && msg.sender == _token), OnlyOwnerOrTokenCanCall()); + require(_token != address(0), ZeroAddress()); _tokenBound = _token; emit TokenBound(_token); } /** - * @dev See {IModularCompliance-unbindToken}. + * @dev See {IERC3643Compliance-unbindToken}. */ function unbindToken(address _token) external override { - require(owner() == msg.sender || msg.sender == _token , "only owner or token can call"); - require(_token == _tokenBound, "This token is not bound"); - require(_token != address(0), "invalid argument - zero address"); + require(owner() == msg.sender || msg.sender == _token , OnlyOwnerOrTokenCanCall()); + require(_token == _tokenBound, TokenNotBound()); + require(_token != address(0), ZeroAddress()); delete _tokenBound; emit TokenUnbound(_token); } - /** - * @dev See {IModularCompliance-addModule}. - */ - function addModule(address _module) external override onlyOwner { - require(_module != address(0), "invalid argument - zero address"); - require(!_moduleBound[_module], "module already bound"); - require(_modules.length <= 24, "cannot add more than 25 modules"); - IModule module = IModule(_module); - if (!module.isPlugAndPlay()) { - require(module.canComplianceBind(address(this)), "compliance is not suitable for binding to the module"); - } - - module.bindCompliance(address(this)); - _modules.push(_module); - _moduleBound[_module] = true; - emit ModuleAdded(_module); - } - /** * @dev See {IModularCompliance-removeModule}. */ function removeModule(address _module) external override onlyOwner { - require(_module != address(0), "invalid argument - zero address"); - require(_moduleBound[_module], "module not bound"); + require(_module != address(0), ZeroAddress()); + require(_moduleBound[_module], ModuleNotBound()); uint256 length = _modules.length; for (uint256 i = 0; i < length; i++) { if (_modules[i] == _module) { @@ -145,14 +149,14 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev See {IModularCompliance-transferred}. + * @dev See {IERC3643Compliance-transferred}. */ function transferred(address _from, address _to, uint256 _value) external onlyToken override { require( _from != address(0) && _to != address(0) - , "invalid argument - zero address"); - require(_value > 0, "invalid argument - no value transfer"); + , ZeroAddress()); + require(_value > 0, ZeroValue()); uint256 length = _modules.length; for (uint256 i = 0; i < length; i++) { IModule(_modules[i]).moduleTransferAction(_from, _to, _value); @@ -160,11 +164,11 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev See {IModularCompliance-created}. + * @dev See {IERC3643Compliance-created}. */ function created(address _to, uint256 _value) external onlyToken override { - require(_to != address(0), "invalid argument - zero address"); - require(_value > 0, "invalid argument - no value mint"); + require(_to != address(0), ZeroAddress()); + require(_value > 0, ZeroValue()); uint256 length = _modules.length; for (uint256 i = 0; i < length; i++) { IModule(_modules[i]).moduleMintAction(_to, _value); @@ -172,11 +176,11 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev See {IModularCompliance-destroyed}. + * @dev See {IERC3643Compliance-destroyed}. */ function destroyed(address _from, uint256 _value) external onlyToken override { - require(_from != address(0), "invalid argument - zero address"); - require(_value > 0, "invalid argument - no value burn"); + require(_from != address(0), ZeroAddress()); + require(_value > 0, ZeroValue()); uint256 length = _modules.length; for (uint256 i = 0; i < length; i++) { IModule(_modules[i]).moduleBurnAction(_from, _value); @@ -184,38 +188,14 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev see {IModularCompliance-callModuleFunction}. + * @dev See {IModularCompliance-addAndSetModule}. */ - function callModuleFunction(bytes calldata callData, address _module) external override onlyOwner { - require(_moduleBound[_module], "call only on bound module"); - // NOTE: Use assembly to call the interaction instead of a low level - // call for two reasons: - // - We don't want to copy the return data, since we discard it for - // interactions. - // - Solidity will under certain conditions generate code to copy input - // calldata twice to memory (the second being a "memcopy loop"). - // solhint-disable-next-line no-inline-assembly - assembly { - let freeMemoryPointer := mload(0x40) - calldatacopy(freeMemoryPointer, callData.offset, callData.length) - if iszero( - call( - gas(), - _module, - 0, - freeMemoryPointer, - callData.length, - 0, - 0 - )) - { - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) - } + function addAndSetModule(address _module, bytes[] calldata _interactions) external onlyOwner override { + require(_interactions.length <= 5, ArraySizeLimited(5)); + addModule(_module); + for (uint256 i = 0; i < _interactions.length; i++) { + callModuleFunction(_interactions[i], _module); } - - emit ModuleInteraction(_module, _selector(callData)); - } /** @@ -233,14 +213,24 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage } /** - * @dev See {IModularCompliance-getTokenBound}. + * @dev See {IERC3643Compliance-getTokenBound}. */ function getTokenBound() external view override returns (address) { return _tokenBound; } /** - * @dev See {IModularCompliance-canTransfer}. + * @dev See {IERC3643Compliance-getTokenBound}. + */ + function isTokenBound(address _token) external view override returns (bool) { + if(_token == _tokenBound) { + return true; + } + return false; + } + + /** + * @dev See {IERC3643Compliance-canTransfer}. */ function canTransfer(address _from, address _to, uint256 _value) external view override returns (bool) { uint256 length = _modules.length; @@ -252,6 +242,71 @@ contract ModularCompliance is IModularCompliance, OwnableUpgradeable, MCStorage return true; } + /** + * @dev See {IModularCompliance-addModule}. + */ + function addModule(address _module) public override onlyOwner { + require(_module != address(0), ZeroAddress()); + require(!_moduleBound[_module], ModuleAlreadyBound()); + require(_modules.length <= 24, MaxModulesReached(25)); + IModule module = IModule(_module); + require(module.isPlugAndPlay() || module.canComplianceBind(address(this)), + ComplianceNotSuitableForBindingToModule(_module)); + + module.bindCompliance(address(this)); + _modules.push(_module); + _moduleBound[_module] = true; + emit ModuleAdded(_module); + } + + /** + * @dev see {IModularCompliance-callModuleFunction}. + */ + function callModuleFunction(bytes calldata callData, address _module) public override onlyOwner { + require(_moduleBound[_module], ModuleNotBound()); + // NOTE: Use assembly to call the interaction instead of a low level + // call for two reasons: + // - We don't want to copy the return data, since we discard it for + // interactions. + // - Solidity will under certain conditions generate code to copy input + // calldata twice to memory (the second being a "memcopy loop"). + // solhint-disable-next-line no-inline-assembly + assembly { + let freeMemoryPointer := mload(0x40) // Load the free memory pointer from memory location 0x40 + + // Copy callData from calldata to the free memory location + calldatacopy(freeMemoryPointer, callData.offset, callData.length) + + if iszero( // Check if the call returns zero (indicating failure) + call( // Perform the external call + gas(), // Provide all available gas + _module, // Address of the target module + 0, // No ether is sent with the call + freeMemoryPointer, // Input data starts at the free memory pointer + callData.length, // Input data length + 0, // Output data location (not used) + 0 // Output data size (not used) + ) + ) { + returndatacopy(0, 0, returndatasize()) // Copy return data to memory starting at position 0 + revert(0, returndatasize()) // Revert the transaction with the return data + } + } + + emit ModuleInteraction(_module, _selector(callData)); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IModularCompliance).interfaceId || + interfaceId == type(IERC3643Compliance).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + /// @dev Extracts the Solidity ABI selector for the specified interaction. /// @param callData Interaction data. /// @return result The 4 byte function selector of the call encoded in diff --git a/contracts/compliance/modular/modules/AbstractModule.sol b/contracts/compliance/modular/modules/AbstractModule.sol index f489062b..53102e47 100644 --- a/contracts/compliance/modular/modules/AbstractModule.sol +++ b/contracts/compliance/modular/modules/AbstractModule.sol @@ -60,11 +60,14 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./IModule.sol"; +import "../../../errors/InvalidArgumentErrors.sol"; +import "../../../errors/ComplianceErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -abstract contract AbstractModule is IModule { +abstract contract AbstractModule is IModule, IERC165 { /// compliance contract binding status mapping(address => bool) private _complianceBound; @@ -73,7 +76,7 @@ abstract contract AbstractModule is IModule { * @dev Throws if `_compliance` is not a bound compliance contract address. */ modifier onlyBoundCompliance(address _compliance) { - require(_complianceBound[_compliance], "compliance not bound"); + require(_complianceBound[_compliance], ComplianceNotBound()); _; } @@ -81,7 +84,7 @@ abstract contract AbstractModule is IModule { * @dev Throws if called from an address that is not a bound compliance contract. */ modifier onlyComplianceCall() { - require(_complianceBound[msg.sender], "only bound compliance can call"); + require(_complianceBound[msg.sender], OnlyBoundComplianceCanCall()); _; } @@ -89,9 +92,9 @@ abstract contract AbstractModule is IModule { * @dev See {IModule-bindCompliance}. */ function bindCompliance(address _compliance) external override { - require(_compliance != address(0), "invalid argument - zero address"); - require(!_complianceBound[_compliance], "compliance already bound"); - require(msg.sender == _compliance, "only compliance contract can call"); + require(_compliance != address(0), ZeroAddress()); + require(!_complianceBound[_compliance], ComplianceAlreadyBound()); + require(msg.sender == _compliance, OnlyComplianceContractCanCall()); _complianceBound[_compliance] = true; emit ComplianceBound(_compliance); } @@ -100,8 +103,8 @@ abstract contract AbstractModule is IModule { * @dev See {IModule-unbindCompliance}. */ function unbindCompliance(address _compliance) external onlyComplianceCall override { - require(_compliance != address(0), "invalid argument - zero address"); - require(msg.sender == _compliance, "only compliance contract can call"); + require(_compliance != address(0), ZeroAddress()); + require(msg.sender == _compliance, OnlyComplianceContractCanCall()); _complianceBound[_compliance] = false; emit ComplianceUnbound(_compliance); } @@ -113,4 +116,13 @@ abstract contract AbstractModule is IModule { return _complianceBound[_compliance]; } + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IModule).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + } diff --git a/contracts/compliance/modular/modules/AbstractModuleUpgradeable.sol b/contracts/compliance/modular/modules/AbstractModuleUpgradeable.sol index b142f92e..5e71cbde 100644 --- a/contracts/compliance/modular/modules/AbstractModuleUpgradeable.sol +++ b/contracts/compliance/modular/modules/AbstractModuleUpgradeable.sol @@ -60,14 +60,19 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "./IModule.sol"; +import "../../../errors/InvalidArgumentErrors.sol"; +import "../../../errors/ComplianceErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../../roles/IERC173.sol"; -abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUpgradeable, UUPSUpgradeable { + +abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableOnceNext2StepUpgradeable, UUPSUpgradeable, IERC165 { struct AbstractModuleStorage { /// compliance contract binding status mapping(address => bool) complianceBound; @@ -82,7 +87,7 @@ abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUp */ modifier onlyBoundCompliance(address _compliance) { AbstractModuleStorage storage s = _getAbstractModuleStorage(); - require(s.complianceBound[_compliance], "compliance not bound"); + require(s.complianceBound[_compliance], ComplianceNotBound()); _; } @@ -91,7 +96,7 @@ abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUp */ modifier onlyComplianceCall() { AbstractModuleStorage storage s = _getAbstractModuleStorage(); - require(s.complianceBound[msg.sender], "only bound compliance can call"); + require(s.complianceBound[msg.sender], OnlyBoundComplianceCanCall()); _; } @@ -100,9 +105,9 @@ abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUp */ function bindCompliance(address _compliance) external override { AbstractModuleStorage storage s = _getAbstractModuleStorage(); - require(_compliance != address(0), "invalid argument - zero address"); - require(!s.complianceBound[_compliance], "compliance already bound"); - require(msg.sender == _compliance, "only compliance contract can call"); + require(_compliance != address(0), ZeroAddress()); + require(!s.complianceBound[_compliance], ComplianceAlreadyBound()); + require(msg.sender == _compliance, OnlyComplianceContractCanCall()); s.complianceBound[_compliance] = true; emit ComplianceBound(_compliance); } @@ -112,8 +117,8 @@ abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUp */ function unbindCompliance(address _compliance) external onlyComplianceCall override { AbstractModuleStorage storage s = _getAbstractModuleStorage(); - require(_compliance != address(0), "invalid argument - zero address"); - require(msg.sender == _compliance, "only compliance contract can call"); + require(_compliance != address(0), ZeroAddress()); + require(msg.sender == _compliance, OnlyComplianceContractCanCall()); s.complianceBound[_compliance] = false; emit ComplianceUnbound(_compliance); } @@ -126,6 +131,16 @@ abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUp return s.complianceBound[_compliance]; } + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IModule).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + // solhint-disable-next-line func-name-mixedcase function __AbstractModule_init() internal onlyInitializing { __Ownable_init(); diff --git a/contracts/compliance/modular/modules/ConditionalTransferModule.sol b/contracts/compliance/modular/modules/ConditionalTransferModule.sol index 46aee9a9..e94f07f3 100644 --- a/contracts/compliance/modular/modules/ConditionalTransferModule.sol +++ b/contracts/compliance/modular/modules/ConditionalTransferModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,47 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "../../../roles/AgentRole.sol"; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted whenever a transfer is approved. +/// @param _from is the address of transfer sender. +/// @param _to is the address of transfer recipient. +/// @param _amount is the token amount to be sent (take care of decimals). +/// @param _token is the token address of the token concerned by the approval. +event TransferApproved(address _from, address _to, uint _amount, address _token); + + +/// @dev This event is emitted whenever a transfer approval is removed. +/// @param _from is the address of transfer sender. +/// @param _to is the address of transfer recipient. +/// @param _amount is the token amount to be sent (take care of decimals). +/// @param _token is the token address of the token concerned by the approval. +event ApprovalRemoved(address _from, address _to, uint _amount, address _token); + + +/// Errors + +/// @dev Thrown when a tranfer is not approved. +/// @param _from the address of the transfer sender. +/// @param _to the address of the transfer receiver. +/// @param _amount the amount of tokens that `_from` was allowed to send to `_to`. +error TransferNotApproved(address _from, address _to, uint _amount); + + /** * this module allows to require the pre-validation of a transfer before allowing it to be executed */ @@ -74,26 +107,6 @@ contract ConditionalTransferModule is AbstractModuleUpgradeable { /// Mapping between transfer details and their approval status (amount of transfers approved) per compliance mapping(address => mapping(bytes32 => uint)) private _transfersApproved; - /** - * this event is emitted whenever a transfer is approved. - * the event is emitted by 'approveTransfer' function. - * `_from` is the address of transfer sender. - * `_to` is the address of transfer recipient - * `_amount` is the token amount to be sent (take care of decimals) - * `_token` is the token address of the token concerned by the approval - */ - event TransferApproved(address _from, address _to, uint _amount, address _token); - - /** - * this event is emitted whenever a transfer approval is removed. - * the event is emitted by 'unApproveTransfer' function. - * `_from` is the address of transfer sender. - * `_to` is the address of transfer recipient - * `_amount` is the token amount to be sent (take care of decimals) - * `_token` is the token address of the token concerned by the approval - */ - event ApprovalRemoved(address _from, address _to, uint _amount, address _token); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -225,7 +238,7 @@ contract ConditionalTransferModule is AbstractModuleUpgradeable { */ function unApproveTransfer(address _from, address _to, uint _amount) public onlyComplianceCall { bytes32 transferHash = calculateTransferHash(_from, _to, _amount, IModularCompliance(msg.sender).getTokenBound()); - require(_transfersApproved[msg.sender][transferHash] > 0, "not approved"); + require(_transfersApproved[msg.sender][transferHash] > 0, TransferNotApproved(_from, _to, _amount)); _transfersApproved[msg.sender][transferHash]--; emit ApprovalRemoved(_from, _to, _amount, IModularCompliance(msg.sender).getTokenBound()); diff --git a/contracts/compliance/modular/modules/CountryAllowModule.sol b/contracts/compliance/modular/modules/CountryAllowModule.sol index 16e15819..a8ab9710 100644 --- a/contracts/compliance/modular/modules/CountryAllowModule.sol +++ b/contracts/compliance/modular/modules/CountryAllowModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,37 +59,49 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "./AbstractModuleUpgradeable.sol"; -contract CountryAllowModule is AbstractModuleUpgradeable { - /// Mapping between country and their allowance status per compliance contract - mapping(address => mapping(uint16 => bool)) private _allowedCountries; +/// Events - /// events +/// @dev This event is emitted whenever a Country has been allowed. +/// @param _compliance compliance address contract. +/// @param _country is the numeric ISO 3166-1 of the restricted country. +event CountryAllowed(address _compliance, uint16 _country); - /** - * this event is emitted whenever a Country has been allowed. - * the event is emitted by 'addAllowedCountry' and 'batchAllowCountries' functions. - * `_country` is the numeric ISO 3166-1 of the restricted country. - */ - event CountryAllowed(address _compliance, uint16 _country); - /** - * this event is emitted whenever a Country has been disallowed. - * the event is emitted by 'removeAllowedCountry' and 'batchDisallowCountries' functions. - * `_country` is the numeric ISO 3166-1 of the disallowed country. - */ - event CountryUnallowed(address _compliance, uint16 _country); - /// Custom Errors +/// @dev this event is emitted whenever a Country has been disallowed. +/// @param _compliance compliance address contract. +/// @param _country is the numeric ISO 3166-1 of the disallowed country. +event CountryUnallowed(address _compliance, uint16 _country); + - error CountryAlreadyAllowed(address _compliance, uint16 _country); - error CountryNotAllowed(address _compliance, uint16 _country); +/// Errors + +/// @dev Thrown when a country is already allowed. +/// @param _compliance compliance contract address. +/// @param _country country code. +error CountryAlreadyAllowed(address _compliance, uint16 _country); + +/// @dev Thrown when a country is not allowed. +/// @param _compliance compliance contract address. +/// @param _country country code. +error CountryNotAllowed(address _compliance, uint16 _country); + + +contract CountryAllowModule is AbstractModuleUpgradeable { + /// Mapping between country and their allowance status per compliance contract + mapping(address => mapping(uint16 => bool)) private _allowedCountries; /// functions @@ -138,7 +151,8 @@ contract CountryAllowModule is AbstractModuleUpgradeable { * emits an `AddedAllowedCountry` event */ function addAllowedCountry(uint16 _country) external onlyComplianceCall { - if ((_allowedCountries[msg.sender])[_country] == true) revert CountryAlreadyAllowed(msg.sender, _country); + require(!(_allowedCountries[msg.sender])[_country], CountryAlreadyAllowed(msg.sender, _country)); + (_allowedCountries[msg.sender])[_country] = true; emit CountryAllowed(msg.sender, _country); } @@ -152,7 +166,8 @@ contract CountryAllowModule is AbstractModuleUpgradeable { * emits an `RemoveAllowedCountry` event */ function removeAllowedCountry(uint16 _country) external onlyComplianceCall { - if ((_allowedCountries[msg.sender])[_country] == false) revert CountryNotAllowed(msg.sender, _country); + require((_allowedCountries[msg.sender])[_country], CountryNotAllowed(msg.sender, _country)); + (_allowedCountries[msg.sender])[_country] = false; emit CountryUnallowed(msg.sender, _country); } diff --git a/contracts/compliance/modular/modules/CountryRestrictModule.sol b/contracts/compliance/modular/modules/CountryRestrictModule.sol index cd07191a..ca5b7ae0 100644 --- a/contracts/compliance/modular/modules/CountryRestrictModule.sol +++ b/contracts/compliance/modular/modules/CountryRestrictModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,32 +59,56 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "./AbstractModuleUpgradeable.sol"; + +/// Events + +/// @dev This event is emitted whenever a Country has been restricted. +/// @param _compliance compliance address contract. +/// @param _country is the numeric ISO 3166-1 of the restricted country. +event AddedRestrictedCountry(address indexed _compliance, uint16 _country); + + +/// @dev This event is emitted whenever a Country has been unrestricted. +/// @param _compliance compliance address contract. +/// @param _country is the numeric ISO 3166-1 of the unrestricted country. +event RemovedRestrictedCountry(address indexed _compliance, uint16 _country); + + +/// Errors + +/// @dev Thrown when a country is already restricted. +/// @param _compliance compliance contract address. +/// @param _country country code. +error CountryAlreadyRestricted(address _compliance, uint16 _country); + +/// @dev Thrown when a country is not restricted. +/// @param _compliance compliance contract address. +/// @param _country country code. +error CountryNotRestricted(address _compliance, uint16 _country); + +/// @dev Thrown when max number of countries is reached in batch. +/// @param _max maximum number in batch. +error MaxCountriesInBatchReached(uint256 _max); + + contract CountryRestrictModule is AbstractModuleUpgradeable { + /// Mapping between country and their restriction status per compliance contract mapping(address => mapping(uint16 => bool)) private _restrictedCountries; - /** - * this event is emitted whenever a Country has been restricted. - * the event is emitted by 'addCountryRestriction' and 'batchRestrictCountries' functions. - * `_country` is the numeric ISO 3166-1 of the restricted country. - */ - event AddedRestrictedCountry(address indexed _compliance, uint16 _country); - - /** - * this event is emitted whenever a Country has been unrestricted. - * the event is emitted by 'removeCountryRestriction' and 'batchUnrestrictCountries' functions. - * `_country` is the numeric ISO 3166-1 of the unrestricted country. - */ - event RemovedRestrictedCountry(address indexed _compliance, uint16 _country); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -100,7 +125,7 @@ contract CountryRestrictModule is AbstractModuleUpgradeable { * emits an `AddedRestrictedCountry` event */ function addCountryRestriction(uint16 _country) external onlyComplianceCall { - require((_restrictedCountries[msg.sender])[_country] == false, "country already restricted"); + require((_restrictedCountries[msg.sender])[_country] == false, CountryAlreadyRestricted(msg.sender, _country)); (_restrictedCountries[msg.sender])[_country] = true; emit AddedRestrictedCountry(msg.sender, _country); } @@ -114,7 +139,7 @@ contract CountryRestrictModule is AbstractModuleUpgradeable { * emits an `RemovedRestrictedCountry` event */ function removeCountryRestriction(uint16 _country) external onlyComplianceCall { - require((_restrictedCountries[msg.sender])[_country] == true, "country not restricted"); + require((_restrictedCountries[msg.sender])[_country] == true, CountryNotRestricted(msg.sender, _country)); (_restrictedCountries[msg.sender])[_country] = false; emit RemovedRestrictedCountry(msg.sender, _country); } @@ -129,9 +154,9 @@ contract CountryRestrictModule is AbstractModuleUpgradeable { * emits _countries.length `AddedRestrictedCountry` events */ function batchRestrictCountries(uint16[] calldata _countries) external onlyComplianceCall { - require(_countries.length < 195, "maximum 195 can be restricted in one batch"); + require(_countries.length < 195, MaxCountriesInBatchReached(195)); for (uint256 i = 0; i < _countries.length; i++) { - require((_restrictedCountries[msg.sender])[_countries[i]] == false, "country already restricted"); + require(!(_restrictedCountries[msg.sender])[_countries[i]], CountryAlreadyRestricted(msg.sender, _countries[i])); (_restrictedCountries[msg.sender])[_countries[i]] = true; emit AddedRestrictedCountry(msg.sender, _countries[i]); } @@ -147,9 +172,9 @@ contract CountryRestrictModule is AbstractModuleUpgradeable { * emits _countries.length `RemovedRestrictedCountry` events */ function batchUnrestrictCountries(uint16[] calldata _countries) external onlyComplianceCall { - require(_countries.length < 195, "maximum 195 can be unrestricted in one batch"); + require(_countries.length < 195, MaxCountriesInBatchReached(195)); for (uint256 i = 0; i < _countries.length; i++) { - require((_restrictedCountries[msg.sender])[_countries[i]] == true, "country not restricted"); + require((_restrictedCountries[msg.sender])[_countries[i]], CountryNotRestricted(msg.sender, _countries[i])); (_restrictedCountries[msg.sender])[_countries[i]] = false; emit RemovedRestrictedCountry(msg.sender, _countries[i]); } diff --git a/contracts/compliance/modular/modules/ExchangeMonthlyLimitsModule.sol b/contracts/compliance/modular/modules/ExchangeMonthlyLimitsModule.sol index b1e530f0..f653831b 100644 --- a/contracts/compliance/modular/modules/ExchangeMonthlyLimitsModule.sol +++ b/contracts/compliance/modular/modules/ExchangeMonthlyLimitsModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,39 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "../../../roles/AgentRole.sol"; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted whenever the Exchange Limit has been updated. +/// @param compliance is the address of the caller Compliance contract. +/// @param _exchangeID is the amount ONCHAINID address of the exchange. +/// @param _newExchangeMonthlyLimit is the amount Limit of tokens to be transferred monthly to an exchange wallet. +event ExchangeMonthlyLimitUpdated(address indexed compliance, address _exchangeID, uint _newExchangeMonthlyLimit); + + +/// @dev This event is emitted whenever an ONCHAINID is tagged as being an exchange ID. +/// @param _newExchangeID is the ONCHAINID address of the exchange to add. +event ExchangeIDAdded(address _newExchangeID); + + +/// @dev This event is emitted whenever an ONCHAINID is untagged as belonging to an exchange. +/// @param _exchangeID is the ONCHAINID being untagged as an exchange ID. +event ExchangeIDRemoved(address _exchangeID); + + contract ExchangeMonthlyLimitsModule is AbstractModuleUpgradeable { /// Struct of transfer Counters struct ExchangeTransferCounter { @@ -83,33 +108,6 @@ contract ExchangeMonthlyLimitsModule is AbstractModuleUpgradeable { /// Mapping for wallets tagged as exchange wallets mapping(address => bool) private _exchangeIDs; - /** - * this event is emitted whenever the Exchange Limit has been updated. - * the event is emitted by 'setExchangeMonthlyLimit' - * `compliance` is the address of the caller Compliance contract. - * `_exchangeID` is the amount ONCHAINID address of the exchange. - * `_newExchangeMonthlyLimit` is the amount Limit of tokens to be transferred monthly to an exchange wallet. - */ - event ExchangeMonthlyLimitUpdated(address indexed compliance, address _exchangeID, uint _newExchangeMonthlyLimit); - - /** - * this event is emitted whenever an ONCHAINID is tagged as being an exchange ID. - * the event is emitted by 'addExchangeID'. - * `_newExchangeID` is the ONCHAINID address of the exchange to add. - */ - event ExchangeIDAdded(address _newExchangeID); - - /** - * this event is emitted whenever an ONCHAINID is untagged as belonging to an exchange. - * the event is emitted by 'removeExchangeID'. - * `_exchangeID` is the ONCHAINID being untagged as an exchange ID. - */ - event ExchangeIDRemoved(address _exchangeID); - - error ONCHAINIDAlreadyTaggedAsExchange(address _exchangeID); - - error ONCHAINIDNotTaggedAsExchange(address _exchangeID); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -137,9 +135,7 @@ contract ExchangeMonthlyLimitsModule is AbstractModuleUpgradeable { * emits an `ExchangeIDAdded` event */ function addExchangeID(address _exchangeID) external onlyOwner { - if (isExchangeID(_exchangeID)) { - revert ONCHAINIDAlreadyTaggedAsExchange(_exchangeID); - } + require(!isExchangeID(_exchangeID), ONCHAINIDAlreadyTaggedAsExchange(_exchangeID)); _exchangeIDs[_exchangeID] = true; emit ExchangeIDAdded(_exchangeID); @@ -153,9 +149,8 @@ contract ExchangeMonthlyLimitsModule is AbstractModuleUpgradeable { * emits an `ExchangeIDRemoved` event */ function removeExchangeID(address _exchangeID) external onlyOwner { - if (!isExchangeID(_exchangeID)) { - revert ONCHAINIDNotTaggedAsExchange(_exchangeID); - } + require(isExchangeID(_exchangeID), ONCHAINIDNotTaggedAsExchange(_exchangeID)); + _exchangeIDs[_exchangeID] = false; emit ExchangeIDRemoved(_exchangeID); } diff --git a/contracts/compliance/modular/modules/IModule.sol b/contracts/compliance/modular/modules/IModule.sol index 83cac328..a0800fd8 100644 --- a/contracts/compliance/modular/modules/IModule.sol +++ b/contracts/compliance/modular/modules/IModule.sol @@ -60,24 +60,20 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -interface IModule { - /// events +// events - /** - * this event is emitted when the compliance contract is bound to the module. - * the event is emitted by the bindCompliance function - * `_compliance` is the address of the compliance contract being bound - */ - event ComplianceBound(address indexed _compliance); +/// @dev This event is emitted when the compliance contract is bound to the module. +/// @param _compliance is the address of the compliance contract being bound +event ComplianceBound(address indexed _compliance); - /** - * this event is emitted when the compliance contract is unbound from the module. - * the event is emitted by the unbindCompliance function - * `_compliance` is the address of the compliance contract being unbound - */ - event ComplianceUnbound(address indexed _compliance); +/// @dev This event is emitted when the compliance contract is unbound from the module. +/// @param _compliance is the address of the compliance contract being unbound +event ComplianceUnbound(address indexed _compliance); + + +interface IModule { /// functions diff --git a/contracts/compliance/modular/modules/InvestorCountryCapModule.sol b/contracts/compliance/modular/modules/InvestorCountryCapModule.sol new file mode 100644 index 00000000..c7b2a9ff --- /dev/null +++ b/contracts/compliance/modular/modules/InvestorCountryCapModule.sol @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2024, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. + */ + +pragma solidity 0.8.27; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "./AbstractModuleUpgradeable.sol"; +import "../IModularCompliance.sol"; +import "../../../token/IToken.sol"; +import "../../../roles/AgentRole.sol"; + +error BatchInitializeTooManyHolders(uint256 holdersCount, uint256 maxHolders); +event CountryCapSet(uint16 indexed country, uint256 cap); +event BypassedIdentityAdded(address indexed identity); +event BypassedIdentityRemoved(address indexed identity); + +error ExpectedPause(); +error IdentityNotBypassed(address identity); +error CapLowerThanCurrent(uint16 country, uint256 cap, uint256 currentCap); +error WalletCountLimitReached(address identity, uint256 maxWallets); + + +uint256 constant MAX_WALLET_PER_IDENTITY = 20; +uint256 constant MAX_HOLDERS_BATCH_INITIALIZE = 50; + +contract InvestorCountryCapModule is AbstractModuleUpgradeable { + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableSet for EnumerableSet.UintSet; + + struct CountryParams { + bool capped; + uint256 cap; + uint256 count; + mapping(address identity => bool counted) identities; + } + + EnumerableSet.UintSet internal _countries; + mapping(address identity => bool bypassed) internal _bypassedIdentities; + + mapping(address compliance => mapping(uint16 country => CountryParams params)) internal _countryParams; + mapping(address compliance => mapping(address identity => EnumerableSet.AddressSet wallets)) internal _identityToWallets; + + /// @notice Used only during batchInitialize / canComplianceBind + mapping(address token => uint256 supply) public calculatedSupply; + + + /// @dev initializes the contract and sets the initial state. + /// @notice This function should only be called once during the contract deployment, and after (optionally) batchInitialize. + function initialize() external initializer { + __AbstractModule_init(); + } + + /// @dev Initialize the module for a compliance and a list of holders + /// @param _compliance Address of the compliance. + /// @param _holders Addresses of the holders already holding tokens (addresses should be unique - no control is done on that). + function batchInitialize(address _compliance, address[] memory _holders) external onlyOwner { + require( + _holders.length < MAX_HOLDERS_BATCH_INITIALIZE, + BatchInitializeTooManyHolders(_holders.length, MAX_HOLDERS_BATCH_INITIALIZE) + ); + + IToken token = IToken(IModularCompliance(_compliance).getTokenBound()); + require(token.paused(), ExpectedPause()); + + uint256 holdersCount = _holders.length; + for (uint256 i; i < holdersCount; i++) { + address holder = _holders[i]; + address idTo = _getIdentity(_compliance, holder); + + if (!_bypassedIdentities[idTo]) { + _registerWallet(_compliance, holder, idTo, _getCountry(_compliance, holder)); + } + + calculatedSupply[address(token)] += token.balanceOf(holder); + } + } + + /// @dev Set the cap for a country + /// @param _country Country code + /// @param _cap New cap + function setCountryCap(uint16 _country, uint256 _cap) external onlyComplianceCall { + CountryParams storage params = _countryParams[msg.sender][_country]; + + // Can't set cap lower than current cap + if (_cap < params.cap) { + revert CapLowerThanCurrent(_country, _cap, params.cap); + } + + params.capped = true; + params.cap = _cap; + + _countries.add(_country); + + emit CountryCapSet(_country, _cap); + } + + /// @dev Add an identity to the list of bypassed identities + /// @param _identity Address of the identity + function addBypassedIdentity(address _identity) external onlyComplianceCall { + _bypassedIdentities[_identity] = true; + + emit BypassedIdentityAdded(_identity); + } + + /// @dev Remove an identity from the list of bypassed identities + /// @param _identity Address of the identity + function removeBypassedIdentity(address _identity) external onlyComplianceCall { + require(_bypassedIdentities[_identity], IdentityNotBypassed(_identity)); + _bypassedIdentities[_identity] = false; + + emit BypassedIdentityRemoved(_identity); + } + + /// @inheritdoc IModule + function moduleBurnAction(address _from, uint256 /*_value*/) external onlyComplianceCall { + address _idFrom = _getIdentity(msg.sender, _from); + + uint16 country = _getCountry(msg.sender, _from); + _removeWalletIfNoBalance(_idFrom, country); + } + + /// @inheritdoc IModule + function moduleMintAction(address _to, uint256 /*_value*/) external onlyComplianceCall { + address _idTo = _getIdentity(msg.sender, _to); + + if (_bypassedIdentities[_idTo]) { + return; + } + + uint16 country = _getCountry(msg.sender, _to); + _registerWallet(msg.sender, _to, _idTo, country); + } + + /// @inheritdoc IModule + function moduleTransferAction(address _from, address _to, uint256 /*_value*/) external onlyComplianceCall { + address _idTo = _getIdentity(msg.sender, _to); + + if (_bypassedIdentities[_idTo]) { + return; + } + + uint16 country = _getCountry(msg.sender, _to); + if (!_countryParams[msg.sender][country].capped) { + return; + } + + _registerWallet(msg.sender, _to, _idTo, country); + _removeWalletIfNoBalance(_getIdentity(msg.sender, _from), country); + } + + /// @inheritdoc IModule + function moduleCheck(address /*_from*/, address _to, uint256 /*_value*/, address _compliance) external view returns (bool) { + address _idTo = _getIdentity(_compliance, _to); + + // Bypassed identity are always allowed + if (_bypassedIdentities[_idTo]) { + return true; + } + + uint16 country = _getCountry(_compliance, _to); + CountryParams storage params = _countryParams[_compliance][country]; + + // If country is not capped, allow transfer + if (!params.capped) { + return true; + } + + // If identity is not already counted, check cap + if (!params.identities[_idTo]) { + return params.count < params.cap; + } + + // Check max wallets per identity + if (!_identityToWallets[_compliance][_idTo].contains(_to)) { + return _identityToWallets[_compliance][_idTo].length() + 1 < MAX_WALLET_PER_IDENTITY; + } + + return true; + } + + /// @inheritdoc IModule + function canComplianceBind(address _compliance) external view override returns (bool) { + IToken token = IToken(IModularCompliance(_compliance).getTokenBound()); + + return token.paused() && calculatedSupply[address(token)] == token.totalSupply(); + } + + /// @inheritdoc IModule + function isPlugAndPlay() public pure override returns (bool) { + return false; + } + + /// @inheritdoc IModule + function name() public pure override returns (string memory) { + return "InvestorCountryCapModule"; + } + + /// @dev Register a wallet for an identity, and check for country change + /// @param _compliance Address of the compliance + /// @param _wallet Address of the wallet + /// @param _identity Address of the identity + /// @param _country Country code + function _registerWallet(address _compliance, address _wallet, address _identity, uint16 _country) internal { + IToken token = IToken(IModularCompliance(_compliance).getTokenBound()); + CountryParams storage params = _countryParams[_compliance][_country]; + + // Register wallet for this country if not already registered + if (!params.identities[_identity]) { + if (token.balanceOf(_wallet) > 0) { + // Wallet has a balance, either: + // - User have several countries (Identity already registered) + // - User country has changed + if (_identityToWallets[_compliance][_identity].length() == 0) { + uint256 countryCount = _countries.length(); + for (uint16 i; i < countryCount; i++) { + uint16 otherCountry = uint16(_countries.at(i)); + if (otherCountry != _country && _countryParams[_compliance][otherCountry].identities[_identity]) { + // Unlink previous country + _countryParams[_compliance][otherCountry].identities[_identity] = false; + _countryParams[_compliance][otherCountry].count--; + } + } + } + } + + params.count++; + params.identities[_identity] = true; + } + + _identityToWallets[_compliance][_identity].add(_wallet); + } + + /// @dev Remove a wallet from an identity if no balance + /// @param _identity Address of the identity + /// @param _country Country code + function _removeWalletIfNoBalance(address _identity, uint16 _country) internal { + if (_bypassedIdentities[_identity]) { + return; + } + + IToken token = IToken(IModularCompliance(msg.sender).getTokenBound()); + uint256 walletCount = _identityToWallets[msg.sender][_identity].length(); + uint256 balance; + for (uint256 i; i < walletCount; i++) { + balance += token.balanceOf(_identityToWallets[msg.sender][_identity].at(i)); + } + + // If balance is 0, the identity has no more wallets and should be uncounted + if (balance == 0) { + _countryParams[msg.sender][_country].count--; + _countryParams[msg.sender][_country].identities[_identity] = false; + } + } + + /// @dev Returns the country code of the wallet owner + /// @param _compliance Address of the compliance + /// @param _userAddress Address of the wallet + function _getCountry(address _compliance, address _userAddress) internal view returns (uint16) { + return IToken(IModularCompliance(_compliance).getTokenBound()).identityRegistry().investorCountry(_userAddress); + } + + /// @dev Returns the ONCHAINID (Identity) of the _userAddress + /// @param _compliance Address of the compliance + /// @param _userAddress Address of the wallet + function _getIdentity(address _compliance, address _userAddress) internal view returns (address) { + return address(IToken(IModularCompliance(_compliance).getTokenBound()).identityRegistry().identity + (_userAddress)); + } + +} diff --git a/contracts/compliance/modular/modules/MaxBalanceModule.sol b/contracts/compliance/modular/modules/MaxBalanceModule.sol index 53751a47..81d64fbf 100644 --- a/contracts/compliance/modular/modules/MaxBalanceModule.sol +++ b/contracts/compliance/modular/modules/MaxBalanceModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,61 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "./AbstractModuleUpgradeable.sol"; +/// events + +/// @dev This event is emitted when the max balance has been set for a compliance bound. +/// @param _compliance is the address of modular compliance concerned. +/// @param _maxBalance is the max amount of tokens that a user can hold. +event MaxBalanceSet(address indexed _compliance, uint256 indexed _maxBalance); + +/// @dev This event is emitted when the balance has been set for a compliance bound. +/// @param _compliance is the address of modular compliance concerned. +/// @param _id the ONCHAINID address of the token holder. +/// @param _balance the current balance of the token holder. +event IDBalancePreSet(address indexed _compliance, address indexed _id, uint256 _balance); + + +/// errors + +/// @dev Thrown when +/// @param _compliance compliance contract address. +/// @param _value value. +/// @param _max maximum value. +error MaxBalanceExceeded(address _compliance, uint256 _value, uint256 _max); + +/// @dev Thrown when preset values are invalid. +/// @param _compliance compliance contract address. +/// @param _id array of ids. +/// @param _balance array of balances. +error InvalidPresetValues(address _compliance, address[] _id, uint256[] _balance); + +/// @dev Thrown when called by other than compliance owner. +/// @param _compliance compliance contract address. +error OnlyComplianceOwnerCanCall(address _compliance); + +/// @dev Thrown when the token is already bound. +/// @param _compliance compliance contract address. +error TokenAlreadyBound(address _compliance); + +/// @dev Thrown when identity not found in the identity registry. +/// @param _userAddress address of user. +error IdentityNotFound(address _userAddress); + + contract MaxBalanceModule is AbstractModuleUpgradeable { /// state variables @@ -81,26 +128,6 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { // solhint-disable-next-line var-name-mixedcase mapping(address => mapping(address => uint256)) private _IDBalance; - /// events - - /** - * this event is emitted when the max balance has been set for a compliance bound. - * `_compliance` is the address of modular compliance concerned - * `_maxBalance` is the max amount of tokens that a user can hold . - */ - event MaxBalanceSet(address indexed _compliance, uint256 indexed _maxBalance); - - event IDBalancePreSet(address indexed _compliance, address indexed _id, uint256 _balance); - - /// errors - error MaxBalanceExceeded(address _compliance, uint256 _value); - - error InvalidPresetValues(address _compliance, address[] _id, uint256[] _balance); - - error OnlyComplianceOwnerCanCall(address _compliance); - - error TokenAlreadyBound(address _compliance); - /// functions /** @@ -131,13 +158,8 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { * emits a `IDBalancePreSet` event */ function preSetModuleState(address _compliance, address _id, uint256 _balance) external { - if (OwnableUpgradeable(_compliance).owner() != msg.sender) { - revert OnlyComplianceOwnerCanCall(_compliance); - } - - if (IModularCompliance(_compliance).isModuleBound(address(this))) { - revert TokenAlreadyBound(_compliance); - } + require(OwnableUpgradeable(_compliance).owner() == msg.sender, OnlyComplianceOwnerCanCall(_compliance)); + require(!IModularCompliance(_compliance).isModuleBound(address(this)), TokenAlreadyBound(_compliance)); _preSetModuleState(_compliance, _id, _balance); } @@ -154,17 +176,9 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { address _compliance, address[] calldata _id, uint256[] calldata _balance) external { - if(_id.length == 0 || _id.length != _balance.length) { - revert InvalidPresetValues(_compliance, _id, _balance); - } - - if (OwnableUpgradeable(_compliance).owner() != msg.sender) { - revert OnlyComplianceOwnerCanCall(_compliance); - } - - if (IModularCompliance(_compliance).isModuleBound(address(this))) { - revert TokenAlreadyBound(_compliance); - } + require(_id.length > 0 && _id.length == _balance.length, InvalidPresetValues(_compliance, _id, _balance)); + require(OwnableUpgradeable(_compliance).owner() == msg.sender, OnlyComplianceOwnerCanCall(_compliance)); + require(!IModularCompliance(_compliance).isModuleBound(address(this)), TokenAlreadyBound(_compliance)); for (uint i = 0; i < _id.length; i++) { _preSetModuleState(_compliance, _id[i], _balance[i]); @@ -179,9 +193,7 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { * Only the owner of the Compliance smart contract can call this function */ function presetCompleted(address _compliance) external { - if (OwnableUpgradeable(_compliance).owner() != msg.sender) { - revert OnlyComplianceOwnerCanCall(_compliance); - } + require(OwnableUpgradeable(_compliance).owner() == msg.sender, OnlyComplianceOwnerCanCall(_compliance)); _compliancePresetStatus[_compliance] = true; } @@ -195,7 +207,8 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { address _idTo = _getIdentity(msg.sender, _to); _IDBalance[msg.sender][_idTo] += _value; _IDBalance[msg.sender][_idFrom] -= _value; - if (_IDBalance[msg.sender][_idTo] > _maxBalance[msg.sender]) revert MaxBalanceExceeded(msg.sender, _value); + require(_IDBalance[msg.sender][_idTo] <= _maxBalance[msg.sender], + MaxBalanceExceeded(msg.sender, _value, _maxBalance[msg.sender])); } /** @@ -205,7 +218,8 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { function moduleMintAction(address _to, uint256 _value) external override onlyComplianceCall { address _idTo = _getIdentity(msg.sender, _to); _IDBalance[msg.sender][_idTo] += _value; - if (_IDBalance[msg.sender][_idTo] > _maxBalance[msg.sender]) revert MaxBalanceExceeded(msg.sender, _value); + require(_IDBalance[msg.sender][_idTo] <= _maxBalance[msg.sender], + MaxBalanceExceeded(msg.sender, _value, _maxBalance[msg.sender])); } /** @@ -301,7 +315,7 @@ contract MaxBalanceModule is AbstractModuleUpgradeable { function _getIdentity(address _compliance, address _userAddress) internal view returns (address) { address identity = address(IToken(IModularCompliance(_compliance).getTokenBound()) .identityRegistry().identity(_userAddress)); - require(identity != address(0), "identity not found"); + require(identity != address(0), IdentityNotFound(_userAddress)); return identity; } } diff --git a/contracts/compliance/modular/modules/MinTransferByCountryModule.sol b/contracts/compliance/modular/modules/MinTransferByCountryModule.sol new file mode 100644 index 00000000..2359a0bb --- /dev/null +++ b/contracts/compliance/modular/modules/MinTransferByCountryModule.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import "../IModularCompliance.sol"; +import "../../../token/IToken.sol"; +import "./AbstractModuleUpgradeable.sol"; + + event MinimumTransferAmountSet(address indexed compliance, uint16 indexed country, uint256 amount); + + +/** + * @title MinTransferByCountry Module + * @dev Enforces minimum transfer amounts for token holders from specified countries + * when creating new investors for that country + */ +contract MinTransferByCountryModule is AbstractModuleUpgradeable { + + mapping(address compliance => mapping(uint16 country => uint256 minAmount)) private _minimumTransferAmounts; + + function initialize() external initializer { + __AbstractModule_init(); + } + + /** + * @dev Sets minimum transfer amount for a country + * @param country Country code + * @param amount Minimum transfer amount + */ + function setMinimumTransferAmount(uint16 country, uint256 amount) external onlyComplianceCall { + _minimumTransferAmounts[msg.sender][country] = amount; + + emit MinimumTransferAmountSet(msg.sender, country, amount); + } + + /// @inheritdoc IModule + // solhint-disable-next-line no-empty-blocks + function moduleTransferAction(address _from, address _to, uint256 _value) external {} + + /// @inheritdoc IModule + // solhint-disable-next-line no-empty-blocks + function moduleMintAction(address _to, uint256 _value) external {} + + /// @inheritdoc IModule + // solhint-disable-next-line no-empty-blocks + function moduleBurnAction(address _from, uint256 _value) external {} + + /// @inheritdoc IModule + function moduleCheck( + address _from, + address _to, + uint256 _amount, + address _compliance + ) external view override returns (bool) { + uint16 recipientCountry = _getCountry(_compliance, _to); + if (_minimumTransferAmounts[_compliance][recipientCountry] == 0) { + return true; + } + + // Check for internal transfer in same country + address idFrom = _getIdentity(_compliance, _from); + address idTo = _getIdentity(_compliance, _to); + if (idFrom == idTo) { + uint16 senderCountry = _getCountry(_compliance, _from); + return senderCountry == recipientCountry + || _amount >= _minimumTransferAmounts[_compliance][recipientCountry]; + } + + IToken token = IToken(IModularCompliance(_compliance).getTokenBound()); + // Check for new user + return token.balanceOf(_to) > 0 + || _amount >= _minimumTransferAmounts[_compliance][recipientCountry]; + } + + /// @inheritdoc IModule + function canComplianceBind(address /*_compliance*/) external pure override returns (bool) { + return true; + } + + /// @inheritdoc IModule + function isPlugAndPlay() external pure override returns (bool) { + return true; + } + + /** + * @dev Module name + */ + function name() public pure returns (string memory) { + return "MinTransferByCountryModule"; + } + + + /// @dev function used to get the country of a wallet address. + /// @param _compliance the compliance contract address for which the country verification is required + /// @param _userAddress the address of the wallet to be checked + /// @return the ISO 3166-1 standard country code of the wallet owner + function _getCountry(address _compliance, address _userAddress) internal view returns (uint16) { + return IToken(IModularCompliance(_compliance).getTokenBound()).identityRegistry().investorCountry(_userAddress); + } + + /// @dev Returns the ONCHAINID (Identity) of the _userAddress + /// @param _compliance the compliance contract address for which the country verification is required + /// @param _userAddress Address of the wallet + /// @return the ONCHAINID (Identity) of the _userAddress + function _getIdentity(address _compliance, address _userAddress) internal view returns (address) { + return address(IToken(IModularCompliance(_compliance).getTokenBound()).identityRegistry().identity + (_userAddress)); + } +} \ No newline at end of file diff --git a/contracts/compliance/modular/modules/ModuleProxy.sol b/contracts/compliance/modular/modules/ModuleProxy.sol index 32d921a4..124b2263 100644 --- a/contracts/compliance/modular/modules/ModuleProxy.sol +++ b/contracts/compliance/modular/modules/ModuleProxy.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/contracts/compliance/modular/modules/SupplyLimitModule.sol b/contracts/compliance/modular/modules/SupplyLimitModule.sol index b67f83ee..7ab2d466 100644 --- a/contracts/compliance/modular/modules/SupplyLimitModule.sol +++ b/contracts/compliance/modular/modules/SupplyLimitModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,25 +59,31 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity ^0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted when the supply limit has been set. +/// @param _compliance is the compliance address. +/// @param _limit is the max amount of tokens in circulation. +event SupplyLimitSet(address _compliance, uint256 _limit); + + contract SupplyLimitModule is AbstractModuleUpgradeable { /// supply limits array mapping(address => uint256) private _supplyLimits; - /** - * this event is emitted when the supply limit has been set. - * `_compliance` is the compliance address. - * `_limit` is the max amount of tokens in circulation. - */ - event SupplyLimitSet(address _compliance, uint256 _limit); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. diff --git a/contracts/compliance/modular/modules/TimeExchangeLimitsModule.sol b/contracts/compliance/modular/modules/TimeExchangeLimitsModule.sol index 9ae84e38..99febb0a 100644 --- a/contracts/compliance/modular/modules/TimeExchangeLimitsModule.sol +++ b/contracts/compliance/modular/modules/TimeExchangeLimitsModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,38 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "../../../roles/AgentRole.sol"; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted whenever an exchange limit is updated for the given compliance address. +/// @param _compliance is the compliance contract address. +/// @param _exchangeID is the ONCHAINID of the exchange. +/// @param _limitValue is the new limit value for the given limit time. +/// @param _limitTime is the period of time of the limit. +event ExchangeLimitUpdated(address indexed _compliance, address _exchangeID, uint _limitValue, uint32 _limitTime); + +/// &dev This event is emitted whenever an ONCHAINID is tagged as an exchange ID. +/// @param _newExchangeID is the ONCHAINID address of the exchange to add. +event ExchangeIDAdded(address _newExchangeID); + +/// @dev This event is emitted whenever an ONCHAINID is untagged as belonging to an exchange. +/// @param _exchangeID is the ONCHAINID being untagged as an exchange ID. +event ExchangeIDRemoved(address _exchangeID); + + contract TimeExchangeLimitsModule is AbstractModuleUpgradeable { /// Struct of transfer Counters struct ExchangeTransferCounter { @@ -97,36 +121,6 @@ contract TimeExchangeLimitsModule is AbstractModuleUpgradeable { /// Mapping for wallets tagged as exchange wallets mapping(address => bool) private _exchangeIDs; - /** - * this event is emitted whenever an exchange limit is updated for the given compliance address - * the event is emitted by 'setExchangeLimit'. - * compliance`is the compliance contract address - * _exchangeID is the ONCHAINID of the exchange - * _limitValue is the new limit value for the given limit time - * _limitTime is the period of time of the limit - */ - event ExchangeLimitUpdated(address indexed compliance, address _exchangeID, uint _limitValue, uint32 _limitTime); - - /** - * this event is emitted whenever an ONCHAINID is tagged as an exchange ID. - * the event is emitted by 'addExchangeID'. - * `_newExchangeID` is the ONCHAINID address of the exchange to add. - */ - event ExchangeIDAdded(address _newExchangeID); - - /** - * this event is emitted whenever an ONCHAINID is untagged as belonging to an exchange. - * the event is emitted by 'removeExchangeID'. - * `_exchangeID` is the ONCHAINID being untagged as an exchange ID. - */ - event ExchangeIDRemoved(address _exchangeID); - - error ONCHAINIDAlreadyTaggedAsExchange(address _exchangeID); - - error ONCHAINIDNotTaggedAsExchange(address _exchangeID); - - error LimitsArraySizeExceeded(address compliance, uint arraySize); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -145,11 +139,9 @@ contract TimeExchangeLimitsModule is AbstractModuleUpgradeable { function setExchangeLimit(address _exchangeID, Limit memory _limit) external onlyComplianceCall { bool limitIsAttributed = _limitValues[msg.sender][_exchangeID][_limit.limitTime].attributedLimit; uint8 limitCount = uint8(_exchangeLimits[msg.sender][_exchangeID].length); - if (!limitIsAttributed && limitCount >= 4) { - revert LimitsArraySizeExceeded(msg.sender, limitCount); - } + require(limitIsAttributed || limitCount < 4, LimitsArraySizeExceeded(msg.sender, limitCount)); - if (!limitIsAttributed && limitCount < 4) { + if (!limitIsAttributed) { _exchangeLimits[msg.sender][_exchangeID].push(_limit); _limitValues[msg.sender][_exchangeID][_limit.limitTime] = IndexLimit(true, limitCount); } else { @@ -167,9 +159,7 @@ contract TimeExchangeLimitsModule is AbstractModuleUpgradeable { * emits an `ExchangeIDAdded` event */ function addExchangeID(address _exchangeID) external onlyOwner { - if (isExchangeID(_exchangeID)) { - revert ONCHAINIDAlreadyTaggedAsExchange(_exchangeID); - } + require(!isExchangeID(_exchangeID), ONCHAINIDAlreadyTaggedAsExchange(_exchangeID)); _exchangeIDs[_exchangeID] = true; emit ExchangeIDAdded(_exchangeID); @@ -183,9 +173,8 @@ contract TimeExchangeLimitsModule is AbstractModuleUpgradeable { * emits an `ExchangeIDRemoved` event */ function removeExchangeID(address _exchangeID) external onlyOwner { - if (!isExchangeID(_exchangeID)) { - revert ONCHAINIDNotTaggedAsExchange(_exchangeID); - } + require(isExchangeID(_exchangeID), ONCHAINIDNotTaggedAsExchange(_exchangeID)); + _exchangeIDs[_exchangeID] = false; emit ExchangeIDRemoved(_exchangeID); } diff --git a/contracts/compliance/modular/modules/TimeTransfersLimitsModule.sol b/contracts/compliance/modular/modules/TimeTransfersLimitsModule.sol index 265d6d5f..df4ab011 100644 --- a/contracts/compliance/modular/modules/TimeTransfersLimitsModule.sol +++ b/contracts/compliance/modular/modules/TimeTransfersLimitsModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,14 +59,29 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "../../../roles/AgentRole.sol"; import "./AbstractModuleUpgradeable.sol"; +import "../../../errors/InvalidArgumentErrors.sol"; + +/// Events + +/// @dev This event is emitted whenever a transfer limit is updated for the given compliance address and limit time. +/// @param _compliance is the compliance contract address. +/// @param _limitTime is the period of time of the limit. +/// @param _limitValue is the new limit value for the given limit time. +event TimeTransferLimitUpdated(address indexed _compliance, uint32 _limitTime, uint256 _limitValue); + contract TimeTransfersLimitsModule is AbstractModuleUpgradeable { /// Struct of transfer Counters @@ -93,17 +109,6 @@ contract TimeTransfersLimitsModule is AbstractModuleUpgradeable { /// Mapping for users Counters mapping(address => mapping(address => mapping(uint32 => TransferCounter))) public usersCounters; - /** - * this event is emitted whenever a transfer limit is updated for the given compliance address and limit time - * the event is emitted by 'setTimeTransferLimit'. - * compliance`is the compliance contract address - * _limitValue is the new limit value for the given limit time - * _limitTime is the period of time of the limit - */ - event TimeTransferLimitUpdated(address indexed compliance, uint32 limitTime, uint256 limitValue); - - error LimitsArraySizeExceeded(address compliance, uint arraySize); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -120,10 +125,9 @@ contract TimeTransfersLimitsModule is AbstractModuleUpgradeable { function setTimeTransferLimit(Limit calldata _limit) external onlyComplianceCall { bool limitIsAttributed = limitValues[msg.sender][_limit.limitTime].attributedLimit; uint8 limitCount = uint8(transferLimits[msg.sender].length); - if (!limitIsAttributed && limitCount >= 4) { - revert LimitsArraySizeExceeded(msg.sender, limitCount); - } - if (!limitIsAttributed && limitCount < 4) { + require(limitIsAttributed || limitCount < 4, LimitsArraySizeExceeded(msg.sender, limitCount)); + + if (!limitIsAttributed) { transferLimits[msg.sender].push(_limit); limitValues[msg.sender][_limit.limitTime] = IndexLimit(true, limitCount); } else { diff --git a/contracts/compliance/modular/modules/TokenListingRestrictionsModule.sol b/contracts/compliance/modular/modules/TokenListingRestrictionsModule.sol new file mode 100644 index 00000000..f3f6b85a --- /dev/null +++ b/contracts/compliance/modular/modules/TokenListingRestrictionsModule.sol @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import "../IModularCompliance.sol"; +import "../../../token/IToken.sol"; +import "./AbstractModuleUpgradeable.sol"; + +/// Types + +enum ListingType { + NOT_CONFIGURED, // default value (token is not configured yet) + WHITELISTING, + BLACKLISTING +} + +enum InvestorAddressType { + WALLET, + ONCHAINID +} + + +/// events + +/// @dev This event is emitted whenever a token is configured with a non-zero listing type +/// @param _tokenAddress the address of the configured token +/// @param _listingType the configured listing type for the token (1: WHITELISTING, 2: BLACKLISTING) +event TokenListingConfigured(address _tokenAddress, ListingType _listingType); + +/// @dev This event is emitted whenever a token is listed (whitelisted or blacklisted) for an investor +/// @param _tokenAddress the address of the listed token +/// @param _investorAddress the investor address (a wallet or an ONCHAINID address) +event TokenListed(address _tokenAddress, address _investorAddress); + +/// @dev This event is emitted whenever a token is unlisted for an investor +/// @param _tokenAddress the address of the unlisted token +/// @param _investorAddress the investor address (a wallet or an ONCHAINID address) +event TokenUnlisted(address _tokenAddress, address _investorAddress); + +/// Errors + +/// @dev Thrown when the token is already configured +/// @param _tokenAddress the address of the token +error TokenAlreadyConfigured(address _tokenAddress); + +/// @dev Thrown when the token is not configured +/// @param _tokenAddress the address of the token +error TokenIsNotConfigured(address _tokenAddress); + +/// @dev Thrown when the token is already listed for the investor +/// @param _tokenAddress the address of the token +/// @param _investorAddress the investor address (a wallet or an ONCHAINID address) +error TokenAlreadyListed(address _tokenAddress, address _investorAddress); + +/// @dev Thrown when the token is not listed for the investor +/// @param _tokenAddress the address of the token +/// @param _investorAddress the investor address (a wallet or an ONCHAINID address) +error TokenIsNotListed(address _tokenAddress, address _investorAddress); + +/// @dev Thrown when the identity is not found +/// @param _tokenAddress the address of the token +/// @param _userAddress the user address (a wallet or an ONCHAINID address) +error IdentityNotFound(address _tokenAddress, address _userAddress); + +/// @dev Thrown when the listing type is invalid for configuration +/// @param _listingType the listing type +error InvalidListingTypeForConfiguration(ListingType _listingType); + + +contract TokenListingRestrictionsModule is AbstractModuleUpgradeable { + + /// Mapping between token and listing type + mapping(address token => ListingType) private _tokenListingType; + + /// Mapping between tokenAddress and investor (wallet or OID address) + /// and listing status (whitelisted or blacklisted depending on the listing type of the token) + mapping(address token => mapping(address investor => bool)) private _tokenInvestorListingStatus; + + /// functions + /** + * @dev initializes the contract and sets the initial state. + * @notice This function should only be called once during the contract deployment. + */ + function initialize() external initializer { + __AbstractModule_init(); + } + + /** + * @dev Configures the listing type of a token + * Can only be called once per token + * @param _listingType can be WHITELISTING(1) or BLACKLISTING(2) + * WHITELISTING(1): investors must whitelist/allow the token address in order to receive it + * BLACKLISTING(2): investors can receive this token by default. If they do not want to receive it, + * they need to blacklist/disallow it. + * Only the owner of the Compliance smart contract can call this function + */ + function configureToken(ListingType _listingType) external onlyComplianceCall { + address tokenAddress = _getBoundTokenAddress(msg.sender); + if (_listingType == ListingType.NOT_CONFIGURED) { + revert InvalidListingTypeForConfiguration(_listingType); + } + + if (_tokenListingType[tokenAddress] != ListingType.NOT_CONFIGURED) { + revert TokenAlreadyConfigured(tokenAddress); + } + + _tokenListingType[tokenAddress] = _listingType; + emit TokenListingConfigured(tokenAddress, _listingType); + } + + /** + * @dev Lists multiple tokens for the investor (caller) + * If the token listing type is WHITELISTING, it will be whitelisted/allowed for the investor. + * If the token listing type is BLACKLISTING, it will be blaclisted/disallowed for the investor. + * @param _tokenAddresses is the array of addresses of tokens to be listed + * @param _addressType can be WALLET(0) or ONCHAINID(1) + * If it is WALLET, the token will be listed only for the caller wallet address + * If it is ONCHAINID, the token will be listed for the ONCHAINID (it will be applied to all wallet addresses of the OID) + * It will revert if the listing type of the token is not configured + */ + function batchListTokens(address[] calldata _tokenAddresses, InvestorAddressType _addressType) external { + for (uint256 i = 0; i < _tokenAddresses.length; i++) { + listToken(_tokenAddresses[i], _addressType); + } + } + + /** + * @dev Unlists multiple tokens for the investor (caller) + * @param _tokenAddresses is the array of addresses of tokens to be unlisted + * @param _addressType can be WALLET(0) or ONCHAINID(1) + * If it is WALLET, the token will be unlisted only for the caller wallet address + * If it is ONCHAINID, the token will be unlisted for the ONCHAINID + */ + function batchUnlistTokens(address[] calldata _tokenAddresses, InvestorAddressType _addressType) external { + for (uint256 i = 0; i < _tokenAddresses.length; i++) { + unlistToken(_tokenAddresses[i], _addressType); + } + } + + /** + * @dev See {IModule-moduleTransferAction}. + * no transfer action required in this module + */ + // solhint-disable-next-line no-empty-blocks + function moduleTransferAction(address _from, address _to, uint256 _value) external override onlyComplianceCall {} + + /** + * @dev See {IModule-moduleMintAction}. + * no mint action required in this module + */ + // solhint-disable-next-line no-empty-blocks + function moduleMintAction(address _to, uint256 _value) external override onlyComplianceCall {} + + /** + * @dev See {IModule-moduleBurnAction}. + * no burn action required in this module + */ + // solhint-disable-next-line no-empty-blocks + function moduleBurnAction(address _from, uint256 _value) external override onlyComplianceCall {} + + /** + * @dev See {IModule-moduleCheck}. + * checks whether the _to address allows this token to receive + * returns TRUE if the token is allowed for the address of _to (or for the OID of _to) + * returns FALSE if the token is not allowed for the address of _to + */ + function moduleCheck( + address /*_from*/, + address _to, + uint256 /*_value*/, + address _compliance + ) external view override returns (bool) { + if (_to == address(0)) { + return true; + } + + address tokenAddress = _getBoundTokenAddress(_compliance); + ListingType listingType = _tokenListingType[tokenAddress]; + if (listingType == ListingType.NOT_CONFIGURED) { + return true; + } + + bool listed = _tokenInvestorListingStatus[tokenAddress][_to] + || _tokenInvestorListingStatus[tokenAddress][_getIdentityByTokenAddress(tokenAddress, _to)]; + + if (listingType == ListingType.BLACKLISTING) { + return !listed; + } + + return listed; + } + + /** + * @dev Returns the configured listing type of the given token address + * @param _tokenAddress the token smart contract to be checked + * returns the listing type of the token: + * NOT_CONFIGURED(0): The token is not configured for this module yet + * WHITELISTING(1): investors must whitelist/allow the token address in order to receive it + * BLACKLISTING(2): investors can receive this token by default. If they do not want to receive it, + * they need to blacklist/disallow it. + */ + function getTokenListingType(address _tokenAddress) external view returns (ListingType) { + return _tokenListingType[_tokenAddress]; + } + + /** + * @dev Returns the listing status of the given investor address for the token + * @param _tokenAddress the token smart contract to be checked + * @param _investorAddress the WALLET or ONCHAINID address of an investor to be checked + * returns the listing status of the given investor. + * If it is true: + * - if the listing type of the token is WHITELISTING(1): investor can receive this token. + * - if the listing type of the token is BLACKLISTING(2): investor can not receive this token. + * If it is false: + * - if the listing type of the token is WHITELISTING(1): investor can not receive this token. + * - if the listing type of the token is BLACKLISTING(2): investor can receive this token. + */ + function getInvestorListingStatus(address _tokenAddress, address _investorAddress) external view returns (bool) { + return _tokenInvestorListingStatus[_tokenAddress][_investorAddress]; + } + + /** + * @dev See {IModule-canComplianceBind}. + */ + function canComplianceBind(address /*_compliance*/) external pure override returns (bool) { + return true; + } + + /** + * @dev See {IModule-isPlugAndPlay}. + */ + function isPlugAndPlay() external pure override returns (bool) { + return true; + } + + /** + * @dev Lists a token for the investor (caller) + * If the token listing type is WHITELISTING, it will be whitelisted/allowed for the investor. + * If the token listing type is BLACKLISTING, it will be blaclisted/disallowed for the investor. + * @param _tokenAddress is the address of the token to be listed + * @param _addressType can be WALLET(0) or ONCHAINID(1) + * If it is WALLET, the token will be listed only for the caller wallet address + * If it is ONCHAINID, the token will be listed for the ONCHAINID (it will be applied to all wallet addresses of the OID) + * It will revert if the listing type of the token is not configured + */ + function listToken(address _tokenAddress, InvestorAddressType _addressType) public { + if (_tokenListingType[_tokenAddress] == ListingType.NOT_CONFIGURED) { + revert TokenIsNotConfigured(_tokenAddress); + } + + address investorAddress = _getInvestorAddressByAddressType(_tokenAddress, msg.sender, _addressType); + if (_tokenInvestorListingStatus[_tokenAddress][investorAddress]) { + revert TokenAlreadyListed(_tokenAddress, investorAddress); + } + + _tokenInvestorListingStatus[_tokenAddress][investorAddress] = true; + emit TokenListed(_tokenAddress, investorAddress); + } + + /** + * @dev Unlists a token for the investor (caller) + * @param _tokenAddress is the address of the token to be unlisted + * @param _addressType can be WALLET(0) or ONCHAINID(1) + * If it is WALLET, the token will be unlisted only for the caller wallet address + * If it is ONCHAINID, the token will be unlisted for the ONCHAINID + */ + function unlistToken(address _tokenAddress, InvestorAddressType _addressType) public { + address investorAddress = _getInvestorAddressByAddressType(_tokenAddress, msg.sender, _addressType); + if (!_tokenInvestorListingStatus[_tokenAddress][investorAddress]) { + revert TokenIsNotListed(_tokenAddress, investorAddress); + } + + _tokenInvestorListingStatus[_tokenAddress][investorAddress] = false; + emit TokenUnlisted(_tokenAddress, investorAddress); + } + + /** + * @dev See {IModule-name}. + */ + function name() public pure returns (string memory _name) { + return "TokenListingRestrictionsModule"; + } + + function _getInvestorAddressByAddressType( + address _tokenAddress, + address _userAddress, + InvestorAddressType _addressType + ) internal view returns (address) { + if (_addressType == InvestorAddressType.WALLET) { + return _userAddress; + } + + address identity = _getIdentityByTokenAddress(_tokenAddress, _userAddress); + if (identity == address(0)) { + revert IdentityNotFound(_tokenAddress, _userAddress); + } + + return identity; + } + + function _getIdentityByTokenAddress(address _tokenAddress, address _userAddress) internal view returns (address) { + return address(IToken(_tokenAddress).identityRegistry().identity(_userAddress)); + } + + function _getBoundTokenAddress(address _compliance) internal view returns (address) { + return IModularCompliance(_compliance).getTokenBound(); + } +} diff --git a/contracts/compliance/modular/modules/TransferFeesModule.sol b/contracts/compliance/modular/modules/TransferFeesModule.sol index 657035fe..8802527a 100644 --- a/contracts/compliance/modular/modules/TransferFeesModule.sol +++ b/contracts/compliance/modular/modules/TransferFeesModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +59,45 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../IModularCompliance.sol"; import "../../../token/IToken.sol"; import "../../../roles/AgentRole.sol"; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted whenever a fee definition is updated for the given compliance address. +/// @param _compliance is the compliance contract address. +/// @param _rate is the rate of the fee in BPS (0.01% = 1, 1% = 100, 100% = 10000). +/// @param _collector is the collector wallet address. +event FeeUpdated(address indexed _compliance, uint256 _rate, address _collector); + + +/// Errors + +/// @dev Thrown when fee rate is out of range. +/// @param _compliance compliance contract address. +/// @param _rate rate value. +error FeeRateIsOutOfRange(address _compliance, uint256 _rate); + +/// @dev Thrown when the collector address is not verified. +/// @param _compliance compliance contract address. +/// @param _collector collector contract address. +error CollectorAddressIsNotVerified(address _compliance, address _collector); + +/// @dev Thrown when transfer fee collection failed. +error TransferFeeCollectionFailed(); + + contract TransferFeesModule is AbstractModuleUpgradeable { /// Struct of fees struct Fee { @@ -77,19 +108,6 @@ contract TransferFeesModule is AbstractModuleUpgradeable { /// Mapping for compliance fees mapping(address => Fee) private _fees; - /** - * this event is emitted whenever a fee definition is updated for the given compliance address - * the event is emitted by 'setFee'. - * compliance is the compliance contract address - * _rate is the rate of the fee (0.01% = 1, 1% = 100, 100% = 10000) - * _collector is the collector wallet address - */ - event FeeUpdated(address indexed compliance, uint256 _rate, address _collector); - - error FeeRateIsOutOfRange(address compliance, uint256 rate); - - error CollectorAddressIsNotVerified(address compliance, address collector); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -107,14 +125,10 @@ contract TransferFeesModule is AbstractModuleUpgradeable { */ function setFee(uint256 _rate, address _collector) external onlyComplianceCall { address tokenAddress = IModularCompliance(msg.sender).getTokenBound(); - if (_rate > 10000) { - revert FeeRateIsOutOfRange(msg.sender, _rate); - } + require(_rate <= 10000, FeeRateIsOutOfRange(msg.sender, _rate)); - IIdentityRegistry identityRegistry = IToken(tokenAddress).identityRegistry(); - if (!identityRegistry.isVerified(_collector)) { - revert CollectorAddressIsNotVerified(msg.sender, _collector); - } + IERC3643IdentityRegistry identityRegistry = IToken(tokenAddress).identityRegistry(); + require(identityRegistry.isVerified(_collector), CollectorAddressIsNotVerified(msg.sender, _collector)); _fees[msg.sender].rate = _rate; _fees[msg.sender].collector = _collector; @@ -144,7 +158,7 @@ contract TransferFeesModule is AbstractModuleUpgradeable { IToken token = IToken(IModularCompliance(msg.sender).getTokenBound()); bool sent = token.forcedTransfer(_to, fee.collector, feeAmount); - require(sent, "transfer fee collection failed"); + require(sent, TransferFeeCollectionFailed()); } /** diff --git a/contracts/compliance/modular/modules/TransferRestrictModule.sol b/contracts/compliance/modular/modules/TransferRestrictModule.sol index 971eb92b..35fdffcc 100644 --- a/contracts/compliance/modular/modules/TransferRestrictModule.sol +++ b/contracts/compliance/modular/modules/TransferRestrictModule.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 +// This contract is also licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= @@ -44,7 +45,7 @@ * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * - * Copyright (C) 2023, Tokeny sàrl. + * Copyright (C) 2024, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,30 +59,34 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This specific smart contract is also licensed under the Creative Commons + * Attribution-NonCommercial 4.0 International License (CC-BY-NC-4.0), + * which prohibits commercial use. For commercial inquiries, please contact + * Tokeny sàrl for licensing options. */ -pragma solidity ^0.8.17; +pragma solidity 0.8.27; import "./AbstractModuleUpgradeable.sol"; +/// Events + +/// @dev This event is emitted when a user is allowed for transfer. +/// @param _compliance is the compliance address. +/// @param _userAddress is the allowed user address. +event UserAllowed(address _compliance, address _userAddress); + +/// @dev This event is emitted when a user is disallowed for transfer. +/// @param _compliance is the compliance address. +/// @param _userAddress is the disallowed user address +event UserDisallowed(address _compliance, address _userAddress); + + contract TransferRestrictModule is AbstractModuleUpgradeable { /// allowed user addresses mapping mapping(address => mapping(address => bool)) private _allowedUserAddresses; - /** - * this event is emitted when a user is allowed for transfer - * `_compliance` is the compliance address. - * `_userAddress` is the allowed user address - */ - event UserAllowed(address _compliance, address _userAddress); - - /** - * this event is emitted when a user is disallowed for transfer - * `_compliance` is the compliance address. - * `_userAddress` is the disallowed user address - */ - event UserDisallowed(address _compliance, address _userAddress); - /** * @dev initializes the contract and sets the initial state. * @notice This function should only be called once during the contract deployment. @@ -172,6 +177,10 @@ contract TransferRestrictModule is AbstractModuleUpgradeable { uint256 /*_value*/, address _compliance ) external view override returns (bool) { + if (_from == address(0) || _to == address(0)) { + return true; + } + if(_allowedUserAddresses[_compliance][_from]) { return true; } diff --git a/contracts/errors/CommonErrors.sol b/contracts/errors/CommonErrors.sol new file mode 100644 index 00000000..12e7eac6 --- /dev/null +++ b/contracts/errors/CommonErrors.sol @@ -0,0 +1,129 @@ + +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +/// @dev Thrown when initialization has failed. +error InitializationFailed(); + +/// @dev Thrown when the implementation authority is invalid. +error InvalidImplementationAuthority(); + + +/// @dev We must use OpenZeppelin libs when upgrading to v >= 5 for errors below + +/** +* @dev The caller account is not authorized to perform an operation. +*/ +error OwnableUnauthorizedAccount(address account); + +/** +* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. +* @param spender Address that may be allowed to operate on tokens without being their owner. +* @param allowance Amount of tokens a `spender` is allowed to operate with. +* @param needed Minimum amount required to perform a transfer. +*/ +error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + +/** +* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. +* @param sender Address whose tokens are being transferred. +* @param balance Current balance for the interacting account. +* @param needed Minimum amount required to perform a transfer. +*/ +error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + +/** +* @dev Indicates a failure with the token `receiver`. Used in transfers. +* @param receiver Address to which tokens are being transferred. +*/ +error ERC20InvalidReceiver(address receiver); + +/** +* @dev Indicates a failure with the token `sender`. Used in transfers. +* @param sender Address whose tokens are being transferred. +*/ +error ERC20InvalidSender(address sender); + +/** +* @dev Indicates a failure with the `spender` to be approved. Used in approvals. +* @param spender Address that may be allowed to operate on tokens without being their owner. +*/ +error ERC20InvalidSpender(address spender); + +/** +* @dev The operation failed because the contract is paused. +*/ +error EnforcedPause(); + +/** +* @dev The operation failed because the contract is not paused. +*/ +error ExpectedPause(); + +/** +* @dev The operation failed because the input array is too big. +* @param _maxSize maximum size for the array. +*/ +error ArraySizeLimited(uint256 _maxSize); diff --git a/contracts/errors/ComplianceErrors.sol b/contracts/errors/ComplianceErrors.sol new file mode 100644 index 00000000..3af7df53 --- /dev/null +++ b/contracts/errors/ComplianceErrors.sol @@ -0,0 +1,93 @@ + +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +/// @dev Thrown when address is not a token bound to compliance contract. +error AddressNotATokenBoundToComplianceContract(); + +/// @dev Thrown when compliance is already bound. +error ComplianceAlreadyBound(); + +/// @dev Thrown when compliance is not bound. +error ComplianceNotBound(); + +/// @dev Thrown when coompliance is not suitable for binding to module. +/// @param module Address of the module. +error ComplianceNotSuitableForBindingToModule(address module); + +/// @dev Thrown when exchange already tagged. +/// @param _exchangeID ONCHAINID of the exchange. +error ONCHAINIDAlreadyTaggedAsExchange(address _exchangeID); + +/// @dev Thrown when exchange is not tagged. +/// @param _exchangeID ONCHAINID of the exchange. +error ONCHAINIDNotTaggedAsExchange(address _exchangeID); + +/// @dev Thrown when call by otther than bound compliance. +error OnlyBoundComplianceCanCall(); + +/// @dev Thrown when call by other than compliance contract. +error OnlyComplianceContractCanCall(); + diff --git a/contracts/errors/InvalidArgumentErrors.sol b/contracts/errors/InvalidArgumentErrors.sol new file mode 100644 index 00000000..6b1e4862 --- /dev/null +++ b/contracts/errors/InvalidArgumentErrors.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + + +/// @dev Thrown when the address is not an ERC20. +/// @param token address of the token. +error AddressNotERC20(address token); + +/// @dev Thrown when limits array size exceeded. +/// @param _compliance compliance contract address. +/// @param _arraySize array size. +error LimitsArraySizeExceeded(address _compliance, uint _arraySize); + +/// @dev Thrown when invalid decimals is set. +/// @param _decimals number of decimals +error DecimalsOutOfRange(uint256 _decimals); + +/// @dev Thrown when the string passed is empty. +error EmptyString(); + +/// @dev Thrown when token amount is zero. +error ZeroValue(); + +/// @dev Thrown when the address passed is the zero address. +error ZeroAddress(); diff --git a/contracts/errors/RoleErrors.sol b/contracts/errors/RoleErrors.sol new file mode 100644 index 00000000..2d1aa1a4 --- /dev/null +++ b/contracts/errors/RoleErrors.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +/// @dev Thrown when account already has role. +error AccountAlreadyHasRole(); + +/// @dev Thrown when account doesn't have role. +error AccountDoesNotHaveRole(); + +/// @dev Thrown when caller doesn't have agent role. +error CallerDoesNotHaveAgentRole(); + +/// @dev Thrown when sender is not admin. +error SenderIsNotAdmin(); + +/// @dev Thrown when sender is not claim registry manager. +error SenderIsNotClaimRegistryManager(); + +/// @dev Thrown when sender is not comp^liance manager. +error SenderIsNotComplianceManager(); + +/// @dev Thrown when sender is not complikance setter. +error SenderIsNotComplianceSetter(); + +/// @dev Thrown when sender is not freezer. +error SenderIsNotFreezer(); + +/// @dev Thrown when sender is not issuer registry manager. +error SenderIsNotIssuersRegistryManager(); + +/// @dev Thrown when sender is not recovery agent. +error SenderIsNotRecoveryAgent(); + +/// @dev Thrown when sender is not registry address setter. +error SenderIsNotRegistryAddressSetter(); + +/// @dev Thrown when sender is not supply modifier. +error SenderIsNotSupplyModifier(); + +/// @dev Thrown when sender is not token information manager. +error SenderIsNotTokenInformationManager(); + +/// @dev Thrown when sender is not transfer manager. +error SenderIsNotTransferManager(); + +/// @dev Thrown when sender is not whitelist manager. +error SenderIsNotWhiteListManager(); diff --git a/contracts/events/CommonEvents.sol b/contracts/events/CommonEvents.sol new file mode 100644 index 00000000..82cd33f9 --- /dev/null +++ b/contracts/events/CommonEvents.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + + +/// event emitted when the implementation authority of the factory contract is set +event ImplementationAuthoritySet(address _implementationAuthority); + diff --git a/contracts/factory/ITREXFactory.sol b/contracts/factory/ITREXFactory.sol index 2064985b..9d6ce4a1 100644 --- a/contracts/factory/ITREXFactory.sol +++ b/contracts/factory/ITREXFactory.sol @@ -59,7 +59,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; + +import "../events/CommonEvents.sol"; + +/// Events + +/// @dev Event emitted whenever a single contract is deployed by the factory. +/// @param _addr Address of the created contract. +event Deployed(address indexed _addr); + +/// @dev Event emitted when the Identity Factory is set. +/// @param _idFactory The address of the identity factory contract. +event IdFactorySet(address _idFactory); + +/// @dev Event emitted by the factory when a full suite of T-REX contracts is deployed. +/// @param _token Address of token contract. +/// @param _ir Address of identity registry contract. +/// @param _irs Address of identity registry storage contract. +/// @param _tir Address of trusted identity registry contract. +/// @param _ctr Address of claim topics registry contract. +/// @param _mc Address of modular compliance contract. +/// @param _salt The salt string that was used to deploy the token. +event TREXSuiteDeployed( + address indexed _token, + address _ir, + address _irs, + address _tir, + address _ctr, + address _mc, + string indexed _salt); + interface ITREXFactory { @@ -101,21 +131,6 @@ interface ITREXFactory { uint256[][] issuerClaims; } - /// events - - /// event emitted whenever a single contract is deployed by the factory - event Deployed(address indexed _addr); - - /// event emitted when the Identity Factory is set - event IdFactorySet(address _idFactory); - - /// event emitted when the implementation authority of the factory contract is set - event ImplementationAuthoritySet(address _implementationAuthority); - - /// event emitted by the factory when a full suite of T-REX contracts is deployed - event TREXSuiteDeployed(address indexed _token, address _ir, address _irs, address _tir, address _ctr, address - _mc, string indexed _salt); - /// functions /** diff --git a/contracts/factory/ITREXGateway.sol b/contracts/factory/ITREXGateway.sol index b7d1da92..a6c25790 100644 --- a/contracts/factory/ITREXGateway.sol +++ b/contracts/factory/ITREXGateway.sol @@ -59,10 +59,50 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./ITREXFactory.sol"; +/// Events + +/// @dev Event emitted when the _factory variable is set/modified. +/// @param _factory The address of the new factory contract. +event FactorySet(address indexed _factory); + +/// @dev Event emitted when the public deployment status is set/modified. +/// @param _publicDeploymentStatus Determines if public deployments are enabled (`true`) or disabled (`false`). +event PublicDeploymentStatusSet(bool indexed _publicDeploymentStatus); + +/// @dev Event emitted when the deployment fees details are set/modified. +/// @param _fee The amount of deployment fee. +/// @param _feeToken Address of the token used for the deployment fee. +/// @param _feeCollector Address that will collect the deployment fees. +event DeploymentFeeSet(uint256 indexed _fee, address indexed _feeToken, address indexed _feeCollector); + +/// @dev Event emitted when the deployment fees are enabled/disabled. +/// @param _isEnabled Determines if deployment fees are enabled (`true`) or disabled (`false`). +event DeploymentFeeEnabled(bool indexed _isEnabled); + +/// @dev Event emitted when an address is flagged as a deployer. +/// @param _deployer Address added to the list of approved deployers. +event DeployerAdded(address indexed _deployer); + +/// @dev Event emitted when a deployer address loses deployment privileges. +/// @param _deployer Address removed from the list of approved deployers. +event DeployerRemoved(address indexed _deployer); + +/// @dev Event emitted when a discount on deployment fees is granted for an address. +/// @param _deployer Address of the deployer to which the discount will be applied. +/// @param _discount The discount rate in BPS. +event FeeDiscountApplied(address indexed _deployer, uint16 _discount); + +/// @dev Event emitted whenever a TREX token has been deployed by the TREX factory through the use of the Gateway. +/// @param _requester Address who called the deployement. +/// @param _intendedOwner Address of the intended owner. +/// @param _feeApplied Fee amount in BPS. +event GatewaySuiteDeploymentProcessed(address indexed _requester, address _intendedOwner, uint256 _feeApplied); + + interface ITREXGateway { /// Types @@ -76,32 +116,6 @@ interface ITREXGateway { address feeCollector; } - /// events - - /// event emitted when the _factory variable is set/modified - event FactorySet(address indexed factory); - - /// event emitted when the public deployment status is set/modified - event PublicDeploymentStatusSet(bool indexed publicDeploymentStatus); - - /// event emitted when the deployment fees details are set/modified - event DeploymentFeeSet(uint256 indexed fee, address indexed feeToken, address indexed feeCollector); - - /// event emitted when the deployment fees are enabled/disabled - event DeploymentFeeEnabled(bool indexed isEnabled); - - /// event emitted when an address is flagged as a deployer - event DeployerAdded(address indexed deployer); - - /// event emitted when a deployer address loses deployment privileges - event DeployerRemoved(address indexed deployer); - - /// event emitted when a discount on deployment fees is granted for an address - event FeeDiscountApplied(address indexed deployer, uint16 discount); - - /// event emitted whenever a TREX token has been deployed by the TREX factory through the use of the Gateway - event GatewaySuiteDeploymentProcessed(address indexed requester, address intendedOwner, uint256 feeApplied); - /// Functions /** diff --git a/contracts/factory/TREXFactory.sol b/contracts/factory/TREXFactory.sol index af8cc9f7..db737ed1 100644 --- a/contracts/factory/TREXFactory.sol +++ b/contracts/factory/TREXFactory.sol @@ -59,7 +59,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../roles/AgentRole.sol"; import "../token/IToken.sol"; @@ -77,6 +77,35 @@ import "../proxy/TrustedIssuersRegistryProxy.sol"; import "../proxy/ModularComplianceProxy.sol"; import "./ITREXFactory.sol"; import "@onchain-id/solidity/contracts/factory/IIdFactory.sol"; +import "../errors/CommonErrors.sol"; +import "../errors/InvalidArgumentErrors.sol"; + +/// Errors + +/// @dev Thrown when claim pattern is invalid. +error InvalidClaimPattern(); + +/// @dev Thrown when compliance pattern is invalid. +error InvalidCompliancePattern(); + +/// @dev Thrown when maximum number of claim issuers is reached. +/// @param _max max value. +error MaxClaimIssuersReached(uint256 _max); + +/// @dev Thrown when maximum number of claim topicsis reached. +/// @param _max max value. +error MaxClaimTopicsReached(uint256 _max); + +/// @dev Thrown when maximum number of agetns is reached. +/// @param _max max value. +error MaxAgentsReached(uint256 _max); + +/// @dev Thrown when maximum number of module actions reached. +/// @param _max max value. +error MaxModuleActionsReached(uint256 _max); + +/// @dev Thrown when token is already deployed. +error TokenAlreadyDeployed(); contract TREXFactory is ITREXFactory, Ownable { @@ -90,6 +119,7 @@ contract TREXFactory is ITREXFactory, Ownable { /// mapping containing info about the token contracts corresponding to salt already used for CREATE2 deployments mapping(string => address) public tokenDeployed; + /// constructor is setting the implementation authority and the Identity Factory of the TREX factory constructor(address implementationAuthority_, address idFactory_) { setImplementationAuthority(implementationAuthority_); @@ -103,20 +133,14 @@ contract TREXFactory is ITREXFactory, Ownable { function deployTREXSuite(string memory _salt, TokenDetails calldata _tokenDetails, ClaimDetails calldata _claimDetails) external override onlyOwner { - require(tokenDeployed[_salt] == address(0) - , "token already deployed"); - require((_claimDetails.issuers).length == (_claimDetails.issuerClaims).length - , "claim pattern not valid"); - require((_claimDetails.issuers).length <= 5 - , "max 5 claim issuers at deployment"); - require((_claimDetails.claimTopics).length <= 5 - , "max 5 claim topics at deployment"); - require((_tokenDetails.irAgents).length <= 5 && (_tokenDetails.tokenAgents).length <= 5 - , "max 5 agents at deployment"); - require((_tokenDetails.complianceModules).length <= 30 - , "max 30 module actions at deployment"); - require((_tokenDetails.complianceModules).length >= (_tokenDetails.complianceSettings).length - , "invalid compliance pattern"); + require(tokenDeployed[_salt] == address(0), TokenAlreadyDeployed()); + require((_claimDetails.issuers).length == (_claimDetails.issuerClaims).length, InvalidClaimPattern()); + require((_claimDetails.issuers).length <= 5, MaxClaimIssuersReached(5)); + require((_claimDetails.claimTopics).length <= 5, MaxClaimTopicsReached(5)); + require((_tokenDetails.irAgents).length <= 5 && (_tokenDetails.tokenAgents).length <= 5, MaxAgentsReached(5)); + require((_tokenDetails.complianceModules).length <= 30, MaxModuleActionsReached(30)); + require((_tokenDetails.complianceModules).length >= (_tokenDetails.complianceSettings).length, + InvalidCompliancePattern()); ITrustedIssuersRegistry tir = ITrustedIssuersRegistry(_deployTIR(_salt, _implementationAuthority)); IClaimTopicsRegistry ctr = IClaimTopicsRegistry(_deployCTR(_salt, _implementationAuthority)); @@ -208,7 +232,7 @@ contract TREXFactory is ITREXFactory, Ownable { * @dev See {ITREXFactory-setImplementationAuthority}. */ function setImplementationAuthority(address implementationAuthority_) public override onlyOwner { - require(implementationAuthority_ != address(0), "invalid argument - zero address"); + require(implementationAuthority_ != address(0), ZeroAddress()); // should not be possible to set an implementation authority that is not complete require( (ITREXImplementationAuthority(implementationAuthority_)).getTokenImplementation() != address(0) @@ -217,7 +241,7 @@ contract TREXFactory is ITREXFactory, Ownable { && (ITREXImplementationAuthority(implementationAuthority_)).getIRSImplementation() != address(0) && (ITREXImplementationAuthority(implementationAuthority_)).getMCImplementation() != address(0) && (ITREXImplementationAuthority(implementationAuthority_)).getTIRImplementation() != address(0), - "invalid Implementation Authority"); + InvalidImplementationAuthority()); _implementationAuthority = implementationAuthority_; emit ImplementationAuthoritySet(implementationAuthority_); } @@ -226,7 +250,7 @@ contract TREXFactory is ITREXFactory, Ownable { * @dev See {ITREXFactory-setIdFactory}. */ function setIdFactory(address idFactory_) public override onlyOwner { - require(idFactory_ != address(0), "invalid argument - zero address"); + require(idFactory_ != address(0), ZeroAddress()); _idFactory = idFactory_; emit IdFactorySet(idFactory_); } diff --git a/contracts/factory/TREXGateway.sol b/contracts/factory/TREXGateway.sol index 847918fb..b8494385 100644 --- a/contracts/factory/TREXGateway.sol +++ b/contracts/factory/TREXGateway.sol @@ -59,15 +59,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./ITREXGateway.sol"; import "../roles/AgentRole.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/RoleErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../roles/IERC173.sol"; -/// A required parameter was set to the Zero address. -error ZeroAddress(); +/// Errors /// The Public Deployment Status is already set properly error PublicDeploymentAlreadyEnabled(); @@ -96,14 +99,11 @@ error PublicCannotDeployOnBehalf(); /// Discount cannot be bigger than 10000 (100%) error DiscountOutOfRange(); -/// Only Owner or Agent can call -error OnlyAdminCall(); - /// Batch Size is too big, could run out of gas error BatchMaxLengthExceeded(uint16 lengthLimit); -contract TREXGateway is ITREXGateway, AgentRole { +contract TREXGateway is ITREXGateway, AgentRole, IERC165 { /// address of the TREX Factory that is managed by the Gateway address private _factory; @@ -136,9 +136,8 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setFactory}. */ function setFactory(address factory) external override onlyOwner { - if(factory == address(0)) { - revert ZeroAddress(); - } + require(factory != address(0), ZeroAddress()); + _factory = factory; emit FactorySet(factory); } @@ -147,12 +146,12 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setPublicDeploymentStatus}. */ function setPublicDeploymentStatus(bool _isEnabled) external override onlyOwner { - if(_isEnabled == _publicDeploymentStatus && _isEnabled == true) { - revert PublicDeploymentAlreadyEnabled(); - } - if(_isEnabled == _publicDeploymentStatus && _isEnabled == false) { + if(_isEnabled == _publicDeploymentStatus) { + if (_isEnabled) + revert PublicDeploymentAlreadyEnabled(); revert PublicDeploymentAlreadyDisabled(); } + _publicDeploymentStatus = _isEnabled; emit PublicDeploymentStatusSet(_isEnabled); } @@ -168,12 +167,12 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-enableDeploymentFee}. */ function enableDeploymentFee(bool _isEnabled) external override onlyOwner { - if(_isEnabled == _deploymentFeeEnabled && _isEnabled == true) { - revert DeploymentFeesAlreadyEnabled(); - } - if(_isEnabled == _deploymentFeeEnabled && _isEnabled == false) { + if(_isEnabled == _deploymentFeeEnabled) { + if (_isEnabled) + revert DeploymentFeesAlreadyEnabled(); revert DeploymentFeesAlreadyDisabled(); } + _deploymentFeeEnabled = _isEnabled; emit DeploymentFeeEnabled(_isEnabled); } @@ -182,9 +181,8 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setDeploymentFee}. */ function setDeploymentFee(uint256 _fee, address _feeToken, address _feeCollector) external override onlyOwner { - if(_feeToken == address(0) || _feeCollector == address(0)) { - revert ZeroAddress(); - } + require(_feeToken != address(0) && _feeCollector != address(0), ZeroAddress()); + _deploymentFee.fee = _fee; _deploymentFee.feeToken = _feeToken; _deploymentFee.feeCollector = _feeCollector; @@ -195,16 +193,12 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchAddDeployer}. */ function batchAddDeployer(address[] calldata deployers) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(deployers.length > 500) { - revert BatchMaxLengthExceeded(500); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(deployers.length <= 500, BatchMaxLengthExceeded(500)); + for (uint256 i = 0; i < deployers.length; i++) { - if(isDeployer(deployers[i])) { - revert DeployerAlreadyExists(deployers[i]); - } + require(!isDeployer(deployers[i]), DeployerAlreadyExists(deployers[i])); + _deployers[deployers[i]] = true; emit DeployerAdded(deployers[i]); } @@ -214,12 +208,9 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-addDeployer}. */ function addDeployer(address deployer) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(isDeployer(deployer)) { - revert DeployerAlreadyExists(deployer); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(!isDeployer(deployer), DeployerAlreadyExists(deployer)); + _deployers[deployer] = true; emit DeployerAdded(deployer); } @@ -228,16 +219,12 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchRemoveDeployer}. */ function batchRemoveDeployer(address[] calldata deployers) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(deployers.length > 500) { - revert BatchMaxLengthExceeded(500); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(deployers.length <= 500, BatchMaxLengthExceeded(500)); + for (uint256 i = 0; i < deployers.length; i++) { - if(!isDeployer(deployers[i])) { - revert DeployerDoesNotExist(deployers[i]); - } + require(isDeployer(deployers[i]), DeployerDoesNotExist(deployers[i])); + delete _deployers[deployers[i]]; emit DeployerRemoved(deployers[i]); } @@ -247,12 +234,9 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-removeDeployer}. */ function removeDeployer(address deployer) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(!isDeployer(deployer)) { - revert DeployerDoesNotExist(deployer); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(isDeployer(deployer), DeployerDoesNotExist(deployer)); + delete _deployers[deployer]; emit DeployerRemoved(deployer); } @@ -261,16 +245,12 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchApplyFeeDiscount}. */ function batchApplyFeeDiscount(address[] calldata deployers, uint16[] calldata discounts) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(deployers.length > 500) { - revert BatchMaxLengthExceeded(500); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(deployers.length <= 500, BatchMaxLengthExceeded(500)); + for (uint256 i = 0; i < deployers.length; i++) { - if(discounts[i] > 10000) { - revert DiscountOutOfRange(); - } + require(discounts[i] <= 10000, DiscountOutOfRange()); + _feeDiscount[deployers[i]] = discounts[i]; emit FeeDiscountApplied(deployers[i], discounts[i]); } @@ -280,12 +260,9 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-applyFeeDiscount}. */ function applyFeeDiscount(address deployer, uint16 discount) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { - revert OnlyAdminCall(); - } - if(discount > 10000) { - revert DiscountOutOfRange(); - } + require(isAgent(msg.sender) || msg.sender == owner(), SenderIsNotAdmin()); + require(discount <= 10000, DiscountOutOfRange()); + _feeDiscount[deployer] = discount; emit FeeDiscountApplied(deployer, discount); } @@ -297,9 +274,8 @@ contract TREXGateway is ITREXGateway, AgentRole { ITREXFactory.TokenDetails[] memory _tokenDetails, ITREXFactory.ClaimDetails[] memory _claimDetails) external override { - if(_tokenDetails.length > 5) { - revert BatchMaxLengthExceeded(5); - } + require(_tokenDetails.length <= 5, BatchMaxLengthExceeded(5)); + for (uint256 i = 0; i < _tokenDetails.length; i++) { deployTREXSuite(_tokenDetails[i], _claimDetails[i]); } @@ -338,14 +314,12 @@ contract TREXGateway is ITREXGateway, AgentRole { */ function deployTREXSuite(ITREXFactory.TokenDetails memory _tokenDetails, ITREXFactory.ClaimDetails memory _claimDetails) public override { - if(_publicDeploymentStatus == false && !isDeployer(msg.sender)) { - revert PublicDeploymentsNotAllowed(); - } - if(_publicDeploymentStatus == true && msg.sender != _tokenDetails.owner && !isDeployer(msg.sender)) { - revert PublicCannotDeployOnBehalf(); - } + require(_publicDeploymentStatus || isDeployer(msg.sender), PublicDeploymentsNotAllowed()); + require(!_publicDeploymentStatus || msg.sender == _tokenDetails.owner || isDeployer(msg.sender), + PublicCannotDeployOnBehalf()); + uint256 feeApplied = 0; - if(_deploymentFeeEnabled == true) { + if(_deploymentFeeEnabled) { if(_deploymentFee.fee > 0 && _feeDiscount[msg.sender] < 10000) { feeApplied = calculateFee(msg.sender); IERC20(_deploymentFee.feeToken).transferFrom( @@ -373,4 +347,14 @@ contract TREXGateway is ITREXGateway, AgentRole { function calculateFee(address deployer) public override view returns(uint256) { return _deploymentFee.fee - ((_feeDiscount[deployer] * _deploymentFee.fee) / 10000); } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(ITREXGateway).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/proxy/AbstractProxy.sol b/contracts/proxy/AbstractProxy.sol index 1844e7b4..fe039482 100644 --- a/contracts/proxy/AbstractProxy.sol +++ b/contracts/proxy/AbstractProxy.sol @@ -60,11 +60,19 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./interface/IProxy.sol"; import "./authority/ITREXImplementationAuthority.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "../errors/CommonErrors.sol"; +import "../errors/InvalidArgumentErrors.sol"; + +/// Errors + +/// @dev Thrown when called by other than the current implementation authority. +error OnlyCurrentImplementationAuthorityCanCall(); + abstract contract AbstractProxy is IProxy, Initializable { @@ -72,8 +80,8 @@ abstract contract AbstractProxy is IProxy, Initializable { * @dev See {IProxy-setImplementationAuthority}. */ function setImplementationAuthority(address _newImplementationAuthority) external override { - require(msg.sender == getImplementationAuthority(), "only current implementationAuthority can call"); - require(_newImplementationAuthority != address(0), "invalid argument - zero address"); + require(msg.sender == getImplementationAuthority(), OnlyCurrentImplementationAuthorityCanCall()); + require(_newImplementationAuthority != address(0), ZeroAddress()); require( (ITREXImplementationAuthority(_newImplementationAuthority)).getTokenImplementation() != address(0) && (ITREXImplementationAuthority(_newImplementationAuthority)).getCTRImplementation() != address(0) @@ -81,7 +89,7 @@ abstract contract AbstractProxy is IProxy, Initializable { && (ITREXImplementationAuthority(_newImplementationAuthority)).getIRSImplementation() != address(0) && (ITREXImplementationAuthority(_newImplementationAuthority)).getMCImplementation() != address(0) && (ITREXImplementationAuthority(_newImplementationAuthority)).getTIRImplementation() != address(0) - , "invalid Implementation Authority"); + , InvalidImplementationAuthority()); _storeImplementationAuthority(_newImplementationAuthority); emit ImplementationAuthoritySet(_newImplementationAuthority); } diff --git a/contracts/proxy/ClaimTopicsRegistryProxy.sol b/contracts/proxy/ClaimTopicsRegistryProxy.sol index 3d40661b..ecccbf65 100644 --- a/contracts/proxy/ClaimTopicsRegistryProxy.sol +++ b/contracts/proxy/ClaimTopicsRegistryProxy.sol @@ -60,14 +60,16 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/CommonErrors.sol"; contract ClaimTopicsRegistryProxy is AbstractProxy { constructor(address implementationAuthority) { - require(implementationAuthority != address(0), "invalid argument - zero address"); + require(implementationAuthority != address(0), ZeroAddress()); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -75,7 +77,7 @@ contract ClaimTopicsRegistryProxy is AbstractProxy { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = logic.delegatecall(abi.encodeWithSignature("init()")); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/IdentityRegistryProxy.sol b/contracts/proxy/IdentityRegistryProxy.sol index 372a9cd6..ff81a49d 100644 --- a/contracts/proxy/IdentityRegistryProxy.sol +++ b/contracts/proxy/IdentityRegistryProxy.sol @@ -60,9 +60,10 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/CommonErrors.sol"; contract IdentityRegistryProxy is AbstractProxy { @@ -77,7 +78,7 @@ contract IdentityRegistryProxy is AbstractProxy { && _trustedIssuersRegistry != address(0) && _claimTopicsRegistry != address(0) && _identityStorage != address(0) - , "invalid argument - zero address"); + , ZeroAddress()); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -90,7 +91,7 @@ contract IdentityRegistryProxy is AbstractProxy { _trustedIssuersRegistry, _claimTopicsRegistry, _identityStorage)); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/IdentityRegistryStorageProxy.sol b/contracts/proxy/IdentityRegistryStorageProxy.sol index 6402ee4c..fcf29b95 100644 --- a/contracts/proxy/IdentityRegistryStorageProxy.sol +++ b/contracts/proxy/IdentityRegistryStorageProxy.sol @@ -60,14 +60,15 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/CommonErrors.sol"; contract IdentityRegistryStorageProxy is AbstractProxy { constructor(address implementationAuthority) { - require(implementationAuthority != address(0), "invalid argument - zero address"); + require(implementationAuthority != address(0), ZeroAddress()); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -75,7 +76,7 @@ contract IdentityRegistryStorageProxy is AbstractProxy { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = logic.delegatecall(abi.encodeWithSignature("init()")); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/ModularComplianceProxy.sol b/contracts/proxy/ModularComplianceProxy.sol index 2c0072a7..b1ec101b 100644 --- a/contracts/proxy/ModularComplianceProxy.sol +++ b/contracts/proxy/ModularComplianceProxy.sol @@ -60,14 +60,17 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/CommonErrors.sol"; +import "../errors/InvalidArgumentErrors.sol"; + contract ModularComplianceProxy is AbstractProxy { constructor(address implementationAuthority) { - require(implementationAuthority != address(0), "invalid argument - zero address"); + require(implementationAuthority != address(0), ZeroAddress()); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -75,7 +78,7 @@ contract ModularComplianceProxy is AbstractProxy { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = logic.delegatecall(abi.encodeWithSignature("init()")); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/TokenProxy.sol b/contracts/proxy/TokenProxy.sol index 0ce67fbd..5494d54e 100644 --- a/contracts/proxy/TokenProxy.sol +++ b/contracts/proxy/TokenProxy.sol @@ -60,9 +60,11 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/CommonErrors.sol"; contract TokenProxy is AbstractProxy { @@ -80,12 +82,12 @@ contract TokenProxy is AbstractProxy { implementationAuthority != address(0) && _identityRegistry != address(0) && _compliance != address(0) - , "invalid argument - zero address"); + , ZeroAddress()); require( keccak256(abi.encode(_name)) != keccak256(abi.encode("")) && keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")) - , "invalid argument - empty string"); - require(0 <= _decimals && _decimals <= 18, "decimals between 0 and 18"); + , EmptyString()); + require(0 <= _decimals && _decimals <= 18, DecimalsOutOfRange(_decimals)); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -103,7 +105,7 @@ contract TokenProxy is AbstractProxy { _onchainID ) ); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/TrustedIssuersRegistryProxy.sol b/contracts/proxy/TrustedIssuersRegistryProxy.sol index 866a8aa4..25b4567f 100644 --- a/contracts/proxy/TrustedIssuersRegistryProxy.sol +++ b/contracts/proxy/TrustedIssuersRegistryProxy.sol @@ -61,14 +61,17 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./AbstractProxy.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/CommonErrors.sol"; + contract TrustedIssuersRegistryProxy is AbstractProxy { constructor(address implementationAuthority) { - require(implementationAuthority != address(0), "invalid argument - zero address"); + require(implementationAuthority != address(0), ZeroAddress()); _storeImplementationAuthority(implementationAuthority); emit ImplementationAuthoritySet(implementationAuthority); @@ -76,7 +79,7 @@ contract TrustedIssuersRegistryProxy is AbstractProxy { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = logic.delegatecall(abi.encodeWithSignature("init()")); - require(success, "Initialization failed."); + require(success, InitializationFailed()); } // solhint-disable-next-line no-complex-fallback diff --git a/contracts/proxy/authority/IAFactory.sol b/contracts/proxy/authority/IAFactory.sol index 8993ab86..9f6af7bd 100644 --- a/contracts/proxy/authority/IAFactory.sol +++ b/contracts/proxy/authority/IAFactory.sol @@ -60,11 +60,11 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./TREXImplementationAuthority.sol"; -contract IAFactory is IIAFactory { +contract IAFactory is IIAFactory, IERC165 { /// variables @@ -74,6 +74,10 @@ contract IAFactory is IIAFactory { /// mapping allowing to know if an IA was deployed by the factory or not mapping(address => bool) private _deployedByFactory; + /// Errors + + error OnlyReferenceIACanDeploy(); + /// functions constructor (address trexFactory) { @@ -84,8 +88,8 @@ contract IAFactory is IIAFactory { * @dev See {IIAFactory-deployIA}. */ function deployIA(address _token) external override returns (address){ - if (ITREXFactory(_trexFactory).getImplementationAuthority() != msg.sender) { - revert("only reference IA can deploy");} + require(ITREXFactory(_trexFactory).getImplementationAuthority() == msg.sender, + OnlyReferenceIACanDeploy()); TREXImplementationAuthority _newIA = new TREXImplementationAuthority(false, ITREXImplementationAuthority(msg.sender).getTREXFactory(), address(this)); _newIA.fetchVersion(ITREXImplementationAuthority(msg.sender).getCurrentVersion()); @@ -102,4 +106,13 @@ contract IAFactory is IIAFactory { function deployedByFactory(address _ia) external view override returns (bool) { return _deployedByFactory[_ia]; } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IIAFactory).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/proxy/authority/IIAFactory.sol b/contracts/proxy/authority/IIAFactory.sol index 40d3ab45..75ca3053 100644 --- a/contracts/proxy/authority/IIAFactory.sol +++ b/contracts/proxy/authority/IIAFactory.sol @@ -60,13 +60,17 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; -interface IIAFactory { +pragma solidity 0.8.27; + + +/// Events - /// events +/// @dev Event emitted when a new TREXImplementationAuthority is deployed. +/// @param _ia Address of implementation authority contract. +event ImplementationAuthorityDeployed(address indexed _ia); - /// event emitted when a new TREXImplementationAuthority is deployed - event ImplementationAuthorityDeployed(address indexed _ia); + +interface IIAFactory { /// functions diff --git a/contracts/proxy/authority/ITREXImplementationAuthority.sol b/contracts/proxy/authority/ITREXImplementationAuthority.sol index 38d967ca..d267ce75 100644 --- a/contracts/proxy/authority/ITREXImplementationAuthority.sol +++ b/contracts/proxy/authority/ITREXImplementationAuthority.sol @@ -60,7 +60,46 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; + +/// events + +/// @dev Event emitted when a new TREX version is added to the contract memory. +/// @param _version Version details. +/// @param _trex Address of contract for this version. +event TREXVersionAdded( + ITREXImplementationAuthority.Version indexed _version, + ITREXImplementationAuthority.TREXContracts indexed _trex); + +/// @dev Event emitted when a new TREX version is fetched from reference contract by auxiliary contract. +/// @param _version Version details. +/// @param _trex Address of contract for this version. +event TREXVersionFetched( + ITREXImplementationAuthority.Version indexed _version, + ITREXImplementationAuthority.TREXContracts indexed _trex); + +/// @dev Event emitted when the current version is updated. +/// @param _version Version details. +event VersionUpdated(ITREXImplementationAuthority.Version indexed _version); + +/// @dev Event emitted by the constructor when the IA is deployed. +/// @param _referenceStatus Main implementation authority or auxiliary contract. +/// @param _trexFactory Address of trexFactory contract. +event ImplementationAuthoritySet(bool _referenceStatus, address _trexFactory); + +/// @dev Event emitted when the TREX factory address is set. +/// @param _trexFactory Address of TRexFactory contract. +event TREXFactorySet(address indexed _trexFactory); + +/// @dev Event emitted when the IA factory address is set. +/// @param _iaFactory Address of implementation authority contract. +event IAFactorySet(address indexed _iaFactory); + +/// @dev Event emitted when a token issuer decides to change current IA for a new one. +/// @param _token Address of the token proxy contract. +/// @param _newImplementationAuthority Address of the new implementation authorirty contract. +event ImplementationAuthorityChanged(address indexed _token, address indexed _newImplementationAuthority); + interface ITREXImplementationAuthority { @@ -90,29 +129,6 @@ interface ITREXImplementationAuthority { uint8 patch; } - /// events - - /// event emitted when a new TREX version is added to the contract memory - event TREXVersionAdded(Version indexed version, TREXContracts indexed trex); - - /// event emitted when a new TREX version is fetched from reference contract by auxiliary contract - event TREXVersionFetched(Version indexed version, TREXContracts indexed trex); - - /// event emitted when the current version is updated - event VersionUpdated(Version indexed version); - - /// event emitted by the constructor when the IA is deployed - event ImplementationAuthoritySet(bool referenceStatus, address trexFactory); - - /// event emitted when the TREX factory address is set - event TREXFactorySet(address indexed trexFactory); - - /// event emitted when the IA factory address is set - event IAFactorySet(address indexed iaFactory); - - /// event emitted when a token issuer decides to change current IA for a new one - event ImplementationAuthorityChanged(address indexed _token, address indexed _newImplementationAuthority); - /// functions /** diff --git a/contracts/proxy/authority/TREXImplementationAuthority.sol b/contracts/proxy/authority/TREXImplementationAuthority.sol index 5aa3ce7e..5a5686ce 100644 --- a/contracts/proxy/authority/TREXImplementationAuthority.sol +++ b/contracts/proxy/authority/TREXImplementationAuthority.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/access/Ownable.sol"; import "./ITREXImplementationAuthority.sol"; @@ -68,8 +68,42 @@ import "../../token/IToken.sol"; import "../interface/IProxy.sol"; import "../../factory/ITREXFactory.sol"; import "./IIAFactory.sol"; +import "../../errors/CommonErrors.sol"; +import "../../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; -contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { +/// Errors + +/// @dev Thrown when caller is not the owner of all impacted contracts. +error CallerNotOwnerOfAllImpactedContracts(); + +/// @dev Thrown when called on reference contract. +error CannotCallOnReferenceContract(); + +/// @dev Thrown when new implementation authority is not a reference contract. +error NewIAIsNotAReferenceContract(); + +/// @dev Thrown when version doesn't exist. +error NonExistingVersion(); + +/// @dev Thrown when called by other than reference contract +error OnlyReferenceContractCanCall(); + +/// @dev Thrown when version is already fetched. +error VersionAlreadyFetched(); + +/// @dev Thrown when version already exists. +error VersionAlreadyExists(); + +/// @dev Thrown when version is already in use. +error VersionAlreadyInUse(); + +/// @dev Thrown when version of new implementation authority is not the same as the current implementation authority version. +error VersionOfNewIAMustBeTheSameAsCurrentIA(); + + +contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable, IERC165 { /// variables @@ -116,7 +150,7 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { require( isReferenceContract() && ITREXFactory(trexFactory).getImplementationAuthority() == address(this) - , "only reference contract can call"); + , OnlyReferenceContractCanCall()); _trexFactory = trexFactory; emit TREXFactorySet(trexFactory); } @@ -128,7 +162,7 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { require( isReferenceContract() && ITREXFactory(_trexFactory).getImplementationAuthority() == address(this) - , "only reference contract can call"); + , OnlyReferenceContractCanCall()); _iaFactory = iaFactory; emit IAFactorySet(iaFactory); } @@ -145,10 +179,9 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { * @dev See {ITREXImplementationAuthority-fetchVersionList}. */ function fetchVersion(Version calldata _version) external override { - require(!isReferenceContract(), "cannot call on reference contract"); - if (_contracts[_versionToBytes(_version)].tokenImplementation != address(0)) { - revert("version fetched already"); - } + require(!isReferenceContract(), CannotCallOnReferenceContract()); + require(_contracts[_versionToBytes(_version)].tokenImplementation == address(0), VersionAlreadyFetched()); + _contracts[_versionToBytes(_version)] = ITREXImplementationAuthority(getReferenceContract()).getContracts(_version); emit TREXVersionFetched(_version, _contracts[_versionToBytes(_version)]); @@ -159,46 +192,41 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { */ // solhint-disable-next-line code-complexity, function-max-lines function changeImplementationAuthority(address _token, address _newImplementationAuthority) external override { - require(_token != address(0), "invalid argument - zero address"); - if(_newImplementationAuthority == address(0) && !isReferenceContract()){ - revert("only reference contract can deploy new IAs");} + require(_token != address(0), ZeroAddress()); + require(_newImplementationAuthority != address(0) || isReferenceContract(), OnlyReferenceContractCanCall()); address _ir = address(IToken(_token).identityRegistry()); address _mc = address(IToken(_token).compliance()); - address _irs = address(IIdentityRegistry(_ir).identityStorage()); - address _ctr = address(IIdentityRegistry(_ir).topicsRegistry()); - address _tir = address(IIdentityRegistry(_ir).issuersRegistry()); + address _irs = address(IERC3643IdentityRegistry(_ir).identityStorage()); + address _ctr = address(IERC3643IdentityRegistry(_ir).topicsRegistry()); + address _tir = address(IERC3643IdentityRegistry(_ir).issuersRegistry()); // calling this function requires ownership of ALL contracts of the T-REX suite - if( - Ownable(_token).owner() != msg.sender - || Ownable(_ir).owner() != msg.sender - || Ownable(_mc).owner() != msg.sender - || Ownable(_irs).owner() != msg.sender - || Ownable(_ctr).owner() != msg.sender - || Ownable(_tir).owner() != msg.sender) { - revert("caller NOT owner of all contracts impacted"); - } + require( + Ownable(_token).owner() == msg.sender + && Ownable(_ir).owner() == msg.sender + && Ownable(_mc).owner() == msg.sender + && Ownable(_irs).owner() == msg.sender + && Ownable(_ctr).owner() == msg.sender + && Ownable(_tir).owner() == msg.sender, + CallerNotOwnerOfAllImpactedContracts()); if(_newImplementationAuthority == address(0)) { _newImplementationAuthority = IIAFactory(_iaFactory).deployIA(_token); } else { - if( - _versionToBytes(ITREXImplementationAuthority(_newImplementationAuthority).getCurrentVersion()) != - _versionToBytes(_currentVersion)) { - revert("version of new IA has to be the same as current IA"); - } - if( - ITREXImplementationAuthority(_newImplementationAuthority).isReferenceContract() && - _newImplementationAuthority != getReferenceContract()) { - revert("new IA is NOT reference contract"); - } - if( - !IIAFactory(_iaFactory).deployedByFactory(_newImplementationAuthority) && - _newImplementationAuthority != getReferenceContract()) { - revert("invalid IA"); - } + require( + _versionToBytes(ITREXImplementationAuthority(_newImplementationAuthority).getCurrentVersion()) == + _versionToBytes(_currentVersion), + VersionOfNewIAMustBeTheSameAsCurrentIA()); + require( + !ITREXImplementationAuthority(_newImplementationAuthority).isReferenceContract() || + _newImplementationAuthority == getReferenceContract(), + NewIAIsNotAReferenceContract()); + require( + IIAFactory(_iaFactory).deployedByFactory(_newImplementationAuthority) || + _newImplementationAuthority == getReferenceContract(), + InvalidImplementationAuthority()); } IProxy(_token).setImplementationAuthority(_newImplementationAuthority); @@ -280,10 +308,9 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { * @dev See {ITREXImplementationAuthority-addTREXVersion}. */ function addTREXVersion(Version calldata _version, TREXContracts calldata _trex) public override onlyOwner { - require(isReferenceContract(), "ONLY reference contract can add versions"); - if (_contracts[_versionToBytes(_version)].tokenImplementation != address(0)) { - revert("version already exists"); - } + require(isReferenceContract(), OnlyReferenceContractCanCall()); + require(_contracts[_versionToBytes(_version)].tokenImplementation == address(0), VersionAlreadyExists()); + require( _trex.ctrImplementation != address(0) && _trex.irImplementation != address(0) @@ -291,7 +318,8 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { && _trex.mcImplementation != address(0) && _trex.tirImplementation != address(0) && _trex.tokenImplementation != address(0) - , "invalid argument - zero address"); + , ZeroAddress()); + _contracts[_versionToBytes(_version)] = _trex; emit TREXVersionAdded(_version, _trex); } @@ -300,12 +328,9 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { * @dev See {ITREXImplementationAuthority-useTREXVersion}. */ function useTREXVersion(Version calldata _version) public override onlyOwner { - if (_versionToBytes(_version) == _versionToBytes(_currentVersion)) { - revert("version already in use"); - } - if (_contracts[_versionToBytes(_version)].tokenImplementation == address(0)) { - revert("invalid argument - non existing version"); - } + require(_versionToBytes(_version) != _versionToBytes(_currentVersion), VersionAlreadyInUse()); + require(_contracts[_versionToBytes(_version)].tokenImplementation != address(0), NonExistingVersion()); + _currentVersion = _version; emit VersionUpdated(_version); } @@ -324,6 +349,16 @@ contract TREXImplementationAuthority is ITREXImplementationAuthority, Ownable { return ITREXFactory(_trexFactory).getImplementationAuthority(); } + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(ITREXImplementationAuthority).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + /** * @dev casting function Version => bytes to allow compare values easier */ diff --git a/contracts/proxy/interface/IProxy.sol b/contracts/proxy/interface/IProxy.sol index f7c64ba7..8f764639 100644 --- a/contracts/proxy/interface/IProxy.sol +++ b/contracts/proxy/interface/IProxy.sol @@ -60,13 +60,12 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -interface IProxy { +import "../../events/CommonEvents.sol"; - /// events - event ImplementationAuthoritySet(address indexed _implementationAuthority); +interface IProxy { /// functions diff --git a/contracts/registry/implementation/ClaimTopicsRegistry.sol b/contracts/registry/implementation/ClaimTopicsRegistry.sol index 4a36931a..b2099942 100644 --- a/contracts/registry/implementation/ClaimTopicsRegistry.sol +++ b/contracts/registry/implementation/ClaimTopicsRegistry.sol @@ -60,13 +60,25 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "../storage/CTRStorage.sol"; import "../interface/IClaimTopicsRegistry.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; -contract ClaimTopicsRegistry is IClaimTopicsRegistry, OwnableUpgradeable, CTRStorage { +/// Errors + +/// @dev Thrown when maximum topic number is reached. +/// @param _max maximum numlber of topics. +error MaxTopicsReached(uint256 _max); + +/// @dev Thrown whern claim topic already exists. +error ClaimTopicAlreadyExists(); + + +contract ClaimTopicsRegistry is IClaimTopicsRegistry, OwnableOnceNext2StepUpgradeable, CTRStorage, IERC165 { function init() external initializer { __Ownable_init(); @@ -77,9 +89,9 @@ contract ClaimTopicsRegistry is IClaimTopicsRegistry, OwnableUpgradeable, CTRSto */ function addClaimTopic(uint256 _claimTopic) external override onlyOwner { uint256 length = _claimTopics.length; - require(length < 15, "cannot require more than 15 topics"); + require(length < 15, MaxTopicsReached(15)); for (uint256 i = 0; i < length; i++) { - require(_claimTopics[i] != _claimTopic, "claimTopic already exists"); + require(_claimTopics[i] != _claimTopic, ClaimTopicAlreadyExists()); } _claimTopics.push(_claimTopic); emit ClaimTopicAdded(_claimTopic); @@ -106,4 +118,14 @@ contract ClaimTopicsRegistry is IClaimTopicsRegistry, OwnableUpgradeable, CTRSto function getClaimTopics() external view override returns (uint256[] memory) { return _claimTopics; } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IERC3643ClaimTopicsRegistry).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/registry/implementation/IdentityRegistry.sol b/contracts/registry/implementation/IdentityRegistry.sol index 877727b0..e8a39871 100644 --- a/contracts/registry/implementation/IdentityRegistry.sol +++ b/contracts/registry/implementation/IdentityRegistry.sol @@ -60,7 +60,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; @@ -71,9 +71,17 @@ import "../interface/IIdentityRegistry.sol"; import "../../roles/AgentRoleUpgradeable.sol"; import "../interface/IIdentityRegistryStorage.sol"; import "../storage/IRStorage.sol"; +import "../../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; +/// error triggered when eligibility checks are disabled and the disable function is called +error EligibilityChecksDisabledAlready(); -contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage { +/// error triggered when eligibility checks are enabled and the enable function is called +error EligibilityChecksEnabledAlready(); + +contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage, IERC165 { /** * @dev the constructor initiates the Identity Registry smart contract @@ -93,13 +101,15 @@ contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage _trustedIssuersRegistry != address(0) && _claimTopicsRegistry != address(0) && _identityStorage != address(0) - , "invalid argument - zero address"); + , ZeroAddress()); _tokenTopicsRegistry = IClaimTopicsRegistry(_claimTopicsRegistry); _tokenIssuersRegistry = ITrustedIssuersRegistry(_trustedIssuersRegistry); _tokenIdentityStorage = IIdentityRegistryStorage(_identityStorage); + _checksDisabled = false; emit ClaimTopicsRegistrySet(_claimTopicsRegistry); emit TrustedIssuersRegistrySet(_trustedIssuersRegistry); emit IdentityStorageSet(_identityStorage); + emit EligibilityChecksEnabled(); __Ownable_init(); } @@ -166,11 +176,30 @@ contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage emit TrustedIssuersRegistrySet(_trustedIssuersRegistry); } + /** + * @dev See {IIdentityRegistry-disableEligibilityChecks}. + */ + function disableEligibilityChecks() external override onlyOwner { + require(!_checksDisabled, EligibilityChecksDisabledAlready()); + _checksDisabled = true; + emit EligibilityChecksDisabled(); + } + + /** + * @dev See {IIdentityRegistry-enableEligibilityChecks}. + */ + function enableEligibilityChecks() external override onlyOwner { + require(_checksDisabled, EligibilityChecksEnabledAlready()); + _checksDisabled = false; + emit EligibilityChecksEnabled(); + } + /** * @dev See {IIdentityRegistry-isVerified}. */ // solhint-disable-next-line code-complexity function isVerified(address _userAddress) external view override returns (bool) { + if(_checksDisabled) {return true;} if (address(identity(_userAddress)) == address(0)) {return false;} uint256[] memory requiredClaimTopics = _tokenTopicsRegistry.getClaimTopics(); if (requiredClaimTopics.length == 0) { @@ -232,21 +261,21 @@ contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage /** * @dev See {IIdentityRegistry-issuersRegistry}. */ - function issuersRegistry() external view override returns (ITrustedIssuersRegistry) { + function issuersRegistry() external view override returns (IERC3643TrustedIssuersRegistry) { return _tokenIssuersRegistry; } /** * @dev See {IIdentityRegistry-topicsRegistry}. */ - function topicsRegistry() external view override returns (IClaimTopicsRegistry) { + function topicsRegistry() external view override returns (IERC3643ClaimTopicsRegistry) { return _tokenTopicsRegistry; } /** * @dev See {IIdentityRegistry-identityStorage}. */ - function identityStorage() external view override returns (IIdentityRegistryStorage) { + function identityStorage() external view override returns (IERC3643IdentityRegistryStorage) { return _tokenIdentityStorage; } @@ -278,4 +307,15 @@ contract IdentityRegistry is IIdentityRegistry, AgentRoleUpgradeable, IRStorage function identity(address _userAddress) public view override returns (IIdentity) { return _tokenIdentityStorage.storedIdentity(_userAddress); } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IIdentityRegistry).interfaceId || + interfaceId == type(IERC3643IdentityRegistry).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/registry/implementation/IdentityRegistryStorage.sol b/contracts/registry/implementation/IdentityRegistryStorage.sol index 3e452788..333b9651 100644 --- a/contracts/registry/implementation/IdentityRegistryStorage.sol +++ b/contracts/registry/implementation/IdentityRegistryStorage.sol @@ -60,15 +60,34 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "../../roles/AgentRoleUpgradeable.sol"; import "../interface/IIdentityRegistryStorage.sol"; import "../storage/IRSStorage.sol"; +import "../../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; -contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeable, IRSStorage { +/// Errors + +/// @dev Thrown when address is already stored +error AddressAlreadyStored(); + +/// @dev Thrown when address is not yet stored. +error AddressNotYetStored(); + +/// @dev Thrown when identity registry is not stored. +error IdentityRegistryNotStored(); + +/// @dev Thrown when maximum numbe of identity registry by identity registry storage is reached. +/// @param _max miximum number of IR by IRS. +error MaxIRByIRSReached(uint256 _max); + + +contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeable, IRSStorage, IERC165 { function init() external initializer { __Ownable_init(); @@ -85,8 +104,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab require( _userAddress != address(0) && address(_identity) != address(0) - , "invalid argument - zero address"); - require(address(_identities[_userAddress].identityContract) == address(0), "address stored already"); + , ZeroAddress()); + require(address(_identities[_userAddress].identityContract) == address(0), AddressAlreadyStored()); _identities[_userAddress].identityContract = _identity; _identities[_userAddress].investorCountry = _country; emit IdentityStored(_userAddress, _identity); @@ -99,8 +118,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab require( _userAddress != address(0) && address(_identity) != address(0) - , "invalid argument - zero address"); - require(address(_identities[_userAddress].identityContract) != address(0), "address not stored yet"); + , ZeroAddress()); + require(address(_identities[_userAddress].identityContract) != address(0), AddressNotYetStored()); IIdentity oldIdentity = _identities[_userAddress].identityContract; _identities[_userAddress].identityContract = _identity; emit IdentityModified(oldIdentity, _identity); @@ -110,8 +129,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab * @dev See {IIdentityRegistryStorage-modifyStoredInvestorCountry}. */ function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external override onlyAgent { - require(_userAddress != address(0), "invalid argument - zero address"); - require(address(_identities[_userAddress].identityContract) != address(0), "address not stored yet"); + require(_userAddress != address(0), ZeroAddress()); + require(address(_identities[_userAddress].identityContract) != address(0), AddressNotYetStored()); _identities[_userAddress].investorCountry = _country; emit CountryModified(_userAddress, _country); } @@ -120,8 +139,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab * @dev See {IIdentityRegistryStorage-removeIdentityFromStorage}. */ function removeIdentityFromStorage(address _userAddress) external override onlyAgent { - require(_userAddress != address(0), "invalid argument - zero address"); - require(address(_identities[_userAddress].identityContract) != address(0), "address not stored yet"); + require(_userAddress != address(0), ZeroAddress()); + require(address(_identities[_userAddress].identityContract) != address(0), AddressNotYetStored()); IIdentity oldIdentity = _identities[_userAddress].identityContract; delete _identities[_userAddress]; emit IdentityUnstored(_userAddress, oldIdentity); @@ -131,8 +150,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab * @dev See {IIdentityRegistryStorage-bindIdentityRegistry}. */ function bindIdentityRegistry(address _identityRegistry) external override { - require(_identityRegistry != address(0), "invalid argument - zero address"); - require(_identityRegistries.length < 300, "cannot bind more than 300 IR to 1 IRS"); + require(_identityRegistry != address(0), ZeroAddress()); + require(_identityRegistries.length < 300, MaxIRByIRSReached(300)); addAgent(_identityRegistry); _identityRegistries.push(_identityRegistry); emit IdentityRegistryBound(_identityRegistry); @@ -142,8 +161,8 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab * @dev See {IIdentityRegistryStorage-unbindIdentityRegistry}. */ function unbindIdentityRegistry(address _identityRegistry) external override { - require(_identityRegistry != address(0), "invalid argument - zero address"); - require(_identityRegistries.length > 0, "identity registry is not stored"); + require(_identityRegistry != address(0), ZeroAddress()); + require(_identityRegistries.length > 0, IdentityRegistryNotStored()); uint256 length = _identityRegistries.length; for (uint256 i = 0; i < length; i++) { if (_identityRegistries[i] == _identityRegistry) { @@ -176,4 +195,14 @@ contract IdentityRegistryStorage is IIdentityRegistryStorage, AgentRoleUpgradeab function storedInvestorCountry(address _userAddress) external view override returns (uint16) { return _identities[_userAddress].investorCountry; } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IERC3643IdentityRegistryStorage).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/registry/implementation/TrustedIssuersRegistry.sol b/contracts/registry/implementation/TrustedIssuersRegistry.sol index 46051773..59f70f3d 100644 --- a/contracts/registry/implementation/TrustedIssuersRegistry.sol +++ b/contracts/registry/implementation/TrustedIssuersRegistry.sol @@ -61,15 +61,45 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "../interface/ITrustedIssuersRegistry.sol"; import "../storage/TIRStorage.sol"; +import "../../errors/InvalidArgumentErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "../../roles/IERC173.sol"; +/// Errors -contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, TIRStorage { +/// @dev Thrown when claim topics is empty. +error ClaimTopicsCannotBeEmpty(); + +/// @dev Thrown when maximum number of claim topics is reached. +/// @param _max maximum number of claim topics. +error MaxClaimTopcisReached(uint256 _max); + +/// @dev Thrown when the maximum number of trusted issuers is reached. +/// @param _max maximum number of trusted issuers. +error MaxTrustedIssuersReached(uint256 _max); + +/// @dev Thrown when called by other than a trusted issuer. +error NotATrustedIssuer(); + +/// @dev Thrown when trusted claim topics is empty. +error TrustedClaimTopicsCannotBeEmpty(); + +/// @dev Thrown when trusted issuer already exists. +error TrustedIssuerAlreadyExists(); + +/// @dev Thrown when trusted issuer doesn"t exist. +error TrustedIssuerDoesNotExist(); + + +contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableOnceNext2StepUpgradeable, TIRStorage, IERC165 { + + /// Functions function init() external initializer { __Ownable_init(); @@ -79,11 +109,11 @@ contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, * @dev See {ITrustedIssuersRegistry-addTrustedIssuer}. */ function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external override onlyOwner { - require(address(_trustedIssuer) != address(0), "invalid argument - zero address"); - require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length == 0, "trusted Issuer already exists"); - require(_claimTopics.length > 0, "trusted claim topics cannot be empty"); - require(_claimTopics.length <= 15, "cannot have more than 15 claim topics"); - require(_trustedIssuers.length < 50, "cannot have more than 50 trusted issuers"); + require(address(_trustedIssuer) != address(0), ZeroAddress()); + require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length == 0, TrustedIssuerAlreadyExists()); + require(_claimTopics.length > 0, TrustedClaimTopicsCannotBeEmpty()); + require(_claimTopics.length <= 15, MaxClaimTopcisReached(15)); + require(_trustedIssuers.length < 50, MaxTrustedIssuersReached(50)); _trustedIssuers.push(_trustedIssuer); _trustedIssuerClaimTopics[address(_trustedIssuer)] = _claimTopics; for (uint256 i = 0; i < _claimTopics.length; i++) { @@ -96,8 +126,8 @@ contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, * @dev See {ITrustedIssuersRegistry-removeTrustedIssuer}. */ function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external override onlyOwner { - require(address(_trustedIssuer) != address(0), "invalid argument - zero address"); - require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, "NOT a trusted issuer"); + require(address(_trustedIssuer) != address(0), ZeroAddress()); + require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, NotATrustedIssuer()); uint256 length = _trustedIssuers.length; for (uint256 i = 0; i < length; i++) { if (_trustedIssuers[i] == _trustedIssuer) { @@ -129,10 +159,10 @@ contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, * @dev See {ITrustedIssuersRegistry-updateIssuerClaimTopics}. */ function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external override onlyOwner { - require(address(_trustedIssuer) != address(0), "invalid argument - zero address"); - require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, "NOT a trusted issuer"); - require(_claimTopics.length <= 15, "cannot have more than 15 claim topics"); - require(_claimTopics.length > 0, "claim topics cannot be empty"); + require(address(_trustedIssuer) != address(0), ZeroAddress()); + require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, NotATrustedIssuer()); + require(_claimTopics.length <= 15, MaxClaimTopcisReached(15)); + require(_claimTopics.length > 0, ClaimTopicsCannotBeEmpty()); for (uint256 i = 0; i < _trustedIssuerClaimTopics[address(_trustedIssuer)].length; i++) { uint256 claimTopic = _trustedIssuerClaimTopics[address(_trustedIssuer)][i]; @@ -181,7 +211,7 @@ contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, * @dev See {ITrustedIssuersRegistry-getTrustedIssuerClaimTopics}. */ function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view override returns (uint256[] memory) { - require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, "trusted Issuer doesn\'t exist"); + require(_trustedIssuerClaimTopics[address(_trustedIssuer)].length != 0, TrustedIssuerDoesNotExist()); return _trustedIssuerClaimTopics[address(_trustedIssuer)]; } @@ -198,4 +228,14 @@ contract TrustedIssuersRegistry is ITrustedIssuersRegistry, OwnableUpgradeable, } return false; } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IERC3643TrustedIssuersRegistry).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId; + } } diff --git a/contracts/registry/interface/IClaimTopicsRegistry.sol b/contracts/registry/interface/IClaimTopicsRegistry.sol index 6268cf61..e7ae6759 100644 --- a/contracts/registry/interface/IClaimTopicsRegistry.sol +++ b/contracts/registry/interface/IClaimTopicsRegistry.sol @@ -60,43 +60,13 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -interface IClaimTopicsRegistry { - /** - * this event is emitted when a claim topic has been added to the ClaimTopicsRegistry - * the event is emitted by the 'addClaimTopic' function - * `claimTopic` is the required claim added to the Claim Topics Registry - */ - event ClaimTopicAdded(uint256 indexed claimTopic); +import "../../ERC-3643/IERC3643ClaimTopicsRegistry.sol"; - /** - * this event is emitted when a claim topic has been removed from the ClaimTopicsRegistry - * the event is emitted by the 'removeClaimTopic' function - * `claimTopic` is the required claim removed from the Claim Topics Registry - */ - event ClaimTopicRemoved(uint256 indexed claimTopic); +// solhint-disable-next-line no-empty-blocks +interface IClaimTopicsRegistry is IERC3643ClaimTopicsRegistry { - /** - * @dev Add a trusted claim topic (For example: KYC=1, AML=2). - * Only owner can call. - * emits `ClaimTopicAdded` event - * cannot add more than 15 topics for 1 token as adding more could create gas issues - * @param _claimTopic The claim topic index - */ - function addClaimTopic(uint256 _claimTopic) external; +// functions that are not part of the original standard can be added here in future versions - /** - * @dev Remove a trusted claim topic (For example: KYC=1, AML=2). - * Only owner can call. - * emits `ClaimTopicRemoved` event - * @param _claimTopic The claim topic index - */ - function removeClaimTopic(uint256 _claimTopic) external; - - /** - * @dev Get the trusted claim topics for the security token - * @return Array of trusted claim topics - */ - function getClaimTopics() external view returns (uint256[] memory); } diff --git a/contracts/registry/interface/IIdentityRegistry.sol b/contracts/registry/interface/IIdentityRegistry.sol index 44fec313..8f0d214e 100644 --- a/contracts/registry/interface/IIdentityRegistry.sol +++ b/contracts/registry/interface/IIdentityRegistry.sol @@ -60,196 +60,51 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -import "./ITrustedIssuersRegistry.sol"; -import "./IClaimTopicsRegistry.sol"; -import "./IIdentityRegistryStorage.sol"; +import "../../ERC-3643/IERC3643IdentityRegistry.sol"; -import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; -import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; +/// Events -interface IIdentityRegistry { - /** - * this event is emitted when the ClaimTopicsRegistry has been set for the IdentityRegistry - * the event is emitted by the IdentityRegistry constructor - * `claimTopicsRegistry` is the address of the Claim Topics Registry contract - */ - event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry); - - /** - * this event is emitted when the IdentityRegistryStorage has been set for the IdentityRegistry - * the event is emitted by the IdentityRegistry constructor - * `identityStorage` is the address of the Identity Registry Storage contract - */ - event IdentityStorageSet(address indexed identityStorage); - - /** - * this event is emitted when the TrustedIssuersRegistry has been set for the IdentityRegistry - * the event is emitted by the IdentityRegistry constructor - * `trustedIssuersRegistry` is the address of the Trusted Issuers Registry contract - */ - event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry); - - /** - * this event is emitted when an Identity is registered into the Identity Registry. - * the event is emitted by the 'registerIdentity' function - * `investorAddress` is the address of the investor's wallet - * `identity` is the address of the Identity smart contract (onchainID) - */ - event IdentityRegistered(address indexed investorAddress, IIdentity indexed identity); - - /** - * this event is emitted when an Identity is removed from the Identity Registry. - * the event is emitted by the 'deleteIdentity' function - * `investorAddress` is the address of the investor's wallet - * `identity` is the address of the Identity smart contract (onchainID) - */ - event IdentityRemoved(address indexed investorAddress, IIdentity indexed identity); - - /** - * this event is emitted when an Identity has been updated - * the event is emitted by the 'updateIdentity' function - * `oldIdentity` is the old Identity contract's address to update - * `newIdentity` is the new Identity contract's - */ - event IdentityUpdated(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); - - /** - * this event is emitted when an Identity's country has been updated - * the event is emitted by the 'updateCountry' function - * `investorAddress` is the address on which the country has been updated - * `country` is the numeric code (ISO 3166-1) of the new country - */ - event CountryUpdated(address indexed investorAddress, uint16 indexed country); - - /** - * @dev Register an identity contract corresponding to a user address. - * Requires that the user doesn't have an identity contract already registered. - * This function can only be called by a wallet set as agent of the smart contract - * @param _userAddress The address of the user - * @param _identity The address of the user's identity contract - * @param _country The country of the investor - * emits `IdentityRegistered` event - */ - function registerIdentity( - address _userAddress, - IIdentity _identity, - uint16 _country - ) external; - - /** - * @dev Removes an user from the identity registry. - * Requires that the user have an identity contract already deployed that will be deleted. - * This function can only be called by a wallet set as agent of the smart contract - * @param _userAddress The address of the user to be removed - * emits `IdentityRemoved` event - */ - function deleteIdentity(address _userAddress) external; - - /** - * @dev Replace the actual identityRegistryStorage contract with a new one. - * This function can only be called by the wallet set as owner of the smart contract - * @param _identityRegistryStorage The address of the new Identity Registry Storage - * emits `IdentityStorageSet` event - */ - function setIdentityRegistryStorage(address _identityRegistryStorage) external; - - /** - * @dev Replace the actual claimTopicsRegistry contract with a new one. - * This function can only be called by the wallet set as owner of the smart contract - * @param _claimTopicsRegistry The address of the new claim Topics Registry - * emits `ClaimTopicsRegistrySet` event - */ - function setClaimTopicsRegistry(address _claimTopicsRegistry) external; - - /** - * @dev Replace the actual trustedIssuersRegistry contract with a new one. - * This function can only be called by the wallet set as owner of the smart contract - * @param _trustedIssuersRegistry The address of the new Trusted Issuers Registry - * emits `TrustedIssuersRegistrySet` event - */ - function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external; - - /** - * @dev Updates the country corresponding to a user address. - * Requires that the user should have an identity contract already deployed that will be replaced. - * This function can only be called by a wallet set as agent of the smart contract - * @param _userAddress The address of the user - * @param _country The new country of the user - * emits `CountryUpdated` event - */ - function updateCountry(address _userAddress, uint16 _country) external; - - /** - * @dev Updates an identity contract corresponding to a user address. - * Requires that the user address should be the owner of the identity contract. - * Requires that the user should have an identity contract already deployed that will be replaced. - * This function can only be called by a wallet set as agent of the smart contract - * @param _userAddress The address of the user - * @param _identity The address of the user's new identity contract - * emits `IdentityUpdated` event - */ - function updateIdentity(address _userAddress, IIdentity _identity) external; - - /** - * @dev function allowing to register identities in batch - * This function can only be called by a wallet set as agent of the smart contract - * Requires that none of the users has an identity contract already registered. - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses of the users - * @param _identities The addresses of the corresponding identity contracts - * @param _countries The countries of the corresponding investors - * emits _userAddresses.length `IdentityRegistered` events - */ - function batchRegisterIdentity( - address[] calldata _userAddresses, - IIdentity[] calldata _identities, - uint16[] calldata _countries - ) external; - - /** - * @dev This functions checks whether a wallet has its Identity registered or not - * in the Identity Registry. - * @param _userAddress The address of the user to be checked. - * @return 'True' if the address is contained in the Identity Registry, 'false' if not. - */ - function contains(address _userAddress) external view returns (bool); - - /** - * @dev This functions checks whether an identity contract - * corresponding to the provided user address has the required claims or not based - * on the data fetched from trusted issuers registry and from the claim topics registry - * @param _userAddress The address of the user to be verified. - * @return 'True' if the address is verified, 'false' if not. - */ - function isVerified(address _userAddress) external view returns (bool); +/// @dev This event is emitted when Eligibility checks are disabled. +event EligibilityChecksDisabled(); - /** - * @dev Returns the onchainID of an investor. - * @param _userAddress The wallet of the investor - */ - function identity(address _userAddress) external view returns (IIdentity); +/// @dev This event is emitted when Eligibility checks are enabled. +event EligibilityChecksEnabled(); - /** - * @dev Returns the country code of an investor. - * @param _userAddress The wallet of the investor - */ - function investorCountry(address _userAddress) external view returns (uint16); - /** - * @dev Returns the IdentityRegistryStorage linked to the current IdentityRegistry. - */ - function identityStorage() external view returns (IIdentityRegistryStorage); +interface IIdentityRegistry is IERC3643IdentityRegistry { /** - * @dev Returns the TrustedIssuersRegistry linked to the current IdentityRegistry. + * @dev Disables the eligibility checks for token transfers and other operations. + * + * This function allows the token owner to disable the eligibility checks, effectively bypassing the + * complex verification process normally required for token operations. Once the eligibility checks + * are disabled, all users will be considered verified by the `isVerified` function, allowing them + * to interact with the token without requiring specific claims or issuer validation. + * + * Requirements: + * - The caller must be the owner of the contract. + * - The eligibility checks must not already be disabled; otherwise, the function will revert with an + * `EligibilityChecksDisabledAlready` error. + * + * Emits an `EligibilityChecksDisabled` event upon successful execution. */ - function issuersRegistry() external view returns (ITrustedIssuersRegistry); + function disableEligibilityChecks() external; /** - * @dev Returns the ClaimTopicsRegistry linked to the current IdentityRegistry. + * @dev Enables the eligibility checks for token transfers and other operations. + * + * This function allows the token owner to re-enable the eligibility checks after they have been + * disabled. Once re-enabled, the `isVerified` function will resume performing the full verification + * process, checking for the required claims and validating them through trusted issuers. + * + * Requirements: + * - The caller must be the owner of the contract. + * - The eligibility checks must currently be disabled; otherwise, the function will revert with an + * `EligibilityChecksEnabledAlready` error. + * + * Emits an `EligibilityChecksEnabled` event upon successful execution. */ - function topicsRegistry() external view returns (IClaimTopicsRegistry); + function enableEligibilityChecks() external; } diff --git a/contracts/registry/interface/IIdentityRegistryStorage.sol b/contracts/registry/interface/IIdentityRegistryStorage.sol index 26e2ff91..18cce88c 100644 --- a/contracts/registry/interface/IIdentityRegistryStorage.sol +++ b/contracts/registry/interface/IIdentityRegistryStorage.sol @@ -60,138 +60,13 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; -import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; +import "../../ERC-3643/IERC3643IdentityRegistryStorage.sol"; -interface IIdentityRegistryStorage { +// solhint-disable-next-line no-empty-blocks +interface IIdentityRegistryStorage is IERC3643IdentityRegistryStorage { - /// events +// functions that are not part of the original standard can be added here in future versions - /** - * this event is emitted when an Identity is registered into the storage contract. - * the event is emitted by the 'registerIdentity' function - * `investorAddress` is the address of the investor's wallet - * `identity` is the address of the Identity smart contract (onchainID) - */ - event IdentityStored(address indexed investorAddress, IIdentity indexed identity); - - /** - * this event is emitted when an Identity is removed from the storage contract. - * the event is emitted by the 'deleteIdentity' function - * `investorAddress` is the address of the investor's wallet - * `identity` is the address of the Identity smart contract (onchainID) - */ - event IdentityUnstored(address indexed investorAddress, IIdentity indexed identity); - - /** - * this event is emitted when an Identity has been updated - * the event is emitted by the 'updateIdentity' function - * `oldIdentity` is the old Identity contract's address to update - * `newIdentity` is the new Identity contract's - */ - event IdentityModified(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); - - /** - * this event is emitted when an Identity's country has been updated - * the event is emitted by the 'updateCountry' function - * `investorAddress` is the address on which the country has been updated - * `country` is the numeric code (ISO 3166-1) of the new country - */ - event CountryModified(address indexed investorAddress, uint16 indexed country); - - /** - * this event is emitted when an Identity Registry is bound to the storage contract - * the event is emitted by the 'addIdentityRegistry' function - * `identityRegistry` is the address of the identity registry added - */ - event IdentityRegistryBound(address indexed identityRegistry); - - /** - * this event is emitted when an Identity Registry is unbound from the storage contract - * the event is emitted by the 'removeIdentityRegistry' function - * `identityRegistry` is the address of the identity registry removed - */ - event IdentityRegistryUnbound(address indexed identityRegistry); - - /// functions - - /** - * @dev adds an identity contract corresponding to a user address in the storage. - * Requires that the user doesn't have an identity contract already registered. - * This function can only be called by an address set as agent of the smart contract - * @param _userAddress The address of the user - * @param _identity The address of the user's identity contract - * @param _country The country of the investor - * emits `IdentityStored` event - */ - function addIdentityToStorage( - address _userAddress, - IIdentity _identity, - uint16 _country - ) external; - - /** - * @dev Removes an user from the storage. - * Requires that the user have an identity contract already deployed that will be deleted. - * This function can only be called by an address set as agent of the smart contract - * @param _userAddress The address of the user to be removed - * emits `IdentityUnstored` event - */ - function removeIdentityFromStorage(address _userAddress) external; - - /** - * @dev Updates the country corresponding to a user address. - * Requires that the user should have an identity contract already deployed that will be replaced. - * This function can only be called by an address set as agent of the smart contract - * @param _userAddress The address of the user - * @param _country The new country of the user - * emits `CountryModified` event - */ - function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external; - - /** - * @dev Updates an identity contract corresponding to a user address. - * Requires that the user address should be the owner of the identity contract. - * Requires that the user should have an identity contract already deployed that will be replaced. - * This function can only be called by an address set as agent of the smart contract - * @param _userAddress The address of the user - * @param _identity The address of the user's new identity contract - * emits `IdentityModified` event - */ - function modifyStoredIdentity(address _userAddress, IIdentity _identity) external; - - /** - * @notice Adds an identity registry as agent of the Identity Registry Storage Contract. - * This function can only be called by the wallet set as owner of the smart contract - * This function adds the identity registry to the list of identityRegistries linked to the storage contract - * cannot bind more than 300 IR to 1 IRS - * @param _identityRegistry The identity registry address to add. - */ - function bindIdentityRegistry(address _identityRegistry) external; - - /** - * @notice Removes an identity registry from being agent of the Identity Registry Storage Contract. - * This function can only be called by the wallet set as owner of the smart contract - * This function removes the identity registry from the list of identityRegistries linked to the storage contract - * @param _identityRegistry The identity registry address to remove. - */ - function unbindIdentityRegistry(address _identityRegistry) external; - - /** - * @dev Returns the identity registries linked to the storage contract - */ - function linkedIdentityRegistries() external view returns (address[] memory); - - /** - * @dev Returns the onchainID of an investor. - * @param _userAddress The wallet of the investor - */ - function storedIdentity(address _userAddress) external view returns (IIdentity); - - /** - * @dev Returns the country code of an investor. - * @param _userAddress The wallet of the investor - */ - function storedInvestorCountry(address _userAddress) external view returns (uint16); } diff --git a/contracts/registry/interface/ITrustedIssuersRegistry.sol b/contracts/registry/interface/ITrustedIssuersRegistry.sol index 498ff7b8..f0232889 100644 --- a/contracts/registry/interface/ITrustedIssuersRegistry.sol +++ b/contracts/registry/interface/ITrustedIssuersRegistry.sol @@ -61,102 +61,12 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; +import "../../ERC-3643/IERC3643TrustedIssuersRegistry.sol"; -import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; +// solhint-disable-next-line no-empty-blocks +interface ITrustedIssuersRegistry is IERC3643TrustedIssuersRegistry { -interface ITrustedIssuersRegistry { - /** - * this event is emitted when a trusted issuer is added in the registry. - * the event is emitted by the addTrustedIssuer function - * `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract - * `claimTopics` is the set of claims that the trusted issuer is allowed to emit - */ - event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics); +// functions that are not part of the original standard can be added here in future versions - /** - * this event is emitted when a trusted issuer is removed from the registry. - * the event is emitted by the removeTrustedIssuer function - * `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract - */ - event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer); - - /** - * this event is emitted when the set of claim topics is changed for a given trusted issuer. - * the event is emitted by the updateIssuerClaimTopics function - * `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract - * `claimTopics` is the set of claims that the trusted issuer is allowed to emit - */ - event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics); - - /** - * @dev registers a ClaimIssuer contract as trusted claim issuer. - * Requires that a ClaimIssuer contract doesn't already exist - * Requires that the claimTopics set is not empty - * Requires that there is no more than 15 claimTopics - * Requires that there is no more than 50 Trusted issuers - * @param _trustedIssuer The ClaimIssuer contract address of the trusted claim issuer. - * @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit - * This function can only be called by the owner of the Trusted Issuers Registry contract - * emits a `TrustedIssuerAdded` event - */ - function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external; - - /** - * @dev Removes the ClaimIssuer contract of a trusted claim issuer. - * Requires that the claim issuer contract to be registered first - * @param _trustedIssuer the claim issuer to remove. - * This function can only be called by the owner of the Trusted Issuers Registry contract - * emits a `TrustedIssuerRemoved` event - */ - function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external; - - /** - * @dev Updates the set of claim topics that a trusted issuer is allowed to emit. - * Requires that this ClaimIssuer contract already exists in the registry - * Requires that the provided claimTopics set is not empty - * Requires that there is no more than 15 claimTopics - * @param _trustedIssuer the claim issuer to update. - * @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit - * This function can only be called by the owner of the Trusted Issuers Registry contract - * emits a `ClaimTopicsUpdated` event - */ - function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external; - - /** - * @dev Function for getting all the trusted claim issuers stored. - * @return array of all claim issuers registered. - */ - function getTrustedIssuers() external view returns (IClaimIssuer[] memory); - - /** - * @dev Function for getting all the trusted issuer allowed for a given claim topic. - * @param claimTopic the claim topic to get the trusted issuers for. - * @return array of all claim issuer addresses that are allowed for the given claim topic. - */ - function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory); - - /** - * @dev Checks if the ClaimIssuer contract is trusted - * @param _issuer the address of the ClaimIssuer contract - * @return true if the issuer is trusted, false otherwise. - */ - function isTrustedIssuer(address _issuer) external view returns (bool); - - /** - * @dev Function for getting all the claim topic of trusted claim issuer - * Requires the provided ClaimIssuer contract to be registered in the trusted issuers registry. - * @param _trustedIssuer the trusted issuer concerned. - * @return The set of claim topics that the trusted issuer is allowed to emit - */ - function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns (uint256[] memory); - - /** - * @dev Function for checking if the trusted claim issuer is allowed - * to emit a certain claim topic - * @param _issuer the address of the trusted issuer's ClaimIssuer contract - * @param _claimTopic the Claim Topic that has to be checked to know if the `issuer` is allowed to emit it - * @return true if the issuer is trusted for this claim topic. - */ - function hasClaimTopic(address _issuer, uint256 _claimTopic) external view returns (bool); } diff --git a/contracts/registry/storage/CTRStorage.sol b/contracts/registry/storage/CTRStorage.sol index 558b7f91..5bb952d9 100644 --- a/contracts/registry/storage/CTRStorage.sol +++ b/contracts/registry/storage/CTRStorage.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; contract CTRStorage { /// @dev All required Claim Topics diff --git a/contracts/registry/storage/IRSStorage.sol b/contracts/registry/storage/IRSStorage.sol index a755f4a0..fac2210d 100644 --- a/contracts/registry/storage/IRSStorage.sol +++ b/contracts/registry/storage/IRSStorage.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; diff --git a/contracts/registry/storage/IRStorage.sol b/contracts/registry/storage/IRStorage.sol index 05484531..956ed581 100644 --- a/contracts/registry/storage/IRStorage.sol +++ b/contracts/registry/storage/IRStorage.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../interface/IClaimTopicsRegistry.sol"; import "../interface/ITrustedIssuersRegistry.sol"; @@ -77,9 +77,12 @@ contract IRStorage { /// @dev Address of the IdentityRegistryStorage Contract IIdentityRegistryStorage internal _tokenIdentityStorage; + /// @dev disables the whole eligibility check system + bool internal _checksDisabled; + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ - uint256[49] private __gap; + uint256[48] private __gap; } \ No newline at end of file diff --git a/contracts/registry/storage/TIRStorage.sol b/contracts/registry/storage/TIRStorage.sol index 8a0eaa99..717f7032 100644 --- a/contracts/registry/storage/TIRStorage.sol +++ b/contracts/registry/storage/TIRStorage.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; diff --git a/contracts/roles/AgentRole.sol b/contracts/roles/AgentRole.sol index cab3a735..e591d940 100644 --- a/contracts/roles/AgentRole.sol +++ b/contracts/roles/AgentRole.sol @@ -61,33 +61,44 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/access/Ownable.sol"; import "./Roles.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/RoleErrors.sol"; + + +/// Events + +/// @dev This event is emitted when an agent is added. +/// @param _agent Address of agent contract +event AgentAdded(address indexed _agent); + +/// @dev This event is emitted when an agent is removed. +/// @param _agent Address of agent contract +event AgentRemoved(address indexed _agent); + contract AgentRole is Ownable { using Roles for Roles.Role; Roles.Role private _agents; - event AgentAdded(address indexed _agent); - event AgentRemoved(address indexed _agent); - modifier onlyAgent() { - require(isAgent(msg.sender), "AgentRole: caller does not have the Agent role"); + require(isAgent(msg.sender), CallerDoesNotHaveAgentRole()); _; } function addAgent(address _agent) public onlyOwner { - require(_agent != address(0), "invalid argument - zero address"); + require(_agent != address(0), ZeroAddress()); _agents.add(_agent); emit AgentAdded(_agent); } function removeAgent(address _agent) public onlyOwner { - require(_agent != address(0), "invalid argument - zero address"); + require(_agent != address(0), ZeroAddress()); _agents.remove(_agent); emit AgentRemoved(_agent); } diff --git a/contracts/roles/AgentRoleUpgradeable.sol b/contracts/roles/AgentRoleUpgradeable.sol index 7667247a..a963fdb1 100644 --- a/contracts/roles/AgentRoleUpgradeable.sol +++ b/contracts/roles/AgentRoleUpgradeable.sol @@ -61,33 +61,42 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; - -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +pragma solidity 0.8.27; +import "../utils/OwnableOnceNext2StepUpgradeable.sol"; import "./Roles.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/RoleErrors.sol"; + +/// Events + +/// @dev This event is emmited when an agent is added. +/// @param _agent Address of agent contract +event AgentAdded(address indexed _agent); -contract AgentRoleUpgradeable is OwnableUpgradeable { +/// @dev This event is emmited when an agent is removed. +/// @param _agent Address of agent contract +event AgentRemoved(address indexed _agent); + + +contract AgentRoleUpgradeable is OwnableOnceNext2StepUpgradeable { using Roles for Roles.Role; Roles.Role private _agents; - event AgentAdded(address indexed _agent); - event AgentRemoved(address indexed _agent); - modifier onlyAgent() { - require(isAgent(msg.sender), "AgentRole: caller does not have the Agent role"); + require(isAgent(msg.sender), CallerDoesNotHaveAgentRole()); _; } function addAgent(address _agent) public onlyOwner { - require(_agent != address(0), "invalid argument - zero address"); + require(_agent != address(0), ZeroAddress()); _agents.add(_agent); emit AgentAdded(_agent); } function removeAgent(address _agent) public onlyOwner { - require(_agent != address(0), "invalid argument - zero address"); + require(_agent != address(0), ZeroAddress()); _agents.remove(_agent); emit AgentRemoved(_agent); } diff --git a/contracts/roles/IERC173.sol b/contracts/roles/IERC173.sol new file mode 100644 index 00000000..5e0670ca --- /dev/null +++ b/contracts/roles/IERC173.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.27; + +/// @title ERC-173 Contract Ownership Standard +/// Note: the ERC-165 identifier for this interface is 0x7f5828d0 +/* is ERC165 */ +interface IERC173 { + /// @dev This emits when ownership of a contract changes. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Set the address of the new owner of the contract + /// @dev Set _newOwner to address(0) to renounce any ownership. + /// @param _newOwner The address of the new owner of the contract + function transferOwnership(address _newOwner) external; + + /// @notice Get the address of the owner + /// @return owner_ The address of the owner. + function owner() external view returns (address owner_); +} diff --git a/contracts/roles/Roles.sol b/contracts/roles/Roles.sol index 648431a5..dd49ace2 100644 --- a/contracts/roles/Roles.sol +++ b/contracts/roles/Roles.sol @@ -61,7 +61,10 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; + +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/RoleErrors.sol"; /** * @title Roles @@ -76,7 +79,7 @@ library Roles { * @dev Give an account access to this role. */ function add(Role storage role, address account) internal { - require(!has(role, account), "Roles: account already has role"); + require(!has(role, account), AccountAlreadyHasRole()); role.bearer[account] = true; } @@ -84,7 +87,7 @@ library Roles { * @dev Remove an account's access to this role. */ function remove(Role storage role, address account) internal { - require(has(role, account), "Roles: account does not have role"); + require(has(role, account), AccountDoesNotHaveRole()); role.bearer[account] = false; } @@ -93,7 +96,7 @@ library Roles { * @return bool */ function has(Role storage role, address account) internal view returns (bool) { - require(account != address(0), "Roles: account is the zero address"); + require(account != address(0), ZeroAddress()); return role.bearer[account]; } } diff --git a/contracts/roles/permissioning/agent/AgentManager.sol b/contracts/roles/permissioning/agent/AgentManager.sol index f6ef7dd9..0a6c6373 100644 --- a/contracts/roles/permissioning/agent/AgentManager.sol +++ b/contracts/roles/permissioning/agent/AgentManager.sol @@ -61,13 +61,14 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "../../../token/IToken.sol"; import "../../../registry/interface/IIdentityRegistry.sol"; import "./AgentRoles.sol"; +import "../../../errors/RoleErrors.sol"; contract AgentManager is AgentRoles { /// @dev the token managed by this AgentManager contract @@ -94,7 +95,7 @@ contract AgentManager is AgentRoles { require( isTransferManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Transfer Manager" + SenderIsNotTransferManager() ); token.forcedTransfer(_from, _to, _amount); } @@ -116,7 +117,7 @@ contract AgentManager is AgentRoles { require( isTransferManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Transfer Manager" + SenderIsNotTransferManager() ); token.batchForcedTransfer(_fromList, _toList, _amounts); } @@ -133,7 +134,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.pause(); } @@ -149,7 +150,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.unpause(); } @@ -169,7 +170,7 @@ contract AgentManager is AgentRoles { require( isSupplyModifier(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Supply Modifier" + SenderIsNotSupplyModifier() ); token.mint(_to, _amount); } @@ -190,7 +191,7 @@ contract AgentManager is AgentRoles { require( isSupplyModifier(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Supply Modifier" + SenderIsNotSupplyModifier() ); token.batchMint(_toList, _amounts); } @@ -212,7 +213,7 @@ contract AgentManager is AgentRoles { isSupplyModifier( address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Supply Modifier" + , SenderIsNotSupplyModifier() ); token.burn(_userAddress, _amount); } @@ -233,7 +234,7 @@ contract AgentManager is AgentRoles { require( isSupplyModifier(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Supply Modifier" + , SenderIsNotSupplyModifier() ); token.batchBurn(_userAddresses, _amounts); } @@ -254,7 +255,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.setAddressFrozen(_userAddress, _freeze); } @@ -274,7 +275,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.batchSetAddressFrozen(_userAddresses, _freeze); } @@ -294,7 +295,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.freezePartialTokens(_userAddress, _amount); } @@ -314,7 +315,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.batchFreezePartialTokens(_userAddresses, _amounts); } @@ -334,7 +335,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.unfreezePartialTokens(_userAddress, _amount); } @@ -354,7 +355,7 @@ contract AgentManager is AgentRoles { require( isFreezer(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Freezer"); + , SenderIsNotFreezer()); token.batchUnfreezePartialTokens(_userAddresses, _amounts); } @@ -375,7 +376,7 @@ contract AgentManager is AgentRoles { require( isRecoveryAgent(address(_managerOnchainID)) && _managerOnchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT Recovery Agent" + , SenderIsNotRecoveryAgent() ); token.recoveryAddress(_lostWallet, _newWallet, _onchainID); } @@ -397,7 +398,7 @@ contract AgentManager is AgentRoles { require( isWhiteListManager(address(_managerOnchainID)) && _managerOnchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT WhiteList Manager" + , SenderIsNotWhiteListManager() ); token.identityRegistry().registerIdentity(_userAddress, _onchainID, _country); } @@ -418,7 +419,7 @@ contract AgentManager is AgentRoles { require( isWhiteListManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT WhiteList Manager" + , SenderIsNotWhiteListManager() ); token.identityRegistry().updateIdentity(_userAddress, _identity); } @@ -439,7 +440,7 @@ contract AgentManager is AgentRoles { require( isWhiteListManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT WhiteList Manager" + , SenderIsNotWhiteListManager() ); token.identityRegistry().updateCountry(_userAddress, _country); } @@ -456,7 +457,7 @@ contract AgentManager is AgentRoles { require( isWhiteListManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2) - , "Role: Sender is NOT WhiteList Manager" + , SenderIsNotWhiteListManager() ); token.identityRegistry().deleteIdentity(_userAddress); } diff --git a/contracts/roles/permissioning/agent/AgentRoles.sol b/contracts/roles/permissioning/agent/AgentRoles.sol index ca7a86de..509e43c9 100644 --- a/contracts/roles/permissioning/agent/AgentRoles.sol +++ b/contracts/roles/permissioning/agent/AgentRoles.sol @@ -61,11 +61,25 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/access/Ownable.sol"; import "../../Roles.sol"; +import "../../../errors/RoleErrors.sol"; + +/// events + +/// @dev This event is emmited when a role is added. +/// @param _agent Address of agent contract +/// @param _role Role label. +event RoleAdded(address indexed _agent, string _role); + +/// @dev This event is emmited when a role is removed. +/// @param _agent Address of agent contract +/// @param _role Role label. +event RoleRemoved(address indexed _agent, string _role); + contract AgentRoles is Ownable { using Roles for Roles.Role; @@ -80,15 +94,10 @@ contract AgentRoles is Ownable { Roles.Role private _whiteListManagers; Roles.Role private _agentAdmin; - /// events - - event RoleAdded(address indexed _agent, string _role); - event RoleRemoved(address indexed _agent, string _role); - /// modifiers modifier onlyAdmin() { - require(owner() == msg.sender || isAgentAdmin(_msgSender()), "Role: Sender is NOT Admin"); + require(owner() == msg.sender || isAgentAdmin(_msgSender()), SenderIsNotAdmin()); _; } diff --git a/contracts/roles/permissioning/agent/AgentRolesUpgradeable.sol b/contracts/roles/permissioning/agent/AgentRolesUpgradeable.sol index b60f624a..b63f8f39 100644 --- a/contracts/roles/permissioning/agent/AgentRolesUpgradeable.sol +++ b/contracts/roles/permissioning/agent/AgentRolesUpgradeable.sol @@ -61,15 +61,26 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; - -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +pragma solidity 0.8.27; +import "../../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "../../Roles.sol"; +import "../../../errors/RoleErrors.sol"; + +/// events + +/// @dev This event is emmited when a role is added. +/// @param _agent Address of agent contract +/// @param _role Role label. +event RoleAdded(address indexed _agent, string _role); -contract AgentRolesUpgradeable is OwnableUpgradeable +/// @dev This event is emmited when a role is removed. +/// @param _agent Address of agent contract +/// @param _role Role label. +event RoleRemoved(address indexed _agent, string _role); - { + +contract AgentRolesUpgradeable is OwnableOnceNext2StepUpgradeable { using Roles for Roles.Role; /// variables @@ -82,15 +93,10 @@ contract AgentRolesUpgradeable is OwnableUpgradeable Roles.Role private _whiteListManagers; Roles.Role private _agentAdmin; - /// events - - event RoleAdded(address indexed _agent, string _role); - event RoleRemoved(address indexed _agent, string _role); - /// modifiers modifier onlyAdmin() { - require(owner() == msg.sender || isAgentAdmin(_msgSender()), "Role: Sender is NOT Admin"); + require(owner() == msg.sender || isAgentAdmin(_msgSender()), SenderIsNotAdmin()); _; } diff --git a/contracts/roles/permissioning/owner/OwnerManager.sol b/contracts/roles/permissioning/owner/OwnerManager.sol index e2bcac83..00df7a97 100644 --- a/contracts/roles/permissioning/owner/OwnerManager.sol +++ b/contracts/roles/permissioning/owner/OwnerManager.sol @@ -61,7 +61,7 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "../../../token/IToken.sol"; import "../../../registry/interface/IIdentityRegistry.sol"; @@ -72,18 +72,22 @@ import "./OwnerRoles.sol"; import "../../AgentRole.sol"; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; +import "../../../errors/RoleErrors.sol"; + +/// Events + +/// @dev Event emitted for each executed interaction with the compliance contract. +/// For gas efficiency, only the interaction calldata selector (first 4 bytes) is included in the event. +/// For interactions without calldata or whose calldata is shorter than 4 bytes, the selector will be `0`. +/// @param _target Address of compliance contract. +/// @param _selector See above comment. +event ComplianceInteraction(address indexed _target, bytes4 _selector); + contract OwnerManager is OwnerRoles { /// @dev the token that is managed by this OwnerManager Contract IToken public token; - /// @dev Event emitted for each executed interaction with the compliance contract. - /// - /// For gas efficiency, only the interaction calldata selector (first 4 - /// bytes) is included in the event. For interactions without calldata or - /// whose calldata is shorter than 4 bytes, the selector will be `0`. - event ComplianceInteraction(address indexed target, bytes4 selector); - /** * @dev the constructor initiates the OwnerManager contract * and sets msg.sender as owner of the contract @@ -104,7 +108,7 @@ contract OwnerManager is OwnerRoles { function callSetIdentityRegistry(address _identityRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Registry Address Setter" + SenderIsNotRegistryAddressSetter() ); token.setIdentityRegistry(_identityRegistry); } @@ -120,7 +124,7 @@ contract OwnerManager is OwnerRoles { function callSetCompliance(address _compliance, IIdentity _onchainID) external { require( isComplianceSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Compliance Setter" + SenderIsNotComplianceSetter() ); token.setCompliance(_compliance); } @@ -135,7 +139,8 @@ contract OwnerManager is OwnerRoles { function callComplianceFunction(bytes calldata callData, IIdentity _onchainID) external { require( isComplianceManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Compliance Manager"); + SenderIsNotComplianceManager() + ); address target = address(token.compliance()); // NOTE: Use assembly to call the interaction instead of a low level @@ -179,7 +184,7 @@ contract OwnerManager is OwnerRoles { function callSetTokenName(string calldata _name, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Token Information Manager" + SenderIsNotTokenInformationManager() ); token.setName(_name); } @@ -195,7 +200,7 @@ contract OwnerManager is OwnerRoles { function callSetTokenSymbol(string calldata _symbol, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Token Information Manager" + SenderIsNotTokenInformationManager() ); token.setSymbol(_symbol); } @@ -211,7 +216,7 @@ contract OwnerManager is OwnerRoles { function callSetTokenOnchainID(address _tokenOnchainID, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Token Information Manager" + SenderIsNotTokenInformationManager() ); token.setOnchainID(_tokenOnchainID); } @@ -227,7 +232,7 @@ contract OwnerManager is OwnerRoles { function callSetClaimTopicsRegistry(address _claimTopicsRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Registry Address Setter" + SenderIsNotRegistryAddressSetter() ); token.identityRegistry().setClaimTopicsRegistry(_claimTopicsRegistry); } @@ -243,7 +248,7 @@ contract OwnerManager is OwnerRoles { function callSetTrustedIssuersRegistry(address _trustedIssuersRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT Registry Address Setter" + SenderIsNotRegistryAddressSetter() ); token.identityRegistry().setTrustedIssuersRegistry(_trustedIssuersRegistry); } @@ -263,7 +268,7 @@ contract OwnerManager is OwnerRoles { ) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT IssuersRegistryManager" + SenderIsNotIssuersRegistryManager() ); token.identityRegistry().issuersRegistry().addTrustedIssuer(_trustedIssuer, _claimTopics); } @@ -279,7 +284,7 @@ contract OwnerManager is OwnerRoles { function callRemoveTrustedIssuer(IClaimIssuer _trustedIssuer, IIdentity _onchainID) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT IssuersRegistryManager" + SenderIsNotIssuersRegistryManager() ); token.identityRegistry().issuersRegistry().removeTrustedIssuer(_trustedIssuer); } @@ -299,7 +304,7 @@ contract OwnerManager is OwnerRoles { ) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT IssuersRegistryManager" + SenderIsNotIssuersRegistryManager() ); token.identityRegistry().issuersRegistry().updateIssuerClaimTopics(_trustedIssuer, _claimTopics); } @@ -315,7 +320,7 @@ contract OwnerManager is OwnerRoles { function callAddClaimTopic(uint256 _claimTopic, IIdentity _onchainID) external { require( isClaimRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT ClaimRegistryManager" + SenderIsNotClaimRegistryManager() ); token.identityRegistry().topicsRegistry().addClaimTopic(_claimTopic); } @@ -331,7 +336,7 @@ contract OwnerManager is OwnerRoles { function callRemoveClaimTopic(uint256 _claimTopic, IIdentity _onchainID) external { require( isClaimRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), - "Role: Sender is NOT ClaimRegistryManager" + SenderIsNotClaimRegistryManager() ); token.identityRegistry().topicsRegistry().removeClaimTopic(_claimTopic); } diff --git a/contracts/roles/permissioning/owner/OwnerRoles.sol b/contracts/roles/permissioning/owner/OwnerRoles.sol index b82d71f8..65daf1a5 100644 --- a/contracts/roles/permissioning/owner/OwnerRoles.sol +++ b/contracts/roles/permissioning/owner/OwnerRoles.sol @@ -61,11 +61,25 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "@openzeppelin/contracts/access/Ownable.sol"; import "../../Roles.sol"; +import "../../../errors/RoleErrors.sol"; + +/// Events + +/// @dev This event is emmited when a role is added. +/// @param _owner Address of contract +/// @param _role Role label. +event RoleAdded(address indexed _owner, string _role); + +/// @dev This event is emmited when a role is removed. +/// @param _owner Address of contract +/// @param _role Role label. +event RoleRemoved(address indexed _owner, string _role); + contract OwnerRoles is Ownable { using Roles for Roles.Role; @@ -80,15 +94,10 @@ contract OwnerRoles is Ownable { Roles.Role private _issuersRegistryManager; Roles.Role private _tokenInfoManager; - /// events - - event RoleAdded(address indexed _owner, string _role); - event RoleRemoved(address indexed _owner, string _role); - /// modifiers modifier onlyAdmin() { - require(owner() == msg.sender || isOwnerAdmin(_msgSender()), "Role: Sender is NOT Admin"); + require(owner() == msg.sender || isOwnerAdmin(_msgSender()), SenderIsNotAdmin()); _; } diff --git a/contracts/roles/permissioning/owner/OwnerRolesUpgradeable.sol b/contracts/roles/permissioning/owner/OwnerRolesUpgradeable.sol index 69702eba..688f5fb9 100644 --- a/contracts/roles/permissioning/owner/OwnerRolesUpgradeable.sol +++ b/contracts/roles/permissioning/owner/OwnerRolesUpgradeable.sol @@ -61,15 +61,26 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; - -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +pragma solidity 0.8.27; +import "../../../utils/OwnableOnceNext2StepUpgradeable.sol"; import "../../Roles.sol"; +import "../../../errors/RoleErrors.sol"; + +/// Events + +/// @dev This event is emmited when a role is added. +/// @param _owner Address of contract +/// @param _role Role label. +event RoleAdded(address indexed _owner, string _role); -contract OwnerRolesUpgradeable is OwnableUpgradeable +/// @dev This event is emmited when a role is removed. +/// @param _owner Address of contract +/// @param _role Role label. +event RoleRemoved(address indexed _owner, string _role); - { + +contract OwnerRolesUpgradeable is OwnableOnceNext2StepUpgradeable { using Roles for Roles.Role; /// variables @@ -82,15 +93,10 @@ contract OwnerRolesUpgradeable is OwnableUpgradeable Roles.Role private _issuersRegistryManager; Roles.Role private _tokenInfoManager; - /// events - - event RoleAdded(address indexed _owner, string _role); - event RoleRemoved(address indexed _owner, string _role); - /// modifiers modifier onlyAdmin() { - require(owner() == msg.sender || isOwnerAdmin(_msgSender()), "Role: Sender is NOT Admin"); + require(owner() == msg.sender || isOwnerAdmin(_msgSender()), SenderIsNotAdmin()); _; } diff --git a/contracts/token/IToken.sol b/contracts/token/IToken.sol index 9f123142..2fe76fd8 100644 --- a/contracts/token/IToken.sol +++ b/contracts/token/IToken.sol @@ -61,400 +61,81 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; - -import "../registry/interface/IIdentityRegistry.sol"; -import "../compliance/modular/IModularCompliance.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +pragma solidity 0.8.27; + +import "./TokenStructs.sol"; +import "../ERC-3643/IERC3643.sol"; + +/// @dev This event is emitted when restrictions on an agent's roles are updated. +/// @param _agent is the address of the agent whose roles are being restricted. +/// @param _disableMint indicates whether the agent is restricted from minting tokens. +/// @param _disableBurn indicates whether the agent is restricted from burning tokens. +/// @param _disableAddressFreeze indicates whether the agent is restricted from freezing addresses. +/// @param _disableForceTransfer indicates whether the agent is restricted from forcing transfers. +/// @param _disablePartialFreeze indicates whether the agent is restricted from partially freezing tokens. +/// @param _disablePause indicates whether the agent is restricted from pausing the token contract. +/// @param _disableRecovery indicates whether the agent is restricted from performing recovery operations. +event AgentRestrictionsSet( + address indexed _agent, + bool _disableMint, + bool _disableBurn, + bool _disableAddressFreeze, + bool _disableForceTransfer, + bool _disablePartialFreeze, + bool _disablePause, + bool _disableRecovery); + +/// @dev This event is emitted when the owner gives or cancels a default allowance. +/// @param _to Address of target. +/// @param _allowance Allowance or disallowance. +event DefaultAllowance(address _to, bool _allowance); + +/// @dev This event is emitted when a user remove the default allowance. +/// @param _user Address of user. +event DefaultAllowanceDisabled(address _user); + +/// @dev This event is emitted when a user adds the default allowance back after disabling. +/// @param _user Address of user. +event DefaultAllowanceEnabled(address _user); /// @dev interface -interface IToken is IERC20 { - - /// events - - /** - * this event is emitted when the token information is updated. - * the event is emitted by the token init function and by the setTokenInformation function - * `_newName` is the name of the token - * `_newSymbol` is the symbol of the token - * `_newDecimals` is the decimals of the token - * `_newVersion` is the version of the token, current version is 3.0 - * `_newOnchainID` is the address of the onchainID of the token - */ - event UpdatedTokenInformation(string indexed _newName, string indexed _newSymbol, uint8 _newDecimals, string - _newVersion, address indexed _newOnchainID); - - /** - * this event is emitted when the IdentityRegistry has been set for the token - * the event is emitted by the token constructor and by the setIdentityRegistry function - * `_identityRegistry` is the address of the Identity Registry of the token - */ - event IdentityRegistryAdded(address indexed _identityRegistry); - - /** - * this event is emitted when the Compliance has been set for the token - * the event is emitted by the token constructor and by the setCompliance function - * `_compliance` is the address of the Compliance contract of the token - */ - event ComplianceAdded(address indexed _compliance); - - /** - * this event is emitted when an investor successfully recovers his tokens - * the event is emitted by the recoveryAddress function - * `_lostWallet` is the address of the wallet that the investor lost access to - * `_newWallet` is the address of the wallet that the investor provided for the recovery - * `_investorOnchainID` is the address of the onchainID of the investor who asked for a recovery - */ - event RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID); - - /** - * this event is emitted when the wallet of an investor is frozen or unfrozen - * the event is emitted by setAddressFrozen and batchSetAddressFrozen functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_isFrozen` is the freezing status of the wallet - * if `_isFrozen` equals `true` the wallet is frozen after emission of the event - * if `_isFrozen` equals `false` the wallet is unfrozen after emission of the event - * `_owner` is the address of the agent who called the function to freeze the wallet - */ - event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); - - /** - * this event is emitted when a certain amount of tokens is frozen on a wallet - * the event is emitted by freezePartialTokens and batchFreezePartialTokens functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_amount` is the amount of tokens that are frozen - */ - event TokensFrozen(address indexed _userAddress, uint256 _amount); - - /** - * this event is emitted when a certain amount of tokens is unfrozen on a wallet - * the event is emitted by unfreezePartialTokens and batchUnfreezePartialTokens functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_amount` is the amount of tokens that are unfrozen - */ - event TokensUnfrozen(address indexed _userAddress, uint256 _amount); - - /** - * this event is emitted when the token is paused - * the event is emitted by the pause function - * `_userAddress` is the address of the wallet that called the pause function - */ - event Paused(address _userAddress); - - /** - * this event is emitted when the token is unpaused - * the event is emitted by the unpause function - * `_userAddress` is the address of the wallet that called the unpause function - */ - event Unpaused(address _userAddress); +interface IToken is IERC3643 { /// functions /** - * @dev sets the token name - * @param _name the name of token to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setName(string calldata _name) external; - - /** - * @dev sets the token symbol - * @param _symbol the token symbol to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setSymbol(string calldata _symbol) external; - - /** - * @dev sets the onchain ID of the token - * @param _onchainID the address of the onchain ID to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setOnchainID(address _onchainID) external; + * @dev The owner of this address can allow or disallow spending without allowance. + * Any `TransferFrom` from these targets won't need allowance (allow = true) or will need allowance (allow = false). + * @param _allow Allow or disallow spending without allowance. + * @param _targets Addresses without allowance needed. + */ + function setAllowanceForAll(bool _allow, address[] calldata _targets) external; /** - * @dev pauses the token contract, when contract is paused investors cannot transfer tokens anymore - * This function can only be called by a wallet set as agent of the token - * emits a `Paused` event - */ - function pause() external; + * @dev The caller can remove default allowance globally. + */ + function disableDefaultAllowance() external; /** - * @dev unpauses the token contract, when contract is unpaused investors can transfer tokens - * if their wallet is not blocked & if the amount to transfer is <= to the amount of free tokens - * This function can only be called by a wallet set as agent of the token - * emits an `Unpaused` event - */ - function unpause() external; + * @dev The caller can get default allowance back globally. + */ + function enableDefaultAllowance() external; /** - * @dev sets an address frozen status for this token. - * @param _userAddress The address for which to update frozen status - * @param _freeze Frozen status of the address - * This function can only be called by a wallet set as agent of the token - * emits an `AddressFrozen` event + * @dev Set restrictions on agent's roles. + * This function can only be called by the contract owner, as enforced by the `onlyOwner` modifier. + * emits an `AgentRestrictionsSet` event upon successfully updating an agent's restrictions. + * @param agent The address of the agent whose permissions are being modified. + * @param restrictions A `TokenRoles` struct containing boolean flags for each role to be restricted. + * Each flag set to `true` disables the corresponding capability for the agent. + * throws AddressNotAgent error if the specified address is not an agent. */ - function setAddressFrozen(address _userAddress, bool _freeze) external; + function setAgentRestrictions(address agent, TokenRoles memory restrictions) external; /** - * @dev freezes token amount specified for given address. - * @param _userAddress The address for which to update frozen tokens - * @param _amount Amount of Tokens to be frozen - * This function can only be called by a wallet set as agent of the token - * emits a `TokensFrozen` event + * @dev Returns A `TokenRoles` struct containing boolean flags for each restricted role. + * Each flag set to `true` disables the corresponding capability for the agent. */ - function freezePartialTokens(address _userAddress, uint256 _amount) external; + function getAgentRestrictions(address agent) external view returns (TokenRoles memory); - /** - * @dev unfreezes token amount specified for given address - * @param _userAddress The address for which to update frozen tokens - * @param _amount Amount of Tokens to be unfrozen - * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event - */ - function unfreezePartialTokens(address _userAddress, uint256 _amount) external; - - /** - * @dev sets the Identity Registry for the token - * @param _identityRegistry the address of the Identity Registry to set - * Only the owner of the token smart contract can call this function - * emits an `IdentityRegistryAdded` event - */ - function setIdentityRegistry(address _identityRegistry) external; - - /** - * @dev sets the compliance contract of the token - * @param _compliance the address of the compliance contract to set - * Only the owner of the token smart contract can call this function - * calls bindToken on the compliance contract - * emits a `ComplianceAdded` event - */ - function setCompliance(address _compliance) external; - - /** - * @dev force a transfer of tokens between 2 whitelisted wallets - * In case the `from` address has not enough free tokens (unfrozen tokens) - * but has a total balance higher or equal to the `amount` - * the amount of frozen tokens is reduced in order to have enough free tokens - * to proceed the transfer, in such a case, the remaining balance on the `from` - * account is 100% composed of frozen tokens post-transfer. - * Require that the `to` address is a verified address, - * @param _from The address of the sender - * @param _to The address of the receiver - * @param _amount The number of tokens to transfer - * @return `true` if successful and revert if unsuccessful - * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_from` - * emits a `Transfer` event - */ - function forcedTransfer( - address _from, - address _to, - uint256 _amount - ) external returns (bool); - - /** - * @dev mint tokens on a wallet - * Improved version of default mint method. Tokens can be minted - * to an address if only it is a verified address as per the security token. - * @param _to Address to mint the tokens to. - * @param _amount Amount of tokens to mint. - * This function can only be called by a wallet set as agent of the token - * emits a `Transfer` event - */ - function mint(address _to, uint256 _amount) external; - - /** - * @dev burn tokens on a wallet - * In case the `account` address has not enough free tokens (unfrozen tokens) - * but has a total balance higher or equal to the `value` amount - * the amount of frozen tokens is reduced in order to have enough free tokens - * to proceed the burn, in such a case, the remaining balance on the `account` - * is 100% composed of frozen tokens post-transaction. - * @param _userAddress Address to burn the tokens from. - * @param _amount Amount of tokens to burn. - * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` - * emits a `Transfer` event - */ - function burn(address _userAddress, uint256 _amount) external; - - /** - * @dev recovery function used to force transfer tokens from a - * lost wallet to a new wallet for an investor. - * @param _lostWallet the wallet that the investor lost - * @param _newWallet the newly provided wallet on which tokens have to be transferred - * @param _investorOnchainID the onchainID of the investor asking for a recovery - * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if there is some frozen tokens on the lost wallet if the recovery process is successful - * emits a `Transfer` event if the recovery process is successful - * emits a `RecoverySuccess` event if the recovery process is successful - * emits a `RecoveryFails` event if the recovery process fails - */ - function recoveryAddress( - address _lostWallet, - address _newWallet, - address _investorOnchainID - ) external returns (bool); - - /** - * @dev function allowing to issue transfers in batch - * Require that the msg.sender and `to` addresses are not frozen. - * Require that the total value should not exceed available balance. - * Require that the `to` addresses are all verified addresses, - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _toList The addresses of the receivers - * @param _amounts The number of tokens to transfer to the corresponding receiver - * emits _toList.length `Transfer` events - */ - function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external; - - /** - * @dev function allowing to issue forced transfers in batch - * Require that `_amounts[i]` should not exceed available balance of `_fromList[i]`. - * Require that the `_toList` addresses are all verified addresses - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_fromList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _fromList The addresses of the senders - * @param _toList The addresses of the receivers - * @param _amounts The number of tokens to transfer to the corresponding receiver - * This function can only be called by a wallet set as agent of the token - * emits `TokensUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]` - * emits _fromList.length `Transfer` events - */ - function batchForcedTransfer( - address[] calldata _fromList, - address[] calldata _toList, - uint256[] calldata _amounts - ) external; - - /** - * @dev function allowing to mint tokens in batch - * Require that the `_toList` addresses are all verified addresses - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _toList The addresses of the receivers - * @param _amounts The number of tokens to mint to the corresponding receiver - * This function can only be called by a wallet set as agent of the token - * emits _toList.length `Transfer` events - */ - function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external; - - /** - * @dev function allowing to burn tokens in batch - * Require that the `_userAddresses` addresses are all verified addresses - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses of the wallets concerned by the burn - * @param _amounts The number of tokens to burn from the corresponding wallets - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `Transfer` events - */ - function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external; - - /** - * @dev function allowing to set frozen addresses in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses for which to update frozen status - * @param _freeze Frozen status of the corresponding address - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `AddressFrozen` events - */ - function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; - - /** - * @dev function allowing to freeze tokens partially in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses on which tokens need to be frozen - * @param _amounts the amount of tokens to freeze on the corresponding address - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `TokensFrozen` events - */ - function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; - - /** - * @dev function allowing to unfreeze tokens partially in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses on which tokens need to be unfrozen - * @param _amounts the amount of tokens to unfreeze on the corresponding address - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `TokensUnfrozen` events - */ - function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 1 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * balanceOf() and transfer(). - */ - function decimals() external view returns (uint8); - - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the address of the onchainID of the token. - * the onchainID of the token gives all the information available - * about the token and is managed by the token issuer or his agent. - */ - function onchainID() external view returns (address); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the TREX version of the token. - * current version is 3.0.0 - */ - function version() external view returns (string memory); - - /** - * @dev Returns the Identity Registry linked to the token - */ - function identityRegistry() external view returns (IIdentityRegistry); - - /** - * @dev Returns the Compliance contract linked to the token - */ - function compliance() external view returns (IModularCompliance); - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() external view returns (bool); - - /** - * @dev Returns the freezing status of a wallet - * if isFrozen returns `true` the wallet is frozen - * if isFrozen returns `false` the wallet is not frozen - * isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by - * a partial freeze or the whole token could be blocked by pause - * @param _userAddress the address of the wallet on which isFrozen is called - */ - function isFrozen(address _userAddress) external view returns (bool); - - /** - * @dev Returns the amount of tokens that are partially frozen on a wallet - * the amount of frozen tokens is always <= to the total balance of the wallet - * @param _userAddress the address of the wallet on which getFrozenTokens is called - */ - function getFrozenTokens(address _userAddress) external view returns (uint256); } diff --git a/contracts/token/Token.sol b/contracts/token/Token.sol index 4ea30918..949f6865 100755 --- a/contracts/token/Token.sol +++ b/contracts/token/Token.sol @@ -61,26 +61,77 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; +pragma solidity 0.8.27; import "./IToken.sol"; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "./TokenStorage.sol"; import "../roles/AgentRoleUpgradeable.sol"; +import "../roles/IERC173.sol"; +import "../errors/InvalidArgumentErrors.sol"; +import "../errors/CommonErrors.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -contract Token is IToken, AgentRoleUpgradeable, TokenStorage { +/// errors + +/// @dev Thrown when address is not an agent. +/// @param _agent address of agent. +error AddressNotAgent(address _agent); + +/// @dev Thrown when agent is not authorized. +/// @param _agent address of agent. +/// @param _reason authorisation label. +error AgentNotAuthorized(address _agent, string _reason); + +/// @dev Thrown when already initialized. +error AlreadyInitialized(); + +/// @dev Thrown when amount is above maximum amount. +/// @param _amount amount value. +/// @param _maxAmount maximum amount value. +error AmountAboveFrozenTokens(uint256 _amount, uint256 _maxAmount); + +/// @dev Thrown when wallet is frozen. +error FrozenWallet(); + +/// @dev Thrown when compliance is not followed. +error ComplianceNotFollowed(); + +/// @dev Thrown when thers is no token to recover. +error NoTokenToRecover(); + +/// @dev Thrown when recovery is not possible. +error RecoveryNotPossible(); + +/// @dev Thrown when transfer is not possible. +error TransferNotPossible(); + +/// @dev Thrown when identity is not verified. +error UnverifiedIdentity(); + +/// @dev Thrown when default allowance is already enabled for _user. +error DefaultAllowanceAlreadyEnabled(address _user); + +/// @dev Thrown when default allowance is already disabled for _user. +error DefaultAllowanceAlreadyDisabled(address _user); + +/// @dev Thrown when default allowance is already set for _target. +error DefaultAllowanceAlreadySet(address _target); + + +contract Token is IToken, AgentRoleUpgradeable, TokenStorage, IERC165 { /// modifiers /// @dev Modifier to make a function callable only when the contract is not paused. modifier whenNotPaused() { - require(!_tokenPaused, "Pausable: paused"); + require(!_tokenPaused, EnforcedPause()); _; } /// @dev Modifier to make a function callable only when the contract is paused. modifier whenPaused() { - require(_tokenPaused, "Pausable: not paused"); + require(_tokenPaused, ExpectedPause()); _; } @@ -110,16 +161,16 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { // as there was a bug with the initializer modifier on these proxies // that check is preventing attackers to call the init functions on those // legacy contracts. - require(owner() == address(0), "already initialized"); + require(owner() == address(0), AlreadyInitialized()); require( _identityRegistry != address(0) && _compliance != address(0) - , "invalid argument - zero address"); + , ZeroAddress()); require( keccak256(abi.encode(_name)) != keccak256(abi.encode("")) && keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")) - , "invalid argument - empty string"); - require(0 <= _decimals && _decimals <= 18, "decimals between 0 and 18"); + , EmptyString()); + require(0 <= _decimals && _decimals <= 18, DecimalsOutOfRange(_decimals)); __Ownable_init(); _tokenName = _name; _tokenSymbol = _symbol; @@ -159,7 +210,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-setName}. */ function setName(string calldata _name) external override onlyOwner { - require(keccak256(abi.encode(_name)) != keccak256(abi.encode("")), "invalid argument - empty string"); + require(keccak256(abi.encode(_name)) != keccak256(abi.encode("")), EmptyString()); _tokenName = _name; emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } @@ -168,7 +219,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-setSymbol}. */ function setSymbol(string calldata _symbol) external override onlyOwner { - require(keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")), "invalid argument - empty string"); + require(keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")), EmptyString()); _tokenSymbol = _symbol; emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } @@ -186,6 +237,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-pause}. */ function pause() external override onlyAgent whenNotPaused { + require(!getAgentRestrictions(msg.sender).disablePause, AgentNotAuthorized(msg.sender, "pause disabled")); _tokenPaused = true; emit Paused(msg.sender); } @@ -194,6 +246,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-unpause}. */ function unpause() external override onlyAgent whenPaused { + require(!getAgentRestrictions(msg.sender).disablePause, AgentNotAuthorized(msg.sender, "pause disabled")); _tokenPaused = false; emit Unpaused(msg.sender); } @@ -207,6 +260,25 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { } } + /** + * @dev See {IToken-setAgentRestrictions}. + */ + function setAgentRestrictions(address agent, TokenRoles memory restrictions) external override onlyOwner { + if(!isAgent(agent)) { + revert AddressNotAgent(agent); + } + _agentsRestrictions[agent] = restrictions; + emit AgentRestrictionsSet( + agent, + restrictions.disableMint, + restrictions.disableBurn, + restrictions.disableAddressFreeze, + restrictions.disableForceTransfer, + restrictions.disablePartialFreeze, + restrictions.disablePause, + restrictions.disableRecovery ); + } + /** * @notice ERC-20 overridden function that include logic to check for trade validity. * Require that the from and to addresses are not frozen. @@ -222,15 +294,19 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { address _to, uint256 _amount ) external override whenNotPaused returns (bool) { - require(!_frozen[_to] && !_frozen[_from], "wallet is frozen"); - require(_amount <= balanceOf(_from) - (_frozenTokens[_from]), "Insufficient Balance"); + require(!_frozen[_to] && !_frozen[_from], FrozenWallet()); + + uint256 balance = balanceOf(_from) - (_frozenTokens[_from]); + require(_amount <= balance, ERC20InsufficientBalance(_from, balance, _amount)); if (_tokenIdentityRegistry.isVerified(_to) && _tokenCompliance.canTransfer(_from, _to, _amount)) { - _approve(_from, msg.sender, _allowances[_from][msg.sender] - (_amount)); + if (!_defaultAllowances[msg.sender] || _defaultAllowanceOptOuts[_from]) { + _approve(_from, msg.sender, _allowances[_from][msg.sender] - (_amount)); + } _transfer(_from, _to, _amount); _tokenCompliance.transferred(_from, _to, _amount); return true; } - revert("Transfer not possible"); + revert TransferNotPossible(); } /** @@ -299,26 +375,63 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { address _newWallet, address _investorOnchainID ) external override onlyAgent returns (bool) { - require(balanceOf(_lostWallet) != 0, "no tokens to recover"); - IIdentity _onchainID = IIdentity(_investorOnchainID); - bytes32 _key = keccak256(abi.encode(_newWallet)); - if (_onchainID.keyHasPurpose(_key, 1)) { - uint256 investorTokens = balanceOf(_lostWallet); - uint256 frozenTokens = _frozenTokens[_lostWallet]; - _tokenIdentityRegistry.registerIdentity(_newWallet, _onchainID, _tokenIdentityRegistry.investorCountry - (_lostWallet)); - forcedTransfer(_lostWallet, _newWallet, investorTokens); - if (frozenTokens > 0) { - freezePartialTokens(_newWallet, frozenTokens); + require(!getAgentRestrictions(msg.sender).disableRecovery, AgentNotAuthorized(msg.sender, "recovery disabled")); + require(balanceOf(_lostWallet) != 0, NoTokenToRecover()); + require(_tokenIdentityRegistry.contains(_lostWallet) || + _tokenIdentityRegistry.contains(_newWallet), RecoveryNotPossible()); + uint256 investorTokens = balanceOf(_lostWallet); + uint256 frozenTokens = _frozenTokens[_lostWallet]; + bool addressFreeze = _frozen[_lostWallet]; + _transfer(_lostWallet, _newWallet, investorTokens); + if(frozenTokens > 0) { + _frozenTokens[_lostWallet] = 0; + emit TokensUnfrozen(_lostWallet, frozenTokens); + _frozenTokens[_newWallet] += frozenTokens; + emit TokensFrozen(_newWallet, frozenTokens); + } + if(addressFreeze) { + _frozen[_lostWallet] = false; + emit AddressFrozen(_lostWallet, false, address(this)); + if(!_frozen[_newWallet]){ + _frozen[_newWallet] = true; + emit AddressFrozen(_newWallet, true, address(this)); } - if (_frozen[_lostWallet] == true) { - setAddressFrozen(_newWallet, true); + } + if(_tokenIdentityRegistry.contains(_lostWallet)) { + if(!_tokenIdentityRegistry.contains(_newWallet)) { + _tokenIdentityRegistry.registerIdentity( + _newWallet, IIdentity(_investorOnchainID), + _tokenIdentityRegistry.investorCountry(_lostWallet)); } _tokenIdentityRegistry.deleteIdentity(_lostWallet); - emit RecoverySuccess(_lostWallet, _newWallet, _investorOnchainID); - return true; } - revert("Recovery not possible"); + emit RecoverySuccess(_lostWallet, _newWallet, _investorOnchainID); + return true; + } + + /// @dev See {IToken-setAllowanceForAll}. + function setAllowanceForAll(bool _allow, address[] calldata _targets) external override onlyOwner { + uint256 targetsCount = _targets.length; + require(targetsCount <= 100, ArraySizeLimited(100)); + for (uint256 i = 0; i < targetsCount; i++) { + require(_defaultAllowances[_targets[i]] != _allow, DefaultAllowanceAlreadySet(_targets[i])); + _defaultAllowances[_targets[i]] = _allow; + emit DefaultAllowance(_targets[i], _allow); + } + } + + /// @dev See {IToken-disableDefaultAllowance}. + function disableDefaultAllowance() external override { + require(!_defaultAllowanceOptOuts[msg.sender], DefaultAllowanceAlreadyDisabled(msg.sender)); + _defaultAllowanceOptOuts[msg.sender] = true; + emit DefaultAllowanceDisabled(msg.sender); + } + + /// @dev See {IToken-enableDefaultAllowance}. + function enableDefaultAllowance() external override { + require(_defaultAllowanceOptOuts[msg.sender], DefaultAllowanceAlreadyEnabled(msg.sender)); + _defaultAllowanceOptOuts[msg.sender] = false; + emit DefaultAllowanceEnabled(msg.sender); } /** @@ -332,20 +445,24 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IERC20-allowance}. */ function allowance(address _owner, address _spender) external view virtual override returns (uint256) { + if (_defaultAllowances[_spender] && !_defaultAllowanceOptOuts[_owner]) { + return type(uint256).max; + } + return _allowances[_owner][_spender]; } /** * @dev See {IToken-identityRegistry}. */ - function identityRegistry() external view override returns (IIdentityRegistry) { + function identityRegistry() external view override returns (IERC3643IdentityRegistry) { return _tokenIdentityRegistry; } /** * @dev See {IToken-compliance}. */ - function compliance() external view override returns (IModularCompliance) { + function compliance() external view override returns (IERC3643Compliance) { return _tokenCompliance; } @@ -415,14 +532,16 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @return `true` if successful and revert if unsuccessful */ function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) { - require(!_frozen[_to] && !_frozen[msg.sender], "wallet is frozen"); - require(_amount <= balanceOf(msg.sender) - (_frozenTokens[msg.sender]), "Insufficient Balance"); + require(!_frozen[_to] && !_frozen[msg.sender], FrozenWallet()); + uint256 balance = balanceOf(msg.sender) - _frozenTokens[msg.sender]; + require(_amount <= balance, ERC20InsufficientBalance(msg.sender, balance, _amount)); if (_tokenIdentityRegistry.isVerified(_to) && _tokenCompliance.canTransfer(msg.sender, _to, _amount)) { _transfer(msg.sender, _to, _amount); _tokenCompliance.transferred(msg.sender, _to, _amount); return true; } - revert("Transfer not possible"); + + revert TransferNotPossible(); } /** @@ -433,7 +552,8 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { address _to, uint256 _amount ) public override onlyAgent returns (bool) { - require(balanceOf(_from) >= _amount, "sender balance too low"); + require(!getAgentRestrictions(msg.sender).disableForceTransfer, AgentNotAuthorized(msg.sender, "forceTransfer disabled")); + require(balanceOf(_from) >= _amount, ERC20InsufficientBalance(_from, balanceOf(_from), _amount)); uint256 freeBalance = balanceOf(_from) - (_frozenTokens[_from]); if (_amount > freeBalance) { uint256 tokensToUnfreeze = _amount - (freeBalance); @@ -445,15 +565,16 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { _tokenCompliance.transferred(_from, _to, _amount); return true; } - revert("Transfer not possible"); + revert TransferNotPossible(); } /** * @dev See {IToken-mint}. */ function mint(address _to, uint256 _amount) public override onlyAgent { - require(_tokenIdentityRegistry.isVerified(_to), "Identity is not verified."); - require(_tokenCompliance.canTransfer(address(0), _to, _amount), "Compliance not followed"); + require(!getAgentRestrictions(msg.sender).disableMint, AgentNotAuthorized(msg.sender, "mint disabled")); + require(_tokenIdentityRegistry.isVerified(_to), UnverifiedIdentity()); + require(_tokenCompliance.canTransfer(address(0), _to, _amount), ComplianceNotFollowed()); _mint(_to, _amount); _tokenCompliance.created(_to, _amount); } @@ -462,7 +583,9 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-burn}. */ function burn(address _userAddress, uint256 _amount) public override onlyAgent { - require(balanceOf(_userAddress) >= _amount, "cannot burn more than balance"); + require(!getAgentRestrictions(msg.sender).disableBurn, AgentNotAuthorized(msg.sender, "burn disabled")); + require(balanceOf(_userAddress) >= _amount, + ERC20InsufficientBalance(_userAddress, balanceOf(_userAddress), _amount)); uint256 freeBalance = balanceOf(_userAddress) - _frozenTokens[_userAddress]; if (_amount > freeBalance) { uint256 tokensToUnfreeze = _amount - (freeBalance); @@ -477,6 +600,8 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-setAddressFrozen}. */ function setAddressFrozen(address _userAddress, bool _freeze) public override onlyAgent { + require(!getAgentRestrictions(msg.sender).disableAddressFreeze, + AgentNotAuthorized(msg.sender, "address freeze disabled")); _frozen[_userAddress] = _freeze; emit AddressFrozen(_userAddress, _freeze, msg.sender); @@ -486,8 +611,11 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-freezePartialTokens}. */ function freezePartialTokens(address _userAddress, uint256 _amount) public override onlyAgent { + require(!getAgentRestrictions(msg.sender).disablePartialFreeze, + AgentNotAuthorized(msg.sender, "partial freeze disabled")); uint256 balance = balanceOf(_userAddress); - require(balance >= _frozenTokens[_userAddress] + _amount, "Amount exceeds available balance"); + require(balance >= _frozenTokens[_userAddress] + _amount, + ERC20InsufficientBalance(_userAddress, balance, _amount)); _frozenTokens[_userAddress] = _frozenTokens[_userAddress] + (_amount); emit TokensFrozen(_userAddress, _amount); } @@ -496,7 +624,9 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-unfreezePartialTokens}. */ function unfreezePartialTokens(address _userAddress, uint256 _amount) public override onlyAgent { - require(_frozenTokens[_userAddress] >= _amount, "Amount should be less than or equal to frozen tokens"); + require(!getAgentRestrictions(msg.sender).disablePartialFreeze, + AgentNotAuthorized(msg.sender, "partial freeze disabled")); + require(_frozenTokens[_userAddress] >= _amount, AmountAboveFrozenTokens(_amount, _frozenTokens[_userAddress])); _frozenTokens[_userAddress] = _frozenTokens[_userAddress] - (_amount); emit TokensUnfrozen(_userAddress, _amount); } @@ -505,7 +635,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {IToken-setIdentityRegistry}. */ function setIdentityRegistry(address _identityRegistry) public override onlyOwner { - _tokenIdentityRegistry = IIdentityRegistry(_identityRegistry); + _tokenIdentityRegistry = IERC3643IdentityRegistry(_identityRegistry); emit IdentityRegistryAdded(_identityRegistry); } @@ -516,7 +646,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { if (address(_tokenCompliance) != address(0)) { _tokenCompliance.unbindToken(address(this)); } - _tokenCompliance = IModularCompliance(_compliance); + _tokenCompliance = IERC3643Compliance(_compliance); _tokenCompliance.bindToken(address(this)); emit ComplianceAdded(_compliance); } @@ -528,6 +658,25 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { return _balances[_userAddress]; } + /** + * @dev See {IToken-getAgentRestrictions}. + */ + function getAgentRestrictions(address agent) public view override returns (TokenRoles memory) { + return _agentsRestrictions[agent]; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == type(IERC20).interfaceId || + interfaceId == type(IToken).interfaceId || + interfaceId == type(IERC173).interfaceId || + interfaceId == type(IERC165).interfaceId || + interfaceId == type(IERC3643).interfaceId; + } + /** * @dev See {ERC20-_transfer}. */ @@ -536,8 +685,8 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { address _to, uint256 _amount ) internal virtual { - require(_from != address(0), "ERC20: transfer from the zero address"); - require(_to != address(0), "ERC20: transfer to the zero address"); + require(_from != address(0), ERC20InvalidSpender(_from)); + require(_to != address(0), ERC20InvalidReceiver(_to)); _beforeTokenTransfer(_from, _to, _amount); @@ -550,7 +699,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {ERC20-_mint}. */ function _mint(address _userAddress, uint256 _amount) internal virtual { - require(_userAddress != address(0), "ERC20: mint to the zero address"); + require(_userAddress != address(0), ERC20InvalidReceiver(_userAddress)); _beforeTokenTransfer(address(0), _userAddress, _amount); @@ -563,7 +712,7 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { * @dev See {ERC20-_burn}. */ function _burn(address _userAddress, uint256 _amount) internal virtual { - require(_userAddress != address(0), "ERC20: burn from the zero address"); + require(_userAddress != address(0), ERC20InvalidSpender(_userAddress)); _beforeTokenTransfer(_userAddress, address(0), _amount); @@ -580,8 +729,8 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { address _spender, uint256 _amount ) internal virtual { - require(_owner != address(0), "ERC20: approve from the zero address"); - require(_spender != address(0), "ERC20: approve to the zero address"); + require(_owner != address(0), ERC20InvalidSender(_owner)); + require(_spender != address(0), ERC20InvalidSpender(_spender)); _allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); @@ -592,4 +741,5 @@ contract Token is IToken, AgentRoleUpgradeable, TokenStorage { */ // solhint-disable-next-line no-empty-blocks function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal virtual {} + } diff --git a/contracts/token/TokenStorage.sol b/contracts/token/TokenStorage.sol index a191be8e..a821ea60 100644 --- a/contracts/token/TokenStorage.sol +++ b/contracts/token/TokenStorage.sol @@ -61,9 +61,10 @@ * along with this program. If not, see . */ -pragma solidity 0.8.17; -import "../compliance/modular/IModularCompliance.sol"; -import "../registry/interface/IIdentityRegistry.sol"; +pragma solidity 0.8.27; +import "../ERC-3643/IERC3643Compliance.sol"; +import "../ERC-3643/IERC3643IdentityRegistry.sol"; +import "./TokenStructs.sol"; contract TokenStorage { /// @dev ERC20 basic variables @@ -85,14 +86,20 @@ contract TokenStorage { bool internal _tokenPaused = false; /// @dev Identity Registry contract used by the onchain validator system - IIdentityRegistry internal _tokenIdentityRegistry; + IERC3643IdentityRegistry internal _tokenIdentityRegistry; /// @dev Compliance contract linked to the onchain validator system - IModularCompliance internal _tokenCompliance; + IERC3643Compliance internal _tokenCompliance; + + mapping(address => TokenRoles) internal _agentsRestrictions; + + mapping(address spender => bool allowance) internal _defaultAllowances; + + mapping(address user => bool optOut) internal _defaultAllowanceOptOuts; /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ - uint256[49] private __gap; + uint256[46] private __gap; } diff --git a/contracts/token/TokenStructs.sol b/contracts/token/TokenStructs.sol new file mode 100644 index 00000000..c8d0357a --- /dev/null +++ b/contracts/token/TokenStructs.sol @@ -0,0 +1,75 @@ + +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + + struct TokenRoles { + bool disableMint; + bool disableBurn; + bool disablePartialFreeze; + bool disableAddressFreeze; + bool disableRecovery; + bool disableForceTransfer; + bool disablePause; + } diff --git a/contracts/utilities/IUtilityChecker.sol b/contracts/utilities/IUtilityChecker.sol new file mode 100644 index 00000000..19d1522e --- /dev/null +++ b/contracts/utilities/IUtilityChecker.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import { IClaimIssuer } from "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; + + +interface IUtilityChecker { + + struct ComplianceCheckDetails { + string moduleName; + bool pass; + } + + struct EligibilityCheckDetails { + IClaimIssuer issuer; + uint256 topic; + bool pass; + } + + /// @dev This function verifies if the transfer is restricted due to frozen addresses or tokens. + /// @param _token The address of the token contract. + /// @param _from The address of the sender. + /// @param _to The address of the recipient. + /// @param _amount The amount of tokens to be transferred. + /// @return _frozen bool Returns true if the transfer is affected by freeze conditions, false otherwise. + /// @return _availableBalance uint256 Available unfreezed balance. + function testFreeze(address _token, address _from, address _to, uint256 _amount) + external view returns (bool _frozen, uint256 _availableBalance); + + /// @dev This function performs a comprehensive check on whether a transfer would succeed: + /// - check if token is paused, + /// - check freeze conditions, + /// - check eligibilty, + /// - check compliance. + /// @param _token The address of the token contract. + /// @param _from The address of the sender. + /// @param _to The address of the recipient. + /// @param _amount The amount of tokens to be transferred. + /// @return _freezeStatus bool + /// Returns true if the transfer would be successful according to pause/freeze conditions, false otherwise. + /// @return _eligibilityStatus bool + /// Returns true if the transfer would be successful according to eligibilty conditions, false otherwise. + /// @return _complianceStatus bool + /// Returns true if the transfer would be successful according to compliance conditions, false otherwise. + function testTransfer(address _token, address _from, address _to, uint256 _amount) + external view returns (bool _freezeStatus, bool _eligibilityStatus, bool _complianceStatus); + + /// @dev Check trade validity and return the status of each module for this transfer. + /// @param _token The address of the token contract. + /// @param _from Address of the sender. + /// @param _to Address of the receiver. + /// @param _value Amount of tokens to transfer. + /// @return _details Array of struct with module name and result of the `moduleCheck` call. + function testTransferDetails(address _token, address _from, address _to, uint256 _value) + external view returns (ComplianceCheckDetails [] memory _details); + + + /// @dev This functions checks whether an identity contract corresponding to the provided user address has the required + /// claims or not based on the data fetched from trusted issuers registry and from the claim topics registry. It + /// returns the details of each (issuer, topic). + /// @param _token Address of the token contract. + /// @param _userAddress Address of the user to be verified. + /// @return _details Array of struct with issuer, topic, and the verified status. + function testVerifiedDetails(address _token, address _userAddress) + external view returns (EligibilityCheckDetails [] memory _details); + +} \ No newline at end of file diff --git a/contracts/utilities/UtilityChecker.sol b/contracts/utilities/UtilityChecker.sol new file mode 100644 index 00000000..0a4a1331 --- /dev/null +++ b/contracts/utilities/UtilityChecker.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import { IClaimIssuer, IIdentity } from "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; +import { IERC3643IdentityRegistry } from "../ERC-3643/IERC3643IdentityRegistry.sol"; +import { IERC3643TrustedIssuersRegistry } from "../ERC-3643/IERC3643TrustedIssuersRegistry.sol"; +import { IModularCompliance } from "../compliance/modular/IModularCompliance.sol"; +import { IModule } from "../compliance/modular/modules/IModule.sol"; +import { IToken } from "../token/IToken.sol"; +import { IUtilityChecker } from "./IUtilityChecker.sol"; + + +contract UtilityChecker is IUtilityChecker, OwnableUpgradeable, UUPSUpgradeable { + + function initialize() external initializer { + __Ownable_init(); + } + + /// @inheritdoc IUtilityChecker + /// @dev This function is not gas optimized and should be called only OFF chain. + function testTransfer(address _token, address _from, address _to, uint256 _amount) + external view override returns (bool _freezeStatus, bool _eligibilityStatus, bool _complianceStatus) { + IToken token = IToken(_token); + + _freezeStatus = !token.paused(); + + (bool frozen, ) = testFreeze(_token, _from, _to, _amount); + _freezeStatus = _freezeStatus && !frozen; + + IERC3643IdentityRegistry ir = token.identityRegistry(); + _eligibilityStatus = ir.isVerified(_to); + + ComplianceCheckDetails [] memory details = testTransferDetails(_token, _from, _to, _amount); + for (uint256 i; i < details.length; i++) { + if (!details[i].pass) { + _complianceStatus = false; + break; + } + } + _complianceStatus = true; + } + + /// @inheritdoc IUtilityChecker + function testVerifiedDetails(address _token, address _userAddress) + public view override returns (EligibilityCheckDetails [] memory _details) { + + IERC3643IdentityRegistry identityRegistry = IToken(_token).identityRegistry(); + IERC3643TrustedIssuersRegistry tokenIssuersRegistry = identityRegistry.issuersRegistry(); + IIdentity identity = identityRegistry.identity(_userAddress); + + uint256 foundClaimTopic; + uint256 scheme; + address issuer; + bytes memory sig; + bytes memory data; + uint256 topic; + uint256[] memory requiredClaimTopics = identityRegistry.topicsRegistry().getClaimTopics(); + uint256 topicsCount = requiredClaimTopics.length; + _details = new EligibilityCheckDetails[](topicsCount); + for (uint256 claimTopic; claimTopic < topicsCount; claimTopic++) { + topic = requiredClaimTopics[claimTopic]; + IClaimIssuer[] memory trustedIssuers = + tokenIssuersRegistry.getTrustedIssuersForClaimTopic(topic); + + for (uint256 i; i < trustedIssuers.length; i++) { + bytes32 claimId = keccak256(abi.encode(trustedIssuers[i], topic)); + (foundClaimTopic, scheme, issuer, sig, data, ) = identity.getClaim(claimId); + if (foundClaimTopic == topic) { + bool pass; + try IClaimIssuer(issuer).isClaimValid(identity, topic, sig, data) returns(bool validity) { + pass = validity; + } + catch { + pass = false; + } + + _details[claimTopic] = EligibilityCheckDetails({ + issuer: trustedIssuers[i], + topic: topic, + pass: pass + }); + } + } + } + } + + /// @inheritdoc IUtilityChecker + function testFreeze(address _token, address _from, address _to, uint256 _amount) + public view override returns (bool _frozen, uint256 _availableBalance) { + IToken token = IToken(_token); + + if (token.isFrozen(_from) || token.isFrozen(_to)) { + _availableBalance = 0; + _frozen = true; + } else { + _availableBalance = token.balanceOf(_from) - token.getFrozenTokens(_from); + _frozen = _amount > _availableBalance; + } + } + + /// @inheritdoc IUtilityChecker + function testTransferDetails(address _token, address _from, address _to, uint256 _value) + public view override returns (ComplianceCheckDetails [] memory _details) { + IModularCompliance compliance = IModularCompliance(address(IToken(_token).compliance())); + address[] memory modules = compliance.getModules(); + uint256 length = modules.length; + _details = new ComplianceCheckDetails[](length); + for (uint256 i; i < length; i++) { + IModule module = IModule(modules[i]); + _details[i] = ComplianceCheckDetails({ + moduleName: module.name(), + pass: module.moduleCheck(_from, _to, _value, address(compliance)) + }); + } + } + + // solhint-disable-next-line no-empty-blocks + function _authorizeUpgrade(address /*newImplementation*/) internal view override onlyOwner { } + +} \ No newline at end of file diff --git a/contracts/utilities/UtilityCheckerProxy.sol b/contracts/utilities/UtilityCheckerProxy.sol new file mode 100644 index 00000000..b5695d74 --- /dev/null +++ b/contracts/utilities/UtilityCheckerProxy.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract UtilityCheckerProxy is ERC1967Proxy { + + // solhint-disable-next-line no-empty-blocks + constructor(address implementation, bytes memory _data) ERC1967Proxy(implementation, _data) { } + +} \ No newline at end of file diff --git a/contracts/utils/InterfaceIdCalculator.sol b/contracts/utils/InterfaceIdCalculator.sol new file mode 100644 index 00000000..0f015a1d --- /dev/null +++ b/contracts/utils/InterfaceIdCalculator.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../roles/IERC173.sol"; +import "../token/IToken.sol"; +import "../proxy/authority/ITREXImplementationAuthority.sol"; +import "../proxy/authority/IIAFactory.sol"; +import "../factory/ITREXGateway.sol"; +import "../DVA/IDVATransferManager.sol"; +import "../compliance/modular/modules/IModule.sol"; +import "../compliance/modular/IModularCompliance.sol"; +import "../registry/interface/IClaimTopicsRegistry.sol"; +import "../registry/interface/IIdentityRegistry.sol"; +import "../registry/interface/IIdentityRegistryStorage.sol"; +import "../registry/interface/ITrustedIssuersRegistry.sol"; + + +contract InterfaceIdCalculator { + /** + * @dev Returns the interface ID for the IERC20 interface. + * IERC20 interface ID is 0x36372b07 + */ + function getIERC20InterfaceId() external pure returns (bytes4) { + return type(IERC20).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643 interface. + * IERC3643 interface ID is 0xb97d944c + */ + function getIERC3643InterfaceId() external pure returns (bytes4) { + return type(IERC3643).interfaceId; + } + + /** + * @dev Returns the interface ID for the IToken interface. + * IToken interface ID is 0x5c0cda7e + */ + function getITokenInterfaceId() external pure returns (bytes4) { + return type(IToken).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC173 interface. + * IERC173 interface ID is 0x7f5828d0 + */ + function getIERC173InterfaceId() external pure returns (bytes4) { + return type(IERC173).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC165 interface. + * IERC165 interface ID is 0x01ffc9a7 + */ + function getIERC165InterfaceId() external pure returns (bytes4) { + return type(IERC165).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643ClaimTopicsRegistry interface. + * IERC3643ClaimTopicsRegistry interface ID is 0x10928b13 + */ + function getIERC3643ClaimTopicsRegistryInterfaceId() external pure returns (bytes4) { + return type(IERC3643ClaimTopicsRegistry).interfaceId; + } + + /** + * @dev Returns the interface ID for the IIdentityRegistry interface. + * IIdentityRegistry interface ID is 0xacb7b4db + */ + function getIIdentityRegistryInterfaceId() external pure returns (bytes4) { + return type(IIdentityRegistry).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643IdentityRegistry interface. + * IERC3643IdentityRegistry interface ID is 0x8ff89f73 + */ + function getIERC3643IdentityRegistryInterfaceId() external pure returns (bytes4) { + return type(IERC3643IdentityRegistry).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643IdentityRegistryStorage interface. + * IERC3643IdentityRegistryStorage interface ID is 0x57defe0d + */ + function getIERC3643IdentityRegistryStorageInterfaceId() external pure returns (bytes4) { + return type(IERC3643IdentityRegistryStorage).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643TrustedIssuersRegistry interface. + * IERC3643TrustedIssuersRegistry interface ID is 0xb0f773b8 + */ + function getIERC3643TrustedIssuersRegistryInterfaceId() external pure returns (bytes4) { + return type(IERC3643TrustedIssuersRegistry).interfaceId; + } + + /** + * @dev Returns the interface ID for the ITREXImplementationAuthority interface. + * ITREXImplementationAuthority interface ID is 0x62dd69be + */ + function getITREXImplementationAuthorityInterfaceId() external pure returns (bytes4) { + return type(ITREXImplementationAuthority).interfaceId; + } + + /** + * @dev Returns the interface ID for the IIAFactory interface. + * IIAFactory interface ID is 0x8c76edf0 + */ + function getIIAFactoryInterfaceId() external pure returns (bytes4) { + return type(IIAFactory).interfaceId; + } + + /** + * @dev Returns the interface ID for the ITREXGateway interface. + * ITREXGateway interface ID is 0x80e89461 + */ + function getITREXGatewayInterfaceId() external pure returns (bytes4) { + return type(ITREXGateway).interfaceId; + } + + /** + * @dev Returns the interface ID for the IDVATransferManager interface. + * IDVATransferManager interface ID is 0xb9eabd9b + */ + function getIDVATransferManagerInterfaceId() external pure returns (bytes4) { + return type(IDVATransferManager).interfaceId; + } + + /** + * @dev Returns the interface ID for the IModularCompliance interface. + * IModularCompliance interface ID is 0x4d6b83d6 + */ + function getIModularComplianceInterfaceId() external pure returns (bytes4) { + return type(IModularCompliance).interfaceId; + } + + /** + * @dev Returns the interface ID for the IERC3643Compliance interface. + * IERC3643Compliance interface ID is 0x3144991c + */ + function getIERC3643ComplianceInterfaceId() external pure returns (bytes4) { + return type(IERC3643Compliance).interfaceId; + } + + /** + * @dev Returns the interface ID for the IModule interface. + * IModule interface ID is 0xb795d01e + */ + function getIModuleInterfaceId() external pure returns (bytes4) { + return type(IModule).interfaceId; + } +} diff --git a/contracts/utils/OwnableOnceNext2StepUpgradeable.sol b/contracts/utils/OwnableOnceNext2StepUpgradeable.sol new file mode 100644 index 00000000..aa177ad9 --- /dev/null +++ b/contracts/utils/OwnableOnceNext2StepUpgradeable.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0 +// +// :+#####%%%%%%%%%%%%%%+ +// .-*@@@%+.:+%@@@@@%%#***%@@%= +// :=*%@@@#=. :#@@% *@@@%= +// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- +// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. +// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ +// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- +// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: +// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. +// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. +// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ +// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- +// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: +// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- +// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- +// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# +// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- +// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: +// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: +// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. +// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. +// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. +// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. +// @@@@@@+. +@@*. .+@@@@@%=. +// -@@@@@= =@@%: -#@@@@%+. +// +@@@@@. =@@@= .+@@@@@*: +// #@@@@#:%@@#. :*@@@@#- +// @@@@@%@@@= :#@@@@+. +// :@@@@@@@#.:#@@@%- +// +@@@@@@-.*@@@*: +// #@@@@#.=@@@+. +// @@@@+-%@%= +// :@@@#%@%= +// +@@@@%- +// :#%%= +// + +/** + * NOTICE + * + * The T-REX software is licensed under a proprietary license or the GPL v.3. + * If you choose to receive it under the GPL v.3 license, the following applies: + * T-REX is a suite of smart contracts implementing the ERC-3643 standard and + * developed by Tokeny to manage and transfer financial assets on EVM blockchains + * + * Copyright (C) 2023, Tokeny sàrl. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +pragma solidity 0.8.27; + +import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; + +contract OwnableOnceNext2StepUpgradeable is Ownable2StepUpgradeable { + + struct Ownable2StepsStorage { + bool firstCall; + } + + bytes32 private constant _STORAGE_SLOT = keccak256("ownableOnceNext2StepUpgradeable.storage.firstCall"); + + function transferOwnership(address newOwner) public override onlyOwner { + Ownable2StepsStorage storage s = _getStorage(); + if (s.firstCall) { + s.firstCall = false; + super._transferOwnership(newOwner); + } + else { + super.transferOwnership(newOwner); + } + } + + function _getStorage() internal pure returns (Ownable2StepsStorage storage s) { + bytes32 position = _STORAGE_SLOT; + assembly { + s.slot := position + } + } + +} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index a9e8ab6e..09fc0657 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -8,12 +8,13 @@ import '@primitivefi/hardhat-dodoc'; const config: HardhatUserConfig = { solidity: { - version: '0.8.17', + version: '0.8.27', settings: { optimizer: { enabled: true, runs: 200, }, + viaIR: true, }, }, gasReporter: { @@ -22,7 +23,7 @@ const config: HardhatUserConfig = { dodoc: { runOnCompile: false, debugMode: true, - outputDir: "./docgen", + outputDir: './docgen', freshOutput: true, }, }; diff --git a/index.d.ts b/index.d.ts index 5aa94f57..774d410d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,7 +6,7 @@ type ContractJSON = { bytecode: string; deployedBytecode: string; linkReferences: any; -} +}; export namespace contracts { // Token @@ -52,6 +52,8 @@ export namespace contracts { export const DVDTransferManager: ContractJSON; // DVA export const DVATransferManager: ContractJSON; + + export const DVATransferManagerProxy: ContractJSON; // compliance export const MCStorage: ContractJSON; export const ModularCompliance: ContractJSON; @@ -69,6 +71,9 @@ export namespace contracts { export const SupplyLimitModule: ContractJSON; export const TransferFeesModule: ContractJSON; export const TransferRestrictModule: ContractJSON; + export const TokenListingRestrictionsModule: ContractJSON; + export const InvestorCountryCapModule: ContractJSON; + export const MinTransferByCountrytModule: ContractJSON; } export namespace interfaces { diff --git a/index.js b/index.js index 5d2563ec..a5d7392f 100644 --- a/index.js +++ b/index.js @@ -52,6 +52,7 @@ const TREXGateway = require('./artifacts/contracts/factory/TREXGateway.sol/TREXG const DVDTransferManager = require('./artifacts/contracts/DVD/DVDTransferManager.sol/DVDTransferManager.json'); // DVA const DVATransferManager = require('./artifacts/contracts/DVA/DVATransferManager.sol/DVATransferManager.json'); +const DVATransferManagerProxy = require('./artifacts/contracts/DVA/DVATransferManagerProxy.sol/DVATransferManagerProxy.json'); const IDVATransferManager = require('./artifacts/contracts/DVA/IDVATransferManager.sol/IDVATransferManager.json'); // compliance const IModularCompliance = require('./artifacts/contracts/compliance/modular/IModularCompliance.sol/IModularCompliance.json'); @@ -72,6 +73,9 @@ const TimeTransfersLimitsModule = require('./artifacts/contracts/compliance/modu const SupplyLimitModule = require('./artifacts/contracts/compliance/modular/modules/SupplyLimitModule.sol/SupplyLimitModule.json'); const TransferFeesModule = require('./artifacts/contracts/compliance/modular/modules/TransferFeesModule.sol/TransferFeesModule.json'); const TransferRestrictModule = require('./artifacts/contracts/compliance/modular/modules/TransferRestrictModule.sol/TransferRestrictModule.json'); +const TokenListingRestrictionsModule = require('./artifacts/contracts/compliance/modular/modules/TokenListingRestrictionsModule.sol/TokenListingRestrictionsModule.json'); +const InvestorCountryCapModule = require('./artifacts/contracts/compliance/modular/modules/InvestorCountryCapModule.sol/InvestorCountryCapModule.json'); +const MinTransferByCountrytModule = require('./artifacts/contracts/compliance/modular/modules/MinTransferByCountrytModule.sol/MinTransferByCountrytModule.json'); module.exports = { contracts: { @@ -118,6 +122,7 @@ module.exports = { DVDTransferManager, // DVA DVATransferManager, + DVATransferManagerProxy, // compliance MCStorage, ModularCompliance, @@ -135,6 +140,9 @@ module.exports = { SupplyLimitModule, TransferFeesModule, TransferRestrictModule, + TokenListingRestrictionsModule, + InvestorCountryCapModule, + MinTransferByCountrytModule, }, interfaces: { IToken, diff --git a/package-lock.json b/package-lock.json index 281fef5c..c2b417d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,44 +1,51 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.3", + "version": "4.2.0-beta1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tokenysolutions/t-rex", - "version": "4.1.3", + "version": "4.2.0-beta1", "license": "SEE LICENSE IN LICENSE.md", "devDependencies": { - "@commitlint/cli": "^17.6.1", - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "@nomiclabs/hardhat-solhint": "^3.0.1", - "@onchain-id/solidity": "^2.0.0", + "@commitlint/cli": "^19.3.0", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@nomiclabs/hardhat-solhint": "^3.1.0", + "@onchain-id/solidity": "^2.2.2-beta2", "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "^4.8.3", - "@openzeppelin/hardhat-upgrades": "^1.28.0", + "@openzeppelin/hardhat-upgrades": "^3.1.0", "@primitivefi/hardhat-dodoc": "^0.2.3", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", + "@typescript-eslint/eslint-plugin": "^7.10.0", + "@typescript-eslint/parser": "^7.10.0", "@xyrusworx/hardhat-solidity-json": "^1.0.2", - "eslint": "^8.39.0", + "eslint": "^8.0.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^8.8.0", + "eslint-config-prettier": "^8.0.0", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-security": "^1.7.1", + "eslint-plugin-chai-friendly": "^0.8.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-security": "^3.0.0", "eth-gas-reporter": "^0.2.27", - "fs-extra": "^11.1.1", - "glob": "^10.2.6", - "hardhat": "^2.14.0", - "husky": "^8.0.3", - "lint-staged": "^13.2.2", - "prettier": "^2.8.8", - "prettier-plugin-solidity": "^1.1.3", - "solhint": "^3.4.1", - "solhint-plugin-prettier": "^0.0.5" - } + "fs-extra": "^11.2.0", + "glob": "^10.4.1", + "hardhat": "^2.22.8", + "husky": "^9.0.11", + "lint-staged": "^15.2.5", + "prettier": "^3.2.5", + "prettier-plugin-solidity": "^1.3.1", + "solhint": "^5.0.1", + "solhint-plugin-prettier": "^0.1.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true, + "peer": true }, "node_modules/@aws-crypto/sha256-js": { "version": "1.2.2", @@ -63,16 +70,16 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.523.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", - "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.577.0.tgz", + "integrity": "sha512-FT2JZES3wBKN/alfmhlo+3ZOq/XJ0C7QOZcDNrpKjB0kqYoKjhVKZ/Hx6ArR0czkKfHzBBEs6y40ebIHx2nSmA==", "dev": true, "dependencies": { - "@smithy/types": "^2.10.1", - "tslib": "^2.5.0" + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/types/node_modules/tslib": { @@ -97,35 +104,37 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.14.5" + "@babel/highlight": "^7.24.6", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -157,25 +166,19 @@ "node": ">=4" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -193,134 +196,47 @@ "node": ">=4" } }, - "node_modules/@chainsafe/as-sha256": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", - "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", - "dev": true - }, - "node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", - "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "node_modules/@chainsafe/ssz": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", - "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", + "node_modules/@commitlint/cli": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", + "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", "dev": true, "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.4.2", - "case": "^1.6.3" - } - }, - "node_modules/@commitlint/cli": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.2.tgz", - "integrity": "sha512-t3N7TZq7lOeqTOyEgfGcaltHqEJf7YDlPg75MldeVPPyz14jZq/+mbGF9tueDLFX8R6RwdymrN6D+U5XwZ8Iwg==", - "dev": true, - "dependencies": { - "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.7.0", - "@commitlint/load": "^17.7.2", - "@commitlint/read": "^17.5.1", - "@commitlint/types": "^17.4.4", - "execa": "^5.0.0", - "lodash.isfunction": "^3.0.9", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", + "@commitlint/format": "^19.3.0", + "@commitlint/lint": "^19.2.2", + "@commitlint/load": "^19.2.0", + "@commitlint/read": "^19.2.1", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", "yargs": "^17.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { - "node": ">=v14" - } - }, - "node_modules/@commitlint/cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@commitlint/cli/node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@commitlint/cli/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">=v18" } }, "node_modules/@commitlint/config-validator": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz", - "integrity": "sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.3.tgz", + "integrity": "sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^19.0.3", "ajv": "^8.11.0" }, "engines": { - "node": ">=v14" - } - }, - "node_modules/@commitlint/config-validator/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "node": ">=v18" } }, - "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/@commitlint/ensure": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.6.7.tgz", - "integrity": "sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.3.tgz", + "integrity": "sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^19.0.3", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -328,193 +244,184 @@ "lodash.upperfirst": "^4.3.1" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/execute-rule": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", - "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", + "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/format": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.4.tgz", - "integrity": "sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", + "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", - "chalk": "^4.1.0" + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/is-ignored": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", - "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz", + "integrity": "sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", - "semver": "7.5.4" + "@commitlint/types": "^19.0.3", + "semver": "^7.6.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/lint": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", - "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", + "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.7.0", - "@commitlint/parse": "^17.7.0", - "@commitlint/rules": "^17.7.0", - "@commitlint/types": "^17.4.4" + "@commitlint/is-ignored": "^19.2.2", + "@commitlint/parse": "^19.0.3", + "@commitlint/rules": "^19.0.3", + "@commitlint/types": "^19.0.3" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.2.tgz", - "integrity": "sha512-XA7WTnsjHZ4YH6ZYsrnxgLdXzriwMMq+utZUET6spbOEEIPBCDLdOQXS26P+v3TTO4hUHOEhzUquaBv3jbBixw==", - "dev": true, - "dependencies": { - "@commitlint/config-validator": "^17.6.7", - "@commitlint/execute-rule": "^17.4.0", - "@commitlint/resolve-extends": "^17.6.7", - "@commitlint/types": "^17.4.4", - "@types/node": "20.5.1", - "chalk": "^4.1.0", - "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.2.0.tgz", + "integrity": "sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.1.0", + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "lodash.uniq": "^4.5.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, - "node_modules/@commitlint/load/node_modules/@types/node": { - "version": "20.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "dev": true - }, "node_modules/@commitlint/message": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", - "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", + "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/parse": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", - "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.3.tgz", + "integrity": "sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^6.0.0", - "conventional-commits-parser": "^4.0.0" + "@commitlint/types": "^19.0.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/read": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.5.1.tgz", - "integrity": "sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.2.1.tgz", + "integrity": "sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==", "dev": true, "dependencies": { - "@commitlint/top-level": "^17.4.0", - "@commitlint/types": "^17.4.4", - "fs-extra": "^11.0.0", - "git-raw-commits": "^2.0.11", - "minimist": "^1.2.6" + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/resolve-extends": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz", - "integrity": "sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz", + "integrity": "sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.6.7", - "@commitlint/types": "^17.4.4", - "import-fresh": "^3.0.0", + "@commitlint/config-validator": "^19.0.3", + "@commitlint/types": "^19.0.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/rules": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", - "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", + "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", "dev": true, "dependencies": { - "@commitlint/ensure": "^17.6.7", - "@commitlint/message": "^17.4.2", - "@commitlint/to-lines": "^17.4.0", - "@commitlint/types": "^17.4.4", - "execa": "^5.0.0" + "@commitlint/ensure": "^19.0.3", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/to-lines": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", - "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", + "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/top-level": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", - "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", + "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", "dev": true, "dependencies": { - "find-up": "^5.0.0" + "find-up": "^7.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/types": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz", - "integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.3.tgz", + "integrity": "sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==", "dev": true, "dependencies": { - "chalk": "^4.1.0" + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@cspotcode/source-map-support": { @@ -522,6 +429,7 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -545,23 +453,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.1", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -576,24 +484,38 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -607,14 +529,110 @@ } }, "node_modules/@eslint/js": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", - "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -773,12 +791,6 @@ "bn.js": "^5.2.1" } }, - "node_modules/@ethersproject/bignumber/node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, "node_modules/@ethersproject/bytes": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", @@ -933,6 +945,12 @@ "scrypt-js": "3.0.1" } }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + }, "node_modules/@ethersproject/keccak256": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", @@ -953,12 +971,6 @@ "js-sha3": "0.8.0" } }, - "node_modules/@ethersproject/keccak256/node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true - }, "node_modules/@ethersproject/logger": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", @@ -1071,12 +1083,6 @@ "ws": "7.4.6" } }, - "node_modules/@ethersproject/providers/node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "dev": true - }, "node_modules/@ethersproject/providers/node_modules/ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", @@ -1183,12 +1189,6 @@ "hash.js": "1.1.7" } }, - "node_modules/@ethersproject/signing-key/node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, "node_modules/@ethersproject/solidity": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", @@ -1361,20 +1361,39 @@ "@ethersproject/strings": "^5.7.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1401,9 +1420,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1435,18 +1454,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1503,25 +1510,28 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "peer": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "peer": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1543,6 +1553,21 @@ "node": ">=12.0.0" } }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/@metamask/eth-sig-util/node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", @@ -1581,11 +1606,31 @@ "rlp": "^2.2.3" } }, - "node_modules/@metamask/eth-sig-util/node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@noble/hashes": { "version": "1.2.0", @@ -1646,228 +1691,132 @@ "node": ">= 8" } }, - "node_modules/@nomicfoundation/ethereumjs-block": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz", - "integrity": "sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw==", + "node_modules/@nomicfoundation/edr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", + "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", "dev": true, "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1" + "@nomicfoundation/edr-darwin-arm64": "0.5.2", + "@nomicfoundation/edr-darwin-x64": "0.5.2", + "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", + "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-x64-musl": "0.5.2", + "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-block/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-blockchain": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz", - "integrity": "sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A==", + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", + "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-ethash": "3.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "level": "^8.0.0", - "lru-cache": "^5.1.1", - "memory-level": "^1.0.0" - }, "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-blockchain/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", + "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-blockchain/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", + "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-blockchain/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/@nomicfoundation/ethereumjs-common": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz", - "integrity": "sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g==", + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", + "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-util": "9.0.1", - "crc-32": "^1.2.0" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-ethash": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz", - "integrity": "sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w==", + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", + "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "abstract-level": "^1.0.3", - "bigint-crypto-utils": "^3.0.23", - "ethereum-cryptography": "0.1.3" - }, "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-ethash/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", + "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-evm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz", - "integrity": "sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ==", + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", + "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", "dev": true, - "dependencies": { - "@ethersproject/providers": "^5.7.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-evm/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", "dev": true, "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "@nomicfoundation/ethereumjs-util": "9.0.4" } }, "node_modules/@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz", - "integrity": "sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", "dev": true, "bin": { - "rlp": "bin/rlp" + "rlp": "bin/rlp.cjs" }, "engines": { - "node": ">=14" + "node": ">=18" } }, - "node_modules/@nomicfoundation/ethereumjs-statemanager": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz", - "integrity": "sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ==", + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", "dev": true, "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1", - "js-sdsl": "^4.1.4" + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, - "node_modules/@nomicfoundation/ethereumjs-statemanager/node_modules/ethereum-cryptography": { + "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", @@ -1890,23 +1839,28 @@ "setimmediate": "^1.0.5" } }, - "node_modules/@nomicfoundation/ethereumjs-trie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz", - "integrity": "sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA==", + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", "dev": true, "dependencies": { - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@types/readable-stream": "^2.3.13", - "ethereum-cryptography": "0.1.3", - "readable-stream": "^3.6.0" + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" }, "engines": { - "node": ">=14" + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, - "node_modules/@nomicfoundation/ethereumjs-trie/node_modules/ethereum-cryptography": { + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", @@ -1929,187 +1883,158 @@ "setimmediate": "^1.0.5" } }, - "node_modules/@nomicfoundation/ethereumjs-trie/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.6.tgz", + "integrity": "sha512-Te1Uyo9oJcTCF0Jy9dztaLpshmlpjLf2yPtWXlXuLjMt3RRSmJLm/+rKVTW6gfadAEs12U/it6D0ZRnnRGiICQ==", "dev": true, + "peer": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" }, - "engines": { - "node": ">= 6" + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "chai": "^4.2.0", + "ethers": "^6.1.0", + "hardhat": "^2.9.4" } }, - "node_modules/@nomicfoundation/ethereumjs-tx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz", - "integrity": "sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w==", + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.6.tgz", + "integrity": "sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==", "dev": true, + "peer": true, "dependencies": { - "@chainsafe/ssz": "^0.9.2", - "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "ethereum-cryptography": "0.1.3" + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" }, - "engines": { - "node": ">=14" + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" } }, - "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/hardhat-ignition": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.4.tgz", + "integrity": "sha512-x1lhLN9ZRSJ9eiNY9AoinMdeQeU4LDQSQOIw90W9DiZIG/g9YUzcTEIY58QTi2TZOF8YFiF6vJqLSePCpi8R1Q==", "dev": true, + "peer": true, "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "@nomicfoundation/ignition-core": "^0.15.4", + "@nomicfoundation/ignition-ui": "^0.15.4", + "chalk": "^4.0.0", + "debug": "^4.3.2", + "fs-extra": "^10.0.0", + "prompts": "^2.4.2" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-verify": "^2.0.1", + "hardhat": "^2.18.0" } }, - "node_modules/@nomicfoundation/ethereumjs-util": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz", - "integrity": "sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA==", + "node_modules/@nomicfoundation/hardhat-ignition-ethers": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.4.tgz", + "integrity": "sha512-vY30V4b788GSziW/nOd0L/4IPw6mwpluahLs4+gPUUKWaHHGMA8OIeHaYpRRljM1i0M/Kg1yIozrDM/aeRebkg==", + "dev": true, + "peer": true, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.4", + "@nomicfoundation/hardhat-ignition": "^0.15.4", + "@nomicfoundation/ignition-core": "^0.15.4", + "ethers": "^6.7.0", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "dependencies": { - "@chainsafe/ssz": "^0.10.0", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "ethereum-cryptography": "0.1.3" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=14" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz", - "integrity": "sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==", + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "peer": true, "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/ssz": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.10.2.tgz", - "integrity": "sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==", + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "peer": true, "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.5.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, + "peer": true + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/@nomicfoundation/ethereumjs-vm": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz", - "integrity": "sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-vm/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "node": ">=12" } }, - "node_modules/@nomicfoundation/hardhat-chai-matchers": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz", - "integrity": "sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ==", + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "peer": true, "dependencies": { - "@ethersproject/abi": "^5.1.2", - "@types/chai-as-promised": "^7.1.3", - "chai-as-promised": "^7.1.1", - "deep-eql": "^4.0.1", - "ordinal": "^1.0.3" + "has-flag": "^4.0.0" }, - "peerDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.0", - "chai": "^4.2.0", - "ethers": "^5.0.0", - "hardhat": "^2.9.4" + "engines": { + "node": ">=8" } }, "node_modules/@nomicfoundation/hardhat-network-helpers": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.9.tgz", - "integrity": "sha512-OXWCv0cHpwLUO2u7bFxBna6dQtCC2Gg/aN/KtJLO7gmuuA28vgmVKYFRCDUqrbjujzgfwQ2aKyZ9Y3vSmDqS7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz", + "integrity": "sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ==", "dev": true, "peer": true, "dependencies": { @@ -2120,32 +2045,200 @@ } }, "node_modules/@nomicfoundation/hardhat-toolbox": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz", - "integrity": "sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-5.0.0.tgz", + "integrity": "sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==", "dev": true, "peerDependencies": { - "@ethersproject/abi": "^5.4.7", - "@ethersproject/providers": "^5.4.7", - "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomiclabs/hardhat-ethers": "^2.0.0", - "@nomiclabs/hardhat-etherscan": "^3.0.0", - "@typechain/ethers-v5": "^10.1.0", - "@typechain/hardhat": "^6.1.2", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", - "@types/node": ">=12.0.0", + "@types/node": ">=18.0.0", "chai": "^4.2.0", - "ethers": "^5.4.7", + "ethers": "^6.4.0", "hardhat": "^2.11.0", "hardhat-gas-reporter": "^1.0.8", "solidity-coverage": "^0.8.1", "ts-node": ">=8.0.0", - "typechain": "^8.1.0", + "typechain": "^8.3.0", "typescript": ">=4.5.0" } }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.7.tgz", + "integrity": "sha512-jiYHBX+K6bBN0YhwFHQ5SWWc3dQZliM3pdgpH33C7tnsVACsX1ubZn6gZ9hfwlzG0tyjFM72XQhpaXQ56cE6Ew==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@ethersproject/address": "^5.0.2", + "cbor": "^8.1.0", + "chalk": "^2.4.2", + "debug": "^4.1.1", + "lodash.clonedeep": "^4.5.0", + "semver": "^6.3.0", + "table": "^6.8.0", + "undici": "^5.14.0" + }, + "peerDependencies": { + "hardhat": "^2.0.4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/ignition-core": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.4.tgz", + "integrity": "sha512-i379lH+xOLFdaDv0KiNma550ZXCHc5ZkmKYhM44xyLMKBlvX6skUVFkgUjjN1gvprgOIxc17GVQXlR1R5FhGZA==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/address": "5.6.1", + "@nomicfoundation/solidity-analyzer": "^0.1.1", + "cbor": "^9.0.0", + "debug": "^4.3.2", + "ethers": "^6.7.0", + "fs-extra": "^10.0.0", + "immer": "10.0.2", + "lodash": "4.17.21", + "ndjson": "2.0.0" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/@ethersproject/address": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", + "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.6.2", + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.1" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@nomicfoundation/ignition-ui": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.4.tgz", + "integrity": "sha512-cHbmuxmhso5n2zdIaaIW4p8NNzrFj0mrnv8ufhAZfM3s3IFrRoGc1zo8hI/n1CiOTPuqUbdZcB79d+2tCKtCNw==", + "dev": true, + "peer": true + }, "node_modules/@nomicfoundation/solidity-analyzer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", @@ -2327,186 +2420,308 @@ "node": ">= 10" } }, - "node_modules/@nomiclabs/hardhat-ethers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz", - "integrity": "sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==", + "node_modules/@nomiclabs/hardhat-solhint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-solhint/-/hardhat-solhint-3.1.0.tgz", + "integrity": "sha512-5jNiYwLuiHZ2B11Ds4U5jH+DR565PqpsdbXml6iYfqMguyJb+ulU2rt58+hprNhoFKZds8cOlf9FaoWvA9KqkA==", "dev": true, - "peer": true, + "dependencies": { + "solhint": "^3.4.0" + }, "peerDependencies": { - "ethers": "^5.0.0", "hardhat": "^2.0.0" } }, - "node_modules/@nomiclabs/hardhat-etherscan": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.7.tgz", - "integrity": "sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", "dev": true, - "peer": true, "dependencies": { - "@ethersproject/abi": "^5.1.2", - "@ethersproject/address": "^5.0.2", - "cbor": "^8.1.0", - "chalk": "^2.4.2", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash": "^4.17.11", - "semver": "^6.3.0", - "table": "^6.8.0", - "undici": "^5.14.0" - }, - "peerDependencies": { - "hardhat": "^2.0.4" + "antlr4ts": "^0.5.0-alpha.4" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "peer": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=7.0.0" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nomiclabs/hardhat-solhint/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "peer": true, "engines": { - "node": ">=4" + "node": ">=14" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "peer": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@nomiclabs/hardhat-solhint/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "peer": true, "dependencies": { - "has-flag": "^3.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/@nomiclabs/hardhat-etherscan/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "peer": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@nomiclabs/hardhat-solhint": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-solhint/-/hardhat-solhint-3.0.1.tgz", - "integrity": "sha512-GqDoStxL1aA9hZul9HRdk+3eEZ/XLBwvIlz3/EJUusUsoadNYEnio9aYOsBeNeZWo+B/Fd3EgMXgbf9wWQLyRA==", + "node_modules/@nomiclabs/hardhat-solhint/node_modules/solhint": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.6.2.tgz", + "integrity": "sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==", "dev": true, "dependencies": { - "solhint": "^3.4.0" + "@solidity-parser/parser": "^0.16.0", + "ajv": "^6.12.6", + "antlr4": "^4.11.0", + "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", + "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" }, - "peerDependencies": { - "hardhat": "^2.0.0" + "bin": { + "solhint": "solhint.js" + }, + "optionalDependencies": { + "prettier": "^2.8.3" + } + }, + "node_modules/@nomiclabs/hardhat-solhint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/@onchain-id/solidity": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@onchain-id/solidity/-/solidity-2.1.0.tgz", - "integrity": "sha512-BBpBmUs8gg99N5NIKzANbs3R6DUhd90z2GEFvScVBhpHvpQhI5IzPNFRP6cXua1Lo0dWdNffxsteB3eUY1XMZw==", - "dev": true + "version": "2.2.2-beta2", + "resolved": "https://registry.npmjs.org/@onchain-id/solidity/-/solidity-2.2.2-beta2.tgz", + "integrity": "sha512-g/T9bHIvXhdQKMsUDhRcyJ6y/qfXo2Pt/CmxizGzeXvqn9mLclxxnu5wJ/lKsPhswKPxpUaPD+i+TgKhKLi/gA==", + "dev": true, + "license": "ISC" }, "node_modules/@openzeppelin/contracts": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.3.tgz", - "integrity": "sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.6.tgz", + "integrity": "sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==", "dev": true }, "node_modules/@openzeppelin/contracts-upgradeable": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.3.tgz", - "integrity": "sha512-jjaHAVRMrE4UuZNfDwjlLGDxTHWIOwTJS2ldnc278a0gevfXfPr8hxKEVBGFBE96kl2G3VHDZhUimw/+G3TG2A==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz", + "integrity": "sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA==", "dev": true }, + "node_modules/@openzeppelin/defender-admin-client": { + "version": "1.54.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-admin-client/-/defender-admin-client-1.54.6.tgz", + "integrity": "sha512-P4lxJDySrekWNuPa7FeyW/UmuxnuIXIAGYr5gZnmnMHRsYNaw+XfgkiCDfoGtjEyJbXYxXttYF6iAZhWQPdf1g==", + "dev": true, + "dependencies": { + "@openzeppelin/defender-base-client": "1.54.6", + "axios": "^1.4.0", + "ethers": "^5.7.2", + "lodash": "^4.17.19", + "node-fetch": "^2.6.0" + } + }, + "node_modules/@openzeppelin/defender-admin-client/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, "node_modules/@openzeppelin/defender-base-client": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-base-client/-/defender-base-client-1.54.1.tgz", - "integrity": "sha512-DRGz/7KN3ZQwu28YWMOaojrC7jjPkz/uCwkC8/C8B11qwZhA5qIVvyhYHhhFOCl0J84+E3TNdvkPD2q3p2WaJw==", + "version": "1.54.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-base-client/-/defender-base-client-1.54.6.tgz", + "integrity": "sha512-PTef+rMxkM5VQ7sLwLKSjp2DBakYQd661ZJiSRywx+q/nIpm3B/HYGcz5wPZCA5O/QcEP6TatXXDoeMwimbcnw==", "dev": true, "dependencies": { "amazon-cognito-identity-js": "^6.0.1", @@ -2516,61 +2731,145 @@ "node-fetch": "^2.6.0" } }, + "node_modules/@openzeppelin/defender-sdk-base-client": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-1.13.1.tgz", + "integrity": "sha512-FI7YdfgDf0px+cXbXyDkS0mpqzyySHeLkKj90ymzAy1/sGYKHNC03vyzMnMfIRuxa4bF15wdL4MCpA60PSWGpQ==", + "dev": true, + "dependencies": { + "amazon-cognito-identity-js": "^6.3.6", + "async-retry": "^1.3.3" + } + }, + "node_modules/@openzeppelin/defender-sdk-deploy-client": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-1.13.1.tgz", + "integrity": "sha512-zQEoURBRMknrOXLDNzK3gXiHfQbDImLKtEVPBOybya/MYqququBdNkRmPpSkJ45LHVbuxWyqRkkGFQp8+l/UQg==", + "dev": true, + "dependencies": { + "@openzeppelin/defender-sdk-base-client": "^1.13.1", + "axios": "^1.6.7", + "lodash": "^4.17.21" + } + }, + "node_modules/@openzeppelin/defender-sdk-network-client": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-network-client/-/defender-sdk-network-client-1.13.1.tgz", + "integrity": "sha512-QR9dTZ6MuJ5o+GwAKH4Hxy+xuElI0iCwpVqN/ntHG0Ar7neHoq/f7jPtk04/DgEmrwTUMLzTKpZWmUO1fxiEMA==", + "dev": true, + "dependencies": { + "@openzeppelin/defender-sdk-base-client": "^1.13.1", + "axios": "^1.6.7", + "lodash": "^4.17.21" + } + }, "node_modules/@openzeppelin/hardhat-upgrades": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.28.0.tgz", - "integrity": "sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.1.0.tgz", + "integrity": "sha512-CQ5Cg2kE8WeW6qajUTacBsmkntiAwJd7f6p+BUtd1fEvEv7si4H2lmAqvjOjkFc9ihIEQxMBy50IsBXSZGktmg==", "dev": true, "dependencies": { - "@openzeppelin/defender-base-client": "^1.46.0", - "@openzeppelin/platform-deploy-client": "^0.8.0", - "@openzeppelin/upgrades-core": "^1.27.0", + "@openzeppelin/defender-admin-client": "^1.52.0", + "@openzeppelin/defender-base-client": "^1.52.0", + "@openzeppelin/defender-sdk-base-client": "^1.10.0", + "@openzeppelin/defender-sdk-deploy-client": "^1.10.0", + "@openzeppelin/defender-sdk-network-client": "^1.10.0", + "@openzeppelin/upgrades-core": "^1.32.0", "chalk": "^4.1.0", "debug": "^4.1.1", - "proper-lockfile": "^4.1.1" + "ethereumjs-util": "^7.1.5", + "proper-lockfile": "^4.1.1", + "undici": "^6.11.1" }, "bin": { "migrate-oz-cli-project": "dist/scripts/migrate-oz-cli-project.js" }, "peerDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.0", - "@nomiclabs/hardhat-etherscan": "^3.1.0", - "ethers": "^5.0.5", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "ethers": "^6.6.0", "hardhat": "^2.0.2" }, "peerDependenciesMeta": { - "@nomiclabs/harhdat-etherscan": { + "@nomicfoundation/hardhat-verify": { "optional": true } } }, - "node_modules/@openzeppelin/platform-deploy-client": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/platform-deploy-client/-/platform-deploy-client-0.8.0.tgz", - "integrity": "sha512-POx3AsnKwKSV/ZLOU/gheksj0Lq7Is1q2F3pKmcFjGZiibf+4kjGxr4eSMrT+2qgKYZQH1ZLQZ+SkbguD8fTvA==", - "deprecated": "@openzeppelin/platform-deploy-client is deprecated. Please use @openzeppelin/defender-sdk-deploy-client", + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@ethersproject/abi": "^5.6.3", - "@openzeppelin/defender-base-client": "^1.46.0", - "axios": "^0.21.2", - "lodash": "^4.17.19", - "node-fetch": "^2.6.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@openzeppelin/platform-deploy-client/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/undici": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz", + "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==", "dev": true, - "dependencies": { - "follow-redirects": "^1.14.0" + "engines": { + "node": ">=18.17" } }, "node_modules/@openzeppelin/upgrades-core": { - "version": "1.32.5", - "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.32.5.tgz", - "integrity": "sha512-R0wprsyJ4xWiRW05kaTfZZkRVpG2g0af3/hpjE7t2mX0Eb2n40MQLokTwqIk4LDzpp910JfLSpB0vBuZ6WNPog==", + "version": "1.33.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.33.1.tgz", + "integrity": "sha512-YRxIRhTY1b+j7+NUUu8Uuem5ugxKexEMVd8dBRWNgWeoN1gS1OCrhgUg0ytL+54vzQ+SGWZDfNnzjVuI1Cj1Zw==", "dev": true, "dependencies": { "cbor": "^9.0.0", @@ -2586,6 +2885,21 @@ "openzeppelin-upgrades-core": "dist/cli/cli.js" } }, + "node_modules/@openzeppelin/upgrades-core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/@openzeppelin/upgrades-core/node_modules/cbor": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", @@ -2598,6 +2912,52 @@ "node": ">=16" } }, + "node_modules/@openzeppelin/upgrades-core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2608,6 +2968,71 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "dev": true, + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@prettier/sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.3.0.tgz", + "integrity": "sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==", + "dev": true, + "funding": { + "url": "https://github.com/prettier/prettier-synchronized?sponsor=1" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, "node_modules/@primitivefi/hardhat-dodoc": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@primitivefi/hardhat-dodoc/-/hardhat-dodoc-0.2.3.tgz", @@ -2622,16 +3047,13 @@ } }, "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@scure/bip32": { "version": "1.1.5", @@ -2730,15 +3152,6 @@ "node": ">=6" } }, - "node_modules/@sentry/node/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@sentry/tracing": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", @@ -2777,16 +3190,28 @@ "node": ">=6" } }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@smithy/types": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.11.0.tgz", - "integrity": "sha512-AR0SXO7FuAskfNhyGfSTThpLRntDI5bOrU0xrpVYU0rZyjl3LBXInZFMTP/NNSd7IS6Ksdtar0QvnrPRIhVrLQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.0.0.tgz", + "integrity": "sha512-VvWuQk2RKFuOr98gFhjca7fkBS+xLLURT8bUjk5XQoV0ZLm7WPwWPPY3/AwzTLuUBDeoKDCthfe1AsTUWaSEhw==", "dev": true, "dependencies": { - "tslib": "^2.5.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/types/node_modules/tslib": { @@ -2796,42 +3221,58 @@ "dev": true }, "node_modules/@solidity-parser/parser": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz", - "integrity": "sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", "dev": true, "dependencies": { "antlr4ts": "^0.5.0-alpha.4" } }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "peer": true }, "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "peer": true }, "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "peer": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "peer": true }, - "node_modules/@typechain/ethers-v5": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz", - "integrity": "sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A==", + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", "dev": true, "peer": true, "dependencies": { @@ -2839,29 +3280,25 @@ "ts-essentials": "^7.0.1" }, "peerDependencies": { - "@ethersproject/abi": "^5.0.0", - "@ethersproject/providers": "^5.0.0", - "ethers": "^5.1.3", - "typechain": "^8.1.1", - "typescript": ">=4.3.0" + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" } }, "node_modules/@typechain/hardhat": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.6.tgz", - "integrity": "sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", "dev": true, "peer": true, "dependencies": { "fs-extra": "^9.1.0" }, "peerDependencies": { - "@ethersproject/abi": "^5.4.7", - "@ethersproject/providers": "^5.4.7", - "@typechain/ethers-v5": "^10.2.1", - "ethers": "^5.4.7", + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", "hardhat": "^2.9.9", - "typechain": "^8.1.1" + "typechain": "^8.3.2" } }, "node_modules/@typechain/hardhat/node_modules/fs-extra": { @@ -2881,25 +3318,25 @@ } }, "node_modules/@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true, "peer": true }, "node_modules/@types/chai-as-promised": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.6.tgz", - "integrity": "sha512-cQLhk8fFarRVZAXUQV1xEnZgMoPxqKojBvRkqPCKPQCzEhpbbSKl1Uu75kDng7k5Ln6LQLUmNBjLlFthCgm1NA==", + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", "dev": true, "peer": true, "dependencies": { @@ -2915,6 +3352,15 @@ "@types/node": "*" } }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", @@ -2925,9 +3371,9 @@ } }, "node_modules/@types/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, "peer": true, "dependencies": { @@ -2935,10 +3381,10 @@ "@types/node": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "node_modules/@types/json5": { @@ -2954,41 +3400,32 @@ "dev": true }, "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true, "peer": true }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true, "peer": true }, "node_modules/@types/node": { - "version": "18.15.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", - "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", "dev": true, "dependencies": { "@types/node": "*" @@ -3002,64 +3439,46 @@ "peer": true }, "node_modules/@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", "dev": true }, - "node_modules/@types/readable-stream": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", - "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, "node_modules/@types/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", "dev": true, "dependencies": { "@types/node": "*" } }, - "node_modules/@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", + "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/type-utils": "7.10.0", + "@typescript-eslint/utils": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3068,26 +3487,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz", + "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3096,16 +3515,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3113,25 +3532,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz", + "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/utils": "7.10.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3140,12 +3559,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3153,21 +3572,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3179,68 +3599,51 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.10.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@xyrusworx/hardhat-solidity-json": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@xyrusworx/hardhat-solidity-json/-/hardhat-solidity-json-1.0.2.tgz", @@ -3253,44 +3656,14 @@ "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true, "peer": true }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-level": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", - "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "catering": "^2.1.0", - "is-buffer": "^2.0.5", - "level-supports": "^4.0.0", - "level-transcoder": "^1.0.1", - "module-error": "^1.0.1", - "queue-microtask": "^1.2.3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3309,22 +3682,13 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, "peer": true, "engines": { - "node": ">= 0.12.0" + "node": ">=0.4.0" } }, "node_modules/adm-zip": { @@ -3337,10 +3701,11 @@ } }, "node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "dev": true + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true, + "peer": true }, "node_modules/agent-base": { "version": "6.0.2", @@ -3368,15 +3733,15 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", "dev": true, "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" + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -3396,21 +3761,10 @@ "js-cookie": "^2.2.1" } }, - "node_modules/amazon-cognito-identity-js/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", "dev": true, "optional": true, "peer": true, @@ -3418,10 +3772,42 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -3464,24 +3850,21 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/antlr4": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.12.0.tgz", - "integrity": "sha512-23iB5IzXJZRZeK9TigzUyrNc9pSmNqAerJRBcNq1ETrmttMWRgaYZzC561IgEO3ygKsDJTYDTozABXa4b/fTQQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.1.tgz", + "integrity": "sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA==", "dev": true, "engines": { "node": ">=16" @@ -3494,9 +3877,9 @@ "dev": true }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -3510,17 +3893,14 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-back": { "version": "3.1.0", @@ -3555,15 +3935,16 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -3593,15 +3974,16 @@ } }, "node_modules/array.prototype.findlast": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.4.tgz", - "integrity": "sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" }, "engines": { @@ -3611,16 +3993,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3629,15 +4013,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -3647,18 +4031,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, - "peer": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3689,15 +4071,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -3717,7 +4090,7 @@ "node_modules/ast-parents": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", - "integrity": "sha1-UI/Q8F0MSHddnszaLhdEIyYejdM=", + "integrity": "sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==", "dev": true }, "node_modules/astral-regex": { @@ -3732,7 +4105,7 @@ "node_modules/async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", "dev": true, "peer": true }, @@ -3748,7 +4121,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "node_modules/at-least-node": { @@ -3777,34 +4150,20 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/base-x": { @@ -3836,22 +4195,22 @@ } ] }, - "node_modules/bigint-crypto-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.2.2.tgz", - "integrity": "sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/blakejs": { @@ -3861,28 +4220,133 @@ "dev": true }, "node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/boxen/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3891,21 +4355,9 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, - "node_modules/browser-level": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", - "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.1", - "module-error": "^1.0.2", - "run-parallel-limit": "^1.1.0" - } - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3947,27 +4399,14 @@ } }, "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "node_modules/buffer-from": { @@ -3976,44 +4415,58 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-to-arraybuffer": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=", - "dev": true, - "peer": true - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true }, - "node_modules/bufferutil": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", - "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, "engines": { - "node": ">=6.14.2" + "node": ">=14.16" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "streamsearch": "^1.1.0" + "@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" }, "engines": { - "node": ">=10.16.0" + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/call-bind": { @@ -4045,55 +4498,23 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/case": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", - "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "node_modules/catering": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", - "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cbor": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", @@ -4108,9 +4529,9 @@ } }, "node_modules/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "peer": true, "dependencies": { @@ -4127,29 +4548,25 @@ } }, "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "peer": true, "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -4178,16 +4595,10 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4200,22 +4611,23 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 6" } }, "node_modules/ci-info": { @@ -4234,23 +4646,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/classic-level": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", - "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.0", - "module-error": "^1.0.1", - "napi-macros": "^2.2.2", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4260,16 +4655,31 @@ "node": ">=6" } }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-table3": { @@ -4288,60 +4698,17 @@ "colors": "^1.1.2" } }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "string-width": "^7.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4360,32 +4727,32 @@ } }, "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -4398,32 +4765,55 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/colorette": { @@ -4457,7 +4847,8 @@ "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/command-line-args": { "version": "5.2.1", @@ -4529,23 +4920,16 @@ "node": ">=4" } }, - "node_modules/command-line-usage/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "peer": true, - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/command-line-usage/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true - }, "node_modules/command-line-usage/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4580,12 +4964,12 @@ } }, "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/compare-func": { @@ -4607,7 +4991,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/concat-stream": { @@ -4625,111 +5009,146 @@ "typedarray": "^0.0.6" } }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/confusing-browser-globals": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", - "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, "node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "dependencies": { - "is-text-path": "^1.0.1", + "is-text-path": "^2.0.0", "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" + "meow": "^12.0.1", + "split2": "^4.0.0" }, "bin": { - "conventional-commits-parser": "cli.js" + "conventional-commits-parser": "cli.mjs" }, "engines": { - "node": ">=14" + "node": ">=16" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" } }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "node_modules/cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz", - "integrity": "sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, "engines": { - "node": ">=v14.21.3" + "node": ">=v16" }, "peerDependencies": { "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", + "cosmiconfig": ">=8.2", "typescript": ">=4" } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -4761,7 +5180,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -4787,18 +5207,72 @@ } }, "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/death": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", - "integrity": "sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg=", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", "dev": true, "peer": true }, @@ -4820,60 +5294,42 @@ } }, "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, - "peer": true, "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "peer": true, - "dependencies": { - "mimic-response": "^1.0.0" + "node": ">=10" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-eql": { @@ -4894,17 +5350,25 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, - "peer": true, "engines": { "node": ">=4.0.0" } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4942,47 +5406,21 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "engines": { "node": ">=0.4.0" } }, - "node_modules/detect-port": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", - "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "peer": true, - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "peer": true, - "dependencies": { - "ms": "2.0.0" + "node": ">= 0.8" } }, - "node_modules/detect-port/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "peer": true - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -5029,13 +5467,6 @@ "node": ">=6.0.0" } }, - "node_modules/dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", - "dev": true, - "peer": true - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -5069,6 +5500,12 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5076,9 +5513,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -5089,12 +5526,13 @@ } }, "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8.6" @@ -5119,17 +5557,21 @@ } }, "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", @@ -5140,10 +5582,11 @@ "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.3", @@ -5154,17 +5597,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -5173,13 +5616,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true, - "peer": true - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -5201,6 +5637,18 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -5242,64 +5690,53 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escodegen": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", "dev": true, "peer": true, "dependencies": { "esprima": "^2.7.1", "estraverse": "^1.9.1", "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/escodegen/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true, - "peer": true, + "optionator": "^0.8.1" + }, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" } }, "node_modules/escodegen/node_modules/estraverse": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", "dev": true, "peer": true, "engines": { @@ -5309,7 +5746,7 @@ "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "peer": true, "dependencies": { @@ -5341,31 +5778,17 @@ "node_modules/escodegen/node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true, "peer": true, "engines": { "node": ">= 0.8.0" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/escodegen/node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "peer": true, "dependencies": { @@ -5376,27 +5799,28 @@ } }, "node_modules/eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", - "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.39.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5404,22 +5828,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -5452,18 +5873,18 @@ } }, "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -5473,14 +5894,14 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -5518,9 +5939,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -5544,9 +5965,9 @@ } }, "node_modules/eslint-plugin-chai-friendly": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz", - "integrity": "sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.8.0.tgz", + "integrity": "sha512-d9W92QHA606QRhxEvPTzGFeZU+CHL5bsyfk7DvaXcxgpfEOny5Pem1G3X2274J1dxStlX/DotuanrQofULhl4g==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5556,26 +5977,28 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -5584,6 +6007,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -5618,48 +6051,60 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } } }, "node_modules/eslint-plugin-security": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", - "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-3.0.0.tgz", + "integrity": "sha512-2Ij7PkmXIF2cKwoVkEgemwoXbOnxg5UfdhdcpNxZwJxC/10dbsdhHISrTyJ/n8DUkt3yiN6P1ywEgcMGjIwHIw==", "dev": true, "dependencies": { "safe-regex": "^2.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -5684,46 +6129,116 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "color-name": "~1.1.4" }, "engines": { - "node": ">=10.13.0" + "node": ">=7.0.0" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/node_modules/minimatch": { @@ -5738,15 +6253,78 @@ "node": "*" } }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5756,9 +6334,9 @@ } }, "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", "dev": true, "peer": true, "bin": { @@ -5766,7 +6344,7 @@ "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/esquery": { @@ -5840,22 +6418,76 @@ } } }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz", + "integrity": "sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw==", "dev": true, "peer": true, "dependencies": { - "js-sha3": "^0.8.0" + "@noble/hashes": "^1.4.0" } }, - "node_modules/ethereum-bloom-filters/node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "dev": true, - "peer": true + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/ethereum-cryptography": { "version": "1.2.0", @@ -5879,6 +6511,21 @@ "ethereumjs-util": "^6.0.0" } }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/ethereumjs-abi/node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", @@ -5918,9 +6565,9 @@ } }, "node_modules/ethereumjs-util": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz", - "integrity": "sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", "dev": true, "dependencies": { "@types/bn.js": "^5.1.0", @@ -5933,21 +6580,6 @@ "node": ">=10.0.0" } }, - "node_modules/ethereumjs-util/node_modules/@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/ethereumjs-util/node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", @@ -5972,57 +6604,65 @@ } }, "node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", "dev": true, "funding": [ { "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + "url": "https://github.com/sponsors/ethers-io/" }, { "type": "individual", "url": "https://www.buymeacoffee.com/ricmoo" } ], + "peer": true, "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true, + "peer": true + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "peer": true + }, "node_modules/ethjs-unit": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", "dev": true, "peer": true, "dependencies": { @@ -6037,7 +6677,7 @@ "node_modules/ethjs-unit/node_modules/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", "dev": true, "peer": true }, @@ -6055,14 +6695,11 @@ "npm": ">=3" } }, - "node_modules/event-target-shim": { + "node_modules/eventemitter3": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "engines": { - "node": ">=6" - } + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true }, "node_modules/evp_bytestokey": { "version": "1.0.3", @@ -6075,23 +6712,23 @@ } }, "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" @@ -6110,15 +6747,15 @@ "dev": true }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -6131,6 +6768,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -6140,13 +6789,13 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz", - "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -6165,9 +6814,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -6190,41 +6839,39 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "peer": true, - "dependencies": { - "is-buffer": "~2.0.3" - }, "bin": { "flat": "cli.js" } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -6232,15 +6879,15 @@ } }, "node_modules/flatted": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.0.tgz", - "integrity": "sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -6282,30 +6929,27 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" } }, "node_modules/fp-ts": { @@ -6315,9 +6959,9 @@ "dev": true }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -6337,21 +6981,19 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -6383,12 +7025,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -6407,6 +7043,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -6446,12 +7094,12 @@ } }, "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6475,9 +7123,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -6528,27 +7176,20 @@ "node": ">=4" } }, - "node_modules/ghost-testrpc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "peer": true, - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/ghost-testrpc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true, - "peer": true - }, "node_modules/ghost-testrpc/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "peer": true, "engines": { @@ -6569,103 +7210,69 @@ } }, "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", "dev": true, "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" }, "bin": { - "git-raw-commits": "cli.js" + "git-raw-commits": "cli.mjs" }, "engines": { - "node": ">=10" + "node": ">=16" } }, "node_modules/glob": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", - "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dev": true, - "peer": true, - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" + "node": ">=10.13.0" } }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "dependencies": { - "ini": "^1.3.4" + "ini": "4.1.1" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/global-modules": { @@ -6696,6 +7303,13 @@ "node": ">=6" } }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "peer": true + }, "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -6710,9 +7324,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -6725,12 +7339,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -6740,69 +7355,72 @@ } }, "node_modules/globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "peer": true, "dependencies": { - "@types/glob": "^7.1.1", "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" + "get-intrinsic": "^1.1.3" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, - "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@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" }, "engines": { - "node": "*" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/got/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/graceful-fs": { @@ -6811,37 +7429,21 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "peer": true, "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -6855,41 +7457,36 @@ "uglify-js": "^3.1.4" } }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "peer": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, "node_modules/hardhat": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.14.0.tgz", - "integrity": "sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ==", + "version": "2.22.8", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.8.tgz", + "integrity": "sha512-hPh2feBGRswkXkoXUFW6NbxgiYtEzp/3uvVFjYROy6fA9LH8BobUyxStlyhSKj4+v1Y23ZoUBOVWL84IcLACrA==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/ethereumjs-block": "5.0.1", - "@nomicfoundation/ethereumjs-blockchain": "7.0.1", - "@nomicfoundation/ethereumjs-common": "4.0.1", - "@nomicfoundation/ethereumjs-evm": "2.0.1", - "@nomicfoundation/ethereumjs-rlp": "5.0.1", - "@nomicfoundation/ethereumjs-statemanager": "2.0.1", - "@nomicfoundation/ethereumjs-trie": "6.0.1", - "@nomicfoundation/ethereumjs-tx": "5.0.1", - "@nomicfoundation/ethereumjs-util": "9.0.1", - "@nomicfoundation/ethereumjs-vm": "7.0.1", + "@nomicfoundation/edr": "^0.5.2", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", "@types/bn.js": "^5.1.0", "@types/lru-cache": "^5.1.0", - "abort-controller": "^3.0.0", "adm-zip": "^0.4.16", "aggregate-error": "^3.0.0", "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", "chalk": "^2.4.2", "chokidar": "^3.4.0", "ci-info": "^2.0.0", @@ -6909,11 +7506,10 @@ "mnemonist": "^0.38.0", "mocha": "^10.0.0", "p-map": "^4.0.0", - "qs": "^6.7.0", "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", - "solc": "0.7.3", + "solc": "0.8.26", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", "tsort": "0.0.1", @@ -6924,9 +7520,6 @@ "bin": { "hardhat": "internal/cli/bootstrap.js" }, - "engines": { - "node": ">=14.0.0" - }, "peerDependencies": { "ts-node": "*", "typescript": "*" @@ -6941,9 +7534,9 @@ } }, "node_modules/hardhat-gas-reporter": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", - "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", + "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", "dev": true, "peer": true, "dependencies": { @@ -6955,15 +7548,6 @@ "hardhat": "^2.0.2" } }, - "node_modules/hardhat/node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/hardhat/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -6976,13 +7560,14 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/hardhat/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "engines": { - "node": ">= 0.8" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/hardhat/node_modules/chalk": { @@ -6999,34 +7584,13 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/hardhat/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/hardhat/node_modules/commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, - "node_modules/hardhat/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/hardhat/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=0.8.0" } }, "node_modules/hardhat/node_modules/find-up": { @@ -7059,6 +7623,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -7084,28 +7649,6 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/hardhat/node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true - }, "node_modules/hardhat/node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -7128,6 +7671,18 @@ "node": ">=4" } }, + "node_modules/hardhat/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/hardhat/node_modules/p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -7152,15 +7707,6 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/hardhat/node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -7170,36 +7716,6 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/qs": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", - "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hardhat/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/hardhat/node_modules/resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -7212,96 +7728,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hardhat/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/hardhat/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, - "node_modules/hardhat/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/hardhat/node_modules/solc": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", - "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", - "dev": true, - "dependencies": { - "command-exists": "^1.2.8", - "commander": "3.0.2", - "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", - "js-sha3": "0.8.0", - "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", - "semver": "^5.5.0", - "tmp": "0.0.33" - }, - "bin": { - "solcjs": "solcjs" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/hardhat/node_modules/solc/node_modules/fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "node_modules/hardhat/node_modules/solc/node_modules/jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/hardhat/node_modules/solc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/hardhat/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/hardhat/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7314,15 +7749,6 @@ "node": ">=4" } }, - "node_modules/hardhat/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/hardhat/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -7332,16 +7758,25 @@ "node": ">= 4.0.0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { - "node": ">= 0.4.0" + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/has-bigints": { @@ -7427,40 +7862,6 @@ "node": ">=4" } }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -7472,9 +7873,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -7502,7 +7903,7 @@ "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, "dependencies": { "hash.js": "^1.0.3", @@ -7510,18 +7911,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/http-basic": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", @@ -7537,6 +7926,28 @@ "node": ">=6.0.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-response-object": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", @@ -7552,6 +7963,19 @@ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "dev": true }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -7566,24 +7990,24 @@ } }, "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=16.17.0" } }, "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true, "bin": { - "husky": "lib/bin.js" + "husky": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" @@ -7622,18 +8046,29 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" } }, + "node_modules/immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "node_modules/import-fresh": { @@ -7661,10 +8096,20 @@ "node": ">=4" } }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -7682,7 +8127,8 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -7696,10 +8142,13 @@ "dev": true }, "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/internal-slot": { "version": "1.0.7", @@ -7753,7 +8202,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-bigint": { @@ -7796,29 +8245,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7832,22 +8258,40 @@ } }, "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, "dependencies": { - "has": "^1.0.3" + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -7858,28 +8302,24 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "dev": true, - "peer": true - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -7895,7 +8335,7 @@ "node_modules/is-hex-prefixed": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", "dev": true, "engines": { "node": ">=6.5.0", @@ -7957,12 +8397,12 @@ } }, "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-regex": { @@ -7997,12 +8437,12 @@ } }, "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8024,12 +8464,12 @@ } }, "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8039,15 +8479,15 @@ } }, "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "dependencies": { - "text-extensions": "^1.0.0" + "text-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-typed-array": { @@ -8092,13 +8532,13 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isomorphic-unfetch": { @@ -8112,9 +8552,9 @@ } }, "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8129,21 +8569,26 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-cookie": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", "dev": true }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -8152,19 +8597,23 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peer": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -8172,21 +8621,28 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "peer": true + }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -8217,9 +8673,9 @@ ] }, "node_modules/jsonschema": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz", - "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", "dev": true, "peer": true, "engines": { @@ -8243,9 +8699,9 @@ } }, "node_modules/keccak": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", - "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8257,18 +8713,13 @@ "node": ">=10.0.0" } }, - "node_modules/keccak/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "json-buffer": "3.0.1" } }, "node_modules/kind-of": { @@ -8276,56 +8727,34 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "peer": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.9" - } - }, - "node_modules/level": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", - "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", - "dev": true, - "dependencies": { - "browser-level": "^1.0.1", - "classic-level": "^1.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/level" + "node": ">=0.10.0" } }, - "node_modules/level-supports": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", - "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, + "peer": true, "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/level-transcoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", - "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", "dev": true, "dependencies": { - "buffer": "^6.0.3", - "module-error": "^1.0.1" + "package-json": "^8.1.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/levn": { @@ -8342,12 +8771,15 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -8357,233 +8789,126 @@ "dev": true }, "node_modules/lint-staged": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz", - "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==", - "dev": true, - "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.5.tgz", + "integrity": "sha512-j+DfX7W9YUvdzEZl3Rk47FhDF6xwDBV5wwsCPw6BwWZVPYJemusQmvb9bRsW23Sqsaa+vRloAWogbK4BUuU2zA==", + "dev": true, + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.4", + "execa": "~8.0.1", + "lilconfig": "~3.1.1", + "listr2": "~8.2.1", + "micromatch": "~4.0.7", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.4.2" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "node_modules/listr2": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", + "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.1", + "wrap-ansi": "^9.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/lint-staged/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/listr2/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8601,11 +8926,19 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "peer": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", @@ -8667,73 +9000,226 @@ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "peer": true, "dependencies": { - "get-func-name": "^2.0.0" + "get-func-name": "^2.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru_map": { @@ -8743,34 +9229,20 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "peer": true }, "node_modules/markdown-table": { "version": "1.1.3", @@ -8778,15 +9250,6 @@ "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", "dev": true }, - "node_modules/mcl-wasm": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", - "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", - "dev": true, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -8798,61 +9261,22 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/memory-level": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", - "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.0", - "functional-red-black-tree": "^1.0.1", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "engines": { "node": ">= 0.10.0" } }, "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16.10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8873,13 +9297,20 @@ "node": ">= 8" } }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "peer": true + }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -8887,62 +9318,48 @@ } }, "node_modules/mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "mime-db": "1.45.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, - "peer": true, - "dependencies": { - "dom-walk": "^0.1.0" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimalistic-assert": { @@ -8954,19 +9371,22 @@ "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -8978,37 +9398,23 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "peer": true, "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" @@ -9024,9 +9430,9 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -9036,13 +9442,12 @@ "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -9057,32 +9462,64 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "balanced-match": "^1.0.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=10" }, @@ -9091,57 +9528,59 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/mocha/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "p-locate": "^5.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mocha/node_modules/minimatch": { @@ -9162,55 +9601,57 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "yocto-queue": "^0.1.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/mocha/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" } }, "node_modules/mocha/node_modules/yargs": { @@ -9231,22 +9672,16 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/module-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", - "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "node_modules/mocha/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ms": { @@ -9255,18 +9690,42 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/napi-macros": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", - "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", - "dev": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/ndjson": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz", + "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==", + "dev": true, + "peer": true, + "dependencies": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.5", + "readable-stream": "^3.6.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "ndjson": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ndjson/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "peer": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -9290,27 +9749,6 @@ "lodash": "^4.17.21" } }, - "node_modules/node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "peer": true, - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "node_modules/node-environment-flags/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9332,9 +9770,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -9354,7 +9792,7 @@ "node_modules/nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", "dev": true, "peer": true, "dependencies": { @@ -9364,21 +9802,6 @@ "nopt": "bin/nopt.js" } }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -9388,22 +9811,49 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/number-to-bn": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", "dev": true, "peer": true, "dependencies": { @@ -9418,14 +9868,14 @@ "node_modules/number-to-bn/node_modules/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", "dev": true, "peer": true }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9468,47 +9918,60 @@ } }, "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "peer": true, "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -9526,31 +9989,31 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -9558,7 +10021,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -9574,52 +10037,47 @@ "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.20" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9641,12 +10099,30 @@ } }, "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "engines": { - "node": ">=6" + "node": ">=4" + } + }, + "node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dev": true, + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parent-module": { @@ -9667,13 +10143,6 @@ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", "dev": true }, - "node_modules/parse-headers": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", - "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==", - "dev": true, - "peer": true - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9693,18 +10162,18 @@ } }, "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9726,30 +10195,21 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -9785,6 +10245,12 @@ "node": ">=0.12" } }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -9809,6 +10275,16 @@ "node": ">=0.10" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -9835,17 +10311,17 @@ "engines": { "node": ">= 0.8.0" } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -9864,40 +10340,27 @@ } }, "node_modules/prettier-plugin-solidity": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", - "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.3.1.tgz", + "integrity": "sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA==", "dev": true, "dependencies": { - "@solidity-parser/parser": "^0.16.0", - "semver": "^7.3.8", - "solidity-comments-extractor": "^0.0.7" + "@solidity-parser/parser": "^0.17.0", + "semver": "^7.5.4", + "solidity-comments-extractor": "^0.0.8" }, "engines": { - "node": ">=12" + "node": ">=16" }, "peerDependencies": { - "prettier": ">=2.3.0 || >=3.0.0-alpha.0" + "prettier": ">=2.3.0" } }, "node_modules/prettier-plugin-solidity/node_modules/@solidity-parser/parser": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", - "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", - "dev": true, - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.6.0" - } + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", + "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", + "dev": true }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -9914,6 +10377,20 @@ "asap": "~2.0.6" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -9934,6 +10411,18 @@ "node": ">= 4" } }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -9941,36 +10430,27 @@ "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, - "peer": true, "dependencies": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "side-channel": "^1.0.6" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/queue-microtask": { @@ -9994,12 +10474,15 @@ ] }, "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/randombytes": { @@ -10011,133 +10494,63 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" + "bin": { + "rc": "cli.js" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/readdirp": { @@ -10155,7 +10568,7 @@ "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "peer": true, "dependencies": { @@ -10166,29 +10579,40 @@ } }, "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dev": true, "peer": true, "dependencies": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, "node_modules/reduce-flatten": { @@ -10202,9 +10626,9 @@ } }, "node_modules/regexp-tree": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", - "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "bin": { "regexp-tree": "bin/regexp-tree" @@ -10228,6 +10652,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/req-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", @@ -10264,7 +10715,7 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -10279,20 +10730,13 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "peer": true - }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -10303,6 +10747,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -10312,18 +10762,6 @@ "node": ">=8" } }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -10333,19 +10771,67 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -10366,15 +10852,16 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -10386,10 +10873,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -10440,12 +10938,6 @@ "rlp": "bin/rlp" } }, - "node_modules/rlp/node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10469,58 +10961,14 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-parallel-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", - "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rustbn.js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", - "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", - "dev": true - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -10538,10 +10986,24 @@ "dev": true }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safe-regex": { "version": "2.1.1", @@ -10601,24 +11063,32 @@ "istanbul": "lib/cli.js" } }, - "node_modules/sc-istanbul/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/sc-istanbul/node_modules/glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { @@ -10635,24 +11105,65 @@ "node_modules/sc-istanbul/node_modules/has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true, "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/sc-istanbul/node_modules/resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", "dev": true, "peer": true }, "node_modules/sc-istanbul/node_modules/supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "peer": true, "dependencies": { @@ -10697,13 +11208,10 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -10711,25 +11219,27 @@ "node": ">=10" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "peer": true + "dependencies": { + "randombytes": "^2.1.0" + } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -10756,6 +11266,12 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -10804,9 +11320,9 @@ } }, "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "peer": true, "dependencies": { @@ -10821,10 +11337,22 @@ "node": ">=4" } }, + "node_modules/shelljs/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/shelljs/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { @@ -10856,57 +11384,41 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/simple-get": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", - "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, - "peer": true, - "dependencies": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } + "peer": true }, "node_modules/slash": { "version": "3.0.0", @@ -10933,39 +11445,57 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", "dev": true, - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "node_modules/solc/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, "node_modules/solhint": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.4.1.tgz", - "integrity": "sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-5.0.1.tgz", + "integrity": "sha512-QeQLS9HGCnIiibt+xiOa/+MuP7BWz9N7C5+Mj9pLHshdkNhuo3AzCpWmjfWVZBUuwIUO3YyCRVIcYLR3YOKGfg==", "dev": true, "dependencies": { - "@solidity-parser/parser": "^0.16.0", + "@solidity-parser/parser": "^0.18.0", "ajv": "^6.12.6", - "antlr4": "^4.11.0", + "antlr4": "^4.13.1-patch-1", "ast-parents": "^0.0.1", "chalk": "^4.1.2", "commander": "^10.0.0", @@ -10974,9 +11504,10 @@ "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": "^6.3.0", + "semver": "^7.5.2", "strip-ansi": "^6.0.1", "table": "^6.8.1", "text-table": "^0.2.0" @@ -10984,51 +11515,135 @@ "bin": { "solhint": "solhint.js" }, - "optionalDependencies": { - "prettier": "^2.8.3" + "optionalDependencies": { + "prettier": "^2.8.3" + } + }, + "node_modules/solhint-plugin-prettier": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.1.0.tgz", + "integrity": "sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==", + "dev": true, + "dependencies": { + "@prettier/sync": "^0.3.0", + "prettier-linter-helpers": "^1.0.0" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-solidity": "^1.0.0" + } + }, + "node_modules/solhint/node_modules/@solidity-parser/parser": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true + }, + "node_modules/solhint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/solhint-plugin-prettier": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz", - "integrity": "sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==", + "node_modules/solhint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "color-convert": "^2.0.1" }, - "peerDependencies": { - "prettier": "^1.15.0 || ^2.0.0", - "prettier-plugin-solidity": "^1.0.0-alpha.14" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/solhint/node_modules/@solidity-parser/parser": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", - "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", + "node_modules/solhint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/solhint/node_modules/argparse": { + "node_modules/solhint/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/solhint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/solhint/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/solhint/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/solhint/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/solhint/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -11044,17 +11659,11 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/solhint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } + "node_modules/solhint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/solhint/node_modules/minimatch": { "version": "5.1.6", @@ -11068,50 +11677,68 @@ "node": ">=10" } }, - "node_modules/solhint/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/solhint/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "optional": true, "bin": { - "semver": "bin/semver.js" + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/solhint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/solidity-ast": { - "version": "0.4.55", - "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.55.tgz", - "integrity": "sha512-qeEU/r/K+V5lrAw8iswf2/yfWAnSGs3WKPHI+zAFKFjX0dIBVXEU/swQ8eJQYHf6PJWUZFO2uWV4V1wEOkeQbA==", + "version": "0.4.56", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.56.tgz", + "integrity": "sha512-HgmsA/Gfklm/M8GFbCX/J1qkVH0spXHgALCNZ8fA8x5X+MFdn/8CP2gr5OVyXjXw6RZTPC/Sxl2RUDQOXyNMeA==", "dev": true, "dependencies": { "array.prototype.findlast": "^1.2.2" } }, "node_modules/solidity-comments-extractor": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", - "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz", + "integrity": "sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==", "dev": true }, "node_modules/solidity-coverage": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz", - "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.12.tgz", + "integrity": "sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw==", "dev": true, "peer": true, "dependencies": { "@ethersproject/abi": "^5.0.9", - "@solidity-parser/parser": "^0.14.1", + "@solidity-parser/parser": "^0.18.0", "chalk": "^2.4.2", "death": "^1.1.0", - "detect-port": "^1.3.0", "difflib": "^0.2.4", "fs-extra": "^8.1.0", "ghost-testrpc": "^0.0.2", "global-modules": "^2.0.0", "globby": "^10.0.1", "jsonschema": "^1.2.4", - "lodash": "^4.17.15", - "mocha": "7.1.2", + "lodash": "^4.17.21", + "mocha": "^10.2.0", "node-emoji": "^1.10.0", "pify": "^4.0.1", "recursive-readdir": "^2.2.2", @@ -11127,345 +11754,150 @@ "hardhat": "^2.11.0" } }, - "node_modules/solidity-coverage/node_modules/ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } + "peer": true }, "node_modules/solidity-coverage/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "peer": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.1" - } - }, - "node_modules/solidity-coverage/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "peer": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/solidity-coverage/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true, - "peer": true - }, - "node_modules/solidity-coverage/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "peer": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/solidity-coverage/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/solidity-coverage/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/solidity-coverage/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/solidity-coverage/node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/solidity-coverage/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/solidity-coverage/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "peer": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/solidity-coverage/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "peer": true, "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/solidity-coverage/node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "node_modules/solidity-coverage/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "peer": true, "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/solidity-coverage/node_modules/mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "peer": true, "dependencies": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">=4" } }, - "node_modules/solidity-coverage/node_modules/mocha/node_modules/supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.8.0" } }, - "node_modules/solidity-coverage/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "peer": true + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } }, - "node_modules/solidity-coverage/node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "node_modules/solidity-coverage/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/solidity-coverage/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/solidity-coverage/node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", "dev": true, "peer": true, "dependencies": { - "p-limit": "^2.0.0" + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/solidity-coverage/node_modules/path-exists": { + "node_modules/solidity-coverage/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "peer": true, "engines": { "node": ">=4" } }, - "node_modules/solidity-coverage/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/solidity-coverage/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "peer": true, - "engines": { - "node": ">=6" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/solidity-coverage/node_modules/readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "node_modules/solidity-coverage/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "peer": true, "dependencies": { - "picomatch": "^2.0.4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/solidity-coverage/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" + "node": "*" } }, "node_modules/solidity-coverage/node_modules/supports-color": { @@ -11491,52 +11923,18 @@ "node": ">= 4.0.0" } }, - "node_modules/solidity-coverage/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/solidity-coverage/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "peer": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/solidity-coverage/node_modules/yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", "dev": true, + "optional": true, "peer": true, "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "amdefine": ">=0.0.4" }, "engines": { - "node": ">=6" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, "node_modules/source-map-support": { @@ -11549,65 +11947,28 @@ "source-map": "^0.6.0" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">= 10.x" } }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, "peer": true }, @@ -11644,38 +12005,28 @@ "node": ">=8" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, - "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { "node": ">=0.6.19" @@ -11689,17 +12040,16 @@ "peer": true }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/string-width-cjs": { @@ -11717,15 +12067,55 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11735,28 +12125,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11790,25 +12183,28 @@ "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", "dev": true, "dependencies": { "is-hex-prefixed": "1.0.0" @@ -11818,18 +12214,6 @@ "npm": ">=3" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -11843,15 +12227,18 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -11889,10 +12276,32 @@ "get-port": "^3.1.0" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -11941,28 +12350,48 @@ "node": ">=8" } }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/table/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "node_modules/table/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/table/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/table/node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -11980,6 +12409,20 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -11990,18 +12433,21 @@ } }, "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, "engines": { - "node": ">=0.10" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/then-request": { @@ -12032,10 +12478,24 @@ "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", "dev": true }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/through2": { @@ -12043,39 +12503,17 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, + "peer": true, "dependencies": { "readable-stream": "3" } }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, + "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -12095,28 +12533,28 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -12138,6 +12576,72 @@ "write-markdown": "dist/write-markdown.js" } }, + "node_modules/ts-command-line-args/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-command-line-args/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-command-line-args/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-command-line-args/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/ts-command-line-args/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ts-essentials": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", @@ -12149,10 +12653,11 @@ } }, "node_modules/ts-node": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", - "integrity": "sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12196,18 +12701,19 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "peer": true, "engines": { "node": ">=0.3.1" } }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } @@ -12221,7 +12727,13 @@ "node_modules/tsort": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", - "integrity": "sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y=", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "dev": true }, "node_modules/tweetnacl-util": { @@ -12265,9 +12777,9 @@ } }, "node_modules/typechain": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.1.tgz", - "integrity": "sha512-fA7clol2IP/56yq6vkMTR+4URF1nGjV82Wx6Rf09EsqD4tkzMAvEaqYxVFCavJm/1xaRga/oD55K+4FtuXwQOQ==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", "dev": true, "peer": true, "dependencies": { @@ -12289,6 +12801,17 @@ "typescript": ">=4.3.0" } }, + "node_modules/typechain/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/typechain/node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -12308,6 +12831,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { @@ -12325,13 +12849,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typechain/node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true, - "peer": true - }, "node_modules/typechain/node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -12342,6 +12859,19 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/typechain/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/typechain/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -12355,6 +12885,22 @@ "node": ">=10" } }, + "node_modules/typechain/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/typechain/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -12419,9 +12965,9 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -12445,16 +12991,17 @@ "dev": true }, "node_modules/typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/typical": { @@ -12468,9 +13015,9 @@ } }, "node_modules/uglify-js": { - "version": "3.12.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.5.tgz", - "integrity": "sha512-SgpgScL4T7Hj/w/GexjnBHi3Ien9WS1Rpfg5y91WXMj9SY997ZCQU76mH4TpLwwfmMvoOU8wiaRkIf6NaH3mtg==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "peer": true, @@ -12497,27 +13044,45 @@ } }, "node_modules/undici": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz", - "integrity": "sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "dependencies": { - "busboy": "^1.6.0" + "@fastify/busboy": "^2.0.0" }, "engines": { - "node": ">=12.18" + "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", "dev": true }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -12526,7 +13091,7 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "engines": { "node": ">= 0.8" @@ -12541,28 +13106,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-set-query": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=", - "dev": true, - "peer": true - }, - "node_modules/utf-8-validate": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", - "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -12573,7 +13116,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/uuid": { @@ -12589,28 +13132,20 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "peer": true }, "node_modules/web3-utils": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.5.3.tgz", - "integrity": "sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", "dev": true, "peer": true, "dependencies": { - "bn.js": "^4.11.9", - "eth-lib": "0.2.8", + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", "ethjs-unit": "0.1.6", "number-to-bn": "1.7.0", "randombytes": "^2.1.0", @@ -12620,16 +13155,72 @@ "node": ">=8.0.0" } }, - "node_modules/web3-utils/node_modules/eth-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", - "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", "dev": true, "peer": true, "dependencies": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" } }, "node_modules/webidl-conversions": { @@ -12679,24 +13270,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true, - "peer": true - }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12705,67 +13289,45 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, - "peer": true, "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "peer": true, + "string-width": "^4.0.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "peer": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "peer": true, "dependencies": { - "ansi-regex": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12774,7 +13336,7 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, "peer": true }, @@ -12793,395 +13355,289 @@ } }, "node_modules/wordwrapjs/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/ws": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", - "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", - "dev": true, - "peer": true, - "dependencies": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/xhr-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", - "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "dev": true, - "peer": true, - "dependencies": { - "buffer-to-arraybuffer": "^0.0.5", - "object-assign": "^4.1.1", - "query-string": "^5.0.1", - "simple-get": "^2.7.0", - "timed-out": "^4.0.1", - "url-set-query": "^1.0.0", - "xhr": "^2.0.4" - } - }, - "node_modules/xhr-request-promise": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", - "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", - "dev": true, - "peer": true, - "dependencies": { - "xhr-request": "^1.1.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true, - "peer": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "peer": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", "dev": true, + "peer": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yargs-unparser/node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "bin": { - "flat": "cli.js" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/yargs/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yargs/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/yargs/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "peer": true, - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=8" } }, - "node_modules/yargs/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "peer": true + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "peer": true + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", "dev": true, "peer": true, - "dependencies": { - "locate-path": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "peer": true, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "bin": { + "yaml": "bin.mjs" }, "engines": { - "node": ">=6" + "node": ">= 14" } }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "peer": true, "dependencies": { - "p-limit": "^2.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, - "peer": true, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "peer": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/yargs/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "peer": true, "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "peer": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=12" } }, "node_modules/yn": { @@ -13189,17 +13645,18 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "peer": true, "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 0ef2ea2a..7ae6caf0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.4", + "version": "4.2.0-beta1", "description": "A fully compliant environment for the issuance and use of tokenized securities.", "main": "index.js", "directories": { @@ -28,7 +28,7 @@ "lint": "npm run lint:sol", "lint:sol": "solhint \"contracts/**/*.sol\"", "docs": "hardhat dodoc", - "prepare": "husky install" + "prepare": "husky" }, "repository": { "type": "git", @@ -41,35 +41,45 @@ }, "homepage": "https://github.com/TokenySolutions/T-REX#README", "devDependencies": { - "@commitlint/cli": "^17.6.1", - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "@nomiclabs/hardhat-solhint": "^3.0.1", - "@onchain-id/solidity": "^2.0.0", + "@commitlint/cli": "^19.3.0", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@nomiclabs/hardhat-solhint": "^3.1.0", + "@onchain-id/solidity": "^2.2.2-beta2", "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "^4.8.3", - "@openzeppelin/hardhat-upgrades": "^1.28.0", + "@openzeppelin/hardhat-upgrades": "^3.1.0", "@primitivefi/hardhat-dodoc": "^0.2.3", + "@typescript-eslint/eslint-plugin": "^7.10.0", + "@typescript-eslint/parser": "^7.10.0", "@xyrusworx/hardhat-solidity-json": "^1.0.2", - "eslint": "^8.39.0", + "eslint": "^8.0.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-security": "^1.7.1", - "hardhat": "^2.14.0", - "husky": "^8.0.3", - "lint-staged": "^13.2.2", - "prettier": "^2.8.8", - "prettier-plugin-solidity": "^1.1.3", - "solhint": "^3.4.1", - "solhint-plugin-prettier": "^0.0.5", - "glob": "^10.2.6", - "fs-extra": "^11.1.1", - "@typescript-eslint/parser": "^6.7.4", - "@typescript-eslint/eslint-plugin": "^6.7.4", + "eslint-config-prettier": "^8.0.0", "eslint-import-resolver-typescript": "^3.6.1", - "eth-gas-reporter": "^0.2.27" + "eslint-plugin-chai-friendly": "^0.8.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-security": "^3.0.0", + "eth-gas-reporter": "^0.2.27", + "fs-extra": "^11.2.0", + "glob": "^10.4.1", + "hardhat": "^2.22.8", + "husky": "^9.0.11", + "lint-staged": "^15.2.5", + "prettier": "^3.2.5", + "prettier-plugin-solidity": "^1.3.1", + "solhint": "^5.0.1", + "solhint-plugin-prettier": "^0.1.0" + }, + "overrides": { + "@openzeppelin/defender-sdk-deploy-client": { + "@openzeppelin/defender-sdk-base-client": "1.13.1" + }, + "@openzeppelin/hardhat-upgrades": { + "@openzeppelin/defender-sdk-deploy-client": "1.13.1", + "@openzeppelin/defender-sdk-base-client": "1.13.1", + "@openzeppelin/defender-sdk-network-client": "1.13.1" + } }, "lint-staged": { "*.js": [ diff --git a/scripts/commit-msg.js b/scripts/commit-msg.js new file mode 100644 index 00000000..aabccf74 --- /dev/null +++ b/scripts/commit-msg.js @@ -0,0 +1,31 @@ +const fs = require('fs'); + +const TYPEMOJIS = Object.seal({ + '✨': ['sparkles', 'feat', 'feature'], + '🐛': ['bug', 'fix', 'bugfix'], + '♻': ['recycle', 'refactor'], + '📝': ['pencil', 'docs', 'note'], + '✅': ['white_check_mark', 'tests'], + '🔀': ['twisted_rightwards_arrows', 'merge'], + '✏': ['pencil2', 'typo'], + '🔧': ['wrench', 'config'], + '➕': ['heavy_plus_sign', 'add', 'plus'], + '➖': ['heavy_minus_sign', 'remove', 'minus'], + '🔖': ['bookmark', 'version', 'release'], + '👷': ['construction_worker', 'ci'], + '🚨': ['rotating_light', 'lint'], + '🚑': ['ambulance', 'hotfix'], + '🎉': ['tada'], +}); + +const commitMessage = fs.readFileSync(process.argv[2]).toString(); + +let editedCommitMessage = commitMessage; + +Object.entries(TYPEMOJIS).forEach(([typemoji, translations]) => { + translations.forEach(translation => { + editedCommitMessage = editedCommitMessage.replace(`:${translation}:`, typemoji); + }); +}); + +fs.writeFileSync(process.argv[2], editedCommitMessage); diff --git a/test/agentManager.test.ts b/test/agentManager.test.ts index bed52164..f7a3082e 100644 --- a/test/agentManager.test.ts +++ b/test/agentManager.test.ts @@ -14,8 +14,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Transfer Manager'); + agentManager.connect(aliceWallet).callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotTransferManager'); }); }); @@ -27,11 +27,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.target); await expect( - agentManager.connect(anotherWallet).callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Transfer Manager'); + agentManager.connect(anotherWallet).callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotTransferManager'); }); }); @@ -43,11 +43,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.target); const transferTx = await agentManager .connect(aliceWallet) - .callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.address); + .callForcedTransfer(aliceWallet.address, bobWallet.address, 200, aliceIdentity.target); await expect(transferTx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, bobWallet.address, 200); }); @@ -70,9 +70,9 @@ describe('AgentManager', () => { [aliceWallet.address, bobWallet.address], [bobWallet.address, aliceWallet.address], [200, 200], - aliceIdentity.address, + aliceIdentity.target, ), - ).to.be.revertedWith('Role: Sender is NOT Transfer Manager'); + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotTransferManager'); }); }); @@ -84,7 +84,7 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.target); await expect( agentManager @@ -93,9 +93,9 @@ describe('AgentManager', () => { [aliceWallet.address, bobWallet.address], [bobWallet.address, aliceWallet.address], [200, 200], - aliceIdentity.address, + aliceIdentity.target, ), - ).to.be.revertedWith('Role: Sender is NOT Transfer Manager'); + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotTransferManager'); }); }); @@ -107,7 +107,7 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addTransferManager(aliceIdentity.target); const transferTx = await agentManager .connect(aliceWallet) @@ -115,7 +115,7 @@ describe('AgentManager', () => { [aliceWallet.address, bobWallet.address], [bobWallet.address, aliceWallet.address], [200, 200], - aliceIdentity.address, + aliceIdentity.target, ); await expect(transferTx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, bobWallet.address, 200); @@ -133,7 +133,10 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callPause(aliceIdentity.address)).to.be.revertedWith('Role: Sender is NOT Freezer'); + await expect(agentManager.connect(aliceWallet).callPause(aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotFreezer', + ); }); }); @@ -145,9 +148,12 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await expect(agentManager.connect(anotherWallet).callPause(aliceIdentity.address)).to.be.revertedWith('Role: Sender is NOT Freezer'); + await expect(agentManager.connect(anotherWallet).callPause(aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotFreezer', + ); }); }); @@ -159,11 +165,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - const pauseTx = await agentManager.connect(aliceWallet).callPause(aliceIdentity.address); + const pauseTx = await agentManager.connect(aliceWallet).callPause(aliceIdentity.target); - await expect(pauseTx).to.emit(token, 'Paused').withArgs(agentManager.address); + await expect(pauseTx).to.emit(token, 'Paused').withArgs(agentManager.target); await expect(token.paused()).to.be.eventually.true; }); }); @@ -178,7 +184,10 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callUnpause(aliceIdentity.address)).to.be.revertedWith('Role: Sender is NOT Freezer'); + await expect(agentManager.connect(aliceWallet).callUnpause(aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotFreezer', + ); }); }); @@ -190,9 +199,12 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await expect(agentManager.connect(anotherWallet).callUnpause(aliceIdentity.address)).to.be.revertedWith('Role: Sender is NOT Freezer'); + await expect(agentManager.connect(anotherWallet).callUnpause(aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotFreezer', + ); }); }); @@ -204,13 +216,13 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await agentManager.connect(aliceWallet).callPause(aliceIdentity.address); + await agentManager.connect(aliceWallet).callPause(aliceIdentity.target); - const pauseTx = await agentManager.connect(aliceWallet).callUnpause(aliceIdentity.address); + const pauseTx = await agentManager.connect(aliceWallet).callUnpause(aliceIdentity.target); - await expect(pauseTx).to.emit(token, 'Unpaused').withArgs(agentManager.address); + await expect(pauseTx).to.emit(token, 'Unpaused').withArgs(agentManager.target); }); }); }); @@ -224,8 +236,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callMint(bobWallet.address, 1000, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Supply Modifier', + await expect(agentManager.connect(aliceWallet).callMint(bobWallet.address, 1000, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotSupplyModifier', ); }); }); @@ -238,10 +251,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); - await expect(agentManager.connect(anotherWallet).callMint(bobWallet.address, 1000, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Supply Modifier', + await expect(agentManager.connect(anotherWallet).callMint(bobWallet.address, 1000, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotSupplyModifier', ); }); }); @@ -254,11 +268,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); - const mintTx = await agentManager.connect(aliceWallet).callMint(bobWallet.address, 1000, aliceIdentity.address); + const mintTx = await agentManager.connect(aliceWallet).callMint(bobWallet.address, 1000, aliceIdentity.target); - await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.constants.AddressZero, bobWallet.address, 1000); + await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.ZeroAddress, bobWallet.address, 1000); }); }); }); @@ -273,8 +287,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Supply Modifier'); + agentManager.connect(aliceWallet).callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotSupplyModifier'); }); }); @@ -286,11 +300,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); await expect( - agentManager.connect(anotherWallet).callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Supply Modifier'); + agentManager.connect(anotherWallet).callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotSupplyModifier'); }); }); @@ -302,14 +316,14 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); const mintTx = await agentManager .connect(aliceWallet) - .callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.address); + .callBatchMint([bobWallet.address, aliceWallet.address], [1000, 500], aliceIdentity.target); - await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.constants.AddressZero, bobWallet.address, 1000); - await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.constants.AddressZero, aliceWallet.address, 500); + await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.ZeroAddress, bobWallet.address, 1000); + await expect(mintTx).to.emit(token, 'Transfer').withArgs(ethers.ZeroAddress, aliceWallet.address, 500); }); }); }); @@ -323,8 +337,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callBurn(bobWallet.address, 1000, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Supply Modifier', + await expect(agentManager.connect(aliceWallet).callBurn(bobWallet.address, 1000, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotSupplyModifier', ); }); }); @@ -337,10 +352,11 @@ describe('AgentManager', () => { identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(bobIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(bobIdentity.target); - await expect(agentManager.connect(anotherWallet).callBurn(bobWallet.address, 200, bobIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Supply Modifier', + await expect(agentManager.connect(anotherWallet).callBurn(bobWallet.address, 200, bobIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotSupplyModifier', ); }); }); @@ -353,11 +369,11 @@ describe('AgentManager', () => { identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(bobIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(bobIdentity.target); - const burnTx = await agentManager.connect(bobWallet).callBurn(bobWallet.address, 200, bobIdentity.address); + const burnTx = await agentManager.connect(bobWallet).callBurn(bobWallet.address, 200, bobIdentity.target); - await expect(burnTx).to.emit(token, 'Transfer').withArgs(bobWallet.address, ethers.constants.AddressZero, 200); + await expect(burnTx).to.emit(token, 'Transfer').withArgs(bobWallet.address, ethers.ZeroAddress, 200); }); }); }); @@ -372,8 +388,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callBatchBurn([bobWallet.address, aliceWallet.address], [500, 1000], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Supply Modifier'); + agentManager.connect(aliceWallet).callBatchBurn([bobWallet.address, aliceWallet.address], [500, 1000], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotSupplyModifier'); }); }); @@ -385,11 +401,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); await expect( - agentManager.connect(anotherWallet).callBatchBurn([bobWallet.address, aliceWallet.address], [500, 100], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Supply Modifier'); + agentManager.connect(anotherWallet).callBatchBurn([bobWallet.address, aliceWallet.address], [500, 100], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotSupplyModifier'); }); }); @@ -401,14 +417,14 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addSupplyModifier(aliceIdentity.target); const burnTx = await agentManager .connect(aliceWallet) - .callBatchBurn([bobWallet.address, aliceWallet.address], [500, 100], aliceIdentity.address); + .callBatchBurn([bobWallet.address, aliceWallet.address], [500, 100], aliceIdentity.target); - await expect(burnTx).to.emit(token, 'Transfer').withArgs(bobWallet.address, ethers.constants.AddressZero, 500); - await expect(burnTx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, ethers.constants.AddressZero, 100); + await expect(burnTx).to.emit(token, 'Transfer').withArgs(bobWallet.address, ethers.ZeroAddress, 500); + await expect(burnTx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, ethers.ZeroAddress, 100); }); }); }); @@ -422,9 +438,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callSetAddressFrozen(aliceIdentity.address, true, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Freezer', - ); + await expect( + agentManager.connect(aliceWallet).callSetAddressFrozen(aliceIdentity.target, true, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -436,11 +452,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await expect(agentManager.connect(anotherWallet).callSetAddressFrozen(aliceIdentity.address, true, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Freezer', - ); + await expect( + agentManager.connect(anotherWallet).callSetAddressFrozen(aliceIdentity.target, true, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -452,11 +468,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - const tx = await agentManager.connect(aliceWallet).callSetAddressFrozen(aliceWallet.address, true, aliceIdentity.address); + const tx = await agentManager.connect(aliceWallet).callSetAddressFrozen(aliceWallet.address, true, aliceIdentity.target); - await expect(tx).to.emit(token, 'AddressFrozen').withArgs(aliceWallet.address, true, agentManager.address); + await expect(tx).to.emit(token, 'AddressFrozen').withArgs(aliceWallet.address, true, agentManager.target); await expect(token.isFrozen(aliceWallet.address)).to.eventually.be.true; }); }); @@ -472,10 +488,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager - .connect(aliceWallet) - .callBatchSetAddressFrozen([aliceIdentity.address, bobWallet.address], [true, false], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + agentManager.connect(aliceWallet).callBatchSetAddressFrozen([aliceIdentity.target, bobWallet.address], [true, false], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -487,13 +501,13 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); await expect( agentManager .connect(anotherWallet) - .callBatchSetAddressFrozen([aliceIdentity.address, bobWallet.address], [true, false], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + .callBatchSetAddressFrozen([aliceIdentity.target, bobWallet.address], [true, false], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -505,14 +519,14 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); const pauseTx = await agentManager .connect(aliceWallet) - .callBatchSetAddressFrozen([aliceWallet.address, bobWallet.address], [true, false], aliceIdentity.address); + .callBatchSetAddressFrozen([aliceWallet.address, bobWallet.address], [true, false], aliceIdentity.target); - await expect(pauseTx).to.emit(token, 'AddressFrozen').withArgs(aliceWallet.address, true, agentManager.address); - await expect(pauseTx).to.emit(token, 'AddressFrozen').withArgs(bobWallet.address, false, agentManager.address); + await expect(pauseTx).to.emit(token, 'AddressFrozen').withArgs(aliceWallet.address, true, agentManager.target); + await expect(pauseTx).to.emit(token, 'AddressFrozen').withArgs(bobWallet.address, false, agentManager.target); }); }); }); @@ -526,9 +540,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callFreezePartialTokens(aliceIdentity.address, 100, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT Freezer', - ); + await expect( + agentManager.connect(aliceWallet).callFreezePartialTokens(aliceIdentity.target, 100, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -540,11 +554,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); await expect( - agentManager.connect(anotherWallet).callFreezePartialTokens(aliceIdentity.address, 100, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + agentManager.connect(anotherWallet).callFreezePartialTokens(aliceIdentity.target, 100, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -556,9 +570,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - const freezeTx = await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.address); + const freezeTx = await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.target); await expect(freezeTx).to.emit(token, 'TokensFrozen').withArgs(aliceWallet.address, 100); }); @@ -575,8 +589,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + agentManager.connect(aliceWallet).callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -588,13 +602,13 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); await expect( agentManager .connect(anotherWallet) - .callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + .callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -606,11 +620,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); const freezeTx = await agentManager .connect(aliceWallet) - .callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address); + .callBatchFreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target); await expect(freezeTx).to.emit(token, 'TokensFrozen').withArgs(aliceWallet.address, 100); await expect(freezeTx).to.emit(token, 'TokensFrozen').withArgs(bobWallet.address, 200); @@ -628,8 +642,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callUnfreezePartialTokens(aliceIdentity.address, 100, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + agentManager.connect(aliceWallet).callUnfreezePartialTokens(aliceIdentity.target, 100, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -641,11 +655,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); await expect( - agentManager.connect(anotherWallet).callUnfreezePartialTokens(aliceIdentity.address, 100, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + agentManager.connect(anotherWallet).callUnfreezePartialTokens(aliceIdentity.target, 100, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -657,11 +671,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.address); + await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.target); - const freezeTx = await agentManager.connect(aliceWallet).callUnfreezePartialTokens(aliceWallet.address, 100, aliceIdentity.address); + const freezeTx = await agentManager.connect(aliceWallet).callUnfreezePartialTokens(aliceWallet.address, 100, aliceIdentity.target); await expect(freezeTx).to.emit(token, 'TokensUnfrozen').withArgs(aliceWallet.address, 100); }); @@ -680,8 +694,8 @@ describe('AgentManager', () => { await expect( agentManager .connect(aliceWallet) - .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -693,13 +707,13 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); await expect( agentManager .connect(anotherWallet) - .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Freezer'); + .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotFreezer'); }); }); @@ -711,14 +725,14 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addFreezer(aliceIdentity.target); - await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.address); - await agentManager.connect(aliceWallet).callFreezePartialTokens(bobWallet.address, 200, aliceIdentity.address); + await agentManager.connect(aliceWallet).callFreezePartialTokens(aliceWallet.address, 100, aliceIdentity.target); + await agentManager.connect(aliceWallet).callFreezePartialTokens(bobWallet.address, 200, aliceIdentity.target); const freezeTx = await agentManager .connect(aliceWallet) - .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.address); + .callBatchUnfreezePartialTokens([aliceWallet.address, bobWallet.address], [100, 200], aliceIdentity.target); await expect(freezeTx).to.emit(token, 'TokensUnfrozen').withArgs(aliceWallet.address, 100); }); @@ -735,8 +749,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Recovery Agent'); + agentManager.connect(aliceWallet).callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotRecoveryAgent'); }); }); @@ -748,35 +762,32 @@ describe('AgentManager', () => { identities: { aliceIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addRecoveryAgent(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addRecoveryAgent(aliceIdentity.target); await expect( - agentManager - .connect(anotherWallet) - .callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT Recovery Agent'); + agentManager.connect(anotherWallet).callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotRecoveryAgent'); }); }); describe('when identity has the RecoveryAgent role and the sender is authorized for it', () => { it('Should perform the recovery of the address', async () => { + const abiCoder = new ethers.AbiCoder(); const { suite: { agentManager, token }, accounts: { tokenAdmin, aliceWallet, bobWallet, anotherWallet }, identities: { aliceIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addRecoveryAgent(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addRecoveryAgent(aliceIdentity.target); - await bobIdentity - .connect(bobWallet) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [anotherWallet.address])), 1, 1); + await bobIdentity.connect(bobWallet).addKey(ethers.keccak256(abiCoder.encode(['address'], [anotherWallet.address])), 1, 1); const recoveryTx = await agentManager .connect(aliceWallet) - .callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address, aliceIdentity.address); + .callRecoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target, aliceIdentity.target); - await expect(recoveryTx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.address); + await expect(recoveryTx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); }); }); }); @@ -791,8 +802,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callRegisterIdentity(bobWallet.address, bobIdentity.address, 42, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT WhiteList Manager'); + agentManager.connect(aliceWallet).callRegisterIdentity(bobWallet.address, bobIdentity.target, 42, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotWhiteListManager'); }); }); @@ -804,11 +815,11 @@ describe('AgentManager', () => { identities: { aliceIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); await expect( - agentManager.connect(bobWallet).callRegisterIdentity(bobWallet.address, bobIdentity.address, 42, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT WhiteList Manager'); + agentManager.connect(bobWallet).callRegisterIdentity(bobWallet.address, bobIdentity.target, 42, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotWhiteListManager'); }); }); @@ -820,13 +831,13 @@ describe('AgentManager', () => { identities: { aliceIdentity, charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); const registerTx = await agentManager .connect(aliceWallet) - .callRegisterIdentity(charlieWallet.address, charlieIdentity.address, 42, aliceIdentity.address); + .callRegisterIdentity(charlieWallet.address, charlieIdentity.target, 42, aliceIdentity.target); - await expect(registerTx).to.emit(identityRegistry, 'IdentityRegistered').withArgs(charlieWallet.address, charlieIdentity.address); + await expect(registerTx).to.emit(identityRegistry, 'IdentityRegistered').withArgs(charlieWallet.address, charlieIdentity.target); await expect(identityRegistry.contains(charlieWallet.address)).to.eventually.be.true; }); @@ -843,8 +854,8 @@ describe('AgentManager', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - agentManager.connect(aliceWallet).callUpdateIdentity(bobWallet.address, bobIdentity.address, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT WhiteList Manager'); + agentManager.connect(aliceWallet).callUpdateIdentity(bobWallet.address, bobIdentity.target, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotWhiteListManager'); }); }); @@ -856,11 +867,11 @@ describe('AgentManager', () => { identities: { aliceIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); await expect( - agentManager.connect(bobWallet).callUpdateIdentity(bobWallet.address, bobIdentity.address, aliceIdentity.address), - ).to.be.revertedWith('Role: Sender is NOT WhiteList Manager'); + agentManager.connect(bobWallet).callUpdateIdentity(bobWallet.address, bobIdentity.target, aliceIdentity.target), + ).to.be.revertedWithCustomError(agentManager, 'SenderIsNotWhiteListManager'); }); }); @@ -872,13 +883,11 @@ describe('AgentManager', () => { identities: { aliceIdentity, bobIdentity, charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); - const updateTx = await agentManager - .connect(aliceWallet) - .callUpdateIdentity(bobWallet.address, charlieIdentity.address, aliceIdentity.address); + const updateTx = await agentManager.connect(aliceWallet).callUpdateIdentity(bobWallet.address, charlieIdentity.target, aliceIdentity.target); - await expect(updateTx).to.emit(identityRegistry, 'IdentityUpdated').withArgs(bobIdentity.address, charlieIdentity.address); + await expect(updateTx).to.emit(identityRegistry, 'IdentityUpdated').withArgs(bobIdentity.target, charlieIdentity.target); }); }); }); @@ -892,8 +901,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT WhiteList Manager', + await expect(agentManager.connect(aliceWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotWhiteListManager', ); }); }); @@ -906,10 +916,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); - await expect(agentManager.connect(bobWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT WhiteList Manager', + await expect(agentManager.connect(bobWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotWhiteListManager', ); }); }); @@ -922,9 +933,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); - const updateTx = await agentManager.connect(aliceWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.address); + const updateTx = await agentManager.connect(aliceWallet).callUpdateCountry(bobWallet.address, 100, aliceIdentity.target); await expect(updateTx).to.emit(identityRegistry, 'CountryUpdated').withArgs(bobWallet.address, 100); }); @@ -940,8 +951,9 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(agentManager.connect(aliceWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT WhiteList Manager', + await expect(agentManager.connect(aliceWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotWhiteListManager', ); }); }); @@ -954,10 +966,11 @@ describe('AgentManager', () => { identities: { aliceIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); - await expect(agentManager.connect(bobWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.address)).to.be.revertedWith( - 'Role: Sender is NOT WhiteList Manager', + await expect(agentManager.connect(bobWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.target)).to.be.revertedWithCustomError( + agentManager, + 'SenderIsNotWhiteListManager', ); }); }); @@ -970,11 +983,11 @@ describe('AgentManager', () => { identities: { aliceIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.address); + await agentManager.connect(tokenAdmin).addWhiteListManager(aliceIdentity.target); - const deleteTx = await agentManager.connect(aliceWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.address); + const deleteTx = await agentManager.connect(aliceWallet).callDeleteIdentity(bobWallet.address, aliceIdentity.target); - await expect(deleteTx).to.emit(identityRegistry, 'IdentityRemoved').withArgs(bobWallet.address, bobIdentity.address); + await expect(deleteTx).to.emit(identityRegistry, 'IdentityRemoved').withArgs(bobWallet.address, bobIdentity.target); await expect(identityRegistry.contains(bobWallet.address)).to.eventually.be.false; }); diff --git a/test/agentRole.test.ts b/test/agentRole.test.ts index 00d15e72..7d022461 100644 --- a/test/agentRole.test.ts +++ b/test/agentRole.test.ts @@ -40,7 +40,7 @@ describe('AgentRole', () => { contracts: { agentRole }, } = await loadFixture(deployAgentFixture); - await expect(agentRole.connect(ownerWallet).addAgent(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(agentRole.connect(ownerWallet).addAgent(ethers.ZeroAddress)).to.be.revertedWithCustomError(agentRole, 'ZeroAddress'); }); }); @@ -53,7 +53,10 @@ describe('AgentRole', () => { } = await loadFixture(deployAgentFixture); await agentRole.connect(ownerWallet).addAgent(aliceWallet.address); - await expect(agentRole.connect(ownerWallet).addAgent(aliceWallet.address)).to.be.revertedWith('Roles: account already has role'); + await expect(agentRole.connect(ownerWallet).addAgent(aliceWallet.address)).to.be.revertedWithCustomError( + agentRole, + 'AccountAlreadyHasRole', + ); }); }); @@ -93,9 +96,7 @@ describe('AgentRole', () => { contracts: { agentRole }, } = await loadFixture(deployAgentFixture); - await expect(agentRole.connect(ownerWallet).removeAgent(ethers.constants.AddressZero)).to.be.revertedWith( - 'invalid argument - zero address', - ); + await expect(agentRole.connect(ownerWallet).removeAgent(ethers.ZeroAddress)).to.be.revertedWithCustomError(agentRole, 'ZeroAddress'); }); }); @@ -107,7 +108,10 @@ describe('AgentRole', () => { contracts: { agentRole }, } = await loadFixture(deployAgentFixture); - await expect(agentRole.connect(ownerWallet).removeAgent(aliceWallet.address)).to.be.revertedWith('Roles: account does not have role'); + await expect(agentRole.connect(ownerWallet).removeAgent(aliceWallet.address)).to.be.revertedWithCustomError( + agentRole, + 'AccountDoesNotHaveRole', + ); }); }); diff --git a/test/authorities/trex-implementation-authority.test.ts b/test/authorities/trex-implementation-authority.test.ts index fbf4c973..689822dd 100644 --- a/test/authorities/trex-implementation-authority.test.ts +++ b/test/authorities/trex-implementation-authority.test.ts @@ -13,7 +13,7 @@ describe('TrexImplementationAuthority', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(trexImplementationAuthority.connect(anotherWallet).setTREXFactory(ethers.constants.AddressZero)).to.be.revertedWith( + await expect(trexImplementationAuthority.connect(anotherWallet).setTREXFactory(ethers.ZeroAddress)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -32,7 +32,7 @@ describe('TrexImplementationAuthority', () => { const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [true, ethers.constants.AddressZero, ethers.constants.AddressZero], + [true, ethers.ZeroAddress, ethers.ZeroAddress], deployer, ); const versionStruct = { @@ -41,22 +41,25 @@ describe('TrexImplementationAuthority', () => { patch: 0, }; const contractsStruct = { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: implementations.identityRegistryStorageImplementation.address, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: implementations.identityRegistryStorageImplementation.target, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }; await otherTrexImplementationAuthority.connect(deployer).addAndUseTREXVersion(versionStruct, contractsStruct); const trexFactory = await ethers.deployContract( 'TREXFactory', - [otherTrexImplementationAuthority.address, identityFactory.address], + [otherTrexImplementationAuthority.target, identityFactory.target], deployer, ); - await expect(trexImplementationAuthority.setTREXFactory(trexFactory.address)).to.be.revertedWith('only reference contract can call'); + await expect(trexImplementationAuthority.setTREXFactory(trexFactory.target)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'OnlyReferenceContractCanCall', + ); }); }); @@ -68,11 +71,11 @@ describe('TrexImplementationAuthority', () => { factories: { identityFactory }, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); - const tx = await trexImplementationAuthority.setTREXFactory(trexFactory.address); - await expect(tx).to.emit(trexImplementationAuthority, 'TREXFactorySet').withArgs(trexFactory.address); - await expect(trexImplementationAuthority.getTREXFactory()).to.eventually.equal(trexFactory.address); + const tx = await trexImplementationAuthority.setTREXFactory(trexFactory.target); + await expect(tx).to.emit(trexImplementationAuthority, 'TREXFactorySet').withArgs(trexFactory.target); + await expect(trexImplementationAuthority.getTREXFactory()).to.eventually.equal(trexFactory.target); }); }); }); @@ -87,7 +90,7 @@ describe('TrexImplementationAuthority', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(trexImplementationAuthority.connect(anotherWallet).setIAFactory(ethers.constants.AddressZero)).to.be.revertedWith( + await expect(trexImplementationAuthority.connect(anotherWallet).setIAFactory(ethers.ZeroAddress)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -103,17 +106,17 @@ describe('TrexImplementationAuthority', () => { factories: { identityFactory }, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); - await trexImplementationAuthority.setTREXFactory(trexFactory.address); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); const implementationAuthorityFactory = await ethers.deployContract( 'TREXImplementationAuthority', - [true, ethers.constants.AddressZero, ethers.constants.AddressZero], + [true, ethers.ZeroAddress, ethers.ZeroAddress], deployer, ); - const tx = await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address); - await expect(tx).to.emit(trexImplementationAuthority, 'IAFactorySet').withArgs(implementationAuthorityFactory.address); + const tx = await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.target); + await expect(tx).to.emit(trexImplementationAuthority, 'IAFactorySet').withArgs(implementationAuthorityFactory.target); }); }); }); @@ -133,7 +136,10 @@ describe('TrexImplementationAuthority', () => { patch: 0, }; - await expect(trexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWith('cannot call on reference contract'); + await expect(trexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'CannotCallOnReferenceContract', + ); }); }); @@ -145,11 +151,11 @@ describe('TrexImplementationAuthority', () => { factories: { identityFactory }, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [false, trexFactory.address, trexImplementationAuthority.address], + [false, trexFactory.target, trexImplementationAuthority.target], deployer, ); @@ -161,7 +167,10 @@ describe('TrexImplementationAuthority', () => { await otherTrexImplementationAuthority.fetchVersion(versionStruct); - await expect(otherTrexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWith('version fetched already'); + await expect(otherTrexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWithCustomError( + otherTrexImplementationAuthority, + 'VersionAlreadyFetched', + ); }); }); @@ -173,11 +182,11 @@ describe('TrexImplementationAuthority', () => { factories: { identityFactory }, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [false, trexFactory.address, trexImplementationAuthority.address], + [false, trexFactory.target, trexImplementationAuthority.target], deployer, ); @@ -208,12 +217,12 @@ describe('TrexImplementationAuthority', () => { patch: 1, }; const contractsStruct = { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: ethers.constants.AddressZero, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: ethers.ZeroAddress, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }; await expect(trexImplementationAuthority.connect(anotherWallet).addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith( @@ -232,11 +241,11 @@ describe('TrexImplementationAuthority', () => { implementations, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [false, trexFactory.address, trexImplementationAuthority.address], + [false, trexFactory.target, trexImplementationAuthority.target], deployer, ); @@ -246,16 +255,17 @@ describe('TrexImplementationAuthority', () => { patch: 0, }; const contractsStruct = { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: implementations.identityRegistryStorageImplementation.address, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: implementations.identityRegistryStorageImplementation.target, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }; - await expect(otherTrexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith( - 'ONLY reference contract can add versions', + await expect(otherTrexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWithCustomError( + otherTrexImplementationAuthority, + 'OnlyReferenceContractCanCall', ); }); }); @@ -274,15 +284,18 @@ describe('TrexImplementationAuthority', () => { patch: 0, }; const contractsStruct = { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: implementations.identityRegistryStorageImplementation.address, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: implementations.identityRegistryStorageImplementation.target, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }; - await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith('version already exists'); + await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'VersionAlreadyExists', + ); }); }); @@ -299,16 +312,17 @@ describe('TrexImplementationAuthority', () => { patch: 1, }; const contractsStruct = { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: ethers.constants.AddressZero, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: ethers.ZeroAddress, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }; - await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'ZeroAddress', ); }); }); @@ -349,7 +363,10 @@ describe('TrexImplementationAuthority', () => { patch: 0, }; - await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWith('version already in use'); + await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'VersionAlreadyInUse', + ); }); }); @@ -365,7 +382,10 @@ describe('TrexImplementationAuthority', () => { patch: 1, }; - await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWith('invalid argument - non existing version'); + await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWithCustomError( + trexImplementationAuthority, + 'NonExistingVersion', + ); }); }); }); @@ -380,8 +400,8 @@ describe('TrexImplementationAuthority', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - trexImplementationAuthority.changeImplementationAuthority(ethers.constants.AddressZero, anotherWallet.address), - ).to.be.revertedWith('invalid argument - zero address'); + trexImplementationAuthority.changeImplementationAuthority(ethers.ZeroAddress, anotherWallet.address), + ).to.be.revertedWithCustomError(trexImplementationAuthority, 'ZeroAddress'); }); }); @@ -395,17 +415,17 @@ describe('TrexImplementationAuthority', () => { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [false, trexFactory.address, trexImplementationAuthority.address], + [false, trexFactory.target, trexImplementationAuthority.target], deployer, ); await expect( - otherTrexImplementationAuthority.changeImplementationAuthority(token.address, ethers.constants.AddressZero), - ).to.be.revertedWith('only reference contract can deploy new IAs'); + otherTrexImplementationAuthority.changeImplementationAuthority(token.target, ethers.ZeroAddress), + ).to.be.revertedWithCustomError(otherTrexImplementationAuthority, 'OnlyReferenceContractCanCall'); }); }); @@ -419,8 +439,8 @@ describe('TrexImplementationAuthority', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - trexImplementationAuthority.connect(anotherWallet).changeImplementationAuthority(token.address, ethers.constants.AddressZero), - ).to.be.revertedWith('caller NOT owner of all contracts impacted'); + trexImplementationAuthority.connect(anotherWallet).changeImplementationAuthority(token.target, ethers.ZeroAddress), + ).to.be.revertedWithCustomError(trexImplementationAuthority, 'CallerNotOwnerOfAllImpactedContracts'); }); }); @@ -433,16 +453,16 @@ describe('TrexImplementationAuthority', () => { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer); - await token.setCompliance(compliance.address); + const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.target], deployer); + await token.setCompliance(compliance.target); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); - const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer); - await trexImplementationAuthority.setTREXFactory(trexFactory.address); - await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address); + const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.target); - const tx = await trexImplementationAuthority.changeImplementationAuthority(token.address, ethers.constants.AddressZero); + const tx = await trexImplementationAuthority.changeImplementationAuthority(token.target, ethers.ZeroAddress); expect(tx).to.emit(trexImplementationAuthority, 'ImplementationAuthorityChanged'); }); }); @@ -461,35 +481,35 @@ describe('TrexImplementationAuthority', () => { implementations, } = await loadFixture(deployFullSuiteFixture); - const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer); - await token.setCompliance(compliance.address); + const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.target], deployer); + await token.setCompliance(compliance.target); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); - const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer); - await trexImplementationAuthority.setTREXFactory(trexFactory.address); - await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address); + const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.target); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [true, trexFactory.address, trexImplementationAuthority.address], + [true, trexFactory.target, trexImplementationAuthority.target], deployer, ); await otherTrexImplementationAuthority.addAndUseTREXVersion( { major: 4, minor: 0, patch: 1 }, { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: implementations.identityRegistryStorageImplementation.address, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: implementations.identityRegistryStorageImplementation.target, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }, ); await expect( - trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address), - ).to.be.revertedWith('version of new IA has to be the same as current IA'); + trexImplementationAuthority.changeImplementationAuthority(token.target, otherTrexImplementationAuthority.target), + ).to.be.revertedWithCustomError(trexImplementationAuthority, 'VersionOfNewIAMustBeTheSameAsCurrentIA'); }); }); @@ -503,35 +523,35 @@ describe('TrexImplementationAuthority', () => { implementations, } = await loadFixture(deployFullSuiteFixture); - const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer); - await token.setCompliance(compliance.address); + const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.target], deployer); + await token.setCompliance(compliance.target); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); - const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer); - await trexImplementationAuthority.setTREXFactory(trexFactory.address); - await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address); + const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.target); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [true, trexFactory.address, trexImplementationAuthority.address], + [true, trexFactory.target, trexImplementationAuthority.target], deployer, ); await otherTrexImplementationAuthority.addAndUseTREXVersion( { major: 4, minor: 0, patch: 0 }, { - tokenImplementation: implementations.tokenImplementation.address, - ctrImplementation: implementations.claimTopicsRegistryImplementation.address, - irImplementation: implementations.identityRegistryImplementation.address, - irsImplementation: implementations.identityRegistryStorageImplementation.address, - tirImplementation: implementations.trustedIssuersRegistryImplementation.address, - mcImplementation: implementations.modularComplianceImplementation.address, + tokenImplementation: implementations.tokenImplementation.target, + ctrImplementation: implementations.claimTopicsRegistryImplementation.target, + irImplementation: implementations.identityRegistryImplementation.target, + irsImplementation: implementations.identityRegistryStorageImplementation.target, + tirImplementation: implementations.trustedIssuersRegistryImplementation.target, + mcImplementation: implementations.modularComplianceImplementation.target, }, ); await expect( - trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address), - ).to.be.revertedWith('new IA is NOT reference contract'); + trexImplementationAuthority.changeImplementationAuthority(token.target, otherTrexImplementationAuthority.target), + ).to.be.revertedWithCustomError(trexImplementationAuthority, 'NewIAIsNotAReferenceContract'); }); }); @@ -544,29 +564,128 @@ describe('TrexImplementationAuthority', () => { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer); - await token.setCompliance(compliance.address); + const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.target], deployer); + await token.setCompliance(compliance.target); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); - const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer); - await trexImplementationAuthority.setTREXFactory(trexFactory.address); - await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address); + const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.target); const otherTrexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [false, trexFactory.address, trexImplementationAuthority.address], + [false, trexFactory.target, trexImplementationAuthority.target], deployer, ); await otherTrexImplementationAuthority.fetchVersion({ major: 4, minor: 0, patch: 0 }); await otherTrexImplementationAuthority.useTREXVersion({ major: 4, minor: 0, patch: 0 }); await expect( - trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address), - ).to.be.revertedWith('invalid IA'); + trexImplementationAuthority.changeImplementationAuthority(token.target, otherTrexImplementationAuthority.target), + ).to.be.revertedWithCustomError(trexImplementationAuthority, 'InvalidImplementationAuthority'); }); }); }); }); }); + describe('ERC165 tests', () => { + describe('TREXImplementationAuthority', () => { + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + authorities: { trexImplementationAuthority }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await trexImplementationAuthority.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the ITREXImplementationAuthority interface ID', async () => { + const { + authorities: { trexImplementationAuthority }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iTREXImplementationAuthorityInterfaceId = await interfaceIdCalculator.getITREXImplementationAuthorityInterfaceId(); + expect(await trexImplementationAuthority.supportsInterface(iTREXImplementationAuthorityInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + authorities: { trexImplementationAuthority }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await trexImplementationAuthority.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + authorities: { trexImplementationAuthority }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await trexImplementationAuthority.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); + }); + describe('IAFactory', () => { + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + accounts: { deployer }, + authorities: { trexImplementationAuthority }, + factories: { identityFactory }, + } = await loadFixture(deployFullSuiteFixture); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + + const iaFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + + const unsupportedInterfaceId = '0x12345678'; + expect(await iaFactory.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IIAFactory interface ID', async () => { + const { + accounts: { deployer }, + authorities: { trexImplementationAuthority }, + factories: { identityFactory }, + } = await loadFixture(deployFullSuiteFixture); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + + const iaFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iIAFactoryInterfaceId = await interfaceIdCalculator.getIIAFactoryInterfaceId(); + expect(await iaFactory.supportsInterface(iIAFactoryInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + accounts: { deployer }, + authorities: { trexImplementationAuthority }, + factories: { identityFactory }, + } = await loadFixture(deployFullSuiteFixture); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); + await trexImplementationAuthority.setTREXFactory(trexFactory.target); + + const iaFactory = await ethers.deployContract('IAFactory', [trexFactory.target], deployer); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await iaFactory.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); + }); + }); }); diff --git a/test/compliance.test.ts b/test/compliance.test.ts index 8814aadb..40f21014 100644 --- a/test/compliance.test.ts +++ b/test/compliance.test.ts @@ -26,7 +26,7 @@ describe('ModularCompliance', () => { suite: { token, compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).bindToken(token.address)).to.be.revertedWith('only owner or token can call'); + await expect(compliance.connect(anotherWallet).bindToken(token.target)).to.be.revertedWithCustomError(compliance, 'OnlyOwnerOrTokenCanCall'); }); }); @@ -41,9 +41,12 @@ describe('ModularCompliance', () => { const compliance = await ethers.deployContract('ModularCompliance', deployer); await compliance.init(); - await compliance.bindToken(token.address); + await compliance.bindToken(token.target); - await expect(compliance.connect(anotherWallet).bindToken(token.address)).to.be.revertedWith('only owner or token can call'); + await expect(compliance.connect(anotherWallet).bindToken(token.target)).to.be.revertedWithCustomError( + compliance, + 'OnlyOwnerOrTokenCanCall', + ); }); }); @@ -55,13 +58,13 @@ describe('ModularCompliance', () => { const compliance = await ethers.deployContract('ModularCompliance'); await compliance.init(); - await compliance.bindToken(token.address); + await compliance.bindToken(token.target); const newCompliance = await ethers.deployContract('ModularCompliance'); - const tx = await token.setCompliance(newCompliance.address); - await expect(tx).to.emit(token, 'ComplianceAdded').withArgs(newCompliance.address); - await expect(tx).to.emit(newCompliance, 'TokenBound').withArgs(token.address); + const tx = await token.setCompliance(newCompliance.target); + await expect(tx).to.emit(token, 'ComplianceAdded').withArgs(newCompliance.target); + await expect(tx).to.emit(newCompliance, 'TokenBound').withArgs(token.target); }); }); }); @@ -76,7 +79,7 @@ describe('ModularCompliance', () => { const compliance = await ethers.deployContract('ModularCompliance', deployer); await compliance.init(); - await expect(compliance.bindToken(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(compliance.bindToken(ethers.ZeroAddress)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); }); @@ -90,7 +93,10 @@ describe('ModularCompliance', () => { suite: { token, compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).unbindToken(token.address)).to.be.revertedWith('only owner or token can call'); + await expect(compliance.connect(anotherWallet).unbindToken(token.target)).to.be.revertedWithCustomError( + compliance, + 'OnlyOwnerOrTokenCanCall', + ); }); }); @@ -101,7 +107,7 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.unbindToken(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(compliance.unbindToken(ethers.ZeroAddress)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); @@ -115,7 +121,7 @@ describe('ModularCompliance', () => { const compliance = await ethers.deployContract('ModularCompliance', deployer); await compliance.init(); - await expect(compliance.unbindToken(token.address)).to.be.revertedWith('This token is not bound'); + await expect(compliance.unbindToken(token.target)).to.be.revertedWithCustomError(compliance, 'TokenNotBound'); }); }); }); @@ -126,14 +132,14 @@ describe('ModularCompliance', () => { suite: { compliance, complianceBeta, token }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await token.setCompliance(compliance.address); + await token.setCompliance(compliance.target); - const tx = await token.setCompliance(complianceBeta.address); - await expect(tx).to.emit(token, 'ComplianceAdded').withArgs(complianceBeta.address); - await expect(tx).to.emit(complianceBeta, 'TokenBound').withArgs(token.address); - await expect(tx).to.emit(compliance, 'TokenUnbound').withArgs(token.address); + const tx = await token.setCompliance(complianceBeta.target); + await expect(tx).to.emit(token, 'ComplianceAdded').withArgs(complianceBeta.target); + await expect(tx).to.emit(complianceBeta, 'TokenBound').withArgs(token.target); + await expect(tx).to.emit(compliance, 'TokenUnbound').withArgs(token.target); - await expect(complianceBeta.getTokenBound()).to.eventually.eq(token.address); + await expect(complianceBeta.getTokenBound()).to.eventually.eq(token.target); }); }); }); @@ -146,9 +152,7 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).addModule(ethers.constants.AddressZero)).to.be.revertedWith( - 'Ownable: caller is not the owner', - ); + await expect(compliance.connect(anotherWallet).addModule(ethers.ZeroAddress)).to.be.revertedWith('Ownable: caller is not the owner'); }); }); @@ -159,7 +163,7 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.addModule(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(compliance.addModule(ethers.ZeroAddress)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); @@ -170,9 +174,9 @@ describe('ModularCompliance', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(module.address); + await compliance.addModule(module.target); - await expect(compliance.addModule(module.address)).to.be.revertedWith('module already bound'); + await expect(compliance.addModule(module.target)).to.be.revertedWithCustomError(compliance, 'ModuleAlreadyBound'); }); }); @@ -183,10 +187,10 @@ describe('ModularCompliance', () => { accounts, suite: { compliance, token }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await compliance.connect(accounts.deployer).bindToken(token.address); + await compliance.connect(accounts.deployer).bindToken(token.target); const module = await ethers.deployContract('MaxBalanceModule'); - await expect(compliance.addModule(module.address)).to.be.revertedWith('compliance is not suitable for binding to the module'); + await expect(compliance.addModule(module.target)).to.be.revertedWithCustomError(compliance, 'ComplianceNotSuitableForBindingToModule'); }); }); @@ -197,15 +201,15 @@ describe('ModularCompliance', () => { suite: { compliance, token }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await compliance.connect(accounts.deployer).bindToken(token.address); + await compliance.connect(accounts.deployer).bindToken(token.target); await token.connect(accounts.tokenAgent).burn(accounts.aliceWallet.address, 1000); await token.connect(accounts.tokenAgent).burn(accounts.bobWallet.address, 500); const module = await ethers.deployContract('MaxBalanceModule'); - const tx = await compliance.addModule(module.address); + const tx = await compliance.addModule(module.target); - await expect(tx).to.emit(compliance, 'ModuleAdded').withArgs(module.address); - await expect(compliance.getModules()).to.eventually.deep.eq([module.address]); + await expect(tx).to.emit(compliance, 'ModuleAdded').withArgs(module.target); + await expect(compliance.getModules()).to.eventually.deep.eq([module.target]); }); }); }); @@ -217,10 +221,10 @@ describe('ModularCompliance', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('CountryAllowModule'); - const tx = await compliance.addModule(module.address); + const tx = await compliance.addModule(module.target); - await expect(tx).to.emit(compliance, 'ModuleAdded').withArgs(module.address); - await expect(compliance.getModules()).to.eventually.deep.eq([module.address]); + await expect(tx).to.emit(compliance, 'ModuleAdded').withArgs(module.target); + await expect(compliance.getModules()).to.eventually.deep.eq([module.target]); }); }); @@ -232,11 +236,11 @@ describe('ModularCompliance', () => { const modules = await Promise.all(Array.from({ length: 25 }, () => ethers.deployContract('CountryAllowModule'))); - await Promise.all(modules.map((module) => compliance.addModule(module.address))); + await Promise.all(modules.map((module) => compliance.addModule(module.target))); const module = await ethers.deployContract('CountryAllowModule'); - await expect(compliance.addModule(module.address)).to.be.revertedWith('cannot add more than 25 modules'); + await expect(compliance.addModule(module.target)).to.be.revertedWithCustomError(compliance, 'MaxModulesReached'); }); }); }); @@ -250,9 +254,7 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).removeModule(ethers.constants.AddressZero)).to.be.revertedWith( - 'Ownable: caller is not the owner', - ); + await expect(compliance.connect(anotherWallet).removeModule(ethers.ZeroAddress)).to.be.revertedWith('Ownable: caller is not the owner'); }); }); @@ -263,7 +265,7 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.removeModule(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(compliance.removeModule(ethers.ZeroAddress)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); @@ -275,7 +277,7 @@ describe('ModularCompliance', () => { const module = await ethers.deployContract('CountryAllowModule'); - await expect(compliance.removeModule(module.address)).to.be.revertedWith('module not bound'); + await expect(compliance.removeModule(module.target)).to.be.revertedWithCustomError(compliance, 'ModuleNotBound'); }); }); @@ -286,16 +288,16 @@ describe('ModularCompliance', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(module.address); + await compliance.addModule(module.target); const moduleB = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(moduleB.address); + await compliance.addModule(moduleB.target); - const tx = await compliance.removeModule(moduleB.address); + const tx = await compliance.removeModule(moduleB.target); - await expect(tx).to.emit(compliance, 'ModuleRemoved').withArgs(moduleB.address); + await expect(tx).to.emit(compliance, 'ModuleRemoved').withArgs(moduleB.target); - await expect(compliance.isModuleBound(moduleB.address)).to.be.eventually.false; + await expect(compliance.isModuleBound(moduleB.target)).to.be.eventually.false; }); }); }); @@ -309,8 +311,9 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).transferred(ethers.constants.AddressZero, ethers.constants.AddressZero, 0)).to.be.revertedWith( - 'error : this address is not a token bound to the compliance contract', + await expect(compliance.connect(anotherWallet).transferred(ethers.ZeroAddress, ethers.ZeroAddress, 0)).to.be.revertedWithCustomError( + compliance, + 'AddressNotATokenBoundToComplianceContract', ); }); }); @@ -323,8 +326,9 @@ describe('ModularCompliance', () => { accounts: { bobWallet, charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).transferred(ethers.constants.AddressZero, bobWallet.address, 10)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(compliance.connect(charlieWallet).transferred(ethers.ZeroAddress, bobWallet.address, 10)).to.be.revertedWithCustomError( + compliance, + 'ZeroAddress', ); }); }); @@ -336,8 +340,9 @@ describe('ModularCompliance', () => { accounts: { charlieWallet, aliceWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).transferred(aliceWallet.address, ethers.constants.AddressZero, 10)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(compliance.connect(charlieWallet).transferred(aliceWallet.address, ethers.ZeroAddress, 10)).to.be.revertedWithCustomError( + compliance, + 'ZeroAddress', ); }); }); @@ -349,8 +354,9 @@ describe('ModularCompliance', () => { accounts: { aliceWallet, bobWallet, charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).transferred(aliceWallet.address, bobWallet.address, 0)).to.be.revertedWith( - 'invalid argument - no value transfer', + await expect(compliance.connect(charlieWallet).transferred(aliceWallet.address, bobWallet.address, 0)).to.be.revertedWithCustomError( + compliance, + 'ZeroValue', ); }); }); @@ -376,8 +382,9 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).created(ethers.constants.AddressZero, 0)).to.be.revertedWith( - 'error : this address is not a token bound to the compliance contract', + await expect(compliance.connect(anotherWallet).created(ethers.ZeroAddress, 0)).to.be.revertedWithCustomError( + compliance, + 'AddressNotATokenBoundToComplianceContract', ); }); }); @@ -390,9 +397,7 @@ describe('ModularCompliance', () => { accounts: { charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).created(ethers.constants.AddressZero, 10)).to.be.revertedWith( - 'invalid argument - zero address', - ); + await expect(compliance.connect(charlieWallet).created(ethers.ZeroAddress, 10)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); @@ -403,7 +408,7 @@ describe('ModularCompliance', () => { accounts: { bobWallet, charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).created(bobWallet.address, 0)).to.be.revertedWith('invalid argument - no value mint'); + await expect(compliance.connect(charlieWallet).created(bobWallet.address, 0)).to.be.revertedWithCustomError(compliance, 'ZeroValue'); }); }); @@ -428,8 +433,9 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(anotherWallet).destroyed(ethers.constants.AddressZero, 0)).to.be.revertedWith( - 'error : this address is not a token bound to the compliance contract', + await expect(compliance.connect(anotherWallet).destroyed(ethers.ZeroAddress, 0)).to.be.revertedWithCustomError( + compliance, + 'AddressNotATokenBoundToComplianceContract', ); }); }); @@ -442,9 +448,7 @@ describe('ModularCompliance', () => { accounts: { charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).destroyed(ethers.constants.AddressZero, 10)).to.be.revertedWith( - 'invalid argument - zero address', - ); + await expect(compliance.connect(charlieWallet).destroyed(ethers.ZeroAddress, 10)).to.be.revertedWithCustomError(compliance, 'ZeroAddress'); }); }); @@ -455,7 +459,7 @@ describe('ModularCompliance', () => { accounts: { aliceWallet, charlieWallet }, } = await loadFixture(deploySuiteWithModuleComplianceBoundToWallet); - await expect(compliance.connect(charlieWallet).destroyed(aliceWallet.address, 0)).to.be.revertedWith('invalid argument - no value burn'); + await expect(compliance.connect(charlieWallet).destroyed(aliceWallet.address, 0)).to.be.revertedWithCustomError(compliance, 'ZeroValue'); }); }); @@ -480,9 +484,9 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect( - compliance.connect(anotherWallet).callModuleFunction(ethers.utils.randomBytes(32), ethers.constants.AddressZero), - ).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(compliance.connect(anotherWallet).callModuleFunction(ethers.randomBytes(32), ethers.ZeroAddress)).to.be.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -493,10 +497,65 @@ describe('ModularCompliance', () => { suite: { compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await expect(compliance.connect(deployer).callModuleFunction(ethers.utils.randomBytes(32), ethers.constants.AddressZero)).to.be.revertedWith( - 'call only on bound module', + await expect(compliance.connect(deployer).callModuleFunction(ethers.randomBytes(32), ethers.ZeroAddress)).to.be.revertedWithCustomError( + compliance, + 'ModuleNotBound', ); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { compliance }, + } = await loadFixture(deploySuiteWithModularCompliancesFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await compliance.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModularCompliance interface ID', async () => { + const { + suite: { compliance }, + } = await loadFixture(deploySuiteWithModularCompliancesFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModularComplianceInterfaceId = await interfaceIdCalculator.getIModularComplianceInterfaceId(); + expect(await compliance.supportsInterface(iModularComplianceInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC3643Compliance interface ID', async () => { + const { + suite: { compliance }, + } = await loadFixture(deploySuiteWithModularCompliancesFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iComplianceInterfaceId = await interfaceIdCalculator.getIERC3643ComplianceInterfaceId(); + expect(await compliance.supportsInterface(iComplianceInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { compliance }, + } = await loadFixture(deploySuiteWithModularCompliancesFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await compliance.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { compliance }, + } = await loadFixture(deploySuiteWithModularCompliancesFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await compliance.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-conditional-transfer.test.ts b/test/compliances/module-conditional-transfer.test.ts index 5134cffd..ad08bdd3 100644 --- a/test/compliances/module-conditional-transfer.test.ts +++ b/test/compliances/module-conditional-transfer.test.ts @@ -9,14 +9,14 @@ describe('ConditionalTransferModule', () => { const { compliance } = context.suite; const module = await ethers.deployContract('ConditionalTransferModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const conditionalTransferModule = await ethers.getContractAt('ConditionalTransferModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const conditionalTransferModule = await ethers.getContractAt('ConditionalTransferModule', proxy.target); - await compliance.addModule(conditionalTransferModule.address); + await compliance.addModule(conditionalTransferModule.target); const mockContract = await ethers.deployContract('MockContract'); - await compliance.bindToken(mockContract.address); + await compliance.bindToken(mockContract.target); return { ...context, suite: { ...context.suite, conditionalTransferModule, mockContract } }; } @@ -41,7 +41,7 @@ describe('ConditionalTransferModule', () => { describe('.canComplianceBind', () => { it('should return true', async () => { const context = await loadFixture(deployComplianceWithConditionalTransferModule); - expect(await context.suite.conditionalTransferModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.conditionalTransferModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -83,8 +83,16 @@ describe('ConditionalTransferModule', () => { const context = await loadFixture(deployComplianceWithConditionalTransferModule); // when - await context.suite.conditionalTransferModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.conditionalTransferModule + .connect(context.accounts.deployer) + .transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.conditionalTransferModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.conditionalTransferModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.conditionalTransferModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.conditionalTransferModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -96,9 +104,9 @@ describe('ConditionalTransferModule', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployComplianceWithConditionalTransferModule); - await expect( - context.suite.conditionalTransferModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero), - ).to.revertedWith('Ownable: caller is not the owner'); + await expect(context.suite.conditionalTransferModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -109,11 +117,11 @@ describe('ConditionalTransferModule', () => { const newImplementation = await ethers.deployContract('ConditionalTransferModule'); // when - await context.suite.conditionalTransferModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.conditionalTransferModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.conditionalTransferModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.conditionalTransferModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -128,7 +136,7 @@ describe('ConditionalTransferModule', () => { await expect( conditionalTransferModule.connect(anotherWallet).batchApproveTransfers([anotherWallet.address], [anotherWallet.address], [10]), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -142,28 +150,29 @@ describe('ConditionalTransferModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( - 'batchApproveTransfers', - [[aliceWallet.address], [bobWallet.address], [10]], - ), - conditionalTransferModule.address, + new ethers.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData('batchApproveTransfers', [ + [aliceWallet.address], + [bobWallet.address], + [10], + ]), + conditionalTransferModule.target, ); await expect(tx) .to.emit(conditionalTransferModule, 'TransferApproved') - .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.address); + .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.target); expect( await conditionalTransferModule.isTransferApproved( - compliance.address, - await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.address), + compliance.target, + await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.target), ), ).to.be.true; await expect( conditionalTransferModule.getTransferApprovals( - compliance.address, - await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.address), + compliance.target, + await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.target), ), ).to.eventually.be.equal(1); }); @@ -180,7 +189,7 @@ describe('ConditionalTransferModule', () => { await expect( conditionalTransferModule.connect(anotherWallet).batchUnApproveTransfers([anotherWallet.address], [anotherWallet.address], [10]), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -196,13 +205,13 @@ describe('ConditionalTransferModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchUnApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( + new ethers.Interface(['function batchUnApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( 'batchUnApproveTransfers', [[aliceWallet.address], [bobWallet.address], [10]], ), - conditionalTransferModule.address, + conditionalTransferModule.target, ), - ).to.be.revertedWith('not approved'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'TransferNotApproved'); }); }); @@ -215,31 +224,32 @@ describe('ConditionalTransferModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( - 'batchApproveTransfers', - [[aliceWallet.address], [bobWallet.address], [10]], - ), - conditionalTransferModule.address, + new ethers.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData('batchApproveTransfers', [ + [aliceWallet.address], + [bobWallet.address], + [10], + ]), + conditionalTransferModule.target, ); const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchUnApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( + new ethers.Interface(['function batchUnApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( 'batchUnApproveTransfers', [[aliceWallet.address], [bobWallet.address], [10]], ), - conditionalTransferModule.address, + conditionalTransferModule.target, ); await expect(tx) .to.emit(conditionalTransferModule, 'ApprovalRemoved') - .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.address); + .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.target); expect( await conditionalTransferModule.isTransferApproved( - compliance.address, - await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.address), + compliance.target, + await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.target), ), ).to.be.false; }); @@ -256,7 +266,7 @@ describe('ConditionalTransferModule', () => { await expect( conditionalTransferModule.connect(anotherWallet).approveTransfer(anotherWallet.address, anotherWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'OnlyBoundComplianceCanCall'); }); }); }); @@ -271,7 +281,7 @@ describe('ConditionalTransferModule', () => { await expect( conditionalTransferModule.connect(anotherWallet).unApproveTransfer(anotherWallet.address, anotherWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'OnlyBoundComplianceCanCall'); }); }); }); @@ -284,7 +294,7 @@ describe('ConditionalTransferModule', () => { accounts: { aliceWallet, bobWallet }, } = await loadFixture(deployComplianceWithConditionalTransferModule); - await expect(conditionalTransferModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.address)).to.eventually.be.false; + await expect(conditionalTransferModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.target)).to.eventually.be.false; }); }); @@ -298,14 +308,15 @@ describe('ConditionalTransferModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( - 'batchApproveTransfers', - [[aliceWallet.address], [bobWallet.address], [10]], - ), - conditionalTransferModule.address, + new ethers.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData('batchApproveTransfers', [ + [aliceWallet.address], + [bobWallet.address], + [10], + ]), + conditionalTransferModule.target, ); - await expect(conditionalTransferModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.address)).to.eventually.be.true; + await expect(conditionalTransferModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.target)).to.eventually.be.true; }); }); }); @@ -318,7 +329,10 @@ describe('ConditionalTransferModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithConditionalTransferModule); - await expect(conditionalTransferModule.moduleBurnAction(anotherWallet.address, 10)).to.be.revertedWith('only bound compliance can call'); + await expect(conditionalTransferModule.moduleBurnAction(anotherWallet.address, 10)).to.be.revertedWithCustomError( + conditionalTransferModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -333,11 +347,11 @@ describe('ConditionalTransferModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ anotherWallet.address, 10, ]), - conditionalTransferModule.address, + conditionalTransferModule.target, ), ).to.eventually.be.fulfilled; }); @@ -352,7 +366,10 @@ describe('ConditionalTransferModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithConditionalTransferModule); - await expect(conditionalTransferModule.moduleMintAction(anotherWallet.address, 10)).to.be.revertedWith('only bound compliance can call'); + await expect(conditionalTransferModule.moduleMintAction(anotherWallet.address, 10)).to.be.revertedWithCustomError( + conditionalTransferModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -367,11 +384,11 @@ describe('ConditionalTransferModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ anotherWallet.address, 10, ]), - conditionalTransferModule.address, + conditionalTransferModule.target, ), ).to.eventually.be.fulfilled; }); @@ -388,7 +405,7 @@ describe('ConditionalTransferModule', () => { await expect( conditionalTransferModule.connect(anotherWallet).moduleTransferAction(aliceWallet.address, bobWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(conditionalTransferModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -404,12 +421,12 @@ describe('ConditionalTransferModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ + new ethers.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ aliceWallet.address, bobWallet.address, 10, ]), - conditionalTransferModule.address, + conditionalTransferModule.target, ), ).to.eventually.be.fulfilled; }); @@ -425,38 +442,82 @@ describe('ConditionalTransferModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData( - 'batchApproveTransfers', - [[aliceWallet.address], [bobWallet.address], [10]], - ), - conditionalTransferModule.address, + new ethers.Interface(['function batchApproveTransfers(address[], address[], uint256[])']).encodeFunctionData('batchApproveTransfers', [ + [aliceWallet.address], + [bobWallet.address], + [10], + ]), + conditionalTransferModule.target, ); const tx = await expect( compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ + new ethers.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ aliceWallet.address, bobWallet.address, 10, ]), - conditionalTransferModule.address, + conditionalTransferModule.target, ), ).to.eventually.be.fulfilled; await expect(tx) .to.emit(conditionalTransferModule, 'ApprovalRemoved') - .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.address); + .withArgs(aliceWallet.address, bobWallet.address, 10, mockContract.target); expect( await conditionalTransferModule.isTransferApproved( - compliance.address, - await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.address), + compliance.target, + await conditionalTransferModule.calculateTransferHash(aliceWallet.address, bobWallet.address, 10, mockContract.target), ), ).to.be.false; }); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { conditionalTransferModule }, + } = await loadFixture(deployComplianceWithConditionalTransferModule); + + const unsupportedInterfaceId = '0x12345678'; + expect(await conditionalTransferModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const { + suite: { conditionalTransferModule }, + } = await loadFixture(deployComplianceWithConditionalTransferModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await conditionalTransferModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { conditionalTransferModule }, + } = await loadFixture(deployComplianceWithConditionalTransferModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await conditionalTransferModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { conditionalTransferModule }, + } = await loadFixture(deployComplianceWithConditionalTransferModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await conditionalTransferModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-country-allow.test.ts b/test/compliances/module-country-allow.test.ts index bf446d86..be8d23ee 100644 --- a/test/compliances/module-country-allow.test.ts +++ b/test/compliances/module-country-allow.test.ts @@ -9,9 +9,9 @@ describe('CountryAllowModule', () => { const { compliance } = context.suite; const module = await ethers.deployContract('CountryAllowModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const countryAllowModule = await ethers.getContractAt('CountryAllowModule', proxy.address); - await compliance.addModule(countryAllowModule.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const countryAllowModule = await ethers.getContractAt('CountryAllowModule', proxy.target); + await compliance.addModule(countryAllowModule.target); return { ...context, suite: { ...context.suite, countryAllowModule } }; } @@ -36,7 +36,7 @@ describe('CountryAllowModule', () => { describe('.canComplianceBind()', () => { it('should return true', async () => { const context = await loadFixture(deployComplianceWithCountryAllowModule); - expect(await context.suite.countryAllowModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.countryAllowModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -78,8 +78,14 @@ describe('CountryAllowModule', () => { const context = await loadFixture(deployComplianceWithCountryAllowModule); // when - await context.suite.countryAllowModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.countryAllowModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.countryAllowModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.countryAllowModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.countryAllowModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.countryAllowModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -91,7 +97,7 @@ describe('CountryAllowModule', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(context.suite.countryAllowModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero)).to.revertedWith( + await expect(context.suite.countryAllowModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -108,25 +114,25 @@ describe('CountryAllowModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, ); const newImplementation = await ethers.deployContract('TestUpgradedCountryAllowModule'); // when - await countryAllowModule.connect(deployer).upgradeTo(newImplementation.address); + await countryAllowModule.connect(deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(countryAllowModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(countryAllowModule.target); + expect(implementationAddress).to.eq(newImplementation.target); - const upgradedContract = await ethers.getContractAt('TestUpgradedCountryAllowModule', countryAllowModule.address); + const upgradedContract = await ethers.getContractAt('TestUpgradedCountryAllowModule', countryAllowModule.target); expect(await upgradedContract.getNewField()).to.be.eq(0); await upgradedContract.connect(deployer).setNewField(222); expect(await upgradedContract.getNewField()).to.be.eq(222); - expect(await upgradedContract.isCountryAllowed(compliance.address, 42)).to.be.true; + expect(await upgradedContract.isCountryAllowed(compliance.target, 42)).to.be.true; }); }); }); @@ -139,7 +145,10 @@ describe('CountryAllowModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(anotherWallet).batchAllowCountries([42, 66])).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(anotherWallet).batchAllowCountries([42, 66])).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -150,7 +159,10 @@ describe('CountryAllowModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(deployer).batchAllowCountries([42, 66])).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(deployer).batchAllowCountries([42, 66])).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -164,17 +176,15 @@ describe('CountryAllowModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [ - [42, 66], - ]), - countryAllowModule.address, + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42, 66]]), + countryAllowModule.target, ); - await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.address, 42); - await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.address, 66); + await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.target, 42); + await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.target, 66); - expect(await countryAllowModule.isCountryAllowed(compliance.address, 42)).to.be.true; - expect(await countryAllowModule.isCountryAllowed(compliance.address, 66)).to.be.true; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 42)).to.be.true; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 66)).to.be.true; }); }); }); @@ -187,7 +197,10 @@ describe('CountryAllowModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(anotherWallet).batchDisallowCountries([42, 66])).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(anotherWallet).batchDisallowCountries([42, 66])).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -198,7 +211,10 @@ describe('CountryAllowModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(deployer).batchDisallowCountries([42, 66])).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(deployer).batchDisallowCountries([42, 66])).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -212,18 +228,17 @@ describe('CountryAllowModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchDisallowCountries(uint16[] calldata countries)']).encodeFunctionData( - 'batchDisallowCountries', - [[42, 66]], - ), - countryAllowModule.address, + new ethers.Interface(['function batchDisallowCountries(uint16[] calldata countries)']).encodeFunctionData('batchDisallowCountries', [ + [42, 66], + ]), + countryAllowModule.target, ); - await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.address, 42); - await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.address, 66); + await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.target, 42); + await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.target, 66); - expect(await countryAllowModule.isCountryAllowed(compliance.address, 42)).to.be.false; - expect(await countryAllowModule.isCountryAllowed(compliance.address, 66)).to.be.false; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 42)).to.be.false; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 66)).to.be.false; }); }); }); @@ -236,7 +251,10 @@ describe('CountryAllowModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(anotherWallet).addAllowedCountry(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(anotherWallet).addAllowedCountry(42)).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -247,7 +265,10 @@ describe('CountryAllowModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(deployer).addAllowedCountry(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(deployer).addAllowedCountry(42)).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -262,20 +283,20 @@ describe('CountryAllowModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, ); await expect( compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, ), ) .to.be.revertedWithCustomError(countryAllowModule, 'CountryAlreadyAllowed') - .withArgs(compliance.address, 42); + .withArgs(compliance.target, 42); }); }); @@ -289,13 +310,13 @@ describe('CountryAllowModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, ); - await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.address, 42); + await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.target, 42); - expect(await countryAllowModule.isCountryAllowed(compliance.address, 42)).to.be.true; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 42)).to.be.true; }); }); }); @@ -309,7 +330,10 @@ describe('CountryAllowModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(anotherWallet).removeAllowedCountry(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(anotherWallet).removeAllowedCountry(42)).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -320,7 +344,10 @@ describe('CountryAllowModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(deployer).removeAllowedCountry(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryAllowModule.connect(deployer).removeAllowedCountry(42)).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -336,12 +363,12 @@ describe('CountryAllowModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function removeAllowedCountry(uint16 country)']).encodeFunctionData('removeAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function removeAllowedCountry(uint16 country)']).encodeFunctionData('removeAllowedCountry', [42]), + countryAllowModule.target, ), ) .to.be.revertedWithCustomError(countryAllowModule, 'CountryNotAllowed') - .withArgs(compliance.address, 42); + .withArgs(compliance.target, 42); }); }); @@ -355,20 +382,20 @@ describe('CountryAllowModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, ); const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function removeAllowedCountry(uint16 country)']).encodeFunctionData('removeAllowedCountry', [42]), - countryAllowModule.address, + new ethers.Interface(['function removeAllowedCountry(uint16 country)']).encodeFunctionData('removeAllowedCountry', [42]), + countryAllowModule.target, ); - await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.address, 42); + await expect(tx).to.emit(countryAllowModule, 'CountryUnallowed').withArgs(compliance.target, 42); - expect(await countryAllowModule.isCountryAllowed(compliance.address, 42)).to.be.false; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 42)).to.be.false; }); }); }); @@ -382,20 +409,18 @@ describe('CountryAllowModule', () => { accounts: { deployer, aliceWallet, bobWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); const contract = await ethers.deployContract('MockContract'); - await compliance.bindToken(contract.address); + await compliance.bindToken(contract.target); await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [ - [42, 66], - ]), - countryAllowModule.address, + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42, 66]]), + countryAllowModule.target, ); await contract.setInvestorCountry(42); - await expect(countryAllowModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.address)).to.be.eventually.true; + await expect(countryAllowModule.moduleCheck(aliceWallet.address, bobWallet.address, 10, compliance.target)).to.be.eventually.true; await expect(compliance.canTransfer(aliceWallet.address, bobWallet.address, 10)).to.be.eventually.true; }); }); @@ -407,20 +432,18 @@ describe('CountryAllowModule', () => { accounts: { deployer, aliceWallet, bobWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); const contract = await ethers.deployContract('MockContract'); - await compliance.bindToken(contract.address); + await compliance.bindToken(contract.target); await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [ - [42, 66], - ]), - countryAllowModule.address, + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42, 66]]), + countryAllowModule.target, ); await contract.setInvestorCountry(10); - await expect(countryAllowModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.address)).to.be.eventually.false; + await expect(countryAllowModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.target)).to.be.eventually.false; await expect(compliance.canTransfer(aliceWallet.address, bobWallet.address, 16)).to.be.eventually.false; }); }); @@ -433,7 +456,7 @@ describe('CountryAllowModule', () => { suite: { countryAllowModule, compliance }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.isComplianceBound(compliance.address)).to.be.eventually.true; + await expect(countryAllowModule.isComplianceBound(compliance.target)).to.be.eventually.true; }); }); @@ -443,7 +466,7 @@ describe('CountryAllowModule', () => { suite: { countryAllowModule }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.isComplianceBound(countryAllowModule.address)).to.be.eventually.false; + await expect(countryAllowModule.isComplianceBound(countryAllowModule.target)).to.be.eventually.false; }); }); }); @@ -456,10 +479,153 @@ describe('CountryAllowModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryAllowModule); - await expect(countryAllowModule.connect(anotherWallet).unbindCompliance(compliance.address)).to.be.revertedWith( - 'only bound compliance can call', + await expect(countryAllowModule.connect(anotherWallet).unbindCompliance(compliance.target)).to.be.revertedWithCustomError( + countryAllowModule, + 'OnlyBoundComplianceCanCall', + ); + }); + }); + }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { countryAllowModule }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + const unsupportedInterfaceId = '0x12345678'; + expect(await countryAllowModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const { + suite: { countryAllowModule }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await countryAllowModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { countryAllowModule }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await countryAllowModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { countryAllowModule }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await countryAllowModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); + describe('.addAndSetModule()', () => { + describe('when module is already bound', () => { + it('should revert', async () => { + const { + suite: { compliance, countryAllowModule }, + accounts: { deployer }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + await expect(compliance.connect(deployer).addAndSetModule(countryAllowModule.target, [])).to.be.revertedWithCustomError( + compliance, + 'ModuleAlreadyBound', + ); + }); + }); + describe('when calling batchAllowCountries not as owner', () => { + it('should revert', async () => { + const { + suite: { compliance, countryAllowModule }, + accounts: { deployer, anotherWallet }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + // Remove the module first + await compliance.connect(deployer).removeModule(countryAllowModule.target); + + await expect( + compliance + .connect(anotherWallet) + .addAndSetModule(countryAllowModule.target, [ + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42]]), + ]), + ).to.be.revertedWith('Ownable: caller is not the owner'); + }); + }); + describe('when providing more than 5 interactions', () => { + it('should revert', async () => { + const { + suite: { compliance, countryAllowModule }, + accounts: { deployer }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + // Remove the module first + await compliance.connect(deployer).removeModule(countryAllowModule.target); + + const interactions = Array.from({ length: 6 }, () => + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42]]), + ); + + await expect(compliance.connect(deployer).addAndSetModule(countryAllowModule.target, interactions)).to.be.revertedWithCustomError( + compliance, + 'ArraySizeLimited', ); }); }); + describe('when calling addAllowedCountry twice with the same country code', () => { + it('should revert with CountryAlreadyAllowed', async () => { + const { + suite: { compliance, countryAllowModule }, + accounts: { deployer }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + // Remove the module first to simulate a fresh start + await compliance.connect(deployer).removeModule(countryAllowModule.target); + + await expect( + compliance + .connect(deployer) + .addAndSetModule(countryAllowModule.target, [ + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + ]), + ).to.be.revertedWithCustomError(countryAllowModule, 'CountryAlreadyAllowed'); + }); + }); + describe('when calling batchAllowCountries twice successfully', () => { + it('should add the module and interact with it', async () => { + const { + suite: { compliance, countryAllowModule }, + accounts: { deployer }, + } = await loadFixture(deployComplianceWithCountryAllowModule); + + // Remove the module first + await compliance.connect(deployer).removeModule(countryAllowModule.target); + + const tx = await compliance + .connect(deployer) + .addAndSetModule(countryAllowModule.target, [ + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[42]]), + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [[66]]), + ]); + + await expect(tx).to.emit(compliance, 'ModuleAdded').withArgs(countryAllowModule.target); + await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.target, 42); + await expect(tx).to.emit(countryAllowModule, 'CountryAllowed').withArgs(compliance.target, 66); + + expect(await countryAllowModule.isCountryAllowed(compliance.target, 42)).to.be.true; + expect(await countryAllowModule.isCountryAllowed(compliance.target, 66)).to.be.true; + }); + }); }); }); diff --git a/test/compliances/module-country-restrict.test.ts b/test/compliances/module-country-restrict.test.ts index b11c5669..84db6842 100644 --- a/test/compliances/module-country-restrict.test.ts +++ b/test/compliances/module-country-restrict.test.ts @@ -9,9 +9,9 @@ describe('CountryRestrictModule', () => { const { compliance } = context.suite; const module = await ethers.deployContract('CountryRestrictModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const countryRestrictModule = await ethers.getContractAt('CountryRestrictModule', proxy.address); - await compliance.addModule(countryRestrictModule.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const countryRestrictModule = await ethers.getContractAt('CountryRestrictModule', proxy.target); + await compliance.addModule(countryRestrictModule.target); return { ...context, suite: { ...context.suite, countryRestrictModule } }; } @@ -35,7 +35,7 @@ describe('CountryRestrictModule', () => { describe('.canComplianceBind()', () => { it('should return true', async () => { const context = await loadFixture(deployComplianceWithCountryRestrictModule); - expect(await context.suite.countryRestrictModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.countryRestrictModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -77,8 +77,16 @@ describe('CountryRestrictModule', () => { const context = await loadFixture(deployComplianceWithCountryRestrictModule); // when - await context.suite.countryRestrictModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.countryRestrictModule + .connect(context.accounts.deployer) + .transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.countryRestrictModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.countryRestrictModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.countryRestrictModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.countryRestrictModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -90,9 +98,9 @@ describe('CountryRestrictModule', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect( - context.suite.countryRestrictModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero), - ).to.revertedWith('Ownable: caller is not the owner'); + await expect(context.suite.countryRestrictModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -103,11 +111,11 @@ describe('CountryRestrictModule', () => { const newImplementation = await ethers.deployContract('CountryRestrictModule'); // when - await context.suite.countryRestrictModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.countryRestrictModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.countryRestrictModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.countryRestrictModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -120,7 +128,10 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).addCountryRestriction(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(anotherWallet).addCountryRestriction(42)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -131,7 +142,10 @@ describe('CountryRestrictModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(deployer).addCountryRestriction(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(deployer).addCountryRestriction(42)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -146,18 +160,18 @@ describe('CountryRestrictModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), + countryRestrictModule.target, ); await expect( compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), + countryRestrictModule.target, ), - ).to.be.revertedWith('country already restricted'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'CountryAlreadyRestricted'); }); }); @@ -171,13 +185,13 @@ describe('CountryRestrictModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), + countryRestrictModule.target, ); - await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.address, 42); + await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.target, 42); - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 42)).to.be.true; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 42)).to.be.true; }); }); }); @@ -191,7 +205,10 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).removeCountryRestriction(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(anotherWallet).removeCountryRestriction(42)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -202,7 +219,10 @@ describe('CountryRestrictModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(deployer).removeCountryRestriction(42)).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(deployer).removeCountryRestriction(42)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -218,12 +238,10 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function removeCountryRestriction(uint16 country)']).encodeFunctionData('removeCountryRestriction', [ - 42, - ]), - countryRestrictModule.address, + new ethers.Interface(['function removeCountryRestriction(uint16 country)']).encodeFunctionData('removeCountryRestriction', [42]), + countryRestrictModule.target, ), - ).to.be.revertedWith('country not restricted'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'CountryNotRestricted'); }); }); @@ -237,20 +255,20 @@ describe('CountryRestrictModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), + countryRestrictModule.target, ); const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function removeCountryRestriction(uint16 country)']).encodeFunctionData('removeCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function removeCountryRestriction(uint16 country)']).encodeFunctionData('removeCountryRestriction', [42]), + countryRestrictModule.target, ); - await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.address, 42); + await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.target, 42); - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 42)).to.be.false; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 42)).to.be.false; }); }); }); @@ -264,7 +282,10 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).batchRestrictCountries([42])).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(anotherWallet).batchRestrictCountries([42])).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -275,7 +296,10 @@ describe('CountryRestrictModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(deployer).batchRestrictCountries([42])).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(deployer).batchRestrictCountries([42])).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -291,13 +315,12 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData( - 'batchRestrictCountries', - [Array.from({ length: 195 }, (_, i) => i)], - ), - countryRestrictModule.address, + new ethers.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ + Array.from({ length: 195 }, (_, i) => i), + ]), + countryRestrictModule.target, ), - ).to.be.revertedWith('maximum 195 can be restricted in one batch'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'MaxCountriesInBatchReached'); }); }); @@ -311,21 +334,20 @@ describe('CountryRestrictModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), - countryRestrictModule.address, + new ethers.Interface(['function addCountryRestriction(uint16 country)']).encodeFunctionData('addCountryRestriction', [42]), + countryRestrictModule.target, ); await expect( compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData( - 'batchRestrictCountries', - [[12, 42, 67]], - ), - countryRestrictModule.address, + new ethers.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ + [12, 42, 67], + ]), + countryRestrictModule.target, ), - ).to.be.revertedWith('country already restricted'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'CountryAlreadyRestricted'); }); }); @@ -338,17 +360,17 @@ describe('CountryRestrictModule', () => { const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ + new ethers.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ [42, 66], ]), - countryRestrictModule.address, + countryRestrictModule.target, ); - await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.address, 42); - await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.address, 66); + await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.target, 42); + await expect(tx).to.emit(countryRestrictModule, 'AddedRestrictedCountry').withArgs(compliance.target, 66); - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 42)).to.be.true; - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 66)).to.be.true; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 42)).to.be.true; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 66)).to.be.true; }); }); }); @@ -361,8 +383,9 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).batchUnrestrictCountries([42])).to.be.revertedWith( - 'only bound compliance can call', + await expect(countryRestrictModule.connect(anotherWallet).batchUnrestrictCountries([42])).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -374,7 +397,10 @@ describe('CountryRestrictModule', () => { accounts: { deployer }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(deployer).batchUnrestrictCountries([42])).to.be.revertedWith('only bound compliance can call'); + await expect(countryRestrictModule.connect(deployer).batchUnrestrictCountries([42])).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -390,13 +416,13 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData( + new ethers.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData( 'batchUnrestrictCountries', [Array.from({ length: 195 }, (_, i) => i)], ), - countryRestrictModule.address, + countryRestrictModule.target, ), - ).to.be.revertedWith('maximum 195 can be unrestricted in one batch'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'MaxCountriesInBatchReached'); }); }); @@ -411,13 +437,13 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData( + new ethers.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData( 'batchUnrestrictCountries', [[12, 42, 67]], ), - countryRestrictModule.address, + countryRestrictModule.target, ), - ).to.be.revertedWith('country not restricted'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'CountryNotRestricted'); }); }); @@ -430,27 +456,26 @@ describe('CountryRestrictModule', () => { await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ + new ethers.Interface(['function batchRestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchRestrictCountries', [ [42, 66], ]), - countryRestrictModule.address, + countryRestrictModule.target, ); const tx = await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData( - 'batchUnrestrictCountries', - [[42, 66]], - ), - countryRestrictModule.address, + new ethers.Interface(['function batchUnrestrictCountries(uint16[] memory countries)']).encodeFunctionData('batchUnrestrictCountries', [ + [42, 66], + ]), + countryRestrictModule.target, ); - await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.address, 42); - await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.address, 66); + await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.target, 42); + await expect(tx).to.emit(countryRestrictModule, 'RemovedRestrictedCountry').withArgs(compliance.target, 66); - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 42)).to.be.false; - expect(await countryRestrictModule.isCountryRestricted(compliance.address, 66)).to.be.false; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 42)).to.be.false; + expect(await countryRestrictModule.isCountryRestricted(compliance.target, 66)).to.be.false; }); }); }); @@ -465,7 +490,7 @@ describe('CountryRestrictModule', () => { await expect( countryRestrictModule.connect(anotherWallet).moduleTransferAction(aliceWallet.address, bobWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(countryRestrictModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -480,12 +505,12 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ + new ethers.Interface(['function moduleTransferAction(address, address, uint256)']).encodeFunctionData('moduleTransferAction', [ aliceWallet.address, bobWallet.address, 10, ]), - countryRestrictModule.address, + countryRestrictModule.target, ), ).to.eventually.be.fulfilled; }); @@ -500,8 +525,9 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).moduleMintAction(anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(countryRestrictModule.connect(anotherWallet).moduleMintAction(anotherWallet.address, 10)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -517,11 +543,11 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ anotherWallet.address, 10, ]), - countryRestrictModule.address, + countryRestrictModule.target, ), ).to.eventually.be.fulfilled; }); @@ -536,8 +562,9 @@ describe('CountryRestrictModule', () => { accounts: { anotherWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); - await expect(countryRestrictModule.connect(anotherWallet).moduleBurnAction(anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(countryRestrictModule.connect(anotherWallet).moduleBurnAction(anotherWallet.address, 10)).to.be.revertedWithCustomError( + countryRestrictModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -553,11 +580,11 @@ describe('CountryRestrictModule', () => { compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ anotherWallet.address, 10, ]), - countryRestrictModule.address, + countryRestrictModule.target, ), ).to.eventually.be.fulfilled; }); @@ -572,21 +599,20 @@ describe('CountryRestrictModule', () => { accounts: { deployer, aliceWallet, bobWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); const contract = await ethers.deployContract('MockContract'); - await compliance.bindToken(contract.address); + await compliance.bindToken(contract.target); await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] calldata countries)']).encodeFunctionData( - 'batchRestrictCountries', - [[42, 66]], - ), - countryRestrictModule.address, + new ethers.Interface(['function batchRestrictCountries(uint16[] calldata countries)']).encodeFunctionData('batchRestrictCountries', [ + [42, 66], + ]), + countryRestrictModule.target, ); await contract.setInvestorCountry(42); - await expect(countryRestrictModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.address)).to.be.eventually.false; + await expect(countryRestrictModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.target)).to.be.eventually.false; }); }); @@ -597,22 +623,64 @@ describe('CountryRestrictModule', () => { accounts: { deployer, aliceWallet, bobWallet }, } = await loadFixture(deployComplianceWithCountryRestrictModule); const contract = await ethers.deployContract('MockContract'); - await compliance.bindToken(contract.address); + await compliance.bindToken(contract.target); await compliance .connect(deployer) .callModuleFunction( - new ethers.utils.Interface(['function batchRestrictCountries(uint16[] calldata countries)']).encodeFunctionData( - 'batchRestrictCountries', - [[42, 66]], - ), - countryRestrictModule.address, + new ethers.Interface(['function batchRestrictCountries(uint16[] calldata countries)']).encodeFunctionData('batchRestrictCountries', [ + [42, 66], + ]), + countryRestrictModule.target, ); await contract.setInvestorCountry(10); - await expect(countryRestrictModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.address)).to.be.eventually.true; + await expect(countryRestrictModule.moduleCheck(aliceWallet.address, bobWallet.address, 16, compliance.target)).to.be.eventually.true; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { countryRestrictModule }, + } = await loadFixture(deployComplianceWithCountryRestrictModule); + + const unsupportedInterfaceId = '0x12345678'; + expect(await countryRestrictModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const { + suite: { countryRestrictModule }, + } = await loadFixture(deployComplianceWithCountryRestrictModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await countryRestrictModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { countryRestrictModule }, + } = await loadFixture(deployComplianceWithCountryRestrictModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await countryRestrictModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { countryRestrictModule }, + } = await loadFixture(deployComplianceWithCountryRestrictModule); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await countryRestrictModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-exchange-monthly-limits.test.ts b/test/compliances/module-exchange-monthly-limits.test.ts index 8adda7e2..6d20b7c5 100644 --- a/test/compliances/module-exchange-monthly-limits.test.ts +++ b/test/compliances/module-exchange-monthly-limits.test.ts @@ -3,15 +3,16 @@ import { ethers, upgrades } from 'hardhat'; import { expect } from 'chai'; import { deployComplianceFixture } from '../fixtures/deploy-compliance.fixture'; import { deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; +import { ExchangeMonthlyLimits } from '../../typechain-types'; async function deployExchangeMonthlyLimitsFixture() { const context = await loadFixture(deployComplianceFixture); const module = await ethers.deployContract('ExchangeMonthlyLimitsModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('ExchangeMonthlyLimitsModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('ExchangeMonthlyLimitsModule', proxy.target); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -26,8 +27,8 @@ async function deployExchangeMonthlyLimitsFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const ExchangeMonthlyLimitsModule = await ethers.getContractFactory('ExchangeMonthlyLimitsModule'); const complianceModule = await upgrades.deployProxy(ExchangeMonthlyLimitsModule, []); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -42,8 +43,8 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { it('should deploy the ExchangeMonthlyLimits contract and bind it to the compliance', async () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); - expect(context.contracts.complianceModule.address).not.to.be.undefined; - expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.address)).to.be.true; + expect(context.contracts.complianceModule.target).not.to.be.undefined; + expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.target)).to.be.true; }); describe('.name()', () => { @@ -64,7 +65,7 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { describe('.canComplianceBind', () => { it('should return true', async () => { const context = await loadFixture(deployExchangeMonthlyLimitsFullSuite); - expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -106,8 +107,14 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.contracts.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.contracts.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -119,9 +126,9 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); - await expect( - context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero), - ).to.revertedWith('Ownable: caller is not the owner'); + await expect(context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -132,11 +139,11 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const newImplementation = await ethers.deployContract('ExchangeMonthlyLimitsModule'); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -147,7 +154,10 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); const exchangeID = context.accounts.anotherWallet.address; - await expect(context.contracts.complianceModule.setExchangeMonthlyLimit(exchangeID, 1)).to.revertedWith('only bound compliance can call'); + await expect(context.contracts.complianceModule.setExchangeMonthlyLimit(exchangeID, 1)).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -157,16 +167,16 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = context.accounts.anotherWallet.address; const tx = await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( 'setExchangeMonthlyLimit', [exchangeID, 100], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect(tx) .to.emit(context.contracts.complianceModule, 'ExchangeMonthlyLimitUpdated') - .withArgs(context.contracts.compliance.address, exchangeID, 100); + .withArgs(context.contracts.compliance.target, exchangeID, 100); }); }); }); @@ -177,14 +187,14 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = context.accounts.anotherWallet.address; await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( 'setExchangeMonthlyLimit', [exchangeID, 100], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); - expect(await context.contracts.complianceModule.getExchangeMonthlyLimit(context.suite.compliance.address, exchangeID)).to.be.eq(100); + expect(await context.contracts.complianceModule.getExchangeMonthlyLimit(context.suite.compliance.target, exchangeID)).to.be.eq(100); }); }); @@ -297,7 +307,10 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -312,24 +325,25 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 100]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 100], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.address, exchangeID, investorID); + const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.target, exchangeID, investorID); expect(counter).to.be.eq(10); }); }); @@ -342,17 +356,17 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, exchangeID, investorID); + const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, exchangeID, investorID); expect(timer).to.be.gt(0); }); }); @@ -364,26 +378,26 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const previousTimer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, exchangeID, investorID); + const previousTimer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, exchangeID, investorID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 11], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, exchangeID, investorID); + const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, exchangeID, investorID); expect(timer).to.be.eq(previousTimer); }); }); @@ -397,27 +411,28 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 100]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 100], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.address, exchangeID, investorID); + const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.target, exchangeID, investorID); expect(counter).to.be.eq(0); - const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, exchangeID, investorID); + const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, exchangeID, investorID); expect(timer).to.be.eq(0); }); }); @@ -433,17 +448,17 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const investorID = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.address, receiverID, investorID); + const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.target, receiverID, investorID); expect(counter).to.be.eq(0); - const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, receiverID, investorID); + const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, receiverID, investorID); expect(timer).to.be.eq(0); }); }); @@ -457,17 +472,17 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const investorID = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.address, receiverID, investorID); + const counter = await context.suite.complianceModule.getMonthlyCounter(context.suite.compliance.target, receiverID, investorID); expect(counter).to.be.eq(0); - const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.address, receiverID, investorID); + const timer = await context.suite.complianceModule.getMonthlyTimer(context.suite.compliance.target, receiverID, investorID); expect(timer).to.be.eq(0); }); }); @@ -484,7 +499,7 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { '0x0000000000000000000000000000000000000000', context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -498,7 +513,7 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { context.accounts.tokenAgent.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -512,7 +527,7 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -527,18 +542,19 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const senderExchangeID = await context.suite.identityRegistry.identity(from); const receiverExchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(senderExchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(senderExchangeID); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(receiverExchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(receiverExchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [receiverExchangeID, 90]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [receiverExchangeID, 90], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -549,16 +565,17 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 90]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 90], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.false; }); }); @@ -569,16 +586,17 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 150], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -589,24 +607,25 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 150], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.false; }); }); @@ -617,24 +636,25 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as ExchangeMonthlyLimits).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)', - ]).encodeFunctionData('setExchangeMonthlyLimit', [exchangeID, 150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeMonthlyLimit(address _exchangeID, uint256 _newExchangeMonthlyLimit)']).encodeFunctionData( + 'setExchangeMonthlyLimit', + [exchangeID, 150], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 40, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 40, context.suite.compliance.target)).to.be.true; }); }); }); @@ -645,8 +665,9 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { it('should revert', async () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); - await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -657,11 +678,11 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -673,8 +694,9 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { it('should revert', async () => { const context = await loadFixture(deployExchangeMonthlyLimitsFixture); - await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -685,14 +707,49 @@ describe('Compliance Module: ExchangeMonthlyLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployExchangeMonthlyLimitsFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.contracts.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployExchangeMonthlyLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployExchangeMonthlyLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployExchangeMonthlyLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-investor-country-cap.test.ts b/test/compliances/module-investor-country-cap.test.ts new file mode 100644 index 00000000..a4153381 --- /dev/null +++ b/test/compliances/module-investor-country-cap.test.ts @@ -0,0 +1,271 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; + +describe('InvestorCountryCapModule', () => { + // Test fixture + async function deployIvestorCountryCapModuleFullSuite() { + const context = await loadFixture(deploySuiteWithModularCompliancesFixture); + + const module = await ethers.deployContract('InvestorCountryCapModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('InvestorCountryCapModule', proxy.target); + + await context.suite.compliance.bindToken(context.suite.token.target); + + await context.suite.token.connect(context.accounts.tokenAgent).pause(); + await complianceModule.batchInitialize(context.suite.compliance.target, [ + context.accounts.aliceWallet.address, + context.accounts.bobWallet.address, + ]); + await context.suite.compliance.addModule(complianceModule.target); + await context.suite.token.connect(context.accounts.tokenAgent).unpause(); + + return { + ...context, + suite: { + ...context.suite, + complianceModule, + }, + }; + } + + describe('Initialization', () => { + it('should initialize correctly', async () => { + const { + suite: { complianceModule }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + expect(await complianceModule.name()).to.equal('InvestorCountryCapModule'); + expect(await complianceModule.isPlugAndPlay()).to.be.false; + }); + }); + + describe('Country Cap Management', () => { + it('should revert when calling directly', async () => { + const { + suite: { complianceModule }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + await expect(complianceModule.setCountryCap(840, 100)).to.be.revertedWithCustomError(complianceModule, 'OnlyBoundComplianceCanCall'); + }); + + it('should set country cap correctly', async () => { + const { + suite: { compliance, complianceModule }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const countryCode = 840; // USA + const cap = 100; + + const tx = await compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [countryCode, cap]), + complianceModule.target, + ); + + await expect(tx).to.emit(complianceModule, 'CountryCapSet').withArgs(countryCode, cap); + }); + + it('should revert when setting cap lower than current count', async () => { + const { + suite: { compliance, complianceModule }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const countryCode = 840; // USA + const cap = 100; + + await compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [countryCode, cap]), + complianceModule.target, + ); + + // Try to set cap lower than current count + await expect( + compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [countryCode, cap - 1]), + complianceModule.target, + ), + ).to.be.revertedWithCustomError(complianceModule, 'CapLowerThanCurrent'); + }); + }); + + describe('Bypassed Identity Management', () => { + it('should add bypassed identity correctly', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const tx = await compliance.callModuleFunction( + new ethers.Interface(['function addBypassedIdentity(address _identity)']).encodeFunctionData('addBypassedIdentity', [aliceWallet.address]), + complianceModule.target, + ); + await expect(tx).to.emit(complianceModule, 'BypassedIdentityAdded').withArgs(aliceWallet.address); + }); + + it('should revert if trying to remove bypassed identity that is not bypassed', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + await expect( + compliance.callModuleFunction( + new ethers.Interface(['function removeBypassedIdentity(address _identity)']).encodeFunctionData('removeBypassedIdentity', [ + aliceWallet.address, + ]), + complianceModule.target, + ), + ).to.be.revertedWithCustomError(complianceModule, 'IdentityNotBypassed'); + }); + + it('should remove bypassed identity correctly', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + await compliance.callModuleFunction( + new ethers.Interface(['function addBypassedIdentity(address _identity)']).encodeFunctionData('addBypassedIdentity', [aliceWallet.address]), + complianceModule.target, + ); + + const tx = await compliance.callModuleFunction( + new ethers.Interface(['function removeBypassedIdentity(address _identity)']).encodeFunctionData('removeBypassedIdentity', [ + aliceWallet.address, + ]), + complianceModule.target, + ); + await expect(tx).to.emit(complianceModule, 'BypassedIdentityRemoved').withArgs(aliceWallet.address); + }); + }); + + describe('Transfer Checks', () => { + it('should allow transfer for bypassed identity', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + await compliance.callModuleFunction( + new ethers.Interface(['function addBypassedIdentity(address _identity)']).encodeFunctionData('addBypassedIdentity', [aliceWallet.address]), + complianceModule.target, + ); + + expect(await complianceModule.moduleCheck(aliceWallet.address, aliceWallet.address, 100, compliance.target)).to.be.true; + }); + + it('should allow transfer when country is not capped', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + expect(await complianceModule.moduleCheck(aliceWallet.address, aliceWallet.address, 100, compliance.target)).to.be.true; + }); + + it('should enforce country cap', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { aliceWallet, charlieWallet, tokenAgent }, + identities: { charlieIdentity }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const aliceCountry = await identityRegistry.investorCountry(aliceWallet.address); + await compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [aliceCountry, 1]), + complianceModule.target, + ); + + await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity, aliceCountry); + + expect(await complianceModule.moduleCheck(aliceWallet.address, charlieWallet.address, 100, compliance.target)).to.be.false; + }); + }); + + describe('Wallet Management', () => { + it('should enforce wallet per identity limit', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { tokenAgent, aliceWallet }, + identities: { aliceIdentity }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const aliceCountry = await identityRegistry.investorCountry(aliceWallet.address); + await compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [aliceCountry, 100]), + complianceModule.target, + ); + + await Promise.all( + Array(21) + .fill(0) + .map(async () => { + const wallet = ethers.Wallet.createRandom().connect(ethers.provider); + await identityRegistry.connect(tokenAgent).registerIdentity(wallet.address, aliceIdentity, aliceCountry); + + expect(await complianceModule.moduleCheck(aliceWallet.address, wallet.address, 10, compliance.target)).to.be.true; + await compliance.callModuleFunction( + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [wallet.address, 10]), + complianceModule.target, + ); + return wallet; + }), + ); + + const wallet = ethers.Wallet.createRandom().connect(ethers.provider); + await identityRegistry.connect(tokenAgent).registerIdentity(wallet.address, aliceIdentity, aliceCountry); + + // This should fail + expect(await complianceModule.moduleCheck(aliceWallet.address, wallet.address, 100, compliance.target)).to.be.false; + }); + }); + + describe('Country Change Handling', () => { + it('should handle country change correctly', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployIvestorCountryCapModuleFullSuite); + + const newCountry = 250; // France + + await compliance.callModuleFunction( + new ethers.Interface(['function setCountryCap(uint16 _country, uint256 _cap)']).encodeFunctionData('setCountryCap', [newCountry, 10]), + complianceModule.target, + ); + + // Setup initial state + expect(await complianceModule.moduleCheck(aliceWallet.address, aliceWallet.address, 100, compliance.target)).to.be.true; + + // Change country + await identityRegistry.connect(tokenAgent).updateCountry(aliceWallet.address, newCountry); + + // Should still be able to transfer after country change + expect(await complianceModule.moduleCheck(aliceWallet.address, aliceWallet.address, 100, compliance.target)).to.be.true; + }); + }); + + describe('Batch Initialize (For gas cost)', () => { + it('should initialize correctly', async () => { + const context = await loadFixture(deploySuiteWithModularCompliancesFixture); + + const module = await ethers.deployContract('InvestorCountryCapModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('InvestorCountryCapModule', proxy.target); + + await context.suite.compliance.bindToken(context.suite.token.target); + + await context.suite.token.connect(context.accounts.tokenAgent).pause(); + await complianceModule.batchInitialize(context.suite.compliance.target, [ + context.accounts.aliceWallet.address, + context.accounts.bobWallet.address, + // context.accounts.charlieWallet.address, + // context.accounts.anotherWallet.address, + ]); + await context.suite.compliance.addModule(complianceModule.target); + await context.suite.token.connect(context.accounts.tokenAgent).unpause(); + }); + }); +}); diff --git a/test/compliances/module-max-balance.test.ts b/test/compliances/module-max-balance.test.ts index 73d37199..7e05b525 100644 --- a/test/compliances/module-max-balance.test.ts +++ b/test/compliances/module-max-balance.test.ts @@ -8,13 +8,13 @@ async function deployMaxBalanceFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('MaxBalanceModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('MaxBalanceModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('MaxBalanceModule', proxy.target); await context.suite.token.connect(context.accounts.tokenAgent).burn(context.accounts.aliceWallet.address, 1000); await context.suite.token.connect(context.accounts.tokenAgent).burn(context.accounts.bobWallet.address, 500); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -29,8 +29,8 @@ describe('Compliance Module: MaxBalance', () => { it('should deploy the MaxBalance contract and bind it to the compliance', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); - expect(context.suite.complianceModule.address).not.to.be.undefined; - expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.address)).to.be.true; + expect(context.suite.complianceModule.target).not.to.be.undefined; + expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.target)).to.be.true; }); describe('.name', () => { @@ -54,7 +54,7 @@ describe('Compliance Module: MaxBalance', () => { it('should return false', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); await context.suite.token.connect(context.accounts.tokenAgent).mint(context.accounts.aliceWallet.address, 1000); - expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.false; }); }); @@ -65,9 +65,9 @@ describe('Compliance Module: MaxBalance', () => { await complianceModule .connect(context.accounts.deployer) - .preSetModuleState(context.suite.compliance.address, context.accounts.aliceWallet.address, 100); + .preSetModuleState(context.suite.compliance.target, context.accounts.aliceWallet.address, 100); - expect(await complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); }); @@ -77,7 +77,7 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployMaxBalanceFullSuite); const complianceModule = await ethers.deployContract('MaxBalanceModule'); - expect(await complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); }); @@ -87,7 +87,10 @@ describe('Compliance Module: MaxBalance', () => { it('should revert', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); - await expect(context.suite.complianceModule.setMaxBalance(100)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.setMaxBalance(100)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -96,11 +99,11 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployMaxBalanceFullSuite); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [100]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [100]), + context.suite.complianceModule.target, ); - await expect(tx).to.emit(context.suite.complianceModule, 'MaxBalanceSet').withArgs(context.suite.compliance.address, 100); + await expect(tx).to.emit(context.suite.complianceModule, 'MaxBalanceSet').withArgs(context.suite.compliance.target, 100); }); }); }); @@ -143,8 +146,14 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployMaxBalanceFullSuite); // when - await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -156,7 +165,7 @@ describe('Compliance Module: MaxBalance', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); - await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero)).to.revertedWith( + await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -169,11 +178,11 @@ describe('Compliance Module: MaxBalance', () => { const newImplementation = await ethers.deployContract('MaxBalanceModule'); // when - await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -185,7 +194,7 @@ describe('Compliance Module: MaxBalance', () => { await expect( context.suite.complianceModule .connect(context.accounts.aliceWallet) - .preSetModuleState(context.suite.compliance.address, context.accounts.aliceWallet.address, 100), + .preSetModuleState(context.suite.compliance.target, context.accounts.aliceWallet.address, 100), ).to.be.revertedWithCustomError(context.suite.complianceModule, `OnlyComplianceOwnerCanCall`); }); }); @@ -197,7 +206,7 @@ describe('Compliance Module: MaxBalance', () => { await expect( context.suite.complianceModule .connect(context.accounts.deployer) - .preSetModuleState(context.suite.compliance.address, context.accounts.aliceWallet.address, 100), + .preSetModuleState(context.suite.compliance.target, context.accounts.aliceWallet.address, 100), ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenAlreadyBound`); }); }); @@ -209,11 +218,11 @@ describe('Compliance Module: MaxBalance', () => { const tx = await complianceModule .connect(context.accounts.deployer) - .preSetModuleState(context.suite.compliance.address, context.accounts.aliceWallet.address, 100); + .preSetModuleState(context.suite.compliance.target, context.accounts.aliceWallet.address, 100); await expect(tx) .to.emit(complianceModule, 'IDBalancePreSet') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address, 100); + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address, 100); }); }); }); @@ -224,7 +233,7 @@ describe('Compliance Module: MaxBalance', () => { it('should revert', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); await expect( - context.suite.complianceModule.connect(context.accounts.aliceWallet).presetCompleted(context.suite.compliance.address), + context.suite.complianceModule.connect(context.accounts.aliceWallet).presetCompleted(context.suite.compliance.target), ).to.be.revertedWithCustomError(context.suite.complianceModule, `OnlyComplianceOwnerCanCall`); }); }); @@ -234,9 +243,9 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployComplianceFixture); const complianceModule = await ethers.deployContract('MaxBalanceModule'); - await complianceModule.connect(context.accounts.deployer).presetCompleted(context.suite.compliance.address); + await complianceModule.connect(context.accounts.deployer).presetCompleted(context.suite.compliance.target); - expect(await complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); }); @@ -248,7 +257,7 @@ describe('Compliance Module: MaxBalance', () => { await expect( context.suite.complianceModule .connect(context.accounts.aliceWallet) - .batchPreSetModuleState(context.suite.compliance.address, [context.accounts.aliceWallet.address], [100]), + .batchPreSetModuleState(context.suite.compliance.target, [context.accounts.aliceWallet.address], [100]), ).to.be.revertedWithCustomError(context.suite.complianceModule, `OnlyComplianceOwnerCanCall`); }); }); @@ -258,7 +267,7 @@ describe('Compliance Module: MaxBalance', () => { it('should revert', async () => { const context = await loadFixture(deployMaxBalanceFullSuite); await expect( - context.suite.complianceModule.connect(context.accounts.deployer).batchPreSetModuleState(context.suite.compliance.address, [], []), + context.suite.complianceModule.connect(context.accounts.deployer).batchPreSetModuleState(context.suite.compliance.target, [], []), ).to.be.revertedWithCustomError(context.suite.complianceModule, `InvalidPresetValues`); }); }); @@ -270,7 +279,7 @@ describe('Compliance Module: MaxBalance', () => { context.suite.complianceModule .connect(context.accounts.deployer) .batchPreSetModuleState( - context.suite.compliance.address, + context.suite.compliance.target, [context.accounts.aliceWallet.address, context.accounts.bobWallet.address], [100], ), @@ -284,7 +293,7 @@ describe('Compliance Module: MaxBalance', () => { await expect( context.suite.complianceModule .connect(context.accounts.deployer) - .batchPreSetModuleState(context.suite.compliance.address, [context.accounts.aliceWallet.address], [100]), + .batchPreSetModuleState(context.suite.compliance.target, [context.accounts.aliceWallet.address], [100]), ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenAlreadyBound`); }); }); @@ -297,16 +306,16 @@ describe('Compliance Module: MaxBalance', () => { const tx = await complianceModule .connect(context.accounts.deployer) .batchPreSetModuleState( - context.suite.compliance.address, + context.suite.compliance.target, [context.accounts.aliceWallet.address, context.accounts.bobWallet.address], [100, 200], ); await expect(tx) .to.emit(complianceModule, 'IDBalancePreSet') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address, 100) + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address, 100) .to.emit(complianceModule, 'IDBalancePreSet') - .withArgs(context.suite.compliance.address, context.accounts.bobWallet.address, 200); + .withArgs(context.suite.compliance.target, context.accounts.bobWallet.address, 200); }); }); }); @@ -319,7 +328,10 @@ describe('Compliance Module: MaxBalance', () => { const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - await expect(context.suite.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -331,38 +343,35 @@ describe('Compliance Module: MaxBalance', () => { const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [ - from, - 150, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [from, 150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [100]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [100]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 40], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.be.revertedWithCustomError(context.suite.complianceModule, `MaxBalanceExceeded`); }); @@ -377,30 +386,27 @@ describe('Compliance Module: MaxBalance', () => { const receiverIdentity = await context.suite.identityRegistry.identity(context.accounts.bobWallet.address); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [ - from, - 150, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [from, 150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 120], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const senderBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.address, senderIdentity); + const senderBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.target, senderIdentity); expect(senderBalance).to.be.eq(30); - const receiverBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.address, receiverIdentity); + const receiverBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.target, receiverIdentity); expect(receiverBalance).to.be.eq(120); }); }); @@ -413,7 +419,10 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployMaxBalanceFullSuite); const to = context.accounts.bobWallet.address; - await expect(context.suite.complianceModule.moduleMintAction(to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.moduleMintAction(to, 10)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -424,17 +433,14 @@ describe('Compliance Module: MaxBalance', () => { const to = context.accounts.aliceWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [ - to, - 160, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [to, 160]), + context.suite.complianceModule.target, ), ).to.be.revertedWithCustomError(context.suite.complianceModule, `MaxBalanceExceeded`); }); @@ -447,16 +453,16 @@ describe('Compliance Module: MaxBalance', () => { const receiverIdentity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [to, 150]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [to, 150]), + context.suite.complianceModule.target, ); - const receiverBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.address, receiverIdentity); + const receiverBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.target, receiverIdentity); expect(receiverBalance).to.be.eq(150); }); }); @@ -469,7 +475,10 @@ describe('Compliance Module: MaxBalance', () => { const context = await loadFixture(deployMaxBalanceFullSuite); const from = context.accounts.bobWallet.address; - await expect(context.suite.complianceModule.moduleBurnAction(from, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.moduleBurnAction(from, 10)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -480,21 +489,21 @@ describe('Compliance Module: MaxBalance', () => { const senderIdentity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [from, 100]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [from, 100]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address _from, uint256 _value)']).encodeFunctionData('moduleBurnAction', [from, 90]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleBurnAction(address _from, uint256 _value)']).encodeFunctionData('moduleBurnAction', [from, 90]), + context.suite.complianceModule.target, ); - const senderBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.address, senderIdentity); + const senderBalance = await context.suite.complianceModule.getIDBalance(context.suite.compliance.target, senderIdentity); expect(senderBalance).to.be.eq(10); }); }); @@ -508,12 +517,13 @@ describe('Compliance Module: MaxBalance', () => { const from = context.accounts.aliceWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); - await expect(context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.address)).to.revertedWith( - 'identity not found', + await expect(context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target)).to.revertedWithCustomError( + context.suite.complianceModule, + 'IdentityNotFound', ); }); }); @@ -525,11 +535,11 @@ describe('Compliance Module: MaxBalance', () => { const from = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 170, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 170, context.suite.compliance.target); expect(result).to.be.false; }); }); @@ -541,16 +551,16 @@ describe('Compliance Module: MaxBalance', () => { const from = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [to, 100]), - context.suite.complianceModule.address, + new ethers.Interface(['function moduleMintAction(address _to, uint256 _value)']).encodeFunctionData('moduleMintAction', [to, 100]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 70, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 70, context.suite.compliance.target); expect(result).to.be.false; }); }); @@ -562,13 +572,48 @@ describe('Compliance Module: MaxBalance', () => { const from = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), - context.suite.complianceModule.address, + new ethers.Interface(['function setMaxBalance(uint256 _max)']).encodeFunctionData('setMaxBalance', [150]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 70, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 70, context.suite.compliance.target); expect(result).to.be.true; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployMaxBalanceFullSuite); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.suite.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployMaxBalanceFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployMaxBalanceFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployMaxBalanceFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-min-transfer-by-country.ts b/test/compliances/module-min-transfer-by-country.ts new file mode 100644 index 00000000..603817e7 --- /dev/null +++ b/test/compliances/module-min-transfer-by-country.ts @@ -0,0 +1,186 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; +import { MinTransferByCountryModule, ModularCompliance } from '../../index.js'; + +describe('MinTransferByCountryModule', () => { + // Test fixture + async function deployMinTransferByCountryModuleFullSuite() { + const context = await loadFixture(deploySuiteWithModularCompliancesFixture); + + const module = await ethers.deployContract('MinTransferByCountryModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('MinTransferByCountryModule', proxy.target); + + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); + + return { + ...context, + suite: { + ...context.suite, + complianceModule, + }, + }; + } + + async function setMinimumTransferAmount( + compliance: ModularCompliance, + complianceModule: MinTransferByCountryModule, + deployer: SignerWithAddress, + countryCode: bigint, + minAmount: bigint, + ) { + return compliance + .connect(deployer) + .callModuleFunction( + new ethers.Interface(['function setMinimumTransferAmount(uint16 country, uint256 amount)']).encodeFunctionData('setMinimumTransferAmount', [ + countryCode, + minAmount, + ]), + complianceModule.target, + ); + } + + describe('Initialization', () => { + it('should initialize correctly', async () => { + const { + suite: { compliance, complianceModule }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + expect(await complianceModule.name()).to.equal('MinTransferByCountryModule'); + expect(await complianceModule.isPlugAndPlay()).to.be.true; + expect(await complianceModule.canComplianceBind(compliance.target)).to.be.true; + }); + }); + + describe('Basic operations', () => { + it('Should mint/burn/transfer tokens if no minimum transfer amount is set', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + await token.connect(tokenAgent).mint(aliceWallet.address, 10); + await token.connect(aliceWallet).transfer(bobWallet.address, 10); + await token.connect(tokenAgent).burn(bobWallet.address, 10); + }); + }); + + describe('Country Settings', () => { + it('should set minimum transfer amount for a country', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { deployer }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = 42n; + const minAmount = ethers.parseEther('100'); + const tx = await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + await expect(tx).to.emit(complianceModule, 'MinimumTransferAmountSet').withArgs(compliance.target, countryCode, minAmount); + }); + + it('should revert when other than compliance tries to set minimum transfer amount', async () => { + const { + suite: { complianceModule }, + accounts: { aliceWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = 1; + const minAmount = ethers.parseEther('100'); + + await expect(complianceModule.connect(aliceWallet).setMinimumTransferAmount(countryCode, minAmount)).to.be.revertedWithCustomError( + complianceModule, + 'OnlyBoundComplianceCanCall', + ); + }); + }); + + describe('Transfer Validation', () => { + it('should allow transfer when amount meets minimum requirement', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = await identityRegistry.investorCountry(aliceWallet.address); + const minAmount = ethers.parseEther('100'); + await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + + const transferAmount = ethers.parseEther('150'); + expect(await complianceModule.moduleCheck(bobWallet.address, aliceWallet.address, transferAmount, compliance.target)).to.be.true; + }); + + it('should prevent transfer when amount is below minimum requirement', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { deployer, charlieWallet, bobWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = await identityRegistry.investorCountry(charlieWallet.address); + const minAmount = ethers.parseEther('100'); + + await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + const transferAmount = ethers.parseEther('99'); + expect(await complianceModule.moduleCheck(bobWallet.address, charlieWallet.address, transferAmount, compliance.target)).to.be.false; + }); + + it('should allow transfer when no minimum amount is set for country', async () => { + const { + suite: { compliance, complianceModule }, + accounts: { aliceWallet, charlieWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + expect(await complianceModule.moduleCheck(aliceWallet.address, charlieWallet.address, 1, compliance.target)).to.be.true; + }); + + it('should allow transfer when user as already a balance', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = await identityRegistry.investorCountry(bobWallet.address); + const minAmount = ethers.parseEther('100'); + + await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + expect(await complianceModule.moduleCheck(aliceWallet.address, bobWallet.address, 1, compliance.target)).to.be.true; + }); + + it('should allow transfer when transfer into same identity and same country with amount below the minimum amount set', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { deployer, aliceWallet, anotherWallet, tokenAgent }, + identities: { aliceIdentity }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = await identityRegistry.investorCountry(aliceWallet.address); + + await identityRegistry.connect(tokenAgent).registerIdentity(anotherWallet.address, aliceIdentity, countryCode); + + const minAmount = ethers.parseEther('100'); + await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + + expect(await complianceModule.moduleCheck(aliceWallet.address, anotherWallet.address, 1, compliance.target)).to.be.true; + }); + + it('should prevent transfer when transfer into same identity and different country with amount below the minimum amount set', async () => { + const { + suite: { compliance, complianceModule, identityRegistry }, + accounts: { deployer, aliceWallet, anotherWallet, tokenAgent }, + identities: { aliceIdentity }, + } = await loadFixture(deployMinTransferByCountryModuleFullSuite); + + const countryCode = 1n + (await identityRegistry.investorCountry(aliceWallet.address)); + + await identityRegistry.connect(tokenAgent).registerIdentity(anotherWallet.address, aliceIdentity, countryCode); + + const minAmount = ethers.parseEther('100'); + await setMinimumTransferAmount(compliance, complianceModule, deployer, countryCode, minAmount); + + expect(await complianceModule.moduleCheck(aliceWallet.address, anotherWallet.address, 1, compliance.target)).to.be.false; + }); + }); +}); diff --git a/test/compliances/module-supply-limit.test.ts b/test/compliances/module-supply-limit.test.ts index a3c800f1..09e13b47 100644 --- a/test/compliances/module-supply-limit.test.ts +++ b/test/compliances/module-supply-limit.test.ts @@ -8,10 +8,10 @@ async function deploySupplyLimitFixture() { const context = await loadFixture(deployComplianceFixture); const module = await ethers.deployContract('SupplyLimitModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('SupplyLimitModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('SupplyLimitModule', proxy.target); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -26,8 +26,8 @@ async function deploySupplyLimitFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const SupplyLimitModule = await ethers.getContractFactory('SupplyLimitModule'); const complianceModule = await upgrades.deployProxy(SupplyLimitModule, []); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -42,8 +42,8 @@ describe('Compliance Module: SupplyLimit', () => { it('should deploy the SupplyLimit contract and bind it to the compliance', async () => { const context = await loadFixture(deploySupplyLimitFixture); - expect(context.suite.complianceModule.address).not.to.be.undefined; - expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.address)).to.be.true; + expect(context.suite.complianceModule.target).not.to.be.undefined; + expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.target)).to.be.true; }); describe('.name()', () => { @@ -64,7 +64,7 @@ describe('Compliance Module: SupplyLimit', () => { describe('.canComplianceBind', () => { it('should return true', async () => { const context = await loadFixture(deploySupplyLimitFullSuite); - expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -106,8 +106,14 @@ describe('Compliance Module: SupplyLimit', () => { const context = await loadFixture(deploySupplyLimitFixture); // when - await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -119,7 +125,7 @@ describe('Compliance Module: SupplyLimit', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deploySupplyLimitFixture); - await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero)).to.revertedWith( + await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -132,11 +138,11 @@ describe('Compliance Module: SupplyLimit', () => { const newImplementation = await ethers.deployContract('SupplyLimitModule'); // when - await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -146,7 +152,10 @@ describe('Compliance Module: SupplyLimit', () => { it('should revert', async () => { const context = await loadFixture(deploySupplyLimitFixture); - await expect(context.suite.complianceModule.setSupplyLimit(100)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.setSupplyLimit(100)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -155,11 +164,11 @@ describe('Compliance Module: SupplyLimit', () => { const context = await loadFixture(deploySupplyLimitFixture); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [100]), - context.suite.complianceModule.address, + new ethers.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [100]), + context.suite.complianceModule.target, ); - await expect(tx).to.emit(context.suite.complianceModule, 'SupplyLimitSet').withArgs(context.suite.compliance.address, 100); + await expect(tx).to.emit(context.suite.complianceModule, 'SupplyLimitSet').withArgs(context.suite.compliance.target, 100); }); }); }); @@ -169,10 +178,10 @@ describe('Compliance Module: SupplyLimit', () => { it('should return', async () => { const context = await loadFixture(deploySupplyLimitFixture); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), - context.suite.complianceModule.address, + new ethers.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), + context.suite.complianceModule.target, ); - const supplyLimit = await context.suite.complianceModule.getSupplyLimit(context.suite.compliance.address); + const supplyLimit = await context.suite.complianceModule.getSupplyLimit(context.suite.compliance.target); expect(supplyLimit).to.be.eq(1600); }); }); @@ -187,11 +196,11 @@ describe('Compliance Module: SupplyLimit', () => { const from = zeroAddress; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), - context.suite.complianceModule.address, + new ethers.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 101, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 101, context.suite.compliance.target); expect(result).to.be.false; }); }); @@ -203,11 +212,11 @@ describe('Compliance Module: SupplyLimit', () => { const from = zeroAddress; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), - context.suite.complianceModule.address, + new ethers.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target); expect(result).to.be.true; }); }); @@ -219,7 +228,7 @@ describe('Compliance Module: SupplyLimit', () => { await expect( context.suite.complianceModule.moduleTransferAction(context.accounts.anotherWallet.address, context.accounts.anotherWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(context.suite.complianceModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -229,11 +238,11 @@ describe('Compliance Module: SupplyLimit', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [context.accounts.anotherWallet.address, context.accounts.anotherWallet.address, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -245,8 +254,9 @@ describe('Compliance Module: SupplyLimit', () => { it('should revert', async () => { const context = await loadFixture(deploySupplyLimitFullSuite); - await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -257,11 +267,11 @@ describe('Compliance Module: SupplyLimit', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -273,8 +283,9 @@ describe('Compliance Module: SupplyLimit', () => { it('should revert', async () => { const context = await loadFixture(deploySupplyLimitFullSuite); - await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -285,15 +296,50 @@ describe('Compliance Module: SupplyLimit', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deploySupplyLimitFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.suite.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deploySupplyLimitFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deploySupplyLimitFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deploySupplyLimitFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-time-exchange-limits.test.ts b/test/compliances/module-time-exchange-limits.test.ts index 4362b4da..b35b6322 100644 --- a/test/compliances/module-time-exchange-limits.test.ts +++ b/test/compliances/module-time-exchange-limits.test.ts @@ -3,15 +3,16 @@ import { ethers, upgrades } from 'hardhat'; import { expect } from 'chai'; import { deployComplianceFixture } from '../fixtures/deploy-compliance.fixture'; import { deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; +import { TimeExchangeLimitsModule } from '../../typechain-types'; async function deployTimeExchangeLimitsFixture() { const context = await loadFixture(deployComplianceFixture); const module = await ethers.deployContract('TimeExchangeLimitsModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('TimeExchangeLimitsModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('TimeExchangeLimitsModule', proxy.target); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -24,10 +25,10 @@ async function deployTimeExchangeLimitsFixture() { async function deployTimeExchangeLimitsFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); - const TimeExchangeLimitsModule = await ethers.getContractFactory('TimeExchangeLimitsModule'); - const complianceModule = await upgrades.deployProxy(TimeExchangeLimitsModule, []); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + const module = await ethers.getContractFactory('TimeExchangeLimitsModule'); + const complianceModule = await upgrades.deployProxy(module, []); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -42,8 +43,8 @@ describe('Compliance Module: TimeExchangeLimits', () => { it('should deploy the TimeExchangeLimits contract and bind it to the compliance', async () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); - expect(context.contracts.complianceModule.address).not.to.be.undefined; - expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.address)).to.be.true; + expect(context.contracts.complianceModule.target).not.to.be.undefined; + expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.target)).to.be.true; }); describe('.name()', () => { @@ -92,8 +93,14 @@ describe('Compliance Module: TimeExchangeLimits', () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.contracts.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.contracts.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -105,9 +112,9 @@ describe('Compliance Module: TimeExchangeLimits', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); - await expect( - context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero), - ).to.revertedWith('Ownable: caller is not the owner'); + await expect(context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -118,11 +125,11 @@ describe('Compliance Module: TimeExchangeLimits', () => { const newImplementation = await ethers.deployContract('TimeExchangeLimitsModule'); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -133,8 +140,9 @@ describe('Compliance Module: TimeExchangeLimits', () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); const exchangeID = context.accounts.anotherWallet.address; - await expect(context.contracts.complianceModule.setExchangeLimit(exchangeID, { limitTime: 1, limitValue: 100 })).to.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.setExchangeLimit(exchangeID, { limitTime: 1, limitValue: 100 })).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -147,17 +155,17 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = context.accounts.anotherWallet.address; const tx = await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 1, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect(tx) .to.emit(context.contracts.complianceModule, 'ExchangeLimitUpdated') - .withArgs(context.contracts.compliance.address, exchangeID, 100, 1); + .withArgs(context.contracts.compliance.target, exchangeID, 100, 1); - const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.address, exchangeID); + const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.target, exchangeID); expect(limits.length).to.be.eq(1); }); }); @@ -167,38 +175,38 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = context.accounts.anotherWallet.address; await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 1, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 2, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 3, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 4, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect( context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 5, limitValue: 100 }]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.be.revertedWithCustomError(context.contracts.complianceModule, `LimitsArraySizeExceeded`); }); @@ -211,24 +219,26 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = context.accounts.anotherWallet.address; await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 1, limitValue: 90 }]), - context.contracts.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 1, limitValue: 90 }], + ), + context.contracts.complianceModule.target, ); const tx = await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 1, limitValue: 100 }]), - context.contracts.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 1, limitValue: 100 }], + ), + context.contracts.complianceModule.target, ); await expect(tx) .to.emit(context.contracts.complianceModule, 'ExchangeLimitUpdated') - .withArgs(context.contracts.compliance.address, exchangeID, 100, 1); + .withArgs(context.contracts.compliance.target, exchangeID, 100, 1); - const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.address, exchangeID); + const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.target, exchangeID); expect(limits.length).to.be.eq(1); expect(limits[0][0]).to.be.eq(1); expect(limits[0][1].toString()).to.be.eq('100'); @@ -243,13 +253,14 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = context.accounts.anotherWallet.address; await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 1, limitValue: 100 }]), - context.contracts.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 1, limitValue: 100 }], + ), + context.contracts.complianceModule.target, ); - const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.address, exchangeID); + const limits = await context.contracts.complianceModule.getExchangeLimits(context.suite.compliance.target, exchangeID); expect(limits.length).to.be.eq(1); expect(limits[0][0]).to.be.eq(1); expect(limits[0][1].toString()).to.be.eq('100'); @@ -264,24 +275,25 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 10000, limitValue: 100 }], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.address, exchangeID, investorID, 10000); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, exchangeID, investorID, 10000); expect(counter.value).to.be.eq(10); }); }); @@ -395,7 +407,10 @@ describe('Compliance Module: TimeExchangeLimits', () => { const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -410,29 +425,24 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter( - context.suite.compliance.address, - exchangeID, - investorID, - 10000, - ); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, exchangeID, investorID, 10000); expect(counter.value).to.be.eq(10); }); }); @@ -445,29 +455,24 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter( - context.suite.compliance.address, - exchangeID, - investorID, - 10000, - ); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, exchangeID, investorID, 10000); expect(counter.timer).to.be.gt(0); }); }); @@ -479,44 +484,39 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const previousCounter = await context.suite.complianceModule.getExchangeCounter( - context.suite.compliance.address, + context.suite.compliance.target, exchangeID, investorID, 10000, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 11], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter( - context.suite.compliance.address, - exchangeID, - investorID, - 10000, - ); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, exchangeID, investorID, 10000); expect(counter.timer).to.be.eq(previousCounter.timer); }); }); @@ -530,24 +530,24 @@ describe('Compliance Module: TimeExchangeLimits', () => { const exchangeID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.address, exchangeID, investorID, 10000); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, exchangeID, investorID, 10000); expect(counter.timer).to.be.eq(0); expect(counter.value).to.be.eq(0); }); @@ -564,21 +564,21 @@ describe('Compliance Module: TimeExchangeLimits', () => { const investorID = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [receiverID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.address, receiverID, investorID, 10000); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, receiverID, investorID, 10000); expect(counter.timer).to.be.eq(0); expect(counter.value).to.be.eq(0); }); @@ -592,24 +592,24 @@ describe('Compliance Module: TimeExchangeLimits', () => { const receiverID = await context.suite.identityRegistry.identity(to); const investorID = await context.suite.identityRegistry.identity(from); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(receiverID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(receiverID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ + new ethers.Interface([ 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', ]).encodeFunctionData('setExchangeLimit', [receiverID, { limitTime: 10000, limitValue: 100 }]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 10], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.address, receiverID, investorID, 10000); + const counter = await context.suite.complianceModule.getExchangeCounter(context.suite.compliance.target, receiverID, investorID, 10000); expect(counter.timer).to.be.eq(0); expect(counter.value).to.be.eq(0); }); @@ -628,7 +628,7 @@ describe('Compliance Module: TimeExchangeLimits', () => { describe('.canComplianceBind', () => { it('should return true', async () => { const context = await loadFixture(deployTimeExchangeLimitsFullSuite); - expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -641,7 +641,7 @@ describe('Compliance Module: TimeExchangeLimits', () => { '0x0000000000000000000000000000000000000000', context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -655,7 +655,7 @@ describe('Compliance Module: TimeExchangeLimits', () => { context.accounts.tokenAgent.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -669,7 +669,7 @@ describe('Compliance Module: TimeExchangeLimits', () => { context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -684,18 +684,19 @@ describe('Compliance Module: TimeExchangeLimits', () => { const senderExchangeID = await context.suite.identityRegistry.identity(from); const receiverExchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(receiverExchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(receiverExchangeID); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(senderExchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(senderExchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [receiverExchangeID, { limitTime: 10000, limitValue: 90 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [receiverExchangeID, { limitTime: 10000, limitValue: 90 }], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -706,16 +707,17 @@ describe('Compliance Module: TimeExchangeLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 90 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 10000, limitValue: 90 }], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.false; }); }); @@ -726,16 +728,17 @@ describe('Compliance Module: TimeExchangeLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 150 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 10000, limitValue: 150 }], + ), + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -746,24 +749,25 @@ describe('Compliance Module: TimeExchangeLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 150 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 10000, limitValue: 150 }], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.false; }); }); @@ -774,24 +778,25 @@ describe('Compliance Module: TimeExchangeLimits', () => { const to = context.accounts.bobWallet.address; const exchangeID = await context.suite.identityRegistry.identity(to); - await context.suite.complianceModule.connect(context.accounts.deployer).addExchangeID(exchangeID); + await (context.suite.complianceModule.connect(context.accounts.deployer) as TimeExchangeLimitsModule).addExchangeID(exchangeID); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface([ - 'function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))', - ]).encodeFunctionData('setExchangeLimit', [exchangeID, { limitTime: 10000, limitValue: 150 }]), - context.suite.complianceModule.address, + new ethers.Interface(['function setExchangeLimit(address _exchangeID, tuple(uint32 limitTime, uint256 limitValue))']).encodeFunctionData( + 'setExchangeLimit', + [exchangeID, { limitTime: 10000, limitValue: 150 }], + ), + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 40, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 40, context.suite.compliance.target)).to.be.true; }); }); }); @@ -802,8 +807,9 @@ describe('Compliance Module: TimeExchangeLimits', () => { it('should revert', async () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); - await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -814,11 +820,11 @@ describe('Compliance Module: TimeExchangeLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -830,8 +836,9 @@ describe('Compliance Module: TimeExchangeLimits', () => { it('should revert', async () => { const context = await loadFixture(deployTimeExchangeLimitsFixture); - await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -842,14 +849,49 @@ describe('Compliance Module: TimeExchangeLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployTimeExchangeLimitsFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.contracts.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployTimeExchangeLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployTimeExchangeLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployTimeExchangeLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-time-transfer-limits.test.ts b/test/compliances/module-time-transfer-limits.test.ts index c3120b61..125541bd 100644 --- a/test/compliances/module-time-transfer-limits.test.ts +++ b/test/compliances/module-time-transfer-limits.test.ts @@ -9,7 +9,7 @@ async function deployTimeTransferLimitsFixture() { const TimeTransfersLimitsModule = await ethers.getContractFactory('TimeTransfersLimitsModule'); const complianceModule = await upgrades.deployProxy(TimeTransfersLimitsModule, []); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -24,11 +24,11 @@ async function deployTimeTransferLimitsFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('TimeTransfersLimitsModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('TimeTransfersLimitsModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('TimeTransfersLimitsModule', proxy.target); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -43,8 +43,8 @@ describe('Compliance Module: TimeTransferLimits', () => { it('should deploy the TimeTransferLimits contract and bind it to the compliance', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - expect(context.contracts.complianceModule.address).not.to.be.undefined; - expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.address)).to.be.true; + expect(context.contracts.complianceModule.target).not.to.be.undefined; + expect(await context.contracts.compliance.isModuleBound(context.contracts.complianceModule.target)).to.be.true; }); describe('.name()', () => { @@ -93,8 +93,14 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFixture); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.contracts.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.contracts.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.contracts.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.contracts.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -106,9 +112,9 @@ describe('Compliance Module: TimeTransferLimits', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - await expect( - context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero), - ).to.revertedWith('Ownable: caller is not the owner'); + await expect(context.contracts.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); }); }); @@ -119,11 +125,11 @@ describe('Compliance Module: TimeTransferLimits', () => { const newImplementation = await ethers.deployContract('TimeTransfersLimitsModule'); // when - await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.contracts.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.contracts.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -133,8 +139,9 @@ describe('Compliance Module: TimeTransferLimits', () => { it('should revert', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - await expect(context.contracts.complianceModule.setTimeTransferLimit({ limitTime: 1, limitValue: 100 })).to.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.setTimeTransferLimit({ limitTime: 1, limitValue: 100 })).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -145,23 +152,23 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFixture); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 1, limitValue: 100 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); const tx = await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 1, limitValue: 50 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect(tx) .to.emit(context.contracts.complianceModule, 'TimeTransferLimitUpdated') - .withArgs(context.contracts.compliance.address, 1, 50); + .withArgs(context.contracts.compliance.target, 1, 50); }); }); @@ -171,40 +178,40 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFixture); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 1, limitValue: 100 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 7, limitValue: 1000 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 30, limitValue: 10000 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 365, limitValue: 100000 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect( context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 3650, limitValue: 1000000 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.be.revertedWithCustomError(context.contracts.complianceModule, `LimitsArraySizeExceeded`); }); @@ -215,16 +222,16 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFixture); const tx = await context.contracts.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 1, limitValue: 100 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await expect(tx) .to.emit(context.contracts.complianceModule, 'TimeTransferLimitUpdated') - .withArgs(context.contracts.compliance.address, 1, 100); + .withArgs(context.contracts.compliance.target, 1, 100); }); }); }); @@ -236,7 +243,7 @@ describe('Compliance Module: TimeTransferLimits', () => { it('should return empty array', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - const limits = await context.contracts.complianceModule.getTimeTransferLimits(context.suite.compliance.address); + const limits = await context.contracts.complianceModule.getTimeTransferLimits(context.suite.compliance.target); expect(limits.length).to.be.eq(0); }); }); @@ -246,22 +253,22 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFixture); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 15, limitValue: 100 }], ), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ); - const limits = await context.contracts.complianceModule.getTimeTransferLimits(context.suite.compliance.address); + const limits = await context.contracts.complianceModule.getTimeTransferLimits(context.suite.compliance.target); expect(limits.length).to.be.eq(2); expect(limits[0].limitTime).to.be.eq(10); expect(limits[0].limitValue).to.be.eq(120); @@ -278,7 +285,10 @@ describe('Compliance Module: TimeTransferLimits', () => { const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.contracts.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -290,35 +300,35 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; const senderIdentity = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 15, limitValue: 100 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const blockTimestamp = await time.latest(); - const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 10); + const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 10); expect(counter1.value).to.be.eq(80); expect(counter1.timer).to.be.eq(blockTimestamp + 10); - const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 15); + const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 15); expect(counter2.value).to.be.eq(80); expect(counter2.timer).to.be.eq(blockTimestamp + 15); }); @@ -331,45 +341,45 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; const senderIdentity = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 100, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 150, limitValue: 100 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 20], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const blockTimestamp = await time.latest(); await time.increase(10); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 30], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 100); + const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 100); expect(counter1.value).to.be.eq(50); expect(counter1.timer).to.be.eq(blockTimestamp + 100); - const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 150); + const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 150); expect(counter2.value).to.be.eq(50); expect(counter2.timer).to.be.eq(blockTimestamp + 150); }); @@ -382,47 +392,47 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; const senderIdentity = await context.suite.identityRegistry.identity(from); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 150, limitValue: 100 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 20], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const blockTimestamp = await time.latest(); await time.increase(30); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 30], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const resetTimestamp = await time.latest(); - const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 10); + const counter1 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 10); expect(counter1.value).to.be.eq(30); expect(counter1.timer).to.be.eq(resetTimestamp + 10); - const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.address, senderIdentity, 150); + const counter2 = await context.suite.complianceModule.usersCounters(context.suite.compliance.target, senderIdentity, 150); expect(counter2.value).to.be.eq(50); expect(counter2.timer).to.be.eq(blockTimestamp + 150); }); @@ -440,7 +450,7 @@ describe('Compliance Module: TimeTransferLimits', () => { describe('.canComplianceBind', () => { it('should return true', async () => { const context = await loadFixture(deployTimeTransferLimitsFullSuite); - expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -453,7 +463,7 @@ describe('Compliance Module: TimeTransferLimits', () => { '0x0000000000000000000000000000000000000000', context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -467,7 +477,7 @@ describe('Compliance Module: TimeTransferLimits', () => { context.accounts.tokenAgent.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.true; }); @@ -478,11 +488,11 @@ describe('Compliance Module: TimeTransferLimits', () => { const context = await loadFixture(deployTimeTransferLimitsFullSuite); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 50 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); expect( @@ -490,7 +500,7 @@ describe('Compliance Module: TimeTransferLimits', () => { context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - context.suite.compliance.address, + context.suite.compliance.target, ), ).to.be.false; }); @@ -504,21 +514,21 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.false; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.false; }); }); @@ -529,14 +539,14 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -547,23 +557,23 @@ describe('Compliance Module: TimeTransferLimits', () => { const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( + new ethers.Interface(['function setTimeTransferLimit(tuple(uint32 limitTime, uint256 limitValue) _limit)']).encodeFunctionData( 'setTimeTransferLimit', [{ limitTime: 10, limitValue: 120 }], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 100], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await time.increase(30); - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); }); @@ -574,8 +584,9 @@ describe('Compliance Module: TimeTransferLimits', () => { it('should revert', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -586,11 +597,11 @@ describe('Compliance Module: TimeTransferLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -602,8 +613,9 @@ describe('Compliance Module: TimeTransferLimits', () => { it('should revert', async () => { const context = await loadFixture(deployTimeTransferLimitsFixture); - await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.contracts.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.contracts.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -614,14 +626,49 @@ describe('Compliance Module: TimeTransferLimits', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.contracts.complianceModule.address, + context.contracts.complianceModule.target, ), ).to.eventually.be.fulfilled; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployTimeTransferLimitsFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.contracts.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployTimeTransferLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployTimeTransferLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployTimeTransferLimitsFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.contracts.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-token-listing-restrictions.test.ts b/test/compliances/module-token-listing-restrictions.test.ts new file mode 100644 index 00000000..ad615a68 --- /dev/null +++ b/test/compliances/module-token-listing-restrictions.test.ts @@ -0,0 +1,718 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { ethers, upgrades } from 'hardhat'; +import { expect } from 'chai'; +import { deploySuiteWithModularCompliancesFixture } from '../fixtures/deploy-full-suite.fixture'; +import { deployComplianceFixture } from '../fixtures/deploy-compliance.fixture'; + +async function deployTokenListingRestrictionsFullSuite() { + const context = await loadFixture(deploySuiteWithModularCompliancesFixture); + const module = await ethers.deployContract('TokenListingRestrictionsModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('TokenListingRestrictionsModule', proxy.target); + + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); + + return { + ...context, + suite: { + ...context.suite, + complianceModule, + }, + }; +} + +describe('Compliance Module: TokenListingRestrictions', () => { + it('should deploy the TokenListingRestrictions contract and bind it to the compliance', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + expect(context.suite.complianceModule.target).not.to.be.undefined; + expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.target)).to.be.true; + }); + + describe('.name', () => { + it('should return the name of the module', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + expect(await context.suite.complianceModule.name()).to.be.equal('TokenListingRestrictionsModule'); + }); + }); + + describe('.isPlugAndPlay', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + expect(await context.suite.complianceModule.isPlugAndPlay()).to.be.true; + }); + }); + + describe('.canComplianceBind', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const complianceModule = await ethers.deployContract('TokenListingRestrictionsModule'); + expect(await complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; + }); + }); + + describe('.owner', () => { + it('should return owner', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect(context.suite.complianceModule.owner()).to.eventually.be.eq(context.accounts.deployer.address); + }); + }); + + describe('.initialize', () => { + it('should be called only once', async () => { + // given + const { + accounts: { deployer }, + } = await loadFixture(deployComplianceFixture); + const module = (await ethers.deployContract('TokenListingRestrictionsModule')).connect(deployer); + await module.initialize(); + + // when & then + await expect(module.initialize()).to.be.revertedWith('Initializable: contract is already initialized'); + expect(await module.owner()).to.be.eq(deployer.address); + }); + }); + + describe('.transferOwnership', () => { + describe('when calling directly', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).transferOwnership(context.accounts.bobWallet.address), + ).to.revertedWith('Ownable: caller is not the owner'); + }); + }); + + describe('when calling with owner account', () => { + it('should transfer ownership', async () => { + // given + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + // when + const tx1 = await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + + // then + const owner = await context.suite.complianceModule.owner(); + expect(owner).to.eq(context.accounts.bobWallet.address); + }); + }); + }); + + describe('.upgradeTo', () => { + describe('when calling directly', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); + }); + }); + + describe('when calling with owner account', () => { + it('should upgrade proxy', async () => { + // given + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const newImplementation = await ethers.deployContract('TokenListingRestrictionsModule'); + + // when + await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); + + // then + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); + }); + }); + }); + + describe('.configureToken', () => { + describe('when calling directly', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect(context.suite.complianceModule.configureToken(1)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); + }); + }); + + describe('when calling via compliance', () => { + describe('when given listing type is zero (NOT_CONFIGURED)', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect( + context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [0]), + context.suite.complianceModule.target, + ), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `InvalidListingTypeForConfiguration`); + }); + }); + + describe('when token is already configured', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await expect( + context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [2]), + context.suite.complianceModule.target, + ), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenAlreadyConfigured`); + }); + }); + + describe('when token is not configured before', () => { + it('should configure the token', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + const tx = await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await expect(tx).to.emit(context.suite.complianceModule, 'TokenListingConfigured').withArgs(context.suite.token.target, 1); + }); + }); + }); + }); + + describe('.listToken', () => { + describe('when token is not configured', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenIsNotConfigured`); + }); + }); + + describe('when token is configured', () => { + describe('when token is listed before', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenAlreadyListed`); + }); + }); + + describe('when token is not already listed', () => { + describe('when the investor address type is WALLET', () => { + it('should list the token', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenListed') + .withArgs(context.suite.token.target, context.accounts.aliceWallet.address); + }); + }); + + describe('when the investor address type is ONCHAINID', () => { + describe('when identity does not exist', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await expect( + context.suite.complianceModule.connect(context.accounts.anotherWallet).listToken(context.suite.token.target, 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `IdentityNotFound`); + }); + }); + + describe('when identity exists', () => { + it('should list the token', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenListed') + .withArgs(context.suite.token.target, context.identities.aliceIdentity.target); + }); + }); + }); + }); + }); + }); + + describe('.batchListTokens', () => { + describe('when token is not configured', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).batchListTokens([context.suite.token.target], 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenIsNotConfigured`); + }); + }); + + describe('when token is configured', () => { + describe('when token is listed before', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).batchListTokens([context.suite.token.target], 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenAlreadyListed`); + }); + }); + + describe('when token is not already listed', () => { + describe('when the investor address type is WALLET', () => { + it('should list tokens', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).batchListTokens([context.suite.token.target], 0); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenListed') + .withArgs(context.suite.token.target, context.accounts.aliceWallet.address); + }); + }); + + describe('when the investor address type is ONCHAINID', () => { + it('should list tokens', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).batchListTokens([context.suite.token.target], 1); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenListed') + .withArgs(context.suite.token.target, context.identities.aliceIdentity.target); + }); + }); + }); + }); + }); + + describe('.unlistToken', () => { + describe('when token is not listed', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).unlistToken(context.suite.token.target, 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenIsNotListed`); + }); + }); + + describe('when token is listed', () => { + describe('when the investor address type is WALLET', () => { + it('should unlist the token', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).unlistToken(context.suite.token.target, 0); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenUnlisted') + .withArgs(context.suite.token.target, context.accounts.aliceWallet.address); + }); + }); + + describe('when the investor address type is ONCHAINID', () => { + it('should unlist the token', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).unlistToken(context.suite.token.target, 1); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenUnlisted') + .withArgs(context.suite.token.target, context.identities.aliceIdentity.target); + }); + }); + }); + }); + + describe('.batchUnlistTokens', () => { + describe('when token is not listed', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await expect( + context.suite.complianceModule.connect(context.accounts.aliceWallet).batchUnlistTokens([context.suite.token.target], 1), + ).to.be.revertedWithCustomError(context.suite.complianceModule, `TokenIsNotListed`); + }); + }); + + describe('when token is not listed', () => { + describe('when the investor address type is WALLET', () => { + it('should unlist tokens', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).batchUnlistTokens([context.suite.token.target], 0); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenUnlisted') + .withArgs(context.suite.token.target, context.accounts.aliceWallet.address); + }); + }); + + describe('when the investor address type is ONCHAINID', () => { + it('should unlist tokens', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + const tx = await context.suite.complianceModule.connect(context.accounts.aliceWallet).batchUnlistTokens([context.suite.token.target], 1); + + await expect(tx) + .to.emit(context.suite.complianceModule, 'TokenUnlisted') + .withArgs(context.suite.token.target, context.identities.aliceIdentity.target); + }); + }); + }); + }); + + describe('.getTokenListingType', () => { + describe('when token is not configured', () => { + it('should return NOT_CONFIGURED(0)', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const result = await context.suite.complianceModule.getTokenListingType(context.suite.token.target); + expect(result).to.be.eq(0); + }); + }); + + describe('when token is configured', () => { + it('should return token listing type', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const result = await context.suite.complianceModule.getTokenListingType(context.suite.token.target); + expect(result).to.be.eq(1); + }); + }); + }); + + describe('.getInvestorListingStatus', () => { + describe('when token is not listed', () => { + it('should return false', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const result = await context.suite.complianceModule.getInvestorListingStatus( + context.suite.token.target, + context.accounts.aliceWallet.address, + ); + expect(result).to.be.false; + }); + }); + + describe('when token is listed', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + const result = await context.suite.complianceModule.getInvestorListingStatus( + context.suite.token.target, + context.accounts.aliceWallet.address, + ); + expect(result).to.be.true; + }); + }); + }); + + describe('.moduleCheck', () => { + describe('when receiver is the null address', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const from = context.accounts.aliceWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const result = await context.suite.complianceModule.moduleCheck(from, ethers.ZeroAddress, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + + describe('when token is not configured', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const from = context.accounts.aliceWallet.address; + const to = context.accounts.bobWallet.address; + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + + describe('when the listing type is WHITELISTING', () => { + describe('when token is not listed', () => { + it('should return false', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.false; + }); + }); + + describe('when token is listed for the wallet', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + + describe('when token is listed for the OID', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [1]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + }); + + describe('when the listing type is BLACKLISTING', () => { + describe('when token is not listed', () => { + it('should return true', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [2]), + context.suite.complianceModule.target, + ); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + + describe('when token is listed for the wallet', () => { + it('should return false', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [2]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 0); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.false; + }); + }); + + describe('when token is listed for the OID', () => { + it('should return false', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + const to = context.accounts.aliceWallet.address; + const from = context.accounts.bobWallet.address; + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function configureToken(uint8 _listingType)']).encodeFunctionData('configureToken', [2]), + context.suite.complianceModule.target, + ); + + await context.suite.complianceModule.connect(context.accounts.aliceWallet).listToken(context.suite.token.target, 1); + + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.false; + }); + }); + }); + }); + + describe('.moduleMintAction', () => { + describe('when calling from a random wallet', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); + }); + }); + + describe('when calling as the compliance', () => { + it('should do nothing', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect( + context.suite.compliance.callModuleFunction( + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + context.accounts.anotherWallet.address, + 10, + ]), + context.suite.complianceModule.target, + ), + ).to.eventually.be.fulfilled; + }); + }); + }); + + describe('.moduleBurnAction', () => { + describe('when calling from a random wallet', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); + }); + }); + + describe('when calling as the compliance', () => { + it('should do nothing', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect( + context.suite.compliance.callModuleFunction( + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + context.accounts.anotherWallet.address, + 10, + ]), + context.suite.complianceModule.target, + ), + ).to.eventually.be.fulfilled; + }); + }); + }); + + describe('.moduleTransfer', () => { + describe('when calling from a random wallet', () => { + it('should revert', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect( + context.suite.complianceModule.moduleTransferAction(context.accounts.aliceWallet.address, context.accounts.anotherWallet.address, 10), + ).to.be.revertedWithCustomError(context.suite.complianceModule, 'OnlyBoundComplianceCanCall'); + }); + }); + + describe('when calling as the compliance', () => { + it('should do nothing', async () => { + const context = await loadFixture(deployTokenListingRestrictionsFullSuite); + + await expect( + context.suite.compliance.callModuleFunction( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + 'moduleTransferAction', + [context.accounts.aliceWallet.address, context.accounts.anotherWallet.address, 80], + ), + context.suite.complianceModule.target, + ), + ).to.eventually.be.fulfilled; + }); + }); + }); +}); diff --git a/test/compliances/module-transfer-fees.test.ts b/test/compliances/module-transfer-fees.test.ts index c63e5b6f..8c157949 100644 --- a/test/compliances/module-transfer-fees.test.ts +++ b/test/compliances/module-transfer-fees.test.ts @@ -8,12 +8,12 @@ async function deployTransferFeesFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('TransferFeesModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('TransferFeesModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('TransferFeesModule', proxy.target); - await context.suite.token.addAgent(complianceModule.address); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.token.addAgent(complianceModule.target); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); const identity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); await context.suite.identityRegistry.connect(context.accounts.tokenAgent).registerIdentity(context.accounts.charlieWallet.address, identity, 0); @@ -31,8 +31,8 @@ describe('Compliance Module: TransferFees', () => { it('should deploy the TransferFees contract and bind it to the compliance', async () => { const context = await loadFixture(deployTransferFeesFullSuite); - expect(context.suite.complianceModule.address).not.to.be.undefined; - expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.address)).to.be.true; + expect(context.suite.complianceModule.target).not.to.be.undefined; + expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.target)).to.be.true; }); describe('.owner', () => { @@ -58,8 +58,14 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); // when - await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -86,7 +92,7 @@ describe('Compliance Module: TransferFees', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployTransferFeesFullSuite); - await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero)).to.revertedWith( + await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -99,11 +105,11 @@ describe('Compliance Module: TransferFees', () => { const newImplementation = await ethers.deployContract('TransferFeesModule'); // when - await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -114,8 +120,9 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.anotherWallet.address; - await expect(context.suite.complianceModule.connect(context.accounts.anotherWallet).setFee(1, collector)).to.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.connect(context.accounts.anotherWallet).setFee(1, collector)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -128,8 +135,8 @@ describe('Compliance Module: TransferFees', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [10001, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [10001, collector]), + context.suite.complianceModule.target, ), ).to.be.revertedWithCustomError(context.suite.complianceModule, `FeeRateIsOutOfRange`); }); @@ -142,8 +149,8 @@ describe('Compliance Module: TransferFees', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), + context.suite.complianceModule.target, ), ).to.be.revertedWithCustomError(context.suite.complianceModule, `CollectorAddressIsNotVerified`); }); @@ -155,13 +162,13 @@ describe('Compliance Module: TransferFees', () => { const collector = context.accounts.aliceWallet.address; const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), + context.suite.complianceModule.target, ); - await expect(tx).to.emit(context.suite.complianceModule, 'FeeUpdated').withArgs(context.suite.compliance.address, 1, collector); + await expect(tx).to.emit(context.suite.complianceModule, 'FeeUpdated').withArgs(context.suite.compliance.target, 1, collector); - const fee = await context.suite.complianceModule.getFee(context.suite.compliance.address); + const fee = await context.suite.complianceModule.getFee(context.suite.compliance.target); expect(fee.rate).to.be.eq(1); expect(fee.collector).to.be.eq(collector); }); @@ -174,11 +181,11 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.aliceWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), + context.suite.complianceModule.target, ); - const fee = await context.suite.complianceModule.getFee(context.suite.compliance.address); + const fee = await context.suite.complianceModule.getFee(context.suite.compliance.target); expect(fee.rate).to.be.eq(1); expect(fee.collector).to.be.eq(collector); }); @@ -195,10 +202,10 @@ describe('Compliance Module: TransferFees', () => { describe('when the module is not registered as a token agent', () => { it('should return false', async () => { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); - await context.suite.compliance.bindToken(context.suite.token.address); + await context.suite.compliance.bindToken(context.suite.token.target); const complianceModule = await ethers.deployContract('TransferFeesModule'); - const result = await complianceModule.canComplianceBind(context.suite.compliance.address); + const result = await complianceModule.canComplianceBind(context.suite.compliance.target); expect(result).to.be.false; }); }); @@ -206,7 +213,7 @@ describe('Compliance Module: TransferFees', () => { describe('when the module is registered as a token agent', () => { it('should return true', async () => { const context = await loadFixture(deployTransferFeesFullSuite); - const result = await context.suite.complianceModule.canComplianceBind(context.suite.compliance.address); + const result = await context.suite.complianceModule.canComplianceBind(context.suite.compliance.target); expect(result).to.be.true; }); }); @@ -219,7 +226,10 @@ describe('Compliance Module: TransferFees', () => { const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - await expect(context.suite.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWith('only bound compliance can call'); + await expect(context.suite.complianceModule.moduleTransferAction(from, to, 10)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', + ); }); }); @@ -229,8 +239,8 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), + context.suite.complianceModule.target, ); const from = context.accounts.aliceWallet.address; @@ -239,11 +249,11 @@ describe('Compliance Module: TransferFees', () => { await context.suite.identityRegistry.connect(context.accounts.tokenAgent).registerIdentity(to, identity, 0); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -256,19 +266,19 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [0, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [0, collector]), + context.suite.complianceModule.target, ); const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -281,18 +291,18 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), + context.suite.complianceModule.target, ); const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [collector, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -305,19 +315,19 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), + context.suite.complianceModule.target, ); const from = context.accounts.bobWallet.address; await context.suite.token.connect(context.accounts.tokenAgent).mint(collector, 5000); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, collector, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -330,19 +340,19 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1, collector]), + context.suite.complianceModule.target, ); const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -355,19 +365,19 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const collector = context.accounts.charlieWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), - context.suite.complianceModule.address, + new ethers.Interface(['function setFee(uint256 _rate, address _collector)']).encodeFunctionData('setFee', [1000, collector]), + context.suite.complianceModule.target, ); const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [from, to, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const collectedAmount = await context.suite.token.balanceOf(collector); @@ -385,8 +395,9 @@ describe('Compliance Module: TransferFees', () => { it('should revert', async () => { const context = await loadFixture(deployTransferFeesFullSuite); - await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -397,11 +408,11 @@ describe('Compliance Module: TransferFees', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -413,8 +424,9 @@ describe('Compliance Module: TransferFees', () => { it('should revert', async () => { const context = await loadFixture(deployTransferFeesFullSuite); - await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -425,11 +437,11 @@ describe('Compliance Module: TransferFees', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -441,7 +453,7 @@ describe('Compliance Module: TransferFees', () => { const context = await loadFixture(deployTransferFeesFullSuite); const from = context.accounts.aliceWallet.address; const to = context.accounts.bobWallet.address; - expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.address)).to.be.true; + expect(await context.suite.complianceModule.moduleCheck(from, to, 100, context.suite.compliance.target)).to.be.true; }); }); @@ -451,4 +463,39 @@ describe('Compliance Module: TransferFees', () => { expect(await context.suite.complianceModule.name()).to.be.equal('TransferFeesModule'); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployTransferFeesFullSuite); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.suite.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployTransferFeesFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployTransferFeesFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployTransferFeesFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/compliances/module-transfer-restrict.test.ts b/test/compliances/module-transfer-restrict.test.ts index 3a560dff..f0bae22d 100644 --- a/test/compliances/module-transfer-restrict.test.ts +++ b/test/compliances/module-transfer-restrict.test.ts @@ -7,11 +7,11 @@ import { deployComplianceFixture } from '../fixtures/deploy-compliance.fixture'; async function deployTransferRestrictFullSuite() { const context = await loadFixture(deploySuiteWithModularCompliancesFixture); const module = await ethers.deployContract('TransferRestrictModule'); - const proxy = await ethers.deployContract('ModuleProxy', [module.address, module.interface.encodeFunctionData('initialize')]); - const complianceModule = await ethers.getContractAt('TransferRestrictModule', proxy.address); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const complianceModule = await ethers.getContractAt('TransferRestrictModule', proxy.target); - await context.suite.compliance.bindToken(context.suite.token.address); - await context.suite.compliance.addModule(complianceModule.address); + await context.suite.compliance.bindToken(context.suite.token.target); + await context.suite.compliance.addModule(complianceModule.target); return { ...context, @@ -26,8 +26,8 @@ describe('Compliance Module: TransferRestrict', () => { it('should deploy the TransferRestrict contract and bind it to the compliance', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - expect(context.suite.complianceModule.address).not.to.be.undefined; - expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.address)).to.be.true; + expect(context.suite.complianceModule.target).not.to.be.undefined; + expect(await context.suite.compliance.isModuleBound(context.suite.complianceModule.target)).to.be.true; }); describe('.name', () => { @@ -49,7 +49,7 @@ describe('Compliance Module: TransferRestrict', () => { it('should return true', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); const complianceModule = await ethers.deployContract('TransferRestrictModule'); - expect(await complianceModule.canComplianceBind(context.suite.compliance.address)).to.be.true; + expect(await complianceModule.canComplianceBind(context.suite.compliance.target)).to.be.true; }); }); @@ -91,8 +91,14 @@ describe('Compliance Module: TransferRestrict', () => { const context = await loadFixture(deployTransferRestrictFullSuite); // when - await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); - + const tx1 = await context.suite.complianceModule.connect(context.accounts.deployer).transferOwnership(context.accounts.bobWallet.address); + expect(tx1) + .to.emit(context.suite.complianceModule, 'OwnershipTransferStarted') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); + const tx2 = await context.suite.complianceModule.connect(context.accounts.bobWallet).acceptOwnership(); + expect(tx2) + .to.emit(context.suite.complianceModule, 'OwnershipTransferred') + .withArgs(context.accounts.deployer.address, context.accounts.bobWallet.address); // then const owner = await context.suite.complianceModule.owner(); expect(owner).to.eq(context.accounts.bobWallet.address); @@ -104,7 +110,7 @@ describe('Compliance Module: TransferRestrict', () => { describe('when calling directly', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.constants.AddressZero)).to.revertedWith( + await expect(context.suite.complianceModule.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -117,11 +123,11 @@ describe('Compliance Module: TransferRestrict', () => { const newImplementation = await ethers.deployContract('TransferRestrictModule'); // when - await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.address); + await context.suite.complianceModule.connect(context.accounts.deployer).upgradeTo(newImplementation.target); // then - const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.address); - expect(implementationAddress).to.eq(newImplementation.address); + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.complianceModule.target); + expect(implementationAddress).to.eq(newImplementation.target); }); }); }); @@ -131,8 +137,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.allowUser(context.accounts.aliceWallet.address)).to.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.allowUser(context.accounts.aliceWallet.address)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -142,15 +149,13 @@ describe('Compliance Module: TransferRestrict', () => { const context = await loadFixture(deployTransferRestrictFullSuite); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [ - context.accounts.aliceWallet.address, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [context.accounts.aliceWallet.address]), + context.suite.complianceModule.target, ); await expect(tx) .to.emit(context.suite.complianceModule, 'UserAllowed') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address); + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address); }); }); }); @@ -160,8 +165,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.batchAllowUsers([context.accounts.aliceWallet.address])).to.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.batchAllowUsers([context.accounts.aliceWallet.address])).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -171,17 +177,17 @@ describe('Compliance Module: TransferRestrict', () => { const context = await loadFixture(deployTransferRestrictFullSuite); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function batchAllowUsers(address[] _identities)']).encodeFunctionData('batchAllowUsers', [ + new ethers.Interface(['function batchAllowUsers(address[] _identities)']).encodeFunctionData('batchAllowUsers', [ [context.accounts.aliceWallet.address, context.accounts.bobWallet.address], ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await expect(tx) .to.emit(context.suite.complianceModule, 'UserAllowed') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address) + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address) .to.emit(context.suite.complianceModule, 'UserAllowed') - .withArgs(context.suite.compliance.address, context.accounts.bobWallet.address); + .withArgs(context.suite.compliance.target, context.accounts.bobWallet.address); }); }); }); @@ -191,8 +197,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.disallowUser(context.accounts.aliceWallet.address)).to.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.disallowUser(context.accounts.aliceWallet.address)).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -201,22 +208,20 @@ describe('Compliance Module: TransferRestrict', () => { it('should disallow user', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [ - context.accounts.aliceWallet.address, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [context.accounts.aliceWallet.address]), + context.suite.complianceModule.target, ); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function disallowUser(address _userAddress)']).encodeFunctionData('disallowUser', [ + new ethers.Interface(['function disallowUser(address _userAddress)']).encodeFunctionData('disallowUser', [ context.accounts.aliceWallet.address, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await expect(tx) .to.emit(context.suite.complianceModule, 'UserDisallowed') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address); + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address); }); }); }); @@ -226,8 +231,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.batchDisallowUsers([context.accounts.aliceWallet.address])).to.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.batchDisallowUsers([context.accounts.aliceWallet.address])).to.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -236,24 +242,24 @@ describe('Compliance Module: TransferRestrict', () => { it('should disallow user', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function batchAllowUsers(address[] _identities)']).encodeFunctionData('batchAllowUsers', [ + new ethers.Interface(['function batchAllowUsers(address[] _identities)']).encodeFunctionData('batchAllowUsers', [ [context.accounts.aliceWallet.address, context.accounts.bobWallet.address], ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function batchDisallowUsers(address[] _identities)']).encodeFunctionData('batchDisallowUsers', [ + new ethers.Interface(['function batchDisallowUsers(address[] _identities)']).encodeFunctionData('batchDisallowUsers', [ [context.accounts.aliceWallet.address, context.accounts.bobWallet.address], ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ); await expect(tx) .to.emit(context.suite.complianceModule, 'UserDisallowed') - .withArgs(context.suite.compliance.address, context.accounts.aliceWallet.address) + .withArgs(context.suite.compliance.target, context.accounts.aliceWallet.address) .to.emit(context.suite.complianceModule, 'UserDisallowed') - .withArgs(context.suite.compliance.address, context.accounts.bobWallet.address); + .withArgs(context.suite.compliance.target, context.accounts.bobWallet.address); }); }); }); @@ -263,13 +269,11 @@ describe('Compliance Module: TransferRestrict', () => { it('should return true', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [ - context.accounts.aliceWallet.address, - ]), - context.suite.complianceModule.address, + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [context.accounts.aliceWallet.address]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.isUserAllowed(context.suite.compliance.address, context.accounts.aliceWallet.address); + const result = await context.suite.complianceModule.isUserAllowed(context.suite.compliance.target, context.accounts.aliceWallet.address); expect(result).to.be.true; }); }); @@ -277,7 +281,7 @@ describe('Compliance Module: TransferRestrict', () => { describe('when user is not allowed', () => { it('should return false', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - const result = await context.suite.complianceModule.isUserAllowed(context.suite.compliance.address, context.accounts.aliceWallet.address); + const result = await context.suite.complianceModule.isUserAllowed(context.suite.compliance.target, context.accounts.aliceWallet.address); expect(result).to.be.false; }); }); @@ -289,11 +293,31 @@ describe('Compliance Module: TransferRestrict', () => { const context = await loadFixture(deployTransferRestrictFullSuite); const to = context.accounts.anotherWallet.address; const from = context.accounts.aliceWallet.address; - const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); expect(result).to.be.false; }); }); + describe('when sender is the null address', () => { + it('should return true', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + const to = context.accounts.anotherWallet.address; + const from = ethers.ZeroAddress; + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + + describe('when recipient is the null address', () => { + it('should return true', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + const to = ethers.ZeroAddress; + const from = context.accounts.aliceWallet.address; + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); + expect(result).to.be.true; + }); + }); + describe('when sender is allowed', () => { it('should return true', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); @@ -301,11 +325,11 @@ describe('Compliance Module: TransferRestrict', () => { const from = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [from]), - context.suite.complianceModule.address, + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [from]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); expect(result).to.be.true; }); }); @@ -317,11 +341,11 @@ describe('Compliance Module: TransferRestrict', () => { const from = context.accounts.bobWallet.address; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [to]), - context.suite.complianceModule.address, + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [to]), + context.suite.complianceModule.target, ); - const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.address); + const result = await context.suite.complianceModule.moduleCheck(from, to, 10, context.suite.compliance.target); expect(result).to.be.true; }); }); @@ -332,8 +356,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleMintAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -344,11 +369,11 @@ describe('Compliance Module: TransferRestrict', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ + new ethers.Interface(['function moduleMintAction(address, uint256)']).encodeFunctionData('moduleMintAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -360,8 +385,9 @@ describe('Compliance Module: TransferRestrict', () => { it('should revert', async () => { const context = await loadFixture(deployTransferRestrictFullSuite); - await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWith( - 'only bound compliance can call', + await expect(context.suite.complianceModule.moduleBurnAction(context.accounts.anotherWallet.address, 10)).to.be.revertedWithCustomError( + context.suite.complianceModule, + 'OnlyBoundComplianceCanCall', ); }); }); @@ -372,11 +398,11 @@ describe('Compliance Module: TransferRestrict', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ + new ethers.Interface(['function moduleBurnAction(address, uint256)']).encodeFunctionData('moduleBurnAction', [ context.accounts.anotherWallet.address, 10, ]), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); @@ -390,7 +416,7 @@ describe('Compliance Module: TransferRestrict', () => { await expect( context.suite.complianceModule.moduleTransferAction(context.accounts.aliceWallet.address, context.accounts.anotherWallet.address, 10), - ).to.be.revertedWith('only bound compliance can call'); + ).to.be.revertedWithCustomError(context.suite.complianceModule, 'OnlyBoundComplianceCanCall'); }); }); @@ -400,14 +426,49 @@ describe('Compliance Module: TransferRestrict', () => { await expect( context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( + new ethers.Interface(['function moduleTransferAction(address _from, address _to, uint256 _value)']).encodeFunctionData( 'moduleTransferAction', [context.accounts.aliceWallet.address, context.accounts.anotherWallet.address, 80], ), - context.suite.complianceModule.address, + context.suite.complianceModule.target, ), ).to.eventually.be.fulfilled; }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.suite.complianceModule.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IModule interface ID', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iModuleInterfaceId = await interfaceIdCalculator.getIModuleInterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(iModuleInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployTransferRestrictFullSuite); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.suite.complianceModule.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/dva.test.ts b/test/dva.test.ts index f21c20a0..3d33421e 100644 --- a/test/dva.test.ts +++ b/test/dva.test.ts @@ -1,16 +1,17 @@ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { Signer } from 'ethers'; +import { ethers, upgrades } from 'hardhat'; -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { deployFullSuiteFixture } from './fixtures/deploy-full-suite.fixture'; describe('DVATransferManager', () => { async function deployFullSuiteWithTransferManager() { const context = await loadFixture(deployFullSuiteFixture); - const transferManager = await ethers.deployContract('DVATransferManager'); - + const implementation = await ethers.deployContract('DVATransferManager'); + const transferManagerProxy = await ethers.deployContract('DVATransferManagerProxy', [implementation.target, '0x8129fc1c']); + const transferManager = await ethers.getContractAt('DVATransferManager', transferManagerProxy.target); return { ...context, suite: { @@ -22,31 +23,32 @@ describe('DVATransferManager', () => { async function deployFullSuiteWithVerifiedTransferManager() { const context = await loadFixture(deployFullSuiteWithTransferManager); + await context.suite.token.connect(context.accounts.deployer).addAgent(context.suite.transferManager.target); const identity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); - await context.suite.identityRegistry.connect(context.accounts.tokenAgent).registerIdentity(context.suite.transferManager.address, identity, 0); + await context.suite.identityRegistry.connect(context.accounts.tokenAgent).registerIdentity(context.suite.transferManager.target, identity, 0); return context; } async function signTransfer( transferID: string, - signer: SignerWithAddress, + signer: Signer, ): Promise<{ v: number; r: string; s: string; }> { - const rawSignature = await signer.signMessage(ethers.utils.arrayify(transferID)); - const { v, r, s } = ethers.utils.splitSignature(rawSignature); + const rawSignature = await signer.signMessage(ethers.getBytes(transferID)); + const { v, r, s } = ethers.Signature.from(rawSignature); return { v, r, s }; } async function deployFullSuiteWithNonSequentialTransfer() { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, false, [context.accounts.charlieWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, false, [context.accounts.charlieWallet.address]); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -56,7 +58,7 @@ describe('DVATransferManager', () => { await context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); return { ...context, @@ -67,10 +69,10 @@ describe('DVATransferManager', () => { async function deployFullSuiteWithSequentialTransfer() { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [context.accounts.charlieWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [context.accounts.charlieWallet.address]); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -80,7 +82,7 @@ describe('DVATransferManager', () => { await context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); return { ...context, @@ -88,6 +90,29 @@ describe('DVATransferManager', () => { }; } + describe('.initialize', () => { + describe('when the contract is not initialized before', () => { + it('should initialize', async () => { + const context = await loadFixture(deployFullSuiteWithTransferManager); + + const implementation = await ethers.deployContract('DVATransferManager'); + const transferManagerProxy = await ethers.deployContract('DVATransferManagerProxy', [implementation.target, '0x']); + const transferManager = await ethers.getContractAt('DVATransferManager', transferManagerProxy.target); + await expect(transferManager.connect(context.accounts.deployer).initialize()).to.eventually.be.fulfilled; + await expect(transferManager.owner()).to.eventually.be.eq(context.accounts.deployer.address); + }); + }); + + describe('when the contract is already initialized', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteWithTransferManager); + // @dev Line below is commented due to Hardhat incompatibility with viaIR, should be tested with newer Hardhat version. + // await expect(context.suite.transferManager.initialize()).to.eventually.be.rejectedWith('Initializable: contract is already initialized'); + await expect(context.suite.transferManager.initialize()).to.be.reverted; + }); + }); + }); + describe('.setApprovalCriteria', () => { describe('when sender is not a token agent', () => { it('should revert', async () => { @@ -96,82 +121,74 @@ describe('DVATransferManager', () => { await expect( context.suite.transferManager .connect(context.accounts.anotherWallet) - .setApprovalCriteria(context.suite.token.address, false, true, true, []), - ).to.be.revertedWithCustomError(context.suite.transferManager, `OnlyTokenAgentCanCall`); + .setApprovalCriteria(context.suite.token.target, false, true, true, []), + ).to.be.revertedWithCustomError(context.suite.transferManager, `OnlyTokenOwnerCanCall`); }); }); describe('when sender is a token agent', () => { - describe('when DVA Manager is not verified for the token', () => { + describe('when DVA Manager is not an agent of the token', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteWithTransferManager); await expect( - context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, true, true, []), - ).to.be.revertedWithCustomError(context.suite.transferManager, `DVAManagerIsNotVerifiedForTheToken`); + context.suite.transferManager.connect(context.accounts.deployer).setApprovalCriteria(context.suite.token.target, false, true, true, []), + ).to.be.revertedWithCustomError(context.suite.transferManager, `DVAManagerIsNotAnAgentOfTheToken`); }); }); - describe('when DVA Manager is verified for the token', () => { + describe('when DVA Manager is an agent of the token', () => { describe('when token is not already registered', () => { it('should modify approval criteria', async () => { const context = await loadFixture(deployFullSuiteWithTransferManager); - const identity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); - await context.suite.identityRegistry - .connect(context.accounts.tokenAgent) - .registerIdentity(context.suite.transferManager.address, identity, 0); + await context.suite.token.connect(context.accounts.deployer).addAgent(context.suite.transferManager.target); const tx = context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [ context.accounts.anotherWallet.address, context.accounts.bobWallet.address, ]); + await tx; - const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.address); - expect(approvalCriteria.includeRecipientApprover).to.be.true; - expect(approvalCriteria.includeAgentApprover).to.be.true; - expect(approvalCriteria.sequentialApproval).to.be.true; - expect(approvalCriteria.additionalApprovers).to.be.eql([context.accounts.anotherWallet.address, context.accounts.bobWallet.address]); - + const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.target); await expect(tx) .to.emit(context.suite.transferManager, 'ApprovalCriteriaSet') .withArgs( - context.suite.token.address, + context.suite.token.target, true, true, true, [context.accounts.anotherWallet.address, context.accounts.bobWallet.address], approvalCriteria.hash, ); + expect(approvalCriteria.includeRecipientApprover).to.be.true; + expect(approvalCriteria.includeAgentApprover).to.be.true; + expect(approvalCriteria.sequentialApproval).to.be.true; + expect(approvalCriteria.additionalApprovers).to.be.eql([context.accounts.anotherWallet.address, context.accounts.bobWallet.address]); }); }); describe('when token is already registered', () => { it('should modify approval criteria', async () => { const context = await loadFixture(deployFullSuiteWithTransferManager); - const identity = await context.suite.identityRegistry.identity(context.accounts.aliceWallet.address); - await context.suite.identityRegistry - .connect(context.accounts.tokenAgent) - .registerIdentity(context.suite.transferManager.address, identity, 0); + await context.suite.token.connect(context.accounts.deployer).addAgent(context.suite.transferManager.target); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [ context.accounts.anotherWallet.address, context.accounts.bobWallet.address, ]); - const previousApprovalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.address); + const previousApprovalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.target); const tx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await tx.wait(); - const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.address); + const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.target); expect(approvalCriteria.includeRecipientApprover).to.be.false; expect(approvalCriteria.includeAgentApprover).to.be.false; expect(approvalCriteria.sequentialApproval).to.be.false; @@ -180,7 +197,7 @@ describe('DVATransferManager', () => { await expect(tx) .to.emit(context.suite.transferManager, 'ApprovalCriteriaSet') - .withArgs(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address], approvalCriteria.hash); + .withArgs(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address], approvalCriteria.hash); }); }); }); @@ -195,7 +212,7 @@ describe('DVATransferManager', () => { await expect( context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 10), + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 10), ).to.be.revertedWithCustomError(context.suite.transferManager, `TokenIsNotRegistered`); }); }); @@ -205,8 +222,8 @@ describe('DVATransferManager', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [ context.accounts.charlieWallet.address, context.accounts.anotherWallet.address, ]); @@ -214,7 +231,7 @@ describe('DVATransferManager', () => { await expect( context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.anotherWallet.address, 10), + .initiateTransfer(context.suite.token.target, context.accounts.anotherWallet.address, 10), ).to.be.revertedWithCustomError(context.suite.transferManager, `RecipientIsNotVerified`); }); }); @@ -223,19 +240,19 @@ describe('DVATransferManager', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [ context.accounts.charlieWallet.address, context.accounts.anotherWallet.address, ]); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); await expect( context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100000), - ).to.be.revertedWith('Insufficient Balance'); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100000), + ).to.be.revertedWithCustomError(context.suite.token, 'ERC20InsufficientBalance'); }); }); @@ -244,10 +261,10 @@ describe('DVATransferManager', () => { it('should initiate the transfer with recipient approver', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, false, true, []); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, false, true, []); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -257,19 +274,17 @@ describe('DVATransferManager', () => { const tx = context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); await expect(tx) .to.emit(context.suite.transferManager, 'TransferInitiated') .withArgs( transferID, - context.suite.token.address, + context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - ( - await context.suite.transferManager.getApprovalCriteria(context.suite.token.address) - ).hash, + (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash, ); await (await tx).wait(); @@ -284,10 +299,10 @@ describe('DVATransferManager', () => { it('should initiate the transfer with token agent approver', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, true, true, []); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, true, true, []); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -297,19 +312,17 @@ describe('DVATransferManager', () => { const tx = context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); await expect(tx) .to.emit(context.suite.transferManager, 'TransferInitiated') .withArgs( transferID, - context.suite.token.address, + context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - ( - await context.suite.transferManager.getApprovalCriteria(context.suite.token.address) - ).hash, + (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash, ); await (await tx).wait(); @@ -324,13 +337,13 @@ describe('DVATransferManager', () => { it('should initiate the transfer with token agent approver', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, true, [ context.accounts.charlieWallet.address, context.accounts.anotherWallet.address, ]); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -340,19 +353,17 @@ describe('DVATransferManager', () => { const tx = context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); await expect(tx) .to.emit(context.suite.transferManager, 'TransferInitiated') .withArgs( transferID, - context.suite.token.address, + context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - ( - await context.suite.transferManager.getApprovalCriteria(context.suite.token.address) - ).hash, + (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash, ); const transfer = await context.suite.transferManager.getTransfer(transferID); @@ -368,13 +379,13 @@ describe('DVATransferManager', () => { it('should initiate the transfer with all approvers', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, true, true, true, [ + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, true, true, true, [ context.accounts.charlieWallet.address, context.accounts.anotherWallet.address, ]); - await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 100000); + await context.suite.token.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 100000); const transferID = await context.suite.transferManager.calculateTransferID( 0, context.accounts.aliceWallet.address, @@ -384,19 +395,17 @@ describe('DVATransferManager', () => { const tx = context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateTransfer(context.suite.token.address, context.accounts.bobWallet.address, 100); + .initiateTransfer(context.suite.token.target, context.accounts.bobWallet.address, 100); await expect(tx) .to.emit(context.suite.transferManager, 'TransferInitiated') .withArgs( transferID, - context.suite.token.address, + context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100, - ( - await context.suite.transferManager.getApprovalCriteria(context.suite.token.address) - ).hash, + (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash, ); await (await tx).wait(); @@ -412,10 +421,10 @@ describe('DVATransferManager', () => { expect(transfer.approvers[3]['approved']).to.be.false; const senderBalance = await context.suite.token.balanceOf(context.accounts.aliceWallet.address); - expect(senderBalance).to.be.eq(900); + expect(senderBalance).to.be.eq(1000); - const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.address); - expect(dvaBalance).to.be.eq(100); + const frozenBalance = await context.suite.token.getFrozenTokens(context.accounts.aliceWallet.address); + expect(frozenBalance).to.be.eq(100); }); }); }); @@ -457,14 +466,14 @@ describe('DVATransferManager', () => { it('should reset approvers', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const tx = context.suite.transferManager.connect(context.accounts.charlieWallet).approveTransfer(context.transferID); await expect(tx) .to.emit(context.suite.transferManager, 'TransferApprovalStateReset') - .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.address)).hash); + .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash); await (await tx).wait(); const transfer = await context.suite.transferManager.getTransfer(context.transferID); @@ -478,8 +487,8 @@ describe('DVATransferManager', () => { it('should approve', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const resetTx = await context.suite.transferManager.connect(context.accounts.charlieWallet).approveTransfer(context.transferID); @@ -528,7 +537,7 @@ describe('DVATransferManager', () => { await expect(tx) .to.emit(context.suite.transferManager, 'TransferCompleted') - .withArgs(context.transferID, context.suite.token.address, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); + .withArgs(context.transferID, context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); const transfer = await context.suite.transferManager.getTransfer(context.transferID); expect(transfer.status).to.be.eq(1); @@ -539,7 +548,7 @@ describe('DVATransferManager', () => { const receiverBalance = await context.suite.token.balanceOf(context.accounts.bobWallet.address); expect(receiverBalance).to.be.eq(600); - const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.address); + const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.target); expect(dvaBalance).to.be.eq(0); }); }); @@ -581,7 +590,7 @@ describe('DVATransferManager', () => { await expect(tx) .to.emit(context.suite.transferManager, 'TransferCompleted') - .withArgs(context.transferID, context.suite.token.address, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); + .withArgs(context.transferID, context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); const transfer = await context.suite.transferManager.getTransfer(context.transferID); expect(transfer.status).to.be.eq(1); @@ -592,7 +601,7 @@ describe('DVATransferManager', () => { const receiverBalance = await context.suite.token.balanceOf(context.accounts.bobWallet.address); expect(receiverBalance).to.be.eq(600); - const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.address); + const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.target); expect(dvaBalance).to.be.eq(0); }); }); @@ -651,8 +660,8 @@ describe('DVATransferManager', () => { it('should reset approvers', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const tx = context.suite.transferManager @@ -661,7 +670,7 @@ describe('DVATransferManager', () => { await expect(tx) .to.emit(context.suite.transferManager, 'TransferApprovalStateReset') - .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.address)).hash); + .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash); await (await tx).wait(); const transfer = await context.suite.transferManager.getTransfer(context.transferID); @@ -675,8 +684,8 @@ describe('DVATransferManager', () => { it('should approve', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const resetTx = await context.suite.transferManager.connect(context.accounts.charlieWallet).approveTransfer(context.transferID); @@ -739,7 +748,7 @@ describe('DVATransferManager', () => { await expect(tx) .to.emit(context.suite.transferManager, 'TransferCompleted') - .withArgs(context.transferID, context.suite.token.address, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); + .withArgs(context.transferID, context.suite.token.target, context.accounts.aliceWallet.address, context.accounts.bobWallet.address, 100); const transfer = await context.suite.transferManager.getTransfer(context.transferID); expect(transfer.status).to.be.eq(1); @@ -750,7 +759,7 @@ describe('DVATransferManager', () => { const receiverBalance = await context.suite.token.balanceOf(context.accounts.bobWallet.address); expect(receiverBalance).to.be.eq(600); - const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.address); + const dvaBalance = await context.suite.token.balanceOf(context.suite.transferManager.target); expect(dvaBalance).to.be.eq(0); }); }); @@ -903,14 +912,14 @@ describe('DVATransferManager', () => { it('should reset approvers', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const tx = context.suite.transferManager.connect(context.accounts.charlieWallet).rejectTransfer(context.transferID); await expect(tx) .to.emit(context.suite.transferManager, 'TransferApprovalStateReset') - .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.address)).hash); + .withArgs(context.transferID, (await context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).hash); await (await tx).wait(); const transfer = await context.suite.transferManager.getTransfer(context.transferID); @@ -924,8 +933,8 @@ describe('DVATransferManager', () => { it('should reject', async () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const modifyTx = await context.suite.transferManager - .connect(context.accounts.tokenAgent) - .setApprovalCriteria(context.suite.token.address, false, false, false, [context.accounts.davidWallet.address]); + .connect(context.accounts.deployer) + .setApprovalCriteria(context.suite.token.target, false, false, false, [context.accounts.davidWallet.address]); await modifyTx.wait(); const resetTx = await context.suite.transferManager.connect(context.accounts.charlieWallet).rejectTransfer(context.transferID); @@ -947,6 +956,32 @@ describe('DVATransferManager', () => { }); }); + describe('.upgradeTo', () => { + describe('when calling directly', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteWithTransferManager); + await expect(context.suite.transferManager.connect(context.accounts.aliceWallet).upgradeTo(ethers.ZeroAddress)).to.revertedWith( + 'Ownable: caller is not the owner', + ); + }); + }); + + describe('when calling with owner account', () => { + it('should upgrade proxy', async () => { + // given + const context = await loadFixture(deployFullSuiteWithTransferManager); + const newImplementation = await ethers.deployContract('DVATransferManager'); + + // when + await context.suite.transferManager.connect(context.accounts.deployer).upgradeTo(newImplementation.target); + + // then + const implementationAddress = await upgrades.erc1967.getImplementationAddress(context.suite.transferManager.target as string); + expect(implementationAddress).to.eq(newImplementation.target); + }); + }); + }); + describe('.getTransfer', () => { describe('when transfer does not exist', () => { it('should revert', async () => { @@ -970,7 +1005,7 @@ describe('DVATransferManager', () => { const context = await loadFixture(deployFullSuiteWithNonSequentialTransfer); const transfer = await context.suite.transferManager.getTransfer(context.transferID); - expect(transfer.tokenAddress).to.be.eq(context.suite.token.address); + expect(transfer.tokenAddress).to.be.eq(context.suite.token.target); expect(transfer.sender).to.be.eq(context.accounts.aliceWallet.address); expect(transfer.recipient).to.be.eq(context.accounts.bobWallet.address); expect(transfer.amount).to.be.eq(100); @@ -1057,7 +1092,7 @@ describe('DVATransferManager', () => { describe('when token is not registered', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); - await expect(context.suite.transferManager.getApprovalCriteria(context.suite.token.address)).to.be.revertedWithCustomError( + await expect(context.suite.transferManager.getApprovalCriteria(context.suite.token.target)).to.be.revertedWithCustomError( context.suite.transferManager, `TokenIsNotRegistered`, ); @@ -1067,7 +1102,7 @@ describe('DVATransferManager', () => { describe('when token is registered', () => { it('should return criteria', async () => { const context = await loadFixture(deployFullSuiteWithSequentialTransfer); - const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.address); + const approvalCriteria = await context.suite.transferManager.getApprovalCriteria(context.suite.token.target); expect(approvalCriteria.includeRecipientApprover).to.be.true; expect(approvalCriteria.includeAgentApprover).to.be.true; expect(approvalCriteria.sequentialApproval).to.be.true; @@ -1083,4 +1118,39 @@ describe('DVATransferManager', () => { expect(await context.suite.transferManager.name()).to.be.equal('DVATransferManager'); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); + + const unsupportedInterfaceId = '0x12345678'; + expect(await context.suite.transferManager.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IDVATransferManager interface ID', async () => { + const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iDVATransferManagerInterfaceId = await interfaceIdCalculator.getIDVATransferManagerInterfaceId(); + expect(await context.suite.transferManager.supportsInterface(iDVATransferManagerInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await context.suite.transferManager.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployFullSuiteWithVerifiedTransferManager); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await context.suite.transferManager.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/dvd.test.ts b/test/dvd.test.ts index 7a9fcd59..a27754c5 100644 --- a/test/dvd.test.ts +++ b/test/dvd.test.ts @@ -2,6 +2,7 @@ import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; import { ethers } from 'hardhat'; +import { EventLog } from 'ethers'; import { deployFullSuiteFixture } from './fixtures/deploy-full-suite.fixture'; @@ -33,17 +34,20 @@ describe('DVDTransferManager', () => { const context = await loadFixture(deployFullSuiteWithTransferManager); await context.suite.erc20A.mint(context.accounts.aliceWallet.address, 1000); - await context.suite.erc20A.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.address, 1000); + await context.suite.erc20A.connect(context.accounts.aliceWallet).approve(context.suite.transferManager.target, 1000); await context.suite.erc20B.mint(context.accounts.bobWallet.address, 500); - await context.suite.erc20B.connect(context.accounts.bobWallet).approve(context.suite.transferManager.address, 500); + await context.suite.erc20B.connect(context.accounts.bobWallet).approve(context.suite.transferManager.target, 500); const tx = await context.suite.transferManager .connect(context.accounts.aliceWallet) - .initiateDVDTransfer(context.suite.erc20A.address, 1000, context.accounts.bobWallet.address, context.suite.erc20B.address, 500); + .initiateDVDTransfer(context.suite.erc20A.target, 1000, context.accounts.bobWallet.address, context.suite.erc20B.target, 500); const receipt = await tx.wait(); + if (!receipt) throw new Error('ContractTransactionReceipt is null'); - const event = receipt.events.find((e: { event?: string }) => e.event === 'DVDTransferInitiated'); - const transferId = event.args.transferID; + const event = receipt.logs.find((e) => (e as EventLog)?.fragment?.name === 'DVDTransferInitiated') as EventLog; + + if (!event) throw new Error('DVDTransferInitiated event not found'); + const transferId = event.args[0]; return { ...context, @@ -62,8 +66,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(anotherWallet).modifyFee(erc20A.address, erc20B.address, 1, 1, 1, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('Ownable: only owner can call'); + transferManager.connect(anotherWallet).modifyFee(erc20A.target, erc20B.target, 1, 1, 1, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'OwnableUnauthorizedAccount'); }); }); @@ -75,8 +79,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(token.address, erc20A.address, 1, 1, 1, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(token.target, erc20A.target, 1, 1, 1, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); it('should revert for invalid fee settings', async () => { @@ -86,8 +90,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, token.address, 1, 1, 1, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(erc20A.target, token.target, 1, 1, 1, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); }); @@ -101,8 +105,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, erc20B.address, 1000, 1, 2, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 1000, 1, 2, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); }); @@ -114,8 +118,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, erc20B.address, 1, 1000, 2, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 1, 1000, 2, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); }); @@ -127,8 +131,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, erc20B.address, 0, 0, 1, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 0, 0, 1, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); }); @@ -140,8 +144,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, erc20B.address, 1000, 1, 10, charlieWallet.address, davidWallet.address), - ).to.be.revertedWith('invalid fee settings'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 1000, 1, 10, charlieWallet.address, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'InvalidFeeSettings'); }); }); @@ -153,8 +157,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(deployer).modifyFee(erc20A.address, erc20B.address, 2, 0, 2, ethers.constants.AddressZero, davidWallet.address), - ).to.be.revertedWith('fee wallet 1 cannot be zero address'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 2, 0, 2, ethers.ZeroAddress, davidWallet.address), + ).to.be.revertedWithCustomError(transferManager, 'ZeroAddress'); }); }); @@ -166,10 +170,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager - .connect(deployer) - .modifyFee(erc20A.address, erc20B.address, 0, 1, 2, ethers.constants.AddressZero, ethers.constants.AddressZero), - ).to.be.revertedWith('fee wallet 2 cannot be zero address'); + transferManager.connect(deployer).modifyFee(erc20A.target, erc20B.target, 0, 1, 2, ethers.ZeroAddress, ethers.ZeroAddress), + ).to.be.revertedWithCustomError(transferManager, 'ZeroAddress'); }); }); }); @@ -183,13 +185,13 @@ describe('DVDTransferManager', () => { const tx = await transferManager .connect(deployer) - .modifyFee(erc20A.address, erc20B.address, 2, 1, 2, charlieWallet.address, davidWallet.address); + .modifyFee(erc20A.target, erc20B.target, 2, 1, 2, charlieWallet.address, davidWallet.address); await expect(tx) .to.emit(transferManager, 'FeeModified') .withArgs( - ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'address'], [erc20A.address, erc20B.address])), - erc20A.address, - erc20B.address, + ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address'], [erc20A.target, erc20B.target])), + erc20A.target, + erc20B.target, 2, 1, 2, @@ -199,9 +201,9 @@ describe('DVDTransferManager', () => { await expect(tx) .to.emit(transferManager, 'FeeModified') .withArgs( - ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'address'], [erc20B.address, erc20A.address])), - erc20B.address, - erc20A.address, + ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address'], [erc20B.target, erc20A.target])), + erc20B.target, + erc20A.target, 1, 2, 2, @@ -218,29 +220,29 @@ describe('DVDTransferManager', () => { const tx = await transferManager .connect(deployer) - .modifyFee(erc20A.address, erc20B.address, 2, 0, 2, charlieWallet.address, ethers.constants.AddressZero); + .modifyFee(erc20A.target, erc20B.target, 2, 0, 2, charlieWallet.address, ethers.ZeroAddress); await expect(tx) .to.emit(transferManager, 'FeeModified') .withArgs( - ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'address'], [erc20A.address, erc20B.address])), - erc20A.address, - erc20B.address, + ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address'], [erc20A.target, erc20B.target])), + erc20A.target, + erc20B.target, 2, 0, 2, charlieWallet.address, - ethers.constants.AddressZero, + ethers.ZeroAddress, ); await expect(tx) .to.emit(transferManager, 'FeeModified') .withArgs( - ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'address'], [erc20B.address, erc20A.address])), - erc20B.address, - erc20A.address, + ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address'], [erc20B.target, erc20A.target])), + erc20B.target, + erc20A.target, 0, 2, 2, - ethers.constants.AddressZero, + ethers.ZeroAddress, charlieWallet.address, ); }); @@ -257,8 +259,8 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await expect( - transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.address, 1000, charlieWallet.address, erc20B.address, 1000), - ).to.be.revertedWith('Not enough tokens in balance'); + transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.target, 1000, charlieWallet.address, erc20B.target, 1000), + ).to.be.revertedWithCustomError(transferManager, 'ERC20InsufficientBalance'); }); }); @@ -271,8 +273,8 @@ describe('DVDTransferManager', () => { await erc20A.connect(deployer).mint(charlieWallet.address, 1000); await expect( - transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.address, 1000, davidWallet.address, erc20B.address, 1000), - ).to.be.revertedWith('not enough allowance to initiate transfer'); + transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.target, 1000, davidWallet.address, erc20B.target, 1000), + ).to.be.revertedWithCustomError(transferManager, 'ERC20InsufficientAllowance'); }); }); @@ -284,10 +286,10 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await erc20A.connect(deployer).mint(charlieWallet.address, 1000); - await erc20A.connect(charlieWallet).approve(transferManager.address, 1000); + await erc20A.connect(charlieWallet).approve(transferManager.target, 1000); await expect( - transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.address, 1000, ethers.constants.AddressZero, erc20B.address, 1000), - ).to.be.revertedWith('counterpart cannot be null'); + transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.target, 1000, ethers.ZeroAddress, erc20B.target, 1000), + ).to.be.revertedWithCustomError(transferManager, 'ZeroAddress'); }); }); @@ -299,10 +301,10 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await erc20A.connect(deployer).mint(charlieWallet.address, 1000); - await erc20A.connect(charlieWallet).approve(transferManager.address, 1000); + await erc20A.connect(charlieWallet).approve(transferManager.target, 1000); await expect( - transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.address, 1000, davidWallet.address, erc20C.address, 1000), - ).to.be.revertedWith('invalid address : address is not an ERC20'); + transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.target, 1000, davidWallet.address, erc20C.target, 1000), + ).to.be.revertedWithCustomError(transferManager, 'AddressNotERC20'); }); }); @@ -314,11 +316,11 @@ describe('DVDTransferManager', () => { } = await loadFixture(deployFullSuiteWithTransferManager); await erc20A.connect(deployer).mint(charlieWallet.address, 1000); - await erc20A.connect(charlieWallet).approve(transferManager.address, 1000); - const tx = await transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.address, 1000, davidWallet.address, erc20B.address, 500); + await erc20A.connect(charlieWallet).approve(transferManager.target, 1000); + const tx = await transferManager.connect(charlieWallet).initiateDVDTransfer(erc20A.target, 1000, davidWallet.address, erc20B.target, 500); await expect(tx) .to.emit(transferManager, 'DVDTransferInitiated') - .withArgs(anyValue, charlieWallet.address, erc20A.address, 1000, davidWallet.address, erc20B.address, 500); + .withArgs(anyValue, charlieWallet.address, erc20A.target, 1000, davidWallet.address, erc20B.target, 500); }); }); }); @@ -331,8 +333,9 @@ describe('DVDTransferManager', () => { accounts: { charlieWallet }, } = await loadFixture(deployFullSuiteWithTransferManager); - await expect(transferManager.connect(charlieWallet).cancelDVDTransfer(ethers.constants.HashZero)).to.be.revertedWith( - 'transfer ID does not exist', + await expect(transferManager.connect(charlieWallet).cancelDVDTransfer(ethers.ZeroHash)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', ); }); }); @@ -345,8 +348,9 @@ describe('DVDTransferManager', () => { values: { transferId }, } = await loadFixture(deployFullSuiteWithTransferManagerAndInitiatedTransfer); - await expect(transferManager.connect(anotherWallet).cancelDVDTransfer(transferId)).to.be.revertedWith( - 'you are not allowed to cancel this transfer', + await expect(transferManager.connect(anotherWallet).cancelDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'CancelOnlyByCounterpartOrOwnerOrAgent', ); }); }); @@ -361,7 +365,10 @@ describe('DVDTransferManager', () => { const tx = await transferManager.connect(aliceWallet).cancelDVDTransfer(transferId); await expect(tx).to.emit(transferManager, 'DVDTransferCancelled').withArgs(transferId); - await expect(transferManager.connect(aliceWallet).cancelDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(aliceWallet).cancelDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); @@ -375,7 +382,10 @@ describe('DVDTransferManager', () => { const tx = await transferManager.connect(bobWallet).cancelDVDTransfer(transferId); await expect(tx).to.emit(transferManager, 'DVDTransferCancelled').withArgs(transferId); - await expect(transferManager.connect(bobWallet).cancelDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(bobWallet).cancelDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); @@ -389,7 +399,10 @@ describe('DVDTransferManager', () => { const tx = await transferManager.connect(deployer).cancelDVDTransfer(transferId); await expect(tx).to.emit(transferManager, 'DVDTransferCancelled').withArgs(transferId); - await expect(transferManager.connect(deployer).cancelDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(deployer).cancelDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); @@ -403,7 +416,10 @@ describe('DVDTransferManager', () => { const tx = await transferManager.connect(deployer).cancelDVDTransfer(transferId); await expect(tx).to.emit(transferManager, 'DVDTransferCancelled').withArgs(transferId); - await expect(transferManager.connect(deployer).cancelDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(deployer).cancelDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); }); @@ -416,8 +432,9 @@ describe('DVDTransferManager', () => { accounts: { charlieWallet }, } = await loadFixture(deployFullSuiteWithTransferManager); - await expect(transferManager.connect(charlieWallet).takeDVDTransfer(ethers.constants.HashZero)).to.be.revertedWith( - 'transfer ID does not exist', + await expect(transferManager.connect(charlieWallet).takeDVDTransfer(ethers.ZeroHash)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', ); }); }); @@ -431,8 +448,9 @@ describe('DVDTransferManager', () => { values: { transferId }, } = await loadFixture(deployFullSuiteWithTransferManagerAndInitiatedTransfer); - await expect(transferManager.connect(anotherWallet).takeDVDTransfer(transferId)).to.be.revertedWith( - 'transfer has to be done by the counterpart or by owner', + await expect(transferManager.connect(anotherWallet).takeDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferOnlyByCounterpartOrOwner', ); }); }); @@ -445,7 +463,7 @@ describe('DVDTransferManager', () => { values: { transferId }, } = await loadFixture(deployFullSuiteWithTransferManagerAndInitiatedTransfer); - await transferManager.modifyFee(erc20A.address, erc20B.address, 1, 2, 2, charlieWallet.address, davidWallet.address); + await transferManager.modifyFee(erc20A.target, erc20B.target, 1, 2, 2, charlieWallet.address, davidWallet.address); const tx = await transferManager.connect(bobWallet).takeDVDTransfer(transferId); await expect(tx).to.emit(transferManager, 'DVDTransferExecuted').withArgs(transferId); @@ -453,7 +471,10 @@ describe('DVDTransferManager', () => { await expect(tx).to.emit(erc20A, 'Transfer').withArgs(aliceWallet.address, charlieWallet.address, 10); await expect(tx).to.emit(erc20B, 'Transfer').withArgs(bobWallet.address, aliceWallet.address, 490); await expect(tx).to.emit(erc20B, 'Transfer').withArgs(bobWallet.address, davidWallet.address, 10); - await expect(transferManager.connect(bobWallet).takeDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(bobWallet).takeDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); @@ -469,7 +490,10 @@ describe('DVDTransferManager', () => { await expect(tx).to.emit(transferManager, 'DVDTransferExecuted').withArgs(transferId); await expect(tx).to.emit(erc20A, 'Transfer').withArgs(aliceWallet.address, bobWallet.address, 1000); await expect(tx).to.emit(erc20B, 'Transfer').withArgs(bobWallet.address, aliceWallet.address, 500); - await expect(transferManager.connect(bobWallet).takeDVDTransfer(transferId)).to.be.revertedWith('transfer ID does not exist'); + await expect(transferManager.connect(bobWallet).takeDVDTransfer(transferId)).to.be.revertedWithCustomError( + transferManager, + 'TransferIDDoesNotExist', + ); }); }); }); @@ -483,7 +507,7 @@ describe('DVDTransferManager', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteWithTransferManager); - expect(await transferManager.isTREXAgent(token.address, anotherWallet.address)).to.be.false; + expect(await transferManager.isTREXAgent(token.target, anotherWallet.address)).to.be.false; }); }); @@ -494,7 +518,7 @@ describe('DVDTransferManager', () => { accounts: { tokenAgent }, } = await loadFixture(deployFullSuiteWithTransferManager); - expect(await transferManager.isTREXAgent(token.address, tokenAgent.address)).to.be.true; + expect(await transferManager.isTREXAgent(token.target, tokenAgent.address)).to.be.true; }); }); }); @@ -507,7 +531,7 @@ describe('DVDTransferManager', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteWithTransferManager); - expect(await transferManager.isTREXOwner(token.address, anotherWallet.address)).to.be.false; + expect(await transferManager.isTREXOwner(token.target, anotherWallet.address)).to.be.false; }); }); @@ -518,7 +542,7 @@ describe('DVDTransferManager', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteWithTransferManager); - expect(await transferManager.isTREXOwner(token.address, deployer.address)).to.be.true; + expect(await transferManager.isTREXOwner(token.target, deployer.address)).to.be.true; }); }); }); @@ -527,12 +551,10 @@ describe('DVDTransferManager', () => { describe('When token does not have an Identity Registry (probably not a ERC3643 token', () => { it('should return false', async () => { const { - suite: { transferManager, token }, + suite: { transferManager }, } = await loadFixture(deployFullSuiteWithTransferManager); - await token.setIdentityRegistry(ethers.constants.AddressZero); - - expect(await transferManager.isTREX(token.address)).to.be.false; + expect(await transferManager.isTREX(transferManager.target)).to.be.false; }); }); }); diff --git a/test/factory.test.ts b/test/factory.test.ts index 63f4c7e4..4cd3065b 100644 --- a/test/factory.test.ts +++ b/test/factory.test.ts @@ -2,11 +2,21 @@ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; import { ethers } from 'hardhat'; -import { Event } from 'ethers'; +import { EventLog, ContractTransactionResponse } from 'ethers'; import OnchainID from '@onchain-id/solidity'; import { deployFullSuiteFixture } from './fixtures/deploy-full-suite.fixture'; describe('TREXFactory', () => { + async function getTREXSuiteDeployedTokenAddress(tx: ContractTransactionResponse): Promise { + const receipt = await tx.wait(); + if (!receipt) throw new Error('ContractTransactionReceipt is null'); + + const event = receipt.logs.find((e) => (e as EventLog)?.fragment?.name === 'TREXSuiteDeployed') as EventLog; + if (!event) throw new Error('TREXSuiteDeployed event not found'); + + return event.args[0]; + } + describe('.deployTREXSuite()', () => { describe('when called by not owner', () => { it('should revert', async () => { @@ -23,8 +33,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -55,8 +65,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -77,8 +87,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -90,7 +100,7 @@ describe('TREXFactory', () => { issuerClaims: [], }, ), - ).to.be.revertedWith('token already deployed'); + ).to.be.revertedWithCustomError(trexFactory, 'TokenAlreadyDeployed'); }); }); @@ -109,8 +119,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -118,11 +128,11 @@ describe('TREXFactory', () => { }, { claimTopics: [], - issuers: [ethers.constants.AddressZero], + issuers: [ethers.ZeroAddress], issuerClaims: [], }, ), - ).to.be.revertedWith('claim pattern not valid'); + ).to.be.revertedWithCustomError(trexFactory, 'InvalidClaimPattern'); }); }); @@ -141,8 +151,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -150,11 +160,11 @@ describe('TREXFactory', () => { }, { claimTopics: [], - issuers: Array.from({ length: 6 }, () => ethers.constants.AddressZero), + issuers: Array.from({ length: 6 }, () => ethers.ZeroAddress), issuerClaims: Array.from({ length: 6 }, () => []), }, ), - ).to.be.revertedWith('max 5 claim issuers at deployment'); + ).to.be.revertedWithCustomError(trexFactory, 'MaxClaimIssuersReached'); }); }); @@ -173,20 +183,20 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], complianceSettings: [], }, { - claimTopics: Array.from({ length: 6 }, () => ethers.constants.HashZero), + claimTopics: Array.from({ length: 6 }, () => ethers.ZeroHash), issuers: [], issuerClaims: [], }, ), - ).to.be.revertedWith('max 5 claim topics at deployment'); + ).to.be.revertedWithCustomError(trexFactory, 'MaxClaimTopicsReached'); }); }); @@ -205,9 +215,9 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, - irAgents: Array.from({ length: 6 }, () => ethers.constants.AddressZero), + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, + irAgents: Array.from({ length: 6 }, () => ethers.ZeroAddress), tokenAgents: [], complianceModules: [], complianceSettings: [], @@ -218,7 +228,7 @@ describe('TREXFactory', () => { issuerClaims: [], }, ), - ).to.be.revertedWith('max 5 agents at deployment'); + ).to.be.revertedWithCustomError(trexFactory, 'MaxAgentsReached'); }); }); @@ -237,11 +247,11 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], - complianceModules: Array.from({ length: 31 }, () => ethers.constants.AddressZero), + complianceModules: Array.from({ length: 31 }, () => ethers.ZeroAddress), complianceSettings: [], }, { @@ -250,7 +260,7 @@ describe('TREXFactory', () => { issuerClaims: [], }, ), - ).to.be.revertedWith('max 30 module actions at deployment'); + ).to.be.revertedWithCustomError(trexFactory, 'MaxModuleActionsReached'); }); }); @@ -269,8 +279,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -282,7 +292,7 @@ describe('TREXFactory', () => { issuerClaims: [], }, ), - ).to.be.revertedWith('invalid compliance pattern'); + ).to.be.revertedWithCustomError(trexFactory, 'InvalidCompliancePattern'); }); }); @@ -303,21 +313,21 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [aliceWallet.address], tokenAgents: [bobWallet.address], - complianceModules: [countryAllowModule.address], + complianceModules: [countryAllowModule.target], complianceSettings: [ - new ethers.utils.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [ + new ethers.Interface(['function batchAllowCountries(uint16[] calldata countries)']).encodeFunctionData('batchAllowCountries', [ [42, 66], ]), ], }, { - claimTopics: [ethers.utils.keccak256(ethers.utils.toUtf8Bytes('DEMO_TOPIC'))], - issuers: [claimIssuerContract.address], - issuerClaims: [[ethers.utils.keccak256(ethers.utils.toUtf8Bytes('DEMO_TOPIC'))]], + claimTopics: [ethers.keccak256(ethers.toUtf8Bytes('DEMO_TOPIC'))], + issuers: [claimIssuerContract.target], + issuerClaims: [[ethers.keccak256(ethers.toUtf8Bytes('DEMO_TOPIC'))]], }, ); expect(tx).to.emit(trexFactory, 'TREXSuiteDeployed'); @@ -343,8 +353,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -357,8 +367,7 @@ describe('TREXFactory', () => { }, ); - const receipt = await tx.wait(); - const tokenAddressExpected = receipt.events.find((event: Event) => event.event === 'TREXSuiteDeployed').args[0]; + const tokenAddressExpected = await getTREXSuiteDeployedTokenAddress(tx); const tokenAddress = await trexFactory.getToken('salt'); expect(tokenAddress).to.equal(tokenAddressExpected); @@ -374,7 +383,7 @@ describe('TREXFactory', () => { factories: { trexFactory }, } = await loadFixture(deployFullSuiteFixture); - await expect(trexFactory.connect(deployer).setIdFactory(ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(trexFactory.connect(deployer).setIdFactory(ethers.ZeroAddress)).to.be.revertedWithCustomError(trexFactory, 'ZeroAddress'); }); }); describe('when try to input a valid address', () => { @@ -386,12 +395,12 @@ describe('TREXFactory', () => { } = await loadFixture(deployFullSuiteFixture); const newIdFactory = await new ethers.ContractFactory(OnchainID.contracts.Factory.abi, OnchainID.contracts.Factory.bytecode, deployer).deploy( - identityImplementationAuthority.address, + identityImplementationAuthority.target, ); - const tx = await trexFactory.setIdFactory(newIdFactory.address); + const tx = await trexFactory.setIdFactory(newIdFactory.target); expect(tx).to.emit(trexFactory, 'IdFactorySet'); - expect(await trexFactory.getIdFactory()).to.equal(newIdFactory.address); + expect(await trexFactory.getIdFactory()).to.equal(newIdFactory.target); }); }); }); @@ -411,8 +420,8 @@ describe('TREXFactory', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -425,8 +434,7 @@ describe('TREXFactory', () => { }, ); - const receipt = await tx.wait(); - const tokenAddress = receipt.events.find((event: Event) => event.event === 'TREXSuiteDeployed').args[0]; + const tokenAddress = await getTREXSuiteDeployedTokenAddress(tx); await expect(trexFactory.connect(aliceWallet).recoverContractOwnership(tokenAddress, aliceWallet.address)).to.be.revertedWith( 'Ownable: caller is not the owner', @@ -444,12 +452,12 @@ describe('TREXFactory', () => { const deployTx = await trexFactory.connect(deployer).deployTREXSuite( 'salt', { - owner: trexFactory.address, + owner: trexFactory.target, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -462,16 +470,15 @@ describe('TREXFactory', () => { }, ); - const receipt = await deployTx.wait(); - const tokenAddress = receipt.events.find((event: Event) => event.event === 'TREXSuiteDeployed').args[0]; - - const tx = await trexFactory.connect(deployer).recoverContractOwnership(tokenAddress, aliceWallet.address); - + const tokenAddress = await getTREXSuiteDeployedTokenAddress(deployTx); const token = await ethers.getContractAt('Token', tokenAddress); - await expect(tx).to.emit(token, 'OwnershipTransferred').withArgs(trexFactory.address, aliceWallet.address); + const tx = await trexFactory.connect(deployer).recoverContractOwnership(tokenAddress, aliceWallet.address); + expect(tx).to.emit(token, 'OwnershipTransferStarted').withArgs(trexFactory.target, aliceWallet.address); + const tx2 = await token.connect(aliceWallet).acceptOwnership(); + expect(tx2).to.emit(token, 'OwnershipTransferStarted').withArgs(trexFactory.target, aliceWallet.address); - await expect(token.owner()).to.eventually.eq(aliceWallet.address); + expect(await token.owner()).to.eq(aliceWallet.address); }); }); }); diff --git a/test/fixtures/deploy-full-suite.fixture.ts b/test/fixtures/deploy-full-suite.fixture.ts index eefa68ed..6d3cafba 100644 --- a/test/fixtures/deploy-full-suite.fixture.ts +++ b/test/fixtures/deploy-full-suite.fixture.ts @@ -1,15 +1,22 @@ -import { BigNumber, Contract, Signer } from 'ethers'; +import { Contract, Signer } from 'ethers'; import { ethers } from 'hardhat'; import OnchainID from '@onchain-id/solidity'; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { IIdFactory } from '../../typechain-types'; -export async function deployIdentityProxy(implementationAuthority: Contract['address'], managementKey: string, signer: Signer) { +export async function deployIdentityProxy(implementationAuthority: Contract['target'], managementKey: string, signer: Signer) { const identity = await new ethers.ContractFactory(OnchainID.contracts.IdentityProxy.abi, OnchainID.contracts.IdentityProxy.bytecode, signer).deploy( implementationAuthority, managementKey, ); + return ethers.getContractAt(OnchainID.contracts.Identity.abi, identity.target, signer); +} - return ethers.getContractAt('Identity', identity.address, signer); +export async function deployClaimIssuer(initialManagementKey: string, signer: Signer) { + const claimIssuer = await new ethers.ContractFactory(OnchainID.contracts.ClaimIssuer.abi, OnchainID.contracts.ClaimIssuer.bytecode, signer).deploy( + initialManagementKey, + ); + return ethers.getContractAt(OnchainID.contracts.ClaimIssuer.abi, claimIssuer.target, signer); } export async function deployFullSuiteFixture() { @@ -17,7 +24,6 @@ export async function deployFullSuiteFixture() { await ethers.getSigners(); const claimIssuerSigningKey = ethers.Wallet.createRandom(); const aliceActionKey = ethers.Wallet.createRandom(); - // Deploy implementations const claimTopicsRegistryImplementation = await ethers.deployContract('ClaimTopicsRegistry', deployer); const trustedIssuersRegistryImplementation = await ethers.deployContract('TrustedIssuersRegistry', deployer); @@ -30,20 +36,17 @@ export async function deployFullSuiteFixture() { OnchainID.contracts.Identity.bytecode, deployer, ).deploy(deployer.address, true); - const identityImplementationAuthority = await new ethers.ContractFactory( OnchainID.contracts.ImplementationAuthority.abi, OnchainID.contracts.ImplementationAuthority.bytecode, deployer, - ).deploy(identityImplementation.address); - + ).deploy(identityImplementation.target); const identityFactory = await new ethers.ContractFactory(OnchainID.contracts.Factory.abi, OnchainID.contracts.Factory.bytecode, deployer).deploy( - identityImplementationAuthority.address, + identityImplementationAuthority.target, ); - const trexImplementationAuthority = await ethers.deployContract( 'TREXImplementationAuthority', - [true, ethers.constants.AddressZero, ethers.constants.AddressZero], + [true, ethers.ZeroAddress, ethers.ZeroAddress], deployer, ); const versionStruct = { @@ -52,102 +55,89 @@ export async function deployFullSuiteFixture() { patch: 0, }; const contractsStruct = { - tokenImplementation: tokenImplementation.address, - ctrImplementation: claimTopicsRegistryImplementation.address, - irImplementation: identityRegistryImplementation.address, - irsImplementation: identityRegistryStorageImplementation.address, - tirImplementation: trustedIssuersRegistryImplementation.address, - mcImplementation: modularComplianceImplementation.address, + tokenImplementation: tokenImplementation.target, + ctrImplementation: claimTopicsRegistryImplementation.target, + irImplementation: identityRegistryImplementation.target, + irsImplementation: identityRegistryStorageImplementation.target, + tirImplementation: trustedIssuersRegistryImplementation.target, + mcImplementation: modularComplianceImplementation.target, }; await trexImplementationAuthority.connect(deployer).addAndUseTREXVersion(versionStruct, contractsStruct); - const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer); - await identityFactory.connect(deployer).addTokenFactory(trexFactory.address); + const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.target, identityFactory.target], deployer); + await (identityFactory.connect(deployer) as IIdFactory).addTokenFactory(trexFactory.target); const claimTopicsRegistry = await ethers - .deployContract('ClaimTopicsRegistryProxy', [trexImplementationAuthority.address], deployer) - .then(async (proxy) => ethers.getContractAt('ClaimTopicsRegistry', proxy.address)); - + .deployContract('ClaimTopicsRegistryProxy', [trexImplementationAuthority.target], deployer) + .then(async (proxy) => ethers.getContractAt('ClaimTopicsRegistry', proxy.target)); const trustedIssuersRegistry = await ethers - .deployContract('TrustedIssuersRegistryProxy', [trexImplementationAuthority.address], deployer) - .then(async (proxy) => ethers.getContractAt('TrustedIssuersRegistry', proxy.address)); - + .deployContract('TrustedIssuersRegistryProxy', [trexImplementationAuthority.target], deployer) + .then(async (proxy) => ethers.getContractAt('TrustedIssuersRegistry', proxy.target)); const identityRegistryStorage = await ethers - .deployContract('IdentityRegistryStorageProxy', [trexImplementationAuthority.address], deployer) - .then(async (proxy) => ethers.getContractAt('IdentityRegistryStorage', proxy.address)); - + .deployContract('IdentityRegistryStorageProxy', [trexImplementationAuthority.target], deployer) + .then(async (proxy) => ethers.getContractAt('IdentityRegistryStorage', proxy.target)); const defaultCompliance = await ethers.deployContract('DefaultCompliance', deployer); const identityRegistry = await ethers .deployContract( 'IdentityRegistryProxy', - [trexImplementationAuthority.address, trustedIssuersRegistry.address, claimTopicsRegistry.address, identityRegistryStorage.address], + [trexImplementationAuthority.target, trustedIssuersRegistry.target, claimTopicsRegistry.target, identityRegistryStorage.target], deployer, ) - .then(async (proxy) => ethers.getContractAt('IdentityRegistry', proxy.address)); + .then(async (proxy) => ethers.getContractAt('IdentityRegistry', proxy.target)); - const tokenOID = await deployIdentityProxy(identityImplementationAuthority.address, tokenIssuer.address, deployer); + const tokenOID = await deployIdentityProxy(identityImplementationAuthority.target, tokenIssuer.address, deployer); const tokenName = 'TREXDINO'; const tokenSymbol = 'TREX'; - const tokenDecimals = BigNumber.from('0'); + const tokenDecimals = 0n; const token = await ethers .deployContract( 'TokenProxy', - [ - trexImplementationAuthority.address, - identityRegistry.address, - defaultCompliance.address, - tokenName, - tokenSymbol, - tokenDecimals, - tokenOID.address, - ], + [trexImplementationAuthority.target, identityRegistry.target, defaultCompliance.target, tokenName, tokenSymbol, tokenDecimals, tokenOID.target], deployer, ) - .then(async (proxy) => ethers.getContractAt('Token', proxy.address)); - - const agentManager = await ethers.deployContract('AgentManager', [token.address], tokenAgent); - - await identityRegistryStorage.connect(deployer).bindIdentityRegistry(identityRegistry.address); + .then(async (proxy) => ethers.getContractAt('Token', proxy.target)); + const agentManager = await ethers.deployContract('AgentManager', [token.target], tokenAgent); + await identityRegistryStorage.connect(deployer).bindIdentityRegistry(identityRegistry.target); await token.connect(deployer).addAgent(tokenAgent.address); - const claimTopics = [ethers.utils.id('CLAIM_TOPIC')]; + const claimTopics = [ethers.keccak256(ethers.toUtf8Bytes('CLAIM_TOPIC'))]; await claimTopicsRegistry.connect(deployer).addClaimTopic(claimTopics[0]); - const claimIssuerContract = await ethers.deployContract('ClaimIssuer', [claimIssuer.address], claimIssuer); + const claimIssuerContract = await deployClaimIssuer(claimIssuer.address, claimIssuer); await claimIssuerContract .connect(claimIssuer) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [claimIssuerSigningKey.address])), 3, 1); + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [claimIssuerSigningKey.address])), 3, 1); - await trustedIssuersRegistry.connect(deployer).addTrustedIssuer(claimIssuerContract.address, claimTopics); + await trustedIssuersRegistry.connect(deployer).addTrustedIssuer(claimIssuerContract.target, claimTopics); - const aliceIdentity = await deployIdentityProxy(identityImplementationAuthority.address, aliceWallet.address, deployer); + const aliceIdentity = await deployIdentityProxy(identityImplementationAuthority.target, aliceWallet.address, deployer); await aliceIdentity .connect(aliceWallet) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [aliceActionKey.address])), 2, 1); - const bobIdentity = await deployIdentityProxy(identityImplementationAuthority.address, bobWallet.address, deployer); - const charlieIdentity = await deployIdentityProxy(identityImplementationAuthority.address, charlieWallet.address, deployer); + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [aliceActionKey.address])), 2, 1); + const bobIdentity = await deployIdentityProxy(identityImplementationAuthority.target, bobWallet.address, deployer); + const charlieIdentity = await deployIdentityProxy(identityImplementationAuthority.target, charlieWallet.address, deployer); await identityRegistry.connect(deployer).addAgent(tokenAgent.address); - await identityRegistry.connect(deployer).addAgent(token.address); + await identityRegistry.connect(deployer).addAgent(token.target); await identityRegistry .connect(tokenAgent) - .batchRegisterIdentity([aliceWallet.address, bobWallet.address], [aliceIdentity.address, bobIdentity.address], [42, 666]); + .batchRegisterIdentity([aliceWallet.address, bobWallet.address], [aliceIdentity.target, bobIdentity.target], [42, 666]); const claimForAlice = { - data: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('Some claim public data.')), - issuer: claimIssuerContract.address, + data: ethers.hexlify(ethers.toUtf8Bytes('Some claim public data.')), + issuer: claimIssuerContract.target, topic: claimTopics[0], scheme: 1, - identity: aliceIdentity.address, + identity: aliceIdentity.target, signature: '', }; claimForAlice.signature = await claimIssuerSigningKey.signMessage( - ethers.utils.arrayify( - ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claimForAlice.identity, claimForAlice.topic, claimForAlice.data]), + ethers.getBytes( + ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode(['address', 'uint256', 'bytes'], [claimForAlice.identity, claimForAlice.topic, claimForAlice.data]), ), ), ); @@ -157,17 +147,17 @@ export async function deployFullSuiteFixture() { .addClaim(claimForAlice.topic, claimForAlice.scheme, claimForAlice.issuer, claimForAlice.signature, claimForAlice.data, ''); const claimForBob = { - data: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('Some claim public data.')), - issuer: claimIssuerContract.address, + data: ethers.hexlify(ethers.toUtf8Bytes('Some claim public data.')), + issuer: claimIssuerContract.target, topic: claimTopics[0], scheme: 1, - identity: bobIdentity.address, + identity: bobIdentity.target, signature: '', }; claimForBob.signature = await claimIssuerSigningKey.signMessage( - ethers.utils.arrayify( - ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claimForBob.identity, claimForBob.topic, claimForBob.data]), + ethers.getBytes( + ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode(['address', 'uint256', 'bytes'], [claimForBob.identity, claimForBob.topic, claimForBob.data]), ), ), ); @@ -180,8 +170,8 @@ export async function deployFullSuiteFixture() { await token.connect(tokenAgent).mint(bobWallet.address, 500); await agentManager.connect(tokenAgent).addAgentAdmin(tokenAdmin.address); - await token.connect(deployer).addAgent(agentManager.address); - await identityRegistry.connect(deployer).addAgent(agentManager.address); + await token.connect(deployer).addAgent(agentManager.target); + await identityRegistry.connect(deployer).addAgent(agentManager.target); await token.connect(tokenAgent).unpause(); @@ -238,9 +228,8 @@ export async function deployFullSuiteFixture() { export async function deploySuiteWithModularCompliancesFixture() { const context = await loadFixture(deployFullSuiteFixture); - - const complianceProxy = await ethers.deployContract('ModularComplianceProxy', [context.authorities.trexImplementationAuthority.address]); - const compliance = await ethers.getContractAt('ModularCompliance', complianceProxy.address); + const complianceProxy = await ethers.deployContract('ModularComplianceProxy', [context.authorities.trexImplementationAuthority.target]); + const compliance = await ethers.getContractAt('ModularCompliance', complianceProxy.target); const complianceBeta = await ethers.deployContract('ModularCompliance'); await complianceBeta.init(); @@ -262,9 +251,9 @@ export async function deploySuiteWithModuleComplianceBoundToWallet() { await compliance.init(); const complianceModuleA = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleA.address); + await compliance.addModule(complianceModuleA.target); const complianceModuleB = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleB.address); + await compliance.addModule(complianceModuleB.target); await compliance.bindToken(context.accounts.charlieWallet.address); diff --git a/test/gateway.test.ts b/test/gateway.test.ts index 0c5113b6..1e95ee57 100644 --- a/test/gateway.test.ts +++ b/test/gateway.test.ts @@ -10,10 +10,10 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - await expect(gateway.connect(context.accounts.anotherWallet).setFactory(context.factories.trexFactory.address)).to.be.revertedWith( + await expect(gateway.connect(context.accounts.anotherWallet).setFactory(context.factories.trexFactory.target)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -23,23 +23,23 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - await expect(gateway.setFactory(ethers.constants.AddressZero)).to.be.revertedWithCustomError(gateway, 'ZeroAddress'); + await expect(gateway.setFactory(ethers.ZeroAddress)).to.be.revertedWithCustomError(gateway, 'ZeroAddress'); }); }); describe('if called with valid address', () => { it('should set Factory', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); - expect(await gateway.getFactory()).to.equal(ethers.constants.AddressZero); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); + expect(await gateway.getFactory()).to.equal(ethers.ZeroAddress); - const tx = await gateway.setFactory(context.factories.trexFactory.address); + const tx = await gateway.setFactory(context.factories.trexFactory.target); expect(tx).to.emit(gateway, 'FactorySet'); - expect(await gateway.getFactory()).to.equal(context.factories.trexFactory.address); + expect(await gateway.getFactory()).to.equal(context.factories.trexFactory.target); }); }); }); @@ -49,8 +49,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.connect(context.accounts.anotherWallet).setPublicDeploymentStatus(true)).to.be.revertedWith( 'Ownable: caller is not the owner', @@ -62,8 +62,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.setPublicDeploymentStatus(false)).to.be.revertedWithCustomError(gateway, 'PublicDeploymentAlreadyDisabled'); await gateway.setPublicDeploymentStatus(true); @@ -74,8 +74,8 @@ describe('TREXGateway', () => { it('should set new status', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); expect(await gateway.getPublicDeploymentStatus()).to.equal(false); const tx = await gateway.setPublicDeploymentStatus(true); @@ -90,8 +90,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).transferFactoryOwnership(context.accounts.anotherWallet.address), @@ -102,8 +102,8 @@ describe('TREXGateway', () => { it('should transfer factory ownership', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tx = await gateway.transferFactoryOwnership(context.accounts.deployer.address); expect(tx).to.emit(context.factories.trexFactory, 'OwnershipTransferred'); @@ -115,8 +115,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.connect(context.accounts.anotherWallet).enableDeploymentFee(true)).to.be.revertedWith( 'Ownable: caller is not the owner', @@ -128,8 +128,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.enableDeploymentFee(false)).to.be.revertedWithCustomError(gateway, 'DeploymentFeesAlreadyDisabled'); await gateway.enableDeploymentFee(true); @@ -140,8 +140,8 @@ describe('TREXGateway', () => { it('should set new status', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); expect(await gateway.isDeploymentFeeEnabled()).to.equal(false); const tx = await gateway.enableDeploymentFee(true); @@ -156,11 +156,11 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( - gateway.connect(context.accounts.anotherWallet).setDeploymentFee(100, context.suite.token.address, context.accounts.anotherWallet.address), + gateway.connect(context.accounts.anotherWallet).setDeploymentFee(100, context.suite.token.target, context.accounts.anotherWallet.address), ).to.be.revertedWith('Ownable: caller is not the owner'); }); }); @@ -169,14 +169,14 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - await expect(gateway.setDeploymentFee(100, ethers.constants.AddressZero, context.accounts.deployer.address)).to.be.revertedWithCustomError( + await expect(gateway.setDeploymentFee(100, ethers.ZeroAddress, context.accounts.deployer.address)).to.be.revertedWithCustomError( gateway, 'ZeroAddress', ); - await expect(gateway.setDeploymentFee(100, context.suite.token.address, ethers.constants.AddressZero)).to.be.revertedWithCustomError( + await expect(gateway.setDeploymentFee(100, context.suite.token.target, ethers.ZeroAddress)).to.be.revertedWithCustomError( gateway, 'ZeroAddress', ); @@ -186,14 +186,14 @@ describe('TREXGateway', () => { it('should set new fees structure', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - const tx = await gateway.setDeploymentFee(100, context.suite.token.address, context.accounts.deployer.address); + const tx = await gateway.setDeploymentFee(100, context.suite.token.target, context.accounts.deployer.address); expect(tx).to.emit(gateway, 'DeploymentFeeSet'); const feeStructure = await gateway.getDeploymentFee(); expect(feeStructure.fee).to.equal(100); - expect(feeStructure.feeToken).to.equal(context.suite.token.address); + expect(feeStructure.feeToken).to.equal(context.suite.token.target); expect(feeStructure.feeCollector).to.equal(context.accounts.deployer.address); }); }); @@ -204,12 +204,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).addDeployer(context.accounts.anotherWallet.address), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -217,8 +217,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.tokenAgent.address); await expect(gateway.addDeployer(context.accounts.tokenAgent.address)).to.be.revertedWithCustomError(gateway, 'DeployerAlreadyExists'); @@ -228,8 +228,8 @@ describe('TREXGateway', () => { it('should add new deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(false); const tx = await gateway.addDeployer(context.accounts.tokenAgent.address); @@ -243,8 +243,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addAgent(context.accounts.tokenAgent.address); await gateway.addDeployer(context.accounts.tokenAgent.address); @@ -258,8 +258,8 @@ describe('TREXGateway', () => { it('should add new deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(false); await gateway.addAgent(context.accounts.tokenAgent.address); @@ -275,12 +275,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).batchAddDeployer([context.accounts.anotherWallet.address]), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -288,8 +288,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.tokenAgent.address); const newDeployers = Array.from({ length: 9 }, () => ethers.Wallet.createRandom().address); @@ -302,8 +302,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const duplicateAddress = ethers.Wallet.createRandom().address; const newDeployers = Array.from({ length: 501 }, () => duplicateAddress); @@ -314,8 +314,8 @@ describe('TREXGateway', () => { it('should add 1 new deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(false); const tx = await gateway.batchAddDeployer([context.accounts.tokenAgent.address]); @@ -324,8 +324,8 @@ describe('TREXGateway', () => { }); it('should add 10 new deployers', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const newDeployers = Array.from({ length: 10 }, () => ethers.Wallet.createRandom().address); for (let i = 0; i < newDeployers.length; i += 1) { expect(await gateway.isDeployer(newDeployers[i])).to.equal(false); @@ -345,8 +345,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addAgent(context.accounts.anotherWallet.address); await gateway.connect(context.accounts.anotherWallet).addDeployer(context.accounts.tokenAgent.address); @@ -363,8 +363,8 @@ describe('TREXGateway', () => { it('should add 1 new deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addAgent(context.accounts.anotherWallet.address); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(false); @@ -374,8 +374,8 @@ describe('TREXGateway', () => { }); it('should add 10 new deployers', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const newDeployers = Array.from({ length: 10 }, () => ethers.Wallet.createRandom().address); for (let i = 0; i < newDeployers.length; i += 1) { expect(await gateway.isDeployer(newDeployers[i])).to.equal(false); @@ -397,12 +397,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).removeDeployer(context.accounts.anotherWallet.address), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -410,8 +410,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.removeDeployer(context.accounts.tokenAgent.address)).to.be.revertedWithCustomError(gateway, 'DeployerDoesNotExist'); }); @@ -420,8 +420,8 @@ describe('TREXGateway', () => { it('should remove deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.tokenAgent.address); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(true); @@ -437,12 +437,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).batchRemoveDeployer([context.accounts.anotherWallet.address]), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -450,8 +450,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.batchRemoveDeployer([context.accounts.tokenAgent.address])).to.be.revertedWithCustomError( gateway, @@ -463,8 +463,8 @@ describe('TREXGateway', () => { it('should remove deployer', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.tokenAgent.address); expect(await gateway.isDeployer(context.accounts.tokenAgent.address)).to.equal(true); @@ -477,8 +477,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const deployers = Array.from({ length: 9 }, () => ethers.Wallet.createRandom().address); await gateway.batchAddDeployer(deployers); @@ -494,8 +494,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const duplicateAddress = ethers.Wallet.createRandom().address; const deployers = Array.from({ length: 501 }, () => duplicateAddress); @@ -510,8 +510,8 @@ describe('TREXGateway', () => { it('should remove deployers', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const deployers = Array.from({ length: 10 }, () => ethers.Wallet.createRandom().address); await gateway.batchAddDeployer(deployers); @@ -533,12 +533,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).applyFeeDiscount(context.accounts.anotherWallet.address, 5000), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -546,8 +546,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.applyFeeDiscount(context.accounts.anotherWallet.address, 12000)).to.be.revertedWithCustomError( gateway, @@ -559,10 +559,10 @@ describe('TREXGateway', () => { it('should apply discount', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - await gateway.setDeploymentFee(20000, context.suite.token.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, context.suite.token.target, context.accounts.deployer.address); expect(await gateway.calculateFee(context.accounts.bobWallet.address)).to.equal(20000); const tx = await gateway.applyFeeDiscount(context.accounts.bobWallet.address, 5000); expect(tx).to.emit(gateway, 'FeeDiscountApplied'); @@ -576,12 +576,12 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).batchApplyFeeDiscount([context.accounts.anotherWallet.address], [5000]), - ).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + ).to.be.revertedWithCustomError(gateway, 'SenderIsNotAdmin'); }); }); describe('when called by owner', () => { @@ -589,8 +589,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect(gateway.batchApplyFeeDiscount([context.accounts.anotherWallet.address], [12000])).to.be.revertedWithCustomError( gateway, @@ -602,8 +602,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const duplicateAddress = ethers.Wallet.createRandom().address; const deployers = Array.from({ length: 501 }, () => duplicateAddress); @@ -616,10 +616,10 @@ describe('TREXGateway', () => { it('should apply discount', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); - await gateway.setDeploymentFee(20000, context.suite.token.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, context.suite.token.target, context.accounts.deployer.address); expect(await gateway.calculateFee(context.accounts.bobWallet.address)).to.equal(20000); const tx = await gateway.batchApplyFeeDiscount([context.accounts.bobWallet.address], [5000]); expect(tx).to.emit(gateway, 'FeeDiscountApplied'); @@ -632,8 +632,8 @@ describe('TREXGateway', () => { it('should revert the whole batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const deployers = Array.from({ length: 10 }, () => ethers.Wallet.createRandom().address); const discounts = Array.from({ length: 9 }, () => Math.floor(Math.random() * 10000)); discounts.push(12000); @@ -649,11 +649,11 @@ describe('TREXGateway', () => { it('should apply discounts to all deployers', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [ethers.ZeroAddress, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const deploymentFee = 20000; - await gateway.setDeploymentFee(deploymentFee, context.suite.token.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(deploymentFee, context.suite.token.target, context.accounts.deployer.address); const deployers = Array.from({ length: 10 }, () => ethers.Wallet.createRandom().address); const discounts = Array.from({ length: 10 }, () => 5000); @@ -679,8 +679,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).deployTREXSuite( @@ -689,8 +689,8 @@ describe('TREXGateway', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -710,8 +710,8 @@ describe('TREXGateway', () => { it('should revert', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await expect( gateway.connect(context.accounts.anotherWallet).deployTREXSuite( @@ -720,8 +720,8 @@ describe('TREXGateway', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -740,8 +740,8 @@ describe('TREXGateway', () => { it('should deploy a token for free', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { @@ -749,8 +749,8 @@ describe('TREXGateway', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -771,22 +771,22 @@ describe('TREXGateway', () => { it('should deploy a token for full fee', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 100000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 20000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 20000); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { owner: context.accounts.anotherWallet.address, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -808,23 +808,23 @@ describe('TREXGateway', () => { it('should deploy a token for half fee', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 100000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); await gateway.applyFeeDiscount(context.accounts.anotherWallet.address, 5000); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 20000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 20000); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { owner: context.accounts.anotherWallet.address, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -850,8 +850,8 @@ describe('TREXGateway', () => { it('should deploy', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const tx = gateway.connect(context.accounts.anotherWallet).deployTREXSuite( @@ -860,8 +860,8 @@ describe('TREXGateway', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -881,8 +881,8 @@ describe('TREXGateway', () => { it('should deploy', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); @@ -892,8 +892,8 @@ describe('TREXGateway', () => { name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -914,23 +914,23 @@ describe('TREXGateway', () => { it('should deploy a token for full fee', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 100000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 20000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 20000); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { owner: context.accounts.anotherWallet.address, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -952,24 +952,24 @@ describe('TREXGateway', () => { it('should deploy a token for half fee', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 100000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); await gateway.applyFeeDiscount(context.accounts.anotherWallet.address, 5000); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 20000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 20000); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { owner: context.accounts.anotherWallet.address, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -991,24 +991,24 @@ describe('TREXGateway', () => { it('should deploy a token for free', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 100000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); await gateway.applyFeeDiscount(context.accounts.anotherWallet.address, 10000); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 20000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 20000); const tx = await gateway.connect(context.accounts.anotherWallet).deployTREXSuite( { owner: context.accounts.anotherWallet.address, name: 'Token name', symbol: 'SYM', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1035,8 +1035,8 @@ describe('TREXGateway', () => { it('should revert for batch deployment', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1046,8 +1046,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1070,8 +1070,8 @@ describe('TREXGateway', () => { it('should revert the whole batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1081,8 +1081,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1099,8 +1099,8 @@ describe('TREXGateway', () => { name: 'Token name behalf', symbol: 'SYM42', decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1121,8 +1121,8 @@ describe('TREXGateway', () => { it('should revert the batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1132,8 +1132,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1155,8 +1155,8 @@ describe('TREXGateway', () => { it('should deploy tokens for free in a batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1166,8 +1166,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1193,14 +1193,14 @@ describe('TREXGateway', () => { it('should deploy tokens for full fee in a batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 500000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 100000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 100000); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1210,8 +1210,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1238,14 +1238,14 @@ describe('TREXGateway', () => { it('should deploy tokens for half fee in a batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, true], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, true], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); const feeToken = await ethers.deployContract('TestERC20', ['FeeToken', 'FT']); await feeToken.mint(context.accounts.anotherWallet.address, 500000); - await gateway.setDeploymentFee(20000, feeToken.address, context.accounts.deployer.address); + await gateway.setDeploymentFee(20000, feeToken.target, context.accounts.deployer.address); await gateway.enableDeploymentFee(true); await gateway.applyFeeDiscount(context.accounts.anotherWallet.address, 5000); - await feeToken.connect(context.accounts.anotherWallet).approve(gateway.address, 50000); + await feeToken.connect(context.accounts.anotherWallet).approve(gateway.target, 50000); const tokenDetailsArray = []; const claimDetailsArray = []; @@ -1255,8 +1255,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1286,8 +1286,8 @@ describe('TREXGateway', () => { it('should deploy in batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const tokenDetailsArray = []; @@ -1298,8 +1298,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1324,8 +1324,8 @@ describe('TREXGateway', () => { it('should deploy in batch', async () => { const context = await loadFixture(deployFullSuiteFixture); - const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); - await context.factories.trexFactory.transferOwnership(gateway.address); + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.target); await gateway.addDeployer(context.accounts.anotherWallet.address); const tokenDetailsArray = []; @@ -1336,8 +1336,8 @@ describe('TREXGateway', () => { name: `Token name ${i}`, symbol: `SYM${i}`, decimals: 8, - irs: ethers.constants.AddressZero, - ONCHAINID: ethers.constants.AddressZero, + irs: ethers.ZeroAddress, + ONCHAINID: ethers.ZeroAddress, irAgents: [], tokenAgents: [], complianceModules: [], @@ -1361,4 +1361,47 @@ describe('TREXGateway', () => { }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + + const unsupportedInterfaceId = '0x12345678'; + expect(await gateway.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the ITREXGateway interface ID', async () => { + const context = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + + const iTREXGatewayInterfaceId = await interfaceIdCalculator.getITREXGatewayInterfaceId(); + expect(await gateway.supportsInterface(iTREXGatewayInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const context = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await gateway.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const context = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.target, false], context.accounts.deployer); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await gateway.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/registries/claim-topics-registry.test.ts b/test/registries/claim-topics-registry.test.ts index 4cf8760d..e1e279df 100644 --- a/test/registries/claim-topics-registry.test.ts +++ b/test/registries/claim-topics-registry.test.ts @@ -1,5 +1,6 @@ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; +import { ethers } from 'hardhat'; import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; describe('ClaimTopicsRegistry', () => { @@ -37,7 +38,10 @@ describe('ClaimTopicsRegistry', () => { await Promise.all(Array.from({ length: 14 }, (_, i) => i).map((i) => claimTopicsRegistry.addClaimTopic(i))); - await expect(claimTopicsRegistry.connect(deployer).addClaimTopic(14)).to.be.revertedWith('cannot require more than 15 topics'); + await expect(claimTopicsRegistry.connect(deployer).addClaimTopic(14)).to.be.revertedWithCustomError( + claimTopicsRegistry, + 'MaxTopicsReached', + ); }); }); @@ -50,7 +54,10 @@ describe('ClaimTopicsRegistry', () => { await claimTopicsRegistry.addClaimTopic(1); - await expect(claimTopicsRegistry.connect(deployer).addClaimTopic(1)).to.be.revertedWith('claimTopic already exists'); + await expect(claimTopicsRegistry.connect(deployer).addClaimTopic(1)).to.be.revertedWithCustomError( + claimTopicsRegistry, + 'ClaimTopicAlreadyExists', + ); }); }); }); @@ -84,4 +91,47 @@ describe('ClaimTopicsRegistry', () => { }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { claimTopicsRegistry }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await claimTopicsRegistry.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IERC3643ClaimTopicsRegistry interface ID', async () => { + const { + suite: { claimTopicsRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iClaimTopicsRegistryInterfaceId = await interfaceIdCalculator.getIERC3643ClaimTopicsRegistryInterfaceId(); + expect(await claimTopicsRegistry.supportsInterface(iClaimTopicsRegistryInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { claimTopicsRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await claimTopicsRegistry.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { claimTopicsRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await claimTopicsRegistry.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/registries/identity-registry-storage.test.ts b/test/registries/identity-registry-storage.test.ts index 9fa0e63f..01ab4638 100644 --- a/test/registries/identity-registry-storage.test.ts +++ b/test/registries/identity-registry-storage.test.ts @@ -26,8 +26,8 @@ describe('IdentityRegistryStorage', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - identityRegistryStorage.connect(anotherWallet).addIdentityToStorage(charlieWallet.address, charlieIdentity.address, 42), - ).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + identityRegistryStorage.connect(anotherWallet).addIdentityToStorage(charlieWallet.address, charlieIdentity.target, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'CallerDoesNotHaveAgentRole'); }); }); @@ -42,8 +42,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(charlieWallet.address, ethers.constants.AddressZero, 42), - ).to.be.revertedWith('invalid argument - zero address'); + identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(charlieWallet.address, ethers.ZeroAddress, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'ZeroAddress'); }); }); @@ -58,8 +58,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(ethers.constants.AddressZero, charlieIdentity.address, 42), - ).to.be.revertedWith('invalid argument - zero address'); + identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(ethers.ZeroAddress, charlieIdentity.target, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'ZeroAddress'); }); }); @@ -74,8 +74,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(bobWallet.address, charlieIdentity.address, 42), - ).to.be.revertedWith('address stored already'); + identityRegistryStorage.connect(tokenAgent).addIdentityToStorage(bobWallet.address, charlieIdentity.target, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'AddressAlreadyStored'); }); }); }); @@ -91,8 +91,8 @@ describe('IdentityRegistryStorage', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - identityRegistryStorage.connect(anotherWallet).modifyStoredIdentity(charlieWallet.address, charlieIdentity.address), - ).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + identityRegistryStorage.connect(anotherWallet).modifyStoredIdentity(charlieWallet.address, charlieIdentity.target), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'CallerDoesNotHaveAgentRole'); }); }); @@ -107,8 +107,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(charlieWallet.address, ethers.constants.AddressZero), - ).to.be.revertedWith('invalid argument - zero address'); + identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(charlieWallet.address, ethers.ZeroAddress), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'ZeroAddress'); }); }); @@ -123,8 +123,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(ethers.constants.AddressZero, charlieIdentity.address), - ).to.be.revertedWith('invalid argument - zero address'); + identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(ethers.ZeroAddress, charlieIdentity.target), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'ZeroAddress'); }); }); @@ -139,8 +139,8 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); await expect( - identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(charlieWallet.address, charlieIdentity.address), - ).to.be.revertedWith('address not stored yet'); + identityRegistryStorage.connect(tokenAgent).modifyStoredIdentity(charlieWallet.address, charlieIdentity.target), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'AddressNotYetStored'); }); }); }); @@ -154,9 +154,9 @@ describe('IdentityRegistryStorage', () => { accounts: { anotherWallet, charlieWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(anotherWallet).modifyStoredInvestorCountry(charlieWallet.address, 42)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', - ); + await expect( + identityRegistryStorage.connect(anotherWallet).modifyStoredInvestorCountry(charlieWallet.address, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'CallerDoesNotHaveAgentRole'); }); }); @@ -170,8 +170,9 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); - await expect(identityRegistryStorage.connect(tokenAgent).modifyStoredInvestorCountry(ethers.constants.AddressZero, 42)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(identityRegistryStorage.connect(tokenAgent).modifyStoredInvestorCountry(ethers.ZeroAddress, 42)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'ZeroAddress', ); }); }); @@ -185,9 +186,9 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); - await expect(identityRegistryStorage.connect(tokenAgent).modifyStoredInvestorCountry(charlieWallet.address, 42)).to.be.revertedWith( - 'address not stored yet', - ); + await expect( + identityRegistryStorage.connect(tokenAgent).modifyStoredInvestorCountry(charlieWallet.address, 42), + ).to.be.revertedWithCustomError(identityRegistryStorage, 'AddressNotYetStored'); }); }); }); @@ -201,8 +202,9 @@ describe('IdentityRegistryStorage', () => { accounts: { anotherWallet, charlieWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(anotherWallet).removeIdentityFromStorage(charlieWallet.address)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(identityRegistryStorage.connect(anotherWallet).removeIdentityFromStorage(charlieWallet.address)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -217,8 +219,9 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); - await expect(identityRegistryStorage.connect(tokenAgent).removeIdentityFromStorage(ethers.constants.AddressZero)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(identityRegistryStorage.connect(tokenAgent).removeIdentityFromStorage(ethers.ZeroAddress)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'ZeroAddress', ); }); }); @@ -232,8 +235,9 @@ describe('IdentityRegistryStorage', () => { await identityRegistryStorage.addAgent(tokenAgent.address); - await expect(identityRegistryStorage.connect(tokenAgent).removeIdentityFromStorage(charlieWallet.address)).to.be.revertedWith( - 'address not stored yet', + await expect(identityRegistryStorage.connect(tokenAgent).removeIdentityFromStorage(charlieWallet.address)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'AddressNotYetStored', ); }); }); @@ -249,7 +253,7 @@ describe('IdentityRegistryStorage', () => { identities: { charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(anotherWallet).bindIdentityRegistry(charlieIdentity.address)).to.be.revertedWith( + await expect(identityRegistryStorage.connect(anotherWallet).bindIdentityRegistry(charlieIdentity.target)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -263,8 +267,9 @@ describe('IdentityRegistryStorage', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(deployer).bindIdentityRegistry(ethers.constants.AddressZero)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(identityRegistryStorage.connect(deployer).bindIdentityRegistry(ethers.ZeroAddress)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'ZeroAddress', ); }); }); @@ -281,8 +286,9 @@ describe('IdentityRegistryStorage', () => { Array.from({ length: 299 }, () => identityRegistryStorage.connect(deployer).bindIdentityRegistry(ethers.Wallet.createRandom().address)), ); - await expect(identityRegistryStorage.connect(deployer).bindIdentityRegistry(charlieIdentity.address)).to.be.revertedWith( - 'cannot bind more than 300 IR to 1 IRS', + await expect(identityRegistryStorage.connect(deployer).bindIdentityRegistry(charlieIdentity.target)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'MaxIRByIRSReached', ); }); }); @@ -298,7 +304,7 @@ describe('IdentityRegistryStorage', () => { identities: { charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(anotherWallet).unbindIdentityRegistry(charlieIdentity.address)).to.be.revertedWith( + await expect(identityRegistryStorage.connect(anotherWallet).unbindIdentityRegistry(charlieIdentity.target)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -312,8 +318,9 @@ describe('IdentityRegistryStorage', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistryStorage.connect(deployer).unbindIdentityRegistry(ethers.constants.AddressZero)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(identityRegistryStorage.connect(deployer).unbindIdentityRegistry(ethers.ZeroAddress)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'ZeroAddress', ); }); }); @@ -325,10 +332,11 @@ describe('IdentityRegistryStorage', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await identityRegistryStorage.unbindIdentityRegistry(identityRegistry.address); + await identityRegistryStorage.unbindIdentityRegistry(identityRegistry.target); - await expect(identityRegistryStorage.connect(deployer).unbindIdentityRegistry(identityRegistry.address)).to.be.revertedWith( - 'identity registry is not stored', + await expect(identityRegistryStorage.connect(deployer).unbindIdentityRegistry(identityRegistry.target)).to.be.revertedWithCustomError( + identityRegistryStorage, + 'IdentityRegistryNotStored', ); }); }); @@ -341,18 +349,58 @@ describe('IdentityRegistryStorage', () => { identities: { charlieIdentity, bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await identityRegistryStorage.bindIdentityRegistry(charlieIdentity.address); - await identityRegistryStorage.bindIdentityRegistry(bobIdentity.address); + await identityRegistryStorage.bindIdentityRegistry(charlieIdentity.target); + await identityRegistryStorage.bindIdentityRegistry(bobIdentity.target); - const tx = await identityRegistryStorage.connect(deployer).unbindIdentityRegistry(charlieIdentity.address); - await expect(tx).to.emit(identityRegistryStorage, 'IdentityRegistryUnbound').withArgs(charlieIdentity.address); + const tx = await identityRegistryStorage.connect(deployer).unbindIdentityRegistry(charlieIdentity.target); + await expect(tx).to.emit(identityRegistryStorage, 'IdentityRegistryUnbound').withArgs(charlieIdentity.target); - await expect(identityRegistryStorage.linkedIdentityRegistries()).to.eventually.be.deep.equal([ - identityRegistry.address, - bobIdentity.address, - ]); + await expect(identityRegistryStorage.linkedIdentityRegistries()).to.eventually.be.deep.equal([identityRegistry.target, bobIdentity.target]); }); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { identityRegistryStorage }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await identityRegistryStorage.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IERC3643IdentityRegistryStorage interface ID', async () => { + const { + suite: { identityRegistryStorage }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iIdentityRegistryStorageInterfaceId = await interfaceIdCalculator.getIERC3643IdentityRegistryStorageInterfaceId(); + expect(await identityRegistryStorage.supportsInterface(iIdentityRegistryStorageInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { identityRegistryStorage }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await identityRegistryStorage.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { identityRegistryStorage }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await identityRegistryStorage.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/registries/identity-registry.test.ts b/test/registries/identity-registry.test.ts index aec1b251..ba0c7023 100644 --- a/test/registries/identity-registry.test.ts +++ b/test/registries/identity-registry.test.ts @@ -11,27 +11,27 @@ describe('IdentityRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect( - identityRegistry.connect(deployer).init(ethers.constants.AddressZero, ethers.constants.AddressZero, ethers.constants.AddressZero), - ).to.be.revertedWith('Initializable: contract is already initialized'); + await expect(identityRegistry.connect(deployer).init(ethers.ZeroAddress, ethers.ZeroAddress, ethers.ZeroAddress)).to.be.revertedWith( + 'Initializable: contract is already initialized', + ); }); it('should reject zero address for Trusted Issuers Registry', async () => { const identityRegistry = await ethers.deployContract('IdentityRegistry'); const address = ethers.Wallet.createRandom().address; - await expect(identityRegistry.init(ethers.constants.AddressZero, address, address)).to.be.revertedWith('invalid argument - zero address'); + await expect(identityRegistry.init(ethers.ZeroAddress, address, address)).to.be.revertedWithCustomError(identityRegistry, 'ZeroAddress'); }); it('should reject zero address for Claim Topics Registry', async () => { const identityRegistry = await ethers.deployContract('IdentityRegistry'); const address = ethers.Wallet.createRandom().address; - await expect(identityRegistry.init(address, ethers.constants.AddressZero, address)).to.be.revertedWith('invalid argument - zero address'); + await expect(identityRegistry.init(address, ethers.ZeroAddress, address)).to.be.revertedWithCustomError(identityRegistry, 'ZeroAddress'); }); it('should reject zero address for Identity Storage', async () => { const identityRegistry = await ethers.deployContract('IdentityRegistry'); const address = ethers.Wallet.createRandom().address; - await expect(identityRegistry.init(address, address, ethers.constants.AddressZero)).to.be.revertedWith('invalid argument - zero address'); + await expect(identityRegistry.init(address, address, ethers.ZeroAddress)).to.be.revertedWithCustomError(identityRegistry, 'ZeroAddress'); }); }); @@ -44,9 +44,9 @@ describe('IdentityRegistry', () => { identities: { bobIdentity, charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).updateIdentity(bobIdentity.address, charlieIdentity.address)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', - ); + await expect( + identityRegistry.connect(anotherWallet).updateIdentity(bobIdentity.target, charlieIdentity.target), + ).to.be.revertedWithCustomError(identityRegistry, 'CallerDoesNotHaveAgentRole'); }); }); }); @@ -60,8 +60,9 @@ describe('IdentityRegistry', () => { identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).updateCountry(bobIdentity.address, 100)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(identityRegistry.connect(anotherWallet).updateCountry(bobIdentity.target, 100)).to.be.revertedWithCustomError( + identityRegistry, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -75,8 +76,9 @@ describe('IdentityRegistry', () => { accounts: { anotherWallet, bobWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).deleteIdentity(bobWallet.address)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(identityRegistry.connect(anotherWallet).deleteIdentity(bobWallet.address)).to.be.revertedWithCustomError( + identityRegistry, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -91,8 +93,8 @@ describe('IdentityRegistry', () => { } = await loadFixture(deployFullSuiteFixture); await expect( - identityRegistry.connect(anotherWallet).registerIdentity(ethers.constants.AddressZero, ethers.constants.AddressZero, 0), - ).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + identityRegistry.connect(anotherWallet).registerIdentity(ethers.ZeroAddress, ethers.ZeroAddress, 0), + ).to.be.revertedWithCustomError(identityRegistry, 'CallerDoesNotHaveAgentRole'); }); }); }); @@ -105,7 +107,7 @@ describe('IdentityRegistry', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).setIdentityRegistryStorage(ethers.constants.AddressZero)).to.be.revertedWith( + await expect(identityRegistry.connect(anotherWallet).setIdentityRegistryStorage(ethers.ZeroAddress)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -118,9 +120,9 @@ describe('IdentityRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - const tx = await identityRegistry.connect(deployer).setIdentityRegistryStorage(ethers.constants.AddressZero); - await expect(tx).to.emit(identityRegistry, 'IdentityStorageSet').withArgs(ethers.constants.AddressZero); - expect(await identityRegistry.identityStorage()).to.be.equal(ethers.constants.AddressZero); + const tx = await identityRegistry.connect(deployer).setIdentityRegistryStorage(ethers.ZeroAddress); + await expect(tx).to.emit(identityRegistry, 'IdentityStorageSet').withArgs(ethers.ZeroAddress); + expect(await identityRegistry.identityStorage()).to.be.equal(ethers.ZeroAddress); }); }); }); @@ -133,7 +135,7 @@ describe('IdentityRegistry', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).setClaimTopicsRegistry(ethers.constants.AddressZero)).to.be.revertedWith( + await expect(identityRegistry.connect(anotherWallet).setClaimTopicsRegistry(ethers.ZeroAddress)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -146,9 +148,9 @@ describe('IdentityRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - const tx = await identityRegistry.connect(deployer).setClaimTopicsRegistry(ethers.constants.AddressZero); - await expect(tx).to.emit(identityRegistry, 'ClaimTopicsRegistrySet').withArgs(ethers.constants.AddressZero); - expect(await identityRegistry.topicsRegistry()).to.be.equal(ethers.constants.AddressZero); + const tx = await identityRegistry.connect(deployer).setClaimTopicsRegistry(ethers.ZeroAddress); + await expect(tx).to.emit(identityRegistry, 'ClaimTopicsRegistrySet').withArgs(ethers.ZeroAddress); + expect(await identityRegistry.topicsRegistry()).to.be.equal(ethers.ZeroAddress); }); }); }); @@ -161,7 +163,7 @@ describe('IdentityRegistry', () => { accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(identityRegistry.connect(anotherWallet).setTrustedIssuersRegistry(ethers.constants.AddressZero)).to.be.revertedWith( + await expect(identityRegistry.connect(anotherWallet).setTrustedIssuersRegistry(ethers.ZeroAddress)).to.be.revertedWith( 'Ownable: caller is not the owner', ); }); @@ -174,9 +176,9 @@ describe('IdentityRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - const tx = await identityRegistry.connect(deployer).setTrustedIssuersRegistry(ethers.constants.AddressZero); - await expect(tx).to.emit(identityRegistry, 'TrustedIssuersRegistrySet').withArgs(ethers.constants.AddressZero); - expect(await identityRegistry.issuersRegistry()).to.be.equal(ethers.constants.AddressZero); + const tx = await identityRegistry.connect(deployer).setTrustedIssuersRegistry(ethers.ZeroAddress); + await expect(tx).to.emit(identityRegistry, 'TrustedIssuersRegistrySet').withArgs(ethers.ZeroAddress); + expect(await identityRegistry.issuersRegistry()).to.be.equal(ethers.ZeroAddress); }); }); }); @@ -190,7 +192,7 @@ describe('IdentityRegistry', () => { identities: { charlieIdentity }, } = await loadFixture(deployFullSuiteFixture); - await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity.address, 0); + await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity.target, 0); await expect(identityRegistry.isVerified(charlieWallet.address)).to.eventually.be.false; @@ -248,13 +250,13 @@ describe('IdentityRegistry', () => { const trickyClaimIssuer = await ethers.deployContract('ClaimIssuerTrick'); const claimTopics = await claimTopicsRegistry.getClaimTopics(); - await trustedIssuersRegistry.removeTrustedIssuer(claimIssuerContract.address); - await trustedIssuersRegistry.addTrustedIssuer(trickyClaimIssuer.address, claimTopics); - await trustedIssuersRegistry.addTrustedIssuer(claimIssuerContract.address, claimTopics); + await trustedIssuersRegistry.removeTrustedIssuer(claimIssuerContract.target); + await trustedIssuersRegistry.addTrustedIssuer(trickyClaimIssuer.target, Array.from(claimTopics)); + await trustedIssuersRegistry.addTrustedIssuer(claimIssuerContract.target, Array.from(claimTopics)); const claimIds = await aliceIdentity.getClaimIdsByTopic(claimTopics[0]); const claim = await aliceIdentity.getClaim(claimIds[0]); await aliceIdentity.connect(aliceWallet).removeClaim(claimIds[0]); - await aliceIdentity.connect(aliceWallet).addClaim(claimTopics[0], 1, trickyClaimIssuer.address, '0x00', '0x00', ''); + await aliceIdentity.connect(aliceWallet).addClaim(claimTopics[0], 1, trickyClaimIssuer.target, '0x00', '0x00', ''); await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri); await expect(identityRegistry.isVerified(aliceWallet.address)).to.eventually.be.true; @@ -269,13 +271,183 @@ describe('IdentityRegistry', () => { const trickyClaimIssuer = await ethers.deployContract('ClaimIssuerTrick'); const claimTopics = await claimTopicsRegistry.getClaimTopics(); - await trustedIssuersRegistry.addTrustedIssuer(trickyClaimIssuer.address, claimTopics); + await trustedIssuersRegistry.addTrustedIssuer(trickyClaimIssuer.target, Array.from(claimTopics)); const claimIds = await aliceIdentity.getClaimIdsByTopic(claimTopics[0]); await aliceIdentity.connect(aliceWallet).removeClaim(claimIds[0]); - await aliceIdentity.connect(aliceWallet).addClaim(claimTopics[0], 1, trickyClaimIssuer.address, '0x00', '0x00', ''); + await aliceIdentity.connect(aliceWallet).addClaim(claimTopics[0], 1, trickyClaimIssuer.target, '0x00', '0x00', ''); await expect(identityRegistry.isVerified(aliceWallet.address)).to.eventually.be.false; }); }); }); + describe('.disableEligibilityChecks()', () => { + describe('when called by a non-owner', () => { + it('should revert with Ownable: caller is not the owner', async () => { + const { + suite: { identityRegistry }, + accounts: { anotherWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(identityRegistry.connect(anotherWallet).disableEligibilityChecks()).to.be.revertedWith('Ownable: caller is not the owner'); + }); + }); + describe('when called by the owner', () => { + it('should disable eligibility checks and allow all addresses to be verified', async () => { + const { + suite: { identityRegistry }, + accounts: { deployer, aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(identityRegistry.connect(deployer).disableEligibilityChecks()).to.emit(identityRegistry, 'EligibilityChecksDisabled'); + + await expect(identityRegistry.isVerified(aliceWallet.address)).to.eventually.be.true; + }); + }); + + describe('when eligibility checks are already disabled', () => { + it('should revert with EligibilityChecksDisabledAlready', async () => { + const { + suite: { identityRegistry }, + accounts: { deployer }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(deployer).disableEligibilityChecks(); + + await expect(identityRegistry.connect(deployer).disableEligibilityChecks()).to.be.revertedWithCustomError( + identityRegistry, + 'EligibilityChecksDisabledAlready', + ); + }); + }); + }); + describe('.enableEligibilityChecks()', () => { + describe('when called by a non-owner', () => { + it('should revert with Ownable: caller is not the owner', async () => { + const { + suite: { identityRegistry }, + accounts: { anotherWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(identityRegistry.connect(anotherWallet).enableEligibilityChecks()).to.be.revertedWith('Ownable: caller is not the owner'); + }); + }); + describe('when called by the owner after disabling', () => { + it('should re-enable eligibility checks and enforce normal verification', async () => { + const { + suite: { identityRegistry }, + accounts: { deployer, anotherWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(deployer).disableEligibilityChecks(); + await expect(identityRegistry.isVerified(anotherWallet.address)).to.eventually.be.true; + + await expect(identityRegistry.connect(deployer).enableEligibilityChecks()).to.emit(identityRegistry, 'EligibilityChecksEnabled'); + + await expect(identityRegistry.isVerified(anotherWallet.address)).to.eventually.be.false; + }); + }); + + describe('when eligibility checks are already enabled', () => { + it('should revert with EligibilityChecksEnabledAlready', async () => { + const { + suite: { identityRegistry }, + accounts: { deployer }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(identityRegistry.connect(deployer).enableEligibilityChecks()).to.be.revertedWithCustomError( + identityRegistry, + 'EligibilityChecksEnabledAlready', + ); + }); + }); + }); + + describe('.isVerified()', () => { + describe('when eligibility checks are disabled', () => { + it('should return true for any address', async () => { + const { + suite: { identityRegistry }, + accounts: { deployer, charlieWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(deployer).disableEligibilityChecks(); + await expect(identityRegistry.isVerified(charlieWallet.address)).to.eventually.be.true; + }); + }); + + describe('when eligibility checks are re-enabled', () => { + it('should resume normal eligibility checks', async () => { + const { + suite: { identityRegistry, claimTopicsRegistry }, + accounts: { deployer, charlieWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(deployer).disableEligibilityChecks(); + await expect(identityRegistry.isVerified(charlieWallet.address)).to.eventually.be.true; + + await identityRegistry.connect(deployer).enableEligibilityChecks(); + + const topics = await claimTopicsRegistry.getClaimTopics(); + if (topics.length > 0) { + await expect(identityRegistry.isVerified(charlieWallet.address)).to.eventually.be.false; + } else { + await expect(identityRegistry.isVerified(charlieWallet.address)).to.eventually.be.true; + } + }); + }); + }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { identityRegistry }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await identityRegistry.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IIdentityRegistry interface ID', async () => { + const { + suite: { identityRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iIdentityRegistryInterfaceId = await interfaceIdCalculator.getIIdentityRegistryInterfaceId(); + expect(await identityRegistry.supportsInterface(iIdentityRegistryInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC3643IdentityRegistry interface ID', async () => { + const { + suite: { identityRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iIdentityRegistryInterfaceId = await interfaceIdCalculator.getIERC3643IdentityRegistryInterfaceId(); + expect(await identityRegistry.supportsInterface(iIdentityRegistryInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { identityRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await identityRegistry.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { identityRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await identityRegistry.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/registries/trusted-issuers-registry.test.ts b/test/registries/trusted-issuers-registry.test.ts index ac80e57b..e7ba0392 100644 --- a/test/registries/trusted-issuers-registry.test.ts +++ b/test/registries/trusted-issuers-registry.test.ts @@ -26,8 +26,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(ethers.constants.AddressZero, [10])).to.be.revertedWith( - 'invalid argument - zero address', + await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(ethers.ZeroAddress, [10])).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'ZeroAddress', ); }); }); @@ -39,11 +40,11 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - const claimTopics = await trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.address); + const claimTopics = await trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.target); - await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(claimIssuerContract.address, claimTopics)).to.be.revertedWith( - 'trusted Issuer already exists', - ); + await expect( + trustedIssuersRegistry.connect(deployer).addTrustedIssuer(claimIssuerContract.target, Array.from(claimTopics)), + ).to.be.revertedWithCustomError(trustedIssuersRegistry, 'TrustedIssuerAlreadyExists'); }); }); @@ -54,8 +55,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, [])).to.be.revertedWith( - 'trusted claim topics cannot be empty', + await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, [])).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'TrustedClaimTopicsCannotBeEmpty', ); }); }); @@ -69,8 +71,9 @@ describe('TrustedIssuersRegistry', () => { const claimTopics = Array.from({ length: 16 }, (_, i) => i); - await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, claimTopics)).to.be.revertedWith( - 'cannot have more than 15 claim topics', + await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, claimTopics)).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'MaxClaimTopcisReached', ); }); }); @@ -91,8 +94,9 @@ describe('TrustedIssuersRegistry', () => { }), ); - await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, claimTopics)).to.be.revertedWith( - 'cannot have more than 50 trusted issuers', + await expect(trustedIssuersRegistry.connect(deployer).addTrustedIssuer(deployer.address, claimTopics)).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'MaxTrustedIssuersReached', ); }); }); @@ -121,8 +125,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).removeTrustedIssuer(ethers.constants.AddressZero)).to.be.revertedWith( - 'invalid argument - zero address', + await expect(trustedIssuersRegistry.connect(deployer).removeTrustedIssuer(ethers.ZeroAddress)).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'ZeroAddress', ); }); }); @@ -134,7 +139,10 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).removeTrustedIssuer(deployer.address)).to.be.revertedWith('NOT a trusted issuer'); + await expect(trustedIssuersRegistry.connect(deployer).removeTrustedIssuer(deployer.address)).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'NotATrustedIssuer', + ); }); }); @@ -156,7 +164,7 @@ describe('TrustedIssuersRegistry', () => { await expect(trustedIssuersRegistry.isTrustedIssuer(anotherWallet.address)).to.eventually.be.false; await expect(trustedIssuersRegistry.getTrustedIssuers()).to.eventually.deep.eq([ - claimIssuerContract.address, + claimIssuerContract.target, bobWallet.address, charlieWallet.address, ]); @@ -187,8 +195,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(ethers.constants.AddressZero, [10])).to.be.revertedWith( - 'invalid argument - zero address', + await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(ethers.ZeroAddress, [10])).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'ZeroAddress', ); }); }); @@ -200,8 +209,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(deployer.address, [10])).to.be.revertedWith( - 'NOT a trusted issuer', + await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(deployer.address, [10])).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'NotATrustedIssuer', ); }); }); @@ -215,9 +225,9 @@ describe('TrustedIssuersRegistry', () => { const claimTopics = Array.from({ length: 16 }, (_, i) => i); - await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.address, claimTopics)).to.be.revertedWith( - 'cannot have more than 15 claim topics', - ); + await expect( + trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.target, claimTopics), + ).to.be.revertedWithCustomError(trustedIssuersRegistry, 'MaxClaimTopcisReached'); }); }); @@ -228,9 +238,9 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.address, [])).to.be.revertedWith( - 'claim topics cannot be empty', - ); + await expect( + trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.target, []), + ).to.be.revertedWithCustomError(trustedIssuersRegistry, 'ClaimTopicsCannotBeEmpty'); }); }); @@ -241,15 +251,15 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - const claimTopics = await trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.address); + const claimTopics = await trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.target); - const tx = await trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.address, [66, 100]); - await expect(tx).to.emit(trustedIssuersRegistry, 'ClaimTopicsUpdated').withArgs(claimIssuerContract.address, [66, 100]); + const tx = await trustedIssuersRegistry.connect(deployer).updateIssuerClaimTopics(claimIssuerContract.target, [66, 100]); + await expect(tx).to.emit(trustedIssuersRegistry, 'ClaimTopicsUpdated').withArgs(claimIssuerContract.target, [66, 100]); - await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.address, 66)).to.eventually.be.true; - await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.address, 100)).to.eventually.be.true; - await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.address, claimTopics[0])).to.eventually.be.false; - await expect(trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.address)).to.eventually.deep.eq([66, 100]); + await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.target, 66)).to.eventually.be.true; + await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.target, 100)).to.eventually.be.true; + await expect(trustedIssuersRegistry.hasClaimTopic(claimIssuerContract.target, claimTopics[0])).to.eventually.be.false; + await expect(trustedIssuersRegistry.getTrustedIssuerClaimTopics(claimIssuerContract.target)).to.eventually.deep.eq([66, 100]); }); }); }); @@ -263,10 +273,54 @@ describe('TrustedIssuersRegistry', () => { accounts: { deployer }, } = await loadFixture(deployFullSuiteFixture); - await expect(trustedIssuersRegistry.connect(deployer).getTrustedIssuerClaimTopics(deployer.address)).to.be.revertedWith( - "trusted Issuer doesn't exist", + await expect(trustedIssuersRegistry.connect(deployer).getTrustedIssuerClaimTopics(deployer.address)).to.be.revertedWithCustomError( + trustedIssuersRegistry, + 'TrustedIssuerDoesNotExist', ); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { trustedIssuersRegistry }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await trustedIssuersRegistry.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IERC3643TrustedIssuersRegistry interface ID', async () => { + const { + suite: { trustedIssuersRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iTrustedIssuersRegistryInterfaceId = await interfaceIdCalculator.getIERC3643TrustedIssuersRegistryInterfaceId(); + expect(await trustedIssuersRegistry.supportsInterface(iTrustedIssuersRegistryInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { trustedIssuersRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await trustedIssuersRegistry.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { trustedIssuersRegistry }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await trustedIssuersRegistry.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/token/token-agent-restrictions.test.ts b/test/token/token-agent-restrictions.test.ts new file mode 100644 index 00000000..2cb2d8ca --- /dev/null +++ b/test/token/token-agent-restrictions.test.ts @@ -0,0 +1,99 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; + +describe('Token - Agent Restrictions', () => { + describe('.setAgentRestrictions()', () => { + describe('when the caller is not the owner', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { anotherWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + await expect( + token.connect(anotherWallet).setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: true, + disableBurn: true, + disableForceTransfer: true, + disableMint: true, + disablePartialFreeze: true, + disablePause: true, + disableRecovery: true, + }), + ).to.be.revertedWith('Ownable: caller is not the owner'); + }); + }); + + describe('when the caller is the owner', () => { + describe('when the given address is not agent', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { anotherWallet }, + } = await loadFixture(deployFullSuiteFixture); + await expect( + token.setAgentRestrictions(anotherWallet.address, { + disableAddressFreeze: true, + disableBurn: true, + disableForceTransfer: true, + disableMint: true, + disablePartialFreeze: true, + disablePause: true, + disableRecovery: true, + }), + ).to.be.revertedWithCustomError(token, `AddressNotAgent`); + }); + }); + + describe('when the given address is an agent', () => { + it('should set restrictions', async () => { + const { + suite: { token }, + accounts: { tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + await expect( + token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: true, + disableBurn: true, + disableForceTransfer: true, + disableMint: true, + disablePartialFreeze: true, + disablePause: true, + disableRecovery: true, + }), + ) + .to.emit(token, 'AgentRestrictionsSet') + .withArgs(tokenAgent.address, true, true, true, true, true, true, true); + }); + }); + }); + }); + + describe('.getAgentRestrictions()', () => { + it('should return restrictions', async () => { + const { + suite: { token }, + accounts: { tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: true, + disableBurn: true, + disableForceTransfer: true, + disableMint: true, + disablePartialFreeze: true, + disablePause: true, + disableRecovery: true, + }); + + const restrictions = await token.getAgentRestrictions(tokenAgent.address); + expect(restrictions.disableAddressFreeze).to.be.true; + expect(restrictions.disableBurn).to.be.true; + expect(restrictions.disableForceTransfer).to.be.true; + expect(restrictions.disableMint).to.be.true; + expect(restrictions.disablePartialFreeze).to.be.true; + expect(restrictions.disablePause).to.be.true; + expect(restrictions.disableRecovery).to.be.true; + }); + }); +}); diff --git a/test/token/token-information.test.ts b/test/token/token-information.test.ts index c097bb9c..0de50cf7 100644 --- a/test/token/token-information.test.ts +++ b/test/token/token-information.test.ts @@ -21,7 +21,7 @@ describe('Token - Information', () => { const { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.setName('')).to.be.revertedWith('invalid argument - empty string'); + await expect(token.setName('')).to.be.revertedWithCustomError(token, 'EmptyString'); }); }); @@ -55,7 +55,7 @@ describe('Token - Information', () => { const { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.setSymbol('')).to.be.revertedWith('invalid argument - empty string'); + await expect(token.setSymbol('')).to.be.revertedWithCustomError(token, 'EmptyString'); }); }); @@ -79,7 +79,7 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).setOnchainID(ethers.constants.AddressZero)).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(token.connect(anotherWallet).setOnchainID(ethers.ZeroAddress)).to.be.revertedWith('Ownable: caller is not the owner'); }); }); @@ -88,11 +88,11 @@ describe('Token - Information', () => { const { suite: { token }, } = await loadFixture(deployFullSuiteFixture); - const tx = await token.setOnchainID(ethers.constants.AddressZero); + const tx = await token.setOnchainID(ethers.ZeroAddress); await expect(tx) .to.emit(token, 'UpdatedTokenInformation') - .withArgs(await token.name(), await token.symbol(), await token.decimals(), await token.version(), ethers.constants.AddressZero); - expect(await token.onchainID()).to.equal(ethers.constants.AddressZero); + .withArgs(await token.name(), await token.symbol(), await token.decimals(), await token.version(), ethers.ZeroAddress); + expect(await token.onchainID()).to.equal(ethers.ZeroAddress); }); }); }); @@ -104,9 +104,7 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).setIdentityRegistry(ethers.constants.AddressZero)).to.be.revertedWith( - 'Ownable: caller is not the owner', - ); + await expect(token.connect(anotherWallet).setIdentityRegistry(ethers.ZeroAddress)).to.be.revertedWith('Ownable: caller is not the owner'); }); }); }); @@ -118,7 +116,7 @@ describe('Token - Information', () => { accounts: { aliceWallet, bobWallet }, } = await loadFixture(deployFullSuiteFixture); - const balance = await token.balanceOf(aliceWallet.address).then(async (b) => b.add(await token.balanceOf(bobWallet.address))); + const balance = await token.balanceOf(aliceWallet.address).then(async (b) => b + (await token.balanceOf(bobWallet.address))); expect(await token.totalSupply()).to.equal(balance); }); }); @@ -130,7 +128,7 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).setCompliance(ethers.constants.AddressZero)).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(token.connect(anotherWallet).setCompliance(ethers.ZeroAddress)).to.be.revertedWith('Ownable: caller is not the owner'); }); }); }); @@ -140,8 +138,8 @@ describe('Token - Information', () => { const { suite: { token, compliance }, } = await loadFixture(deploySuiteWithModularCompliancesFixture); - await token.setCompliance(compliance.address); - expect(await token.compliance()).to.equal(compliance.address); + await token.setCompliance(compliance.target); + expect(await token.compliance()).to.equal(compliance.target); }); }); @@ -152,7 +150,28 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).pause()).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + await expect(token.connect(anotherWallet).pause()).to.be.revertedWithCustomError(token, 'CallerDoesNotHaveAgentRole'); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: false, + disablePause: true, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).pause()).to.be.revertedWithCustomError(token, 'AgentNotAuthorized'); }); }); @@ -176,7 +195,7 @@ describe('Token - Information', () => { accounts: { tokenAgent }, } = await loadFixture(deployFullSuiteFixture); await token.connect(tokenAgent).pause(); - await expect(token.connect(tokenAgent).pause()).to.be.revertedWith('Pausable: paused'); + await expect(token.connect(tokenAgent).pause()).to.be.revertedWithCustomError(token, 'EnforcedPause'); }); }); }); @@ -189,7 +208,29 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).unpause()).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + await expect(token.connect(anotherWallet).unpause()).to.be.revertedWithCustomError(token, 'CallerDoesNotHaveAgentRole'); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + await token.connect(tokenAgent).pause(); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: false, + disablePause: true, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).unpause()).to.be.revertedWithCustomError(token, 'AgentNotAuthorized'); }); }); @@ -213,7 +254,7 @@ describe('Token - Information', () => { suite: { token }, accounts: { tokenAgent }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).unpause()).to.be.revertedWith('Pausable: not paused'); + await expect(token.connect(tokenAgent).unpause()).to.be.revertedWithCustomError(token, 'ExpectedPause'); }); }); }); @@ -226,8 +267,9 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).setAddressFrozen(anotherWallet.address, true)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(token.connect(anotherWallet).setAddressFrozen(anotherWallet.address, true)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -240,8 +282,9 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).freezePartialTokens(anotherWallet.address, 1)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(token.connect(anotherWallet).freezePartialTokens(anotherWallet.address, 1)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -253,8 +296,9 @@ describe('Token - Information', () => { suite: { token }, accounts: { tokenAgent, anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).freezePartialTokens(anotherWallet.address, 1)).to.be.revertedWith( - 'Amount exceeds available balance', + await expect(token.connect(tokenAgent).freezePartialTokens(anotherWallet.address, 1)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', ); }); }); @@ -268,8 +312,9 @@ describe('Token - Information', () => { suite: { token }, accounts: { anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(anotherWallet).unfreezePartialTokens(anotherWallet.address, 1)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(token.connect(anotherWallet).unfreezePartialTokens(anotherWallet.address, 1)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', ); }); }); @@ -281,11 +326,77 @@ describe('Token - Information', () => { suite: { token }, accounts: { tokenAgent, anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).unfreezePartialTokens(anotherWallet.address, 1)).to.be.revertedWith( - 'Amount should be less than or equal to frozen tokens', + await expect(token.connect(tokenAgent).unfreezePartialTokens(anotherWallet.address, 1)).to.be.revertedWithCustomError( + token, + 'AmountAboveFrozenTokens', ); }); }); }); }); + describe('.supportsInterface()', () => { + it('should return false for unsupported interfaces', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + + const unsupportedInterfaceId = '0x12345678'; + expect(await token.supportsInterface(unsupportedInterfaceId)).to.equal(false); + }); + + it('should correctly identify the IERC20 interface ID', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc20InterfaceId = await interfaceIdCalculator.getIERC20InterfaceId(); + expect(await token.supportsInterface(ierc20InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IToken interface ID', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iTokenInterfaceId = await interfaceIdCalculator.getITokenInterfaceId(); + expect(await token.supportsInterface(iTokenInterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC3643 interface ID', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const iErc3643InterfaceId = await interfaceIdCalculator.getIERC3643InterfaceId(); + expect(await token.supportsInterface(iErc3643InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC173 interface ID', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc173InterfaceId = await interfaceIdCalculator.getIERC173InterfaceId(); + expect(await token.supportsInterface(ierc173InterfaceId)).to.equal(true); + }); + + it('should correctly identify the IERC165 interface ID', async () => { + const { + suite: { token }, + } = await loadFixture(deployFullSuiteFixture); + const InterfaceIdCalculator = await ethers.getContractFactory('InterfaceIdCalculator'); + const interfaceIdCalculator = await InterfaceIdCalculator.deploy(); + + const ierc165InterfaceId = await interfaceIdCalculator.getIERC165InterfaceId(); + expect(await token.supportsInterface(ierc165InterfaceId)).to.equal(true); + }); + }); }); diff --git a/test/token/token-recovery.test.ts b/test/token/token-recovery.test.ts index 94767d14..dc82b7eb 100644 --- a/test/token/token-recovery.test.ts +++ b/test/token/token-recovery.test.ts @@ -15,11 +15,39 @@ describe('Token - Recovery', () => { await bobIdentity .connect(bobWallet) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [anotherWallet.address])), 1, 1); + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [anotherWallet.address])), 1, 1); - await expect(token.connect(anotherWallet).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', - ); + await expect( + token.connect(anotherWallet).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target), + ).to.be.revertedWithCustomError(token, 'CallerDoesNotHaveAgentRole'); + }); + }); + + describe('when agent permission is restricted', () => { + it('should reverts', async () => { + const { + suite: { token }, + accounts: { bobWallet, anotherWallet, tokenAgent }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await bobIdentity + .connect(bobWallet) + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [anotherWallet.address])), 1, 1); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: false, + disablePause: false, + disableRecovery: true, + }); + + await expect( + token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target), + ).to.be.revertedWithCustomError(token, 'AgentNotAuthorized'); }); }); @@ -34,65 +62,151 @@ describe('Token - Recovery', () => { await token.connect(bobWallet).transfer(aliceWallet.address, await token.balanceOf(bobWallet.address)); - await expect(token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address)).to.be.revertedWith( - 'no tokens to recover', - ); + await expect( + token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target), + ).to.be.revertedWithCustomError(token, 'NoTokenToRecover'); }); }); + describe('when wallet has frozen token', () => { + it('should recover and freeze tokens on the new wallet', async () => { + const { + suite: { token }, + accounts: { tokenAgent, bobWallet, anotherWallet }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); - describe('when new wallet is not authorized on the identity', () => { + await bobIdentity + .connect(bobWallet) + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [anotherWallet.address])), 1, 1); + + await token.connect(tokenAgent).freezePartialTokens(bobWallet.address, 50); + + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); + await expect(token.getFrozenTokens(anotherWallet.address)).to.be.eventually.eq(50); + await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); + await expect(tx).to.emit(token, 'TokensFrozen').withArgs(anotherWallet.address, 50); + }); + }); + describe('when identity registry does not contain the lost or new wallet', () => { it('should revert', async () => { const { - suite: { token }, + suite: { token, identityRegistry }, accounts: { tokenAgent, bobWallet, anotherWallet }, identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address)).to.be.revertedWith( - 'Recovery not possible', - ); + await identityRegistry.connect(tokenAgent).deleteIdentity(bobWallet.address); + + await expect( + token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target), + ).to.be.revertedWithCustomError(token, 'RecoveryNotPossible'); }); }); - describe('when wallet is frozen', () => { - it('should recover and freeze the new wallet', async () => { + describe('when recovery is successful with identity transfer', () => { + it('should update the identity registry correctly', async () => { const { - suite: { token }, + suite: { token, identityRegistry }, accounts: { tokenAgent, bobWallet, anotherWallet }, identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await bobIdentity - .connect(bobWallet) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [anotherWallet.address])), 1, 1); + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); - await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); + await expect(identityRegistry.contains(bobWallet.address)).to.eventually.be.false; + await expect(identityRegistry.contains(anotherWallet.address)).to.eventually.be.true; + await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); + }); + }); + + describe('when the new wallet is already in the identity registry', () => { + it('should only remove the lost wallet from the registry', async () => { + const { + suite: { token, identityRegistry }, + accounts: { tokenAgent, bobWallet, anotherWallet }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(tokenAgent).registerIdentity(anotherWallet.address, bobIdentity.target, 1); + + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); - const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address); - await expect(token.isFrozen(anotherWallet.address)).to.be.eventually.true; - await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.address); - await expect(tx).to.emit(token, 'AddressFrozen').withArgs(anotherWallet.address, true, tokenAgent.address); + await expect(identityRegistry.contains(bobWallet.address)).to.eventually.be.false; + await expect(identityRegistry.contains(anotherWallet.address)).to.eventually.be.true; + await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); }); }); - describe('when wallet has frozen token', () => { - it('should recover and freeze tokens on the new wallet', async () => { + describe('when a recovery already happened on another token with same IRS', () => { + it('should recover without touching IRS', async () => { const { - suite: { token }, + suite: { token, identityRegistry }, accounts: { tokenAgent, bobWallet, anotherWallet }, identities: { bobIdentity }, } = await loadFixture(deployFullSuiteFixture); - await bobIdentity - .connect(bobWallet) - .addKey(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address'], [anotherWallet.address])), 1, 1); + await identityRegistry.connect(tokenAgent).deleteIdentity(bobWallet.address); + await identityRegistry.connect(tokenAgent).registerIdentity(anotherWallet.address, bobIdentity.target, 1); - await token.connect(tokenAgent).freezePartialTokens(bobWallet.address, 50); + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); - const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.address); - await expect(token.getFrozenTokens(anotherWallet.address)).to.be.eventually.eq(50); - await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.address); - await expect(tx).to.emit(token, 'TokensFrozen').withArgs(anotherWallet.address, 50); + await expect(identityRegistry.contains(bobWallet.address)).to.eventually.be.false; + await expect(identityRegistry.contains(anotherWallet.address)).to.eventually.be.true; + await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); + }); + }); + + describe('when the old wallet is frozen', () => { + describe('when the new wallet is not frozen', () => { + it('should transfer the frozen status and transfer frozen tokens', async () => { + const { + suite: { token }, + accounts: { tokenAgent, bobWallet, anotherWallet }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); + await token.connect(tokenAgent).freezePartialTokens(bobWallet.address, 50); + + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); + + await expect(token.isFrozen(anotherWallet.address)).to.eventually.be.true; + await expect(token.getFrozenTokens(anotherWallet.address)).to.eventually.eq(50); + await expect(tx).to.emit(token, 'TokensFrozen').withArgs(anotherWallet.address, 50); + }); + }); + describe('when the new wallet is already frozen', () => { + it('should transfer frozen tokens and keep freeze status', async () => { + const { + suite: { token }, + accounts: { tokenAgent, bobWallet, anotherWallet }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); + await token.connect(tokenAgent).setAddressFrozen(anotherWallet.address, true); + await token.connect(tokenAgent).freezePartialTokens(bobWallet.address, 30); + + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); + + await expect(token.isFrozen(anotherWallet.address)).to.eventually.be.true; + await expect(token.getFrozenTokens(anotherWallet.address)).to.eventually.eq(30); + await expect(tx).to.emit(token, 'TokensFrozen').withArgs(anotherWallet.address, 30); + }); + }); + }); + describe('when there are no frozen tokens', () => { + it('should recover tokens without freezing any', async () => { + const { + suite: { token }, + accounts: { tokenAgent, bobWallet, anotherWallet }, + identities: { bobIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + const tx = await token.connect(tokenAgent).recoveryAddress(bobWallet.address, anotherWallet.address, bobIdentity.target); + + await expect(token.getFrozenTokens(anotherWallet.address)).to.eventually.eq(0); + await expect(tx).to.emit(token, 'RecoverySuccess').withArgs(bobWallet.address, anotherWallet.address, bobIdentity.target); }); }); }); diff --git a/test/token/token-transfer.test.ts b/test/token/token-transfer.test.ts index 0b4d1490..f63afeea 100644 --- a/test/token/token-transfer.test.ts +++ b/test/token/token-transfer.test.ts @@ -60,7 +60,7 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).pause(); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWith('Pausable: paused'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWithCustomError(token, 'EnforcedPause'); }); }); @@ -73,7 +73,7 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWith('wallet is frozen'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWithCustomError(token, 'FrozenWallet'); }); }); @@ -86,7 +86,7 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).setAddressFrozen(aliceWallet.address, true); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWith('wallet is frozen'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWithCustomError(token, 'FrozenWallet'); }); }); @@ -99,7 +99,10 @@ describe('Token - Transfers', () => { const balance = await token.balanceOf(aliceWallet.address); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, balance.add(1000))).to.be.revertedWith('Insufficient Balance'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, balance + 1000n)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', + ); }); }); @@ -111,9 +114,12 @@ describe('Token - Transfers', () => { } = await loadFixture(deployFullSuiteFixture); const balance = await token.balanceOf(aliceWallet.address); - await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance.sub(100)); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance - 100n); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, balance)).to.be.revertedWith('Insufficient Balance'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, balance)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', + ); }); }); @@ -124,7 +130,7 @@ describe('Token - Transfers', () => { accounts: { aliceWallet, anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(aliceWallet).transfer(anotherWallet.address, 100)).to.be.revertedWith('Transfer not possible'); + await expect(token.connect(aliceWallet).transfer(anotherWallet.address, 100)).to.be.revertedWithCustomError(token, 'TransferNotPossible'); }); }); @@ -136,10 +142,10 @@ describe('Token - Transfers', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const complianceModuleA = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleA.address); - await token.setCompliance(compliance.address); + await compliance.addModule(complianceModuleA.target); + await token.setCompliance(compliance.target); - await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWith('Transfer not possible'); + await expect(token.connect(aliceWallet).transfer(bobWallet.address, 100)).to.be.revertedWithCustomError(token, 'TransferNotPossible'); }); }); @@ -179,7 +185,10 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).pause(); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWith('Pausable: paused'); + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'EnforcedPause', + ); }); }); @@ -192,7 +201,10 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).setAddressFrozen(aliceWallet.address, true); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWith('wallet is frozen'); + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'FrozenWallet', + ); }); }); @@ -205,7 +217,10 @@ describe('Token - Transfers', () => { await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWith('wallet is frozen'); + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'FrozenWallet', + ); }); }); @@ -218,8 +233,9 @@ describe('Token - Transfers', () => { const balance = await token.balanceOf(aliceWallet.address); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, balance.add(1000))).to.be.revertedWith( - 'Insufficient Balance', + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, balance + 1000n)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', ); }); }); @@ -232,10 +248,11 @@ describe('Token - Transfers', () => { } = await loadFixture(deployFullSuiteFixture); const balance = await token.balanceOf(aliceWallet.address); - await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance.sub(100)); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance - 100n); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, balance)).to.be.revertedWith( - 'Insufficient Balance', + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, balance)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', ); }); }); @@ -247,8 +264,9 @@ describe('Token - Transfers', () => { accounts: { aliceWallet, anotherWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, anotherWallet.address, 100)).to.be.revertedWith( - 'Transfer not possible', + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, anotherWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'TransferNotPossible', ); }); }); @@ -261,11 +279,12 @@ describe('Token - Transfers', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const complianceModuleA = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleA.address); - await token.setCompliance(compliance.address); + await compliance.addModule(complianceModuleA.target); + await token.setCompliance(compliance.target); - await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWith( - 'Transfer not possible', + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'TransferNotPossible', ); }); }); @@ -295,8 +314,33 @@ describe('Token - Transfers', () => { accounts: { aliceWallet, bobWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(aliceWallet).forcedTransfer(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWith( - 'AgentRole: caller does not have the Agent role', + await expect(token.connect(aliceWallet).forcedTransfer(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', + ); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: true, + disableMint: false, + disablePartialFreeze: false, + disablePause: false, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'AgentNotAuthorized', ); }); }); @@ -310,8 +354,9 @@ describe('Token - Transfers', () => { const balance = await token.balanceOf(aliceWallet.address); - await expect(token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, balance.add(1000))).to.be.revertedWith( - 'sender balance too low', + await expect(token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, balance + 1000n)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', ); }); }); @@ -323,8 +368,9 @@ describe('Token - Transfers', () => { accounts: { aliceWallet, anotherWallet, tokenAgent }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).forcedTransfer(aliceWallet.address, anotherWallet.address, 100)).to.be.revertedWith( - 'Transfer not possible', + await expect(token.connect(tokenAgent).forcedTransfer(aliceWallet.address, anotherWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'TransferNotPossible', ); }); }); @@ -337,8 +383,8 @@ describe('Token - Transfers', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const complianceModuleA = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleA.address); - await token.setCompliance(compliance.address); + await compliance.addModule(complianceModuleA.target); + await token.setCompliance(compliance.target); const tx = await token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, 100); await expect(tx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, bobWallet.address, 100); @@ -353,17 +399,21 @@ describe('Token - Transfers', () => { } = await loadFixture(deployFullSuiteFixture); const balance = await token.balanceOf(aliceWallet.address); - await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance.sub(100)); - - const tx = await token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, balance.sub(50)); - await expect(tx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, bobWallet.address, balance.sub(50)); - await expect(tx).to.emit(token, 'TokensUnfrozen').withArgs(aliceWallet.address, balance.sub(150)); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance - 100n); + + const tx = await token.connect(tokenAgent).forcedTransfer(aliceWallet.address, bobWallet.address, balance - 50n); + await expect(tx) + .to.emit(token, 'Transfer') + .withArgs(aliceWallet.address, bobWallet.address, balance - 50n); + await expect(tx) + .to.emit(token, 'TokensUnfrozen') + .withArgs(aliceWallet.address, balance - 150n); await expect(token.getFrozenTokens(aliceWallet.address)).to.be.eventually.equal(50); }); }); }); - describe('.mint', () => { + describe('.mint()', () => { describe('when sender is not an agent', () => { it('should revert', async () => { const { @@ -371,7 +421,28 @@ describe('Token - Transfers', () => { accounts: { aliceWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(aliceWallet).mint(aliceWallet.address, 100)).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + await expect(token.connect(aliceWallet).mint(aliceWallet.address, 100)).to.be.revertedWithCustomError(token, 'CallerDoesNotHaveAgentRole'); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: true, + disablePartialFreeze: false, + disablePause: false, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).mint(aliceWallet.address, 100)).to.be.revertedWithCustomError(token, 'AgentNotAuthorized'); }); }); @@ -382,7 +453,7 @@ describe('Token - Transfers', () => { accounts: { anotherWallet, tokenAgent }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(tokenAgent).mint(anotherWallet.address, 100)).to.be.revertedWith('Identity is not verified.'); + await expect(token.connect(tokenAgent).mint(anotherWallet.address, 100)).to.be.revertedWithCustomError(token, 'UnverifiedIdentity'); }); }); @@ -394,10 +465,10 @@ describe('Token - Transfers', () => { } = await loadFixture(deploySuiteWithModularCompliancesFixture); const complianceModuleA = await ethers.deployContract('CountryAllowModule'); - await compliance.addModule(complianceModuleA.address); - await token.setCompliance(compliance.address); + await compliance.addModule(complianceModuleA.target); + await token.setCompliance(compliance.target); - await expect(token.connect(tokenAgent).mint(aliceWallet.address, 100)).to.be.revertedWith('Compliance not followed'); + await expect(token.connect(tokenAgent).mint(aliceWallet.address, 100)).to.be.revertedWithCustomError(token, 'ComplianceNotFollowed'); }); }); }); @@ -410,7 +481,28 @@ describe('Token - Transfers', () => { accounts: { aliceWallet }, } = await loadFixture(deployFullSuiteFixture); - await expect(token.connect(aliceWallet).burn(aliceWallet.address, 100)).to.be.revertedWith('AgentRole: caller does not have the Agent role'); + await expect(token.connect(aliceWallet).burn(aliceWallet.address, 100)).to.be.revertedWithCustomError(token, 'CallerDoesNotHaveAgentRole'); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: true, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: false, + disablePause: false, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).burn(aliceWallet.address, 100)).to.be.revertedWithCustomError(token, 'AgentNotAuthorized'); }); }); @@ -423,7 +515,10 @@ describe('Token - Transfers', () => { const balance = await token.balanceOf(aliceWallet.address); - await expect(token.connect(tokenAgent).burn(aliceWallet.address, balance.add(1000))).to.be.revertedWith('cannot burn more than balance'); + await expect(token.connect(tokenAgent).burn(aliceWallet.address, balance + 1000n)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', + ); }); }); @@ -435,13 +530,270 @@ describe('Token - Transfers', () => { } = await loadFixture(deployFullSuiteFixture); const balance = await token.balanceOf(aliceWallet.address); - await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance.sub(100)); - - const tx = await token.connect(tokenAgent).burn(aliceWallet.address, balance.sub(50)); - await expect(tx).to.emit(token, 'Transfer').withArgs(aliceWallet.address, ethers.constants.AddressZero, balance.sub(50)); - await expect(tx).to.emit(token, 'TokensUnfrozen').withArgs(aliceWallet.address, balance.sub(150)); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, balance - 100n); + + const tx = await token.connect(tokenAgent).burn(aliceWallet.address, balance - 50n); + await expect(tx) + .to.emit(token, 'Transfer') + .withArgs(aliceWallet.address, ethers.ZeroAddress, balance - 50n); + await expect(tx) + .to.emit(token, 'TokensUnfrozen') + .withArgs(aliceWallet.address, balance - 150n); await expect(token.getFrozenTokens(aliceWallet.address)).to.be.eventually.equal(50); }); }); }); + + describe('.freezePartialTokens()', () => { + describe('when sender is not an agent', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(aliceWallet).freezePartialTokens(aliceWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', + ); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: true, + disablePause: false, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'AgentNotAuthorized', + ); + }); + }); + + describe('when freeze amount exceeds the balance', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, 999999999999)).to.be.revertedWithCustomError( + token, + 'ERC20InsufficientBalance', + ); + }); + }); + + describe('when freeze amount does not exceed the balance', () => { + it('should freeze', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, 100)) + .to.emit(token, 'TokensFrozen') + .withArgs(aliceWallet.address, 100); + }); + }); + }); + + describe('.unfreezePartialTokens()', () => { + describe('when sender is not an agent', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(aliceWallet).unfreezePartialTokens(aliceWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'CallerDoesNotHaveAgentRole', + ); + }); + }); + + describe('when agent permission is restricted', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.setAgentRestrictions(tokenAgent.address, { + disableAddressFreeze: false, + disableBurn: false, + disableForceTransfer: false, + disableMint: false, + disablePartialFreeze: true, + disablePause: false, + disableRecovery: false, + }); + + await expect(token.connect(tokenAgent).unfreezePartialTokens(aliceWallet.address, 100)).to.be.revertedWithCustomError( + token, + 'AgentNotAuthorized', + ); + }); + }); + + describe('when unfreeze amount exceeds the frozen amount', () => { + it('should revert', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(tokenAgent).unfreezePartialTokens(aliceWallet.address, 5000)).to.be.revertedWithCustomError( + token, + 'AmountAboveFrozenTokens', + ); + }); + }); + + describe('when freeze amount does not exceed the balance', () => { + it('should freeze', async () => { + const { + suite: { token }, + accounts: { aliceWallet, tokenAgent }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, 200); + await expect(token.connect(tokenAgent).unfreezePartialTokens(aliceWallet.address, 100)) + .to.emit(token, 'TokensUnfrozen') + .withArgs(aliceWallet.address, 100); + }); + }); + }); + + describe('.setAllowanceForAll()', () => { + it('should only allow the owner to set default allowances', async () => { + const { + suite: { token }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(aliceWallet).setAllowanceForAll(true, [bobWallet.address])).to.be.revertedWith('Ownable: caller is not the owner'); + + await expect(token.connect(deployer).setAllowanceForAll(true, [bobWallet.address])) + .to.emit(token, 'DefaultAllowance') + .withArgs(bobWallet.address, true); + }); + + it('should revert if default allowance is already set for a target', async () => { + const { + suite: { token }, + accounts: { deployer, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(deployer).setAllowanceForAll(true, [bobWallet.address]); + + await expect(token.connect(deployer).setAllowanceForAll(true, [bobWallet.address])) + .to.be.revertedWithCustomError(token, 'DefaultAllowanceAlreadySet') + .withArgs(bobWallet.address); + }); + + it('should allow transfer without explicit allowance for addresses with default allowance', async () => { + const { + suite: { token }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await expect(token.connect(aliceWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.reverted; + + await token.connect(deployer).setAllowanceForAll(true, [bobWallet.address]); + + await expect(token.connect(bobWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)) + .to.emit(token, 'Transfer') + .withArgs(aliceWallet.address, bobWallet.address, 100); + }); + + it('should allow users to opt out of default allowance', async () => { + const { + suite: { token }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(deployer).setAllowanceForAll(true, [bobWallet.address]); + + await expect(token.connect(bobWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)) + .to.emit(token, 'Transfer') + .withArgs(aliceWallet.address, bobWallet.address, 100); + + await expect(token.connect(aliceWallet).disableDefaultAllowance()).to.emit(token, 'DefaultAllowanceDisabled').withArgs(aliceWallet.address); + + await expect(token.connect(bobWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)).to.be.reverted; + + await expect(token.connect(aliceWallet).enableDefaultAllowance()).to.emit(token, 'DefaultAllowanceEnabled').withArgs(aliceWallet.address); + + await expect(token.connect(bobWallet).transferFrom(aliceWallet.address, bobWallet.address, 100)) + .to.emit(token, 'Transfer') + .withArgs(aliceWallet.address, bobWallet.address, 100); + }); + + it('should revert if a user tries to disable an already disabled default allowance', async () => { + const { + suite: { token }, + accounts: { aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(aliceWallet).disableDefaultAllowance(); + + await expect(token.connect(aliceWallet).disableDefaultAllowance()) + .to.be.revertedWithCustomError(token, 'DefaultAllowanceAlreadyDisabled') + .withArgs(aliceWallet.address); + }); + + it('should revert if a user tries to enable an already enabled default allowance', async () => { + const { + suite: { token }, + accounts: { aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(aliceWallet).disableDefaultAllowance(); + await token.connect(aliceWallet).enableDefaultAllowance(); + + await expect(token.connect(aliceWallet).enableDefaultAllowance()) + .to.be.revertedWithCustomError(token, 'DefaultAllowanceAlreadyEnabled') + .withArgs(aliceWallet.address); + }); + + it('should return max uint256 as allowance for addresses with default allowance when user has not opted out', async () => { + const { + suite: { token }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(deployer).setAllowanceForAll(true, [bobWallet.address]); + + const allowance = await token.allowance(aliceWallet.address, bobWallet.address); + expect(allowance).to.equal(ethers.MaxUint256); + }); + + it('should return actual allowance when user has opted out of default allowance', async () => { + const { + suite: { token }, + accounts: { deployer, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(deployer).setAllowanceForAll(true, [bobWallet.address]); + await token.connect(aliceWallet).disableDefaultAllowance(); + + const allowance = await token.allowance(aliceWallet.address, bobWallet.address); + expect(allowance).to.equal(0); + }); + }); }); diff --git a/test/utilities/compliance-check.test.ts b/test/utilities/compliance-check.test.ts new file mode 100644 index 00000000..144f81f8 --- /dev/null +++ b/test/utilities/compliance-check.test.ts @@ -0,0 +1,93 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { deployComplianceFixture } from '../fixtures/deploy-compliance.fixture'; + +async function deployComplianceWithCountryAllowModule() { + const context = await loadFixture(deployComplianceFixture); + const { compliance } = context.suite; + + const module = await ethers.deployContract('CountryAllowModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const countryAllowModule = await ethers.getContractAt('CountryAllowModule', proxy.target); + await compliance.addModule(countryAllowModule.target); + + const contract = await ethers.deployContract('MockContract'); + await compliance.bindToken(contract.target); + await contract.setCompliance(compliance.target); + + return { ...context, suite: { ...context.suite, countryAllowModule, mock: contract } }; +} + +describe('UtilityChecker.testTransferDetails', () => { + it('should return no pass for single module', async () => { + const context = await loadFixture(deployComplianceWithCountryAllowModule); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await utilityChecker.testTransferDetails( + context.suite.mock.target, + context.accounts.aliceWallet, + context.accounts.bobWallet, + 100, + ); + expect(results.length).to.equal(1); + expect(results[0][0]).to.equal('CountryAllowModule'); + expect(results[0][1]).to.equal(false); + }); + + it('should return no pass for multiple modules', async () => { + const context = await loadFixture(deployComplianceWithCountryAllowModule); + + const transferRestrictModule = await ethers.deployContract('TransferRestrictModule'); + await ethers.deployContract('ModuleProxy', [transferRestrictModule.target, transferRestrictModule.interface.encodeFunctionData('initialize')]); + await context.suite.compliance.addModule(transferRestrictModule.target); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await utilityChecker.testTransferDetails( + context.suite.mock.target, + context.accounts.aliceWallet, + context.accounts.bobWallet, + 100, + ); + expect(results.length).to.equal(2); + expect(results[0][0]).to.equal('CountryAllowModule'); + expect(results[0][1]).to.equal(false); + expect(results[1][0]).to.equal('TransferRestrictModule'); + expect(results[1][1]).to.equal(false); + }); + + it('should return pass for multiple modules', async () => { + const context = await loadFixture(deployComplianceWithCountryAllowModule); + + const transferRestrictModule = await ethers.deployContract('TransferRestrictModule'); + await ethers.deployContract('ModuleProxy', [transferRestrictModule.target, transferRestrictModule.interface.encodeFunctionData('initialize')]); + await context.suite.compliance.addModule(transferRestrictModule.target); + + await context.suite.compliance + .connect(context.accounts.deployer) + .callModuleFunction( + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + context.suite.countryAllowModule.target, + ); + await context.suite.mock.setInvestorCountry(42); + + await context.suite.compliance.callModuleFunction( + new ethers.Interface(['function allowUser(address _userAddress)']).encodeFunctionData('allowUser', [context.accounts.aliceWallet.address]), + transferRestrictModule.target, + ); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await utilityChecker.testTransferDetails( + context.suite.mock.target, + context.accounts.aliceWallet, + context.accounts.bobWallet, + 100, + ); + expect(results.length).to.equal(2); + expect(results[0][0]).to.equal('CountryAllowModule'); + expect(results[0][1]).to.equal(true); + expect(results[1][0]).to.equal('TransferRestrictModule'); + expect(results[1][1]).to.equal(true); + }); +}); diff --git a/test/utilities/eligibility-check.test.ts b/test/utilities/eligibility-check.test.ts new file mode 100644 index 00000000..f4c803c8 --- /dev/null +++ b/test/utilities/eligibility-check.test.ts @@ -0,0 +1,114 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { deployClaimIssuer, deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; + +async function addClaim(claimIssuerContract, claimIssuerSigningKey, claimTopic, wallet, identity) { + const claim = { + data: ethers.hexlify(ethers.toUtf8Bytes('Some claim public data 2.')), + issuer: claimIssuerContract.target, + topic: claimTopic, + scheme: 1, + identity: identity.target, + signature: '', + }; + claim.signature = await claimIssuerSigningKey.signMessage( + ethers.getBytes( + ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data])), + ), + ); + + await identity.connect(wallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, ''); +} + +describe('UtilityChecker.testVerifiedDetails', () => { + it('should return false when the identity is registered with topics', async () => { + const { + suite: { identityRegistry, token }, + accounts: { tokenAgent, charlieWallet }, + identities: { charlieIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity.target, 0); + + const eligibilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await eligibilityChecker.testVerifiedDetails(token.target, charlieWallet.address); + expect(results.length).to.be.equal(1); + const result = results[0]; + expect(result[0]).to.be.equal(ethers.ZeroAddress); + expect(result[1]).to.be.equal(0); + expect(result[2]).to.be.equal(false); + }); + + it('should return empty result when the identity is registered without topics', async () => { + const { + suite: { identityRegistry, claimTopicsRegistry, token }, + accounts: { tokenAgent, charlieWallet }, + identities: { charlieIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity.target, 0); + const topics = await claimTopicsRegistry.getClaimTopics(); + await Promise.all(topics.map((topic) => claimTopicsRegistry.removeClaimTopic(topic))); + + const eligibilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await eligibilityChecker.testVerifiedDetails(token.target, charlieWallet.address); + expect(results.length).to.be.equal(0); + }); + + it('should return true after fixture', async () => { + const { + suite: { token, claimTopicsRegistry, trustedIssuersRegistry }, + accounts: { aliceWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const eligibilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await eligibilityChecker.testVerifiedDetails(token.target, aliceWallet.address); + expect(results.length).to.be.equal(1); + + const topics = await claimTopicsRegistry.getClaimTopics(); + topics.forEach(async (topic, i) => { + const result = results[i]; + expect(result[0]).to.be.equal(await trustedIssuersRegistry.getTrustedIssuersForClaimTopic(topic)); + expect(result[1]).to.be.equal(topic); + expect(result[2]).to.be.equal(true); + }); + }); + + it('should return true for multiple issuers and topics', async () => { + const { + suite: { token, claimTopicsRegistry, trustedIssuersRegistry }, + accounts: { deployer, aliceWallet }, + identities: { aliceIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + const [, , , , , , , , , , claimIssuer] = await ethers.getSigners(); + const claimIssuerSigningKey = ethers.Wallet.createRandom(); + + const claimIssuerContract = await deployClaimIssuer(claimIssuer.address, claimIssuer); + await claimIssuerContract + .connect(claimIssuer) + .addKey(ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['address'], [claimIssuerSigningKey.address])), 3, 1); + + const claimTopics = [ethers.keccak256(ethers.toUtf8Bytes('CLAIM_TOPIC_2')), ethers.keccak256(ethers.toUtf8Bytes('CLAIM_TOPIC_3'))]; + await claimTopicsRegistry.connect(deployer).addClaimTopic(claimTopics[0]); + await claimTopicsRegistry.connect(deployer).addClaimTopic(claimTopics[1]); + await trustedIssuersRegistry.connect(deployer).addTrustedIssuer(claimIssuerContract.target, claimTopics); + + await addClaim(claimIssuerContract, claimIssuerSigningKey, claimTopics[0], aliceWallet, aliceIdentity); + await addClaim(claimIssuerContract, claimIssuerSigningKey, claimTopics[1], aliceWallet, aliceIdentity); + + const eligibilityChecker = await ethers.deployContract('UtilityChecker'); + const results = await eligibilityChecker.testVerifiedDetails(token.target, aliceWallet.address); + + expect(results.length).to.be.equal(3); + const topics = await claimTopicsRegistry.getClaimTopics(); + topics.forEach(async (topic, i) => { + const result = results[i]; + expect(result[0]).to.be.equal(await trustedIssuersRegistry.getTrustedIssuersForClaimTopic(topic)); + expect(result[1]).to.be.equal(topic); + expect(result[2]).to.be.equal(true); + }); + }); +}); diff --git a/test/utilities/freeze-check.test.ts b/test/utilities/freeze-check.test.ts new file mode 100644 index 00000000..cfffeb8c --- /dev/null +++ b/test/utilities/freeze-check.test.ts @@ -0,0 +1,72 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; + +describe('UtilityChecker.testFreeze', () => { + describe('When sender is frozen', () => { + it('should return true', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(tokenAgent).setAddressFrozen(aliceWallet.address, true); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const [success, balance] = await utilityChecker.testFreeze(token.target, aliceWallet.address, bobWallet.address, 100); + expect(success).to.be.equal(true); + expect(balance).to.be.equal(0); + }); + }); + + describe('When recipient is frozen', () => { + it('should return true', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const [success, balance] = await utilityChecker.testFreeze(token.target, aliceWallet.address, bobWallet.address, 100); + expect(success).to.be.equal(true); + expect(balance).to.be.equal(0); + }); + }); + + describe('When unfrozen balance is unsufficient', () => { + it('should return true', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const initialBalance = await token.balanceOf(aliceWallet.address); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, initialBalance - 10n); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const [success, balance] = await utilityChecker.testFreeze(token.target, aliceWallet.address, bobWallet.address, 100); + expect(success).to.be.equal(true); + expect(balance).to.be.equal(10); + }); + }); + + describe('When nominal case', () => { + it('should return false', async () => { + const { + suite: { token }, + accounts: { aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + const initialBalance = await token.balanceOf(aliceWallet.address); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const [success, balance] = await utilityChecker.testFreeze(token.target, aliceWallet.address, bobWallet.address, 100); + expect(success).to.be.equal(false); + expect(balance).to.be.equal(initialBalance); + }); + }); +}); diff --git a/test/utilities/transfer-check.test.ts b/test/utilities/transfer-check.test.ts new file mode 100644 index 00000000..bac2b655 --- /dev/null +++ b/test/utilities/transfer-check.test.ts @@ -0,0 +1,160 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers'; +import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture'; + +import { Token } from '../../typechain-types'; + +async function deployComplianceAndCountryAllowModule(token: Token, deployer: HardhatEthersSigner) { + const compliance = await ethers.deployContract('ModularCompliance'); + await compliance.init(); + await token.connect(deployer).setCompliance(compliance.target); + + const module = await ethers.deployContract('CountryAllowModule'); + const proxy = await ethers.deployContract('ModuleProxy', [module.target, module.interface.encodeFunctionData('initialize')]); + const countryAllowModule = await ethers.getContractAt('CountryAllowModule', proxy.target); + + await compliance.addModule(countryAllowModule.target); + await compliance.bindToken(token.target); + + await compliance + .connect(deployer) + .callModuleFunction( + new ethers.Interface(['function addAllowedCountry(uint16 country)']).encodeFunctionData('addAllowedCountry', [42]), + countryAllowModule.target, + ); +} + +describe('UtilityChecker.testTransfer', () => { + describe('When sender is frozen', () => { + it('should return false', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet, deployer }, + } = await loadFixture(deployFullSuiteFixture); + await deployComplianceAndCountryAllowModule(token, deployer); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + await token.connect(tokenAgent).setAddressFrozen(aliceWallet.address, true); + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(false); + expect(result[1]).to.be.equal(true); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('When recipient is frozen', () => { + it('should return false', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet, deployer }, + } = await loadFixture(deployFullSuiteFixture); + await deployComplianceAndCountryAllowModule(token, deployer); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + await token.connect(tokenAgent).setAddressFrozen(bobWallet.address, true); + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(false); + expect(result[1]).to.be.equal(true); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('When unfrozen balance is unsufficient', () => { + it('should return false', async () => { + const { + suite: { token }, + accounts: { tokenAgent, aliceWallet, bobWallet, deployer }, + } = await loadFixture(deployFullSuiteFixture); + await deployComplianceAndCountryAllowModule(token, deployer); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + const initialBalance = await token.balanceOf(aliceWallet.address); + await token.connect(tokenAgent).freezePartialTokens(aliceWallet.address, initialBalance - 10n); + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(false); + expect(result[1]).to.be.equal(true); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('When nominal case', () => { + it('should return true', async () => { + const { + suite: { token, identityRegistry }, + accounts: { deployer, tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await deployComplianceAndCountryAllowModule(token, deployer); + await identityRegistry.connect(tokenAgent).updateCountry(bobWallet.address, 42); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(true); + expect(result[1]).to.be.equal(true); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('When no identity is registered', () => { + it('should return false', async () => { + const { + suite: { token, identityRegistry }, + accounts: { deployer, tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + await deployComplianceAndCountryAllowModule(token, deployer); + await identityRegistry.connect(tokenAgent).deleteIdentity(bobWallet.address); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(true); + expect(result[1]).to.be.equal(false); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('When the identity is registered with topics', () => { + it('should return false', async () => { + const { + suite: { identityRegistry, token }, + accounts: { deployer, tokenAgent, aliceWallet, charlieWallet }, + identities: { charlieIdentity }, + } = await loadFixture(deployFullSuiteFixture); + + await identityRegistry.connect(tokenAgent).registerIdentity(charlieWallet.address, charlieIdentity.target, 0); + + await deployComplianceAndCountryAllowModule(token, deployer); + const utilityChecker = await ethers.deployContract('UtilityChecker'); + + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, charlieWallet.address, 100); + expect(result[0]).to.be.equal(true); + expect(result[1]).to.be.equal(false); + expect(result[2]).to.be.equal(true); + }); + }); + + describe('After fixture', () => { + it('should return true ', async () => { + const { + suite: { token, identityRegistry }, + accounts: { deployer, tokenAgent, aliceWallet, bobWallet }, + } = await loadFixture(deployFullSuiteFixture); + + await deployComplianceAndCountryAllowModule(token, deployer); + await identityRegistry.connect(tokenAgent).updateCountry(bobWallet.address, 42); + + const utilityChecker = await ethers.deployContract('UtilityChecker'); + const result = await utilityChecker.testTransfer(token.target, aliceWallet.address, bobWallet.address, 100); + expect(result[0]).to.be.equal(true); + expect(result[1]).to.be.equal(true); + expect(result[2]).to.be.equal(true); + }); + }); +}); diff --git a/test/utilities/upgrade.test.ts b/test/utilities/upgrade.test.ts new file mode 100644 index 00000000..8822d9d5 --- /dev/null +++ b/test/utilities/upgrade.test.ts @@ -0,0 +1,35 @@ +import { expect } from 'chai'; +import { ethers, upgrades } from 'hardhat'; + +describe('UtilityChecker.upgrateTo', () => { + describe('when calling directly', () => { + it('should revert', async () => { + const [, , , , , aliceWallet] = await ethers.getSigners(); + + const implementation = await ethers.deployContract('UtilityChecker'); + const proxy = await ethers.deployContract('UtilityCheckerProxy', [implementation.target, '0x8129fc1c']); + const utilityChecker = await ethers.getContractAt('UtilityChecker', proxy.target); + + const newImplementation = await ethers.deployContract('UtilityChecker'); + + await expect(utilityChecker.connect(aliceWallet).upgradeTo(newImplementation.target)).to.revertedWith('Ownable: caller is not the owner'); + }); + }); + + describe('when calling with owner account', () => { + it('should upgrade proxy', async () => { + const [deployer] = await ethers.getSigners(); + + const implementation = await ethers.deployContract('UtilityChecker'); + const proxy = await ethers.deployContract('UtilityCheckerProxy', [implementation.target, '0x8129fc1c']); + const utilityChecker = await ethers.getContractAt('UtilityChecker', proxy.target); + + const newImplementation = await ethers.deployContract('UtilityChecker'); + + await utilityChecker.connect(deployer).upgradeTo(newImplementation.target); + + const implementationAddress = await upgrades.erc1967.getImplementationAddress(utilityChecker.target as string); + expect(implementationAddress).to.eq(newImplementation.target); + }); + }); +});