diff --git a/README.md b/README.md index a4f9b07c0..88d31150e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Inverter Network Smart Contracts -*Inverter is the pioneering web3 protocol for token economies, enabling conditional token issuance, dynamic utility management, and token distribution. Build, customize, and innovate with Inverter's modular logic and extensive web3 interoperability.* + +_Inverter is the pioneering web3 protocol for token economies, enabling conditional token issuance, dynamic utility management, and token distribution. Build, customize, and innovate with Inverter's modular logic and extensive web3 interoperability._ ## Installation @@ -15,13 +16,14 @@ The Inverter Network smart contracts are developed using the [foundry toolchain] ## Usage Common tasks are executed through a `Makefile`. The most common commands are: -* `make build` to compile the project. -* `make test` to run the test suite. - * Note: _Some of our tests require a working Sepolia RPC URL, as we test certain contracts via fork testing. We implemented fallbacks for these particular test cases in the code directly, so they will work even without any RPC set in the environment. In the unlikely case that the tests do not work without RPC, please set a working one via `export SEPOLIA_RPC_URL=https://rpc-url-here`._ -* `make pre-commit` to ensure all of the development requirements are met, such as - * the Foundry Formatter has been run. - * the scripts are all working. - * the tests all run without any issues. + +- `make build` to compile the project. +- `make test` to run the test suite. + - Note: _Some of our tests require a working Sepolia RPC URL, as we test certain contracts via fork testing. We implemented fallbacks for these particular test cases in the code directly, so they will work even without any RPC set in the environment. In the unlikely case that the tests do not work without RPC, please set a working one via `export SEPOLIA_RPC_URL=https://rpc-url-here`._ +- `make pre-commit` to ensure all of the development requirements are met, such as + - the Foundry Formatter has been run. + - the scripts are all working. + - the tests all run without any issues. Additionally, the `Makefile` supports a help command, i.e. `make help`. @@ -35,18 +37,23 @@ $ make help ``` ## Documentation + The protocol is based on our [technical specification](https://docs.google.com/document/d/1j6WXBZzyYCOfO36ZYvKkgqrO2UAcy0kW5eJeZousn7E), which outlines its architecture and is the foundation of the implementation. Our documentation can be found [here](https://docs.inverter.network). ## Dependencies + - [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) - [OpenZeppelin Upgradeable-Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable) -- [UMAProtocol](https://github.com/UMAprotocol/protocol) (_for the [KPIRewarder Staking Module](./src/modules/logicModule/LM_PC_KPIRewarder_v2.sol)_) +- [UMAProtocol](https://github.com/UMAprotocol/protocol) (_for the [KPIRewarder Staking Module](./src/modules/logicModule/LM_PC_KPIRewarder_v3.sol)_) ## Contributing + You are considering to contribute to our protocol? Awesome - please refer to our [Contribution Guidelines](./CONTRIBUTING.md) to find our about the processes we established to ensure highest quality within our codebase. ## Security + Our [Security Policy](./SECURITY.md) provides details about our Security Guidelines, audits, and more. If you have discovered a potential security vulnerability within the Inverter Protocol, please report it to us by emailing [security@inverter.network](mailto:security@inverter.network). ------ +--- + _Disclaimer: This is experimental software and is provided on an "as is" and "as available" basis. We do not give any warranties and will not be liable for any loss incurred through any use of this codebase._ diff --git a/audits/2025-06-05-team-omega-authorizer-update.pdf b/audits/2025-06-05-team-omega-authorizer-update.pdf new file mode 100644 index 000000000..513520973 Binary files /dev/null and b/audits/2025-06-05-team-omega-authorizer-update.pdf differ diff --git a/docs/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.md b/docs/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.md index 0c4a85dfc..42dfb2793 100644 --- a/docs/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.md +++ b/docs/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.md @@ -5,7 +5,7 @@ This is a basic description of the bonding surface redeeming restricted repayer ## Basic Functionality of the underlying Bonding Surface Redeeming Contract Because the Bonding Surface Redeeming Restricted Repayer Seizable Contract is based on the Bonding Surface Redeeming Contract, it inherits the functionalities of this contract. These functionalities are described in the linked document: -[FM_BC_BondingSurface_Redeeming_v1.md](FM_BC_BondingSurface_Redeeming_v1.md) +[FM_BC_BondingSurface_Redeeming_v2.md](FM_BC_BondingSurface_Redeeming_v2.md) ## Added Functionalities @@ -49,7 +49,7 @@ For the Bonding Surface Redeeming Restricted Repayer Seizable Contract setup the As the Bonding Surface Redeeming Restricted Repayer Seizable Contract is based on the Bonding Surface Redeeming Contract, this contract needs the same basic setup as the Bonding Surface Redeeming Contract: -See [FM_BC_BondingSurface_Redeeming_v1.md](FM_BC_BondingSurface_Redeeming_v1.md) +See [FM_BC_BondingSurface_Redeeming_v2.md](FM_BC_BondingSurface_Redeeming_v2.md) The Setup differs in the following steps. diff --git a/docs/src/modules/authorizer/role/AUT_Roles_v2.md b/docs/src/modules/authorizer/role/AUT_Roles_v2.md new file mode 100644 index 000000000..49cdc84e2 --- /dev/null +++ b/docs/src/modules/authorizer/role/AUT_Roles_v2.md @@ -0,0 +1,261 @@ +# AUT_Roles_v2 + +## Purpose of Contract + +This contract provides the access control mechanism for managing roles and permissions across different modules within the Inverter Network, ensuring secure and controlled access to critical functionalities. + +This contract has the following key features: + +- **Role creation and management**: This includes the ability to create roles, revoke roles, assigning and revoking role admins, which can add and remove role members. +- **Role-based access control**: This includes the ability to grant roles access to functions that implement the permissioned modifier. Functions can also be set to public access by adding the public role to the function permissions. + +## Glossary + +To understand the functionalities of the following contract, it is important to be familiar with the following definitions. + +### Roles + +A role has the following properties: + +- A unique identifier (ID) +- A label +- A list of members +- A associated admin role + +The id is a value assigned by the authorizer module and is used to reference the role in the different functions of the authorizer module. + +The label is a string that is emitted as an event when the role is created. It is used to make the role human-readable in the frontend and has no practical use on-chain. + +The members are the addresses that inhabit the role. The admin role is the role that can add and remove new members to the role. + +### Native Roles + +There are three native roles that are created with the authorizer module: + +- The default admin role +- The public role +- The burn admin role + +The default admin role holds the highest admin privileges and can access every `permissioned` function at all times. The public role is a role that can be added to a function's permission list and is used to make functions be publicly accessible. The burn admin role is a placeholder role that indicates that the admin role of a role has been burned and is no longer usable. + +### Permissioned Modifier + +Most of the state altering functions in a workflow are so called `permissioned` functions. This means that only roles that have been granted the according function permission can call the function. The permissioned status is enforced by the `permissioned` modifier. + +Some of the native roles have special rights in this system. The default admin role can access every `permissioned` function regardless of wether the default admin role was granted the permission or not. If the public role is granted the permission to a function, then every caller can access the function, regardless of wether they inhabit a already added role or not. + +Note: As a workflow is intialized without any native permissions, some functions that could be perceived as "this should be publicly accessible" are not. Examples for this could be the "buy" and "sell" functions of some funding manager modules or the stake and unstake functions of the staking logic module. For these functions, the public role has to be added to the access of the respective function. + +## Inheritance + +### Class Diagramm + +```mermaid +classDiagram + + Module <|-- AUT_Roles_v2 + AccessControlEnumerableUpgradeable <|-- AUT_Roles_v2 + + note for Module "Base contract for every module implementation" + class Module{ + -IOrchestrator_v2 __Module_orchestrator + + permissioned + } + + note for AccessControlEnumerableUpgradeable "OpenZeppelin Authorization System" + class AccessControlEnumerableUpgradeable { + - mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers + + getRoleMember() + + hasRole() + + getRoleAdmin() + + grantRole() + + revokeRole() + } + + class AUT_Roles_v2{ + - mapping(address target => mapping(bytes4 selector => bytes32[] roleIds)) _permissions; + + getPermissions() + + isRolePermissioned() + + hasPermission() + + createRole() + + labelRole() + + transferAdminRole() + + burnRoleAdmin() + + addAccessPermission() + + removeAccessPermission() + + createRoleAndAddAccessPermissions() + } + +``` + +### Base Contracts + +This contract is based on the following contracts and inherits their functionalities: + +- [IAuthorizer_v2](../IAuthorizer_v2.md): Implementation interface. +- [Module_v2](../../base/Module_v2.md): Inverter network base module functionality. +- [AccessControlEnumerableUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/extensions/AccessControlEnumerableUpgradeable.sol): Access control functionality. + +Functions that have been overridden to adapt functionalities are outlined below. + +### Key Changes to the Base Contracts + +_The purpose of this section is to highlight which functions of the base contract have been overridden and why._ + +- `grantRole`: This function has been overridden to allow only the distribution of roles that have already been created by the authorizer module. + +## Key Functionalities + +In this section the key functionalities of the authorizer module are described. + +### Role Management + +This section describes the functionalities of the authorizer module regarding role management. + +#### Role Creation + +A role can be created by calling the `createRole` function. The function takes the following parameters: + +- The role name +- The role id of the role that will become the admin of the new role. +- The addresses of the initial members of the new role. + +The rolename in this context refers to the label of the role and is therefore not referenceable onchain. The function can only be called by a permissioned address (See [permissioned](#permissioned-modifier)). + +#### Role Labeling + +The label of a role can be overwritten by calling the `labelRole` function. With this a new event is emitted, that signals the frontend that the label has been updated. The function can only be called by a permissioned address.(See [permissioned](#permissioned-modifier)). + +#### Role granting + +A role can be granted to a address by calling the `grantRole` function. The function takes the following parameters: + +- The role id of the role to grant +- The address to grant the role to + +The `grantRole` function can only be called by according admin of the role. + +#### Role revoking + +A role can be revoked from an address by calling the `revokeRole` function. The function takes the following parameters: + +- The role id of the role to revoke +- The address to revoke the role from + +The `revokeRole` function can only be called by according admin of the role. + +#### Transferal of Admin Roles + +The admin role of a role can be transferred by calling the `transferAdminRole` function. The function takes the following parameters: + +- The role id of the role to transfer the admin from +- The role id of the role to transfer the admin to + +The `transferAdminRole` function can only be called by according admin of the role. + +#### Burning of Admin Roles: + +The admin role can be burned by calling the `burnRoleAdmin` function. The function takes the following parameters: + +- The role id of the role to burn the admin from + +If the admin role is burned, then no members can be added or removed from a role anymore. Remember: This step is irreversible. + +### Role Based Access Control + +This section describes the functionalities of the authorizer module regarding role based access control. + +#### Adding access permissions + +Adding access permissions is done by calling the `addAccessPermission` function. This function takes the following parameters: + +- The contract for which the permission is added +- The function selector of the target function +- The role ID of the role that will receive the permission + +Example: Adding the role "BOUNTY_MANAGER" to the `createBounty` function of the "bountyManager" contract would look like this: + +```solidity +authorizer.addAccessPermission( + address(bountyManager), + bountyManager.createBounty.selector, + bountyManagerId +); +``` + +The function can only be called by a permissioned address.(See [permissioned](#permissioned-modifier)). + +#### Making a function public + +A function can be made public by calling the `addAccessPermission` function with the public role as target role. + +Example: Making the `buy` function of the funding manager contract public would look like this: + +```solidity +authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() +);` +``` + +The public role can be removed in the same way as any other role. + +### Mixed Utility - `createRoleAndAddAccessPermissions()` + +This function is a convenience function that combines the creation of a new role and the adding of access permissions. It takes the following parameters: + +- The name of the role +- The role id of the role that will become the admin of the new role. +- The addresses of the initial members of the new role. +- The addresses of the targets contracts. +- The selectors of the functions. + +Note: The selectors of the functions are linked to the respective target contracts. As the selectors are passedas a 2 Dimensional array, the first dimension is coupled to target contract and the second one contains the actual selectors for that target contract. + +Example: The target contracts are the fundingManager at position 0 in the array and the logic module at position 1. The selectors therefore contain two arrays, one for position 0 and one for position 1. A call would look like this: + +```solidity +'authorizer.createRoleAndAddAccessPermissions( + "newRole", + authorizer.DEFAULT_ADMIN_ROLE(), + [ + initialMember1, + initialMember2 + ], + [ + fundingManager.address, + logicModule.address + ], + [ + [ + fundingManager.buy.selector, + fundingManager.sell.selector + ], + [ + logicModule.execute.selector + ] + ] +) +``` + +The function can only be called by a permissioned address.(See [permissioned](#permissioned-modifier)). + +## Deployment + +### Deployment Parameters + +The list of deployment parameters can be found in the _Technical Reference_ section of the documentation under the `init()` function ([https://docs.inverter.network/contracts/technical-reference/modules/authorizer/role/aut_roles_v2.sol]()). + +### Deployment + +Deployment should be done using one of the methods provided below: + +- **Manual deployment:** Through Inverter Network's [Control Room application](https://beta.controlroom.inverter.network/). +- **SDK deployment:** Through Inverter Network's [TypeScript SDK](https://docs.inverter.network/sdk/typescript-sdk/guides/deploy-a-workflow) or [React SDK](https://docs.inverter.network/sdk/react-sdk/guides/deploy-a-workflow). + +### Setup Steps + +#### Optional Setup Steps + +Because a workflow and its authorizer module are deployed without any native permissions (except the initial admin role [here](#native-roles)), it might be necessary for some modules to modify the access of their functions. For this the sections [Mixed Utility](#mixed-utility---createroleandaddaccesspermissions), [Role Management](#role-management) and [Role Based Access Control](#role-based-access-control) can be referenced. diff --git a/docs/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.md b/docs/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.md new file mode 100644 index 000000000..59d30b289 --- /dev/null +++ b/docs/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.md @@ -0,0 +1,221 @@ +# AUT_TokenGated_Roles_v2 + +## Purpose of Contract + +This contract extends the Inverter's role-based access control to include token gating, enabling roles to be conditionally assigned based on token ownership. This mechanism allows for dynamic permissioning tied to specific token holdings. + +This contract has the following key features: + +- Token-based access checks before role assignment. +- Supports both {ERC20} and {ERC721} tokens. + +## Glossary + +To understand the functionalities of the following contract, it is important to be familiar with the following definitions. + +### Token Gated Role + +With this contract it is possible to extend the base functionality of the [AUT_Roles_v2](./AUT_Roles_v2.md) contract to make a role token gated. A token gated role behaves in all respects like a regular role, but handles the membership of that role differently. A member of a token gated role is only allowed to access the role functionalities if they hold a certain amount of a token. + +The implementation of this contract uses a few tricks to achieve this. Without going into too much detail, this is the main part that is needed to understand the basic mechanism: + +In the contract, token gating is implemented by storing the token address in the role’s members property, instead of directly listing user addresses. This setup allows the contract to check the token balance of a user against a defined threshold when access is requested. + +Example: We want to restrict a role to users who hold a certain amount of Token A. Therefore, we configure the role to be token-gated and set a required token amount the user needs to hold as threshold. When we then call `grantRole` with the address of Token A, the role becomes accessible only to users whose wallet holds at least the specified amount of that token. + +## Inheritance + +### Class Diagramm + +```mermaid +classDiagram + + note for AUT_Roles_v2 "OpenZeppelin Authorization System" + AUT_Roles_v2 <|-- AUT_TokenGated_Roles_v2 + + + + + class AUT_Roles_v2{ + - mapping(address target => mapping(bytes4 selector => bytes32[] roleIds)) _permissions; + + getPermissions() + + isRolePermissioned() + + hasPermission() + + createRole() + + labelRole() + + transferAdminRole() + + burnRoleAdmin() + + addAccessPermission() + + removeAccessPermission() + + createRoleAndAddAccessPermissions() + } + + class AUT_TokenGated_Roles_v2{ + - mapping(bytes32 => bool) _isTokenGated + - mapping(bytes32 => uint) _thresholdMap + + isTokenGated() + + hasTokenRole() + + getThresholdValue() + + setTokenGated() + + setThreshold() + } + +``` + +### Base Contracts + +This contract is based on the following contracts and inherits their functionalities: + +- [IAUT_TokenGated_Roles_v2](./interfaces/IAUT_TokenGated_Roles_v2.md): Implementation interface. +- [Module_v2](../../base/Module_v2.md): Inverter network base module functionality. +- [AccessControlEnumerableUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/extensions/AccessControlEnumerableUpgradeable.sol): Access control functionality. +- [AUT_Roles_v2](./AUT_Roles_v2.md): Base contract for the role-based access control. + +Functions that have been overridden to adapt functionalities are outlined below. + +### Key Changes to the Base Contracts + +_The purpose of this section is to highlight which functions of the base contract have been overridden and why._ + +- `hasRole`: This function has been overridden to allow token gating. +- `grantRole`: This function has been overridden to allow token gating. +- `revokeRole`: This function has been overridden to allow token gating. + +## Key Functionalities + +In this section the key functionalities of the authorizer module are described. + +### Token based Access Control + +This section describes the functionalities of the authorizer module regarding token based access control. + +#### Making a role token gated + +Making a role token gated is done by calling the `setTokenGated` function. This function takes the following parameters: + +- The role id of the role that we change the token gated status of. +- The boolean that indicates if the role should be token gated or not. + +When calling this function, the role can not contain any members, when it is switched to and from token gated. + +Example: Making the role "Whitelisted" token gated would look like this: + +```solidity +authorizer.setTokenGated( + whitelistedRoleId, + true +); +``` + +The function can only be called by a permissioned address (See [permissioned](#permissioned-modifier)). + +#### Setting the token threshold + +Setting the token threshold needed to pass the token gate is done by calling the `setTokenThreshold` function. This function takes the following parameters: + +- The role id of the role to set the threshold for. +- The address of the token to set the threshold for. +- The threshold value to set. + +Example: Setting the threshold for the token "USDC" to 100 would look like this: + +```solidity +authorizer.setTokenThreshold( + whitelistedRoleId, + USDC, + 100 +); +``` + +The function can only be called by a permissioned address (See [permissioned](#permissioned-modifier)). + +#### Adding a token to the token gate + +Adding a token to the token gate is done by calling the `grantRole` function. This function takes the following parameters: + +- The role id of the role to grant. +- The address of the token to grant the role to. + +The grantRole function can only be called by according admin of the role. +Example: Adding a token gate to the token gated role "Whitelisted" would look like this: + +```solidity +authorizer.grantRole( + whitelistedRoleId, + address(USDC) +); +``` + +#### Removing a token from the token gate + +Remove a token from the token gate is done by calling the `revokeRole` function. This function behaves like the regular revokeRole function, except that it sets the threshold for the role and token combination to 0 as well. +Example: Removing the token from the role "Whitelisted" would look like this: + +```solidity +authorizer.revokeRole( + whitelistedRoleId, + address(USDC) +); +``` + +#### Reversing a token gate + +In case the token gated status of a role needs to be reverted, the `setTokenGated` function can be used. The same restrictions as for the `setTokenGated` function apply here as well (see above). + +Example: Reversing the token gated status of the role "Whitelisted" would look like this: + +```solidity +authorizer.setTokenGated( + whitelistedRoleId, + false +); +``` + +### Full process of making a role token gated + +To make a role token gated, the following steps need to be taken in order: + +```mermaid +flowchart LR + + 1{ + Make role + token gated + } + 2{ + Set token + threshold + } + 3{ + Add token to the + token gate + } + + 1 --> 2 + 2 --> 3 +``` + +The first step is to make the role token gated. This is done by calling the `setTokenGated` function (see [here](#making-a-role-token-gated)). Remember that this function can only be called by a permissioned address and can not contain any members, when it is switched to and from token gated. + +The second step is to set the threshold for the role and token combination. This is done by calling the `setTokenThreshold` function (see [here](#setting-the-token-threshold)). This function is also permissioned. + +The third step is to actually add the token to the token gate. This is done by calling the `grantRole` function (see [here](#adding-a-token-to-the-token-gate)). This function can only be called by according admin of the role. + +## Deployment + +### Deployment Parameters + +The list of deployment parameters can be found in the _Technical Reference_ section of the documentation under the `init()` function ([https://docs.inverter.network/contracts/technical-reference/modules/authorizer/role/AUT_TokenGated_Roles_v2.sol]()). + +### Deployment + +Deployment should be done using one of the methods provided below: + +- **Manual deployment:** Through Inverter Network's [Control Room application](https://beta.controlroom.inverter.network/). +- **SDK deployment:** Through Inverter Network's [TypeScript SDK](https://docs.inverter.network/sdk/typescript-sdk/guides/deploy-a-workflow) or [React SDK](https://docs.inverter.network/sdk/react-sdk/guides/deploy-a-workflow). + +### Setup Steps + +#### Optional Setup Steps + +Because a workflow and its authorizer module are deployed without any native permissions (except the initial admin role [here](#native-roles)), it might be necessary for some modules to modify the access of their functions. For this look up the sections `Mixed Utility`,`Role Management` and `Role Based Access Control` from the [AUT_Roles_v2](./AUT_Roles_v2.md) as well as the section [Full process of making a role token gated](#full-process-of-making-a-role-token-gated). diff --git a/remappings.txt b/remappings.txt index 8407e5472..9d4bf4949 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,6 +3,7 @@ @df/=lib/deterministic-factory/src/ @uma/=lib/uma-protocol/packages/core/contracts/ @ex/=src/external/ +@aut/=src/modules/authorizer/ @fm/=src/modules/fundingManager/ @pp/=src/modules/paymentProcessor/ @lm/=src/modules/logicModule/ diff --git a/script/deploymentScript/DeploymentScript.s.sol b/script/deploymentScript/DeploymentScript.s.sol index 51c479308..34598931a 100644 --- a/script/deploymentScript/DeploymentScript.s.sol +++ b/script/deploymentScript/DeploymentScript.s.sol @@ -22,8 +22,8 @@ import { InverterBeacon_v1, IInverterBeacon_v1 } from "src/proxies/InverterBeacon_v1.sol"; -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; import {Ownable} from "@oz/access/Ownable.sol"; import {EIP712} from "@oz/utils/cryptography/EIP712.sol"; @@ -395,7 +395,7 @@ contract DeploymentScript is ModuleBeaconDeployer_v1 { // Verify that the TransactionForwarder is linked correctly in the Orchestrator require( - Orchestrator_v1(orchestratorBeacon.getImplementationAddress()) + Orchestrator_v2(orchestratorBeacon.getImplementationAddress()) .trustedForwarder() == forwarder, "Deployment failed - Orchestrator Beacon not initialized correctly, Forwarder is not correct." ); @@ -463,7 +463,7 @@ contract DeploymentScript is ModuleBeaconDeployer_v1 { (IInverterBeacon_v1 testBeacon,) = ModuleFactory_v1(moduleFactory) .getBeaconAndId(initialMetadataRegistration[0]); - IModule_v1.Metadata memory testMetadata = IModule_v1.Metadata( + IModule_v2.Metadata memory testMetadata = IModule_v2.Metadata( type(uint).max, type(uint).max, type(uint).max, diff --git a/script/deploymentScript/TestnetDeploymentScript.s.sol b/script/deploymentScript/TestnetDeploymentScript.s.sol index c0bb91cd7..0299ba6fe 100644 --- a/script/deploymentScript/TestnetDeploymentScript.s.sol +++ b/script/deploymentScript/TestnetDeploymentScript.s.sol @@ -13,7 +13,7 @@ import {Testnet_ModuleFactory_v1} from // Interfaces import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; // Mocks @@ -118,7 +118,7 @@ contract TestnetDeploymentScript is DeploymentScript { moduleFactory ).getBeaconAndId(initialMetadataRegistration[0]); - IModule_v1.Metadata memory testMetadata = IModule_v1.Metadata( + IModule_v2.Metadata memory testMetadata = IModule_v2.Metadata( type(uint).max, type(uint).max, type(uint).max, diff --git a/script/deploymentSuite/MetadataCollection_v1.s.sol b/script/deploymentSuite/MetadataCollection_v1.s.sol index 3a2b230df..20fe028fb 100644 --- a/script/deploymentSuite/MetadataCollection_v1.s.sol +++ b/script/deploymentSuite/MetadataCollection_v1.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; // Interfaces -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; /** * @title Inverter Metadata Collection @@ -17,12 +17,12 @@ contract MetadataCollection_v1 { // External Contracts // Governor - IModule_v1.Metadata public governorMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public governorMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/InverterNetwork/contracts", "Governor_v1" ); // TransactionForwarder - IModule_v1.Metadata public forwarderMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public forwarderMetadata = IModule_v2.Metadata( 1, 0, 0, @@ -31,7 +31,7 @@ contract MetadataCollection_v1 { ); // FeeManager - IModule_v1.Metadata public feeManagerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public feeManagerMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/InverterNetwork/contracts", "FeeManager_v1" ); @@ -39,7 +39,7 @@ contract MetadataCollection_v1 { // Factories // ModuleFactory - IModule_v1.Metadata public moduleFactoryMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public moduleFactoryMetadata = IModule_v2.Metadata( 1, 0, 0, @@ -48,7 +48,7 @@ contract MetadataCollection_v1 { ); // OrchestratorFactory - IModule_v1.Metadata public orchestratorFactoryMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public orchestratorFactoryMetadata = IModule_v2.Metadata( 1, 0, 0, @@ -60,88 +60,77 @@ contract MetadataCollection_v1 { // Orchestrator // Orchestrator - IModule_v1.Metadata public orchestratorMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public orchestratorMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "Orchestrator_v1" + "Orchestrator_v2" ); // ------------------------------------------------------------------------ // Authorizer // RoleAuthorizer - IModule_v1.Metadata public roleAuthorizerMetadata = IModule_v1.Metadata( - 1, 0, 0, "https://github.com/InverterNetwork/contracts", "AUT_Roles_v1" + IModule_v2.Metadata public roleAuthorizerMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", "AUT_Roles_v2" ); // TokenGatedRoleAuthorizer - IModule_v1.Metadata public tokenGatedRoleAuthorizerMetadata = IModule_v1 + IModule_v2.Metadata public tokenGatedRoleAuthorizerMetadata = IModule_v2 .Metadata( - 1, + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "AUT_TokenGated_Roles_v1" + "AUT_TokenGated_Roles_v2" ); // VotingRoles - IModule_v1.Metadata public votingRolesMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public votingRolesMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "AUT_EXT_VotingRoles_v1" + "AUT_EXT_VotingRoles_v2" ); // ------------------------------------------------------------------------ // Funding Manager // BancorRedeemingVirtualSupplyFundingManager - IModule_v1.Metadata public - bancorRedeemingVirtualSupplyFundingManagerMetadata = IModule_v1.Metadata( - 1, - 0, - 0, - "https://github.com/InverterNetwork/contracts", - "FM_BC_Bancor_Redeeming_VirtualSupply_v1" - ); - - // RestrictedBancorRedeemingVirtualSupplyFundingManager - IModule_v1.Metadata public - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata = - IModule_v1.Metadata( - 1, + IModule_v2.Metadata public + bancorRedeemingVirtualSupplyFundingManagerMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1" + "FM_BC_Bancor_Redeeming_VirtualSupply_v2" ); // BondingSurfaceRedeemingFundingManager - IModule_v1.Metadata public bondingSurfaceRedeemingFundingManagerMetadata = - IModule_v1.Metadata( - 1, + IModule_v2.Metadata public bondingSurfaceRedeemingFundingManagerMetadata = + IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "FM_BC_BondingSurface_Redeeming_v1" + "FM_BC_BondingSurface_Redeeming_v2" ); // BondingSurfaceRedeemingRestrictedRepayerSeizableFundingManager - IModule_v1.Metadata public + IModule_v2.Metadata public bondingSurfaceRedeemingRestrictedRepayerSeizableFundingManagerMetadata = - IModule_v1.Metadata( - 1, + IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1" + "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2" ); // DepositVaultFundingManager - IModule_v1.Metadata public depositVaultFundingManagerMetadata = IModule_v1 + IModule_v2.Metadata public depositVaultFundingManagerMetadata = IModule_v2 .Metadata( 1, 0, @@ -151,115 +140,115 @@ contract MetadataCollection_v1 { ); // OracleRedeemingFundingManager - IModule_v1.Metadata public oracleRedeemingFundingManagerMetadata = - IModule_v1.Metadata( - 1, + IModule_v2.Metadata public oracleRedeemingFundingManagerMetadata = + IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "FM_PC_Oracle_Redeeming_v1" + "FM_PC_Oracle_Redeeming_v2" ); // Funding Manager - Extensions - // FM_EXT_TokenVault_v1 - IModule_v1.Metadata public tokenVaultMetadata = IModule_v1.Metadata( - 1, + // FM_EXT_TokenVault_v2 + IModule_v2.Metadata public tokenVaultMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "FM_EXT_TokenVault_v1" + "FM_EXT_TokenVault_v2" ); // ------------------------------------------------------------------------ // Logic Module // Oracle_Permissioned - IModule_v1.Metadata public oraclePermissionedMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public oraclePermissionedMetadata = IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_Oracle_Permissioned_v1" + "LM_Oracle_Permissioned_v2" ); // Bounties - IModule_v1.Metadata public bountiesMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public bountiesMetadata = IModule_v2.Metadata( + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_Bounties_v2" + "LM_PC_Bounties_v3" ); // KPIRewarder - IModule_v1.Metadata public kpiRewarderMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public kpiRewarderMetadata = IModule_v2.Metadata( + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_KPIRewarder_v2" + "LM_PC_KPIRewarder_v3" ); // PaymentRouter - IModule_v1.Metadata public paymentRouterMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public paymentRouterMetadata = IModule_v2.Metadata( + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_PaymentRouter_v2" + "LM_PC_PaymentRouter_v3" ); // RecurringPayments - IModule_v1.Metadata public recurringPaymentsMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public recurringPaymentsMetadata = IModule_v2.Metadata( + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_RecurringPayments_v2" + "LM_PC_RecurringPayments_v3" ); // Staking - IModule_v1.Metadata public stakingMetadata = IModule_v1.Metadata( - 1, + IModule_v2.Metadata public stakingMetadata = IModule_v2.Metadata( + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_Staking_v2" + "LM_PC_Staking_v3" ); // ------------------------------------------------------------------------ // Payment Processor // QueueManualExecutionPaymentProcessor - IModule_v1.Metadata public queueManualExecutionPaymentProcessorMetadata = - IModule_v1.Metadata( - 1, + IModule_v2.Metadata public queueManualExecutionPaymentProcessorMetadata = + IModule_v2.Metadata( + 2, 0, 0, "https://github.com/InverterNetwork/contracts", - "PP_Queue_ManualExecution_v1" + "PP_Queue_ManualExecution_v2" ); // QueuePaymentProcessor - IModule_v1.Metadata public queuePaymentProcessorMetadata = IModule_v1 + IModule_v2.Metadata public queuePaymentProcessorMetadata = IModule_v2 .Metadata( - 1, 0, 0, "https://github.com/InverterNetwork/contracts", "PP_Queue_v1" + 2, 0, 0, "https://github.com/InverterNetwork/contracts", "PP_Queue_v2" ); // SimplePaymentProcessor - IModule_v1.Metadata public simplePaymentProcessorMetadata = IModule_v1 + IModule_v2.Metadata public simplePaymentProcessorMetadata = IModule_v2 .Metadata( - 1, 0, 0, "https://github.com/InverterNetwork/contracts", "PP_Simple_v2" + 3, 0, 0, "https://github.com/InverterNetwork/contracts", "PP_Simple_v3" ); // StreamingPaymentProcessor - IModule_v1.Metadata public streamingPaymentProcessorMetadata = IModule_v1 + IModule_v2.Metadata public streamingPaymentProcessorMetadata = IModule_v2 .Metadata( - 1, + 3, 0, 0, "https://github.com/InverterNetwork/contracts", - "PP_Streaming_v2" + "PP_Streaming_v3" ); } diff --git a/script/deploymentSuite/ModuleBeaconDeployer_v1.s.sol b/script/deploymentSuite/ModuleBeaconDeployer_v1.s.sol index bd5fdc461..7e05be2eb 100644 --- a/script/deploymentSuite/ModuleBeaconDeployer_v1.s.sol +++ b/script/deploymentSuite/ModuleBeaconDeployer_v1.s.sol @@ -13,7 +13,7 @@ import {ProxyAndBeaconDeployer_v1} from // Interfaces import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; /** * @title Inverter Module Beacon Deployer Script @@ -30,7 +30,7 @@ contract ModuleBeaconDeployer_v1 is new ProxyAndBeaconDeployer_v1(); // ModuleFactory Registration Data - IModule_v1.Metadata[] initialMetadataRegistration; + IModule_v2.Metadata[] initialMetadataRegistration; IInverterBeacon_v1[] initialBeaconRegistration; // Orchestrator Beacon @@ -51,7 +51,7 @@ contract ModuleBeaconDeployer_v1 is orchestratorMetadata.title, reverter, governor, - impl_orc_Orchestrator_v1, + impl_orc_Orchestrator_v2, orchestratorMetadata.majorVersion, orchestratorMetadata.minorVersion, orchestratorMetadata.patchVersion @@ -69,7 +69,7 @@ contract ModuleBeaconDeployer_v1 is roleAuthorizerMetadata.title, reverter, governor, - impl_mod_Aut_Roles_v1, + impl_mod_AUT_Roles_v2, roleAuthorizerMetadata.majorVersion, roleAuthorizerMetadata.minorVersion, roleAuthorizerMetadata.patchVersion @@ -85,7 +85,7 @@ contract ModuleBeaconDeployer_v1 is tokenGatedRoleAuthorizerMetadata.title, reverter, governor, - impl_mod_Aut_TokenGated_Roles_v1, + impl_mod_AUT_TokenGated_Roles_v2, tokenGatedRoleAuthorizerMetadata.majorVersion, tokenGatedRoleAuthorizerMetadata.minorVersion, tokenGatedRoleAuthorizerMetadata.patchVersion @@ -101,7 +101,7 @@ contract ModuleBeaconDeployer_v1 is votingRolesMetadata.title, reverter, governor, - impl_mod_Aut_Ext_VotingRoles_v1, + impl_mod_AUT_EXT_VotingRoles_v2, votingRolesMetadata.majorVersion, votingRolesMetadata.minorVersion, votingRolesMetadata.patchVersion @@ -122,7 +122,7 @@ contract ModuleBeaconDeployer_v1 is bancorRedeemingVirtualSupplyFundingManagerMetadata.title, reverter, governor, - impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v1, + impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v2, bancorRedeemingVirtualSupplyFundingManagerMetadata .majorVersion, bancorRedeemingVirtualSupplyFundingManagerMetadata @@ -133,28 +133,6 @@ contract ModuleBeaconDeployer_v1 is ) ); - // RestrictedBancorRedeemingVirtualSupplyFundingManager - initialMetadataRegistration.push( - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata - ); - initialBeaconRegistration.push( - IInverterBeacon_v1( - proxyAndBeaconDeployer.deployInverterBeacon( - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata - .title, - reverter, - governor, - impl_mod_FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1, - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata - .majorVersion, - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata - .minorVersion, - restrictedBancorRedeemingVirtualSupplyFundingManagerMetadata - .patchVersion - ) - ) - ); - // BondingSurfaceRedeemingFundingManager initialMetadataRegistration.push( bondingSurfaceRedeemingFundingManagerMetadata @@ -165,7 +143,7 @@ contract ModuleBeaconDeployer_v1 is bondingSurfaceRedeemingFundingManagerMetadata.title, reverter, governor, - impl_mod_FM_BC_BondingSurface_Redeeming_v1, + impl_mod_FM_BC_BondingSurface_Redeeming_v2, bondingSurfaceRedeemingFundingManagerMetadata.majorVersion, bondingSurfaceRedeemingFundingManagerMetadata.minorVersion, bondingSurfaceRedeemingFundingManagerMetadata.patchVersion @@ -184,7 +162,7 @@ contract ModuleBeaconDeployer_v1 is .title, reverter, governor, - impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, + impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, bondingSurfaceRedeemingRestrictedRepayerSeizableFundingManagerMetadata .majorVersion, bondingSurfaceRedeemingRestrictedRepayerSeizableFundingManagerMetadata @@ -219,7 +197,7 @@ contract ModuleBeaconDeployer_v1 is oracleRedeemingFundingManagerMetadata.title, reverter, governor, - impl_mod_FM_PC_Oracle_Redeeming_v1, + impl_mod_FM_PC_Oracle_Redeeming_v2, oracleRedeemingFundingManagerMetadata.majorVersion, oracleRedeemingFundingManagerMetadata.minorVersion, oracleRedeemingFundingManagerMetadata.patchVersion @@ -237,7 +215,7 @@ contract ModuleBeaconDeployer_v1 is tokenVaultMetadata.title, reverter, governor, - impl_mod_FM_EXT_TokenVault_v1, + impl_mod_FM_EXT_TokenVault_v2, tokenVaultMetadata.majorVersion, tokenVaultMetadata.minorVersion, tokenVaultMetadata.patchVersion @@ -256,7 +234,7 @@ contract ModuleBeaconDeployer_v1 is oraclePermissionedMetadata.title, reverter, governor, - impl_mod_LM_PC_Oracle_Permissioned_v1, + impl_mod_LM_Oracle_Permissioned_v2, oraclePermissionedMetadata.majorVersion, oraclePermissionedMetadata.minorVersion, oraclePermissionedMetadata.patchVersion @@ -272,7 +250,7 @@ contract ModuleBeaconDeployer_v1 is bountiesMetadata.title, reverter, governor, - impl_mod_LM_PC_Bounties_v2, + impl_mod_LM_PC_Bounties_v3, bountiesMetadata.majorVersion, bountiesMetadata.minorVersion, bountiesMetadata.patchVersion @@ -288,7 +266,7 @@ contract ModuleBeaconDeployer_v1 is kpiRewarderMetadata.title, reverter, governor, - impl_mod_LM_PC_KPIRewarder_v2, + impl_mod_LM_PC_KPIRewarder_v3, kpiRewarderMetadata.majorVersion, kpiRewarderMetadata.minorVersion, kpiRewarderMetadata.patchVersion @@ -304,7 +282,7 @@ contract ModuleBeaconDeployer_v1 is paymentRouterMetadata.title, reverter, governor, - impl_mod_LM_PC_PaymentRouter_v2, + impl_mod_LM_PC_PaymentRouter_v3, paymentRouterMetadata.majorVersion, paymentRouterMetadata.minorVersion, paymentRouterMetadata.patchVersion @@ -320,7 +298,7 @@ contract ModuleBeaconDeployer_v1 is recurringPaymentsMetadata.title, reverter, governor, - impl_mod_LM_PC_RecurringPayments_v2, + impl_mod_LM_PC_RecurringPayments_v3, recurringPaymentsMetadata.majorVersion, recurringPaymentsMetadata.minorVersion, recurringPaymentsMetadata.patchVersion @@ -336,7 +314,7 @@ contract ModuleBeaconDeployer_v1 is stakingMetadata.title, reverter, governor, - impl_mod_LM_PC_Staking_v2, + impl_mod_LM_PC_Staking_v3, stakingMetadata.majorVersion, stakingMetadata.minorVersion, stakingMetadata.patchVersion @@ -357,7 +335,7 @@ contract ModuleBeaconDeployer_v1 is queueManualExecutionPaymentProcessorMetadata.title, reverter, governor, - impl_mod_PP_Queue_ManualExecution_v1, + impl_mod_PP_Queue_ManualExecution_v2, queueManualExecutionPaymentProcessorMetadata.majorVersion, queueManualExecutionPaymentProcessorMetadata.minorVersion, queueManualExecutionPaymentProcessorMetadata.patchVersion @@ -373,7 +351,7 @@ contract ModuleBeaconDeployer_v1 is queuePaymentProcessorMetadata.title, reverter, governor, - impl_mod_PP_Queue_v1, + impl_mod_PP_Queue_v2, queuePaymentProcessorMetadata.majorVersion, queuePaymentProcessorMetadata.minorVersion, queuePaymentProcessorMetadata.patchVersion @@ -389,7 +367,7 @@ contract ModuleBeaconDeployer_v1 is simplePaymentProcessorMetadata.title, reverter, governor, - impl_mod_PP_Simple_v2, + impl_mod_PP_Simple_v3, simplePaymentProcessorMetadata.majorVersion, simplePaymentProcessorMetadata.minorVersion, simplePaymentProcessorMetadata.patchVersion @@ -405,7 +383,7 @@ contract ModuleBeaconDeployer_v1 is streamingPaymentProcessorMetadata.title, reverter, governor, - impl_mod_PP_Streaming_v2, + impl_mod_PP_Streaming_v3, streamingPaymentProcessorMetadata.majorVersion, streamingPaymentProcessorMetadata.minorVersion, streamingPaymentProcessorMetadata.patchVersion diff --git a/script/deploymentSuite/SingletonDeployer_v1.s.sol b/script/deploymentSuite/SingletonDeployer_v1.s.sol index fc35b4372..879138495 100644 --- a/script/deploymentSuite/SingletonDeployer_v1.s.sol +++ b/script/deploymentSuite/SingletonDeployer_v1.s.sol @@ -47,38 +47,37 @@ contract SingletonDeployer_v1 is ProtocolConstants_v1 { // Modules // Authorizer - address public impl_mod_Aut_Roles_v1; - address public impl_mod_Aut_TokenGated_Roles_v1; - address public impl_mod_Aut_Ext_VotingRoles_v1; + address public impl_mod_AUT_Roles_v2; + address public impl_mod_AUT_TokenGated_Roles_v2; + address public impl_mod_AUT_EXT_VotingRoles_v2; // Funding Managers - address public impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v1; - address public impl_mod_FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1; - address public impl_mod_FM_BC_BondingSurface_Redeeming_v1; + address public impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v2; + address public impl_mod_FM_BC_BondingSurface_Redeeming_v2; address public - impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1; + impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2; address public impl_mod_FM_DepositVault_v1; - address public impl_mod_FM_PC_Oracle_Redeeming_v1; + address public impl_mod_FM_PC_Oracle_Redeeming_v2; // Funding Managers - Extensions - address public impl_mod_FM_EXT_TokenVault_v1; + address public impl_mod_FM_EXT_TokenVault_v2; // Logic Modules - address public impl_mod_LM_PC_Oracle_Permissioned_v1; - address public impl_mod_LM_PC_Bounties_v2; - address public impl_mod_LM_PC_KPIRewarder_v2; - address public impl_mod_LM_PC_PaymentRouter_v2; - address public impl_mod_LM_PC_RecurringPayments_v2; - address public impl_mod_LM_PC_Staking_v2; + address public impl_mod_LM_Oracle_Permissioned_v2; + address public impl_mod_LM_PC_Bounties_v3; + address public impl_mod_LM_PC_KPIRewarder_v3; + address public impl_mod_LM_PC_PaymentRouter_v3; + address public impl_mod_LM_PC_RecurringPayments_v3; + address public impl_mod_LM_PC_Staking_v3; // Payment Processors - address public impl_mod_PP_Queue_ManualExecution_v1; - address public impl_mod_PP_Queue_v1; - address public impl_mod_PP_Simple_v2; - address public impl_mod_PP_Streaming_v2; + address public impl_mod_PP_Queue_ManualExecution_v2; + address public impl_mod_PP_Queue_v2; + address public impl_mod_PP_Simple_v3; + address public impl_mod_PP_Streaming_v3; // Orchestrator - address public impl_orc_Orchestrator_v1; + address public impl_orc_Orchestrator_v2; //-------------------------------------------------------------------------- // Factory Usage @@ -164,47 +163,40 @@ contract SingletonDeployer_v1 is ProtocolConstants_v1 { // Authorizer console2.log(" -- Authorizer"); - impl_mod_Aut_Roles_v1 = deployAndLogWithCreate2( - "AUT_Roles_v1", vm.getCode("AUT_Roles_v1.sol:AUT_Roles_v1") + impl_mod_AUT_Roles_v2 = deployAndLogWithCreate2( + "AUT_Roles_v2", vm.getCode("AUT_Roles_v2.sol:AUT_Roles_v2") ); - impl_mod_Aut_TokenGated_Roles_v1 = deployAndLogWithCreate2( - "AUT_TokenGated_Roles_v1", - vm.getCode("AUT_TokenGated_Roles_v1.sol:AUT_TokenGated_Roles_v1") + impl_mod_AUT_TokenGated_Roles_v2 = deployAndLogWithCreate2( + "AUT_TokenGated_Roles_v2", + vm.getCode("AUT_TokenGated_Roles_v2.sol:AUT_TokenGated_Roles_v2") ); - impl_mod_Aut_Ext_VotingRoles_v1 = deployAndLogWithCreate2( - "AUT_EXT_VotingRoles_v1", - vm.getCode("AUT_EXT_VotingRoles_v1.sol:AUT_EXT_VotingRoles_v1") + impl_mod_AUT_EXT_VotingRoles_v2 = deployAndLogWithCreate2( + "AUT_EXT_VotingRoles_v2", + vm.getCode("AUT_EXT_VotingRoles_v2.sol:AUT_EXT_VotingRoles_v2") ); // Funding Managers console2.log(" -- Funding Managers"); - impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v1 = + impl_mod_FM_BC_Bancor_Redeeming_VirtualSupply_v2 = deployAndLogWithCreate2( - "FM_BC_Bancor_Redeeming_VirtualSupply_v1", + "FM_BC_Bancor_Redeeming_VirtualSupply_v2", vm.getCode( - "FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol:FM_BC_Bancor_Redeeming_VirtualSupply_v1" - ) - ); - impl_mod_FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1 = - deployAndLogWithCreate2( - "FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1", - vm.getCode( - "FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol:FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1" + "FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol:FM_BC_Bancor_Redeeming_VirtualSupply_v2" ) ); - impl_mod_FM_BC_BondingSurface_Redeeming_v1 = deployAndLogWithCreate2( - "FM_BC_BondingSurface_Redeeming_v1", + impl_mod_FM_BC_BondingSurface_Redeeming_v2 = deployAndLogWithCreate2( + "FM_BC_BondingSurface_Redeeming_v2", vm.getCode( - "FM_BC_BondingSurface_Redeeming_v1.sol:FM_BC_BondingSurface_Redeeming_v1" + "FM_BC_BondingSurface_Redeeming_v2.sol:FM_BC_BondingSurface_Redeeming_v2" ) ); - impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 = + impl_mod_FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 = deployAndLogWithCreate2( - "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1", + "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2", vm.getCode( - "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol:FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1" + "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol:FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2" ) ); @@ -213,81 +205,81 @@ contract SingletonDeployer_v1 is ProtocolConstants_v1 { vm.getCode("FM_DepositVault_v1.sol:FM_DepositVault_v1") ); - impl_mod_FM_PC_Oracle_Redeeming_v1 = deployAndLogWithCreate2( - "FM_PC_Oracle_Redeeming_v1", + impl_mod_FM_PC_Oracle_Redeeming_v2 = deployAndLogWithCreate2( + "FM_PC_Oracle_Redeeming_v2", vm.getCode( - "FM_PC_Oracle_Redeeming_v1.sol:FM_PC_Oracle_Redeeming_v1" + "FM_PC_Oracle_Redeeming_v2.sol:FM_PC_Oracle_Redeeming_v2" ) ); // Funding Manager - Extensions console2.log(" --- Funding Managers - Extensions"); - impl_mod_FM_EXT_TokenVault_v1 = deployAndLogWithCreate2( - "FM_EXT_TokenVault_v1", - vm.getCode("FM_EXT_TokenVault_v1.sol:FM_EXT_TokenVault_v1") + impl_mod_FM_EXT_TokenVault_v2 = deployAndLogWithCreate2( + "FM_EXT_TokenVault_v2", + vm.getCode("FM_EXT_TokenVault_v2.sol:FM_EXT_TokenVault_v2") ); // Logic Modules console2.log(" -- Logic Modules"); - impl_mod_LM_PC_Oracle_Permissioned_v1 = deployAndLogWithCreate2( - "LM_Oracle_Permissioned_v1", + impl_mod_LM_Oracle_Permissioned_v2 = deployAndLogWithCreate2( + "LM_Oracle_Permissioned_v2", vm.getCode( - "LM_Oracle_Permissioned_v1.sol:LM_Oracle_Permissioned_v1" + "LM_Oracle_Permissioned_v2.sol:LM_Oracle_Permissioned_v2" ) ); - impl_mod_LM_PC_Bounties_v2 = deployAndLogWithCreate2( - "LM_PC_Bounties_v2", - vm.getCode("LM_PC_Bounties_v2.sol:LM_PC_Bounties_v2") + impl_mod_LM_PC_Bounties_v3 = deployAndLogWithCreate2( + "LM_PC_Bounties_v3", + vm.getCode("LM_PC_Bounties_v3.sol:LM_PC_Bounties_v3") ); - impl_mod_LM_PC_KPIRewarder_v2 = deployAndLogWithCreate2( - "LM_PC_KPIRewarder_v2", - vm.getCode("LM_PC_KPIRewarder_v2.sol:LM_PC_KPIRewarder_v2") + impl_mod_LM_PC_KPIRewarder_v3 = deployAndLogWithCreate2( + "LM_PC_KPIRewarder_v3", + vm.getCode("LM_PC_KPIRewarder_v3.sol:LM_PC_KPIRewarder_v3") ); - impl_mod_LM_PC_PaymentRouter_v2 = deployAndLogWithCreate2( - "LM_PC_PaymentRouter_v2", - vm.getCode("LM_PC_PaymentRouter_v2.sol:LM_PC_PaymentRouter_v2") + impl_mod_LM_PC_PaymentRouter_v3 = deployAndLogWithCreate2( + "LM_PC_PaymentRouter_v3", + vm.getCode("LM_PC_PaymentRouter_v3.sol:LM_PC_PaymentRouter_v3") ); - impl_mod_LM_PC_RecurringPayments_v2 = deployAndLogWithCreate2( - "LM_PC_RecurringPayments_v2", + impl_mod_LM_PC_RecurringPayments_v3 = deployAndLogWithCreate2( + "LM_PC_RecurringPayments_v3", vm.getCode( - "LM_PC_RecurringPayments_v2.sol:LM_PC_RecurringPayments_v2" + "LM_PC_RecurringPayments_v3.sol:LM_PC_RecurringPayments_v3" ) ); - impl_mod_LM_PC_Staking_v2 = deployAndLogWithCreate2( - "LM_PC_Staking_v2", - vm.getCode("LM_PC_Staking_v2.sol:LM_PC_Staking_v2") + impl_mod_LM_PC_Staking_v3 = deployAndLogWithCreate2( + "LM_PC_Staking_v3", + vm.getCode("LM_PC_Staking_v3.sol:LM_PC_Staking_v3") ); // Payment Processors console2.log(" -- Payment Processors"); - impl_mod_PP_Queue_ManualExecution_v1 = deployAndLogWithCreate2( - "PP_Queue_ManualExecution_v1", + impl_mod_PP_Queue_ManualExecution_v2 = deployAndLogWithCreate2( + "PP_Queue_ManualExecution_v2", vm.getCode( - "PP_Queue_ManualExecution_v1.sol:PP_Queue_ManualExecution_v1" + "PP_Queue_ManualExecution_v2.sol:PP_Queue_ManualExecution_v2" ) ); - impl_mod_PP_Queue_v1 = deployAndLogWithCreate2( - "PP_Queue_v1", vm.getCode("PP_Queue_v1.sol:PP_Queue_v1") + impl_mod_PP_Queue_v2 = deployAndLogWithCreate2( + "PP_Queue_v2", vm.getCode("PP_Queue_v2.sol:PP_Queue_v2") ); - impl_mod_PP_Simple_v2 = deployAndLogWithCreate2( - "PP_Simple_v2", vm.getCode("PP_Simple_v2.sol:PP_Simple_v2") + impl_mod_PP_Simple_v3 = deployAndLogWithCreate2( + "PP_Simple_v3", vm.getCode("PP_Simple_v3.sol:PP_Simple_v3") ); - impl_mod_PP_Streaming_v2 = deployAndLogWithCreate2( - "PP_Streaming_v2", vm.getCode("PP_Streaming_v2.sol:PP_Streaming_v2") + impl_mod_PP_Streaming_v3 = deployAndLogWithCreate2( + "PP_Streaming_v3", vm.getCode("PP_Streaming_v3.sol:PP_Streaming_v3") ); // Orchestrator console2.log(" - Orchestrator"); - impl_orc_Orchestrator_v1 = deployAndLogWithCreate2( - "Orchestrator_v1", + impl_orc_Orchestrator_v2 = deployAndLogWithCreate2( + "Orchestrator_v2", abi.encodePacked( - vm.getCode("Orchestrator_v1.sol:Orchestrator_v1"), + vm.getCode("Orchestrator_v2.sol:Orchestrator_v2"), abi.encode(transactionForwarder) ) ); diff --git a/script/testnetContracts/Testnet_ModuleFactory_v1.sol b/script/testnetContracts/Testnet_ModuleFactory_v1.sol index d0157cdd7..a870737ba 100644 --- a/script/testnetContracts/Testnet_ModuleFactory_v1.sol +++ b/script/testnetContracts/Testnet_ModuleFactory_v1.sol @@ -6,7 +6,7 @@ import { ModuleFactory_v1, IModuleFactory_v1, IInverterBeacon_v1, - IModule_v1 + IModule_v2 } from "src/factories/ModuleFactory_v1.sol"; /** @@ -39,7 +39,7 @@ contract Testnet_ModuleFactory_v1 is ModuleFactory_v1 { /// @inheritdoc IModuleFactory_v1 function registerMetadata( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external override(ModuleFactory_v1) { _registerMetadata(metadata, beacon); diff --git a/script/utils/DeployNewModule.s.sol b/script/utils/DeployNewModule.s.sol index 6b96e6065..812514e8d 100644 --- a/script/utils/DeployNewModule.s.sol +++ b/script/utils/DeployNewModule.s.sol @@ -14,7 +14,7 @@ import {IDeterministicFactory_v1} from import { Governor_v1, - IModule_v1, + IModule_v2, IInverterBeacon_v1 } from "@ex/governance/Governor_v1.sol"; @@ -75,7 +75,7 @@ contract DeployNewModule is Script, ProtocolConstants_v1 { Governor_v1 governor = Governor_v1(deployedGovernor); vm.startBroadcast(deployerPrivateKey); governor.registerMetadataInModuleFactory( - IModule_v1.Metadata( + IModule_v2.Metadata( majorVersion, minorVersion, patchVersion, diff --git a/script/utils/UpgradeModule.s.sol b/script/utils/UpgradeModule.s.sol index f5414fdb6..c23ce9311 100644 --- a/script/utils/UpgradeModule.s.sol +++ b/script/utils/UpgradeModule.s.sol @@ -14,7 +14,7 @@ import {IDeterministicFactory_v1} from import { Governor_v1, - IModule_v1, + IModule_v2, IInverterBeacon_v1 } from "@ex/governance/Governor_v1.sol"; @@ -83,7 +83,7 @@ contract UpgradeModule is Script, ProtocolConstants_v1 { { ModuleFactory_v1 moduleFactory = ModuleFactory_v1(deployedModuleFactory); (IInverterBeacon_v1 beacon,) = moduleFactory.getBeaconAndId( - IModule_v1.Metadata( + IModule_v2.Metadata( majorVersion, 0, 0, diff --git a/script/workflowDeploymentAndSetupScripts/DeployAndSetupNavBasedPimWorkflow.s.sol b/script/workflowDeploymentAndSetupScripts/DeployAndSetupNavBasedPimWorkflow.s.sol deleted file mode 100644 index 98ccc4403..000000000 --- a/script/workflowDeploymentAndSetupScripts/DeployAndSetupNavBasedPimWorkflow.s.sol +++ /dev/null @@ -1,896 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -// Internal -import {ERC20IssuanceUpgradeable_Blacklist_v1} from - "@ex/token/ERC20IssuanceUpgradeable_Blacklist_v1.sol"; -import {IOrchestratorFactory_v1} from - "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {AUT_Roles_v1} from "src/modules/authorizer/role/AUT_Roles_v1.sol"; -import {PP_Queue_ManualExecution_v1} from "@pp/PP_Queue_ManualExecution_v1.sol"; -import {FM_PC_Oracle_Redeeming_v1} from - "@fm/oracle/FM_PC_Oracle_Redeeming_v1.sol"; -import {LM_Oracle_Permissioned_v1} from "@lm/LM_Oracle_Permissioned_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {DeploymentScript} from "script/deploymentScript/DeploymentScript.s.sol"; - -// External -import "forge-std/Script.sol"; -import {TransparentUpgradeableProxy} from - "@oz/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20Metadata} from "@oz/token/ERC20/extensions/IERC20Metadata.sol"; -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -contract DeployAndSetupNavBasedPimWorkflow is DeploymentScript { - // Issuance Token - ERC20IssuanceUpgradeable_Blacklist_v1 internal _issuanceToken; - string internal _tokenName; - string internal _tokenSymbol; - uint internal _maxSupply; - uint8 internal _tokenDecimals; - address internal _tokenOwner; - address internal _tokenProxyAdmin; - address internal _blacklistManager; - - // Role Authorizer - address internal _workflowAdmin; - - // Collateral Token (external) - address internal _collateralToken; - - // Oracle Based Funding Manager - address internal _projectTreasury; - uint internal _buyFeeInBps; - uint internal _maxBuyFeeInBps; - uint internal _sellFeeInBps; - uint internal _maxSellFeeInBps; - bool internal _isDirectOperationOnly; - bytes internal _workflowConfigData; - address internal _whitelistRole; - address internal _whitelistRoleAdmin; - address internal _queueExecutorRole; - address internal _queueExecutorRoleAdmin; - - // Manual Queue Payment Processor - address internal _cancelledOrdersTreasury; - address internal _failedOrdersTreasury; - address internal _queueOperatorRole; - address internal _queueOperatorRoleAdmin; - - // Oracle - address internal _priceSetterRole; - address internal _priceSetterRoleAdmin; - - // Workflow config - bool internal _independentUpdateBool; - address internal _independentUpdateAdmin; - - // Orchestrator - IOrchestrator_v1 internal _orchestrator; - - // Workflow Modules - FM_PC_Oracle_Redeeming_v1 internal _fundingManager; - AUT_Roles_v1 internal _authorizer; - PP_Queue_ManualExecution_v1 internal _paymentProcessor; - LM_Oracle_Permissioned_v1 internal _oracleModule; - - /** - * @dev This script does the following: - * 1. Deploys the issuance token - * 2. Deploys the workflow - * 3. Sets up the following modules: - * * Funding Manager - * * Authorizer - * * Payment Processor - * * Oracle - * 4. Sets up the workflow admin - * 5. Revokes admin role from deployer - * @dev To utilize the workflow, the following steps are required AFTER running this script: - * 1. Open buy and sell in the funding manager - * 2. Set the price in the oracle - */ - function run() public override { - console2.log("\n==============================================="); - console2.log(" DEPLOYING NAV BASED PIM WORKFLOW"); - console2.log(" ================================\n"); - - // Load and validate deployment variables - _loadAndValidateDeploymentVariables(); - - // Deploy external issuance token - _deployToken(); - - // Deploy workflow - _deployWorkflow(); - - // Setup workflow - _setupWorkflow(); - - // Set workflow admin - _setupWorkflowAdmin(); - - console2.log("================================================"); - console2.log(" DEPLOYMENT SUMMARY "); - console2.log("================================================"); - console2.log(" Status: SUCCESSFUL "); - console2.log(" Chain ID: ", block.chainid); - console2.log(" All contracts deployed and configured "); - console2.log("================================================\n"); - } - - // ------------------------------------------------------------------------- - // Deployment functions - - function _deployToken() internal { - console2.log("Token Deployment"); - console2.log("----------------"); - console2.log(" Deploying issuance token..."); - - // Deploy the implementation contract - vm.startBroadcast(deployerPrivateKey); - ERC20IssuanceUpgradeable_Blacklist_v1 implementation = - new ERC20IssuanceUpgradeable_Blacklist_v1(); - // Deploy a simple proxy that delegates to the implementation - address proxy = address( - new TransparentUpgradeableProxy( - address(implementation), - _tokenProxyAdmin, - abi.encodeWithSelector( - ERC20IssuanceUpgradeable_Blacklist_v1 - .__ERC20IssuanceBlacklist_init - .selector, - _tokenName, - _tokenSymbol, - _tokenDecimals, - _maxSupply - ) - ) - ); - vm.stopBroadcast(); - - // Store issuance token - _issuanceToken = ERC20IssuanceUpgradeable_Blacklist_v1(proxy); - console2.log(" [OK] Issuance token deployed successfully"); - _logAddress(" Issuance token deployed at", proxy); - } - - function _deployWorkflow() internal { - console2.log("\nWorkflow Deployment"); - console2.log("-------------------"); - // --------------------------------------------------------------------- - // Deploy workflow - - // Get the orchestrator factory - IOrchestratorFactory_v1 orchestratorFactory = - IOrchestratorFactory_v1(getDeployedOrchestratorFactory()); - - console2.log(" Deploying Inverter Protocol workflow..."); - // Create workflow - vm.startBroadcast(deployerPrivateKey); - _orchestrator = orchestratorFactory.createOrchestrator( - _getWorkflowConfig(), - _getFundingManagerConfig(), - _getAuthorizerConfig(), - _getPaymentProcessorConfig(), - _getOptionalModulesConfigs() - ); - vm.stopBroadcast(); - - // --------------------------------------------------------------------- - // Store workflow addresses - - // Store modules - // Store funding manager - _fundingManager = - FM_PC_Oracle_Redeeming_v1(address(_orchestrator.fundingManager())); - // Store Authorizer - _authorizer = AUT_Roles_v1(address(_orchestrator.authorizer())); - // Store Payment Processor - _paymentProcessor = PP_Queue_ManualExecution_v1( - address(_orchestrator.paymentProcessor()) - ); - // Store Oracle module - _oracleModule = - LM_Oracle_Permissioned_v1(address(_orchestrator.listModules()[0])); - - // --------------------------------------------------------------------- - // Validate workflow deployment - - console2.log(" Validating deployed workflow..."); - _validateWorkflowDeployment(); - console2.log(" [OK] Workflow deployed successfully\n"); - - // --------------------------------------------------------------------- - // Log workflow addresses - console2.log("Deployed Contract Addresses"); - console2.log("---------------------------"); - _logAddress(" * Orchestrator", address(_orchestrator)); - _logAddress(" * Funding Manager", address(_fundingManager)); - _logAddress(" * Authorizer", address(_authorizer)); - _logAddress(" * Payment Processor", address(_paymentProcessor)); - _logAddress(" * Oracle Module", address(_oracleModule)); - _logAddress(" * Issuance Token", address(_issuanceToken)); - console2.log("\n"); - } - - // ------------------------------------------------------------------------- - // Setup functions - - function _setupWorkflowAdmin() internal { - console2.log("Administrative Setup"); - console2.log("--------------------"); - - console2.log("Setting up workflow admin..."); - vm.startBroadcast(deployerPrivateKey); - _authorizer.grantRole(_authorizer.getAdminRole(), _workflowAdmin); - require( - _authorizer.checkForRole(_authorizer.getAdminRole(), _workflowAdmin) - == true, - "Workflow admin not set correctly" - ); - console2.log(" [OK] Workflow admin configured"); - console2.log(" Workflow admin: ", _workflowAdmin); - - // Revoke admin role from deployer - console2.log("\n Revoking admin role from deployer..."); - _authorizer.revokeRole(_authorizer.getAdminRole(), deployer); - require( - _authorizer.checkForRole(_authorizer.getAdminRole(), deployer) - == false, - "Admin role not revoked from deployer" - ); - vm.stopBroadcast(); - console2.log(" [OK] Admin role revoked from deployer\n"); - } - - function _setupWorkflow() internal { - console2.log("Workflow Configuration"); - console2.log("----------------------"); - // Setup token - _setupAndVerifyToken(); - // Setup Oracle - _setupAndVerifyOracle(); - // Setup Funding Manager - _setupAndVerifyFundingManager(); - // Setup Payment Processor - _setupAndVerifyPaymentProcessor(); - console2.log("\n [OK] Workflow setup successful\n"); - } - - function _setupAndVerifyPaymentProcessor() internal { - console2.log("\n Payment Processor Setup"); - // --------------------------------------------------------------------- - // Roles - vm.startBroadcast(deployerPrivateKey); - // Set Queue Operator Role - _paymentProcessor.grantModuleRole( - _paymentProcessor.getQueueOperatorRole(), _queueOperatorRole - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRole() - ), - _queueOperatorRole - ), - "Queue operator role not set correctly" - ); - _logAddress(" * Queue Operator", _queueOperatorRole); - - // Set Queue Operator Role Admin - _paymentProcessor.grantModuleRole( - _paymentProcessor.getQueueOperatorRoleAdmin(), - _queueOperatorRoleAdmin - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRoleAdmin() - ), - _queueOperatorRoleAdmin - ), - "Queue operator role admin not set correctly" - ); - _logAddress(" * Queue Op Admin", _queueOperatorRoleAdmin); - - // Set admin role for queue operator role - _authorizer.transferAdminRole( - _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRole() - ), - _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRoleAdmin() - ) - ); - require( - _authorizer.getRoleAdmin( - _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRole() - ) - ) - == _authorizer.generateRoleId( - address(_paymentProcessor), - _paymentProcessor.getQueueOperatorRoleAdmin() - ), - "Admin role for queue operator role not set correctly" - ); - vm.stopBroadcast(); - } - - function _setupAndVerifyFundingManager() internal { - console2.log("\n Funding Manager Setup"); - // --------------------------------------------------------------------- - // Setters - vm.startBroadcast(deployerPrivateKey); - // Set Oracle address - _fundingManager.setOracleAddress(address(_oracleModule)); - require( - _fundingManager.getOracle() == address(_oracleModule), - "Funding manager oracle not set correctly" - ); - _logAddress(" * Oracle", address(_oracleModule)); - // --------------------------------------------------------------------- - // Roles - - // Set Whitelist Role - _fundingManager.grantModuleRole( - _fundingManager.getWhitelistRole(), _whitelistRole - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_fundingManager), _fundingManager.getWhitelistRole() - ), - _whitelistRole - ), - "Whitelist role not set correctly" - ); - _logAddress(" * Whitelist Role", _whitelistRole); - - // Set Whitelist Role Admin - _fundingManager.grantModuleRole( - _fundingManager.getWhitelistRoleAdmin(), _whitelistRoleAdmin - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getWhitelistRoleAdmin() - ), - _whitelistRoleAdmin - ), - "Whitelist role admin not set correctly" - ); - _logAddress(" * Whitelist Admin", _whitelistRoleAdmin); - - // Set admin role for whitelist role - _authorizer.transferAdminRole( - _authorizer.generateRoleId( - address(_fundingManager), _fundingManager.getWhitelistRole() - ), - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getWhitelistRoleAdmin() - ) - ); - require( - _authorizer.getRoleAdmin( - _authorizer.generateRoleId( - address(_fundingManager), _fundingManager.getWhitelistRole() - ) - ) - == _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getWhitelistRoleAdmin() - ), - "Admin role for whitelist role not set correctly" - ); - - // Set Queue Executor Role - _fundingManager.grantModuleRole( - _fundingManager.getQueueExecutorRole(), _queueExecutorRole - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getQueueExecutorRole() - ), - _queueExecutorRole - ), - "Queue executor role not set correctly" - ); - _logAddress(" * Queue Executor", _queueExecutorRole); - - // Set Queue Executor Role Admin - _fundingManager.grantModuleRole( - _fundingManager.getQueueExecutorRoleAdmin(), _queueExecutorRoleAdmin - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getQueueExecutorRoleAdmin() - ), - _queueExecutorRoleAdmin - ), - "Queue executor role admin not set correctly" - ); - _logAddress(" * Queue Exec Admin", _queueExecutorRoleAdmin); - - // Set admin role for queue executor role - _authorizer.transferAdminRole( - _authorizer.generateRoleId( - address(_fundingManager), _fundingManager.getQueueExecutorRole() - ), - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getQueueExecutorRoleAdmin() - ) - ); - require( - _authorizer.getRoleAdmin( - _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getQueueExecutorRole() - ) - ) - == _authorizer.generateRoleId( - address(_fundingManager), - _fundingManager.getQueueExecutorRoleAdmin() - ), - "Admin role for queue executor role not set correctly" - ); - vm.stopBroadcast(); - } - - function _setupAndVerifyOracle() internal { - console2.log("\n Oracle Setup"); - // --------------------------------------------------------------------- - // Roles - - vm.startBroadcast(deployerPrivateKey); - // Set price setter role - _oracleModule.grantModuleRole( - _oracleModule.getPriceSetterRole(), _priceSetterRole - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_oracleModule), _oracleModule.getPriceSetterRole() - ), - _priceSetterRole - ), - "Price setter role not set correctly" - ); - _logAddress(" * Price Setter", _priceSetterRole); - - // Set price setter role admin - _oracleModule.grantModuleRole( - _oracleModule.getPriceSetterRoleAdmin(), _priceSetterRoleAdmin - ); - require( - _authorizer.checkForRole( - _authorizer.generateRoleId( - address(_oracleModule), - _oracleModule.getPriceSetterRoleAdmin() - ), - _priceSetterRoleAdmin - ), - "Price setter role admin not set correctly" - ); - _logAddress(" * Price Setter Admin", _priceSetterRoleAdmin); - - // Set admin role for price setter role - _authorizer.transferAdminRole( - _authorizer.generateRoleId( - address(_oracleModule), _oracleModule.getPriceSetterRole() - ), - _authorizer.generateRoleId( - address(_oracleModule), _oracleModule.getPriceSetterRoleAdmin() - ) - ); - require( - _authorizer.getRoleAdmin( - _authorizer.generateRoleId( - address(_oracleModule), _oracleModule.getPriceSetterRole() - ) - ) - == _authorizer.generateRoleId( - address(_oracleModule), _oracleModule.getPriceSetterRoleAdmin() - ), - "Admin role for price setter role not set correctly" - ); - vm.stopBroadcast(); - } - - function _setupAndVerifyToken() internal { - console2.log("\n Issuance Token Setup"); - // --------------------------------------------------------------------- - // Setters - - // Set minter - vm.startBroadcast(deployerPrivateKey); - _issuanceToken.setMinter(address(_fundingManager), true); - require( - _issuanceToken.allowedMinters(address(_fundingManager)), - "Minter not set correctly" - ); - _logAddress(" * Minter", address(_fundingManager)); - // --------------------------------------------------------------------- - // Roles - - // Set Blacklist Manager - _issuanceToken.setBlacklistManager(_blacklistManager, true); - require( - _issuanceToken.isBlacklistManager(_blacklistManager) == true, - "Blacklist manager not set correctly" - ); - _logAddress(" * Blacklist Manager", _blacklistManager); - // Set new owner - _issuanceToken.transferOwnership(_tokenOwner); - require( - _issuanceToken.owner() == _tokenOwner, - "Token owner not set correctly" - ); - _logAddress(" * Owner", _tokenOwner); - vm.stopBroadcast(); - } - - // ------------------------------------------------------------------------- - // Workflow config functions - - function _getWorkflowConfig() - internal - view - returns (IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig_) - { - workflowConfig_ = IOrchestratorFactory_v1.WorkflowConfig( - _independentUpdateBool, _independentUpdateAdmin - ); - } - - function _getFundingManagerConfig() - internal - view - returns ( - IOrchestratorFactory_v1.ModuleConfig memory fundingManagerConfig_ - ) - { - // Get the funding manager config - fundingManagerConfig_ = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( - 1, - 0, - 0, - "https://github.com/InverterNetwork/contracts", - "FM_PC_Oracle_Redeeming_v1" - ), - abi.encode( - _projectTreasury, - _issuanceToken, - _collateralToken, - _buyFeeInBps, - _sellFeeInBps, - _maxSellFeeInBps, - _maxBuyFeeInBps, - _isDirectOperationOnly - ) - ); - } - - function _getAuthorizerConfig() - internal - view - returns (IOrchestratorFactory_v1.ModuleConfig memory authorizerConfig_) - { - // Get the authorizer config - authorizerConfig_ = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( - 1, - 0, - 0, - "https://github.com/InverterNetwork/contracts", - "AUT_Roles_v1" - ), - abi.encode(deployer) // Set deployer as initial admin. Later replaced by the workflow admin. - ); - } - - function _getPaymentProcessorConfig() - internal - view - returns ( - IOrchestratorFactory_v1.ModuleConfig memory paymentProcessorConfig_ - ) - { - paymentProcessorConfig_ = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( - 1, - 0, - 0, - "https://github.com/InverterNetwork/contracts", - "PP_Queue_ManualExecution_v1" - ), - abi.encode(_cancelledOrdersTreasury, _failedOrdersTreasury) - ); - } - - function _getOptionalModulesConfigs() - internal - view - returns ( - IOrchestratorFactory_v1.ModuleConfig[] memory optionalModulesConfigs_ - ) - { - optionalModulesConfigs_ = new IOrchestratorFactory_v1.ModuleConfig[](1); - optionalModulesConfigs_[0] = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( - 1, - 0, - 0, - "https://github.com/InverterNetwork/contracts", - "LM_Oracle_Permissioned_v1" - ), - abi.encode(_collateralToken) - ); - } - - // ------------------------------------------------------------------------- - // Validation functions - - function _loadAndValidateDeploymentVariables() internal { - console2.log("Environment Setup"); - console2.log("-----------------"); - console2.log(" Loading and validating environment variables..."); - - // Load and validate environment variables of workflow - _loadAndValidateEnvironmentVariablesOfWorkflow(); - - // Validate Inverter Protocol deployment variables from ENV - _validateInverterProtocolDeploymentVariables(); - - // Load Inverter Protocol deployed contracts - loadDeployedContracts(); - console2.log( - " [OK] Loading Inverter Protocol Infrastructure Contracts\n" - ); - } - - function _validateWorkflowDeployment() internal view { - // Validate deployments - _validateFundingManagerDeployment(); - _validateAuthorizerDeployment(); - _validatePaymentProcessorDeployment(); - _validateOracleDeployment(); - console2.log(" [OK] Deployed workflow validated"); - } - - function _validateOracleDeployment() internal view { - // Validate oracle deployment - require( - _oracleModule.getCollateralTokenDecimals() - == IERC20Metadata(_collateralToken).decimals(), - "Oracle collateral token decimals not set correctly" - ); - require( - _oracleModule.orchestrator() == _orchestrator, - "Oracle orchestrator not set correctly" - ); - } - - function _validatePaymentProcessorDeployment() internal view { - // Validate payment processor deployment - require( - _paymentProcessor.getCanceledOrdersTreasury() - == _cancelledOrdersTreasury, - "Payment processor cancelled orders treasury not set" - ); - require( - _paymentProcessor.getFailedOrdersTreasury() == _failedOrdersTreasury, - "Payment processor failed orders treasury not set" - ); - require( - _paymentProcessor.orchestrator() == _orchestrator, - "Payment processor orchestrator not set correctly" - ); - } - - function _validateAuthorizerDeployment() internal view { - // Validate authorizer deployment - require( - _authorizer.checkForRole(_authorizer.getAdminRole(), deployer), - "Authorizer admin role not set correctly" - ); - require( - _authorizer.orchestrator() == _orchestrator, - "Authorizer orchestrator not set correctly" - ); - } - - function _validateFundingManagerDeployment() internal view { - // Validate funding manager deployment - require( - _fundingManager.getProjectTreasury() == _projectTreasury, - "Funding manager project treasury not set" - ); - require( - _fundingManager.getIssuanceToken() == address(_issuanceToken), - "Funding manager issuance token not set" - ); - require( - _fundingManager.token() == IERC20(_collateralToken), - "Funding manager token not set" - ); - require( - _fundingManager.getBuyFee() == _buyFeeInBps, - "Funding manager buy fee not set" - ); - require( - _fundingManager.getSellFee() == _sellFeeInBps, - "Funding manager sell fee not set" - ); - require( - _fundingManager.getMaxProjectBuyFee() == _maxBuyFeeInBps, - "Funding manager max project buy fee not set" - ); - require( - _fundingManager.getMaxProjectSellFee() == _maxSellFeeInBps, - "Funding manager max project sell fee not set" - ); - require( - _fundingManager.getIsDirectOperationsOnly() - == _isDirectOperationOnly, - "Funding manager is direct operations only not set" - ); - require( - _fundingManager.orchestrator() == _orchestrator, - "Funding manager orchestrator not set correctly" - ); - } - - function _validateInverterProtocolDeploymentVariables() internal view { - _validateValidChainId(block.chainid); - console2.log(" [OK] Validating Inverter Protocol deployment address"); - } - - function _validateValidChainId(uint chainId_) internal view { - for (uint i = 0; i < deployedMainnets.length; i++) { - if (chainId_ == deployedMainnets[i]) { - return; - } - } - for (uint i = 0; i < deployedTestnets.length; i++) { - if (chainId_ == deployedTestnets[i]) { - return; - } - } - // revert("Invalid chain id"); - } - - function _loadAndValidateEnvironmentVariablesOfWorkflow() internal { - // Issuance Token - // --------------------------------------------------------------------- - _tokenName = vm.envString("TOKEN_NAME_STRING"); - require(bytes(_tokenName).length > 0, "Token name not set"); - - _tokenSymbol = vm.envString("TOKEN_SYMBOL_STRING"); - require(bytes(_tokenSymbol).length > 0, "Token symbol not set"); - - _tokenDecimals = uint8(vm.envUint("TOKEN_DECIMALS")); - require(_tokenDecimals > 0, "Token decimals not set"); - - _maxSupply = vm.envUint("MAX_SUPPLY"); - require(_maxSupply > 0, "Max supply not set"); - - _tokenOwner = vm.envAddress("TOKEN_OWNER_ADDRESS"); - require(_tokenOwner != address(0), "Token owner not set"); - - _tokenProxyAdmin = vm.envAddress("TOKEN_PROXY_ADMIN_ADDRESS"); - require(_tokenProxyAdmin != address(0), "Token proxy admin not set"); - - _blacklistManager = vm.envAddress("BLACKLIST_MANAGER_ADDRESS"); - require(_blacklistManager != address(0), "Blacklist manager not set"); - - // Role Authorizer - // --------------------------------------------------------------------- - _workflowAdmin = vm.envAddress("WORKFLOW_ADMIN_ADDRESS"); - require(_workflowAdmin != address(0), "Workflow admin not set"); - - // Collateral Token (external) - // --------------------------------------------------------------------- - _collateralToken = vm.envAddress("COLLATERAL_TOKEN_ADDRESS"); - require(_collateralToken != address(0), "Collateral token not set"); - - // Oracle Based Funding Manager - // --------------------------------------------------------------------- - _projectTreasury = vm.envAddress("PROJECT_TREASURY_ADDRESS"); - require(_projectTreasury != address(0), "Project treasury not set"); - - _buyFeeInBps = vm.envUint("BUY_FEE_IN_BPS"); - - _maxBuyFeeInBps = vm.envUint("MAX_BUY_FEE_IN_BPS"); - - _sellFeeInBps = vm.envUint("SELL_FEE_IN_BPS"); - - _maxSellFeeInBps = vm.envUint("MAX_SELL_FEE_IN_BPS"); - - _isDirectOperationOnly = vm.envBool("IS_DIRECT_OPERATION_ONLY_BOOL"); - - _whitelistRole = vm.envAddress("WHITELIST_ROLE_ADDRESS"); - require(_whitelistRole != address(0), "Whitelist role not set"); - - _whitelistRoleAdmin = vm.envAddress("WHITELIST_ROLE_ADMIN_ADDRESS"); - require( - _whitelistRoleAdmin != address(0), "Whitelist role admin not set" - ); - - _queueExecutorRole = vm.envAddress("QUEUE_EXECUTOR_ROLE_ADDRESS"); - require(_queueExecutorRole != address(0), "Queue executor role not set"); - - _queueExecutorRoleAdmin = - vm.envAddress("QUEUE_EXECUTOR_ROLE_ADMIN_ADDRESS"); - require( - _queueExecutorRoleAdmin != address(0), - "Queue executor role admin not set" - ); - - // Oracle - // --------------------------------------------------------------------- - _priceSetterRole = vm.envAddress("PRICE_SETTER_ROLE_ADDRESS"); - require(_priceSetterRole != address(0), "Price setter role not set"); - - _priceSetterRoleAdmin = vm.envAddress("PRICE_SETTER_ROLE_ADMIN_ADDRESS"); - require( - _priceSetterRoleAdmin != address(0), - "Price setter role admin not set" - ); - - // Manual Queue Payment Processor - // --------------------------------------------------------------------- - _cancelledOrdersTreasury = - vm.envAddress("CANCELLED_ORDER_TREASURY_ADDRESS"); - require( - _cancelledOrdersTreasury != address(0), - "Cancelled orders treasury not set" - ); - - _failedOrdersTreasury = vm.envAddress("FAILED_ORDER_TREASURY_ADDRESS"); - require( - _failedOrdersTreasury != address(0), - "Failed orders treasury not set" - ); - - _queueOperatorRole = vm.envAddress("QUEUE_OPERATOR_ROLE_ADDRESS"); - require(_queueOperatorRole != address(0), "Queue operator role not set"); - - _queueOperatorRoleAdmin = - vm.envAddress("QUEUE_EXECUTOR_ROLE_ADMIN_ADDRESS"); - require( - _queueOperatorRoleAdmin != address(0), - "Queue operator role admin not set" - ); - - // Workflow config - // --------------------------------------------------------------------- - _independentUpdateBool = vm.envBool("INDEPENDENT_UPDATE_BOOL"); - _independentUpdateAdmin = - vm.envAddress("INDEPENDENT_UPDATE_ADMIN_ADDRESS"); - - console2.log(" [OK] Environment variables loaded and validated"); - } - - // ------------------------------------------------------------------------- - // Logging functions - - function _logAddress(string memory label, address addr) internal view { - console2.log(string.concat(label, ": "), addr); - } -} diff --git a/script/workflowDeploymentAndSetupScripts/WorkflowDeployment.md b/script/workflowDeploymentAndSetupScripts/WorkflowDeployment.md deleted file mode 100644 index 2c73a4a42..000000000 --- a/script/workflowDeploymentAndSetupScripts/WorkflowDeployment.md +++ /dev/null @@ -1,65 +0,0 @@ -# Inverter Protocol: Workflow Deployment Guide - -## Introduction - -This document provides instructions for deploying workflows within the Inverter Protocol. It outlines the necessary steps and environment variable configurations for successful deployment. - ---- - -## Yield Bearing Stable Token Workflow - -Follow these steps to deploy the Yield Bearing Stable Token workflow: - -1. **Prepare Environment File:** - * Duplicate the example environment file: `example.dev.navBasedPimWorkflow.env` - * Rename the duplicated file to: `dev.navBasedPimWorkflow.env` (removing the `example.` prefix). - -2. **Configure Deployment Variables:** - * Open the new `.env.navBasedPimWorkflow` file. - * Locate the **Workflow Deployment Parameters** section. - * Replace all placeholder/demo values in this section with your actual deployment-specific variables. - * Locate the **Etherscan API Keys** section. - * Replace the placeholder/demo values to your Etherscan API key. - -3. **Load Environment Variables:** - * Source the environment file in your terminal session. This loads the variables you configured in the previous step. - ```bash - source script/workflowDeploymentAndSetupScripts/dev.navBasedPimWorkflow.env - ``` - -4. **Deploy and Verify the Workflow:** - * Execute the deployment script using `forge`. This command performs the deployment, setup, and contract verification. - * You will need to provide the following command-line arguments (ensure the corresponding environment variables like `$OPTIMISM_SEPOLIA_RPC_URL`, `$OPTIMISM_ETHERSCAN_API_KEY`, and `$VERIFIER_URL` are set, either from the sourced file or your shell environment): - * `--rpc-url`: The RPC endpoint URL for your target blockchain network (e.g., `$OPTIMISM_SEPOLIA_RPC_URL` for Optimism Sepolia). - * `--etherscan-api-key`: Your Etherscan API key for the target network (e.g., `$OPTIMISM_ETHERSCAN_API_KEY`). - * `--verifier-url`: The Etherscan API URL used for verification on the target network (e.g., `$OPTIMISM_SEPOLIA_ETHERSCAN_URL`). - - * Run the following command: - ```bash - forge script script/workflowDeploymentAndSetupScripts/DeployAndSetupNavBasedPimWorkflow.s.sol \ - --rpc-url $OPTIMISM_SEPOLIA_RPC_URL \ - -vvv \ - --broadcast \ - --etherscan-api-key $OPTIMISM_ETHERSCAN_API_KEY \ - --verifier-url $OPTIMISM_SEPOLIA_ETHERSCAN_URL \ - --verify - ``` - *(Note: Replace `$OPTIMISM_SEPOLIA_RPC_URL`, `$OPTIMISM_ETHERSCAN_API_KEY`, and `$OPTIMISM_SEPOLIA_ETHERSCAN_URL` with the actual environment variables containing your specific URLs and key if they differ from the example names.)* - ---- - -### Troubleshooting: Verification Failures - -Contract verification can sometimes fail due to intermittent Etherscan issues. If a contract doesn't verify automatically during deployment: - -1. Identify the address (``) of the contract that failed verification (usually visible in the `forge script` output). -2. Run the `forge verify-contract` command manually for that specific address: - - ```bash - forge verify-contract \ - --rpc-url $RPC_URL \ - --etherscan-api-key $ETHERSCAN_API_KEY \ - --verifier-url $VERIFIER_URL \ - --watch - ``` - *(Ensure you use the same `$RPC_URL`, `$ETHERSCAN_API_KEY`, and `$VERIFIER_URL` values corresponding to the network where the contract was deployed.)* \ No newline at end of file diff --git a/script/workflowDeploymentAndSetupScripts/example.dev.navBasedPimWorkflow.env b/script/workflowDeploymentAndSetupScripts/example.dev.navBasedPimWorkflow.env deleted file mode 100644 index 9d5cbed23..000000000 --- a/script/workflowDeploymentAndSetupScripts/example.dev.navBasedPimWorkflow.env +++ /dev/null @@ -1,108 +0,0 @@ -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Development Environment Variables -# -# WARNING: This file is part of the git repo. DO NOT INCLUDE SENSITIVE DATA! -# -# The environment variables are read by -# - Solidity scripts in script/ -# - forge commands -# -# Note that the variables need to be exported in order for make to read them -# directly. -# -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# ------------------------------------------------------------------------------ - -# RPC endpoints (these are public links, substitute with alchemy or similar for less rate limiting) - -# Local -export RPC_URL="http://127.0.0.1:8545" # Local anvil node - -# Ethereum -export ETHEREUM_RPC_URL=https://cloudflare-eth.com -export SEPOLIA_RPC_URL=https://rpc.ankr.com/eth_sepolia/919d3fa62bc6fc3bddabdc497f7e6fcd1c242ae86588bcdede81c171daab5df7 - -# Optimism -export OPTIMISM_SEPOLIA_RPC_URL=https://sepolia.optimism.io -export OPTIMISM_RPC_URL=https://mainnet.optimism.io - -# Polygon -export POLYGON_AMOY_RPC_URL=https://rpc-amoy.polygon.technology/ -export POLYGON_CARDONA_RPC_URL=https://rpc.cardona.zkevm-rpc.com - - -# ------------------------------------------------------------------------------ -# Wallets - -# Deployer Wallet -# (Note that for this example we are reusing anvil's default wallets.) -export DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - -# ------------------------------------------------------------------------------ -# Contract Verification - -# Etherscan API Keys -export ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 -export POLYGONSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 -export OPTIMISM_ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 -export OPTIMISM_ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Optimisim Etherscan URLs -export OPTIMISM_SEPOLIA_ETHERSCAN_URL=https://api-sepolia-optimistic.etherscan.io/api -export OPTIMISM_ETHERSCAN_URL=https://api-optimistic.etherscan.io/api - -# Ethereum Etherscan URLs -export SEPOLIA_ETHERSCAN_URL=https://api-sepolia.etherscan.io/api -export ETHEREUM_ETHERSCAN_URL=https://api.etherscan.io/api - - -# ------------------------------------------------------------------------------ -# Command to run a deployment script -# In general, the command to run a deployment script will look like this: -# forge script script/deploymentScript/DeploymentScript.s.sol --rpc-url $SEPOLIA_RPC_URL --chain-id 11155111 --private-key $WALLET_DEPLOYER_PK --etherscan-api-key $ETHERSCAN_API_KEY --verify --broadcast --legacy -vvv - - -# ------------------------------------------------------------------------------ -# Workflow Deployment Parameters - -# Issuance Token -export TOKEN_NAME_STRING='Test Token' -export TOKEN_SYMBOL_STRING='T-Token' -export TOKEN_DECIMALS=18 -export MAX_SUPPLY=115792089237316195423570985008687907853269984665640564039457584007913129639935 -export TOKEN_OWNER_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export TOKEN_PROXY_ADMIN_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export BLACKLIST_MANAGER_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Role Authorizer -export WORKFLOW_ADMIN_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Collateral Token (external) -export COLLATERAL_TOKEN_ADDRESS=0x065775C7aB4E60ad1776A30DCfB15325d231Ce4F - -# Oracle Based Funding Manager -export PROJECT_TREASURY_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export BUY_FEE_IN_BPS=0 -export MAX_BUY_FEE_IN_BPS=0 -export SELL_FEE_IN_BPS=0 -export MAX_SELL_FEE_IN_BPS=0 -export IS_DIRECT_OPERATION_ONLY_BOOL=true -export WHITELIST_ROLE_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export WHITELIST_ROLE_ADMIN_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export QUEUE_EXECUTOR_ROLE_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export QUEUE_EXECUTOR_ROLE_ADMIN_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Manual Queue Payment Processor -export CANCELLED_ORDER_TREASURY_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export FAILED_ORDER_TREASURY_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export QUEUE_OPERATOR_ROLE_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export QUEUE_OPERATOR_ROLE_ADMIN=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Permissoned Oracle -export PRICE_SETTER_ROLE_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 -export PRICE_SETTER_ROLE_ADMIN_ADDRESS=ABC123ABC123ABC123ABC123ABC123ABC1 - -# Workflow config -export INDEPENDENT_UPDATE_BOOL=false -export INDEPENDENT_UPDATE_ADMIN_ADDRESS=0x0000000000000000000000000000000000000000 diff --git a/src/external/governance/Governor_v1.sol b/src/external/governance/Governor_v1.sol index a3af26a8b..f2760c4ec 100644 --- a/src/external/governance/Governor_v1.sol +++ b/src/external/governance/Governor_v1.sol @@ -7,7 +7,7 @@ import {IFeeManager_v1} from "@ex/fees/interfaces/IFeeManager_v1.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; import { IModuleFactory_v1, - IModule_v1 + IModule_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; // Internal Dependencies @@ -340,7 +340,7 @@ contract Governor_v1 is /// @inheritdoc IGovernor_v1 function registerMetadataInModuleFactory( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external onlyCommunityOrTeamMultisig { _addBeaconToLinkedBeacons(beacon); diff --git a/src/external/governance/interfaces/IGovernor_v1.sol b/src/external/governance/interfaces/IGovernor_v1.sol index 390697460..abf05515f 100644 --- a/src/external/governance/interfaces/IGovernor_v1.sol +++ b/src/external/governance/interfaces/IGovernor_v1.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; // Internal Dependencies -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IModuleFactory_v1} from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; @@ -267,7 +267,7 @@ interface IGovernor_v1 { /// @param metadata The metadata that will be registered. /// @param beacon The {IInverterBeacon_v1} that will be registered. function registerMetadataInModuleFactory( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external; diff --git a/src/factories/ModuleFactory_v1.sol b/src/factories/ModuleFactory_v1.sol index 07f419c1b..2675c3ea4 100644 --- a/src/factories/ModuleFactory_v1.sol +++ b/src/factories/ModuleFactory_v1.sol @@ -4,8 +4,8 @@ pragma solidity 0.8.23; // Internal Interfaces import { IModuleFactory_v1, - IOrchestrator_v1, - IModule_v1 + IOrchestrator_v2, + IModule_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IOrchestratorFactory_v1} from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; @@ -73,7 +73,7 @@ contract ModuleFactory_v1 is /// @dev Modifier to guarantee function is only callable with valid /// metadata. - modifier validMetadata(IModule_v1.Metadata memory data) { + modifier validMetadata(IModule_v2.Metadata memory data) { if (!LibMetadata.isValid(data)) { revert ModuleFactory__InvalidMetadata(); } @@ -109,7 +109,7 @@ contract ModuleFactory_v1 is mapping(bytes32 => IInverterBeacon_v1) private _beacons; /// @dev Mapping of proxy address to orchestrator address. - /// @dev moduleProxy => {IOrchestrator_v1}. + /// @dev moduleProxy => {IOrchestrator_v2}. mapping(address => address) private _orchestratorOfProxy; /// @dev Maps a users address to a nonce used for the create2-based deployment. @@ -137,7 +137,7 @@ contract ModuleFactory_v1 is /// @param initialMetadataRegistration List of {IInverterBeacon_v1}s addresses that will be registered during the initialization. function init( address _governor, - IModule_v1.Metadata[] memory initialMetadataRegistration, + IModule_v2.Metadata[] memory initialMetadataRegistration, IInverterBeacon_v1[] memory initialBeaconRegistration ) external initializer { __Ownable_init(_governor); @@ -166,23 +166,23 @@ contract ModuleFactory_v1 is /// @inheritdoc IModuleFactory_v1 function createAndInitModule( - IModule_v1.Metadata memory metadata, - IOrchestrator_v1 orchestrator, + IModule_v2.Metadata memory metadata, + IOrchestrator_v2 orchestrator, bytes memory configData, IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig ) external returns (address) { address proxy = createModuleProxy(metadata, orchestrator, workflowConfig); - IModule_v1(proxy).init(orchestrator, metadata, configData); + IModule_v2(proxy).init(orchestrator, metadata, configData); return proxy; } /// @inheritdoc IModuleFactory_v1 function createModuleProxy( - IModule_v1.Metadata memory metadata, - IOrchestrator_v1 orchestrator, + IModule_v2.Metadata memory metadata, + IOrchestrator_v2 orchestrator, IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig ) public returns (address) { // Note that the metadata's validity is not checked because the @@ -232,7 +232,7 @@ contract ModuleFactory_v1 is // Public View Functions /// @inheritdoc IModuleFactory_v1 - function getBeaconAndId(IModule_v1.Metadata memory metadata) + function getBeaconAndId(IModule_v2.Metadata memory metadata) public view returns (IInverterBeacon_v1, bytes32) @@ -256,7 +256,7 @@ contract ModuleFactory_v1 is /// @inheritdoc IModuleFactory_v1 function registerMetadata( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external virtual onlyOwner { _registerMetadata(metadata, beacon); @@ -269,7 +269,7 @@ contract ModuleFactory_v1 is /// @param metadata The metadata to register. /// @param beacon The {IInverterBeacon_v1} to register the metadata to. function _registerMetadata( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) internal validMetadata(metadata) validBeacon(beacon) { IInverterBeacon_v1 oldBeacon; diff --git a/src/factories/OrchestratorFactory_v1.sol b/src/factories/OrchestratorFactory_v1.sol index 7c16f4af7..d2fe92e14 100644 --- a/src/factories/OrchestratorFactory_v1.sol +++ b/src/factories/OrchestratorFactory_v1.sol @@ -4,15 +4,15 @@ pragma solidity 0.8.23; // Internal Interfaces import { IOrchestratorFactory_v1, - IOrchestrator_v1, - IModule_v1 + IOrchestrator_v2, + IModule_v2 } from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; import { IFundingManager_v1, - IAuthorizer_v1, - IPaymentProcessor_v2, + IAuthorizer_v2, + IPaymentProcessor_v3, IGovernor_v1 -} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +} from "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IModuleFactory_v1} from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; @@ -40,12 +40,12 @@ import { /** * @title Inverter Orchestrator Factory * - * @notice {OrchestratorFactory_v1} facilitates the deployment of {Orchestrator_v1}s and their + * @notice {OrchestratorFactory_v1} facilitates the deployment of {Orchestrator_v2}s and their * associated modules for the Inverter Network, ensuring seamless creation and * configuration of various components in a single transaction. * * @dev Utilizes {ERC2771ContextUpgradeable} for meta-transaction capabilities and {ERC165Upgradeable} for interface - * detection. {Orchestrator_v1}s are deployed through EIP-1167 minimal proxies for efficiency. + * detection. {Orchestrator_v2}s are deployed through EIP-1167 minimal proxies for efficiency. * Integrates with the module factory to instantiate necessary modules with custom * configurations, supporting complex setup with interdependencies among modules. * @@ -82,10 +82,10 @@ contract OrchestratorFactory_v1 is /// @inheritdoc IOrchestratorFactory_v1 address public override moduleFactory; - /// @dev Maps the `id` to the {Orchestrator_v1}s. + /// @dev Maps the `id` to the {Orchestrator_v2}s. mapping(uint => address) private _orchestrators; - /// @dev The counter of the current {Orchestrator_v1} `id`. + /// @dev The counter of the current {Orchestrator_v2} `id`. /// @dev Starts counting from 1. uint private _orchestratorIdCounter; @@ -118,7 +118,7 @@ contract OrchestratorFactory_v1 is /// @notice The factories initializer function. /// @param governor_ The address of the {Governor_v1} contract. - /// @param beacon_ The address of the {IInverterBeacon_v1} containing the {Orchestrator_v1} implementation. + /// @param beacon_ The address of the {IInverterBeacon_v1} containing the {Orchestrator_v2} implementation. /// @param moduleFactory_ The address of the {ModuleFactory_v1} contract. function init( address governor_, @@ -149,7 +149,7 @@ contract OrchestratorFactory_v1 is ModuleConfig memory authorizerConfig, ModuleConfig memory paymentProcessorConfig, ModuleConfig[] memory moduleConfigs - ) external returns (IOrchestrator_v1) { + ) external returns (IOrchestrator_v2) { address proxy; // If the workflow should fetch their updates themselves if (workflowConfig.independentUpdates) { @@ -183,25 +183,25 @@ contract OrchestratorFactory_v1 is address fundingManager = IModuleFactory_v1(moduleFactory) .createAndInitModule( fundingManagerConfig.metadata, - IOrchestrator_v1(proxy), + IOrchestrator_v2(proxy), fundingManagerConfig.configData, workflowConfig ); - // Deploy and cache {IAuthorizer_v1} module. + // Deploy and cache {IAuthorizer_v2} module. address authorizer = IModuleFactory_v1(moduleFactory) .createAndInitModule( authorizerConfig.metadata, - IOrchestrator_v1(proxy), + IOrchestrator_v2(proxy), authorizerConfig.configData, workflowConfig ); - // Deploy and cache {IPaymentProcessor_v2} module. + // Deploy and cache {IPaymentProcessor_v3} module. address paymentProcessor = IModuleFactory_v1(moduleFactory) .createAndInitModule( paymentProcessorConfig.metadata, - IOrchestrator_v1(proxy), + IOrchestrator_v2(proxy), paymentProcessorConfig.configData, workflowConfig ); @@ -213,20 +213,20 @@ contract OrchestratorFactory_v1 is emit OrchestratorCreated(_orchestratorIdCounter, proxy); // Initialize orchestrator. - IOrchestrator_v1(proxy).init( + IOrchestrator_v2(proxy).init( _orchestratorIdCounter, moduleFactory, modules, IFundingManager_v1(fundingManager), - IAuthorizer_v1(authorizer), - IPaymentProcessor_v2(paymentProcessor), + IAuthorizer_v2(authorizer), + IPaymentProcessor_v3(paymentProcessor), IGovernor_v1(IModuleFactory_v1(moduleFactory).governor()) ); // Init the rest of the modules _initModules(modules, moduleConfigs, proxy); - return IOrchestrator_v1(proxy); + return IOrchestrator_v2(proxy); } /// @inheritdoc IOrchestratorFactory_v1 @@ -249,7 +249,7 @@ contract OrchestratorFactory_v1 is /// @dev Creates the modules based on their `moduleConfigs. /// @param moduleConfigs The config data of the modules that will be created with this function call. - /// @param orchestratorProxy The address of the {Orchestrator_v1} Proxy that will be linked to the modules. + /// @param orchestratorProxy The address of the {Orchestrator_v2} Proxy that will be linked to the modules. /// @param workflowConfig The workflow's config data. function _createModuleProxies( ModuleConfig[] memory moduleConfigs, @@ -262,7 +262,7 @@ contract OrchestratorFactory_v1 is for (uint i; i < moduleConfigs.length; ++i) { modules[i] = IModuleFactory_v1(moduleFactory).createModuleProxy( moduleConfigs[i].metadata, - IOrchestrator_v1(orchestratorProxy), + IOrchestrator_v2(orchestratorProxy), workflowConfig ); } @@ -272,7 +272,7 @@ contract OrchestratorFactory_v1 is /// @dev Internal function to initialize the modules. /// @param modules The modules to initialize. /// @param moduleConfigs The config data of the modules that will be initialized. - /// @param proxy The address of the {Orchestrator_v1} Proxy that will be linked to the modules. + /// @param proxy The address of the {Orchestrator_v2} Proxy that will be linked to the modules. function _initModules( address[] memory modules, ModuleConfig[] memory moduleConfigs, @@ -281,8 +281,8 @@ contract OrchestratorFactory_v1 is // Deploy and cache optional modules. for (uint i; i < modules.length; ++i) { - IModule_v1(modules[i]).init( - IOrchestrator_v1(proxy), + IModule_v2(modules[i]).init( + IOrchestrator_v2(proxy), moduleConfigs[i].metadata, moduleConfigs[i].configData ); diff --git a/src/factories/interfaces/IModuleFactory_v1.sol b/src/factories/interfaces/IModuleFactory_v1.sol index dba0479c1..10be904f0 100644 --- a/src/factories/interfaces/IModuleFactory_v1.sol +++ b/src/factories/interfaces/IModuleFactory_v1.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IOrchestratorFactory_v1} from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; @@ -38,17 +38,17 @@ interface IModuleFactory_v1 { /// @param metadata The registered Metadata. /// @param beacon The registered Beacon. event MetadataRegistered( - IModule_v1.Metadata metadata, IInverterBeacon_v1 indexed beacon + IModule_v2.Metadata metadata, IInverterBeacon_v1 indexed beacon ); - /// @notice Event emitted when new module created for an {Orchestrator_v1}. - /// @param orchestrator The corresponding {Orchestrator_v1}. + /// @notice Event emitted when new module created for an {Orchestrator_v2}. + /// @param orchestrator The corresponding {Orchestrator_v2}. /// @param module The created module instance. /// @param metadata The registered metadata. event ModuleCreated( address indexed orchestrator, address indexed module, - IModule_v1.Metadata metadata + IModule_v2.Metadata metadata ); /// @notice Event emitted when {Governor_v1} is set. @@ -68,25 +68,25 @@ interface IModuleFactory_v1 { /// @notice Creates a module instance identified by given `metadata` and initiates it. /// @param metadata The module's `metadata`. - /// @param orchestrator The {Orchestrator_v1} instance of the module. + /// @param orchestrator The {Orchestrator_v2} instance of the module. /// @param configData The configData of the module. /// @param workflowConfig The configData of the workflow. /// @return moduleProxyAddress Returns the address of the created module proxy. function createAndInitModule( - IModule_v1.Metadata memory metadata, - IOrchestrator_v1 orchestrator, + IModule_v2.Metadata memory metadata, + IOrchestrator_v2 orchestrator, bytes memory configData, IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig ) external returns (address); /// @notice Creates a module proxy instance identified by given `metadata`. /// @param metadata The module's metadata. - /// @param orchestrator The {Orchestrator_v1} instance of the module. + /// @param orchestrator The {Orchestrator_v2} instance of the module. /// @param workflowConfig The configData of the workflow. /// @return Returns the address of the created module proxy. function createModuleProxy( - IModule_v1.Metadata memory metadata, - IOrchestrator_v1 orchestrator, + IModule_v2.Metadata memory metadata, + IOrchestrator_v2 orchestrator, IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig ) external returns (address); @@ -95,14 +95,14 @@ interface IModuleFactory_v1 { /// @param metadata The module's metadata. /// @return beacon The module's {IInverterBeacon_v1} instance registered. /// @return id The metadata's id. - function getBeaconAndId(IModule_v1.Metadata memory metadata) + function getBeaconAndId(IModule_v2.Metadata memory metadata) external view returns (IInverterBeacon_v1, bytes32); - /// @notice Returns the {Orchestrator_v1} address of a beacon proxy. + /// @notice Returns the {Orchestrator_v2} address of a beacon proxy. /// @param proxy The beacon proxy address. - /// @return orchestratorAddress The corresponding {Orchestrator_v1} address for the provided proxy. + /// @return orchestratorAddress The corresponding {Orchestrator_v2} address for the provided proxy. function getOrchestratorOfProxy(address proxy) external view @@ -114,7 +114,7 @@ interface IModuleFactory_v1 { /// @param metadata The module's metadata. /// @param beacon The module's {IInverterBeacon_v1} instance. function registerMetadata( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external; } diff --git a/src/factories/interfaces/IOrchestratorFactory_v1.sol b/src/factories/interfaces/IOrchestratorFactory_v1.sol index fa13ea249..0822f305a 100644 --- a/src/factories/interfaces/IOrchestratorFactory_v1.sol +++ b/src/factories/interfaces/IOrchestratorFactory_v1.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; @@ -30,8 +30,8 @@ interface IOrchestratorFactory_v1 { //-------------------------------------------------------------------------- // Events - /// @notice Event emitted when a new {Orchestrator_v1} is created. - /// @param orchestratorId The id of the {Orchestrator_v1}. + /// @notice Event emitted when a new {Orchestrator_v2} is created. + /// @param orchestratorId The id of the {Orchestrator_v2}. /// @param orchestratorAddress The address of the {Orchestrator. event OrchestratorCreated( uint indexed orchestratorId, address indexed orchestratorAddress @@ -62,18 +62,18 @@ interface IOrchestratorFactory_v1 { /// @param metadata The module's metadata. /// @param configData Variable config data for specific module implementations. struct ModuleConfig { - IModule_v1.Metadata metadata; + IModule_v2.Metadata metadata; bytes configData; } //-------------------------------------------------------------------------- // Functions - /// @notice Creates a new {Orchestrator_v1}. + /// @notice Creates a new {Orchestrator_v2}. /// @param workflowConfig The workflow's config data. /// @param fundingManagerConfig The config data for the orchestrator's {IFundingManager_v1} /// instance. - /// @param authorizerConfig The config data for the {Orchestrator_v1}'s {IAuthorizer_v1} + /// @param authorizerConfig The config data for the {Orchestrator_v2}'s {IAuthorizer_v2} /// instance. /// @param paymentProcessorConfig The config data for the orchestrator's /// {IPaymentProcessor_v1} instance. @@ -86,22 +86,22 @@ interface IOrchestratorFactory_v1 { ModuleConfig memory authorizerConfig, ModuleConfig memory paymentProcessorConfig, ModuleConfig[] memory moduleConfigs - ) external returns (IOrchestrator_v1); + ) external returns (IOrchestrator_v2); - /// @notice Returns the {IOrchestrator_v1} {IInverterBeacon_v1} address. - /// @return OrchestratorImplementationBeacon The {IInverterBeacon_v1} of the {Orchestrator_v1} Implementation. + /// @notice Returns the {IOrchestrator_v2} {IInverterBeacon_v1} address. + /// @return OrchestratorImplementationBeacon The {IInverterBeacon_v1} of the {Orchestrator_v2} Implementation. function beacon() external view returns (IInverterBeacon_v1); /// @notice Returns the {IModuleFactory_v1} implementation address. /// @return ModuleFactoryAddress The address of the linked {ModuleFactory_v1}. function moduleFactory() external view returns (address); - /// @notice Returns the {IOrchestrator_v1} address that corresponds to the given id. + /// @notice Returns the {IOrchestrator_v2} address that corresponds to the given id. /// @param id The requested orchestrator's id. - /// @return orchestratorAddress The address of the corresponding {Orchestrator_v1}. + /// @return orchestratorAddress The address of the corresponding {Orchestrator_v2}. function getOrchestratorByID(uint id) external view returns (address); - /// @notice Returns the counter of the current {Orchestrator_v1} id. - /// @return id The id of the next created {Orchestrator_v1}. + /// @notice Returns the counter of the current {Orchestrator_v2} id. + /// @return id The id of the next created {Orchestrator_v2}. function getOrchestratorIDCounter() external view returns (uint); } diff --git a/src/factories/interfaces/IPIM_WorkflowFactory_v1.sol b/src/factories/interfaces/IPIM_WorkflowFactory_v1.sol deleted file mode 100644 index 729a6855e..000000000 --- a/src/factories/interfaces/IPIM_WorkflowFactory_v1.sol +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; -import {IOrchestratorFactory_v1} from - "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {IFM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {IPIM_WorkflowFactory_v1} from - "src/factories/interfaces/IPIM_WorkflowFactory_v1.sol"; -import {IERC20Issuance_v1} from - "src/external/token/interfaces/IERC20Issuance_v1.sol"; - -// Internal Dependencies -import {ERC20Issuance_v1} from "src/external/token/ERC20Issuance_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -interface IPIM_WorkflowFactory_v1 { - //-------------------------------------------------------------------------- - // Errors - /// @notice Error thrown when an unpermissioned address tries to claim fees or to transfer role. - error PIM_WorkflowFactory__OnlyPimFeeRecipient(); - /// @notice Error thrown when the curve is deployed with an invalid configuration. - error PIM_WorkflowFactory__InvalidConfiguration(); - - //-------------------------------------------------------------------------- - // Events - - /// @notice Event emitted when a new PIM workflow is created. - /// @param bondingCurve The address of the bonding curve. - /// @param issuanceToken The address of the issuance token. - /// @param deployer The address of the deployer. - /// @param recipient The address of the recipient. - /// @param isRenouncedIssuanceToken If ownership over the issuance token should be renounced. - /// @param isRenouncedWorkflow If admin rights over the workflow should be renounced. - event PIMWorkflowCreated( - address indexed bondingCurve, - address indexed issuanceToken, - address indexed deployer, - address recipient, - bool isRenouncedIssuanceToken, - bool isRenouncedWorkflow - ); - - /// @notice Event emitted when factory owner sets new fee. - /// @param oldRecipient The previous pim fee recipient. - /// @param newRecipient The new pim fee recipient. - event PimFeeRecipientUpdated( - address indexed oldRecipient, address indexed newRecipient - ); - - /// @notice Event emitted when PIM fee (buy/sell fees) is claimed. - /// @param claimer The address of the claimer. - /// @param amount The amount claimed. - event PimFeeClaimed(address indexed claimer, uint amount); - - //-------------------------------------------------------------------------- - // Structs - - /// @notice Struct for the issuance token parameters. - /// @param name The name of the issuance token. - /// @param symbol The symbol of the issuance token. - /// @param decimals The decimals of the issuance token. - /// @param maxSupply The maximum supply of the issuance token. - struct IssuanceTokenParams { - string name; - string symbol; - uint8 decimals; - uint maxSupply; - } - - /// @notice Struct for the issuance token parameters. - /// @param fundingManagerMetadata The {FundingManager_v1}'s metadata. - /// @param authorizerMetadata The {Authorizer_v1}'s metadata. - /// @param bcProperties The bonding curve's properties. - /// @param issuanceTokenParams The issuance token's parameters. - /// @param recipient The recipient of the initial issuance token supply. - /// @param admin Is set as token owner and workflow admin unless renounced. - /// @param collateralToken The collateral token. - /// @param firstCollateralIn Amount of collateral that is used for the first purchase from the bonding curve. - /// @param isRenouncedIssuanceToken If ownership over the issuance token should be renounced. - /// @param isRenouncedWorkflow If admin rights over the workflow should be renounced. - /// @param withInitialLiquidity If true initial liquidity will be added to the bonding curve. - /// In this case the recipient will receive the initial issuance token supply. - /// If false initial liquidity will not be added to the bonding curve and initial token. - struct PIMConfig { - IModule_v1.Metadata fundingManagerMetadata; - IModule_v1.Metadata authorizerMetadata; - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties - bcProperties; - IssuanceTokenParams issuanceTokenParams; - address admin; - address recipient; - address collateralToken; - uint firstCollateralIn; - bool isRenouncedIssuanceToken; - bool isRenouncedWorkflow; - bool withInitialLiquidity; - } - - //-------------------------------------------------------------------------- - // Functions - - /// @notice Deploys a workflow with a bonding curve and an issuance token. - /// @param workflowConfig The workflow's config data. - /// @param paymentProcessorConfig The config data for the {Orchestrator_v1}'s {IPaymentProcessor_v1} instance. - /// @param moduleConfigs Variable length set of optional module's config data. - /// @param PIMConfig The configuration for the issuance token and the bonding curve. - /// @return Returns the address of {Orchestrator_v1} and the address of the issuance token. - function createPIMWorkflow( - IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig, - IOrchestratorFactory_v1.ModuleConfig memory paymentProcessorConfig, - IOrchestratorFactory_v1.ModuleConfig[] memory moduleConfigs, - IPIM_WorkflowFactory_v1.PIMConfig memory PIMConfig - ) external returns (IOrchestrator_v1, ERC20Issuance_v1); - - /// @notice Updates who can claim the buy/sell fees of a given bonding curve. - /// @dev Only callable by the currently eligible fee recipient. - /// @param fundingManager The address of the bonding curve from which to withdraw fees. - /// @param to The address that should be eligible to claim fees in the future. - function transferPimFeeEligibility(address fundingManager, address to) - external; - - /// @notice Withdraws the buy/sell fees of a given bonding curve. - /// @dev Only callable by the currently eligible fee recipient. - /// @param fundingManager The address of the bonding curve from which to withdraw fees. - /// @param to The address to which the fees are sent. - function withdrawPimFee(address fundingManager, address to) external; - - /// @notice Returns the address of the {OrchestratorFractory_v1}. - /// @return Address of the {OrchestratorFractory_v1}. - function orchestratorFactory() external view returns (address); -} diff --git a/src/factories/workflow-specific/PIM_WorkflowFactory_v1.sol b/src/factories/workflow-specific/PIM_WorkflowFactory_v1.sol deleted file mode 100644 index 99212117b..000000000 --- a/src/factories/workflow-specific/PIM_WorkflowFactory_v1.sol +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IOrchestratorFactory_v1} from - "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IFM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {IPIM_WorkflowFactory_v1} from - "src/factories/interfaces/IPIM_WorkflowFactory_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -// External Implementations -import {ERC20Issuance_v1} from "src/external/token/ERC20Issuance_v1.sol"; - -// External Dependencies -import {Ownable2Step} from "@oz/access/Ownable2Step.sol"; -import {Ownable} from "@oz/access/Ownable.sol"; -import {ERC2771Context, Context} from "@oz/metatx/ERC2771Context.sol"; - -/** - * @title Inverter PIM Workflow Factory - * - * @notice Facilitates the creation and configuration and setup of PIM workflows, including the deployment of - * bonding curves and issuance tokens. It also provides functionalities for updating fee recipients and - * withdrawing fees from bonding curves. - * - * @dev An owned factory for deploying PIM workflows. Deploying the PIM workflow includes: - * - Deploying the bonding curve and issuance token. - * - Enable bonding curve to mint issuance token. - * - Transfer initial collateral supply from msg.sender to bonding curve and mint issuance token to recipient. - * - If applicable make first purchase. - * - If flag is set, renounce admin role by setting factory as admin. - * - * @custom:security-contact security@inverter.network - * For any security concerns or findings, please refer to our Security Policy at - * security.inverter.network or email us directly. - * - * @custom:author Inverter Network - */ -contract PIM_WorkflowFactory_v1 is - Ownable2Step, - ERC2771Context, - IPIM_WorkflowFactory_v1 -{ - //-------------------------------------------------------------------------- - // State Variables - - /// @dev store address of {Orchestratorfactory_v1}. - address public orchestratorFactory; - - /// @dev mapping of Funding Manager address to `feeRecipient` address. - mapping(address fundingManager => address feeRecipient) private - _pimFeeRecipients; - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Modifier to guarantee the caller is the fee recipient for the given funding manager. - modifier onlyPimFeeRecipient(address fundingManager) { - if (_msgSender() != _pimFeeRecipients[fundingManager]) { - revert PIM_WorkflowFactory__OnlyPimFeeRecipient(); - } - _; - } - - //-------------------------------------------------------------------------- - // Constructor - - constructor( - address _orchestratorFactory, - address _owner, - address _trustedForwarder - ) Ownable(_owner) ERC2771Context(_trustedForwarder) { - orchestratorFactory = _orchestratorFactory; - } - - //-------------------------------------------------------------------------- - // Public Mutating Functions - - /// @inheritdoc IPIM_WorkflowFactory_v1 - function createPIMWorkflow( - IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig, - IOrchestratorFactory_v1.ModuleConfig memory paymentProcessorConfig, - IOrchestratorFactory_v1.ModuleConfig[] memory moduleConfigs, - IPIM_WorkflowFactory_v1.PIMConfig memory PIMConfig - ) - external - returns (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) - { - // deploy issuance token - issuanceToken = new ERC20Issuance_v1( - PIMConfig.issuanceTokenParams.name, - PIMConfig.issuanceTokenParams.symbol, - PIMConfig.issuanceTokenParams.decimals, - PIMConfig.issuanceTokenParams.maxSupply - ); - issuanceToken.setMinter(address(this), true); - - // assemble fundingManager config, authorizer config and deploy orchestrator - IOrchestratorFactory_v1.ModuleConfig memory fundingManagerConfig = - IOrchestratorFactory_v1.ModuleConfig( - PIMConfig.fundingManagerMetadata, - abi.encode( - address(issuanceToken), - PIMConfig.bcProperties, - PIMConfig.collateralToken - ) - ); - IOrchestratorFactory_v1.ModuleConfig memory authorizerConfig = - IOrchestratorFactory_v1.ModuleConfig( - PIMConfig.authorizerMetadata, abi.encode(address(this)) - ); - orchestrator = IOrchestratorFactory_v1(orchestratorFactory) - .createOrchestrator( - workflowConfig, - fundingManagerConfig, - authorizerConfig, - paymentProcessorConfig, - moduleConfigs - ); - - // get bonding curve / funding manager - address fundingManager = address(orchestrator.fundingManager()); - - // enable bonding curve to mint issuance token - issuanceToken.setMinter(fundingManager, true); - - // transfer initial collateral supply from msg.sender to bonding curve and mint issuance token to recipient - _manageInitialSupplies( - IBondingCurveBase_v1(fundingManager), - IERC20(PIMConfig.collateralToken), - issuanceToken, - PIMConfig.bcProperties.initialCollateralSupply, - PIMConfig.bcProperties.initialIssuanceSupply, - PIMConfig.recipient, - PIMConfig.withInitialLiquidity - ); - - // if applicable make first purchase - _manageInitialPurchase( - IBondingCurveBase_v1(fundingManager), - IERC20(PIMConfig.collateralToken), - PIMConfig.firstCollateralIn, - PIMConfig.recipient - ); - - // disable factory to mint issuance token - issuanceToken.setMinter(address(this), false); - - // if isRenouncedToken flag is set burn owner role, else transfer ownership to specified admin - if (PIMConfig.isRenouncedIssuanceToken) { - _transferTokenOwnership(issuanceToken, address(0)); - } else { - _transferTokenOwnership(issuanceToken, PIMConfig.admin); - } - - // if isRenouncedWorkflow flag is set factory keeps admin rights over workflow, else transfer admin rights to - // specified admin - if (PIMConfig.isRenouncedWorkflow) { - // record the admin as fee recipient eligible to claim buy/sell fees - _pimFeeRecipients[fundingManager] = PIMConfig.admin; - } else { - _transferWorkflowAdminRights(orchestrator, PIMConfig.admin); - } - - emit IPIM_WorkflowFactory_v1.PIMWorkflowCreated( - fundingManager, - address(issuanceToken), - _msgSender(), - PIMConfig.recipient, - PIMConfig.isRenouncedIssuanceToken, - PIMConfig.isRenouncedWorkflow - ); - - return (orchestrator, issuanceToken); - } - - //-------------------------------------------------------------------------- - // Permissioned Functions - - /// @inheritdoc IPIM_WorkflowFactory_v1 - function withdrawPimFee(address fundingManager, address to) - external - onlyPimFeeRecipient(fundingManager) - { - uint amount = - IBondingCurveBase_v1(fundingManager).projectCollateralFeeCollected(); - IBondingCurveBase_v1(fundingManager).withdrawProjectCollateralFee( - to, amount - ); - emit IPIM_WorkflowFactory_v1.PimFeeClaimed(_msgSender(), amount); - } - - /// @inheritdoc IPIM_WorkflowFactory_v1 - function transferPimFeeEligibility(address fundingManager, address to) - external - onlyPimFeeRecipient(fundingManager) - { - _pimFeeRecipients[fundingManager] = to; - emit IPIM_WorkflowFactory_v1.PimFeeRecipientUpdated(_msgSender(), to); - } - - //-------------------------------------------------------------------------- - // Internal Functions - - function _manageInitialSupplies( - IBondingCurveBase_v1 fundingManager, - IERC20 collateralToken, - ERC20Issuance_v1 issuanceToken, - uint initialCollateralSupply, - uint initialIssuanceSupply, - address recipient, - bool withInitialLiquidity - ) private { - if (withInitialLiquidity) { - // collateral token is paid for by the msg.sender - collateralToken.transferFrom( - _msgSender(), address(fundingManager), initialCollateralSupply - ); - // issuance token is minted to the the specified recipient - issuanceToken.mint(recipient, initialIssuanceSupply); - } else { - // issuance token is minted to the burn address - issuanceToken.mint(address(0xDEAD), initialIssuanceSupply); - } - } - - function _manageInitialPurchase( - IBondingCurveBase_v1 fundingManager, - IERC20 collateralToken, - uint firstCollateralIn, - address recipient - ) private { - // transfer initial collateral amount from deployer to factory - collateralToken.transferFrom( - _msgSender(), address(this), firstCollateralIn - ); - - // set allowance for curve to spend factory's tokens - collateralToken.approve(address(fundingManager), firstCollateralIn); - - // make first purchase - IBondingCurveBase_v1(fundingManager).buyFor( - recipient, firstCollateralIn, 1 - ); - } - - function _transferTokenOwnership( - ERC20Issuance_v1 issuanceToken, - address newAdmin - ) private { - if (newAdmin == address(0)) { - issuanceToken.renounceOwnership(); - } else { - issuanceToken.transferOwnership(newAdmin); - } - } - - function _transferWorkflowAdminRights( - IOrchestrator_v1 orchestrator, - address newAdmin - ) private { - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - // if renounced flag is set, add zero address as admin (because workflow must have at least one admin set) - orchestrator.authorizer().grantRole(adminRole, newAdmin); - // and revoke admin role from factory - orchestrator.authorizer().revokeRole(adminRole, address(this)); - } - - //-------------------------------------------------------------------------- - // ERC2771 Context - - /// Needs to be overridden, because they are imported via the Ownable2Step as well. - function _msgSender() - internal - view - virtual - override(ERC2771Context, Context) - returns (address sender) - { - return ERC2771Context._msgSender(); - } - - /// Needs to be overridden, because they are imported via the Ownable2Step as well. - function _msgData() - internal - view - virtual - override(ERC2771Context, Context) - returns (bytes calldata) - { - return ERC2771Context._msgData(); - } - - function _contextSuffixLength() - internal - view - virtual - override(ERC2771Context, Context) - returns (uint) - { - return ERC2771Context._contextSuffixLength(); - } -} diff --git a/src/modules/authorizer/IAuthorizer_v2.sol b/src/modules/authorizer/IAuthorizer_v2.sol new file mode 100644 index 000000000..6bad68fac --- /dev/null +++ b/src/modules/authorizer/IAuthorizer_v2.sol @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IAccessControlEnumerable} from + "@oz/access/extensions/IAccessControlEnumerable.sol"; + +/** + * @title Inverter Authorizer Interface + * + * @notice Provides the access control mechanism for managing roles and + * permissions across different modules within the Inverter Network, + * ensuring secure and controlled access to critical functionalities. + * + * @dev Inherits functionality from: + * - IAuthorizer_v2: Implementation interface. + * - Module_v2: Inverter network base module functionality. + * - AccessControlEnumerableUpgradeable: Access control functionality. + * + * Key features: + * + * - Role creation and management. This includes the ability to + * create roles, revoke roles, assigning and revoking role + * admins, which can add and remove role members. + * + * - Role-based access control. This includes the ability to grant + * roles access to functions that implement the permissioned + * modifier. Functions can also be set to public access by + * adding the public role to the function permissions. + * + * @custom:documentation See https://github.com/InverterNetwork/contracts/tree/dev/docs/src/modules/authorizer/role/AUT_Roles_v2.md + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ +interface IAuthorizer_v2 is IAccessControlEnumerable { + // ======================================================================== + // Errors + + /// @notice The provided initial admin address is invalid. + error Module__Authorizer__InvalidInitialAdmin(); + + /// @notice The provided role ID is the default admin role. + error Module__Authorizer__CannotModifyAdminRoleAccess(); + + /// @notice The provided role ID is not existing. + error Module__Authorizer__RoleIdNotExisting(); + + /// @notice The provided input length is not valid. + error Module__Authorizer__InvalidInputLength(); + + /// @notice The function is only callable by an active Module. + /// @param module_ The address of the module. + error Module__Authorizer__NotActiveModule(address module_); + + /// @notice The function is only callable if the Module is self-managing + /// its roles. + error Module__Authorizer__ModuleNotSelfManaged(); + + /// @notice There always needs to be at least one admin. + error Module__Authorizer__AdminRoleCannotBeEmpty(); + + /// @notice The orchestrator cannot own itself. + error Module__Authorizer__OrchestratorCannotHaveAdminRole(); + + // ======================================================================== + // Events + + /// @notice Emits when a role is added to a function permission. + /// @param target_ The address of the target contract. + /// @param functionSelector_ The selector of the function. + /// @param roleId_ The ID of the role. + event AccessPermissionAdded( + address target_, bytes4 functionSelector_, bytes32 roleId_ + ); + + /// @notice Emits when a role is removed from a function permission. + /// @param target_ The address of the target contract. + /// @param functionSelector_ The selector of the function. + /// @param roleId_ The ID of the role. + event AccessPermissionRemoved( + address target_, bytes4 functionSelector_, bytes32 roleId_ + ); + + /// @notice Emits when a role is created. + /// @param roleId_ The ID of the role. + /// @param roleName The name of the role. + event RoleCreated(bytes32 roleId_, string roleName); + + /// @notice Emits when a role is labeled. + /// @param roleId_ The ID of the role. + /// @param newRoleName The new name of the role. + event RoleLabeled(bytes32 roleId_, string newRoleName); + + /// @notice Emits when a role admin is burned. + /// @param roleId_ The ID of the role for which the admin was burned. + event RoleAdminBurned(bytes32 roleId_); + + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Role Management + + /// @notice Returns the role ID of the admin role. + /// @return defaultAdminId_ The role ID of the default admin. + function getAdminRole() external view returns (bytes32 defaultAdminId_); + + // ------------------------------------------------------------------------ + // Getter - Authorization + + /// @notice Returns the permissions of the given function in the target + /// contract. + /// @param target_ The address of the target contract. + /// @param selector_ The selector of the function. + /// @return permissions_ The roleIds that are permissioned to call the + /// function. + function getPermissions(address target_, bytes4 selector_) + external + view + returns (bytes32[] memory permissions_); + + /// @notice Returns the number of created role IDs. + /// @return lastAssignedRoleId_ The number of created role IDs. + function getLastAssignedRoleId() + external + view + returns (uint lastAssignedRoleId_); + + /// @notice Returns whether the given roleId has the permission to call the + /// given function in the target contract. + /// @param target_ The address of the target contract. + /// @param selector_ The selector of the function. + /// @param roleId_ The roleId that we want to check. + /// @return isRolePermissioned_ Returns whether the roleId is permissioned + /// to call the function. + function isRolePermissioned( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) external view returns (bool isRolePermissioned_); + + /// @notice Checks whether the given caller address holds the required role + /// to execute the given function in the target contract. + /// @dev Returns true if the address holds the Default Admin role. + /// Returns true if the function permissions contain the public + /// role. + /// @param caller_ The address of the caller. + /// @param target_ The address of the target contract. + /// @param selector_ The selector of the function. + /// @return hasPermission_ Returns if the address can call the function. + function hasPermission(address caller_, address target_, bytes4 selector_) + external + view + returns (bool hasPermission_); + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - Role Management + + /// @notice Creates a new role and adds initial members to it. + /// @dev Function access controlled by authorizer. + /// @dev The role of the admin has to be created already. + /// @param roleName_ The name of the role to create. + /// @param respectiveAdminRole_ The role ID of the admin role. + /// @param initialMembers_ The addresses of the initial members. + /// @return newRoleId_ The ID of the newly created role. + function createRole( + string memory roleName_, + bytes32 respectiveAdminRole_, + address[] memory initialMembers_ + ) external returns (bytes32 newRoleId_); + + /// @notice Changes the name of a role. + /// @dev Labels are emitted as events and are therefore not accessible + /// on-chain. + /// @dev Function access controlled by authorizer. + /// @dev The role has to be created already. + /// @param roleId_ The ID of the role to change the name of. + /// @param newRoleName_ The new name of the role. + function labelRole(bytes32 roleId_, string memory newRoleName_) external; + + /// @notice Transfer the admin rights to a given role. + /// @dev Only callable by the Admin of the role. + /// @dev The role has to be created already. + /// @dev The admin of the roleId_ can not be burned. + /// @param roleId_ The role on which to peform the admin transfer. + /// @param newAdminRoleId_ The new role to which to transfer admin + /// access to. + function transferAdminRole(bytes32 roleId_, bytes32 newAdminRoleId_) + external; + + /// @notice Burns the admin of the given roleId. + /// @dev Only callable by the Admin of the role. + /// @dev The role has to be created already. + /// @dev Does nothing if the admin was already burned. + /// @param roleId_ The role for which to burn the admin. + function burnRoleAdmin(bytes32 roleId_) external; + + // ------------------------------------------------------------------------ + // Mutating - Authorization + + /// @notice Adds a new permission to the given roleId to call the given + /// function in the target contract. + /// @dev Function access controlled by authorizer. + /// @dev The roleId must have already been created. + /// @dev Does nothing if the roleId permission is already added to the + ///function. + /// @param target_ The address of the target contract. + /// @param selector_ The selector of the function. + /// @param roleId_ The roleId that will receive the permission. + function addAccessPermission( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) external; + + /// @notice Removes a permission from the given roleid to call the given + /// function in the target contract. + /// @dev Function access controlled by authorizer. + /// @dev Does nothing if the roleId is not linked to the function. + /// @param target_ The address of the target contract. + /// @param selector_ The selector of the function. + /// @param roleId_ The roleId to remove. + function removeAccessPermission( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) external; + + // ------------------------------------------------------------------------ + // Mutating - Mixed Utility + + /// @notice Creates a new role, adds initial members to it and adds + /// permission to call to the respective functions. + /// @dev Function access controlled by authorizer. + /// @dev The role of the admin has to be created already. + /// @dev The array of targets corresponds with the two dimensional array + /// of selectors. The first position of targets therefore is + /// assigned to the first position of the selector array. The + /// second dimension of the selector array contains all the + /// function selectors of the target, which get the newly created + /// role added as a permissioned role. + /// @dev The target contracts array must have the same length as the + /// selector array. + /// @param roleName_ The name of the role to create. + /// @param respectiveAdminRole_ The role ID of the admin role. + /// @param initialMembers_ The addresses of the initial members. + /// @param targets_ The addresses of the target contracts. + /// @param selectors_ The selectors of the functions. + /// @return newRoleId_ The ID of the newly created role. + function createRoleAndAddAccessPermissions( + string memory roleName_, + bytes32 respectiveAdminRole_, + address[] memory initialMembers_, + address[] memory targets_, + bytes4[][] memory selectors_ + ) external returns (bytes32 newRoleId_); +} diff --git a/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol b/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol deleted file mode 100644 index c3c910b03..000000000 --- a/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol +++ /dev/null @@ -1,425 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IAUT_EXT_VotingRoles_v1} from - "src/modules/authorizer/role/interfaces/IAUT_EXT_VotingRoles_v1.sol"; - -// Internal Dependencies -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -/** - * @title Inverter Voting Role Manager - * - * @notice Facilitates voting and motion management within the Inverter Network, - * allowing designated voters to participate in governance through proposals, - * voting, and execution of decisions. - * - * @dev Supports setting thresholds for decision-making, managing voter lists, - * creating motions, casting votes, and executing actions based on collective - * decisions. This structure enhances governance transparency and efficacy. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ - -contract AUT_EXT_VotingRoles_v1 is IAUT_EXT_VotingRoles_v1, Module_v1 { - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(Module_v1) - returns (bool) - { - return interfaceId == type(IAUT_EXT_VotingRoles_v1).interfaceId - || super.supportsInterface(interfaceId); - } - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Reverts if caller is not the module itself. - modifier onlySelf() { - if (_msgSender() != address(this)) { - revert Module__CallerNotAuthorized( - bytes32("onlySelf"), _msgSender() - ); - } - _; - } - - /// @dev Reverts if caller is not a voter. - modifier onlyVoter() { - if (!isVoter[_msgSender()]) { - revert Module__VotingRoleManager__CallerNotVoter(); - } - _; - } - - /// @dev Reverts if voter address is invalid. - /// @param voter The address to check. - modifier isValidVoterAddress(address voter) { - if ( - voter == address(0) || voter == address(this) - || voter == address(orchestrator()) - ) { - revert Module__VotingRoleManager__InvalidVoterAddress(); - } - _; - } - - //-------------------------------------------------------------------------- - // Constants - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public constant MAX_VOTING_DURATION = 2 weeks; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public constant MIN_VOTING_DURATION = 1 days; - - //-------------------------------------------------------------------------- - // Storage - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - mapping(address => bool) public isVoter; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - mapping(bytes32 => Motion) public motions; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public motionCount; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public voterCount; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public threshold; - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - uint public voteDuration; - - /// @dev Storage gap for future upgrades. - uint[50] private __gap; - - //-------------------------------------------------------------------------- - // Initialization - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata, - bytes memory configData - ) external override initializer { - __Module_init(orchestrator_, metadata); - - // Decode configData to list of voters, the required threshold, and the - // voting duration. - address[] memory voters; - uint threshold_; - uint voteDuration_; - (voters, threshold_, voteDuration_) = - abi.decode(configData, (address[], uint, uint)); - - uint votersLen = voters.length; - - // Revert if list of voters is empty. - if (votersLen == 0) { - revert Module__VotingRoleManager__EmptyVoters(); - } - - // Revert if the threshold is set incorrectly - _validateThreshold(votersLen, threshold_); - - // Revert if votingDuration outside of bounds. - if ( - voteDuration_ < MIN_VOTING_DURATION - || voteDuration_ > MAX_VOTING_DURATION - ) { - revert Module__VotingRoleManager__InvalidVotingDuration(); - } - - // Write voters to storage. - address voter; - for (uint i; i < votersLen; ++i) { - voter = voters[i]; - - if ( - voter == address(0) || voter == address(this) - || voter == address(orchestrator()) - ) { - revert Module__VotingRoleManager__InvalidVoterAddress(); - } - - if (isVoter[voter]) { - revert Module__VotingRoleManager__IsAlreadyVoter(); - } - - isVoter[voter] = true; - emit VoterAdded(voter); - } - - // Write count of voters to storage. - voterCount = votersLen; - - // Write threshold to storage. - threshold = threshold_; - emit ThresholdUpdated(0, threshold_); - - // Write voteDuration to storage. - voteDuration = voteDuration_; - emit VoteDurationUpdated(0, voteDuration_); - } - - //-------------------------------------------------------------------------- - // Data Retrieval Functions - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function getReceipt(bytes32 _ID, address voter) - public - view - returns (Receipt memory) - { - Receipt memory _r = motions[_ID].receipts[voter]; - - return (_r); - } - - //-------------------------------------------------------------------------- - // Configuration Functions - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function setThreshold(uint newThreshold) public onlySelf { - // Revert if the threshold is set incorrectly - _validateThreshold(voterCount, newThreshold); - - emit ThresholdUpdated(threshold, newThreshold); - threshold = newThreshold; - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function setVotingDuration(uint newVoteDuration) external onlySelf { - // Revert if votingDuration outside of bounds. - if ( - newVoteDuration < MIN_VOTING_DURATION - || newVoteDuration > MAX_VOTING_DURATION - ) { - revert Module__VotingRoleManager__InvalidVotingDuration(); - } - - emit VoteDurationUpdated(voteDuration, newVoteDuration); - voteDuration = newVoteDuration; - } - - //-------------------------------------------------------------------------- - // Voter Management Functions - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function addVoter(address who) public onlySelf isValidVoterAddress(who) { - if (!isVoter[who]) { - isVoter[who] = true; - unchecked { - ++voterCount; - } - emit VoterAdded(who); - } - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function addVoterAndUpdateThreshold(address who, uint newThreshold) - external - { - // Add the new voter - addVoter(who); - - // Set the new threshold (also validates it) - setThreshold(newThreshold); - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function removeVoter(address who) public onlySelf { - _removeVoter(who); - - // Revert if the threshold would be invalid after this - _validateThreshold(voterCount, threshold); - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function removeVoterAndUpdateThreshold(address who, uint newThreshold) - external - onlySelf - { - _removeVoter(who); - - // Set the new threshold (also validates it) - setThreshold(newThreshold); - } - - //-------------------------------------------------------------------------- - // Internal Functions - - /// @dev Removes a voter from the list of voters. - /// @param who The address of the voter to remove. - function _removeVoter(address who) internal { - // Revert if trying to remove the last voter - if (voterCount == 1) { - revert Module__VotingRoleManager__EmptyVoters(); - } - - if (isVoter[who]) { - delete isVoter[who]; - unchecked { - --voterCount; - } - emit VoterRemoved(who); - } - } - - //-------------------------------------------------------------------------- - // Governance Functions - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function createMotion(address target, bytes calldata action) - external - onlyVoter - returns (bytes32) - { - // Cache motion's id. - bytes32 motionId = - keccak256(abi.encodePacked(target, action, motionCount)); - - // Get pointer to motion. - // Note that the motion instance is uninitialized. - Motion storage motion_ = motions[motionId]; - - // Initialize motion. - motion_.target = target; - motion_.action = action; - - motion_.startTimestamp = block.timestamp; - motion_.endTimestamp = block.timestamp + voteDuration; - motion_.requiredThreshold = threshold; - - emit MotionCreated(motionId); - - // Increase the motion count. - unchecked { - ++motionCount; - } - - return motionId; - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function castVote(bytes32 motionId, uint8 support) external onlyVoter { - // Revert if support invalid. - // 0 = for - // 1 = against - // 2 = abstain - if (support > 2) { - revert Module__VotingRoleManager__InvalidSupport(); - } - - // Get pointer to the motion. - Motion storage motion_ = motions[motionId]; - - // Revert if motionID invalid - if (motion_.startTimestamp == 0) { - revert Module__VotingRoleManager__InvalidMotionId(); - } - - // Revert if voting duration exceeded - if (block.timestamp > motion_.endTimestamp) { - revert Module__VotingRoleManager__MotionVotingPhaseClosed(); - } - - // Revert if caller attempts to double vote. - if (motion_.receipts[_msgSender()].hasVoted) { - revert Module__VotingRoleManager__AttemptedDoubleVote(); - } - - if (support == 0) { - unchecked { - ++motion_.forVotes; - } - } else if (support == 1) { - unchecked { - ++motion_.againstVotes; - } - } else if (support == 2) { - unchecked { - ++motion_.abstainVotes; - } - } - - address voter = _msgSender(); - - motion_.receipts[voter] = Receipt(true, support); - - emit VoteCast(motionId, voter, support); - } - - /// @inheritdoc IAUT_EXT_VotingRoles_v1 - function executeMotion(bytes32 motionId) external { - // Get pointer to the motion. - Motion storage motion_ = motions[motionId]; - - // Revert if motionId invalid. - if (motion_.startTimestamp == 0) { - revert Module__VotingRoleManager__InvalidMotionId(); - } - - // Revert if voting duration not exceeded. - if (block.timestamp <= motion_.endTimestamp) { - revert Module__VotingRoleManager__MotionInVotingPhase(); - } - - // Revert if necessary threshold was not reached - if (motion_.forVotes < motion_.requiredThreshold) { - revert Module__VotingRoleManager__ThresholdNotReached(); - } - - // Revert if motion already executed. - if (motion_.executedAt != 0) { - revert Module__VotingRoleManager__MotionAlreadyExecuted(); - } - - // Updating executedAt here to prevent reentrancy - motion_.executedAt = block.timestamp; - - // Execute `action` on `target`. - bool result; - bytes memory returnData; - (result, returnData) = motion_.target.call(motion_.action); - - // Save execution's result. - motion_.executionResult = result; - motion_.executionReturnData = returnData; - - emit MotionExecuted(motionId); - } - - //-------------------------------------------------------------------------- - // Internal - - /// @dev Internal function to validate the threshold. - /// @param _voters The number of voters. - /// @param _threshold The threshold. - function _validateThreshold(uint _voters, uint _threshold) internal pure { - // Revert if one of these conditions is met - // - Threshold is higher than the amount of voters - // - There are less than 3 voters and the threshold is set to 0 - // - There are 3 or more voters and the threshold is less than 2 - if ( - _threshold > _voters || (_voters >= 3 && _threshold < 2) - || (_voters < 3 && _threshold == 0) - ) { - revert Module__VotingRoleManager__InvalidThreshold(); - } - } -} diff --git a/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol b/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol new file mode 100644 index 000000000..f923885cb --- /dev/null +++ b/src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal Interfaces +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IAUT_EXT_VotingRoles_v2} from + "src/modules/authorizer/extensions/interfaces/IAUT_EXT_VotingRoles_v2.sol"; + +// Internal Dependencies +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; +/** + * @title Inverter Voting Role Manager + * + * @notice Facilitates voting and motion management within the Inverter + * Network, allowing designated voters to participate in governance + * through proposals, voting, and execution of decisions. + * + * @dev Supports setting thresholds for decision-making, managing voter + * lists, creating _motions, casting votes, and executing actions + * based on collective decisions. This structure enhances governance + * transparency and efficacy. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ + +contract AUT_EXT_VotingRoles_v2 is IAUT_EXT_VotingRoles_v2, Module_v2 { + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v2) + returns (bool isInterfaceId_) + { + return interfaceId_ == type(IAUT_EXT_VotingRoles_v2).interfaceId + || super.supportsInterface(interfaceId_); + } + + //========================================================================== + // Modifiers + + /// @notice Reverts if caller is not the module itself. + modifier onlySelf() { + if (_msgSender() != address(this)) { + revert Module__VotingRoleManager__OnlySelfCallAllowed(); + } + _; + } + + /// @notice Reverts if caller is not a voter. + modifier onlyVoter() { + if (!_isVoter[_msgSender()]) { + revert Module__VotingRoleManager__CallerNotVoter(); + } + _; + } + + /// @notice Reverts if voter address is invalid. + /// @param voter_ The address to check. + modifier isValidVoterAddress(address voter_) { + if ( + voter_ == address(0) || voter_ == address(this) + || voter_ == address(orchestrator()) + ) { + revert Module__VotingRoleManager__InvalidVoterAddress(); + } + _; + } + + //========================================================================== + // Constants + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + uint public constant MAX_VOTING_DURATION = 2 weeks; + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + uint public constant MIN_VOTING_DURATION = 1 days; + + //========================================================================== + // Storage + + /// @notice Mapping that stores if an address is a voter. + mapping(address voter => bool isVoter) internal _isVoter; + + /// @notice Mapping that stores the motions. + mapping(bytes32 motionId => Motion motion) internal _motions; + + /// @notice The counter for motions. + uint internal _motionCount; + + /// @notice The counter for voters. + uint internal _voterCount; + + /// @notice The threshold for motions. + uint internal _threshold; + + /// @notice The duration for voting. + uint internal _voteDuration; + + /// @dev Storage gap for future upgrades. + uint[50] private __gap; + + //========================================================================== + // Initialization + + /// @inheritdoc Module_v2 + function init( + IOrchestrator_v2 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override initializer { + __Module_init(orchestrator_, metadata_); + + // Decode configData to list of voters, the required threshold, and the + // voting duration. + address[] memory voters; + uint threshold; + uint voteDuration; + (voters, threshold, voteDuration) = + abi.decode(configData_, (address[], uint, uint)); + + uint votersLen = voters.length; + + // Revert if list of voters is empty. + if (votersLen == 0) { + revert Module__VotingRoleManager__EmptyVoters(); + } + + // Revert if the threshold is set incorrectly. + _validateThreshold(votersLen, threshold); + + // Revert if votingDuration outside of bounds. + if ( + voteDuration < MIN_VOTING_DURATION + || voteDuration > MAX_VOTING_DURATION + ) { + revert Module__VotingRoleManager__InvalidVotingDuration(); + } + + // Write voters to storage. + address voter; + for (uint i; i < votersLen; ++i) { + voter = voters[i]; + + if ( + voter == address(0) || voter == address(this) + || voter == address(orchestrator()) + ) { + revert Module__VotingRoleManager__InvalidVoterAddress(); + } + + if (_isVoter[voter]) { + revert Module__VotingRoleManager__IsAlreadyVoter(); + } + + _isVoter[voter] = true; + emit VoterAdded(voter); + } + + // Write count of voters to storage. + _voterCount = votersLen; + + // Write threshold to storage. + _threshold = threshold; + emit ThresholdUpdated(0, threshold); + + // Write _voteDuration to storage. + _voteDuration = voteDuration; + emit VoteDurationUpdated(0, voteDuration); + } + + //========================================================================== + // Getter Functions + + //-------------------------------------------------------------------------- + // Getter - State Access Functions + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function isVoter(address who_) external view returns (bool isVoter_) { + return _isVoter[who_]; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getMotion(bytes32 id_) + external + view + returns ( + address target_, + bytes memory action_, + uint startTimestamp_, + uint endTimestamp_, + uint requiredThreshold_, + uint forVotes_, + uint againstVotes_, + uint abstainVotes_, + uint executedAt_, + bool executionResult_, + bytes memory executionReturnData_ + ) + { + Motion storage motion_ = _motions[id_]; + + return ( + motion_.target, + motion_.action, + motion_.startTimestamp, + motion_.endTimestamp, + motion_.requiredThreshold, + motion_.forVotes, + motion_.againstVotes, + motion_.abstainVotes, + motion_.executedAt, + motion_.executionResult, + motion_.executionReturnData + ); + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getMotionCount() external view returns (uint motionCount_) { + return _motionCount; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getVoterCount() external view returns (uint voterCount_) { + return _voterCount; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getThreshold() external view returns (uint threshold_) { + return _threshold; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getVoteDuration() external view returns (uint voteDuration_) { + return _voteDuration; + } + + //-------------------------------------------------------------------------- + // Data Retrieval Functions + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function getReceipt(bytes32 id_, address voter_) + public + view + returns (Receipt memory receipt_) + { + Receipt memory r = _motions[id_].receipts[voter_]; + + return (r); + } + + //========================================================================== + // Mutating Functions + + //-------------------------------------------------------------------------- + // Mutating - Configuration Functions + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function setThreshold(uint newThreshold_) public onlySelf { + // Revert if the threshold is set incorrectly. + _validateThreshold(_voterCount, newThreshold_); + + emit ThresholdUpdated(_threshold, newThreshold_); + _threshold = newThreshold_; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function setVotingDuration(uint newVoteDuration_) external onlySelf { + // Revert if votingDuration outside of bounds. + if ( + newVoteDuration_ < MIN_VOTING_DURATION + || newVoteDuration_ > MAX_VOTING_DURATION + ) { + revert Module__VotingRoleManager__InvalidVotingDuration(); + } + + emit VoteDurationUpdated(_voteDuration, newVoteDuration_); + _voteDuration = newVoteDuration_; + } + + //-------------------------------------------------------------------------- + // Mutating - Voter Management Functions + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function addVoter(address who_) public onlySelf isValidVoterAddress(who_) { + if (!_isVoter[who_]) { + _addVoter(who_); + // Validate threshold after adding voter. + _validateThreshold(_voterCount, _threshold); + } + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function addVoterAndUpdateThreshold(address who_, uint newThreshold_) + external + { + if (!_isVoter[who_]) { + // Add the new voter. + _addVoter(who_); + } + // Set the new threshold (also validates it). + setThreshold(newThreshold_); + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function removeVoter(address who_) public onlySelf { + _removeVoter(who_); + + // Revert if the threshold would be invalid after this. + _validateThreshold(_voterCount, _threshold); + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function removeVoterAndUpdateThreshold(address who_, uint newThreshold_) + external + onlySelf + { + _removeVoter(who_); + + // Set the new threshold (also validates it). + setThreshold(newThreshold_); + } + + //-------------------------------------------------------------------------- + // Mutating - Governance Functions + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function createMotion(address target_, bytes calldata action_) + external + onlyVoter + returns (bytes32 motionId_) + { + // Cache motion's id. + bytes32 motionId = + keccak256(abi.encodePacked(target_, action_, _motionCount)); + + // Get pointer to motion. + // Note that the motion instance is uninitialized. + Motion storage motion_ = _motions[motionId]; + + // Initialize motion. + motion_.target = target_; + motion_.action = action_; + + motion_.startTimestamp = block.timestamp; + motion_.endTimestamp = block.timestamp + _voteDuration; + motion_.requiredThreshold = _threshold; + + emit MotionCreated(motionId); + + // Increase the motion count. + unchecked { + ++_motionCount; + } + + return motionId; + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function castVote(bytes32 motionId_, uint8 support_) external onlyVoter { + // Revert if support invalid. + // - 0 = for + // - 1 = against + // - 2 = abstain + if (support_ > 2) { + revert Module__VotingRoleManager__InvalidSupport(); + } + + // Get pointer to the motion. + Motion storage motion_ = _motions[motionId_]; + + // Revert if motionID invalid. + if (motion_.startTimestamp == 0) { + revert Module__VotingRoleManager__InvalidMotionId(); + } + + // Revert if voting duration exceeded. + if (block.timestamp > motion_.endTimestamp) { + revert Module__VotingRoleManager__MotionVotingPhaseClosed(); + } + + // Revert if caller attempts to double vote. + if (motion_.receipts[_msgSender()].hasVoted) { + revert Module__VotingRoleManager__AttemptedDoubleVote(); + } + + if (support_ == 0) { + unchecked { + ++motion_.forVotes; + } + } else if (support_ == 1) { + unchecked { + ++motion_.againstVotes; + } + } else if (support_ == 2) { + unchecked { + ++motion_.abstainVotes; + } + } + + address voter = _msgSender(); + + motion_.receipts[voter] = Receipt(true, support_); + + emit VoteCast(motionId_, voter, support_); + } + + /// @inheritdoc IAUT_EXT_VotingRoles_v2 + function executeMotion(bytes32 motionId_) external { + // Get pointer to the motion. + Motion storage motion_ = _motions[motionId_]; + + // Revert if motionId invalid. + if (motion_.startTimestamp == 0) { + revert Module__VotingRoleManager__InvalidMotionId(); + } + + // Revert if voting duration not exceeded. + if (block.timestamp <= motion_.endTimestamp) { + revert Module__VotingRoleManager__MotionInVotingPhase(); + } + + // Revert if necessary threshold was not reached. + if (motion_.forVotes < motion_.requiredThreshold) { + revert Module__VotingRoleManager__ThresholdNotReached(); + } + + // Revert if motion already executed. + if (motion_.executedAt != 0) { + revert Module__VotingRoleManager__MotionAlreadyExecuted(); + } + + // Updating executedAt here to prevent reentrancy. + motion_.executedAt = block.timestamp; + + // Execute `action` on `target`. + bool result; + bytes memory returnData; + (result, returnData) = motion_.target.call(motion_.action); + + // Save execution's result. + motion_.executionResult = result; + motion_.executionReturnData = returnData; + + emit MotionExecuted(motionId_); + } + + //========================================================================== + // Internal Functions + + /// @notice Removes a voter from the list of voters. + /// @param who_ The address of the voter to remove. + function _removeVoter(address who_) internal { + // Revert if trying to remove the last voter. + if (_voterCount == 1) { + revert Module__VotingRoleManager__EmptyVoters(); + } + + if (_isVoter[who_]) { + delete _isVoter[who_]; + unchecked { + --_voterCount; + } + emit VoterRemoved(who_); + } + } + + /// @notice Internal function to validate the threshold. + /// @param voters_ The number of voters. + /// @param threshold_ The threshold. + function _validateThreshold(uint voters_, uint threshold_) internal pure { + // Revert if one of these conditions is met: + // - Threshold is higher than the amount of voters + // - There are less than 3 voters and the threshold is set to 0 + // - There are 3 or more voters and the threshold is less than 2 + if ( + threshold_ > voters_ || (voters_ >= 3 && threshold_ < 2) + || (voters_ < 3 && threshold_ == 0) + ) { + revert Module__VotingRoleManager__InvalidThreshold(); + } + } + + /// @notice Internal function to add a voter to the list of voters. + /// @dev This function does not validate the threshold. + /// @param voter_ The address of the voter to add. + function _addVoter(address voter_) internal { + _isVoter[voter_] = true; + unchecked { + ++_voterCount; + } + + emit VoterAdded(voter_); + } +} diff --git a/src/modules/authorizer/extensions/interfaces/IAUT_EXT_VotingRoles_v2.sol b/src/modules/authorizer/extensions/interfaces/IAUT_EXT_VotingRoles_v2.sol new file mode 100644 index 000000000..daa5365fb --- /dev/null +++ b/src/modules/authorizer/extensions/interfaces/IAUT_EXT_VotingRoles_v2.sol @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +/** + * @title Inverter Voting Role Manager Interface + * + * @notice Facilitates voting and motion management within the Inverter + * Network, allowing designated voters to participate in governance + * through proposals, voting, and execution of decisions. + * + * @dev Supports setting thresholds for decision-making, managing voter + * lists, creating _motions, casting votes, and executing actions + * based on collective decisions. This structure enhances governance + * transparency and efficacy. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ +interface IAUT_EXT_VotingRoles_v2 { + // ======================================================================== + // Structs + + /// @notice A motion is a proposal to execute an action on a target + /// contract. + /// @param target The address of the contract to execute the action on. + /// @param action The action data to execute on the target contract. + /// @param startTimestamp The timestamp at which the motion starts. + /// @param endTimestamp The timestamp at which the motion ends. + /// @param requiredThreshold The required threshold of votes to pass the + /// motion. + /// @param forVotes The number of votes in favor of the motion. + /// @param againstVotes The number of votes against the motion. + /// @param abstainVotes The number of votes abstaining from the motion. + /// @param receipts The receipts of votes for the motion address. + /// @param executedAt The timestamp at which the motion was executed. + /// @param executionResult The result of the execution. + /// @param executionReturnData The return data of the execution. + struct Motion { + address target; + bytes action; + uint startTimestamp; + uint endTimestamp; + uint requiredThreshold; + uint forVotes; + uint againstVotes; + uint abstainVotes; + mapping(address => Receipt) receipts; + uint executedAt; + bool executionResult; + bytes executionReturnData; + } + + /// @notice A receipt is a vote cast for a motion. + /// @param hasVoted Whether the voter has already voted. + /// @param support The value that indicates wether the voter supports the + /// motion. + struct Receipt { + bool hasVoted; + uint8 support; + } + // ======================================================================== + // Errors + + /// @notice This function is only callable by a motion of this contract. + error Module__VotingRoleManager__OnlySelfCallAllowed(); + + /// @notice The action would leave an empty voter list. + error Module__VotingRoleManager__EmptyVoters(); + + /// @notice The supplied voter address is invalid. + error Module__VotingRoleManager__InvalidVoterAddress(); + + /// @notice The threshold cannot exceed the amount of voters. + /// or be too low to be considered safe. + error Module__VotingRoleManager__InvalidThreshold(); + + /// @notice The supplied voting duration is invalid. + error Module__VotingRoleManager__InvalidVotingDuration(); + + /// @notice The function can only be called by a voter. + error Module__VotingRoleManager__CallerNotVoter(); + + /// @notice The address is already a voter. + error Module__VotingRoleManager__IsAlreadyVoter(); + + /// @notice The value given as vote is invalid. + error Module__VotingRoleManager__InvalidSupport(); + + /// @notice The supplied ID is referencing a motion that doesn't exist. + error Module__VotingRoleManager__InvalidMotionId(); + + /// @notice A user cannot vote twice. + error Module__VotingRoleManager__AttemptedDoubleVote(); + + /// @notice A motion cannot be executed if the voting duration hasn't + /// passed. + error Module__VotingRoleManager__MotionInVotingPhase(); + + /// @notice A motion cannot be voted on if the duration has been exceeded. + error Module__VotingRoleManager__MotionVotingPhaseClosed(); + + /// @notice A motion cannot be executed twice. + error Module__VotingRoleManager__MotionAlreadyExecuted(); + + /// @notice A motion cannot be executed if it didn't reach the threshold. + error Module__VotingRoleManager__ThresholdNotReached(); + + // ======================================================================== + // Events + + /// @notice Event emitted when a new voter address gets added. + /// @param who_ The added address. + event VoterAdded(address indexed who_); + + /// @notice Event emitted when a voter address gets removed. + /// @param who_ The removed address. + event VoterRemoved(address indexed who_); + + /// @notice Event emitted when the required threshold changes. + /// @param oldThreshold_ The old threshold. + /// @param newThreshold_ The new threshold. + event ThresholdUpdated(uint oldThreshold_, uint newThreshold_); + + /// @notice Event emitted when the voting duration changes. + /// @param oldVotingDuration_ The old voting duration. + /// @param newVotingDuration_ The new voting duration. + event VoteDurationUpdated(uint oldVotingDuration_, uint newVotingDuration_); + + /// @notice Event emitted when a motion is created. + /// @param motionId_ The motion ID. + event MotionCreated(bytes32 indexed motionId_); + + /// @notice Event emitted when a vote is cast for a motion. + /// @param motionId_ The motion ID. + /// @param voter_ The address of a voter. + /// @param support_ Value that indicates how the voter supports the motion. + event VoteCast( + bytes32 indexed motionId_, + address indexed voter_, + uint8 indexed support_ + ); + + /// @notice Event emitted when a motion is executed. + /// @param motionId_ The motion ID. + event MotionExecuted(bytes32 indexed motionId_); + + // ======================================================================== + // Public Getter Functions + + //-------------------------------------------------------------------------- + // Getter - Constants + + /// @notice The maximum voting duration. + /// @return maxVotingDuration_ The maximum voting duration. + function MAX_VOTING_DURATION() + external + view + returns (uint maxVotingDuration_); + + /// @notice The minimum voting duration. + /// @return minVotingDuration_ The minimum voting duration. + function MIN_VOTING_DURATION() + external + view + returns (uint minVotingDuration_); + + //-------------------------------------------------------------------------- + // Getter - State Access Functions + + /// @notice Checks whether an address is a voter. + /// @param who_ The address to check. + /// @return isVoter_ Whether the address is a voter. + function isVoter(address who_) external view returns (bool isVoter_); + + /// @notice Gets the motion data. + /// @param motionId_ The ID of the motion. + /// @return target_ The address of the contract to execute the action on. + /// @return action_ The action data to execute on the target contract. + /// @return startTimestamp_ The timestamp at which the motion starts. + /// @return endTimestamp_ The timestamp at which the motion ends. + /// @return requiredThreshold_ The required threshold of votes to pass the + /// motion. + /// @return forVotes_ The number of votes in favor of the motion. + /// @return againstVotes_ The number of votes against the motion. + /// @return abstainVotes_ The number of votes abstaining from the motion. + /// @return executedAt_ The timestamp at which the motion was executed. + /// @return executionResult_ The result of the execution. + /// @return executionReturnData_ The return data of the execution. + function getMotion(bytes32 motionId_) + external + view + returns ( + address target_, + bytes memory action_, + uint startTimestamp_, + uint endTimestamp_, + uint requiredThreshold_, + uint forVotes_, + uint againstVotes_, + uint abstainVotes_, + uint executedAt_, + bool executionResult_, + bytes memory executionReturnData_ + ); + + /// @notice Gets the number of motions. + /// @return motionCount_ The number of motions. + function getMotionCount() external view returns (uint motionCount_); + + /// @notice Gets the number of voters. + /// @return voterCount_ The number of voters. + function getVoterCount() external view returns (uint voterCount_); + + /// @notice Gets the threshold. + /// @return threshold_ The threshold. + function getThreshold() external view returns (uint threshold_); + + /// @notice Gets the voting duration. + /// @return voteDuration_ The voting duration. + function getVoteDuration() external view returns (uint voteDuration_); + + /// @notice Gets the receipt of a voter for a motion. + /// @param id_ The ID of the motion. + /// @param voter_ The address of the voter. + /// @return receipt_ The receipt of the voter. + function getReceipt(bytes32 id_, address voter_) + external + view + returns (Receipt memory receipt_); + + //========================================================================== + // Mutating Functions + + //-------------------------------------------------------------------------- + // Mutating - Configuration Functions + + /// @notice Sets the threshold. + /// @param newThreshold_ The new threshold. + function setThreshold(uint newThreshold_) external; + + /// @notice Sets the voting duration. + /// @param newVoteDuration_ The new voting duration. + function setVotingDuration(uint newVoteDuration_) external; + + //-------------------------------------------------------------------------- + // Mutating - Voter Management Functions + + /// @notice Adds a voter. + /// @dev Beware that adding a voter has implications for already + /// existing motions and might change how easy a threshold can be + /// reached / a motion can be executed. + /// @param who_ The address to add. + function addVoter(address who_) external; + + /// @notice Adds a voter and updates the threshold. + /// @dev Beware that adding a voter has implications for already + /// existing motions and might change how easy a threshold can be + /// reached / a motion can be executed. + /// @param who_ The address to add. + /// @param newThreshold_ The new threshold. + function addVoterAndUpdateThreshold(address who_, uint newThreshold_) + external; + + /// @notice Removes a voter. + /// @dev Beware that removing a voter has implications for already + /// existing motions and might change how easy a threshold can be + /// reached / a motion can be executed. This can even lead to a + /// threshold in which a motion can't be executed anymore. + /// @param who_ The address to remove. + function removeVoter(address who_) external; + + /// @notice Removes a voter and updates the threshold. + /// @dev Beware that removing a voter has implications for already + /// existing motions and might change how easy a threshold can be + /// reached / a motion can be executed. This can even lead to a + /// threshold in which a motion can't be executed anymore. + /// @param who_ The address to remove. + /// @param newThreshold_ The new threshold. + function removeVoterAndUpdateThreshold(address who_, uint newThreshold_) + external; + + //-------------------------------------------------------------------------- + // Mutating - Governance Functions + + /// @notice Creates a motion. + /// @param target_ The address of the contract to execute the action on. + /// @param action_ The action data to execute on the target contract. + /// @return motionId_ The ID of the created motion. + function createMotion(address target_, bytes calldata action_) + external + returns (bytes32 motionId_); + + /// @notice Casts a vote for a motion. + /// @param motionId_ The ID of the motion. + /// @param support_ The value that indicates wether the voter supports the + /// motion. + function castVote(bytes32 motionId_, uint8 support_) external; + + /// @notice Executes a motion. + /// @param motionId_ The ID of the motion. + function executeMotion(bytes32 motionId_) external; +} diff --git a/src/modules/authorizer/role/AUT_Roles_v1.sol b/src/modules/authorizer/role/AUT_Roles_v1.sol deleted file mode 100644 index 420736f50..000000000 --- a/src/modules/authorizer/role/AUT_Roles_v1.sol +++ /dev/null @@ -1,328 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -// Internal Dependencies -import {Module_v1} from "src/modules/base/Module_v1.sol"; - -// External Dependencies -import {ERC165Upgradeable} from - "@oz-up/utils/introspection/ERC165Upgradeable.sol"; -import { - ERC2771ContextUpgradeable, - ContextUpgradeable -} from "@oz-up/metatx/ERC2771ContextUpgradeable.sol"; -import {AccessControlEnumerableUpgradeable} from - "@oz-up/access/extensions/AccessControlEnumerableUpgradeable.sol"; - -/** - * @title Inverter Roles Authorizer - * - * @notice Provides a robust access control mechanism for managing roles and permissions - * across different modules within the Inverter Network, ensuring secure and - * controlled access to critical functionalities. - * - * @dev Extends {AccessControlEnumerableUpgradeable} and integrates with {Module_v1} to - * offer fine-grained access control through role-based permissions. Utilizes - * ERC2771 for meta-transactions to enhance module interaction experiences. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ -contract AUT_Roles_v1 is - IAuthorizer_v1, - AccessControlEnumerableUpgradeable, - Module_v1 -{ - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(Module_v1, AccessControlEnumerableUpgradeable) - returns (bool) - { - return interfaceId == type(IAuthorizer_v1).interfaceId - || super.supportsInterface(interfaceId); - } - - //-------------------------------------------------------------------------- - // Storage - /// @notice The role that is used as a placeholder for a burned admin role. - bytes32 public constant BURN_ADMIN_ROLE = - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - /// @dev Storage gap for future upgrades. - uint[50] private __gap; - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Verifies that the caller is an active module. - /// @param module The address of the module. - modifier onlyModule(address module) { - if (!orchestrator().isModule(module)) { - revert Module__Authorizer__NotActiveModule(module); - } - _; - } - - /// @dev Verifies that the admin being removed is not the last one. - /// @param role The id number of the role. - modifier notLastAdmin(bytes32 role) { - if ( - role == DEFAULT_ADMIN_ROLE - && getRoleMemberCount(DEFAULT_ADMIN_ROLE) <= 1 - ) { - revert Module__Authorizer__AdminRoleCannotBeEmpty(); - } - _; - } - - /// @dev Verifies that the admin being added is not the {Orchestrator_v1}. - /// @param role The id number of the role. - /// @param who The user we want to check on. - modifier noSelfAdmin(bytes32 role, address who) { - if (role == DEFAULT_ADMIN_ROLE && who == address(orchestrator())) { - revert Module__Authorizer__OrchestratorCannotHaveAdminRole(); - } - _; - } - - //-------------------------------------------------------------------------- - // Initialization - - /// @inheritdoc Module_v1 - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata, - bytes memory configData - ) external override initializer { - __Module_init(orchestrator_, metadata); - - (address initialAdmin) = abi.decode(configData, (address)); - - __RoleAuthorizer_init(initialAdmin); - } - - /// @notice Initializes the role authorizer. - /// @param initialAdmin The initial admin of the role authorizer. - function __RoleAuthorizer_init(address initialAdmin) - internal - onlyInitializing - { - if (initialAdmin == address(0)) { - revert Module__Authorizer__InvalidInitialAdmin(); - } - - // Note about DEFAULT_ADMIN_ROLE: The Admin of the workflow holds the DEFAULT_ADMIN_ROLE, and has admin - // privileges on all Modules in the contract. - // It is defined in the AccessControl contract and identified with bytes32("0x00") - // Modules can opt out of this on a per-role basis by setting the admin role to "BURN_ADMIN_ROLE". - - // make the BURN_ADMIN_ROLE immutable - _setRoleAdmin(BURN_ADMIN_ROLE, BURN_ADMIN_ROLE); - - // set the initial admin as the DEFAULT_ADMIN_ROLE - _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin); - } - - //-------------------------------------------------------------------------- - // Public functions - - /// @inheritdoc IAuthorizer_v1 - function checkForRole(bytes32 role, address who) - external - view - virtual - returns (bool) - { - return hasRole(role, who); - } - - /// @inheritdoc IAuthorizer_v1 - function generateRoleId(address module, bytes32 role) - public - pure - returns (bytes32) - { - // Generate Role ID from module and role - return keccak256(abi.encodePacked(module, role)); - } - - /// @inheritdoc IAuthorizer_v1 - function grantRoleFromModule(bytes32 role, address target) - external - onlyModule(_msgSender()) - { - bytes32 roleId = generateRoleId(_msgSender(), role); - _grantRole(roleId, target); - } - - /// @inheritdoc IAuthorizer_v1 - function grantRoleFromModuleBatched( - bytes32 role, - address[] calldata targets - ) external onlyModule(_msgSender()) { - bytes32 roleId = generateRoleId(_msgSender(), role); - for (uint i = 0; i < targets.length; i++) { - _grantRole(roleId, targets[i]); - } - } - - /// @inheritdoc IAuthorizer_v1 - function revokeRoleFromModule(bytes32 role, address target) - external - onlyModule(_msgSender()) - { - bytes32 roleId = generateRoleId(_msgSender(), role); - _revokeRole(roleId, target); - } - - /// @inheritdoc IAuthorizer_v1 - function revokeRoleFromModuleBatched( - bytes32 role, - address[] calldata targets - ) external onlyModule(_msgSender()) { - bytes32 roleId = generateRoleId(_msgSender(), role); - for (uint i = 0; i < targets.length; i++) { - _revokeRole(roleId, targets[i]); - } - } - - /// @inheritdoc IAuthorizer_v1 - function transferAdminRole(bytes32 roleId, bytes32 newAdmin) - external - onlyRole(getRoleAdmin(roleId)) - { - _setRoleAdmin(roleId, newAdmin); - } - - /// @inheritdoc IAuthorizer_v1 - function burnAdminFromModuleRole(bytes32 role) - external - onlyModule(_msgSender()) - { - bytes32 roleId = generateRoleId(_msgSender(), role); - _setRoleAdmin(roleId, BURN_ADMIN_ROLE); - } - - /// @inheritdoc IAuthorizer_v1 - function grantGlobalRole(bytes32 role, address target) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { - bytes32 roleId = generateRoleId(address(orchestrator()), role); - _grantRole(roleId, target); - } - - /// @inheritdoc IAuthorizer_v1 - function grantGlobalRoleBatched(bytes32 role, address[] calldata targets) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { - bytes32 roleId = generateRoleId(address(orchestrator()), role); - for (uint i = 0; i < targets.length; i++) { - _grantRole(roleId, targets[i]); - } - } - - /// @inheritdoc IAuthorizer_v1 - function revokeGlobalRole(bytes32 role, address target) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { - bytes32 roleId = generateRoleId(address(orchestrator()), role); - _revokeRole(roleId, target); - } - - /// @inheritdoc IAuthorizer_v1 - function revokeGlobalRoleBatched(bytes32 role, address[] calldata targets) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { - bytes32 roleId = generateRoleId(address(orchestrator()), role); - for (uint i = 0; i < targets.length; i++) { - _revokeRole(roleId, targets[i]); - } - } - - /// @inheritdoc IAuthorizer_v1 - function getAdminRole() public pure returns (bytes32) { - return DEFAULT_ADMIN_ROLE; - } - - //-------------------------------------------------------------------------- - // Overloaded and overridden functions - - /// @notice Overrides {_revokeRole} to prevent having an empty `ADMIN` role. - /// @param role The id number of the role. - /// @param who The user we want to check on. - /// @return bool Returns if revoke has been succesful. - function _revokeRole(bytes32 role, address who) - internal - virtual - override - notLastAdmin(role) - returns (bool) - { - return super._revokeRole(role, who); - } - - /// @notice Overrides {_grantRole} to prevent having the {Orchestrator_v1} having the `OWNER` role. - /// @param role The id of the role. - /// @param who The user we want to check on. - /// @return bool Returns if grant has been succesful. - function _grantRole(bytes32 role, address who) - internal - virtual - override - noSelfAdmin(role, who) - returns (bool) - { - return super._grantRole(role, who); - } - - //-------------------------------------------------------------------------- - // ERC2771 Context Upgradeable - - /// Needs to be overridden, because they are imported via the AccessControlEnumerableUpgradeable as well. - function _msgSender() - internal - view - virtual - override(ContextUpgradeable, ERC2771ContextUpgradeable) - returns (address sender) - { - return ERC2771ContextUpgradeable._msgSender(); - } - - /// Needs to be overridden, because they are imported via the AccessControlEnumerableUpgradeable as well. - function _msgData() - internal - view - virtual - override(ContextUpgradeable, ERC2771ContextUpgradeable) - returns (bytes calldata) - { - return ERC2771ContextUpgradeable._msgData(); - } - - function _contextSuffixLength() - internal - view - virtual - override(ContextUpgradeable, ERC2771ContextUpgradeable) - returns (uint) - { - return ERC2771ContextUpgradeable._contextSuffixLength(); - } -} diff --git a/src/modules/authorizer/role/AUT_Roles_v2.sol b/src/modules/authorizer/role/AUT_Roles_v2.sol new file mode 100644 index 000000000..29638e5f0 --- /dev/null +++ b/src/modules/authorizer/role/AUT_Roles_v2.sol @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal Interfaces +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; + +// Internal Dependencies +import {Module_v2} from "src/modules/base/Module_v2.sol"; + +// External Dependencies +import {ERC165Upgradeable} from + "@oz-up/utils/introspection/ERC165Upgradeable.sol"; +import { + ERC2771ContextUpgradeable, + ContextUpgradeable +} from "@oz-up/metatx/ERC2771ContextUpgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@oz-up/access/extensions/AccessControlEnumerableUpgradeable.sol"; + +/** + * @title Inverter Roles Authorizer + * + * @notice Provides the access control mechanism for managing roles and + * permissions across different modules within the Inverter Network, + * ensuring secure and controlled access to critical functionalities. + * + * @dev Inherits functionality from: + * - IAuthorizer_v2: Implementation interface. + * - Module_v2: Inverter network base module functionality. + * - AccessControlEnumerableUpgradeable: Access control functionality. + * + * Key features: + * - Role creation and management. This includes the ability to + * create roles, revoke roles, assigning and revoking role + * admins, which can add and remove role members. + * - Role-based access control. This includes the ability to grant + * roles access to functions that implement the permissioned + * modifier. Functions can also be set to public access by + * adding the public role to the function permissions. * + * + * @custom:documentation See https://github.com/InverterNetwork/contracts/tree/dev/docs/src/modules/authorizer/role/AUT_Roles_v2.md + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ +contract AUT_Roles_v2 is + IAuthorizer_v2, + Module_v2, + AccessControlEnumerableUpgradeable +{ + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(Module_v2, AccessControlEnumerableUpgradeable) + returns (bool isInterfaceId_) + { + return interfaceId_ == type(IAuthorizer_v2).interfaceId + || super.supportsInterface(interfaceId_); + } + + // ======================================================================== + // Modifiers + + /// @notice Verifies that the roleId is not the default admin role. + /// @param roleId_ The id of the role. + modifier idNotDefaultAdmin(bytes32 roleId_) { + if (roleId_ == DEFAULT_ADMIN_ROLE) { + revert Module__Authorizer__CannotModifyAdminRoleAccess(); + } + _; + } + + /// @notice Verifies that the roleId is already existing. + /// @param roleId_ The id of the role. + modifier idExists(bytes32 roleId_) { + // If the given roleId is greater than the last assigned roleId, then + // it is not existing. + if (uint(roleId_) > _lastAssignedRoleId) { + revert Module__Authorizer__RoleIdNotExisting(); + } + _; + } + + // ======================================================================== + // Storage + + /// @notice The public role. + bytes32 public constant PUBLIC_ROLE = bytes32(uint(1)); + + /// @notice The burned admin role. + bytes32 public constant BURN_ADMIN_ROLE = + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + /// @notice Mapping that stores the role IDs that can be used to call + /// functions on a target contract. + /// @dev target The address of the target contract. + /// @dev selector The function selector of the function to call. + /// @dev roleIds The role IDs that can be used to call the function. + mapping(address target => mapping(bytes4 selector => bytes32[] roleIds)) + internal _permissions; + + /// @notice The counter for role IDs. + /// @dev This is used to generate unique role IDs for each role. + /// @dev Starts at 1, which symbolizes two roles: PUBLIC_ROLE and + /// DEFAULT_ADMIN_ROLE, but is immediately incremented when a role + /// is created. + uint internal _lastAssignedRoleId; + + /// @dev Storage gap for future upgrades. + uint[50] private __gap; + + // ======================================================================== + // Initialization + + /// @inheritdoc Module_v2 + function init( + IOrchestrator_v2 orchestrator_, + Metadata memory metadata_, + bytes memory configData_ + ) external override initializer { + __Module_init(orchestrator_, metadata_); + + (address initialAdmin) = abi.decode(configData_, (address)); + + __RoleAuthorizer_init(initialAdmin); + } + + /// @notice Initializes the role authorizer. + /// @param initialAdmin_ The initial admin of the role authorizer. + function __RoleAuthorizer_init(address initialAdmin_) + internal + onlyInitializing + { + if (initialAdmin_ == address(0)) { + revert Module__Authorizer__InvalidInitialAdmin(); + } + + // Start with 1 to account for the two native roles: + // DEFAULT_ADMIN_ROLE at 0 and PUBLIC_ROLE at 1. + _lastAssignedRoleId = 1; + + // Note about DEFAULT_ADMIN_ROLE: + // The admin of the workflow holds the DEFAULT_ADMIN_ROLE, and has + // admin privileges on all modules in the contract. + // It is defined in the AccessControl contract and identified with + // bytes32("0x00"). + // Modules can opt out of this on a per-role basis by setting the admin + // role to "BURN_ADMIN_ROLE". + + // make the BURN_ADMIN_ROLE immutable. + _setRoleAdmin(BURN_ADMIN_ROLE, BURN_ADMIN_ROLE); + + // set the initial admin as the DEFAULT_ADMIN_ROLE + _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin_); + } + + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Role Management + + /// @inheritdoc IAuthorizer_v2 + function getAdminRole() + external + pure + virtual + returns (bytes32 defaultAdminId_) + { + return DEFAULT_ADMIN_ROLE; + } + + // ------------------------------------------------------------------------ + // Getter - Authorization + + /// @inheritdoc IAuthorizer_v2 + function getPermissions(address target_, bytes4 selector_) + external + view + virtual + returns (bytes32[] memory permissions_) + { + permissions_ = _permissions[target_][selector_]; + } + + /// @inheritdoc IAuthorizer_v2 + function getLastAssignedRoleId() + external + view + virtual + returns (uint lastAssignedRoleId_) + { + lastAssignedRoleId_ = _lastAssignedRoleId; + } + + /// @inheritdoc IAuthorizer_v2 + function isRolePermissioned( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) public view virtual returns (bool isRolePermissioned_) { + bytes32[] memory permissions_ = _permissions[target_][selector_]; + for (uint i = 0; i < permissions_.length; i++) { + if (permissions_[i] == roleId_) { + return true; + } + } + return false; + } + + /// @inheritdoc IAuthorizer_v2 + function hasPermission(address caller_, address target_, bytes4 selector_) + external + view + virtual + returns (bool hasPermission_) + { + // If caller is the admin, they can call any function. + if (hasRole(DEFAULT_ADMIN_ROLE, caller_)) { + return true; + } + + bytes32[] memory roleIds = _permissions[target_][selector_]; + uint permissionLength = roleIds.length; + + // Go through each role and check if the caller has permission. + for (uint i = 0; i < permissionLength; i++) { + if ( + // Return true if the role is the public role + // or if the caller has the role. + roleIds[i] == PUBLIC_ROLE || hasRole(roleIds[i], caller_) + ) { + return true; + } + } + // Caller does not have any of the roles, so they cannot call the + // function. + return false; + } + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - Role Management + + /// @inheritdoc IAuthorizer_v2 + function createRole( + string memory roleName_, + bytes32 respectiveAdminRole_, + address[] memory initialMembers_ + ) + public + virtual + permissioned + idExists(respectiveAdminRole_) + returns (bytes32 newRoleId_) + { + newRoleId_ = bytes32(++_lastAssignedRoleId); + + emit RoleCreated(newRoleId_, roleName_); + + _setRoleAdmin(newRoleId_, respectiveAdminRole_); + + uint length = initialMembers_.length; + for (uint i = 0; i < length; i++) { + _grantRole(newRoleId_, initialMembers_[i]); + } + } + + /// @inheritdoc IAuthorizer_v2 + function labelRole(bytes32 roleId_, string memory newRoleName_) + external + virtual + permissioned + idExists(roleId_) + { + emit RoleLabeled(roleId_, newRoleName_); + } + + /// @inheritdoc IAuthorizer_v2 + function transferAdminRole(bytes32 roleId_, bytes32 newAdminRoleId_) + external + virtual + onlyRole(getRoleAdmin(roleId_)) + idExists(roleId_) + idExists(newAdminRoleId_) + { + _setRoleAdmin(roleId_, newAdminRoleId_); + } + + /// @inheritdoc IAuthorizer_v2 + function burnRoleAdmin(bytes32 roleId_) + external + virtual + onlyRole(getRoleAdmin(roleId_)) + idExists(roleId_) + { + // Burn admin from the role. + _setRoleAdmin(roleId_, BURN_ADMIN_ROLE); + emit RoleAdminBurned(roleId_); + } + + // ------------------------------------------------------------------------ + // Mutating - Authorization + + /// @inheritdoc IAuthorizer_v2 + function addAccessPermission( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) + public + virtual + permissioned + idNotDefaultAdmin(roleId_) + idExists(roleId_) + { + // if RoleId already has a permission, do nothing. + if (isRolePermissioned(target_, selector_, roleId_)) { + return; + } + + _permissions[target_][selector_].push(roleId_); + emit AccessPermissionAdded(target_, selector_, roleId_); + } + + /// @inheritdoc IAuthorizer_v2 + function removeAccessPermission( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) public virtual permissioned { + bytes32[] memory permissions = _permissions[target_][selector_]; + uint permissionsLength = permissions.length; + + for (uint i = 0; i < permissionsLength; i++) { + if (permissions[i] == roleId_) { + // Replace the element to be removed with the last one. + _permissions[target_][selector_][i] = + _permissions[target_][selector_][permissionsLength - 1]; + // Remove the last element. + _permissions[target_][selector_].pop(); + + // Emit Event and exit the function once the value is removed. + emit AccessPermissionRemoved(target_, selector_, roleId_); + return; + } + } + // Do nothing if the value is not found. + } + + // ------------------------------------------------------------------------ + // Mutating - Mixed Utility + + /// @inheritdoc IAuthorizer_v2 + function createRoleAndAddAccessPermissions( + string memory roleName_, + bytes32 respectiveAdminRole_, + address[] memory initialMembers_, + address[] memory targets_, + bytes4[][] memory selectors_ + ) + external + virtual + permissioned + idExists(respectiveAdminRole_) + returns (bytes32 newRoleId_) + { + uint targetsLength = targets_.length; + if (targetsLength != selectors_.length) { + revert Module__Authorizer__InvalidInputLength(); + } + + newRoleId_ = + createRole(roleName_, respectiveAdminRole_, initialMembers_); + + // Run through all target and selector combinations and add permission + // to role id. + + for (uint i = 0; i < targetsLength; i++) { + for (uint j = 0; j < selectors_[i].length; j++) { + addAccessPermission(targets_[i], selectors_[i][j], newRoleId_); + } + } + } + + // ======================================================================== + // Internal Functions + + // ------------------------------------------------------------------------ + // Internal - Upstream Function Implementations + + /// @notice Overrides {_grantRole} to make sure only existing roles can be + /// granted. + /// @param role_ The id of the role. + /// @param who_ The user we want to check on. + /// @return success_ Returns if grant has been successful. + function _grantRole(bytes32 role_, address who_) + internal + virtual + override + idExists(role_) + returns (bool success_) + { + return super._grantRole(role_, who_); + } + + //-------------------------------------------------------------------------- + // Internal - ERC2771 Context Upgradeable + + /// @dev Needs to be overridden, because they are imported via the + /// AccessControlEnumerableUpgradeable as well. + function _msgSender() + internal + view + virtual + override(ContextUpgradeable, ERC2771ContextUpgradeable) + returns (address sender_) + { + return ERC2771ContextUpgradeable._msgSender(); + } + + /// @dev Needs to be overridden, because they are imported via the + /// AccessControlEnumerableUpgradeable as well. + function _msgData() + internal + view + virtual + override(ContextUpgradeable, ERC2771ContextUpgradeable) + returns (bytes calldata msgData_) + { + return ERC2771ContextUpgradeable._msgData(); + } + + /// @dev Needs to be overridden, because they are imported via the + /// AccessControlEnumerableUpgradeable as well. + function _contextSuffixLength() + internal + view + virtual + override(ContextUpgradeable, ERC2771ContextUpgradeable) + returns (uint contextSuffixLength_) + { + return ERC2771ContextUpgradeable._contextSuffixLength(); + } +} diff --git a/src/modules/authorizer/role/AUT_TokenGated_Roles_v1.sol b/src/modules/authorizer/role/AUT_TokenGated_Roles_v1.sol deleted file mode 100644 index bbd9e1881..000000000 --- a/src/modules/authorizer/role/AUT_TokenGated_Roles_v1.sol +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Interfaces -import {IAUT_TokenGated_Roles_v1} from - "@aut/role/interfaces/IAUT_TokenGated_Roles_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; - -// Internal Dependencies -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; - -// External Interfaces -import {IAccessControl} from "@oz/access/IAccessControl.sol"; - -// External Dependencies -import {ERC165Upgradeable} from - "@oz-up/utils/introspection/ERC165Upgradeable.sol"; - -import {AccessControlUpgradeable} from - "@oz-up/access/AccessControlUpgradeable.sol"; -import {AccessControlEnumerableUpgradeable} from - "@oz-up/access/extensions/AccessControlEnumerableUpgradeable.sol"; - -interface TokenInterface { - function balanceOf(address _owner) external view returns (uint balance); -} - -/** - * @title Inverter Token-Gated Role Authorizer - * - * @notice Extends the Inverter's role-based access control to include token gating, - * enabling roles to be conditionally assigned based on token ownership. - * This mechanism allows for dynamic permissioning tied to specific token - * holdings. - * - * @dev Builds on {AUT_Roles_v1} by integrating token-based access checks before - * role assignment. Utilizes checks on token balances to gate access, - * supporting both {ERC20} and {ERC721} tokens as qualifiers for role eligibility. - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! - * - * @author Inverter Network - */ -contract AUT_TokenGated_Roles_v1 is IAUT_TokenGated_Roles_v1, AUT_Roles_v1 { - /// @inheritdoc ERC165Upgradeable - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AUT_Roles_v1) - returns (bool) - { - return interfaceId == type(IAUT_TokenGated_Roles_v1).interfaceId - || super.supportsInterface(interfaceId); - } - - /* - * This Module expands on the AUT_Roles_v1 by adding the possibility to set a role as "Token-Gated" - * Instead of whitelisting a user address, the whitelisted addresses will correspond to a token address, and on - * authorization the contract will check on ownership of one of the specifed tokens. - */ - - //-------------------------------------------------------------------------- - // Modifiers - - /// @dev Modifier to guarantee function is only callable when the role is empty. - /// @param roleId The ID of the role to be checked. - modifier onlyEmptyRole(bytes32 roleId) { - // Check that the role is empty - if (getRoleMemberCount(roleId) != 0) { - revert Module__AUT_TokenGated_Roles__RoleNotEmpty(); - } - - _; - } - - /// @dev Modifier to guarantee function is only callable when the role is token-gated. - /// @param roleId The ID of the role to be checked. - modifier onlyTokenGated(bytes32 roleId) { - if (!isTokenGated[roleId]) { - revert Module__AUT_TokenGated_Roles__RoleNotTokenGated(); - } - _; - } - - /// @dev Modifier to guarantee function is only callable when the threshold is valid. - /// @param threshold The threshold to be checked. - modifier validThreshold(uint threshold) { - // Since base ERC721 does not have a total/max supply, we can only enforce that the value should be non-zero - if (threshold == 0) { - revert Module__AUT_TokenGated_Roles__InvalidThreshold(threshold); - } - _; - } - - //-------------------------------------------------------------------------- - // Storage - - /// @dev Stores if a role is token gated. - mapping(bytes32 => bool) public isTokenGated; - /// @dev Stores the threshold amount for each token in a role. - mapping(bytes32 => uint) public thresholdMap; - - /// @dev Storage gap for future upgrades. - uint[50] private __gap; - - //-------------------------------------------------------------------------- - // View functions - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function hasTokenRole(bytes32 role, address who) - external - view - onlyTokenGated(role) - returns (bool) - { - return _hasTokenRole(role, who); - } - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function getThresholdValue(bytes32 roleId, address token) - public - view - returns (uint) - { - bytes32 thresholdId = keccak256(abi.encodePacked(roleId, token)); - return thresholdMap[thresholdId]; - } - - //-------------------------------------------------------------------------- - // State-altering functions - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function makeRoleTokenGatedFromModule(bytes32 role) - public - onlyModule(_msgSender()) - onlyEmptyRole(generateRoleId(_msgSender(), role)) - { - bytes32 roleId = generateRoleId(_msgSender(), role); - - isTokenGated[roleId] = true; - emit ChangedTokenGating(roleId, true); - } - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function grantTokenRoleFromModule( - bytes32 role, - address token, - uint threshold - ) external onlyModule(_msgSender()) { - bytes32 roleId = generateRoleId(_msgSender(), role); - _setThreshold(roleId, token, threshold); - _grantRole(roleId, token); - } - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function setThresholdFromModule(bytes32 role, address token, uint threshold) - public - onlyModule(_msgSender()) - { - bytes32 roleId = generateRoleId(_msgSender(), role); - _setThreshold(roleId, token, threshold); - } - - //-------------------------------------------------------------------------- - // Setters for the Admin - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function setTokenGated(bytes32 role, bool to) - public - onlyRole(getRoleAdmin(role)) - onlyEmptyRole(role) - { - isTokenGated[role] = to; - emit ChangedTokenGating(role, to); - } - - /// @inheritdoc IAUT_TokenGated_Roles_v1 - function setThreshold(bytes32 roleId, address token, uint threshold) - public - onlyRole(getRoleAdmin(roleId)) - { - _setThreshold(roleId, token, threshold); - } - - //-------------------------------------------------------------------------- - // Overloaded and overridden functions - - /// @notice Grants a role to an address. - /// @param role The role to grant. - /// @param who The address to grant the role to. - /// @return bool Returns true if the role has been granted succesfully. - /// @dev Overrides {_grantRole} from {AUT_ROLES_v1} to enforce interface implementation and threshold existence - /// when role is token-gated. - /// @dev Please note: current check for validating a valid token is not conclusive and could be - /// circumvented through a `callback()` function. - function _grantRole(bytes32 role, address who) - internal - virtual - override - returns (bool) - { - if (isTokenGated[role]) { - // Make sure that a threshold has been set before granting the role - if (getThresholdValue(role, who) == 0) { - revert Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold( - role, who - ); - } - - // Check that address has code attached - uint32 size; - assembly { - size := extcodesize(who) - } - if (size == 0) { - revert Module__AUT_TokenGated_Roles__InvalidToken(who); - } - - // Execute a balanceOf call to the address - (bool success, bytes memory data) = who.call( - abi.encodeWithSelector( - TokenInterface.balanceOf.selector, address(this) - ) - ); - // If the call was either unsuccessful or the return data is not - // 32 bytes long (i.e. not a uint256), it's deemed invalid - if (!success || data.length != 32) { - revert Module__AUT_TokenGated_Roles__InvalidToken(who); - } - } - - return super._grantRole(role, who); - } - - /// @param role The id number of the role. - /// @param who The user we want to check on. - /// @return bool Returns if revoke has been succesful. - /// @dev Overrides {_revokeRole} to clean up threshold data on revoking. - function _revokeRole(bytes32 role, address who) - internal - virtual - override - returns (bool) - { - if (isTokenGated[role]) { - // Set the threshold to 0 before revoking the role from the token - bytes32 thresholdId = keccak256(abi.encodePacked(role, who)); - thresholdMap[thresholdId] = 0; - emit ChangedTokenThreshold(role, who, 0); - } - return super._revokeRole(role, who); - } - - //-------------------------------------------------------------------------- - // Internal Functions - - /// @notice Sets the minimum threshold for a token-gated role. - /// @param roleId The ID of the role to be modified. - /// @param token The token for which to the threshold. - /// @param threshold The user will need to have at least this number to qualify for the role. - /// @dev This function does not validate the threshold. It is technically possible to set a threshold above the - /// total supply of the token. - function _setThreshold(bytes32 roleId, address token, uint threshold) - internal - onlyTokenGated(roleId) - validThreshold(threshold) - { - bytes32 thresholdId = keccak256(abi.encodePacked(roleId, token)); - thresholdMap[thresholdId] = threshold; - emit ChangedTokenThreshold(roleId, token, threshold); - } - - /// @notice Internal function that checks if an account qualifies for a token-gated role. - /// @param role The role to be checked. - /// @param who The account to be checked. - function _hasTokenRole(bytes32 role, address who) - internal - view - returns (bool) - { - uint numberOfAllowedTokens = getRoleMemberCount(role); - - for (uint i; i < numberOfAllowedTokens; ++i) { - address tokenAddr = getRoleMember(role, i); - bytes32 thresholdId = keccak256(abi.encodePacked(role, tokenAddr)); - uint tokenThreshold = thresholdMap[thresholdId]; - - // Should work with both ERC20 and ERC721 - try TokenInterface(tokenAddr).balanceOf(who) returns ( - uint tokenBalance - ) { - if (tokenBalance >= tokenThreshold) { - return true; - } - } catch { - // If the call fails, we continue to the next token. - // Emitting an event here would make this function (and the functions calling it) non-view. - // note we already enforce Interface implementation when granting the role. - } - } - - return false; - } - - /// @inheritdoc IAuthorizer_v1 - /// @notice In case the role is token gated, it will check if {who} holds a balance - /// above the threshold for at least one of the required tokens. - function checkForRole(bytes32 role, address who) - external - view - virtual - override(AUT_Roles_v1, IAuthorizer_v1) - returns (bool) - { - if (isTokenGated[role]) { - return _hasTokenRole(role, who); - } else { - return hasRole(role, who); - } - } -} diff --git a/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.sol b/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.sol new file mode 100644 index 000000000..4d9b6c327 --- /dev/null +++ b/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.sol @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +// Internal Interfaces +import {IAUT_TokenGated_Roles_v2} from + "@aut/role/interfaces/IAUT_TokenGated_Roles_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; + +// Internal Dependencies +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; + +// External Interfaces +import {IAccessControl} from "@oz/access/IAccessControl.sol"; + +// External Dependencies +import {ERC165Upgradeable} from + "@oz-up/utils/introspection/ERC165Upgradeable.sol"; + +import {AccessControlUpgradeable} from + "@oz-up/access/AccessControlUpgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@oz-up/access/extensions/AccessControlEnumerableUpgradeable.sol"; + +/** + * @title Token Interface + * + * @notice This Interface is an abstraction of token based contracts that is + * referenced in the Token-Gated Role Authorizer. + * + * @dev It only contains the balanceOf function, which should be + * implemented by any of the following token contracts and their + * derivatives: + * - ERC20 + * - ERC721 + * This interface is used to ensure that the token-gated role + * authorizer can be used with any token contract that implements + * the balanceOf function. + * + * @author Inverter Network + */ +interface TokenInterface { + /// @notice Returns the balance of the given address. + /// @param owner_ The address to check the balance of. + /// @return balance_ The balance of the given address. + function balanceOf(address owner_) external view returns (uint balance_); +} + +/** + * @title Inverter Token-Gated Role Authorizer + * + * @notice Extends the Inverter's role-based access control to include token + * gating, enabling roles to be conditionally assigned based on token + * ownership. This mechanism allows for dynamic permissioning tied to + * specific token holdings. + * + * @dev Inherits functionality from: + * - {IAUT_TokenGated_Roles_v2}: Implementation interface. + * - {AUT_Roles_v2}: Inverter's role-based access control. + * + * Key features: + * - Token-based access checks before role assignment. + * - Supports both {ERC20} and {ERC721} tokens. + * + * @custom:documentation See https://github.com/InverterNetwork/contracts/tree/dev/docs/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.md + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ +contract AUT_TokenGated_Roles_v2 is IAUT_TokenGated_Roles_v2, AUT_Roles_v2 { + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId_) + public + view + virtual + override(AUT_Roles_v2) + returns (bool isInterfaceId_) + { + return interfaceId_ == type(IAUT_TokenGated_Roles_v2).interfaceId + || super.supportsInterface(interfaceId_); + } + + /* + * This Module expands on the AUT_Roles_v2 by adding the possibility to set + * a role as "Token-Gated". Instead of whitelisting a user address, the + * whitelisted addresses will correspond to a token address, and on + * authorization the contract will check on ownership of one of the specifed + * tokens. + */ + + // ======================================================================== + // Modifiers + + /// @notice Modifier to guarantee function is only callable when the role is + /// empty. + /// @param roleId_ The ID of the role to be checked. + modifier onlyEmptyRole(bytes32 roleId_) { + // Check that the role is empty. + if (getRoleMemberCount(roleId_) != 0) { + revert Module__AUT_TokenGated_Roles__RoleNotEmpty(); + } + + _; + } + + /// @notice Modifier to guarantee that the role is not the public role. + /// @param roleId_ The ID of the role to be checked. + modifier notPublicRole(bytes32 roleId_) { + if (PUBLIC_ROLE == roleId_) { + revert Module__AUT_TokenGated_Roles__RoleIsPublic(); + } + _; + } + + /// @notice Modifier to guarantee function is only callable when the role + /// is token-gated. + /// @param roleId_ The ID of the role to be checked. + modifier onlyTokenGated(bytes32 roleId_) { + if (!_isTokenGated[roleId_]) { + revert Module__AUT_TokenGated_Roles__RoleNotTokenGated(); + } + _; + } + + /// @notice Modifier to guarantee function is only callable when the + /// threshold is valid. + /// @param threshold_ The threshold to be checked. + modifier validThreshold(uint threshold_) { + // Since base ERC721 does not have a total/max supply, we can only + // enforce that the value should be non-zero. + if (threshold_ == 0) { + revert Module__AUT_TokenGated_Roles__InvalidThreshold(threshold_); + } + _; + } + + // ======================================================================== + // Storage + + /// @dev Stores if a role is token gated. + /// @dev roleId The id of the role. + /// @dev tokenGated If the role is token gated. + mapping(bytes32 roleId => bool tokenGated) internal _isTokenGated; + + /// @dev Stores the threshold amount for each token in a role. + /// @dev roleId The id of the role. + /// @dev threshold The threshold amount. + mapping(bytes32 roleId => uint threshold) internal _thresholdMap; + + /// @dev Storage gap for future upgrades. + uint[50] private __gap; + + // ======================================================================== + // Public Getter Functions + + /// @inheritdoc IAUT_TokenGated_Roles_v2 + function isTokenGated(bytes32 roleId_) + external + view + returns (bool isTokenGated_) + { + return _isTokenGated[roleId_]; + } + + /// @inheritdoc IAUT_TokenGated_Roles_v2 + function hasTokenRole(bytes32 roleId_, address who_) + external + view + onlyTokenGated(roleId_) + returns (bool hasRole_) + { + return _hasTokenRole(roleId_, who_); + } + + /// @inheritdoc IAUT_TokenGated_Roles_v2 + function getThresholdValue(bytes32 roleId_, address token_) + public + view + returns (uint threshold_) + { + bytes32 thresholdId = keccak256(abi.encodePacked(roleId_, token_)); + return _thresholdMap[thresholdId]; + } + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - TokenGated Settings + + /// @inheritdoc IAUT_TokenGated_Roles_v2 + function setTokenGated(bytes32 roleId_, bool to_) + public + permissioned + idExists(roleId_) + onlyEmptyRole(roleId_) + notPublicRole(roleId_) + { + _isTokenGated[roleId_] = to_; + emit ChangedTokenGating(roleId_, to_); + } + + /// @inheritdoc IAUT_TokenGated_Roles_v2 + function setThreshold(bytes32 roleId_, address token_, uint threshold_) + public + permissioned + idExists(roleId_) + { + _setThreshold(roleId_, token_, threshold_); + } + + //-------------------------------------------------------------------------- + // Overloaded and overridden functions + + /// @inheritdoc IAccessControl + /// @notice In case the role is token gated, it will check if {who_} holds a + /// balance above the threshold for at least one of the required + ///tokens. + /// @param roleId_ The id number of the role. + /// @param who_ The user we want to check on. + /// @return hasRole_ Returns if the account has the role. + function hasRole(bytes32 roleId_, address who_) + public + view + virtual + override(AccessControlUpgradeable, IAccessControl) + returns (bool hasRole_) + { + if (_isTokenGated[roleId_]) { + return _hasTokenRole(roleId_, who_); + } else { + return super.hasRole(roleId_, who_); + } + } + + /// @notice Grants a role to an address. + /// @param roleId_ The role to grant. + /// @param who_ The address to grant the role to. + /// @return success_ Returns true if the role has been granted succesfully. + /// @dev Overrides {_grantRole} from {AUT_Roles_v2} to enforce interface + /// implementation and threshold existence when role is token-gated. + /// @dev Please note: current check for validating a valid token is not + /// conclusive and could be circumvented through a `callback()` + /// function. + function _grantRole(bytes32 roleId_, address who_) + internal + virtual + override + returns (bool success_) + { + if (_isTokenGated[roleId_]) { + // Check that address has code attached. + uint32 size; + assembly { + size := extcodesize(who_) + } + if (size == 0) { + revert Module__AUT_TokenGated_Roles__InvalidToken(who_); + } + + // Make sure that a threshold has been set before granting the role. + if (getThresholdValue(roleId_, who_) == 0) { + revert Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold( + roleId_, who_ + ); + } + + // Execute a balanceOf call to the address. + (bool success, bytes memory data) = who_.call( + abi.encodeWithSelector( + TokenInterface.balanceOf.selector, address(this) + ) + ); + // If the call was either unsuccessful or the return data is not + // 32 bytes long (i.e. not a uint256), it's deemed invalid. + if (!success || data.length != 32) { + revert Module__AUT_TokenGated_Roles__InvalidToken(who_); + } + } + + return super._grantRole(roleId_, who_); + } + + /// @notice Revokes a role from an address. + /// @dev Overrides {_revokeRole} to clean up threshold data on revoking. + /// @param roleId_ The id number of the role. + /// @param who_ The user we want to check on. + /// @return success_ Returns if revoke has been succesful. + function _revokeRole(bytes32 roleId_, address who_) + internal + virtual + override + returns (bool success_) + { + if (_isTokenGated[roleId_]) { + // Set the threshold to 0 before revoking the role from the token. + bytes32 thresholdId = keccak256(abi.encodePacked(roleId_, who_)); + _thresholdMap[thresholdId] = 0; + emit ChangedTokenThreshold(roleId_, who_, 0); + } + return super._revokeRole(roleId_, who_); + } + + //-------------------------------------------------------------------------- + // Internal Functions + + /// @notice Sets the minimum threshold for a token-gated role. + /// @dev This function does not validate the threshold. It is + /// technically possible to set a threshold above the total supply + /// of the token. + /// @param roleId_ The ID of the role to be modified. + /// @param token_ The token for which to the threshold. + /// @param threshold_ The user will need to have at least this number to + /// qualify for the role. + function _setThreshold(bytes32 roleId_, address token_, uint threshold_) + internal + onlyTokenGated(roleId_) + validThreshold(threshold_) + { + bytes32 thresholdId = keccak256(abi.encodePacked(roleId_, token_)); + _thresholdMap[thresholdId] = threshold_; + emit ChangedTokenThreshold(roleId_, token_, threshold_); + } + + /// @notice Internal function that checks if an account qualifies for a + /// token-gated role. + /// @param roleId_ The id of the role to be checked. + /// @param who_ The account to be checked. + /// @return hasRokenRole_ Returns if the account has the role. + function _hasTokenRole(bytes32 roleId_, address who_) + internal + view + returns (bool hasRokenRole_) + { + uint numberOfAllowedTokens = getRoleMemberCount(roleId_); + + address tokenAddr; + bytes32 thresholdId; + uint tokenThreshold; + for (uint i; i < numberOfAllowedTokens; ++i) { + tokenAddr = getRoleMember(roleId_, i); + thresholdId = keccak256(abi.encodePacked(roleId_, tokenAddr)); + tokenThreshold = _thresholdMap[thresholdId]; + + // Should work with both ERC20 and ERC721. + try TokenInterface(tokenAddr).balanceOf(who_) returns ( + uint tokenBalance + ) { + if (tokenBalance >= tokenThreshold) { + return true; + } + } catch { + // If the call fails, we continue to the next token. + // Emitting an event here would make this function + // (and the functions calling it) non-view. + // note we already enforce Interface implementation when + // granting the role. + } + } + + return false; + } +} diff --git a/src/modules/authorizer/role/interfaces/IAUT_EXT_VotingRoles_v1.sol b/src/modules/authorizer/role/interfaces/IAUT_EXT_VotingRoles_v1.sol deleted file mode 100644 index 548e6104b..000000000 --- a/src/modules/authorizer/role/interfaces/IAUT_EXT_VotingRoles_v1.sol +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -interface IAUT_EXT_VotingRoles_v1 { - //-------------------------------------------------------------------------- - // Structs - - /// @notice A motion is a proposal to execute an action on a target contract. - /// @param target The address of the contract to execute the action on. - /// @param action The action data to execute on the target contract. - /// @param startTimestamp The timestamp at which the motion starts. - /// @param endTimestamp The timestamp at which the motion ends. - /// @param requiredThreshold The required threshold of votes to pass the motion. - /// @param forVotes The number of votes in favor of the motion. - /// @param againstVotes The number of votes against the motion. - /// @param abstainVotes The number of votes abstaining from the motion. - /// @param receipts The receipts of votes for the motion address => Receipt - /// @param executedAt The timestamp at which the motion was executed. - /// @param executionResult The result of the execution. - /// @param executionReturnData The return data of the execution. - struct Motion { - address target; - bytes action; - uint startTimestamp; - uint endTimestamp; - uint requiredThreshold; - uint forVotes; - uint againstVotes; - uint abstainVotes; - mapping(address => Receipt) receipts; - uint executedAt; - bool executionResult; - bytes executionReturnData; - } - - /// @notice A receipt is a vote cast for a motion. - /// @param hasVoted Whether the voter has already voted. - /// @param support The value that indicates wether the voter supports the motion. - struct Receipt { - bool hasVoted; - uint8 support; - } - - //-------------------------------------------------------------------------- - // Errors - - /// @notice The action would leave an empty voter list. - error Module__VotingRoleManager__EmptyVoters(); - - /// @notice The supplied voter address is invalid. - error Module__VotingRoleManager__InvalidVoterAddress(); - - /// @notice The threshold cannot exceed the amount of voters. - /// or be too low to be considered safe. - error Module__VotingRoleManager__InvalidThreshold(); - - /// @notice The supplied voting duration is invalid. - error Module__VotingRoleManager__InvalidVotingDuration(); - - /// @notice The function can only be called by a voter. - error Module__VotingRoleManager__CallerNotVoter(); - - /// @notice The address is already a voter. - error Module__VotingRoleManager__IsAlreadyVoter(); - - /// @notice The value given as vote is invalid. - error Module__VotingRoleManager__InvalidSupport(); - - /// @notice The supplied ID is referencing a motion that doesn't exist. - error Module__VotingRoleManager__InvalidMotionId(); - - /// @notice A user cannot vote twice. - error Module__VotingRoleManager__AttemptedDoubleVote(); - - /// @notice A motion cannot be executed if the voting duration hasn't passed. - error Module__VotingRoleManager__MotionInVotingPhase(); - - /// @notice A motion cannot be voted on if the duration has been exceeded. - error Module__VotingRoleManager__MotionVotingPhaseClosed(); - - /// @notice A motion cannot be executed twice. - error Module__VotingRoleManager__MotionAlreadyExecuted(); - - /// @notice A motion cannot be executed if it didn't reach the threshold. - error Module__VotingRoleManager__ThresholdNotReached(); - - //-------------------------------------------------------------------------- - // Events - - /// @notice Event emitted when a new voter address gets added. - /// @param who The added address. - event VoterAdded(address indexed who); - - /// @notice Event emitted when a voter address gets removed. - /// @param who The removed address. - event VoterRemoved(address indexed who); - - /// @notice Event emitted when the required threshold changes. - /// @param oldThreshold The old threshold. - /// @param newThreshold The new threshold. - event ThresholdUpdated(uint oldThreshold, uint newThreshold); - - /// @notice Event emitted when the voting duration changes. - /// @param oldVotingDuration The old voting duration. - /// @param newVotingDuration The new voting duration. - event VoteDurationUpdated(uint oldVotingDuration, uint newVotingDuration); - - /// @notice Event emitted when a motion is created. - /// @param motionId The motion ID. - event MotionCreated(bytes32 indexed motionId); - - /// @notice Event emitted when a vote is cast for a motion. - /// @param motionId The motion ID. - /// @param voter The address of a voter. - /// @param motionId Value that indicates how the voter supports the motion. - event VoteCast( - bytes32 indexed motionId, address indexed voter, uint8 indexed support - ); - - /// @notice Event emitted when a motion is executed. - /// @param motionId The motion ID. - event MotionExecuted(bytes32 indexed motionId); - - //-------------------------------------------------------------------------- - // Functions - - /// @notice The maximum voting duration. - /// @return The maximum voting duration. - function MAX_VOTING_DURATION() external view returns (uint); - - /// @notice The minimum voting duration. - /// @return The minimum voting duration. - function MIN_VOTING_DURATION() external view returns (uint); - - /// @notice Checks whether an address is a voter. - /// @param who The address to check. - /// @return Whether the address is a voter. - function isVoter(address who) external view returns (bool); - - /// @notice Adds a voter. - /// @param who The address to add. - function addVoter(address who) external; - - /// @notice Adds a voter and updates the threshold. - /// @param who The address to add. - /// @param newThreshold The new threshold. - function addVoterAndUpdateThreshold(address who, uint newThreshold) - external; - - /// @notice Removes a voter. - /// @param who The address to remove. - function removeVoter(address who) external; - - /// @notice Removes a voter and updates the threshold. - /// @param who The address to remove. - /// @param newThreshold The new threshold. - function removeVoterAndUpdateThreshold(address who, uint newThreshold) - external; - - /// @notice Gets the motion data. - /// @param motionId The ID of the motion. - /// @return target The address of the contract to execute the action on. - /// @return action The action data to execute on the target contract. - /// @return startTimestamp The timestamp at which the motion starts. - /// @return endTimestamp The timestamp at which the motion ends. - /// @return requiredThreshold The required threshold of votes to pass the motion. - /// @return forVotes The number of votes in favor of the motion. - /// @return againstVotes The number of votes against the motion. - /// @return abstainVotes The number of votes abstaining from the motion. - /// @return executedAt The timestamp at which the motion was executed. - /// @return executionResult The result of the execution. - /// @return executionReturnData The return data of the execution. - function motions(bytes32 motionId) - external - view - returns ( - address, - bytes memory, - uint, - uint, - uint, - uint, - uint, - uint, - uint, - bool, - bytes memory - ); - - /// @notice Gets the number of motions. - /// @return The number of motions. - function motionCount() external view returns (uint); - - /// @notice Gets the number of voters. - /// @return The number of voters. - function voterCount() external view returns (uint); - - /// @notice Gets the threshold. - /// @return The threshold. - function threshold() external view returns (uint); - - /// @notice Gets the receipt of a voter for a motion. - /// @param _ID The ID of the motion. - /// @param voter The address of the voter. - /// @return The receipt of the voter. - function getReceipt(bytes32 _ID, address voter) - external - view - returns (Receipt memory); - - /// @notice Gets the voting duration. - /// @return The voting duration. - function voteDuration() external view returns (uint); - - /// @notice Sets the threshold. - /// @param newThreshold The new threshold. - function setThreshold(uint newThreshold) external; - - /// @notice Sets the voting duration. - /// @param newVoteDuration The new voting duration. - function setVotingDuration(uint newVoteDuration) external; - - /// @notice Creates a motion. - /// @param target The address of the contract to execute the action on. - /// @param action The action data to execute on the target contract. - /// @return The ID of the created motion. - function createMotion(address target, bytes calldata action) - external - returns (bytes32); - - /// @notice Casts a vote for a motion. - /// @param motionId The ID of the motion. - /// @param support The value that indicates wether the voter supports the motion. - function castVote(bytes32 motionId, uint8 support) external; - - /// @notice Executes a motion. - /// @param motionId The ID of the motion. - function executeMotion(bytes32 motionId) external; -} diff --git a/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v1.sol b/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v1.sol deleted file mode 100644 index 365ece78e..000000000 --- a/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v1.sol +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; - -interface IAUT_TokenGated_Roles_v1 is IAuthorizer_v1 { - //-------------------------------------------------------------------------- - // Events - - /// @notice Event emitted when the token-gating of a role changes. - /// @param role The role that was modified. - /// @param newValue The new value of the role. - event ChangedTokenGating(bytes32 role, bool newValue); - - /// @notice Event emitted when the threshold of a token-gated role changes. - /// @param role The role that was modified. - /// @param token The token for which the threshold was modified. - /// @param newValue The new value of the threshold. - event ChangedTokenThreshold(bytes32 role, address token, uint newValue); - - //-------------------------------------------------------------------------- - // Errors - - /// @notice The function is only callable by an active Module. - error Module__AUT_TokenGated_Roles__RoleNotTokenGated(); - - /// @notice The function is only callable if the Module is self-managing its roles. - error Module__AUT_TokenGated_Roles__RoleNotEmpty(); - - /// @notice The token doesn't support balance query. - /// @param token The token address that is not supported. - error Module__AUT_TokenGated_Roles__InvalidToken(address token); - - /// @notice The given threshold is invalid. - /// @param threshold The threshold that is not valid. - error Module__AUT_TokenGated_Roles__InvalidThreshold(uint threshold); - - /// @notice The role is token-gated but no threshold is set. - /// @param role The role that doesnt have threshold. - /// @param token The token for which the threshold was not set. - error Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold( - bytes32 role, address token - ); - - //-------------------------------------------------------------------------- - // Public functions - - /// @notice Checks if an account qualifies for a token-gated role. - /// @param role The role to be checked. - /// @param who The account to be checked. - /// @return True if the account qualifies for the role. - function hasTokenRole(bytes32 role, address who) - external - view - returns (bool); - - /// @notice Returns the threshold balance for a given token necessary to qualify for a - /// specific role. If the value is 0, the supplied token is not part of the. - /// role's token gating. - /// @dev In case the queried role is not token gated, all calls will return 0. - /// @param roleId The role to be checked on. - /// @param token The token to check the threshold for. - /// @return The threshold amount necessary to qualify for a given token role. - function getThresholdValue(bytes32 roleId, address token) - external - returns (uint); - - /// @notice Sets up a token-gated empty role. - /// @param role The role to be made token-gated. - /// @dev This function is only callable by an active Module for itself. Admin should use `setTokenGated()`. - /// @dev Calling this function does not specify WHICH token to use for gating. That has to be done - /// with 'grantTokenFromModule()'. - function makeRoleTokenGatedFromModule(bytes32 role) external; - - /// @notice One-step setup for Modules to create a token-gated role and set its threshold. - /// Please be aware that using tokens that are transferable and have active markets could - /// make the token-gated authorization vulnerable to flash loans, potentially bypassing. - /// the authorization mechanism. - /// @param role The role to be made token-gated. - /// @param token The token for which the threshold will be set. - /// @param threshold The minimum balance of the token required to qualify for the role. - function grantTokenRoleFromModule( - bytes32 role, - address token, - uint threshold - ) external; - - /// @notice Allows a Module to set the threshold of one of it's roles. - /// @param role The token-gated role. - /// @param token The token for which the threshold will be set. - /// @param threshold The new minimum balance of the token required to qualify for the role. - function setThresholdFromModule(bytes32 role, address token, uint threshold) - external; - - /// @notice Sets if a role is token-gated or not. - /// @param role The ID of the role to be modified. - /// @param to The new value to be set. - /// @dev Admin access for rescue purposes. If the role has active members, they need to be reovked first. - function setTokenGated(bytes32 role, bool to) external; - - /// @notice Sets the minimum threshold for a token-gated role. - /// @param roleId The ID of the role to be modified. - /// @param token The token for which to the threshold. - /// @param threshold The user will need to have at least this number to qualify for the role. - /// @dev This function does not validate the threshold. It is technically possible to set a threshold above - /// the total supply of the token. - function setThreshold(bytes32 roleId, address token, uint threshold) - external; -} diff --git a/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v2.sol b/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v2.sol new file mode 100644 index 000000000..b92c06578 --- /dev/null +++ b/src/modules/authorizer/role/interfaces/IAUT_TokenGated_Roles_v2.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; + +/** + * @title Inverter Token-Gated Role Authorizer Interface + * + * @notice Extends the Inverter's role-based access control to include token + * gating, enabling roles to be conditionally assigned based on token + * ownership. This mechanism allows for dynamic permissioning tied to + * specific token holdings. + * + * @dev Inherits functionality from: + * - {IAUT_TokenGated_Roles_v2}: Implementation interface. + * - {AUT_Roles_v2}: Inverter's role-based access control. + * + * Key feeatures: + * - Token-based access checks before role assignment. + * - Supports both {ERC20} and {ERC721} tokens. + * + * @custom:documentation See https://github.com/InverterNetwork/contracts/tree/dev/docs/src/modules/authorizer/role/AUT_TokenGated_Roles_v2.md + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @custom:inverter-standard-version v0.1.0 + * + * @author Inverter Network + */ +interface IAUT_TokenGated_Roles_v2 is IAuthorizer_v2 { + //======================================================================= + // Errors + + /// @notice The function is only callable by an active Module. + error Module__AUT_TokenGated_Roles__RoleNotTokenGated(); + + /// @notice The function is only callable if the Module is self-managing + /// its roles. + error Module__AUT_TokenGated_Roles__RoleNotEmpty(); + + /// @notice The function is only callable if the Module is not the public + /// role. + error Module__AUT_TokenGated_Roles__RoleIsPublic(); + + /// @notice The token doesn't support balance query. + /// @param token_ The token address that is not supported. + error Module__AUT_TokenGated_Roles__InvalidToken(address token_); + + /// @notice The given threshold is invalid. + /// @param threshold_ The threshold that is not valid. + error Module__AUT_TokenGated_Roles__InvalidThreshold(uint threshold_); + + /// @notice The role is token-gated but no threshold is set. + /// @param roleId_ The role that doesnt have threshold. + /// @param token_ The token for which the threshold was not set. + error Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold( + bytes32 roleId_, address token_ + ); + + //======================================================================= + // Events + + /// @notice Event emitted when the token-gating of a role changes. + /// @param roleId_ The role id of the role that was modified. + /// @param newValue_ The new value of the role. + event ChangedTokenGating(bytes32 roleId_, bool newValue_); + + /// @notice Event emitted when the threshold of a token-gated role changes. + /// @param roleId_ The role id of the role that was modified. + /// @param token_ The token for which the threshold was modified. + /// @param newValue_ The new value of the threshold. + event ChangedTokenThreshold( + bytes32 roleId_, address token_, uint newValue_ + ); + + // ======================================================================== + // Public Getter Functions + + /// @notice Returns if a role is token-gated or not. + /// @param roleId_ The ID of the role to be checked. + /// @return isTokenGated_ True if the role is token-gated. + function isTokenGated(bytes32 roleId_) + external + view + returns (bool isTokenGated_); + + /// @notice Checks if an account qualifies for a token-gated role. + /// @param roleId_ The role to be checked. + /// @param who_ The account to be checked. + /// @return hasTokenRole_ True if the account qualifies for the role. + function hasTokenRole(bytes32 roleId_, address who_) + external + view + returns (bool hasTokenRole_); + + /// @notice Returns the threshold balance for a given token necessary to + /// qualify for a specific role. If the value is 0, the supplied + /// token is not part of the role's token gating. + /// @dev In case the queried role is not token gated, all calls will + /// return 0. + /// @param roleId_ The role to be checked on. + /// @param token_ The token to check the threshold for. + /// @return threshold_ The threshold amount necessary to qualify for a + /// given token role. + function getThresholdValue(bytes32 roleId_, address token_) + external + returns (uint threshold_); + + // ======================================================================== + // Mutating Functions + + /// @notice Sets if a role is token-gated or not. + /// @dev Admin access for rescue purposes. If the role has active + /// members, they need to be reovked first. + /// @param roleId_ The ID of the role to be modified. + /// @param to_ The new value to be set. + + function setTokenGated(bytes32 roleId_, bool to_) external; + + /// @notice Sets the minimum threshold for a token-gated role. + /// @dev This function does not validate the threshold. It is + /// technically possible to set a threshold above the total supply + /// of the token. + /// @param roleId_ The ID of the role to be modified. + /// @param token_ The token for which to the threshold. + /// @param threshold_ The user will need to have at least this number to + /// qualify for the role. + + function setThreshold(bytes32 roleId_, address token_, uint threshold_) + external; +} diff --git a/src/modules/base/IModule_v1.sol b/src/modules/base/IModule_v1.sol index 9c2bf04d1..8590983f1 100644 --- a/src/modules/base/IModule_v1.sol +++ b/src/modules/base/IModule_v1.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; interface IModule_v1 { //-------------------------------------------------------------------------- @@ -27,7 +27,7 @@ interface IModule_v1 { // Events /// @notice Module has been initialized. - /// @param parentOrchestrator The address of the {Orchestrator_v1} the module is linked to. + /// @param parentOrchestrator The address of the {Orchestrator_v2} the module is linked to. /// @param metadata The metadata of the module. event ModuleInitialized( address indexed parentOrchestrator, Metadata metadata @@ -49,19 +49,19 @@ interface IModule_v1 { /// @param caller The address that is required to have the role. error Module__CallerNotAuthorized(bytes32 role, address caller); - /// @notice Function is only callable by the {Orchestrator_v1}. + /// @notice Function is only callable by the {Orchestrator_v2}. error Module__OnlyCallableByOrchestrator(); /// @notice Function is only callable by a {IERC20PaymentClientBase_v2}. error Module__OnlyCallableByPaymentClient(); - /// @notice Given {Orchestrator_v1} address invalid. + /// @notice Given {Orchestrator_v2} address invalid. error Module__InvalidOrchestratorAddress(); /// @notice Given metadata invalid. error Module__InvalidMetadata(); - /// @notice {Orchestrator_v1} callback triggered failed. + /// @notice {Orchestrator_v2} callback triggered failed. /// @param funcSig The signature of the function called. error Module_OrchestratorCallbackFailed(string funcSig); @@ -74,12 +74,12 @@ interface IModule_v1 { /// @notice The module's initializer function. /// @dev CAN be overridden by downstream contract. /// @dev MUST call `__Module_init()`. - /// @param orchestrator The module's {Orchestrator_v1} instance. + /// @param orchestrator The module's {Orchestrator_v2} instance. /// @param metadata The module's metadata. /// @param configData Variable config data for specific module /// implementations. function init( - IOrchestrator_v1 orchestrator, + IOrchestrator_v2 orchestrator, Metadata memory metadata, bytes memory configData ) external; @@ -104,9 +104,9 @@ interface IModule_v1 { /// @return The module's title. function title() external view returns (string memory); - /// @notice Returns the module's {Orchestrator_v1} interface, {IOrchestrator_v1}. + /// @notice Returns the module's {Orchestrator_v2} interface, {IOrchestrator_v2}. /// @return The module's {Orchestrator_1}. - function orchestrator() external view returns (IOrchestrator_v1); + function orchestrator() external view returns (IOrchestrator_v2); //@todo what to do with this? Should this be version 1? /// @notice Grants a module role to a target address. /// @param role The role to grant. diff --git a/src/modules/base/IModule_v2.sol b/src/modules/base/IModule_v2.sol new file mode 100644 index 000000000..824ad3da5 --- /dev/null +++ b/src/modules/base/IModule_v2.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal Interfaces +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; + +/** + * @title Inverter Module Interface + * + * @dev This Contract is the basic building block for all Modules in the Inverter Network. + * It contains references to other contracts, modifier for access restriction, + * metadata to identify the module type as well as utility functions for general + * module interactions. + * + * This contract provides a framework for triggering and receiving {Orchestrator_v2} + * callbacks (via `call`) and a modifier to authenticate + * callers via the module's {Orchestrator_v2}. + * + * Each module is identified via a unique identifier based on its major + * version, title, and url given in the metadata. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 + * + * @author Inverter Network + */ +interface IModule_v2 { + // ======================================================================== + // Structs + + /// @notice The module's metadata. + /// @param majorVersion The module's major version. + /// @param minorVersion The module's minor version. + /// @param patchVersion The module's patch version. + /// @param url The module's URL. + /// @param title The module's title. + struct Metadata { + uint majorVersion; + uint minorVersion; + uint patchVersion; + string url; + string title; + } + + // ======================================================================== + // Errors + + /// @notice Function is only callable by authorized caller. + error Module__CallerNotPermissioned(); + + /// @notice Function is only callable by a {IERC20PaymentClientBase_v3}. + error Module__OnlyCallableByPaymentClient(); + + /// @notice Given {Orchestrator_v2} address invalid. + error Module__InvalidOrchestratorAddress(); + + /// @notice Given metadata invalid. + error Module__InvalidMetadata(); + + /// @notice {Orchestrator_v2} callback triggered failed. + /// @param funcSig The signature of the function called. + error Module_OrchestratorCallbackFailed(string funcSig); + + /// @dev Invalid Address. + error Module__InvalidAddress(); + + /// @dev The given function is no longer supported. + error Module__FunctionDeprecated(); + + // ======================================================================== + // Events + + /// @notice Module has been initialized. + /// @param parentOrchestrator The address of the {Orchestrator_v2} the module is linked to. + /// @param metadata The metadata of the module. + event ModuleInitialized( + address indexed parentOrchestrator, Metadata metadata + ); + + /// @notice Event emitted when protocol fee has been transferred to the treasury. + /// @param token The token received as protocol fee. + /// @param treasury The protocol treasury address receiving the token fee amount. + /// @param feeAmount The fee amount transferred to the treasury. + event ProtocolFeeTransferred( + address indexed token, address indexed treasury, uint feeAmount + ); + + // ======================================================================== + // Initialization + + /// @notice The module's initializer function. + /// @dev CAN be overridden by downstream contract. + /// @dev MUST call `__Module_init()`. + /// @param orchestrator The module's {Orchestrator_v2} instance. + /// @param metadata The module's metadata. + /// @param configData Variable config data for specific module + /// implementations. + function init( + IOrchestrator_v2 orchestrator, + Metadata memory metadata, + bytes memory configData + ) external; + + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Module State + + /// @notice Returns the module's identifier. + /// @dev The identifier is defined as the keccak256 hash of the module's + /// abi packed encoded major version, url and title. + /// @return The module's identifier. + function identifier() external view returns (bytes32); + + /// @notice Returns the module's version. + /// @return The module's major version. + /// @return The module's minor version. + /// @return The module's patch version. + function version() external view returns (uint, uint, uint); + + /// @notice Returns the module's URL. + /// @return The module's URL. + function url() external view returns (string memory); + + /// @notice Returns the module's title. + /// @return The module's title. + function title() external view returns (string memory); + + /// @notice Returns the module's {Orchestrator_v2} interface, {IOrchestrator_v2}. + /// @return The module's {Orchestrator_1}. + function orchestrator() external view returns (IOrchestrator_v2); +} diff --git a/src/modules/base/Module_v1.sol b/src/modules/base/Module_v2.sol similarity index 64% rename from src/modules/base/Module_v1.sol rename to src/modules/base/Module_v2.sol index 8c0cffbca..072088b35 100644 --- a/src/modules/base/Module_v1.sol +++ b/src/modules/base/Module_v2.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; import {IGovernor_v1} from "@ex/governance/interfaces/IGovernor_v1.sol"; import {IFeeManager_v1} from "@ex/fees/interfaces/IFeeManager_v1.sol"; @@ -12,6 +12,8 @@ import {IERC20PaymentClientBase_v1} from "@lm/interfaces/IERC20PaymentClientBase_v1.sol"; import {IERC20PaymentClientBase_v2} from "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // Internal Libraries import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; @@ -25,6 +27,9 @@ import { import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; +// External Libraries +import {ERC165Checker} from "@oz/utils/introspection/ERC165Checker.sol"; + /** * @title Inverter Module * @@ -33,21 +38,24 @@ import {ERC165Upgradeable} from * metadata to identify the module type as well as utility functions for general * module interactions. * - * This contract provides a framework for triggering and receiving {Orchestrator_v1} + * This contract provides a framework for triggering and receiving {Orchestrator_v2} * callbacks (via `call`) and a modifier to authenticate - * callers via the module's {Orchestrator_v1}. + * callers via the module's {Orchestrator_v2}. * * Each module is identified via a unique identifier based on its major * version, title, and url given in the metadata. * * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer to our Security Policy - * at security.inverter.network or email us directly! + * In case of any concerns or findings, please refer to + * our Security Policy at security.inverter.network or + * email us directly! + * + * @custom:version v2.0.0 * * @author Inverter Network */ -abstract contract Module_v1 is - IModule_v1, +abstract contract Module_v2 is + IModule_v2, Initializable, ERC2771ContextUpgradeable, ERC165Upgradeable @@ -60,11 +68,11 @@ abstract contract Module_v1 is override(ERC165Upgradeable) returns (bool) { - return interfaceId == type(IModule_v1).interfaceId + return interfaceId == type(IModule_v2).interfaceId || super.supportsInterface(interfaceId); } - //-------------------------------------------------------------------------- + // ======================================================================== // Storage // // Variables are prefixed with `__Module_`. @@ -72,7 +80,7 @@ abstract contract Module_v1 is /// @dev The module's orchestrator instance. /// /// @custom:invariant Not mutated after initialization. - IOrchestrator_v1 internal __Module_orchestrator; + IOrchestrator_v2 internal __Module_orchestrator; /// @dev The module's metadata. /// @@ -82,61 +90,25 @@ abstract contract Module_v1 is /// @dev Storage gap for future upgrades. uint[50] private __gap; - //-------------------------------------------------------------------------- + // ======================================================================== // Modifiers // // Note that the modifiers declared here are available in dowstream // contracts too. To not make unnecessary modifiers available, this contract // inlines argument validations not needed in downstream contracts. - /// @dev Modifier to guarantee function is only callable by addresses - /// authorized via {Orchestrator_v1}. - modifier onlyOrchestratorAdmin() { - _checkRoleModifier( - __Module_orchestrator.authorizer().getAdminRole(), _msgSender() - ); + modifier permissioned() { + _checkAuthorization(_msgSender(), _msgData()); _; } /// @dev Modifier to guarantee function is only callable by a module registered within the - /// workflows's {Orchestrator_v1} and the module is implementing the {IERC20PaymentClientBase_v2} interface. + /// workflows's {Orchestrator_v2} and the module is implementing the {IERC20PaymentClientBase_v3} interface. modifier onlyPaymentClient() { _onlyPaymentClientModifier(); _; } - /// @dev Modifier to guarantee function is only callable by addresses that hold a specific module-assigned role. - modifier onlyModuleRole(bytes32 role) { - _checkRoleModifier( - __Module_orchestrator.authorizer().generateRoleId( - address(this), role - ), - _msgSender() - ); - _; - } - - /// @dev Modifier to guarantee function is only callable by addresses that hold a specific module-assigned role. - modifier onlyModuleRoleAdmin(bytes32 role) { - bytes32 moduleRole = __Module_orchestrator.authorizer().generateRoleId( - address(this), role - ); - _checkRoleModifier( - __Module_orchestrator.authorizer().getRoleAdmin(moduleRole), - _msgSender() - ); - _; - } - - /// @dev Modifier to guarantee function is only callable by the {Orchestrator_v1}. - /// @dev onlyOrchestrator functions MUST only access the module's storage, i.e. - /// `__Module_` variables. - /// @dev Note to use function prefix `__Module_`. - modifier onlyOrchestrator() { - _onlyOrchestratorModifier(); - _; - } - /// @dev Checks if the given Address is valid. /// @param to The address to check. modifier validAddress(address to) { @@ -144,16 +116,16 @@ abstract contract Module_v1 is _; } - //-------------------------------------------------------------------------- + // ======================================================================== // Initialization constructor() ERC2771ContextUpgradeable(address(0)) { _disableInitializers(); } - /// @inheritdoc IModule_v1 + /// @inheritdoc IModule_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory /*configData*/ ) external virtual initializer { @@ -162,9 +134,9 @@ abstract contract Module_v1 is /// @dev The initialization function MUST be called by the upstream /// contract in their overridden `init()` function. - /// @param orchestrator_ The module's {Orchestrator_v1}. + /// @param orchestrator_ The module's {Orchestrator_v2}. function __Module_init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata ) internal onlyInitializing { // Write orchestrator to storage. @@ -182,15 +154,18 @@ abstract contract Module_v1 is emit ModuleInitialized(address(orchestrator_), metadata); } - //-------------------------------------------------------------------------- - // Public View Functions + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Module State - /// @inheritdoc IModule_v1 + /// @inheritdoc IModule_v2 function identifier() public view returns (bytes32) { return LibMetadata.identifier(__Module_metadata); } - /// @inheritdoc IModule_v1 + /// @inheritdoc IModule_v2 function version() public view returns (uint, uint, uint) { return ( __Module_metadata.majorVersion, @@ -199,63 +174,61 @@ abstract contract Module_v1 is ); } - /// @inheritdoc IModule_v1 + /// @inheritdoc IModule_v2 function url() public view returns (string memory) { return __Module_metadata.url; } - /// @inheritdoc IModule_v1 + /// @inheritdoc IModule_v2 function title() public view returns (string memory) { return __Module_metadata.title; } - /// @inheritdoc IModule_v1 - function orchestrator() public view returns (IOrchestrator_v1) { + /// @inheritdoc IModule_v2 + function orchestrator() public view returns (IOrchestrator_v2) { return __Module_orchestrator; } //-------------------------------------------------------------------------- - // Role Management - - /// @inheritdoc IModule_v1 - function grantModuleRole(bytes32 role, address target) - external - onlyModuleRoleAdmin(role) - { - __Module_orchestrator.authorizer().grantRoleFromModule(role, target); - } - - /// @inheritdoc IModule_v1 - function grantModuleRoleBatched(bytes32 role, address[] calldata targets) - external - onlyModuleRoleAdmin(role) - { - __Module_orchestrator.authorizer().grantRoleFromModuleBatched( - role, targets - ); - } + // Getter - ERC2771 Context Upgradeable Overrides - /// @inheritdoc IModule_v1 - function revokeModuleRole(bytes32 role, address target) - external - onlyModuleRoleAdmin(role) + /// @notice Checks if the provided address is the trusted forwarder. + /// @param forwarder The contract address to be verified. + /// @return bool Is the given address the trusted forwarder. + /// @dev We imitate here the EIP2771 Standard to enable metatransactions + /// As it currently stands we dont want to feed the forwarder address to each module individually and we decided to + /// move this to the orchestrator. + function isTrustedForwarder(address forwarder) + public + view + virtual + override(ERC2771ContextUpgradeable) + returns (bool) { - __Module_orchestrator.authorizer().revokeRoleFromModule(role, target); + return __Module_orchestrator.isTrustedForwarder(forwarder); } - /// @inheritdoc IModule_v1 - function revokeModuleRoleBatched(bytes32 role, address[] calldata targets) - external - onlyModuleRoleAdmin(role) + /// @notice Returns the trusted forwarder. + /// @return address The trusted forwarder. + /// @dev We imitate here the EIP2771 Standard to enable metatransactions. + /// As it currently stands we dont want to feed the forwarder address to each module individually and we decided to + /// move this to the orchestrator. + function trustedForwarder() + public + view + virtual + override(ERC2771ContextUpgradeable) + returns (address) { - __Module_orchestrator.authorizer().revokeRoleFromModuleBatched( - role, targets - ); + return __Module_orchestrator.trustedForwarder(); } - //-------------------------------------------------------------------------- + // ======================================================================== // Internal Functions + // ------------------------------------------------------------------------ + // Internal - Fees + /// @notice Returns the collateral fee for the specified workflow module function and the according treasury /// address of this workflow. /// @param functionSelector The function selector of the target function. @@ -296,22 +269,29 @@ abstract contract Module_v1 is ); } - /// @dev Checks if the caller has the specified role. - /// @param role The role to check. - /// @param addr The address to check. - function _checkRoleModifier(bytes32 role, address addr) internal view { - if (!__Module_orchestrator.authorizer().checkForRole(role, addr)) { - revert Module__CallerNotAuthorized(role, addr); - } - } + // ------------------------------------------------------------------------ + // Internal - Authorization - /// @dev Checks if the caller is the orchestrator. - function _onlyOrchestratorModifier() internal view { - if (_msgSender() != address(__Module_orchestrator)) { - revert Module__OnlyCallableByOrchestrator(); + /// @notice Checks if the caller can call the function that implements the locked modifier. + /// @param caller_ The address of the caller. + /// @param data_ The data of the call. + function _checkAuthorization(address caller_, bytes calldata data_) + internal + view + { + // If caller cannot call the function, revert. + if ( + !__Module_orchestrator.authorizer().hasPermission( + caller_, address(this), bytes4(data_[0:4]) + ) + ) { + revert Module__CallerNotPermissioned(); } } + // ------------------------------------------------------------------------ + // Internal - Modifiers + /// @dev Checks if the given address is an valid address. /// @param to The address to check. function _validAddressModifier(address to) internal view { @@ -320,52 +300,21 @@ abstract contract Module_v1 is } } - /// @dev Checks if the caller is an {ERC20PaymentClientBase_v2} module. + /// @dev Checks if the caller is an {ERC20PaymentClientBase_v3} module. function _onlyPaymentClientModifier() internal view { if ( !__Module_orchestrator.isModule(_msgSender()) || ( - !ERC165Upgradeable(_msgSender()).supportsInterface( - type(IERC20PaymentClientBase_v1).interfaceId + !ERC165Checker.supportsInterface( + _msgSender(), type(IERC20PaymentClientBase_v1).interfaceId ) - && !ERC165Upgradeable(_msgSender()).supportsInterface( - type(IERC20PaymentClientBase_v2).interfaceId + && !ERC165Checker.supportsInterface( + _msgSender(), type(IERC20PaymentClientBase_v2).interfaceId + ) + && !ERC165Checker.supportsInterface( + _msgSender(), type(IERC20PaymentClientBase_v3).interfaceId ) ) ) revert Module__OnlyCallableByPaymentClient(); } - - //-------------------------------------------------------------------------- - // ERC2771 Context Upgradeable - - /// @notice Checks if the provided address is the trusted forwarder. - /// @param forwarder The contract address to be verified. - /// @return bool Is the given address the trusted forwarder. - /// @dev We imitate here the EIP2771 Standard to enable metatransactions - /// As it currently stands we dont want to feed the forwarder address to each module individually and we decided to - /// move this to the orchestrator. - function isTrustedForwarder(address forwarder) - public - view - virtual - override(ERC2771ContextUpgradeable) - returns (bool) - { - return __Module_orchestrator.isTrustedForwarder(forwarder); - } - - /// @notice Returns the trusted forwarder. - /// @return address The trusted forwarder. - /// @dev We imitate here the EIP2771 Standard to enable metatransactions. - /// As it currently stands we dont want to feed the forwarder address to each module individually and we decided to - /// move this to the orchestrator. - function trustedForwarder() - public - view - virtual - override(ERC2771ContextUpgradeable) - returns (address) - { - return __Module_orchestrator.trustedForwarder(); - } } diff --git a/src/modules/fundingManager/IFundingManager_v1.sol b/src/modules/fundingManager/IFundingManager_v1.sol index b35990ae8..d59e968ec 100644 --- a/src/modules/fundingManager/IFundingManager_v1.sol +++ b/src/modules/fundingManager/IFundingManager_v1.sol @@ -28,7 +28,7 @@ interface IFundingManager_v1 { function token() external view returns (IERC20); /// @notice Transfer a specified amount of Tokens to a designated receiver address. - /// @dev This function MUST be restricted to be called only by the {Orchestrator_v1}. + /// @dev This function MUST be restricted to be called only by the {Orchestrator_v2}. /// @dev This function CAN update internal user balances to account for the new token balance. /// @param to The address that will receive the tokens. /// @param amount The amount of tokens to be transfered. diff --git a/src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol b/src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol similarity index 90% rename from src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol rename to src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol index 40f09bad5..512d3ddea 100644 --- a/src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol +++ b/src/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol @@ -2,24 +2,24 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IFM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IFM_BC_Bancor_Redeeming_VirtualSupply_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import {IERC20Issuance_v1} from "@ex/token/interfaces/IERC20Issuance_v1.sol"; // Internal Dependencies -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; import { - IBondingCurveBase_v1, - BondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; + IBondingCurveBase_v2, + BondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import { - IRedeemingBondingCurveBase_v1, - RedeemingBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; + IRedeemingBondingCurveBase_v2, + RedeemingBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; import { IVirtualCollateralSupplyBase_v1, VirtualCollateralSupplyBase_v1 @@ -52,11 +52,11 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * to manage the calculations for token issuance and redemption rates * based on specified reserve ratios. * - * @dev Inherits {BondingCurveBase_v1}, {RedeemingBondingCurveBase_v1}, + * @dev Inherits {BondingCurveBase_v2}, {RedeemingBondingCurveBase_v2}, * {VirtualIssuanceSupplyBase_v1}, and * {VirtualCollateralSupplyBase_v1}. Implements formulaWrapper * functions for bonding curve calculations using the {BancorFormula}. - * {Orchestrator_v1} Admin manages configuration such as virtual + * {Orchestrator_v2} Admin manages configuration such as virtual * supplies and reserve ratios. Ensure interaction adheres to defined * transactional limits and decimal precision requirements to prevent * computational overflows or underflows. @@ -66,16 +66,16 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version 1.1.3 + * @custom:version 2.0.0 * * @author Inverter Network */ -contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, +contract FM_BC_Bancor_Redeeming_VirtualSupply_v2 is + IFM_BC_Bancor_Redeeming_VirtualSupply_v2, IFundingManager_v1, VirtualIssuanceSupplyBase_v1, VirtualCollateralSupplyBase_v1, - RedeemingBondingCurveBase_v1 + RedeemingBondingCurveBase_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) @@ -85,12 +85,12 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is override( VirtualIssuanceSupplyBase_v1, VirtualCollateralSupplyBase_v1, - RedeemingBondingCurveBase_v1 + RedeemingBondingCurveBase_v2 ) returns (bool supportsInterface_) { return interfaceId - == type(IFM_BC_Bancor_Redeeming_VirtualSupply_v1).interfaceId + == type(IFM_BC_Bancor_Redeeming_VirtualSupply_v2).interfaceId || interfaceId == type(IFundingManager_v1).interfaceId || super.supportsInterface(interfaceId); } @@ -140,12 +140,12 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is // ------------------------------------------------------------------------- // Init Function - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); address issuanceToken; @@ -216,9 +216,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function buyFor(address _receiver, uint _depositAmount, uint _minAmountOut) public virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) - validReceiver(_receiver) + override(BondingCurveBase_v2, IBondingCurveBase_v2) + permissioned buyingIsEnabled + validReceiver(_receiver) { (uint amountIssued, uint collateralFeeAmount) = _buyOrder(_receiver, _depositAmount, _minAmountOut); @@ -238,10 +239,14 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function buy(uint _depositAmount, uint _minAmountOut) public virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) + override(BondingCurveBase_v2, IBondingCurveBase_v2) + permissioned buyingIsEnabled { - buyFor(_msgSender(), _depositAmount, _minAmountOut); + (uint amountIssued, uint collateralFeeAmount) = + _buyOrder(_msgSender(), _depositAmount, _minAmountOut); + _addVirtualIssuanceAmount(amountIssued); + _addVirtualCollateralAmount(_depositAmount - collateralFeeAmount); } /// @notice Redeem tokens and direct the proceeds to a specified receiver address. This function is subject @@ -257,9 +262,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function sellTo(address _receiver, uint _depositAmount, uint _minAmountOut) public virtual - override(RedeemingBondingCurveBase_v1) - validReceiver(_receiver) + override(RedeemingBondingCurveBase_v2) + permissioned sellingIsEnabled + validReceiver(_receiver) { (uint redeemAmount, uint issuanceFeeAmount) = _sellOrder(_receiver, _depositAmount, _minAmountOut); @@ -279,16 +285,20 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function sell(uint _depositAmount, uint _minAmountOut) public virtual - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) + permissioned sellingIsEnabled { - sellTo(_msgSender(), _depositAmount, _minAmountOut); + (uint redeemAmount, uint issuanceFeeAmount) = + _sellOrder(_msgSender(), _depositAmount, _minAmountOut); + _subVirtualIssuanceAmount(_depositAmount - issuanceFeeAmount); + _subVirtualCollateralAmount(redeemAmount); } // ------------------------------------------------------------------------- // Public Data Query Functions - /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v2 function getReserveRatioForBuying() external view @@ -297,7 +307,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is return reserveRatioForBuying; } - /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v2 function getReserveRatioForSelling() external view @@ -318,7 +328,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function getStaticPriceForBuying() external view - override(BondingCurveBase_v1, IBondingCurveBase_v1) + override(BondingCurveBase_v2, IBondingCurveBase_v2) returns (uint staticPriceForBuying_) { return ( @@ -342,7 +352,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function getStaticPriceForSelling() external view - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint staticPriceForSelling_) { return ( @@ -366,79 +376,85 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is } // ------------------------------------------------------------------------- - // OnlyOrchestrator Functions - - /// @inheritdoc IFundingManager_v1 - function transferOrchestratorToken(address to_, uint amount_) - external - virtual - onlyPaymentClient - { - if ( - amount_ - > token().balanceOf(address(this)) - projectCollateralFeeCollected - ) { - revert InvalidOrchestratorTokenWithdrawAmount(); - } - token().safeTransfer(to_, amount_); - - emit TransferOrchestratorToken(to_, amount_); - } + // Permissioned Functions /// @inheritdoc IVirtualIssuanceSupplyBase_v1 + /// @dev Function access controlled by authorizer. function setVirtualIssuanceSupply(uint virtualSupply_) external virtual override(VirtualIssuanceSupplyBase_v1) - onlyOrchestratorAdmin + permissioned onlyWhenCurveInteractionsAreClosed { _setVirtualIssuanceSupply(virtualSupply_); } /// @inheritdoc IVirtualCollateralSupplyBase_v1 + /// @dev Function access controlled by authorizer. function setVirtualCollateralSupply(uint virtualSupply_) external virtual override(VirtualCollateralSupplyBase_v1) - onlyOrchestratorAdmin + permissioned onlyWhenCurveInteractionsAreClosed { _setVirtualCollateralSupply(virtualSupply_); } - /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v2 function setReserveRatioForBuying(uint32 reserveRatio_) external virtual - onlyOrchestratorAdmin + permissioned onlyWhenCurveInteractionsAreClosed { _setReserveRatioForBuying(reserveRatio_); } - /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + /// @inheritdoc IFM_BC_Bancor_Redeeming_VirtualSupply_v2 + /// @dev Function access controlled by authorizer. function setReserveRatioForSelling(uint32 reserveRatio_) external virtual - onlyOrchestratorAdmin + permissioned onlyWhenCurveInteractionsAreClosed { _setReserveRatioForSelling(reserveRatio_); } + // ------------------------------------------------------------------------- + // PaymentClient Functions + + /// @inheritdoc IFundingManager_v1 + function transferOrchestratorToken(address to_, uint amount_) + external + virtual + onlyPaymentClient + { + if ( + amount_ + > token().balanceOf(address(this)) - projectCollateralFeeCollected + ) { + revert InvalidOrchestratorTokenWithdrawAmount(); + } + token().safeTransfer(to_, amount_); + + emit TransferOrchestratorToken(to_, amount_); + } + // ------------------------------------------------------------------------- // Upstream Function Implementations /// @dev Calculates the amount of tokens to mint for a given deposit amount using the {BancorFormula}. - /// This internal function is an override of {BondingCurveBase_v1}'s abstract function. + /// This internal function is an override of {BondingCurveBase_v2}'s abstract function. /// It handles decimal conversions and calculations through the bonding curve. /// @param depositAmount_ The amount of collateral deposited to purchase tokens. /// @return mintAmount_ The amount of tokens that will be minted. function _issueTokensFormulaWrapper(uint depositAmount_) internal view - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (uint mintAmount_) { // Calculate mint amount through bonding curve @@ -466,7 +482,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is } /// @dev Calculates the amount of collateral to be received when redeeming a given amount of tokens. - /// This internal function is an override of {RedeemingBondingCurveBase_v1}'s abstract function. + /// This internal function is an override of {RedeemingBondingCurveBase_v2}'s abstract function. /// It handles decimal conversions and calculations through the bonding curve. Note the {BancorFormula} /// assumes 18 decimals for all tokens. /// @param depositAmount_ The amount of tokens to be redeemed for collateral. @@ -474,7 +490,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is function _redeemTokensFormulaWrapper(uint depositAmount_) internal view - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint redeemAmount_) { // Calculate redeem amount through bonding curve @@ -508,13 +524,13 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is // Internal Functions /// @dev Sets the issuance token for the Bonding Curve Funding Manager. - /// This function overrides the internal function set in {BondingCurveBase_v1}, adding + /// This function overrides the internal function set in {BondingCurveBase_v2}, adding /// an input validation specific for the {BancorFormula} utilizing implementation, after which /// it updates the `issuanceToken` state variable and caches the decimals as `issuanceTokenDecimals`. /// @param issuanceToken_ The token which will be issued by the Bonding Curve. function _setIssuanceToken(address issuanceToken_) internal - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) { uint8 _decimals = IERC20Metadata(issuanceToken_).decimals(); // An input verification is needed here since the Bancor formula, which determines the @@ -608,7 +624,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupply_v1 is // ------------------------------------------------------------------------- // Overridden Internal Functions - /// @inheritdoc BondingCurveBase_v1 + /// @inheritdoc BondingCurveBase_v2 function _processCollateralTokensForBuyOperation(uint _amount) internal virtual diff --git a/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol b/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol similarity index 62% rename from src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol rename to src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol index ef44f364e..b47daedb6 100644 --- a/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol +++ b/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol @@ -2,30 +2,31 @@ pragma solidity 0.8.23; // Internal -import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {FM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol"; -import {RedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; -import {BondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; +import {FM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol"; +import {RedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; +import {BondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import {FixedPointMathLib} from "@modLib/FixedPointMathLib.sol"; -import {FM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; -import {IFM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; -import {IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; +import {FM_BC_Bancor_Redeeming_VirtualSupply_v2} from + "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; +import {IFM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; +import {IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import {IBondingSurface} from "@fm/bondingCurve/interfaces/IBondingSurface.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -42,8 +43,8 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * bonding curve. * * @dev This contract inherits functionalties from the contracts: - * - BondingCurveBase_v1 - * - RedeemingBondingCurveBase_v1 + * - BondingCurveBase_v2 + * - RedeemingBondingCurveBase_v2 * - Repayer * The contract should be used by the orchestrator admin to manage all * the configuration for the bonding curve as well as the opening and @@ -57,15 +58,15 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.1 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, - FM_BC_BondingSurface_Redeeming_v1 +contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 is + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, + FM_BC_BondingSurface_Redeeming_v2 { using SafeERC20 for IERC20; @@ -74,11 +75,11 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is public view virtual - override(FM_BC_BondingSurface_Redeeming_v1) + override(FM_BC_BondingSurface_Redeeming_v2) returns (bool supportsInterface_) { return interfaceId_ - == type(IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1) + == type(IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2) .interfaceId || interfaceId_ == type(IRepayer_v1).interfaceId || super.supportsInterface(interfaceId_); } @@ -92,14 +93,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is uint64 public constant MAX_FEE = 100; /// @notice Time interval between seizes. uint64 public constant SEIZE_DELAY = 7 days; - /// @notice Role associated with the managing of the bonding curve values. - bytes32 public constant RISK_MANAGER_ROLE = "RISK_MANAGER"; - /// @notice Role associated with the managing of setting withdraw addresses - /// and setting the fee. - bytes32 public constant COVER_MANAGER_ROLE = "COVER_MANAGER"; - /// @notice Role that can use buy and sell regardless wether these - /// functions are restricted or not - bytes32 public constant CURVE_INTERACTION_ROLE = "CURVE_USER"; // ======================================================================== // Storage @@ -118,8 +111,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is uint internal _lastSeizeTimestamp; /// @notice Address of the reserve pool. address internal _tokenVault; - /// @notice Restricts buying and selling functionalities to specific role. - bool internal _buyAndSellIsRestricted; /// @notice Storage gap for future upgrades. uint[50] private __gap; @@ -127,12 +118,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is // ======================================================================== // Modifiers - /// @notice Modifier to ensure buy and sell restrictions are met. - modifier onlyIfNotBuyAndSellRestricted() { - _onlyIfNotBuyAndSellRestrictedModifier(); - _; - } - /// @notice Modifier to ensure only the LiquidityVaultController can call /// the function. modifier onlyLiquidityVaultController() { @@ -143,12 +128,12 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is // ======================================================================== // Init Function - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(FM_BC_BondingSurface_Redeeming_v1) initializer { + ) external override(FM_BC_BondingSurface_Redeeming_v2) initializer { __Module_init(orchestrator_, metadata_); address issuanceToken; @@ -156,27 +141,23 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is BondingCurveProperties memory bondingCurveProperties; address liquidityVaultController; uint64 newSeize; - // Indicates whether buying and selling is restricted to the - // CURVE_INTERACTION_ROLE or open to anyone. - bool buyAndSellIsRestricted; ( issuanceToken, acceptedToken, bondingCurveProperties, liquidityVaultController, - newSeize, - buyAndSellIsRestricted + newSeize ) = abi.decode( configData_, - (address, address, BondingCurveProperties, address, uint64, bool) + (address, address, BondingCurveProperties, address, uint64) ); __Module_init(orchestrator_, metadata_); - __FM_BC_BondingSurface_Redeeming_v1_Init( + __FM_BC_BondingSurface_Redeeming_v2_Init( issuanceToken, acceptedToken, bondingCurveProperties ); - __FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Init( - liquidityVaultController, newSeize, buyAndSellIsRestricted + __FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2_Init( + liquidityVaultController, newSeize ); } @@ -186,37 +167,31 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is /// @param liquidityVaultController_ The address of the /// LiquidityVaultController. /// @param newSeize_ The new seize value. - /// @param buyAndSellIsRestricted_ Whether buy and sell is restricted. - function __FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Init( + function __FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2_Init( address liquidityVaultController_, - uint64 newSeize_, - bool buyAndSellIsRestricted_ + uint64 newSeize_ ) internal onlyInitializing { _liquidityVaultController = liquidityVaultController_; - // Set buy and sell restriction to restricted if true. By default buy - // and sell are unrestricted. - _buyAndSellIsRestricted = buyAndSellIsRestricted_; - _setSeize(newSeize_); } // ======================================================================== // Public Getter Functions - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function getSeizableAmount() public view returns (uint amount_) { uint currentBalance = _getCapitalAvailable(); return (currentBalance * _currentSeize) / BPS; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function getCurrentSeize() public view returns (uint64 currentSeize_) { return _currentSeize; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function getLiquidityVaultController() public view @@ -225,7 +200,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is return address(_liquidityVaultController); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function getLastSeizeTimestamp() public view @@ -234,20 +209,11 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is return _lastSeizeTimestamp; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function getTokenVault() public view returns (address tokenVault_) { return address(_tokenVault); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function isBuyAndSellRestricted() - public - view - returns (bool buyAndSellIsRestricted_) - { - return _buyAndSellIsRestricted; - } - /// @inheritdoc IRepayer_v1 function getRepayableAmount() external @@ -261,61 +227,18 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is // Public Mutating Functions // ------------------------------------------------------------------------ - // Mutating - Token Manipulation Functions - - /// @inheritdoc IBondingCurveBase_v1 - /// @dev The buy functionality can be restricted to the - /// CURVE_INTERACTION_ROLE. - function buyFor(address receiver_, uint depositAmount_, uint minAmountOut_) - public - virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) - onlyIfNotBuyAndSellRestricted - { - super.buyFor(receiver_, depositAmount_, minAmountOut_); - } + // Mutating - Permissioned Functions - /// @inheritdoc IBondingCurveBase_v1 - /// @dev The buy functionality can be restricted to the - /// CURVE_INTERACTION_ROLE. - function buy(uint depositAmount_, uint minAmountOut_) - public - virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) - { - buyFor(_msgSender(), depositAmount_, minAmountOut_); - } - - /// @inheritdoc IRedeemingBondingCurveBase_v1 - /// @dev The sell functionality can be restricted to the - /// CURVE_INTERACTION_ROLE. - function sellTo(address receiver_, uint depositAmount_, uint minAmountOut_) - public - virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) - onlyIfNotBuyAndSellRestricted - { - super.sellTo(receiver_, depositAmount_, minAmountOut_); - } - - /// @inheritdoc IRedeemingBondingCurveBase_v1 - /// @dev The sell functionality can be restricted to the - /// CURVE_INTERACTION_ROLE. - function sell(uint depositAmount_, uint minAmountOut_) - public - virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) - { - sellTo(_msgSender(), depositAmount_, minAmountOut_); - } - - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function burnIssuanceToken(uint amount_) external { + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + function burnIssuanceToken(uint amount_) external permissioned { _burn(_msgSender(), amount_); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function burnIssuanceTokenFor(address owner_, uint amount_) external { + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + function burnIssuanceTokenFor(address owner_, uint amount_) + external + permissioned + { if (owner_ != _msgSender()) { // Does not update allowance if set to infinite. _spendAllowance(owner_, _msgSender(), amount_); @@ -324,59 +247,19 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is _burn(owner_, amount_); } - // ------------------------------------------------------------------------ - // Mutating - OnlyLiquidityVaultController Functions - - /// @inheritdoc IRepayer_v1 - function transferRepayment(address to_, uint amount_) - external - validReceiver(to_) - onlyLiquidityVaultController - { - if (amount_ > _getRepayableAmount()) { - revert Repayer__InsufficientCollateralForRepayerTransfer(); - } - __Module_orchestrator.fundingManager().token().safeTransfer( - to_, amount_ - ); - if (MIN_RESERVE > token().balanceOf(address(this))) { - revert FM_BC_BondingSurface_Redeeming_v1__MinReserveReached(); - } - - emit RepaymentTransfer(to_, amount_); - } - - // ------------------------------------------------------------------------ - // Mutating - OnlyCoverManager Functions - - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function restrictBuyAndSell() external onlyModuleRole(COVER_MANAGER_ROLE) { - _buyAndSellIsRestricted = true; - emit BuyAndSellIsRestricted(); - } - - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function unrestrictBuyAndSell() - external - onlyModuleRole(COVER_MANAGER_ROLE) - { - _buyAndSellIsRestricted = false; - emit BuyAndSellIsUnrestricted(); - } - - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function seize(uint amount_) public onlyModuleRole(COVER_MANAGER_ROLE) { + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + function seize(uint amount_) public permissioned { uint seizableAmount = getSeizableAmount(); if (amount_ > seizableAmount) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeizeAmount( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeizeAmount( seizableAmount ); } // solhint-disable-next-line not-rely-on-time else if (_lastSeizeTimestamp + SEIZE_DELAY > block.timestamp) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__SeizeTimeout( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__SeizeTimeout( _lastSeizeTimestamp + SEIZE_DELAY ); } @@ -393,22 +276,19 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is emit CollateralSeized(amount_); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function adjustSeize(uint64 seize_) - public - onlyModuleRole(COVER_MANAGER_ROLE) - { + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + function adjustSeize(uint64 seize_) public permissioned { _setSeize(seize_); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 function setLiquidityVaultControllerContract(address lvc_) external - onlyModuleRole(COVER_MANAGER_ROLE) + permissioned { if (address(lvc_) == address(0) || address(lvc_) == address(this)) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidInputAddress( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidInputAddress( ); } emit LiquidityVaultControllerChanged( @@ -418,80 +298,53 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is } /// @inheritdoc IRepayer_v1 - function setRepayableAmount(uint amount_) - external - onlyModuleRole(COVER_MANAGER_ROLE) - { + function setRepayableAmount(uint amount_) external permissioned { if (amount_ > _getSmallerCaCr()) { revert - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount(); + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount(); } emit RepayableAmountChanged(amount_, _repayableAmount); _repayableAmount = amount_; } - // ------------------------------------------------------------------------ - // Mutating - RedeemingBondingCurveBase_v1 Overrides - - /// @inheritdoc IRedeemingBondingCurveBase_v1 - function setSellFee(uint fee_) - external - virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) - onlyModuleRole(COVER_MANAGER_ROLE) - { - _setSellFee(fee_); + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + function setTokenVault(address tokenVault_) external permissioned { + _setTokenVault(tokenVault_); } // ------------------------------------------------------------------------ - // Mutating - OnlyRiskManager Functions + // Mutating - OnlyLiquidityVaultController Functions - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 - function setCapitalRequired(uint newCapitalRequired_) - public - override( - FM_BC_BondingSurface_Redeeming_v1, IFM_BC_BondingSurface_Redeeming_v1 - ) - onlyModuleRole(RISK_MANAGER_ROLE) + /// @inheritdoc IRepayer_v1 + function transferRepayment(address to_, uint amount_) + external + validReceiver(to_) + onlyLiquidityVaultController { - _setCapitalRequired(newCapitalRequired_); - } + if (amount_ > _getRepayableAmount()) { + revert Repayer__InsufficientCollateralForRepayerTransfer(); + } + __Module_orchestrator.fundingManager().token().safeTransfer( + to_, amount_ + ); + if (MIN_RESERVE > token().balanceOf(address(this))) { + revert FM_BC_BondingSurface_Redeeming_v2__MinReserveReached(); + } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 - function setBasePriceMultiplier(uint newBasePriceMultiplier_) - public - override( - FM_BC_BondingSurface_Redeeming_v1, IFM_BC_BondingSurface_Redeeming_v1 - ) - onlyModuleRole(RISK_MANAGER_ROLE) - { - _setBasePriceMultiplier(newBasePriceMultiplier_); + emit RepaymentTransfer(to_, amount_); } // ------------------------------------------------------------------------ - // Mutating - OnlyOrchestratorAdmin Functions + // Mutating - Out of Order - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - function setTokenVault(address tokenVault_) - external - onlyOrchestratorAdmin - { - _setTokenVault(tokenVault_); - } - - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function withdrawProjectCollateralFee( address, /* receiver_ */ uint /* amount_ */ - ) - public - view - override(BondingCurveBase_v1, IBondingCurveBase_v1) - onlyOrchestratorAdmin - { + ) public view override(BondingCurveBase_v2, IBondingCurveBase_v2) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidFunctionality( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidFunctionality( ); } @@ -515,7 +368,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is function _setSeize(uint64 seize_) internal { if (seize_ > MAX_SEIZE) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeize( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeize( seize_ ); } @@ -569,27 +422,14 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is function _ensureOnlyLiquidityVaultController() internal view { if (_msgSender() != address(_liquidityVaultController)) { revert - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidLiquidityVaultController( - _msgSender() - ); - } - } - - /// @notice Validate if buy and sell is restricted, and if so - /// check if the caller has the CURVE_INTERACTION_ROLE. - function _onlyIfNotBuyAndSellRestrictedModifier() internal view { - if (_buyAndSellIsRestricted) { - _checkRoleModifier( - __Module_orchestrator.authorizer().generateRoleId( - address(this), CURVE_INTERACTION_ROLE - ), + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidLiquidityVaultController( _msgSender() ); } } // ------------------------------------------------------------------------ - // Internal - BondingCurveBase_v1 Overrides + // Internal - BondingCurveBase_v2 Overrides /// @notice Validates the project fee. /// @dev Reverts if the project fee is greater than the maximum fee. @@ -597,7 +437,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is function _validateProjectFee(uint projectFee_) internal pure - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) { if (projectFee_ > MAX_FEE) { revert Module__BondingCurveBase__InvalidFeePercentage(); diff --git a/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol b/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol similarity index 84% rename from src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol rename to src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol index aecfea3ae..32aa13b26 100644 --- a/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol +++ b/src/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol @@ -2,25 +2,25 @@ pragma solidity 0.8.23; // Internal -import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {RedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; -import {BondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; +import {RedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; +import {BondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import {FixedPointMathLib} from "src/modules/lib/FixedPointMathLib.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; import { - IFM_BC_BondingSurface_Redeeming_v1, + IFM_BC_BondingSurface_Redeeming_v2, IFundingManager_v1 -} from "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; +} from "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IBondingSurface} from "@fm/bondingCurve/interfaces/IBondingSurface.sol"; -import {IAuthorizer_v1} from "src/modules/authorizer/IAuthorizer_v1.sol"; +import {IAuthorizer_v2} from "src/modules/authorizer/IAuthorizer_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -36,8 +36,8 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * bonding curve. * * @dev This contract inherits functionalties from the contracts: - * - BondingCurveBase_v1 - * - RedeemingBondingCurveBase_v1 + * - BondingCurveBase_v2 + * - RedeemingBondingCurveBase_v2 * - Repayer * The contract should be used by the orchestrator admin or manager * to manage all the configuration for the bonding curve as well as the @@ -51,26 +51,26 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.1 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -contract FM_BC_BondingSurface_Redeeming_v1 is - RedeemingBondingCurveBase_v1, - IFM_BC_BondingSurface_Redeeming_v1 +contract FM_BC_BondingSurface_Redeeming_v2 is + RedeemingBondingCurveBase_v2, + IFM_BC_BondingSurface_Redeeming_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public view virtual - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (bool supportsInterface_) { return interfaceId_ - == type(IFM_BC_BondingSurface_Redeeming_v1).interfaceId + == type(IFM_BC_BondingSurface_Redeeming_v2).interfaceId || interfaceId_ == type(IFundingManager_v1).interfaceId || super.supportsInterface(interfaceId_); } @@ -104,12 +104,12 @@ contract FM_BC_BondingSurface_Redeeming_v1 is // ======================================================================== // Init Function - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external virtual override(Module_v1) initializer { + ) external virtual override(Module_v2) initializer { address issuanceToken; address acceptedToken; BondingCurveProperties memory bondingCurveProperties; @@ -118,7 +118,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is abi.decode(configData_, (address, address, BondingCurveProperties)); __Module_init(orchestrator_, metadata_); - __FM_BC_BondingSurface_Redeeming_v1_Init( + __FM_BC_BondingSurface_Redeeming_v2_Init( issuanceToken, acceptedToken, bondingCurveProperties ); } @@ -128,7 +128,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is /// @param issuanceToken_ The token that is used to issue bonds. /// @param acceptedToken_ The token that is accepted as collateral. /// @param bondingCurveProperties_ The properties of the bonding curve. - function __FM_BC_BondingSurface_Redeeming_v1_Init( + function __FM_BC_BondingSurface_Redeeming_v2_Init( address issuanceToken_, address acceptedToken_, BondingCurveProperties memory bondingCurveProperties_ @@ -150,8 +150,8 @@ contract FM_BC_BondingSurface_Redeeming_v1 is ) ) { revert - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidBondingSurfaceFormula(); + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidBondingSurfaceFormula(); } // Set formula contract. _formula = IBondingSurface(bondingCurveProperties_.formula); @@ -177,11 +177,11 @@ contract FM_BC_BondingSurface_Redeeming_v1 is // ======================================================================== // Public Getter Functions - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function getStaticPriceForBuying() external view - override(BondingCurveBase_v1, IBondingCurveBase_v1) + override(BondingCurveBase_v2, IBondingCurveBase_v2) returns (uint staticPriceForBuying_) { return _formula.spotPrice( @@ -189,12 +189,12 @@ contract FM_BC_BondingSurface_Redeeming_v1 is ); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 /// @dev The return value is formatted in PPM. function getStaticPriceForSelling() external view - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2, IRedeemingBondingCurveBase_v2) returns (uint staticPriceForSelling_) { return _formula.spotPrice( @@ -202,7 +202,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is ); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function getBondingSurfaceFormula() external view @@ -211,7 +211,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is return address(_formula); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function getCapitalRequired() external view @@ -220,7 +220,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is return _capitalRequired; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function getBasePriceMultiplier() external view @@ -229,7 +229,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is return _basePriceMultiplier; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function getBasePriceToCapitalRatio() external view @@ -238,7 +238,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is return _basePriceToCapitalRatio; } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function calculateBasePriceToCapitalRatio( uint capitalRequired_, uint basePriceMultiplier_ @@ -265,22 +265,22 @@ contract FM_BC_BondingSurface_Redeeming_v1 is // Mutating Functions // ------------------------------------------------------------------------ - // Mutating - OnlyOrchestratorAdmin Functions + // Mutating - Permissioned Functions - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function setCapitalRequired(uint newCapitalRequired_) public virtual - onlyOrchestratorAdmin + permissioned { _setCapitalRequired(newCapitalRequired_); } - /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v1 + /// @inheritdoc IFM_BC_BondingSurface_Redeeming_v2 function setBasePriceMultiplier(uint newBasePriceMultiplier_) public virtual - onlyOrchestratorAdmin + permissioned { _setBasePriceMultiplier(newBasePriceMultiplier_); } @@ -305,7 +305,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is token().safeTransfer(to_, amount_); if (MIN_RESERVE > token().balanceOf(address(this))) { - revert FM_BC_BondingSurface_Redeeming_v1__MinReserveReached(); + revert FM_BC_BondingSurface_Redeeming_v2__MinReserveReached(); } emit TransferOrchestratorToken(to_, amount_); @@ -331,7 +331,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is /// @param newCapitalRequired_ the new capital that is required. function _setCapitalRequired(uint newCapitalRequired_) internal { if (newCapitalRequired_ == 0) { - revert FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount(); + revert FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount(); } emit CapitalRequiredChanged(_capitalRequired, newCapitalRequired_); _capitalRequired = newCapitalRequired_; @@ -343,7 +343,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is /// @param newBasePriceMultiplier_ The new base price multiplier. function _setBasePriceMultiplier(uint newBasePriceMultiplier_) internal { if (newBasePriceMultiplier_ == 0) { - revert FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount(); + revert FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount(); } emit BasePriceMultiplierChanged( _basePriceMultiplier, newBasePriceMultiplier_ @@ -377,7 +377,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is basePriceMultiplier_, capitalRequired_, FixedPointMathLib.WAD ); if (basePriceToCapitalRatio_ > 1e36) { - revert FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount(); + revert FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount(); } } @@ -386,7 +386,7 @@ contract FM_BC_BondingSurface_Redeeming_v1 is /// @notice Calculates the amount of tokens to mint for a given deposit /// amount using the formula contract. - /// @dev This internal function is an override of BondingCurveBase_v1's + /// @dev This internal function is an override of BondingCurveBase_v2's /// virtual function. /// @param depositAmount_ The amount of collateral deposited to /// purchase tokens. @@ -394,12 +394,12 @@ contract FM_BC_BondingSurface_Redeeming_v1 is function _issueTokensFormulaWrapper(uint depositAmount_) internal view - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (uint mintAmount_) { uint capitalAvailable = _getCapitalAvailable(); if (capitalAvailable == 0) { - revert FM_BC_BondingSurface_Redeeming_v1__NoCapitalAvailable(); + revert FM_BC_BondingSurface_Redeeming_v2__NoCapitalAvailable(); } mintAmount_ = _formula.tokenOut( @@ -410,20 +410,20 @@ contract FM_BC_BondingSurface_Redeeming_v1 is /// @notice Calculates the amount of collateral to be received when /// redeeming a given amount of tokens. /// @dev This internal function is an override of - /// RedeemingBondingCurveBase_v1's virtual function. + /// RedeemingBondingCurveBase_v2's virtual function. /// @param depositAmount_ The amount of tokens to be redeemed for /// collateral. /// @return redeemAmount_ The amount of collateral that will be received. function _redeemTokensFormulaWrapper(uint depositAmount_) internal view - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint redeemAmount_) { // Subtract fee collected from capital held by contract. uint capitalAvailable = _getCapitalAvailable(); if (capitalAvailable == 0) { - revert FM_BC_BondingSurface_Redeeming_v1__NoCapitalAvailable(); + revert FM_BC_BondingSurface_Redeeming_v2__NoCapitalAvailable(); } redeemAmount_ = _formula.tokenIn( depositAmount_, capitalAvailable, _basePriceToCapitalRatio @@ -431,11 +431,11 @@ contract FM_BC_BondingSurface_Redeeming_v1 is // The asset pool must never be empty. if (capitalAvailable - redeemAmount_ < MIN_RESERVE) { - revert FM_BC_BondingSurface_Redeeming_v1__MinReserveReached(); + revert FM_BC_BondingSurface_Redeeming_v2__MinReserveReached(); } } - /// @inheritdoc BondingCurveBase_v1 + /// @inheritdoc BondingCurveBase_v2 function _processCollateralTokensForBuyOperation(uint _amount) internal virtual diff --git a/src/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol b/src/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol deleted file mode 100644 index 41dc3a0b2..000000000 --- a/src/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.23; - -// Internal Dependencies - -import { - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFundingManager_v1 -} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; - -/** - * @title Inverter Restricted Bancor Virtual Supply Bonding Curve Funding Manager - * - * @notice This contract enables the issuance and redeeming of tokens on a - * bonding curve, using a virtual supply for both the issuance and - * the collateral as input. It integrates Aragon's Bancor Formula to - * manage the calculations for token issuance and redemption rates - * based on specified reserve ratios. - * - * @dev It overrides the `buyFor()` and `sellTo()` functions of its parent - * contract to limit them to callers holding a "Curve Interaction" - * role. Since the upstream functions `buy()` and `sell()` call these - * functions internally, they also become gated. - * - * PLEASE NOTE: This means that the workflow itself can only mint - * tokens through buying and selling by somebody with the - * `CURVE_INTERACTION_ROLE`, but NOT that there are no other ways to - * mint tokens. The Bonding Curve uses an external token contract, and - * there is no guarantee that said uses an external token contract, and - * there is no guarantee that said contract won't have an additional - * way to mint tokens (and potentially sell them on the cruve to - * receive backing collateral) - * - * @custom:security-contact security@inverter.network - * In case of any concerns or findings, please refer - * to our Security Policy at security.inverter.network - * or email us directly! - * - * @custom:version 1.1.3 - * - * @author Inverter Network - */ -contract FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1 is - FM_BC_Bancor_Redeeming_VirtualSupply_v1 -{ - // ------------------------------------------------------------------------- - // Errors - - /// @notice The feature is deactivated in this implementation. - error Module__FM_BC_Restricted_Bancor_Redeeming_VirtualSupply__FeatureDeactivated( - ); - - // ------------------------------------------------------------------------- - // Storage - - /// @dev Minter/Burner Role. - bytes32 public constant CURVE_INTERACTION_ROLE = "CURVE_USER"; - - /// @dev Storage gap for future upgrades. - uint[50] private __gap; - - // ------------------------------------------------------------------------- - // Public Functions - - /// @inheritdoc FM_BC_Bancor_Redeeming_VirtualSupply_v1 - /// @dev Adds additional role check to the buyFor function. - function buyFor(address _receiver, uint _depositAmount, uint _minAmountOut) - public - override - onlyModuleRole(CURVE_INTERACTION_ROLE) - { - super.buyFor(_receiver, _depositAmount, _minAmountOut); - } - - /// @inheritdoc FM_BC_Bancor_Redeeming_VirtualSupply_v1 - /// @dev Adds addtional role check to the sellTo function. - function sellTo(address _receiver, uint _depositAmount, uint _minAmountOut) - public - override - onlyModuleRole(CURVE_INTERACTION_ROLE) - { - super.sellTo(_receiver, _depositAmount, _minAmountOut); - } -} diff --git a/src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.sol b/src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.sol similarity index 94% rename from src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.sol rename to src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.sol index 08419bffd..9cbf68db6 100644 --- a/src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.sol +++ b/src/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.23; // Internal Dependencies -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; // External Interfaces import {IERC20Issuance_v1} from "@ex/token/interfaces/IERC20Issuance_v1.sol"; @@ -35,20 +35,20 @@ import {ERC165Upgradeable} from * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version 1.1.3 + * @custom:version 2.0.0 * * @author Inverter Network */ -abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { +abstract contract BondingCurveBase_v2 is IBondingCurveBase_v2, Module_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId == type(IBondingCurveBase_v1).interfaceId + return interfaceId == type(IBondingCurveBase_v2).interfaceId || super.supportsInterface(interfaceId); } @@ -95,42 +95,48 @@ abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { // ------------------------------------------------------------------------- // Public Functions - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function buyFor(address _receiver, uint _depositAmount, uint _minAmountOut) public virtual + permissioned buyingIsEnabled validReceiver(_receiver) { _buyOrder(_receiver, _depositAmount, _minAmountOut); } - /// @inheritdoc IBondingCurveBase_v1 - function buy(uint _depositAmount, uint _minAmountOut) public virtual { - buyFor(_msgSender(), _depositAmount, _minAmountOut); + /// @inheritdoc IBondingCurveBase_v2 + function buy(uint _depositAmount, uint _minAmountOut) + public + virtual + permissioned + buyingIsEnabled + { + _buyOrder(_msgSender(), _depositAmount, _minAmountOut); } // ------------------------------------------------------------------------- - // OnlyOrchestrator Functions + // Permissioned Functions - /// @inheritdoc IBondingCurveBase_v1 - function openBuy() external virtual onlyOrchestratorAdmin { + /// @inheritdoc IBondingCurveBase_v2 + function openBuy() external virtual permissioned { buyIsOpen = true; emit BuyingEnabled(); } - /// @inheritdoc IBondingCurveBase_v1 - function closeBuy() external virtual onlyOrchestratorAdmin { + /// @inheritdoc IBondingCurveBase_v2 + function closeBuy() external virtual permissioned { buyIsOpen = false; emit BuyingDisabled(); } - /// @inheritdoc IBondingCurveBase_v1 - function setBuyFee(uint _fee) external virtual onlyOrchestratorAdmin { + /// @inheritdoc IBondingCurveBase_v2 + function setBuyFee(uint _fee) external virtual permissioned { _setBuyFee(_fee); } - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function calculatePurchaseReturn(uint _depositAmount) public view @@ -166,12 +172,12 @@ abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { ); } - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function withdrawProjectCollateralFee(address _receiver, uint _amount) public virtual + permissioned validReceiver(_receiver) - onlyOrchestratorAdmin { if (_amount > projectCollateralFeeCollected) { revert Module__BondingCurveBase__InvalidWithdrawAmount(); @@ -189,7 +195,7 @@ abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { // ------------------------------------------------------------------------- // Public Functions - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 /// @dev Always returns the address of the issuance token, even if the // issuance token is wrapped. function getIssuanceToken() external view virtual returns (address) { @@ -218,7 +224,7 @@ abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { // ------------------------------------------------------------------------- // Public Functions Implemented in Downstream Contract - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function getStaticPriceForBuying() external view virtual returns (uint); // ------------------------------------------------------------------------- @@ -462,7 +468,7 @@ abstract contract BondingCurveBase_v1 is IBondingCurveBase_v1, Module_v1 { // transfer fee amount _token.safeTransfer(_treasury, _feeAmount); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(_token), _treasury, _feeAmount ); } diff --git a/src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol b/src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol similarity index 89% rename from src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol rename to src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol index 58707b426..6601ae8db 100644 --- a/src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol +++ b/src/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; // Internal Dependencies -import {BondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; +import {BondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -25,7 +25,7 @@ import {ERC165Upgradeable} from * @notice Manages the redemption of issuance for collateral along a bonding curve in the * Inverter Network, including fee handling and sell functionality control. * - * @dev Inherits from {BondingCurveBase_v1}. Extends by providing core functionalities for + * @dev Inherits from {BondingCurveBase_v2}. Extends by providing core functionalities for * redeem operations, fee adjustments, and redemption calculations. * Fee calculations utilize BPS for precision. Redeem-specific calculations should be * implemented in derived contracts. @@ -35,23 +35,23 @@ import {ERC165Upgradeable} from * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version 1.1.4 + * @custom:version 2.0.0 * * @author Inverter Network */ -abstract contract RedeemingBondingCurveBase_v1 is - IRedeemingBondingCurveBase_v1, - BondingCurveBase_v1 +abstract contract RedeemingBondingCurveBase_v2 is + IRedeemingBondingCurveBase_v2, + BondingCurveBase_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (bool) { - return interfaceId == type(IRedeemingBondingCurveBase_v1).interfaceId + return interfaceId == type(IRedeemingBondingCurveBase_v2).interfaceId || super.supportsInterface(interfaceId); } @@ -81,42 +81,48 @@ abstract contract RedeemingBondingCurveBase_v1 is // ------------------------------------------------------------------------- // Public Functions - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 function sellTo(address _receiver, uint _depositAmount, uint _minAmountOut) public virtual + permissioned sellingIsEnabled validReceiver(_receiver) { _sellOrder(_receiver, _depositAmount, _minAmountOut); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 - function sell(uint _depositAmount, uint _minAmountOut) public virtual { - sellTo(_msgSender(), _depositAmount, _minAmountOut); + /// @inheritdoc IRedeemingBondingCurveBase_v2 + function sell(uint _depositAmount, uint _minAmountOut) + public + virtual + permissioned + sellingIsEnabled + { + _sellOrder(_msgSender(), _depositAmount, _minAmountOut); } // ------------------------------------------------------------------------- - // OnlyOrchestrator Functions + // Permissioned Functions - /// @inheritdoc IRedeemingBondingCurveBase_v1 - function openSell() external virtual onlyOrchestratorAdmin { + /// @inheritdoc IRedeemingBondingCurveBase_v2 + function openSell() external virtual permissioned { sellIsOpen = true; emit SellingEnabled(); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 - function closeSell() external virtual onlyOrchestratorAdmin { + /// @inheritdoc IRedeemingBondingCurveBase_v2 + function closeSell() external virtual permissioned { sellIsOpen = false; emit SellingDisabled(); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 - function setSellFee(uint _fee) external virtual onlyOrchestratorAdmin { + /// @inheritdoc IRedeemingBondingCurveBase_v2 + function setSellFee(uint _fee) external virtual permissioned { _setSellFee(_fee); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 function calculateSaleReturn(uint _depositAmount) public view @@ -155,7 +161,7 @@ abstract contract RedeemingBondingCurveBase_v1 is // ------------------------------------------------------------------------- // Public Functions Implemented in Downstream Contract - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 function getStaticPriceForSelling() external view virtual returns (uint); // ------------------------------------------------------------------------- diff --git a/src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v1.sol b/src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v2.sol similarity index 82% rename from src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v1.sol rename to src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v2.sol index 863c956c9..03c2ecad2 100644 --- a/src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v1.sol +++ b/src/modules/fundingManager/bondingCurve/interfaces/IBondingCurveBase_v2.sol @@ -1,7 +1,27 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -interface IBondingCurveBase_v1 { +/** + * @title Inverter Bonding Curve Funding Manager Base Interface + * + * @notice Manages the issuance of token for collateral along a bonding curve in the + * Inverter Network, including fee handling and sell functionality control. + * + * @dev Provides core functionalities for issuance operations, fee adjustments, + * and issuance calculations. + * Fee calculations utilize BPS for precision. Issuance-specific calculations should be + * implemented in derived contracts. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer + * to our Security Policy at security.inverter.network + * or email us directly! + * + * @custom:version 2.0.0 + * + * @author Inverter Network + */ +interface IBondingCurveBase_v2 { //-------------------------------------------------------------------------- // Errors @@ -98,6 +118,7 @@ interface IBondingCurveBase_v1 { // Functions /// @notice Buy tokens on behalf of a specified receiver address. + /// @dev Function access controlled by authorizer. /// @dev Redirects to the internal function `_buyOrder` by passing the receiver address and deposit amount. /// @param _receiver The address that will receive the bought tokens. /// @param _depositAmount The amount of collateral token deposited. @@ -106,24 +127,25 @@ interface IBondingCurveBase_v1 { external; /// @notice Buy tokens for the sender's address. + /// @dev Function access controlled by authorizer. /// @dev Redirects to the internal function `_buyOrder` by passing the sender's address and deposit amount. /// @param _depositAmount The amount of collateral token depoisited. /// @param _minAmountOut The minimum acceptable amount the user expects to receive from the transaction. function buy(uint _depositAmount, uint _minAmountOut) external; /// @notice Opens the buying functionality for the token. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// Reverts if buying is already open. + /// @dev Function access controlled by authorizer. + /// @dev Reverts if buying is already open. function openBuy() external; /// @notice Closes the buying functionality for the token. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// Reverts if buying is already closed. + /// @dev Function access controlled by authorizer. + /// @dev Reverts if buying is already closed. function closeBuy() external; /// @notice Sets the fee percentage for buying tokens, payed in collateral. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// The fee cannot exceed 10000 basis points. Reverts if an invalid fee is provided. + /// @dev Function access controlled by authorizer. + /// @dev The fee cannot exceed 10000 basis points. Reverts if an invalid fee is provided. /// @param _fee The fee in basis points. function setBuyFee(uint _fee) external; @@ -141,6 +163,7 @@ interface IBondingCurveBase_v1 { returns (uint mintAmount); /// @notice Withdraw project collateral fee to the receiver address. + /// @dev Function access controlled by authorizer. /// @param _receiver The address that will receive the fee. /// @param _amount The amount of fee to withdraw. function withdrawProjectCollateralFee(address _receiver, uint _amount) diff --git a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v2.sol similarity index 95% rename from src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol rename to src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v2.sol index fdaf3d1d5..d164f856d 100644 --- a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol +++ b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v2.sol @@ -13,11 +13,11 @@ pragma solidity ^0.8.0; * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version 1.1.2 + * @custom:version v2.0.0 * * @author Inverter Network */ -interface IFM_BC_Bancor_Redeeming_VirtualSupply_v1 { +interface IFM_BC_Bancor_Redeeming_VirtualSupply_v2 { // ======================================================================== // Errors @@ -111,14 +111,14 @@ interface IFM_BC_Bancor_Redeeming_VirtualSupply_v1 { /// @notice Set the reserve ratio used for issuing tokens on a bonding /// curve. - /// @dev This function can only be called by the {Orchestrator_v1} admin. + /// @dev Function access controlled by authorizer. /// @param reserveRatio_ The new reserve ratio for buying, expressed in /// PPM. function setReserveRatioForBuying(uint32 reserveRatio_) external; /// @notice Set the reserve ratio used for redeeming tokens on a bonding /// curve. - /// @dev This function can only be called by the {Orchestrator_v1} admin. + /// @dev Function access controlled by authorizer. /// @param reserveRatio_ The new reserve ratio for selling, expressed in /// PPM. function setReserveRatioForSelling(uint32 reserveRatio_) external; diff --git a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol similarity index 77% rename from src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol rename to src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol index 23a5642ef..651d125e5 100644 --- a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol +++ b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; //Internal Dependencies -import {IFM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; +import {IFM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; /** @@ -19,45 +19,45 @@ import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -interface IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is - IFM_BC_BondingSurface_Redeeming_v1, +interface IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 is + IFM_BC_BondingSurface_Redeeming_v2, IRepayer_v1 { // ======================================================================== // Errors /// @notice Invalid address passed as argument. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidInputAddress( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidInputAddress( ); /// @notice Buy fee can not be set. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidFunctionality( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidFunctionality( ); /// @notice Invalid Liquidity Vault Controller. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidLiquidityVaultController( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidLiquidityVaultController( address invalidLiquidityVaultController ); /// @notice Seize cannot be bigger than MAX_SEIZE = 1%. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeize( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeize( uint64 seize ); /// @notice Amount exeeds the seizable amount, defined by a percentage of /// total collateral. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeizeAmount( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeizeAmount( uint amount ); /// @notice Timestamp is still in the future, so a seize is not allowed. - error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__SeizeTimeout( + error FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__SeizeTimeout( uint allowedTimestamp ); @@ -78,9 +78,6 @@ interface IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is /// @notice Emits when the token vault gets updated. event TokenVaultSet(address tokenVault); - /// @notice Emits when buy and sell restriction is set. - event BuyAndSellIsRestricted(); - /// @notice Emits when buy and sell restriction is removed. event BuyAndSellIsUnrestricted(); @@ -118,61 +115,48 @@ interface IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 is /// @return tokenVault_ The address of the token vault. function getTokenVault() external view returns (address tokenVault_); - /// @notice Returns whether buy and sell is restricted. - /// @return buyAndSellIsRestricted_ Whether buy and sell is restricted. - function isBuyAndSellRestricted() - external - view - returns (bool buyAndSellIsRestricted_); - // ======================================================================== // Public Mutating Functions - // Mutating - Token Manipulation Functions + // ------------------------------------------------------------------------- + // Mutating - Permissioned Functions /// @notice Burn amount of tokens from message sender. + /// @dev Function access controlled by authorizer. /// @param amount_ Amount token to be burned. function burnIssuanceToken(uint amount_) external; /// @notice Burn `amount` tokens belonging to `owner`. + /// @dev Function access controlled by authorizer. /// @param owner_ Address whose tokens will be burnt. /// @param amount_ Burn amount. function burnIssuanceTokenFor(address owner_, uint amount_) external; - // ------------------------------------------------------------------------- - // Mutating - OnlyCoverManager Functions - - /// @notice Restricts buying and selling functionalities to the - /// CURVE_INTERACTION_ROLE. - /// @dev Only callable by the COVER_MANAGER_ROLE. - function restrictBuyAndSell() external; - - /// @notice Unrestricts buying and selling functionalities to the - /// CURVE_INTERACTION_ROLE. - /// @dev Only callable by the COVER_MANAGER_ROLE. - function unrestrictBuyAndSell() external; - - /// @notice Allows the COVER_MANAGER_ROLE to seize assets from this pool. - /// @dev As the COVER_MANAGER_ROLE has ability to basically rug - /// the projects, a timelock and max. - /// seizable percentage has been added. + /// @notice Seizes assets from this pool. + /// @dev Function access controlled by authorizer. + /// @dev This function has a timelock and max. + /// seizable percentage to prevent rugging the projects. /// @param amount_ Number of tokens to be removed from the pool. function seize(uint amount_) external; /// @notice Adjust the seize percentage, which is seizable from the /// contract. + /// @dev Function access controlled by authorizer. /// @param seize_ The seize in percentage, expressed as BPS. function adjustSeize(uint64 seize_) external; /// @notice Sets a new liquidity valut controller address. + /// @dev Function access controlled by authorizer. /// @param lvc_ Address of the liquidity vault controller. function setLiquidityVaultControllerContract(address lvc_) external; - // ------------------------------------------------------------------------- - // Mutating - OnlyOrchestratorAdmin Functions + /// @notice Sets the Repayable amount. + /// @dev Function access controlled by authorizer. + /// @param amount_ The new Repayable amount. + function setRepayableAmount(uint amount_) external; /// @notice Sets the token vault address. - /// @dev Only callable by OrchestratorAdmin. + /// @dev Function access controlled by authorizer. /// @param tokenVault_ The address of the token vault. function setTokenVault(address tokenVault_) external; } diff --git a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol similarity index 89% rename from src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol rename to src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol index eb69c5897..722a7552c 100644 --- a/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol +++ b/src/modules/fundingManager/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.0; // Internal Dependencies import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; /** * @title Inverter Redeeming Bonding Surface Funding Manager Interface @@ -17,30 +17,30 @@ import {IRedeemingBondingCurveBase_v1} from * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -interface IFM_BC_BondingSurface_Redeeming_v1 is +interface IFM_BC_BondingSurface_Redeeming_v2 is IFundingManager_v1, - IRedeemingBondingCurveBase_v1 + IRedeemingBondingCurveBase_v2 { // ======================================================================== // Errors /// @notice Invalid Bonding Surface Formula contract. - error FM_BC_BondingSurface_Redeeming_v1__InvalidBondingSurfaceFormula(); + error FM_BC_BondingSurface_Redeeming_v2__InvalidBondingSurfaceFormula(); /// @notice Amount does not match the requirements. - error FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount(); + error FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount(); /// @notice No capital is available. - error FM_BC_BondingSurface_Redeeming_v1__NoCapitalAvailable(); + error FM_BC_BondingSurface_Redeeming_v2__NoCapitalAvailable(); /// @notice The minimum reserve has been reached. - error FM_BC_BondingSurface_Redeeming_v1__MinReserveReached(); + error FM_BC_BondingSurface_Redeeming_v2__MinReserveReached(); // ======================================================================== // Events @@ -135,13 +135,15 @@ interface IFM_BC_BondingSurface_Redeeming_v1 is // Public Mutating Functions // ------------------------------------------------------------------------ - // Mutating - OnlyOrchestratorAdmin Functions + // Mutating - Permissioned Functions /// @notice Update the capital required used for the bonding curve. + /// @dev Function access controlled by authorizer. /// @param newCapitalRequired_ The new capital required. function setCapitalRequired(uint newCapitalRequired_) external; /// @notice Update the base price multiplier used for the bonding curve. + /// @dev Function access controlled by authorizer. /// @param newBasePriceMultiplier_ The new base price multiplier. function setBasePriceMultiplier(uint newBasePriceMultiplier_) external; } diff --git a/src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol b/src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol similarity index 87% rename from src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol rename to src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol index 4dfd770ce..69320801a 100644 --- a/src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol +++ b/src/modules/fundingManager/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; /** * @title Inverter Redeeming Bonding Curve Funding Manager Base Interface @@ -16,11 +16,11 @@ import {IBondingCurveBase_v1} from * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version 1.1.3 + * @custom:version 2.0.0 * * @author Inverter Network */ -interface IRedeemingBondingCurveBase_v1 is IBondingCurveBase_v1 { +interface IRedeemingBondingCurveBase_v2 is IBondingCurveBase_v2 { //-------------------------------------------------------------------------- // Errors @@ -61,6 +61,7 @@ interface IRedeemingBondingCurveBase_v1 is IBondingCurveBase_v1 { // Functions /// @notice Redeem tokens and directs the proceeds to a specified receiver address. + /// @dev Function access controlled by authorizer. /// @dev This function wraps the `_sellOrder` internal function with specified parameters to handle /// the transaction and direct the proceeds. /// @param _receiver The address that will receive the redeemed tokens. @@ -70,24 +71,25 @@ interface IRedeemingBondingCurveBase_v1 is IBondingCurveBase_v1 { external; /// @notice Redeem collateral for the sender's address. + /// @dev Function access controlled by authorizer. /// @dev Redirects to the internal function `_sellOrder` by passing the sender's address and deposit amount. /// @param _depositAmount The amount of issued token deposited. /// @param _minAmountOut The minimum acceptable amount the user expects to receive from the transaction. function sell(uint _depositAmount, uint _minAmountOut) external; /// @notice Opens the selling functionality for the collateral. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// Reverts if selling is already open. + /// @dev Function access controlled by authorizer. + /// @dev Reverts if selling is already open. function openSell() external; /// @notice Closes the selling functionality for the collateral. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// Reverts if selling is already closed. + /// @dev Function access controlled by authorizer. + /// @dev Reverts if selling is already closed. function closeSell() external; /// @notice Sets the fee percentage for selling collateral, payed in collateral. - /// @dev Only callable by the {Orchestrator_v1} admin. - /// The fee cannot exceed 10000 basis points. Reverts if an invalid fee is provided. + /// @dev Function access controlled by authorizer. + /// @dev The fee cannot exceed 10000 basis points. Reverts if an invalid fee is provided. /// @param _fee The fee in basis points. function setSellFee(uint _fee) external; diff --git a/src/modules/fundingManager/depositVault/FM_DepositVault_v1.sol b/src/modules/fundingManager/depositVault/FM_DepositVault_v1.sol index e2d5b3fe1..823d0748f 100644 --- a/src/modules/fundingManager/depositVault/FM_DepositVault_v1.sol +++ b/src/modules/fundingManager/depositVault/FM_DepositVault_v1.sol @@ -2,14 +2,14 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import {IFM_DepositVault_v1} from "@fm/depositVault/interfaces/IFM_DepositVault_v1.sol"; // Internal Dependencies -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/extensions/IERC20Metadata.sol"; @@ -40,14 +40,14 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; contract FM_DepositVault_v1 is IFundingManager_v1, IFM_DepositVault_v1, - Module_v1 + Module_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { return interfaceId == type(IFundingManager_v1).interfaceId @@ -73,9 +73,9 @@ contract FM_DepositVault_v1 is //-------------------------------------------------------------------------- // Init Function - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData ) external override initializer { @@ -118,7 +118,7 @@ contract FM_DepositVault_v1 is } //-------------------------------------------------------------------------- - // OnlyOrchestrator Mutating Functions + // PaymentClient Mutating Functions /// @inheritdoc IFundingManager_v1 function transferOrchestratorToken(address to, uint amount) @@ -127,7 +127,6 @@ contract FM_DepositVault_v1 is validAddress(to) { token().safeTransfer(to, amount); - emit TransferOrchestratorToken(to, amount); } @@ -149,7 +148,7 @@ contract FM_DepositVault_v1 is // transfer fee amount token_.safeTransfer(treasury_, feeAmount_); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(token_), treasury_, feeAmount_ ); } diff --git a/src/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.sol b/src/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.sol similarity index 86% rename from src/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.sol rename to src/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.sol index 8d6724e63..dbad6086e 100644 --- a/src/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.sol +++ b/src/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.23; // Internal -import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {IFM_EXT_TokenVault_v1} from - "src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; +import {IFM_EXT_TokenVault_v2} from + "src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -24,22 +24,22 @@ import {ERC165Upgradeable} from * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -contract FM_EXT_TokenVault_v1 is IFM_EXT_TokenVault_v1, Module_v1 { +contract FM_EXT_TokenVault_v2 is IFM_EXT_TokenVault_v2, Module_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId_) public view virtual - override(Module_v1) + override(Module_v2) returns (bool supportsInterface_) { - return interfaceId_ == type(IFM_EXT_TokenVault_v1).interfaceId + return interfaceId_ == type(IFM_EXT_TokenVault_v2).interfaceId || super.supportsInterface(interfaceId_); } @@ -57,11 +57,11 @@ contract FM_EXT_TokenVault_v1 is IFM_EXT_TokenVault_v1, Module_v1 { // ======================================================================== // Public Mutating Functions - /// @inheritdoc IFM_EXT_TokenVault_v1 + /// @inheritdoc IFM_EXT_TokenVault_v2 function withdraw(address token_, uint amount_, address recipient_) external virtual - onlyOrchestratorAdmin + permissioned validAddress(token_) amountIsValid(amount_) validAddress(recipient_) diff --git a/src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v1.sol b/src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v2.sol similarity index 92% rename from src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v1.sol rename to src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v2.sol index a880e6932..a2b20d550 100644 --- a/src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v1.sol +++ b/src/modules/fundingManager/extensions/interfaces/IFM_EXT_TokenVault_v2.sol @@ -13,13 +13,13 @@ pragma solidity ^0.8.0; * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:inverter-standard-version v0.1.0 * * @author Inverter Network */ -interface IFM_EXT_TokenVault_v1 { +interface IFM_EXT_TokenVault_v2 { // ======================================================================== // Errors @@ -41,7 +41,7 @@ interface IFM_EXT_TokenVault_v1 { // Public Mutating Functions /// @notice Allows for withdrawal of reserve tokens. - /// @dev This function is only callable by the orchestrator admin. + /// @dev Function access controlled by authorizer. /// @param token_ The token to withdraw. /// @param amount_ The amount of tokens to withdraw. /// @param recipient_ The address to send the tokens to. diff --git a/src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol b/src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol similarity index 76% rename from src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol rename to src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol index 1b87bb30f..499b2a994 100644 --- a/src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol +++ b/src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol @@ -2,28 +2,28 @@ pragma solidity 0.8.23; // Internal -import {IFM_PC_Oracle_Redeeming_v1} from - "@fm/oracle/interfaces/IFM_PC_Oracle_Redeeming_v1.sol"; +import {IFM_PC_Oracle_Redeeming_v2} from + "@fm/oracle/interfaces/IFM_PC_Oracle_Redeeming_v2.sol"; import {IERC20Issuance_Blacklist_v1} from "@ex/token/interfaces/IERC20Issuance_Blacklist_v1.sol"; import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {BondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; -import {RedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; +import {BondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; +import {RedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; import {FM_BC_Tools} from "@fm/bondingCurve/FM_BC_Tools.sol"; import { - ERC20PaymentClientBase_v2, - IERC20PaymentClientBase_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + IERC20PaymentClientBase_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; import {IERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; import {IFeeManager_v1} from "@ex/fees/interfaces/IFeeManager_v1.sol"; @@ -44,9 +44,9 @@ import {ERC165Upgradeable} from * client system. * * @dev Inherits functionality from: - * - IFM_PC_Oracle_Redeeming_v1: Implementation interface. - * - ERC20PaymentClientBase_v2: Payment processing capabilities. - * - RedeemingBondingCurveBase_v1: Token issuance and redemption logic. + * - IFM_PC_Oracle_Redeeming_v2: Implementation interface. + * - ERC20PaymentClientBase_v3: Payment processing capabilities. + * - RedeemingBondingCurveBase_v2: Token issuance and redemption logic. * * Key features: * - Oracle-driven token pricing. @@ -57,9 +57,6 @@ import {ERC165Upgradeable} from * Mints new tokens during purchases and burns tokens during * sell operations at oracle-determined prices. * - * - Whitelisting system for controlled token distribution. - * Restricts token purchases and sales to approved addresses. - * * - Queue-based redemption and payment processing. * Creates payment orders in a queue and sends them to the payment * processor for executing token redemptions. @@ -89,100 +86,64 @@ import {ERC165Upgradeable} from * setter function. * - Example: module.setOracleAddress(oracleAddress); * - * 3. Setup Whitelist: + * 3. Enable Trading: + * - Purpose: Makes the the buy/sell functionality of the + * contract public. Trading must be explicitly + * enabled. + * - How: The OrchestratorAdmin must enable both buying + * and selling operations separately. + * - Example: authorizer.addAccessPermission(buy.selector); + * authorizer.addAccessPermission(sell.selector); + * module.openBuy(); + * module.openSell(); + * + * OPTIONAL setup steps for enhanced administration: + * + * 1. Setup Whitelist: * - Purpose: Implements access control for buy/sell * functions. Only whitelisted addresses can * participate in token buy & sell operations to * provide a security layer for controlled token * distribution and compliance. - * - How: The OrchestratorAdmin (or WHITELIST_ROLE_ADMIN - * if configured) must: - * 1. Retrieve the whitelist role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getWhitelistRole(), - * userAddress - * ); + * - How: The OrchestratorAdmin must: + * 1. Create a whitelist role + * 2. Add access permission for the buy() and + * sell() functions to the whitelist role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); + * - Notice: This assumes that the function access + * permissions currently don't contain the + * public role. * - * 4. Setup Queue Executors: + * 2. Setup Queue Executors: * - Purpose: Implements access control for authorized * addresses that can process the redemption * queue. - * - How: The OrchestratorAdmin (or - * QUEUE_EXECUTOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the executor role identifier. - * 2. Grant the role to designated executors. - * - Example: module.grantModuleRole( - * module.getQueueExecutorRole(), - * executorAddress - * ); - * - * 5. Enable Trading: - * - Purpose: Activates the buy/sell functionality of the - * contract. Trading must be explicitly enabled. - * - How: The OrchestratorAdmin must enable both buying - * and selling operations separately. - * - Example: module.openBuy(); - * module.openSell(); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Whitelist Admin: - * - Purpose: Enables delegation of whitelist management to - * a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. - * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getWhitelistRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getWhitelistRoleAdmin() - * ) - * ); - * - * 2. Custom Queue Executor Admin: - * - Purpose: Allows delegation of queue executor - * management to a dedicated admin role instead - * of the OrchestratorAdmin. This allows for - * more granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the - * Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueExecutorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueExecutorRoleAdmin() - * ) - * ); + * 1. Create a queue executor role + * 2. Add access permission for the executeRedemptionQueue() function. + * 3. Grant the role to designated executors. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -contract FM_PC_Oracle_Redeeming_v1 is - IFM_PC_Oracle_Redeeming_v1, - ERC20PaymentClientBase_v2, - RedeemingBondingCurveBase_v1 +contract FM_PC_Oracle_Redeeming_v2 is + IFM_PC_Oracle_Redeeming_v2, + ERC20PaymentClientBase_v3, + RedeemingBondingCurveBase_v2 { // ------------------------------------------------------------------------- // Libraries @@ -196,10 +157,10 @@ contract FM_PC_Oracle_Redeeming_v1 is function supportsInterface(bytes4 interfaceId_) public view - override(ERC20PaymentClientBase_v2, RedeemingBondingCurveBase_v1) + override(ERC20PaymentClientBase_v3, RedeemingBondingCurveBase_v2) returns (bool isSupported_) { - return interfaceId_ == type(IFM_PC_Oracle_Redeeming_v1).interfaceId + return interfaceId_ == type(IFM_PC_Oracle_Redeeming_v2).interfaceId || interfaceId_ == type(IFundingManager_v1).interfaceId || super.supportsInterface(interfaceId_); } @@ -207,27 +168,6 @@ contract FM_PC_Oracle_Redeeming_v1 is // ------------------------------------------------------------------------- // Constants - /// @notice Role identifier for accounts who are whitelisted to buy and sell. - bytes32 internal constant WHITELIST_ROLE = "WHITELIST_ROLE"; - - /// @notice Role identifier for the admin authorized to assign the whitelist - /// role. - /// @dev This role should be set as the role admin for the WHITELIST_ROLE - /// within the Authorizer module. - bytes32 internal constant WHITELIST_ROLE_ADMIN = "WHITELIST_ROLE_ADMIN"; - - /// @notice Role identifier for accounts who are allowed to manually execute - /// the redemption queue. - bytes32 internal constant QUEUE_EXECUTOR_ROLE = "QUEUE_EXECUTOR_ROLE"; - - /// @notice Role identifier for the admin authorized to assign the queue - /// execution role. - /// role. - /// @dev This role should be set as the role admin for the - /// QUEUE_EXECUTOR_ROLE within the Authorizer module. - bytes32 internal constant QUEUE_EXECUTOR_ROLE_ADMIN = - "QUEUE_EXECUTOR_ROLE_ADMIN"; - /// @notice Flag used for the payment order. uint internal constant FLAG_ORDER_ID = 0; @@ -318,10 +258,10 @@ contract FM_PC_Oracle_Redeeming_v1 is /// - bool: isDirectOperationsOnly: Whether only direct operations /// are allowed. function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { // Initialize base module. __Module_init(orchestrator_, metadata_); @@ -367,75 +307,40 @@ contract FM_PC_Oracle_Redeeming_v1 is flags |= bytes32(1 << FLAG_ORDER_ID); flags |= bytes32(1 << FLAG_PROJECT_FEE); - __ERC20PaymentClientBase_v2_init(flags); + __ERC20PaymentClientBase_v3_init(flags); } // ------------------------------------------------------------------------- // Public View Functions - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function getWhitelistRole() public pure virtual returns (bytes32 role_) { - return WHITELIST_ROLE; - } - - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function getWhitelistRoleAdmin() - public - pure - virtual - returns (bytes32 role_) - { - return WHITELIST_ROLE_ADMIN; - } - - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function getQueueExecutorRole() - public - pure - virtual - returns (bytes32 role_) - { - return QUEUE_EXECUTOR_ROLE; - } - - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function getQueueExecutorRoleAdmin() - public - pure - virtual - returns (bytes32 role_) - { - return QUEUE_EXECUTOR_ROLE_ADMIN; - } - /// @inheritdoc IFundingManager_v1 function token() public view virtual override returns (IERC20 token_) { return _token; } - /// @inheritdoc IBondingCurveBase_v1 + /// @inheritdoc IBondingCurveBase_v2 function getStaticPriceForBuying() public view virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) + override(BondingCurveBase_v2, IBondingCurveBase_v2) returns (uint buyPrice_) { return _oracle.getPriceForIssuance(); } - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 function getStaticPriceForSelling() public view virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2, IRedeemingBondingCurveBase_v2) returns (uint sellPrice_) { return _oracle.getPriceForRedemption(); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getOpenRedemptionAmount() external view @@ -445,12 +350,12 @@ contract FM_PC_Oracle_Redeeming_v1 is return _openRedemptionAmount; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getOrderId() external view virtual returns (uint orderId_) { return _orderId; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getProjectTreasury() external view @@ -460,7 +365,7 @@ contract FM_PC_Oracle_Redeeming_v1 is return _projectTreasury; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getIsDirectOperationsOnly() public view @@ -470,17 +375,17 @@ contract FM_PC_Oracle_Redeeming_v1 is return _isDirectOperationsOnly; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getOracle() external view virtual returns (address oracle_) { return address(_oracle); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getBuyFee() public view virtual returns (uint buyFee_) { return buyFee; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getMaxProjectBuyFee() public view @@ -490,7 +395,7 @@ contract FM_PC_Oracle_Redeeming_v1 is return _maxProjectBuyFee; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getMaxProjectSellFee() public view @@ -500,12 +405,12 @@ contract FM_PC_Oracle_Redeeming_v1 is return _maxProjectSellFee; } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function getSellFee() public view virtual returns (uint fee_) { return sellFee; } - /// @inheritdoc IRedeemingBondingCurveBase_v1 + /// @inheritdoc IRedeemingBondingCurveBase_v2 /// @dev Function uses the collateral sell fee from the payment processor /// function processPayments() to calculate the sale return. /// This is done because the collateral fee will be collected when the @@ -514,7 +419,7 @@ contract FM_PC_Oracle_Redeeming_v1 is public view virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2, IRedeemingBondingCurveBase_v2) returns (uint redeemAmount_) { // Set min amount out to 1 for price calculation @@ -551,49 +456,27 @@ contract FM_PC_Oracle_Redeeming_v1 is // ------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc BondingCurveBase_v1 - function buy(uint collateralAmount_, uint minAmountOut_) - public - virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) - onlyModuleRole(WHITELIST_ROLE) - { - super.buyFor(_msgSender(), collateralAmount_, minAmountOut_); - } - - /// @inheritdoc BondingCurveBase_v1 + /// @inheritdoc BondingCurveBase_v2 function buyFor(address receiver_, uint depositAmount_, uint minAmountOut_) public virtual - override(BondingCurveBase_v1, IBondingCurveBase_v1) - onlyModuleRole(WHITELIST_ROLE) + override(BondingCurveBase_v2, IBondingCurveBase_v2) thirdPartyOperationsEnabled { super.buyFor(receiver_, depositAmount_, minAmountOut_); } - /// @inheritdoc RedeemingBondingCurveBase_v1 - function sell(uint depositAmount_, uint minAmountOut_) - public - virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) - onlyModuleRole(WHITELIST_ROLE) - { - super.sellTo(_msgSender(), depositAmount_, minAmountOut_); - } - - /// @inheritdoc RedeemingBondingCurveBase_v1 + /// @inheritdoc RedeemingBondingCurveBase_v2 function sellTo(address receiver_, uint depositAmount_, uint minAmountOut_) public virtual - override(RedeemingBondingCurveBase_v1, IRedeemingBondingCurveBase_v1) - onlyModuleRole(WHITELIST_ROLE) + override(RedeemingBondingCurveBase_v2, IRedeemingBondingCurveBase_v2) thirdPartyOperationsEnabled { super.sellTo(receiver_, depositAmount_, minAmountOut_); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function depositReserve(uint amount_) external virtual { if (amount_ == 0) { revert Module__FM_PC_ExternalPrice_Redeeming_InvalidAmount(); @@ -605,11 +488,11 @@ contract FM_PC_Oracle_Redeeming_v1 is emit ReserveDeposited(_msgSender(), amount_); } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function amountPaid(address token_, uint amount_) public virtual - override(ERC20PaymentClientBase_v2, IERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3, IERC20PaymentClientBase_v3) { _deductFromOpenRedemptionAmount(amount_); super.amountPaid(token_, amount_); @@ -626,39 +509,31 @@ contract FM_PC_Oracle_Redeeming_v1 is emit TransferOrchestratorToken(to_, amount_); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function setProjectTreasury(address projectTreasury_) external virtual - onlyOrchestratorAdmin + permissioned { _setProjectTreasury(projectTreasury_); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function setOracleAddress(address oracle_) - external - virtual - onlyOrchestratorAdmin - { + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 + function setOracleAddress(address oracle_) external virtual permissioned { _setOracleAddress(oracle_); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 function setIsDirectOperationsOnly(bool isDirectOperationsOnly_) public virtual - onlyOrchestratorAdmin + permissioned { _setIsDirectOperationsOnly(isDirectOperationsOnly_); } - /// @inheritdoc IFM_PC_Oracle_Redeeming_v1 - function executeRedemptionQueue() - external - virtual - onlyModuleRole(QUEUE_EXECUTOR_ROLE) - { + /// @inheritdoc IFM_PC_Oracle_Redeeming_v2 + function executeRedemptionQueue() external virtual permissioned { (bool success, bytes memory data) = address( __Module_orchestrator.paymentProcessor() ).call( @@ -755,7 +630,7 @@ contract FM_PC_Oracle_Redeeming_v1 is // Process payments through the payment processor. __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } @@ -775,7 +650,7 @@ contract FM_PC_Oracle_Redeeming_v1 is ) internal virtual - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns ( uint totalCollateralTokenMovedOut_, uint projectCollateralFeeAmount_ @@ -865,7 +740,7 @@ contract FM_PC_Oracle_Redeeming_v1 is function _projectFeeCollected(uint _projectFeeAmount) internal virtual - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) { emit ProjectCollateralFeeAdded(_projectFeeAmount); } @@ -895,13 +770,13 @@ contract FM_PC_Oracle_Redeeming_v1 is } /// @notice Sets the sell fee. - /// @dev Overrides the internal function from RedeemingBondingCurveBase_v1. + /// @dev Overrides the internal function from RedeemingBondingCurveBase_v2. /// Revert if sell fee exceeds max project sell fee. /// @param fee_ The fee percentage to set. function _setSellFee(uint fee_) internal virtual - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) { // Check that fee doesn't exceed maximum allowed if (fee_ > _maxProjectSellFee) { @@ -914,13 +789,13 @@ contract FM_PC_Oracle_Redeeming_v1 is } /// @notice Sets the buy fee. - /// @dev Overrides the internal function from BondingCurveBase_v1. + /// @dev Overrides the internal function from BondingCurveBase_v2. /// Revert if buy fee exceeds max project buy fee. /// @param fee_ The fee percentage to set. function _setBuyFee(uint fee_) internal virtual - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) { // Check that fee doesn't exceed maximum allowed. if (fee_ > _maxProjectBuyFee) { @@ -937,7 +812,7 @@ contract FM_PC_Oracle_Redeeming_v1 is internal view virtual - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (uint mintAmount_) { // Calculate the mint amount. @@ -956,7 +831,7 @@ contract FM_PC_Oracle_Redeeming_v1 is internal view virtual - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint redeemAmount_) { // Convert issuance token deposit amount to collateral token decimals. @@ -974,13 +849,13 @@ contract FM_PC_Oracle_Redeeming_v1 is /// @dev Sets the issuance token. /// This function overrides the internal function set in - /// {BondingCurveBase_v1}, and it updates the `issuanceToken` state + /// {BondingCurveBase_v2}, and it updates the `issuanceToken` state /// variable and caches the decimals as `_issuanceTokenDecimals`. /// @param issuanceToken_ The token which will be issued by the Bonding Curve. function _setIssuanceToken(address issuanceToken_) internal virtual - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) { uint8 decimals_ = IERC20Metadata(issuanceToken_).decimals(); @@ -1042,7 +917,7 @@ contract FM_PC_Oracle_Redeeming_v1 is emit RedemptionAmountUpdated(_openRedemptionAmount); } - /// @inheritdoc BondingCurveBase_v1 + /// @inheritdoc BondingCurveBase_v2 function _handleIssuanceTokensAfterBuy(address recipient_, uint amount_) internal virtual @@ -1052,7 +927,7 @@ contract FM_PC_Oracle_Redeeming_v1 is IERC20Issuance_v1(issuanceToken).mint(recipient_, amount_); } - /// @inheritdoc BondingCurveBase_v1 + /// @inheritdoc BondingCurveBase_v2 /// @dev Implementation transfer collateral tokens to the project treasury. function _processCollateralTokensForBuyOperation(uint _amount) internal @@ -1062,7 +937,7 @@ contract FM_PC_Oracle_Redeeming_v1 is IERC20(token()).safeTransfer(_projectTreasury, _amount); } - /// @inheritdoc RedeemingBondingCurveBase_v1 + /// @inheritdoc RedeemingBondingCurveBase_v2 /// @dev Implementation does not transfer collateral tokens to recipient /// as the payout is managed through a redemption queue. function _handleCollateralTokensAfterSell(address recipient_, uint amount_) @@ -1073,7 +948,7 @@ contract FM_PC_Oracle_Redeeming_v1 is // This function is not used in this implementation. } - /// @inheritdoc ERC20PaymentClientBase_v2 + /// @inheritdoc ERC20PaymentClientBase_v3 /// @dev We do not need to ensure the token balance because all the /// collateral is taken out. function _ensureTokenBalance(address token_) internal virtual override { @@ -1092,7 +967,7 @@ contract FM_PC_Oracle_Redeeming_v1 is function _getFunctionFeesAndTreasuryAddresses(bytes4 selector_) internal view - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns ( address collateralTreasury_, address issuanceTreasury_, diff --git a/src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v1.sol b/src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v2.sol similarity index 69% rename from src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v1.sol rename to src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v2.sol index 1ecdf63ef..10afd3f31 100644 --- a/src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v1.sol +++ b/src/modules/fundingManager/oracle/interfaces/IFM_PC_Oracle_Redeeming_v2.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.0; // Internal import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v2.sol"; /** * @title External Price Oracle Funding Manager with Payment Client. @@ -18,9 +18,9 @@ import {IRedeemingBondingCurveBase_v1} from * client system. * * @dev Inherits functionality from: - * - IFM_PC_Oracle_Redeeming_v1: Implementation interface. - * - ERC20PaymentClientBase_v2: Payment processing capabilities. - * - RedeemingBondingCurveBase_v1: Token issuance and redemption logic. + * - IFM_PC_Oracle_Redeeming_v2: Implementation interface. + * - ERC20PaymentClientBase_v3: Payment processing capabilities. + * - RedeemingBondingCurveBase_v2: Token issuance and redemption logic. * * Key features: * - Oracle-driven token pricing. @@ -31,9 +31,6 @@ import {IRedeemingBondingCurveBase_v1} from * Mints new tokens during purchases and burns tokens during * sell operations at oracle-determined prices. * - * - Whitelisting system for controlled token distribution. - * Restricts token purchases and sales to approved addresses. - * * - Queue-based redemption and payment processing. * Creates payment orders in a queue and sends them to the payment * processor for executing token redemptions. @@ -63,100 +60,64 @@ import {IRedeemingBondingCurveBase_v1} from * setter function. * - Example: module.setOracleAddress(oracleAddress); * - * 3. Setup Whitelist: + * 3. Enable Trading: + * - Purpose: Makes the the buy/sell functionality of the + * contract public. Trading must be explicitly + * enabled. + * - How: The OrchestratorAdmin must enable both buying + * and selling operations separately. + * - Example: authorizer.addAccessPermission(buy.selector); + * authorizer.addAccessPermission(sell.selector); + * module.openBuy(); + * module.openSell(); + * + * OPTIONAL setup steps for enhanced administration: + * + * 1. Setup Whitelist: * - Purpose: Implements access control for buy/sell * functions. Only whitelisted addresses can * participate in token buy & sell operations to * provide a security layer for controlled token * distribution and compliance. - * - How: The OrchestratorAdmin (or WHITELIST_ROLE_ADMIN - * if configured) must: - * 1. Retrieve the whitelist role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getWhitelistRole(), - * userAddress - * ); + * - How: The OrchestratorAdmin must: + * 1. Create a whitelist role + * 2. Add access permission for the buy() and + * sell() functions to the whitelist role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); + * - Notice: This assumes that the function access + * permissions currently don't contain the + * public role. * - * 4. Setup Queue Executors: + * 2. Setup Queue Executors: * - Purpose: Implements access control for authorized * addresses that can process the redemption * queue. - * - How: The OrchestratorAdmin (or - * QUEUE_EXECUTOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the executor role identifier. - * 2. Grant the role to designated executors. - * - Example: module.grantModuleRole( - * module.getQueueExecutorRole(), - * executorAddress - * ); - * - * 5. Enable Trading: - * - Purpose: Activates the buy/sell functionality of the - * contract. Trading must be explicitly enabled. - * - How: The OrchestratorAdmin must enable both buying - * and selling operations separately. - * - Example: module.openBuy(); - * module.openSell(); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Whitelist Admin: - * - Purpose: Enables delegation of whitelist management to - * a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. - * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getWhitelistRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getWhitelistRoleAdmin() - * ) - * ); - * - * 2. Custom Queue Executor Admin: - * - Purpose: Allows delegation of queue executor - * management to a dedicated admin role instead - * of the OrchestratorAdmin. This allows for - * more granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the - * Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueExecutorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueExecutorRoleAdmin() - * ) - * ); + * 1. Create a queue executor role + * 2. Add access permission for the executeRedemptionQueue() function. + * 3. Grant the role to designated executors. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -interface IFM_PC_Oracle_Redeeming_v1 is +interface IFM_PC_Oracle_Redeeming_v2 is IFundingManager_v1, - IERC20PaymentClientBase_v2, - IRedeemingBondingCurveBase_v1 + IERC20PaymentClientBase_v3, + IRedeemingBondingCurveBase_v2 { // ------------------------------------------------------------------------- // Type Declarations @@ -317,25 +278,6 @@ interface IFM_PC_Oracle_Redeeming_v1 is /// @return fee_ The current sell fee. function getSellFee() external view returns (uint fee_); - /// @notice Gets the whitelist role identifier - /// @return role_ The whitelist role identifier - function getWhitelistRole() external pure returns (bytes32 role_); - - /// @notice Gets the whitelist role admin identifier - /// @return role_ The whitelist role admin identifier - function getWhitelistRoleAdmin() external pure returns (bytes32 role_); - - /// @notice Gets the queue executor role identifier - /// @return role_ The queue executor role identifier - function getQueueExecutorRole() external pure returns (bytes32 role_); - - /// @notice Gets the queue executor role admin identifier - /// @return role_ The queue executor role admin identifier - function getQueueExecutorRoleAdmin() - external - pure - returns (bytes32 role_); - /// @notice Gets the oracle address. /// @return oracle_ The address of the oracle. function getOracle() external view returns (address oracle_); @@ -344,23 +286,28 @@ interface IFM_PC_Oracle_Redeeming_v1 is // External Functions /// @notice Allows depositing collateral to provide reserves for redemptions. + /// @dev This function is always publicly callable. /// @param amount_ The amount of collateral to deposit. function depositReserve(uint amount_) external; /// @notice Sets the project treasury address. - /// @param projectTreasury_ The address of the project treasury. + /// @dev Function access controlled by authorizer. + /// @param projectTreasury_ The address of the project treasury. function setProjectTreasury(address projectTreasury_) external; /// @notice Sets the oracle address. - /// @param oracle_ The address of the oracle. + /// @dev Function access controlled by authorizer. + /// @param oracle_ The address of the oracle. function setOracleAddress(address oracle_) external; /// @notice Toggles whether the contract only allows direct operations or not. + /// @dev Function access controlled by authorizer. /// @param isDirectOperationsOnly_ The new value for the flag. function setIsDirectOperationsOnly(bool isDirectOperationsOnly_) external; /// @notice Manually executes the redemption queue in the workflows Payment /// Processor. + /// @dev Function access controlled by authorizer. /// @dev If this function is called but the Payment Processor does not /// implement the option to manually execute the redemption queue /// then this function will revert. diff --git a/src/modules/lib/LibMetadata.sol b/src/modules/lib/LibMetadata.sol index 157c65f5e..e62970a8e 100644 --- a/src/modules/lib/LibMetadata.sol +++ b/src/modules/lib/LibMetadata.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; /** * @title Inverter Metadata Library * - * @dev Provides common functions for {IModule_v1}'s Metadata type. + * @dev Provides common functions for {IModule_v2}'s Metadata type. * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy @@ -19,7 +19,7 @@ library LibMetadata { /// @dev Returns the identifier for given metadata. /// @param metadata The metadata. /// @return The metadata's identifier. - function identifier(IModule_v1.Metadata memory metadata) + function identifier(IModule_v2.Metadata memory metadata) internal pure returns (bytes32) @@ -32,7 +32,7 @@ library LibMetadata { /// @dev Returns whether the given metadata is valid. /// @param metadata The metadata. /// @return True if metadata valid, false otherwise. - function isValid(IModule_v1.Metadata memory metadata) + function isValid(IModule_v2.Metadata memory metadata) internal pure returns (bool) diff --git a/src/modules/logicModule/LM_Oracle_Permissioned_v1.sol b/src/modules/logicModule/LM_Oracle_Permissioned_v2.sol similarity index 59% rename from src/modules/logicModule/LM_Oracle_Permissioned_v1.sol rename to src/modules/logicModule/LM_Oracle_Permissioned_v2.sol index e3356925c..b772f49cb 100644 --- a/src/modules/logicModule/LM_Oracle_Permissioned_v1.sol +++ b/src/modules/logicModule/LM_Oracle_Permissioned_v2.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.23; // Internal -import {ILM_Oracle_Permissioned_v1} from - "@lm/interfaces/ILM_Oracle_Permissioned_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {ILM_Oracle_Permissioned_v2} from + "@lm/interfaces/ILM_Oracle_Permissioned_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; // External @@ -22,9 +22,9 @@ import {ERC165Upgradeable} from * for both issuance (buying) and redemption (selling) operations. * * @dev This contract inherits from: - * - ILM_Oracle_Permissioned_v1: Implementation interface. + * - ILM_Oracle_Permissioned_v2: Implementation interface. * - IOraclePrice_v1: Oracle price interface. - * - Module_v1: Base module functionality. + * - Module_v2: Base module functionality. * * Key features: * - Two separate price feeds for issuance and redemption. @@ -32,7 +32,7 @@ import {ERC165Upgradeable} from * and redemption operations. * * - Manual price setting. - * Prices are manually set by the price setter role and must be + * Prices are manually set and must be * non-zero values. * * - Price decimal denominations. @@ -44,54 +44,34 @@ import {ERC165Upgradeable} from * - To price redeeming 1 token at 0.5 collateral with 6 decimal * collateral: 500_000 * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Price Setter Role: * - Purpose: The price setter role is authorized to set * prices for issuance and redemption operations. - * - How: The OrchestratorAdmin (or PRICE_SETTER_ROLE_ADMIN - * if configured) must: - * 1. Retrieve the price setter role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getPriceSetterRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Price Setter Role Admin: - * - Purpose: Enables delegation of price setter role - * management to a dedicated admin role instead of - * relying on the OrchestratorAdmin. This allows - * for more granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getPriceSetterRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getPriceSetterRoleAdmin() - * ) - * ); + * 1. Create a price setter role + * 2. Add access permission for the + * setIssuancePrice(), setRedemptionPrice() + * and setIssuanceAndRedemptionPrice() + * functions to the price setter role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -contract LM_Oracle_Permissioned_v1 is ILM_Oracle_Permissioned_v1, Module_v1 { +contract LM_Oracle_Permissioned_v2 is ILM_Oracle_Permissioned_v2, Module_v2 { // ------------------------------------------------------------------------- // ERC165 @@ -102,25 +82,11 @@ contract LM_Oracle_Permissioned_v1 is ILM_Oracle_Permissioned_v1, Module_v1 { override returns (bool) { - return interfaceId == type(ILM_Oracle_Permissioned_v1).interfaceId + return interfaceId == type(ILM_Oracle_Permissioned_v2).interfaceId || interfaceId == type(IOraclePrice_v1).interfaceId || super.supportsInterface(interfaceId); } - // ------------------------------------------------------------------------- - // Constants - - /// @notice Role identifier for accounts authorized to set prices. - /// @dev This role should be granted to trusted price feeders only. - bytes32 internal constant PRICE_SETTER_ROLE = "PRICE_SETTER_ROLE"; - - /// @notice Role identifier for the admin authorized to assign the price - /// setter role. - /// @dev This role should be set as the role admin within the Authorizer - /// module. - bytes32 internal constant PRICE_SETTER_ROLE_ADMIN = - "PRICE_SETTER_ROLE_ADMIN"; - // ------------------------------------------------------------------------- // State Variables @@ -148,10 +114,10 @@ contract LM_Oracle_Permissioned_v1 is ILM_Oracle_Permissioned_v1, Module_v1 { /// @param configData_ The config data of the module, comprised of: /// - address: collateralToken: The collateral token address. function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); // Decode collateral token address from configData_. @@ -164,7 +130,7 @@ contract LM_Oracle_Permissioned_v1 is ILM_Oracle_Permissioned_v1, Module_v1 { //-------------------------------------------------------------------------- // Public View Functions - /// @inheritdoc ILM_Oracle_Permissioned_v1 + /// @inheritdoc ILM_Oracle_Permissioned_v2 function getCollateralTokenDecimals() external view @@ -184,47 +150,24 @@ contract LM_Oracle_Permissioned_v1 is ILM_Oracle_Permissioned_v1, Module_v1 { return _redemptionPrice; } - /// @inheritdoc ILM_Oracle_Permissioned_v1 - function getPriceSetterRole() external pure virtual returns (bytes32) { - return PRICE_SETTER_ROLE; - } - - /// @inheritdoc ILM_Oracle_Permissioned_v1 - function getPriceSetterRoleAdmin() - external - pure - virtual - returns (bytes32) - { - return PRICE_SETTER_ROLE_ADMIN; - } - //-------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc ILM_Oracle_Permissioned_v1 - function setIssuancePrice(uint price_) - external - virtual - onlyModuleRole(PRICE_SETTER_ROLE) - { + /// @inheritdoc ILM_Oracle_Permissioned_v2 + function setIssuancePrice(uint price_) external virtual permissioned { _setIssuancePrice(price_); } - /// @inheritdoc ILM_Oracle_Permissioned_v1 - function setRedemptionPrice(uint price_) - external - virtual - onlyModuleRole(PRICE_SETTER_ROLE) - { + /// @inheritdoc ILM_Oracle_Permissioned_v2 + function setRedemptionPrice(uint price_) external virtual permissioned { _setRedemptionPrice(price_); } - /// @inheritdoc ILM_Oracle_Permissioned_v1 + /// @inheritdoc ILM_Oracle_Permissioned_v2 function setIssuanceAndRedemptionPrice( uint issuancePrice_, uint redemptionPrice_ - ) external virtual onlyModuleRole(PRICE_SETTER_ROLE) { + ) external virtual permissioned { _setIssuancePrice(issuancePrice_); _setRedemptionPrice(redemptionPrice_); } diff --git a/src/modules/logicModule/LM_PC_Bounties_v2.sol b/src/modules/logicModule/LM_PC_Bounties_v3.sol similarity index 89% rename from src/modules/logicModule/LM_PC_Bounties_v2.sol rename to src/modules/logicModule/LM_PC_Bounties_v3.sol index 51b314424..2220f4214 100644 --- a/src/modules/logicModule/LM_PC_Bounties_v2.sol +++ b/src/modules/logicModule/LM_PC_Bounties_v3.sol @@ -2,20 +2,20 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -import {ILM_PC_Bounties_v2} from "@lm/interfaces/ILM_PC_Bounties_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {ILM_PC_Bounties_v3} from "@lm/interfaces/ILM_PC_Bounties_v3.sol"; import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + IERC20PaymentClientBase_v3, + IPaymentProcessor_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // Internal Dependencies import { - ERC20PaymentClientBase_v2, - Module_v1 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + Module_v2 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // External Dependencies import {ERC165Upgradeable} from @@ -34,7 +34,7 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol"; * allowing participants to propose, update, and claim bounties securely * and transparently. * - * @dev Extends {ERC20PaymentClientBase_v2} to integrate payment processing with + * @dev Extends {ERC20PaymentClientBase_v3} to integrate payment processing with * bounty management, supporting dynamic additions, updates, and the locking * of bounties. Utilizes roles for managing permissions and maintaining robust * control over bounty operations. @@ -43,18 +43,20 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol"; * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { +contract LM_PC_Bounties_v3 is ILM_PC_Bounties_v3, ERC20PaymentClientBase_v3 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { - return interfaceId == type(ILM_PC_Bounties_v2).interfaceId + return interfaceId == type(ILM_PC_Bounties_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -234,13 +236,6 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { /// @dev Marks the beginning of the list. uint internal constant _SENTINEL = type(uint).max; - /// @dev Role for the bounty issuer. - bytes32 public constant BOUNTY_ISSUER_ROLE = "BOUNTY_ISSUER"; - /// @dev Role for the claimant. - bytes32 public constant CLAIMANT_ROLE = "CLAIMANT"; - /// @dev Role for the verifier. - bytes32 public constant VERIFIER_ROLE = "VERIFIER"; - //-------------------------------------------------------------------------- // Storage @@ -268,15 +263,15 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { //-------------------------------------------------------------------------- // Initialization - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); // This module does not use any PaymentOrder flags. - __ERC20PaymentClientBase_v2_init(bytes32(0)); + __ERC20PaymentClientBase_v3_init(bytes32(0)); // init empty list of bounties and claims _bountyList.init(); _claimList.init(); @@ -285,7 +280,7 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { //-------------------------------------------------------------------------- // Getter Functions - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function getBountyInformation(uint bountyId) external view @@ -295,17 +290,17 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { return _bountyRegistry[bountyId]; } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function listBountyIds() external view returns (uint[] memory) { return _bountyList.listIds(); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function isExistingBountyId(uint bountyId) public view returns (bool) { return _bountyList.isExistingId(bountyId); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function getClaimInformation(uint claimId) external view @@ -315,17 +310,17 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { return _claimRegistry[claimId]; } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function listClaimIds() external view returns (uint[] memory) { return _claimList.listIds(); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function isExistingClaimId(uint claimId) public view returns (bool) { return _claimList.isExistingId(claimId); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function listClaimIdsForContributorAddress(address contributorAddrs) external view @@ -337,28 +332,28 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { //-------------------------------------------------------------------------- // Mutating Functions - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function addBounty( uint minimumPayoutAmount, uint maximumPayoutAmount, bytes calldata details ) external - onlyModuleRole(BOUNTY_ISSUER_ROLE) + permissioned validPayoutAmounts(minimumPayoutAmount, maximumPayoutAmount) returns (uint id) { return _addBounty(minimumPayoutAmount, maximumPayoutAmount, details); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function addBountyBatch( uint[] calldata minimumPayoutAmounts, uint[] calldata maximumPayoutAmounts, bytes[] calldata detailArray ) external - onlyModuleRole(BOUNTY_ISSUER_ROLE) + permissioned validArrayLengths( minimumPayoutAmounts.length, maximumPayoutAmounts.length, @@ -381,10 +376,10 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { } } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function updateBounty(uint bountyId, bytes calldata details) external - onlyModuleRole(BOUNTY_ISSUER_ROLE) + permissioned validBountyId(bountyId) notLocked(bountyId) { @@ -393,10 +388,10 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { emit BountyUpdated(bountyId, details); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function lockBounty(uint bountyId) external - onlyModuleRole(BOUNTY_ISSUER_ROLE) + permissioned validBountyId(bountyId) notLocked(bountyId) { @@ -405,14 +400,14 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { emit BountyLocked(bountyId); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function addClaim( uint bountyId, Contributor[] calldata contributors, bytes calldata details ) external - onlyModuleRole(CLAIMANT_ROLE) + permissioned validBountyId(bountyId) notLocked(bountyId) returns (uint id) @@ -446,16 +441,16 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { return claimId; } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function updateClaimContributors( uint claimId, Contributor[] calldata contributors ) external + permissioned validClaimId(claimId) notClaimed(claimId) notLocked(_claimRegistry[claimId].bountyId) - onlyModuleRole(CLAIMANT_ROLE) { _validContributorsForBounty( contributors, _bountyRegistry[_claimRegistry[claimId].bountyId] @@ -487,7 +482,7 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { emit ClaimContributorsUpdated(claimId, contributors); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function updateClaimDetails(uint claimId, bytes calldata details) external validClaimId(claimId) @@ -500,10 +495,10 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { emit ClaimDetailsUpdated(claimId, details); } - /// @inheritdoc ILM_PC_Bounties_v2 + /// @inheritdoc ILM_PC_Bounties_v3 function verifyClaim(uint claimId, Contributor[] calldata contributors) external - onlyModuleRole(VERIFIER_ROLE) + permissioned validClaimId(claimId) notClaimed(claimId) notLocked(_claimRegistry[claimId].bountyId) @@ -542,7 +537,7 @@ contract LM_PC_Bounties_v2 is ILM_PC_Bounties_v2, ERC20PaymentClientBase_v2 { // when done process the Payments correctly __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); // Set completed to true diff --git a/src/modules/logicModule/LM_PC_KPIRewarder_v2.sol b/src/modules/logicModule/LM_PC_KPIRewarder_v3.sol similarity index 83% rename from src/modules/logicModule/LM_PC_KPIRewarder_v2.sol rename to src/modules/logicModule/LM_PC_KPIRewarder_v3.sol index 40dfbbea3..0b32f06f8 100644 --- a/src/modules/logicModule/LM_PC_KPIRewarder_v2.sol +++ b/src/modules/logicModule/LM_PC_KPIRewarder_v3.sol @@ -2,25 +2,25 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ILM_PC_KPIRewarder_v2} from "@lm/interfaces/ILM_PC_KPIRewarder_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {ILM_PC_KPIRewarder_v3} from "@lm/interfaces/ILM_PC_KPIRewarder_v3.sol"; import { - ILM_PC_Staking_v2, - LM_PC_Staking_v2, + ILM_PC_Staking_v3, + LM_PC_Staking_v3, SafeERC20, IERC20, - ERC20PaymentClientBase_v2 -} from "./LM_PC_Staking_v2.sol"; + ERC20PaymentClientBase_v3 +} from "./LM_PC_Staking_v3.sol"; import { - IOptimisticOracleIntegrator, - OptimisticOracleIntegrator, + IOptimisticOracleIntegrator_v3, + OptimisticOracleIntegrator_v3, OptimisticOracleV3CallbackRecipientInterface } from - "src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol"; + "src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol"; // Internal Dependencies -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; // External Dependencies import {ERC165Upgradeable} from @@ -32,19 +32,21 @@ import {ERC165Upgradeable} from * @notice Provides a mechanism for distributing rewards to stakers based * on Key Performance Indicators (KPIs). * - * @dev Extends {LM_PC_Staking_v2} and integrates with {OptimisticOracleIntegrator} + * @dev Extends {LM_PC_Staking_v3} and integrates with {OptimisticOracleIntegrator_v3} * to enable KPI-based reward distribution within the staking manager. * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract LM_PC_KPIRewarder_v2 is - ILM_PC_KPIRewarder_v2, - LM_PC_Staking_v2, - OptimisticOracleIntegrator +contract LM_PC_KPIRewarder_v3 is + ILM_PC_KPIRewarder_v3, + LM_PC_Staking_v3, + OptimisticOracleIntegrator_v3 { using SafeERC20 for IERC20; @@ -53,11 +55,11 @@ contract LM_PC_KPIRewarder_v2 is public view virtual - override(OptimisticOracleIntegrator, LM_PC_Staking_v2) + override(OptimisticOracleIntegrator_v3, LM_PC_Staking_v3) returns (bool) { - return interfaceId == type(ILM_PC_KPIRewarder_v2).interfaceId - || interfaceId == type(ILM_PC_Staking_v2).interfaceId + return interfaceId == type(ILM_PC_KPIRewarder_v3).interfaceId + || interfaceId == type(ILM_PC_Staking_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -104,15 +106,15 @@ contract LM_PC_KPIRewarder_v2 is */ - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData ) external virtual - override(LM_PC_Staking_v2, OptimisticOracleIntegrator) + override(LM_PC_Staking_v3, OptimisticOracleIntegrator_v3) initializer { __Module_init(orchestrator_, metadata); @@ -125,8 +127,8 @@ contract LM_PC_KPIRewarder_v2 is uint64 liveness ) = abi.decode(configData, (address, address, uint, address, uint64)); - __LM_PC_Staking_v2_init(stakingTokenAddr); - __OptimisticOracleIntegrator_init( + __LM_PC_Staking_v3_init(stakingTokenAddr); + __OptimisticOracleIntegrator_v3_init( currencyAddr, defaultBond, ooAddr, liveness ); } @@ -134,12 +136,12 @@ contract LM_PC_KPIRewarder_v2 is //-------------------------------------------------------------------------- // View functions - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 function getKPI(uint KPInum) external view returns (KPI memory) { return registryOfKPIs[KPInum]; } - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 function getAssertionConfig(bytes32 assertionId) external view @@ -148,12 +150,12 @@ contract LM_PC_KPIRewarder_v2 is return assertionConfig[assertionId]; } - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 function getKPICounter() external view returns (uint) { return KPICounter; } - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 function getAssertionPending() external view returns (bool) { return assertionPending; } @@ -161,7 +163,7 @@ contract LM_PC_KPIRewarder_v2 is // ======================================================================== // Assertion Manager functions: - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 /// @dev about the asserter address: any address can be set as asserter, it will be expected to pay for the /// bond on posting. /// The bond tokens can also be deposited in the Module and used to pay for itself, @@ -172,12 +174,12 @@ contract LM_PC_KPIRewarder_v2 is uint assertedValue, address asserter, uint targetKPI - ) public onlyModuleRole(ASSERTER_ROLE) returns (bytes32 assertionId) { + ) public permissioned returns (bytes32 assertionId) { // ================================================================== // Pre-check if (assertionPending) { - revert Module__LM_PC_KPIRewarder_v2__UnresolvedAssertionExists(); + revert Module__LM_PC_KPIRewarder_v3__UnresolvedAssertionExists(); } //-------------------------------------------------------------------------- @@ -190,12 +192,12 @@ contract LM_PC_KPIRewarder_v2 is && address(defaultCurrency) == stakingToken ) { revert - Module__LM_PC_KPIRewarder_v2__ModuleCannotUseStakingTokenAsBond(); + Module__LM_PC_KPIRewarder_v3__ModuleCannotUseStakingTokenAsBond(); } // Make sure that we are targeting an existing KPI if (KPICounter == 0 || targetKPI >= KPICounter) { - revert Module__LM_PC_KPIRewarder_v2__InvalidKPINumber(); + revert Module__LM_PC_KPIRewarder_v3__InvalidKPINumber(); } // ===================================================================== @@ -216,11 +218,11 @@ contract LM_PC_KPIRewarder_v2 is //-------------------------------------------------------------------------- // Admin Configuration Functions: - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 /// @dev Top up funds to pay the optimistic oracle fee + function depositFeeFunds(uint amount) external - onlyOrchestratorAdmin nonReentrant validAmount(amount) { @@ -229,27 +231,27 @@ contract LM_PC_KPIRewarder_v2 is emit FeeFundsDeposited(address(defaultCurrency), amount); } - /// @inheritdoc ILM_PC_KPIRewarder_v2 + /// @inheritdoc ILM_PC_KPIRewarder_v3 function createKPI( bool _continuous, uint[] calldata _trancheValues, uint[] calldata _trancheRewards - ) external onlyOrchestratorAdmin returns (uint) { + ) external permissioned returns (uint) { uint _numOfTranches = _trancheValues.length; if (_numOfTranches < 1 || _numOfTranches > 20) { - revert Module__LM_PC_KPIRewarder_v2__InvalidTrancheNumber(); + revert Module__LM_PC_KPIRewarder_v3__InvalidTrancheNumber(); } if (_numOfTranches != _trancheRewards.length) { - revert Module__LM_PC_KPIRewarder_v2__InvalidKPIValueLengths(); + revert Module__LM_PC_KPIRewarder_v3__InvalidKPIValueLengths(); } uint _totalKPIRewards = _trancheRewards[0]; if (_numOfTranches > 1) { for (uint i = 1; i < _numOfTranches; i++) { if (_trancheValues[i - 1] >= _trancheValues[i]) { - revert Module__LM_PC_KPIRewarder_v2__InvalidKPITrancheValues( + revert Module__LM_PC_KPIRewarder_v3__InvalidKPITrancheValues( ); } @@ -281,20 +283,21 @@ contract LM_PC_KPIRewarder_v2 is } //-------------------------------------------------------------------------- - // New user facing functions (stake() is a LM_PC_Staking_v2 override) : + // New user facing functions (stake() is a LM_PC_Staking_v3 override) : - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function stake(uint amount) external override nonReentrant + permissioned validAmount(amount) { // ================================================================== // Pre-check if (assertionPending) { - revert Module__LM_PC_KPIRewarder_v2__CannotStakeWhenAssertionPending( + revert Module__LM_PC_KPIRewarder_v3__CannotStakeWhenAssertionPending( ); } @@ -302,18 +305,15 @@ contract LM_PC_KPIRewarder_v2 is _stake(sender, amount); - // transfer funds to LM_PC_Staking_v2 + // transfer funds to LM_PC_Staking_v3 IERC20(stakingToken).safeTransferFrom(sender, address(this), amount); } - /// @inheritdoc ILM_PC_KPIRewarder_v2 - function deleteStuckAssertion(bytes32 assertionId) - public - onlyOrchestratorAdmin - { + /// @inheritdoc ILM_PC_KPIRewarder_v3 + function deleteStuckAssertion(bytes32 assertionId) public permissioned { // Ensure the assertionId exists in this contract (since malicious assertions could callback this contract) if (assertionData[assertionId].dataId == bytes32(0x0)) { - revert Module__LM_PC_KPIRewarder_v2__NonExistentAssertionId( + revert Module__LM_PC_KPIRewarder_v3__NonExistentAssertionId( assertionId ); } @@ -322,12 +322,12 @@ contract LM_PC_KPIRewarder_v2 is oo.getAssertion(assertionId).expirationTime; if (block.timestamp <= assertionExpirationTime) { - revert Module__LM_PC_KPIRewarder_v2__AssertionNotStuck(assertionId); + revert Module__LM_PC_KPIRewarder_v3__AssertionNotStuck(assertionId); } try oo.settleAssertion(assertionId) { // If the assertion can be settled, it doesn't qualify as stuck and we revert - revert Module__LM_PC_KPIRewarder_v2__AssertionNotStuck(assertionId); + revert Module__LM_PC_KPIRewarder_v3__AssertionNotStuck(assertionId); } catch { delete assertionConfig[assertionId]; delete assertionData[assertionId]; @@ -346,7 +346,7 @@ contract LM_PC_KPIRewarder_v2 is ) public override { // Ensure the assertionId exists in this contract (since malicious assertions could callback this contract) if (assertionData[assertionId].dataId == bytes32(0x0)) { - revert Module__LM_PC_KPIRewarder_v2__NonExistentAssertionId( + revert Module__LM_PC_KPIRewarder_v3__NonExistentAssertionId( assertionId ); } diff --git a/src/modules/logicModule/LM_PC_PaymentRouter_v2.sol b/src/modules/logicModule/LM_PC_PaymentRouter_v3.sol similarity index 76% rename from src/modules/logicModule/LM_PC_PaymentRouter_v2.sol rename to src/modules/logicModule/LM_PC_PaymentRouter_v3.sol index f858a6305..b6a955cb9 100644 --- a/src/modules/logicModule/LM_PC_PaymentRouter_v2.sol +++ b/src/modules/logicModule/LM_PC_PaymentRouter_v3.sol @@ -2,21 +2,21 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -import {ILM_PC_PaymentRouter_v2} from - "@lm/interfaces/ILM_PC_PaymentRouter_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {ILM_PC_PaymentRouter_v3} from + "@lm/interfaces/ILM_PC_PaymentRouter_v3.sol"; import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + IERC20PaymentClientBase_v3, + IPaymentProcessor_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // Internal Dependencies import { - ERC20PaymentClientBase_v2, - Module_v1 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + Module_v2 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // External Dependencies import {ERC165Upgradeable} from @@ -27,7 +27,7 @@ import {ERC165Upgradeable} from * * @notice This module enables pushing payments directly to the Payment Processor. * - * @dev Extends {ERC20PaymentClientBase_v2} to integrate payment processing with + * @dev Extends {ERC20PaymentClientBase_v3} to integrate payment processing with * bounty management, supporting dynamic additions, updates, and the locking * of bounties. Utilizes roles for managing permissions and maintaining robust * control over bounty operations. @@ -36,30 +36,29 @@ import {ERC165Upgradeable} from * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract LM_PC_PaymentRouter_v2 is - ILM_PC_PaymentRouter_v2, - ERC20PaymentClientBase_v2 +contract LM_PC_PaymentRouter_v3 is + ILM_PC_PaymentRouter_v3, + ERC20PaymentClientBase_v3 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { - return interfaceId == type(ILM_PC_PaymentRouter_v2).interfaceId + return interfaceId == type(ILM_PC_PaymentRouter_v3).interfaceId || super.supportsInterface(interfaceId); } //-------------------------------------------------------------------------- // Storage - /// @dev The role that allows the pushing of payments. - bytes32 public constant PAYMENT_PUSHER_ROLE = "PAYMENT_PUSHER"; - uint8 public constant FLAG_START = 1; uint8 public constant FLAG_CLIFF = 2; uint8 public constant FLAG_END = 3; @@ -67,10 +66,10 @@ contract LM_PC_PaymentRouter_v2 is //-------------------------------------------------------------------------- // Initializer function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory /* configData */ - ) external virtual override(Module_v1) initializer { + ) external virtual override(Module_v2) initializer { __Module_init(orchestrator_, metadata); // Set the flags for the PaymentOrders (this module uses 3 flags). @@ -79,13 +78,13 @@ contract LM_PC_PaymentRouter_v2 is flags |= bytes32(1 << FLAG_CLIFF); flags |= bytes32(1 << FLAG_END); - __ERC20PaymentClientBase_v2_init(flags); + __ERC20PaymentClientBase_v3_init(flags); } //-------------------------------------------------------------------------- // Mutating Functions - /// @inheritdoc ILM_PC_PaymentRouter_v2 + /// @inheritdoc ILM_PC_PaymentRouter_v3 function pushPayment( address recipient, address paymentToken, @@ -93,7 +92,7 @@ contract LM_PC_PaymentRouter_v2 is uint start, uint cliff, uint end - ) public onlyModuleRole(PAYMENT_PUSHER_ROLE) { + ) public permissioned { bytes32 flags; bytes32[] memory data; @@ -120,11 +119,11 @@ contract LM_PC_PaymentRouter_v2 is // call PaymentProcessor __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } - /// @inheritdoc ILM_PC_PaymentRouter_v2 + /// @inheritdoc ILM_PC_PaymentRouter_v3 function pushPaymentBatched( uint8 numOfOrders, address[] calldata recipients, @@ -133,14 +132,14 @@ contract LM_PC_PaymentRouter_v2 is uint start, uint cliff, uint end - ) public onlyModuleRole(PAYMENT_PUSHER_ROLE) { + ) public permissioned { // Validate all arrays have the same length if ( recipients.length != numOfOrders || paymentTokens.length != numOfOrders || amounts.length != numOfOrders ) { - revert Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch(); + revert Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch(); } bytes32 flags; @@ -172,7 +171,7 @@ contract LM_PC_PaymentRouter_v2 is // call PaymentProcessor __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } } diff --git a/src/modules/logicModule/LM_PC_RecurringPayments_v2.sol b/src/modules/logicModule/LM_PC_RecurringPayments_v3.sol similarity index 86% rename from src/modules/logicModule/LM_PC_RecurringPayments_v2.sol rename to src/modules/logicModule/LM_PC_RecurringPayments_v3.sol index e9279a506..16a0a4e8b 100644 --- a/src/modules/logicModule/LM_PC_RecurringPayments_v2.sol +++ b/src/modules/logicModule/LM_PC_RecurringPayments_v3.sol @@ -2,20 +2,20 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ILM_PC_RecurringPayments_v2} from - "@lm/interfaces/ILM_PC_RecurringPayments_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {ILM_PC_RecurringPayments_v3} from + "@lm/interfaces/ILM_PC_RecurringPayments_v3.sol"; import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + IERC20PaymentClientBase_v3, + IPaymentProcessor_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // Internal Dependencies import { - ERC20PaymentClientBase_v2, - Module_v1 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + Module_v2 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // External Dependencies import {ERC165Upgradeable} from @@ -33,28 +33,30 @@ import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; * * @dev Uses epochs to define the period of recurring payments and supports operations * such as adding, removing, and triggering payments based on time cycles. - * Integrates with {ERC20PaymentClientBase_v2} for handling actual payment + * Integrates with {ERC20PaymentClientBase_v3} for handling actual payment * transactions. Note that it will use the token type stored in the FundingManager for the payments. * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract LM_PC_RecurringPayments_v2 is - ILM_PC_RecurringPayments_v2, - ERC20PaymentClientBase_v2 +contract LM_PC_RecurringPayments_v3 is + ILM_PC_RecurringPayments_v3, + ERC20PaymentClientBase_v3 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { - return interfaceId == type(ILM_PC_RecurringPayments_v2).interfaceId + return interfaceId == type(ILM_PC_RecurringPayments_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -122,12 +124,12 @@ contract LM_PC_RecurringPayments_v2 is //-------------------------------------------------------------------------- // Initialization - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); // Set empty list of RecurringPayment _paymentList.init(); @@ -147,18 +149,18 @@ contract LM_PC_RecurringPayments_v2 is flags |= bytes32(1 << FLAG_START); flags |= bytes32(1 << FLAG_END); - __ERC20PaymentClientBase_v2_init(flags); + __ERC20PaymentClientBase_v3_init(flags); } //-------------------------------------------------------------------------- // Getter Functions - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getEpochLength() external view returns (uint) { return epochLength; } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getRecurringPaymentInformation(uint id) external view @@ -168,17 +170,17 @@ contract LM_PC_RecurringPayments_v2 is return _paymentRegistry[id]; } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function listRecurringPaymentIds() external view returns (uint[] memory) { return _paymentList.listIds(); } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getPreviousPaymentId(uint id) external view returns (uint) { return _paymentList.getPreviousId(id); } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function isExistingRecurringPaymentId(uint id) public view returns (bool) { return _paymentList.isExistingId(id); } @@ -186,7 +188,7 @@ contract LM_PC_RecurringPayments_v2 is //-------------------------------------------------------------------------- // Epoch Functions - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getEpochFromTimestamp(uint timestamp) external view @@ -195,12 +197,12 @@ contract LM_PC_RecurringPayments_v2 is return timestamp / epochLength; } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getCurrentEpoch() public view returns (uint epoch) { return block.timestamp / epochLength; } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function getFutureEpoch(uint xEpochsInTheFuture) external view @@ -212,14 +214,14 @@ contract LM_PC_RecurringPayments_v2 is //-------------------------------------------------------------------------- // Mutating Functions - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function addRecurringPayment( uint amount, uint startEpoch, address recipient ) external - onlyOrchestratorAdmin + permissioned validAmount(amount) validStartEpoch(startEpoch) validRecipient(recipient) @@ -248,10 +250,10 @@ contract LM_PC_RecurringPayments_v2 is return recurringPaymentId; } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 function removeRecurringPayment(uint prevId, uint id) external - onlyOrchestratorAdmin + permissioned { // trigger to resolve the given Payment _triggerFor(id, _paymentList.getNextId(id)); @@ -268,12 +270,14 @@ contract LM_PC_RecurringPayments_v2 is //-------------------------------------------------------------------------- // Trigger - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 + /// @dev This function is always publicly callable. function trigger() external { _triggerFor(_paymentList.getNextId(_SENTINEL), _SENTINEL); } - /// @inheritdoc ILM_PC_RecurringPayments_v2 + /// @inheritdoc ILM_PC_RecurringPayments_v3 + /// @dev This function is always publicly callable. function triggerFor(uint startId, uint endId) external validId(startId) @@ -285,10 +289,13 @@ contract LM_PC_RecurringPayments_v2 is _triggerFor(startId, _paymentList.getNextId(endId)); } + //-------------------------------------------------------------------------- + // Internal Functions + /// @dev Triggers the given RecurringPayment. /// @param startId The id of the first RecurringPayment to trigger. /// @param endId The id of the last RecurringPayment to trigger. - function _triggerFor(uint startId, uint endId) private { + function _triggerFor(uint startId, uint endId) internal { // Set startId to be the current position in List uint currentId = startId; @@ -369,7 +376,7 @@ contract LM_PC_RecurringPayments_v2 is emit RecurringPaymentsTriggered(currentEpoch); __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } } diff --git a/src/modules/logicModule/LM_PC_Staking_v2.sol b/src/modules/logicModule/LM_PC_Staking_v3.sol similarity index 88% rename from src/modules/logicModule/LM_PC_Staking_v2.sol rename to src/modules/logicModule/LM_PC_Staking_v3.sol index 3f817963e..d4e9fbd05 100644 --- a/src/modules/logicModule/LM_PC_Staking_v2.sol +++ b/src/modules/logicModule/LM_PC_Staking_v3.sol @@ -2,19 +2,20 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {ILM_PC_Staking_v2} from "@lm/interfaces/ILM_PC_Staking_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {ILM_PC_Staking_v3} from "@lm/interfaces/ILM_PC_Staking_v3.sol"; + +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // Internal Dependencies import { - ERC20PaymentClientBase_v2, - Module_v1 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + Module_v2 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -33,18 +34,20 @@ import {ReentrancyGuardUpgradeable} from * * @notice Provides a mechanism for users to stake tokens and earn rewards. * - * @dev Extends {ERC20PaymentClientBase_v2} and integrates with the Payment Processor + * @dev Extends {ERC20PaymentClientBase_v3} and integrates with the Payment Processor * to enable the distribution of rewards to stakers. * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract LM_PC_Staking_v2 is - ILM_PC_Staking_v2, - ERC20PaymentClientBase_v2, +contract LM_PC_Staking_v3 is + ILM_PC_Staking_v3, + ERC20PaymentClientBase_v3, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; @@ -54,10 +57,10 @@ contract LM_PC_Staking_v2 is public view virtual - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { - return interfaceId == type(ILM_PC_Staking_v2).interfaceId + return interfaceId == type(ILM_PC_Staking_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -102,24 +105,24 @@ contract LM_PC_Staking_v2 is //-------------------------------------------------------------------------- // Initialization - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external virtual override(Module_v1) initializer { + ) external virtual override(Module_v2) initializer { __ReentrancyGuard_init(); __Module_init(orchestrator_, metadata); address _stakingToken = abi.decode(configData, (address)); - __LM_PC_Staking_v2_init(_stakingToken); + __LM_PC_Staking_v3_init(_stakingToken); - __ERC20PaymentClientBase_v2_init(bytes32(0)); // This module does not use any PaymentOrder flags + __ERC20PaymentClientBase_v3_init(bytes32(0)); // This module does not use any PaymentOrder flags } /// @dev Initializes the staking contract. /// @param _stakingToken The address of the token that can be staked. - function __LM_PC_Staking_v2_init(address _stakingToken) + function __LM_PC_Staking_v3_init(address _stakingToken) internal onlyInitializing { @@ -129,17 +132,17 @@ contract LM_PC_Staking_v2 is //-------------------------------------------------------------------------- // Getter Functions - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getBalance(address user) external view returns (uint) { return balances[user]; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getEarned(address user) external view returns (uint) { return _earned(user, _calculateRewardValue()); } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getEstimatedReward(uint amount, uint duration) external view @@ -164,32 +167,32 @@ contract LM_PC_Staking_v2 is return (amount * duration * rewardRate) / totalSupply; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getStakingToken() external view returns (address) { return stakingToken; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getTotalSupply() external view returns (uint) { return totalSupply; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getRewardRate() external view returns (uint) { return rewardRate; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getRewardsEnd() external view returns (uint) { return rewardsEnd; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getRewardValue() external view returns (uint) { return rewardValue; } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function getLastUpdate() external view returns (uint) { return lastUpdate; } @@ -197,28 +200,30 @@ contract LM_PC_Staking_v2 is //-------------------------------------------------------------------------- // Mutating Functions - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function stake(uint amount) external virtual nonReentrant validAmount(amount) + permissioned { address sender = _msgSender(); _stake(sender, amount); - // transfer funds to LM_PC_Staking_v2 + // transfer funds to LM_PC_Staking_v3 IERC20(stakingToken).safeTransferFrom(sender, address(this), amount); } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 /// @dev this function will revert with a Over/Underflow error in case amount is higher than balance. function unstake(uint amount) external virtual nonReentrant validAmount(amount) + permissioned { address sender = _msgSender(); // Update rewardValue, updatedTimestamp and earned values @@ -241,7 +246,7 @@ contract LM_PC_Staking_v2 is emit Unstaked(sender, amount); } - /// @inheritdoc ILM_PC_Staking_v2 + /// @inheritdoc ILM_PC_Staking_v3 function claimRewards() external virtual nonReentrant { address recipient = _msgSender(); @@ -249,16 +254,13 @@ contract LM_PC_Staking_v2 is _distributeRewards(recipient); } - /// @inheritdoc ILM_PC_Staking_v2 - function setRewards(uint amount, uint duration) - external - onlyOrchestratorAdmin - { + /// @inheritdoc ILM_PC_Staking_v3 + function setRewards(uint amount, uint duration) external permissioned { _setRewards(amount, duration); } //-------------------------------------------------------------------------- - // Private Functions + // Internal Functions /// @dev Stakes tokens. /// @param depositFor The address of the user. @@ -380,7 +382,7 @@ contract LM_PC_Staking_v2 is ); __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); emit RewardsDistributed(recipient, amount); @@ -409,7 +411,7 @@ contract LM_PC_Staking_v2 is // RewardRate cant be zero if (rewardRate == 0) { - revert Module__LM_PC_Staking_v2__InvalidRewardRate(); + revert Module__LM_PC_Staking_v3__InvalidRewardRate(); } // Rewards end is now plus duration @@ -428,7 +430,7 @@ contract LM_PC_Staking_v2 is _token == address(0) || _token == address(orchestrator().fundingManager().token()) ) { - revert Module__LM_PC_Staking_v2__InvalidStakingToken(); + revert Module__LM_PC_Staking_v3__InvalidStakingToken(); } stakingToken = _token; emit StakingTokenSet(_token); @@ -438,7 +440,7 @@ contract LM_PC_Staking_v2 is /// @param duration The duration of the reward period. function _ensureValidDuration(uint duration) internal pure { if (duration == 0) { - revert Module__LM_PC_Staking_v2__InvalidDuration(); + revert Module__LM_PC_Staking_v3__InvalidDuration(); } } } diff --git a/src/modules/logicModule/abstracts/ERC20PaymentClientBase_v2.sol b/src/modules/logicModule/abstracts/ERC20PaymentClientBase_v3.sol similarity index 90% rename from src/modules/logicModule/abstracts/ERC20PaymentClientBase_v2.sol rename to src/modules/logicModule/abstracts/ERC20PaymentClientBase_v3.sol index 2db4e4b04..ad57b20c7 100644 --- a/src/modules/logicModule/abstracts/ERC20PaymentClientBase_v2.sol +++ b/src/modules/logicModule/abstracts/ERC20PaymentClientBase_v3.sol @@ -2,14 +2,15 @@ pragma solidity 0.8.23; // Internal Interfaces -import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; + // Internal Dependencies -import {Module_v1, ContextUpgradeable} from "src/modules/base/Module_v1.sol"; +import {Module_v2, ContextUpgradeable} from "src/modules/base/Module_v2.sol"; // External Libraries import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; @@ -28,7 +29,7 @@ import {ERC165Upgradeable} from * that can be processed by authorized payment processors, ensuring efficient * and secure transactions. * - * @dev Utilizes {SafeERC20} for token operations and integrates with {IPaymentProcessor_v2} + * @dev Utilizes {SafeERC20} for token operations and integrates with {IPaymentProcessor_v3} * to handle token payments. This abstract contract must be extended by modules * that manage {ERC20} payment orders, supporting complex payment scenarios. * @@ -36,21 +37,23 @@ import {ERC165Upgradeable} from * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -abstract contract ERC20PaymentClientBase_v2 is - IERC20PaymentClientBase_v2, - Module_v1 +abstract contract ERC20PaymentClientBase_v3 is + IERC20PaymentClientBase_v3, + Module_v2 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId == type(IERC20PaymentClientBase_v2).interfaceId + return interfaceId == type(IERC20PaymentClientBase_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -102,7 +105,7 @@ abstract contract ERC20PaymentClientBase_v2 is /// @dev Initializes the staking contract. /// @param flags_ The flags, represented as an array of uint8 containing /// the flag IDs between 0 and 255. - function __ERC20PaymentClientBase_v2_init(bytes32 flags_) + function __ERC20PaymentClientBase_v3_init(bytes32 flags_) internal onlyInitializing { @@ -163,9 +166,9 @@ abstract contract ERC20PaymentClientBase_v2 is } //-------------------------------------------------------------------------- - // IERC20PaymentClientBase_v2 Functions + // IERC20PaymentClientBase_v3 Functions - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function paymentOrders() external view @@ -175,7 +178,7 @@ abstract contract ERC20PaymentClientBase_v2 is return _orders; } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function outstandingTokenAmount(address token_) external view @@ -185,7 +188,7 @@ abstract contract ERC20PaymentClientBase_v2 is return _outstandingTokenAmounts[token_]; } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function collectPaymentOrders() external virtual @@ -196,7 +199,7 @@ abstract contract ERC20PaymentClientBase_v2 is ) { // Ensure caller is authorized to act as payment processor. - if (!_isAuthorizedPaymentProcessor(IPaymentProcessor_v2(_msgSender()))) + if (!_isAuthorizedPaymentProcessor(IPaymentProcessor_v3(_msgSender()))) { revert Module__ERC20PaymentClientBase__CallerNotAuthorized(); } @@ -240,7 +243,7 @@ abstract contract ERC20PaymentClientBase_v2 is // Ensure payment processor is able to fetch the tokens from address(this). _ensureTokenAllowance( - IPaymentProcessor_v2(_msgSender()), tokens_[i] + IPaymentProcessor_v3(_msgSender()), tokens_[i] ); // Ensure that the Client will have sufficient funds. @@ -255,10 +258,10 @@ abstract contract ERC20PaymentClientBase_v2 is return (paymentOrders_, tokens_, totalAmounts_); } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function amountPaid(address token_, uint amount_) public virtual { // Ensure caller is authorized to act as payment processor. - if (!_isAuthorizedPaymentProcessor(IPaymentProcessor_v2(_msgSender()))) + if (!_isAuthorizedPaymentProcessor(IPaymentProcessor_v3(_msgSender()))) { revert Module__ERC20PaymentClientBase__CallerNotAuthorized(); } @@ -267,12 +270,12 @@ abstract contract ERC20PaymentClientBase_v2 is _outstandingTokenAmounts[token_] -= amount_; } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function getFlags() public view returns (bytes32 flags_) { return (_flags); } - /// @inheritdoc IERC20PaymentClientBase_v2 + /// @inheritdoc IERC20PaymentClientBase_v3 function getFlagCount() public view returns (uint8 flagCount_) { return _flagCount; } @@ -313,7 +316,7 @@ abstract contract ERC20PaymentClientBase_v2 is } //-------------------------------------------------------------------------- - // {ERC20PaymentClientBase_v2} Function Implementations + // {ERC20PaymentClientBase_v3} Function Implementations /// @dev Ensures `amount` of payment tokens exist in address(this). In case the token being paid out is the /// FundingManager token, it will trigger a callback to the FundingManager to transfer the tokens to @@ -340,7 +343,7 @@ abstract contract ERC20PaymentClientBase_v2 is } /// @dev Ensures `amount` of token allowance for payment processor(s). - function _ensureTokenAllowance(IPaymentProcessor_v2 spender, address token) + function _ensureTokenAllowance(IPaymentProcessor_v3 spender, address token) internal virtual { @@ -350,7 +353,7 @@ abstract contract ERC20PaymentClientBase_v2 is } /// @dev Returns whether address `who` is an authorized payment processor. - function _isAuthorizedPaymentProcessor(IPaymentProcessor_v2 who) + function _isAuthorizedPaymentProcessor(IPaymentProcessor_v3 who) internal view virtual diff --git a/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator.sol b/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator_v3.sol similarity index 78% rename from src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator.sol rename to src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator_v3.sol index 59308e76f..819cadcc6 100644 --- a/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator.sol +++ b/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator_v3.sol @@ -4,7 +4,20 @@ pragma solidity ^0.8.0; import {OptimisticOracleV3CallbackRecipientInterface} from "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/optimistic-oracle-v3/interfaces/OptimisticOracleV3CallbackRecipientInterface.sol"; -interface IOptimisticOracleIntegrator is +/** + * @title Inverter Optimistic Oracle Integrator Interface + * + * @notice This module allows for the integration of the UMA OptimisticOracleV3 contract with our modules. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface IOptimisticOracleIntegrator_v3 is OptimisticOracleV3CallbackRecipientInterface { //-------------------------------------------------------------------------- @@ -55,22 +68,22 @@ interface IOptimisticOracleIntegrator is // Errors /// @notice Invalid default currency. - error Module__OptimisticOracleIntegrator__InvalidDefaultCurrency(); + error Module__OptimisticOracleIntegrator_v3__InvalidDefaultCurrency(); /// @notice Invalid default liveness. - error Module__OptimisticOracleIntegrator__InvalidDefaultLiveness(); + error Module__OptimisticOracleIntegrator_v3__InvalidDefaultLiveness(); /// @notice Invalid Optimistic Oracle instance. - error Module__OptimisticOracleIntegrator__InvalidOOInstance(); + error Module__OptimisticOracleIntegrator_v3__InvalidOOInstance(); /// @notice Caller is not Optimistic Oracle instance. - error Module__OptimisticOracleIntegrator__CallerNotOO(); + error Module__OptimisticOracleIntegrator_v3__CallerNotOO(); /// @notice Bond given for the specified currency is below minimum. - error Module__OptimisticOracleIntegrator__CurrencyBondTooLow(); + error Module__OptimisticOracleIntegrator_v3__CurrencyBondTooLow(); /// @notice Asserter holds insufficient funds to pay for bond. - error Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond(); + error Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond(); //========================================================================== // Functions @@ -98,22 +111,26 @@ interface IOptimisticOracleIntegrator is // Setter Functions /// @notice Sets the default currency and amount for the bond. + /// @dev Function access controlled by authorizer. /// @param _newCurrency The address of the new default currency. /// @param _newBond The new bond amount. function setDefaultCurrencyAndBond(address _newCurrency, uint _newBond) external; /// @notice Sets the OptimisticOracleV3 instance where assertions will be published to. + /// @dev Function access controlled by authorizer. /// @param _newOO The address of the new OptimisticOracleV3 instance. function setOptimisticOracle(address _newOO) external; /// @notice Sets the default time assertions will be open for dispute. + /// @dev Function access controlled by authorizer. /// @param _newLiveness The new liveness in seconds. function setDefaultAssertionLiveness(uint64 _newLiveness) external; // State mutating functions /// @notice Asserts data for a specific dataId on behalf of an asserter address. + /// @dev Function access controlled by authorizer. /// @param dataId The id of the data to assert. /// @param data The data to assert. /// @param asserter The address doing the asserter. If zero defaults to _msgSender(). diff --git a/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol b/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol similarity index 85% rename from src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol rename to src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol index 1c8e3bcba..89fd06e11 100644 --- a/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol +++ b/src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IOptimisticOracleIntegrator} from - "src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IOptimisticOracleIntegrator_v3} from + "src/modules/logicModule/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/IOptimisticOracleIntegrator_v3.sol"; // Internal Dependencies -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -32,12 +32,14 @@ import {ERC165Upgradeable} from * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -abstract contract OptimisticOracleIntegrator is - IOptimisticOracleIntegrator, - Module_v1 +abstract contract OptimisticOracleIntegrator_v3 is + IOptimisticOracleIntegrator_v3, + Module_v2 { using SafeERC20 for IERC20; @@ -46,21 +48,15 @@ abstract contract OptimisticOracleIntegrator is public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId == type(IOptimisticOracleIntegrator).interfaceId + return interfaceId == type(IOptimisticOracleIntegrator_v3).interfaceId || interfaceId == type(OptimisticOracleV3CallbackRecipientInterface).interfaceId || super.supportsInterface(interfaceId); } - //========================================================================== - // Constants - - /// @dev The role that is allowed to assert data. - bytes32 public constant ASSERTER_ROLE = keccak256("DATA_ASSERTER"); - //========================================================================== // Storage @@ -85,9 +81,9 @@ abstract contract OptimisticOracleIntegrator is //========================================================================== // Initialization - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData ) external virtual override initializer { @@ -96,12 +92,12 @@ abstract contract OptimisticOracleIntegrator is (address currencyAddr, uint bondAmount, address ooAddr, uint64 liveness) = abi.decode(configData, (address, uint, address, uint64)); - __OptimisticOracleIntegrator_init( + __OptimisticOracleIntegrator_v3_init( currencyAddr, bondAmount, ooAddr, liveness ); } - function __OptimisticOracleIntegrator_init( + function __OptimisticOracleIntegrator_v3_init( address currencyAddr, uint bondAmount, address ooAddr, @@ -115,13 +111,13 @@ abstract contract OptimisticOracleIntegrator is //-------------------------------------------------------------------------- // Getter Functions - /// @inheritdoc IOptimisticOracleIntegrator + /// @inheritdoc IOptimisticOracleIntegrator_v3 function getData(bytes32 assertionId) public view returns (bool, bytes32) { if (!assertionData[assertionId].resolved) return (false, 0); return (true, assertionData[assertionId].data); } - /// @inheritdoc IOptimisticOracleIntegrator + /// @inheritdoc IOptimisticOracleIntegrator_v3 function getAssertion(bytes32 assertionId) public view @@ -133,23 +129,23 @@ abstract contract OptimisticOracleIntegrator is //========================================================================== // Setter Functions - /// @inheritdoc IOptimisticOracleIntegrator + /// @inheritdoc IOptimisticOracleIntegrator_v3 function setDefaultCurrencyAndBond(address _newCurrency, uint _newBond) public - onlyOrchestratorAdmin + permissioned { _setDefaultCurrencyAndBond(_newCurrency, _newBond); } - /// @inheritdoc IOptimisticOracleIntegrator - function setOptimisticOracle(address _newOO) public onlyOrchestratorAdmin { + /// @inheritdoc IOptimisticOracleIntegrator_v3 + function setOptimisticOracle(address _newOO) public permissioned { _setOptimisticOracle(_newOO); } - /// @inheritdoc IOptimisticOracleIntegrator + /// @inheritdoc IOptimisticOracleIntegrator_v3 function setDefaultAssertionLiveness(uint64 _newLiveness) public - onlyOrchestratorAdmin + permissioned { _setDefaultAssertionLiveness(_newLiveness); } @@ -164,10 +160,11 @@ abstract contract OptimisticOracleIntegrator is internal { if (address(_newCurrency) == address(0)) { - revert Module__OptimisticOracleIntegrator__InvalidDefaultCurrency(); + revert Module__OptimisticOracleIntegrator_v3__InvalidDefaultCurrency( + ); } if (_newBond < oo.getMinimumBond(address(_newCurrency))) { - revert Module__OptimisticOracleIntegrator__CurrencyBondTooLow(); + revert Module__OptimisticOracleIntegrator_v3__CurrencyBondTooLow(); } defaultCurrency = IERC20(_newCurrency); @@ -178,7 +175,7 @@ abstract contract OptimisticOracleIntegrator is /// @param _newOO The address of the new OptimisticOracleV3 instance. function _setOptimisticOracle(address _newOO) internal { if (_newOO == address(0)) { - revert Module__OptimisticOracleIntegrator__InvalidOOInstance(); + revert Module__OptimisticOracleIntegrator_v3__InvalidOOInstance(); } oo = OptimisticOracleV3Interface(_newOO); defaultIdentifier = oo.defaultIdentifier(); @@ -189,7 +186,8 @@ abstract contract OptimisticOracleIntegrator is function _setDefaultAssertionLiveness(uint64 _newLiveness) internal { if (_newLiveness < 21_600) { // 21600 seconds = 6 hours - revert Module__OptimisticOracleIntegrator__InvalidDefaultLiveness(); + revert Module__OptimisticOracleIntegrator_v3__InvalidDefaultLiveness( + ); } assertionLiveness = _newLiveness; } @@ -197,14 +195,14 @@ abstract contract OptimisticOracleIntegrator is //-------------------------------------------------------------------------- // Mutating Functions - /// @inheritdoc IOptimisticOracleIntegrator + /// @inheritdoc IOptimisticOracleIntegrator_v3 /// @dev Data can be asserted many times with the same combination of arguments, resulting in unique assertionIds. /// This is because the block.timestamp is included in the claim. The consumer contract must /// store the returned assertionId identifiers to able to get the information using getData. function assertDataFor(bytes32 dataId, bytes32 data_, address asserter) public virtual - onlyModuleRole(ASSERTER_ROLE) + permissioned returns (bytes32 assertionId) { asserter = asserter == address(0) ? _msgSender() : asserter; @@ -212,7 +210,7 @@ abstract contract OptimisticOracleIntegrator is // ensure we have enough balance if (defaultCurrency.balanceOf(address(this)) < defaultBond) { revert - Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond( + Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond( ); } } else { @@ -232,7 +230,7 @@ abstract contract OptimisticOracleIntegrator is || address(defaultCurrency).code.length == 0 ) { revert - Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond( + Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond( ); } } @@ -283,7 +281,7 @@ abstract contract OptimisticOracleIntegrator is bool assertedTruthfully ) public virtual { if (_msgSender() != address(oo)) { - revert Module__OptimisticOracleIntegrator__CallerNotOO(); + revert Module__OptimisticOracleIntegrator_v3__CallerNotOO(); } DataAssertion memory dataAssertion = assertionData[assertionId]; diff --git a/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v1.sol b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v1.sol index b666e3bc9..8884d8dec 100644 --- a/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v1.sol +++ b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v1.sol @@ -1,10 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -// Internal Interfaces -import {IPaymentProcessor_v1} from - "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; - /** * @title Inverter ERC20 Payment Client Base Interface * @@ -33,6 +29,8 @@ import {IPaymentProcessor_v1} from * contain 3 elements of the type specified in the master list, each * stored as bytes32 value. * + * @custom:version v1.0.0 + * * @author Inverter Network */ interface IERC20PaymentClientBase_v1 { diff --git a/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol index 911f0d299..50a06eebe 100644 --- a/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol +++ b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol @@ -1,10 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -// Internal Interfaces -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; - /** * @title Inverter ERC20 Payment Client Base Interface * @@ -33,6 +29,8 @@ import {IPaymentProcessor_v2} from * contain 3 elements of the type specified in the master list, each * stored as bytes32 value. * + * @custom:version v2.0.0 + * * @author Inverter Network */ interface IERC20PaymentClientBase_v2 { diff --git a/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol new file mode 100644 index 000000000..e5f5a8631 --- /dev/null +++ b/src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +/** + * @title Inverter ERC20 Payment Client Base Interface + * + * @notice Enables modules within the Inverter Network to create and manage + * payment orders that can be processed by authorized payment + * processors, ensuring efficient and secure transactions. Refer to + * the implementations contract for more details. + * + * @dev STRUCTURING OF THE FLAGS AND DATA FIELDS + * The PaymentOrder struct implements a flag system to manage the + * information payloads received by the payment processor. It is + * comprised of a bytes32 value that indicates the number of flags + * that are active, and a bytes32[] value that stores the + * corresponding values. + * + * For example: + * If the value of 'flags' is '0000 [...] 0000 1011', then that order + * stores values for the paramters 0, 1 and 3 of the master list. The + * byte code for simple flag setups might also be represented by + * hexadecimal values like 0xB, which has the same value as the bit + * combination above. + * + * If a module wants to set flags, it can use bit shifts, in this case + * 1 << 0, 1 << 1 and 1 << 3. + * Afterwards, to be correct, the following data variable should + * contain 3 elements of the type specified in the master list, each + * stored as bytes32 value. + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface IERC20PaymentClientBase_v3 { + //------------------------------------------------------------------------- + // MASTER LIST OF PAYMENT ORDER FLAGS + + /* + | Flag | Type | Name | Description | + |------|---------|----------|------------------------------------| + | 0 | uint256 | orderID | ID of the order within the client | + | 1 | uint256 | start | Start date of the streaming period | + | 2 | uint256 | cliff | Duration of the cliff period | + | 3 | uint256 | end | Due Date of the order | + | 4 | uint256 | projectFee | Project fee for the order | + | 5 | uint256 | maxFee | Maximum fee | + | 6 | uint256 | TTL | Time-to-live | + | ... | ... | ... | (yet unassigned) | + | 255 | . | . | (Max Value) | + |------|---------|----------|------------------------------------| + */ + + //-------------------------------------------------------------------------- + // Structs + + /// @notice Struct used to store information about a payment order. + /// @param recipient The recipient of the payment. + /// @param paymentToken The token in which to pay. Assumed to always + /// be on the local chain. + /// @param amount The amount of tokens to pay. + /// @param originChainId The id of the origin chain. + /// @param targetChainId The id of the target chain. + /// @param flags Flags that indicate which information the data array + /// contains. + /// @param data Array of additional data regarding the payment order. + struct PaymentOrder { + address recipient; + address paymentToken; + uint amount; + uint originChainId; + uint targetChainId; + bytes32 flags; + bytes32[] data; + } + + //-------------------------------------------------------------------------- + // Errors + + /// @notice Function is only callable by authorized address. + error Module__ERC20PaymentClientBase__CallerNotAuthorized(); + + /// @notice ERC20 token transfer failed. + error Module__ERC20PaymentClientBase__TokenTransferFailed(); + + /// @notice Insufficient funds to fulfill the payment. + /// @param token The token in which the payment was made. + error Module__ERC20PaymentClientBase__InsufficientFunds(address token); + + /// @notice Given recipient invalid. + error Module__ERC20PaymentClientBase__InvalidRecipient(); + + /// @notice Given token invalid. + error Module__ERC20PaymentClientBase__InvalidToken(); + + /// @notice Given amount invalid. + error Module__ERC20PaymentClientBase__InvalidAmount(); + + /// @notice Given paymentOrder is invalid. + error Module__ERC20PaymentClientBase__InvalidPaymentOrder(); + + /// @notice Given mismatch between flag count and supplied array length. + error Module__ERC20PaymentClientBase__MismatchBetweenFlagCountAndArrayLength( + uint8 flagCount, uint arrayLength + ); + + /// @notice Given number of flags exceeds the limit. + error Module__ERC20PaymentClientBase_v3__FlagAmountTooHigh(); + + //-------------------------------------------------------------------------- + // Events + + /// @notice Added a payment order. + /// @param recipient The address that will receive the payment. + /// @param token The token in which to pay. + /// @param amount The amount of tokens the payment consists of. + /// @param originChainId The id of the origin chain. + /// @param targetChainId The id of the target chain. + /// @param flags Flags that indicate additional data used by the payment + /// order. + /// @param data Array of additional data regarding the payment order. + event PaymentOrderAdded( + address indexed recipient, + address indexed token, + uint amount, + uint originChainId, + uint targetChainId, + bytes32 flags, + bytes32[] data + ); + + /// @notice Emitted when the flags are set. + /// @param flagCount The number of flags set. + /// @param newFlags The newly set flags. + event FlagsSet(uint8 flagCount, bytes32 newFlags); + + //-------------------------------------------------------------------------- + // Functions + + /// @notice Returns the list of outstanding payment orders. + /// @return list of payment orders. + function paymentOrders() external view returns (PaymentOrder[] memory); + + /// @notice Returns the total outstanding token payment amount. + /// @param token_ The token in which to pay. + /// @return total_ amount of token to pay. + function outstandingTokenAmount(address token_) + external + view + returns (uint total_); + + /// @notice Collects outstanding payment orders. + /// @dev Marks the orders as completed for the client. + /// @return paymentOrders_ list of payment orders. + /// @return tokens_ list of token addresses. + /// @return totalAmounts_ list of amounts. + function collectPaymentOrders() + external + returns ( + PaymentOrder[] memory paymentOrders_, + address[] memory tokens_, + uint[] memory totalAmounts_ + ); + + /// @notice Notifies the PaymentClient, that tokens have been paid out accordingly. + /// @dev Payment Client will reduce the total amount of tokens it will stock up by the given amount. + /// @dev This has to be called by a paymentProcessor. + /// @param token_ The token in which the payment was made. + /// @param amount_ amount of tokens that have been paid out. + function amountPaid(address token_, uint amount_) external; + + /// @notice Returns the flags used when creating payment orders in this + /// client. + /// @return flags_ The flags this client will use. + function getFlags() external view returns (bytes32 flags_); + + /// @notice Returns the number of flags this client uses for PaymentOrders. + /// @return flagCount_ The number of flags. + function getFlagCount() external view returns (uint8 flagCount_); +} diff --git a/src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v1.sol b/src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v2.sol similarity index 67% rename from src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v1.sol rename to src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v2.sol index a732871bb..49779e55f 100644 --- a/src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v1.sol +++ b/src/modules/logicModule/interfaces/ILM_Oracle_Permissioned_v2.sol @@ -12,9 +12,9 @@ import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; * for both issuance (buying) and redemption (selling) operations. * * @dev This contract inherits from: - * - ILM_Oracle_Permissioned_v1: Implementation interface. + * - ILM_Oracle_Permissioned_v2: Implementation interface. * - IOraclePrice_v1: Oracle price interface. - * - Module_v1: Base module functionality. + * - Module_v2: Base module functionality. * * Key features: * - Two separate price feeds for issuance and redemption. @@ -34,54 +34,34 @@ import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; * - To price redeeming 1 token at 0.5 collateral with 6 decimal * collateral: 500_000 * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Price Setter Role: * - Purpose: The price setter role is authorized to set * prices for issuance and redemption operations. - * - How: The OrchestratorAdmin (or PRICE_SETTER_ROLE_ADMIN - * if configured) must: - * 1. Retrieve the price setter role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getPriceSetterRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Price Setter Role Admin: - * - Purpose: Enables delegation of price setter role - * management to a dedicated admin role instead of - * relying on the OrchestratorAdmin. This allows - * for more granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getPriceSetterRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getPriceSetterRoleAdmin() - * ) - * ); + * 1. Create a price setter role + * 2. Add access permission for the + * setIssuancePrice(), setRedemptionPrice() + * and setIssuanceAndRedemptionPrice() + * functions to the price setter role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer * to our Security Policy at security.inverter.network * or email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -interface ILM_Oracle_Permissioned_v1 is IOraclePrice_v1 { +interface ILM_Oracle_Permissioned_v2 is IOraclePrice_v1 { // ------------------------------------------------------------------------- // Errors @@ -94,6 +74,7 @@ interface ILM_Oracle_Permissioned_v1 is IOraclePrice_v1 { /// @notice Sets the issuance price for token issuance (buying tokens) /// Price represents how much collateral is paid for 1 issuance token. + /// @dev Function access controlled by authorizer. /// @dev Must be non-zero and denominated in collateral token decimals. /// For example: With 6 decimal collateral token, /// - To price 1 issuance token at 1.5 collateral, use 1_500_000 @@ -103,6 +84,7 @@ interface ILM_Oracle_Permissioned_v1 is IOraclePrice_v1 { /// @notice Sets the redemption price for token redemption (selling tokens) /// Price represents how much collateral is returned for 1 issuance token. + /// @dev Function only callable by claim contributors /// @dev Must be non-zero and denominated in collateral token decimals. /// For example: With 6 decimal collateral token, /// - To price 1 issuance token at 1.5 collateral, use 1_500_000 @@ -112,6 +94,7 @@ interface ILM_Oracle_Permissioned_v1 is IOraclePrice_v1 { /// @notice Sets both issuance and redemption prices atomically, denominated /// in the collateral token decimals. + /// @dev Function only callable by claim contributors /// @dev Both prices must be non-zero. Both the issuance and redemption /// prices should be denominated in the collateral token decimals. /// For example, if the collateral token has 6 decimals and the @@ -131,12 +114,4 @@ interface ILM_Oracle_Permissioned_v1 is IOraclePrice_v1 { /// are denominated. /// @return decimals_ The decimals of the collateral token. function getCollateralTokenDecimals() external view returns (uint8); - - /// @notice Gets the price setter role identifier. - /// @return bytes32 The PRICE_SETTER_ROLE identifier - function getPriceSetterRole() external pure returns (bytes32); - - /// @notice Gets the price setter role admin identifier. - /// @return bytes32 The PRICE_SETTER_ROLE_ADMIN identifier - function getPriceSetterRoleAdmin() external pure returns (bytes32); } diff --git a/src/modules/logicModule/interfaces/ILM_PC_Bounties_v2.sol b/src/modules/logicModule/interfaces/ILM_PC_Bounties_v3.sol similarity index 87% rename from src/modules/logicModule/interfaces/ILM_PC_Bounties_v2.sol rename to src/modules/logicModule/interfaces/ILM_PC_Bounties_v3.sol index adac3ec7d..39b2cff9d 100644 --- a/src/modules/logicModule/interfaces/ILM_PC_Bounties_v2.sol +++ b/src/modules/logicModule/interfaces/ILM_PC_Bounties_v3.sol @@ -1,10 +1,30 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; - -interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; + +/** + * @title Inverter Bounty Manager Interface + * + * @notice Provides functionality to manage bounties and process claims, + * allowing participants to propose, update, and claim bounties securely + * and transparently. + * + * @dev Extends {ERC20PaymentClientBase_v3} to integrate payment processing with + * bounty management, supporting dynamic additions, updates, and the locking + * of bounties. Utilizes roles for managing permissions and maintaining robust + * control over bounty operations. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface ILM_PC_Bounties_v3 is IERC20PaymentClientBase_v3 { //-------------------------------------------------------------------------- // Structs @@ -188,6 +208,7 @@ interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { // Bounty Mutating Functions /// @notice Adds a new Bounty. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param minimumPayoutAmount The minimum amount of tokens the Bounty will pay out upon being claimed. /// @param maximumPayoutAmount The maximum amount of tokens the Bounty will pay out upon being claimed. @@ -200,6 +221,7 @@ interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { ) external returns (uint); /// @notice Adds a new array of Bounties. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param minimumPayoutAmounts The array of minimum amount of tokens the Bounty will pay out upon being claimed /// @param maximumPayoutAmounts The array of maximum amount of tokens the Bounty will pay out upon being claimed @@ -212,18 +234,21 @@ interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { ) external returns (uint[] memory ids); /// @notice Updates a Bounty's informations. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param bountyId The id of the Bounty that will be updated. /// @param details The Bounty's details. function updateBounty(uint bountyId, bytes calldata details) external; /// @notice Locks the Bounty so it cant be claimed. + /// @dev Function access controlled by authorizer. /// @dev Only callable by authorized addresses. /// @dev Reverts if id invalid. /// @param bountyId The id of the Bounty that will be locked. function lockBounty(uint bountyId) external; /// @notice Adds a new Claim. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param bountyId The id of the bounty this claim belongs to. /// @param contributors The contributor information for the Claim. @@ -236,6 +261,7 @@ interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { ) external returns (uint); /// @notice Updates a Claim's contributor informations. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param claimId The id of the Claim that will be updated. /// @param contributors The contributor information for the Claim. @@ -245,13 +271,14 @@ interface ILM_PC_Bounties_v2 is IERC20PaymentClientBase_v2 { ) external; /// @notice Updates a Claim Details. + /// @dev Function only callable by claim contributors /// @param claimId The id of the Claim that will be updated. /// @param details The Claim's details. function updateClaimDetails(uint claimId, bytes calldata details) external; /// @notice Completes a Bounty by verifying a claim. - /// @dev Only callable by authorized addresses. + /// @dev Function access controlled by authorizer. /// @dev Reverts if id invalid. /// @dev contributors should be copied out of the given Claim. The parameter is used to prevent front running. /// @param claimId The id of the Claim that wants to claim the Bounty. diff --git a/src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v2.sol b/src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v3.sol similarity index 80% rename from src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v2.sol rename to src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v3.sol index 23e3194b2..25b02a07a 100644 --- a/src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v2.sol +++ b/src/modules/logicModule/interfaces/ILM_PC_KPIRewarder_v3.sol @@ -1,7 +1,24 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -interface ILM_PC_KPIRewarder_v2 { +/** + * @title Inverter KPI Rewarder Module Interface + * + * @notice Provides a mechanism for distributing rewards to stakers based + * on Key Performance Indicators (KPIs). + * + * @dev Extends {LM_PC_Staking_v3} and integrates with {OptimisticOracleIntegrator_v3} + * to enable KPI-based reward distribution within the staking manager. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface ILM_PC_KPIRewarder_v3 { //-------------------------------------------------------------------------- // Structs @@ -35,33 +52,33 @@ interface ILM_PC_KPIRewarder_v2 { // Errors /// @notice The KPI beinge created has either no tranches or too many. - error Module__LM_PC_KPIRewarder_v2__InvalidTrancheNumber(); + error Module__LM_PC_KPIRewarder_v3__InvalidTrancheNumber(); /// @notice The number of tranches in the KPI does not match the number of rewards. - error Module__LM_PC_KPIRewarder_v2__InvalidKPIValueLengths(); + error Module__LM_PC_KPIRewarder_v3__InvalidKPIValueLengths(); /// @notice The values for the tranches are not in ascending order. - error Module__LM_PC_KPIRewarder_v2__InvalidKPITrancheValues(); + error Module__LM_PC_KPIRewarder_v3__InvalidKPITrancheValues(); /// @notice The KPI number is invalid. - error Module__LM_PC_KPIRewarder_v2__InvalidKPINumber(); + error Module__LM_PC_KPIRewarder_v3__InvalidKPINumber(); /// @notice The Token used paying the bond cannot be the same that is being staked. - error Module__LM_PC_KPIRewarder_v2__ModuleCannotUseStakingTokenAsBond(); + error Module__LM_PC_KPIRewarder_v3__ModuleCannotUseStakingTokenAsBond(); /// @notice An assertion can only by posted if the preceding one is resolved. - error Module__LM_PC_KPIRewarder_v2__UnresolvedAssertionExists(); + error Module__LM_PC_KPIRewarder_v3__UnresolvedAssertionExists(); /// @notice The user cannot stake while an assertion is unresolved. - error Module__LM_PC_KPIRewarder_v2__CannotStakeWhenAssertionPending(); + error Module__LM_PC_KPIRewarder_v3__CannotStakeWhenAssertionPending(); /// @notice Callback received references non existent assertionId. - error Module__LM_PC_KPIRewarder_v2__NonExistentAssertionId( + error Module__LM_PC_KPIRewarder_v3__NonExistentAssertionId( bytes32 assertionId ); /// @notice The assertion that is being removed was not stuck. - error Module__LM_PC_KPIRewarder_v2__AssertionNotStuck(bytes32 assertionId); + error Module__LM_PC_KPIRewarder_v3__AssertionNotStuck(bytes32 assertionId); //-------------------------------------------------------------------------- // Events @@ -103,9 +120,34 @@ interface ILM_PC_KPIRewarder_v2 { event DeletedStuckAssertion(bytes32 indexed assertionId); //-------------------------------------------------------------------------- - // Functions + // Getter + + /// @notice Returns the KPI with the given number. + /// @param KPInum The number of the KPI to return. + /// @return The KPI. + function getKPI(uint KPInum) external view returns (KPI memory); + + /// @notice Returns the Assertion Configuration for a given assertionId. + /// @param assertionId The id of the Assertion to return. + /// @return The Assertion Configuration. + function getAssertionConfig(bytes32 assertionId) + external + view + returns (RewardRoundConfiguration memory); + + /// @notice Returns the current KPI counter. + /// @return The KPI counter. + function getKPICounter() external view returns (uint); + + /// @notice Returns the assertion pending flag. + /// @return The assertion pending flag. + function getAssertionPending() external view returns (bool); + + //-------------------------------------------------------------------------- + // Mutating /// @notice Posts an assertion to the Optimistic Oracle, specifying the KPI to use and the asserted value. + /// @dev Function access controlled by authorizer. /// @param dataId The dataId to be posted. /// @param assertedValue The target value that will be asserted and posted as data to the oracle. /// @param asserter The address of the asserter. @@ -119,6 +161,7 @@ interface ILM_PC_KPIRewarder_v2 { ) external returns (bytes32 assertionId); /// @notice Creates a KPI for the Rewarder. + /// @dev Function access controlled by authorizer. /// @param _continuous Should the tranche rewards be distributed continuously or in steps. /// @param _trancheValues The value at which the tranches end. /// @param _trancheRewards The rewards to be distributed at completion of each tranche. @@ -133,29 +176,9 @@ interface ILM_PC_KPIRewarder_v2 { /// @param amount The amount to deposit. function depositFeeFunds(uint amount) external; - /// @notice Returns the KPI with the given number. - /// @param KPInum The number of the KPI to return. - /// @return The KPI. - function getKPI(uint KPInum) external view returns (KPI memory); - - /// @notice Returns the Assertion Configuration for a given assertionId. - /// @param assertionId The id of the Assertion to return. - /// @return The Assertion Configuration. - function getAssertionConfig(bytes32 assertionId) - external - view - returns (RewardRoundConfiguration memory); - /// @notice Deletes a stuck assertion. + /// @dev Function access controlled by authorizer. /// @dev This function is only callable by the Orchestrator Admin. /// @param assertionId The id of the assertion to delete. function deleteStuckAssertion(bytes32 assertionId) external; - - /// @notice Returns the current KPI counter. - /// @return The KPI counter. - function getKPICounter() external view returns (uint); - - /// @notice Returns the assertion pending flag. - /// @return The assertion pending flag. - function getAssertionPending() external view returns (bool); } diff --git a/src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v2.sol b/src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v3.sol similarity index 67% rename from src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v2.sol rename to src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v3.sol index 7010d9893..cb47fcdf7 100644 --- a/src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v2.sol +++ b/src/modules/logicModule/interfaces/ILM_PC_PaymentRouter_v3.sol @@ -1,17 +1,36 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -interface ILM_PC_PaymentRouter_v2 { +/** + * @title Inverter Payment Router interface + * + * @notice This module enables pushing payments directly to the Payment Processor. + * + * @dev Extends {ERC20PaymentClientBase_v3} to integrate payment processing with + * bounty management, supporting dynamic additions, updates, and the locking + * of bounties. Utilizes roles for managing permissions and maintaining robust + * control over bounty operations. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface ILM_PC_PaymentRouter_v3 { // ----------------------------------------------------------------------------- // Errors /// @notice Given arrays' length mismatch. - error Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch(); + error Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch(); //-------------------------------------------------------------------------- // Mutating Functions /// @notice Adds a new Payment Order. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. /// @param recipient The address that will receive the payment. /// @param paymentToken The token in which to pay. @@ -30,6 +49,7 @@ interface ILM_PC_PaymentRouter_v2 { /// @notice Adds multiple Payment Orders in one batch. These PaymentOrders will share start, /// cliff and end timestamps. + /// @dev Function access controlled by authorizer. /// @dev Reverts if an argument invalid. The number of orders to be added in one batch is capped at 255. /// @param numOfOrders The number of orders to add. /// @param recipients The addresses that will receive the payments. diff --git a/src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v2.sol b/src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v3.sol similarity index 86% rename from src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v2.sol rename to src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v3.sol index 9d3f77308..2bc73bb6b 100644 --- a/src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v2.sol +++ b/src/modules/logicModule/interfaces/ILM_PC_RecurringPayments_v3.sol @@ -1,7 +1,27 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -interface ILM_PC_RecurringPayments_v2 { +/** + * @title Inverter Recurring Payment Manager Interface + * + * @notice Facilitates the creation, management, and execution of scheduled recurring + * payments within the Inverter Network, allowing for systematic and timed + * financial commitments or subscriptions. + * + * @dev Uses epochs to define the period of recurring payments and supports operations + * such as adding, removing, and triggering payments based on time cycles. + * Integrates with {ERC20PaymentClientBase_v3} for handling actual payment + * transactions. Note that it will use the token type stored in the FundingManager for the payments. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface ILM_PC_RecurringPayments_v3 { //-------------------------------------------------------------------------- // Structs @@ -127,6 +147,7 @@ interface ILM_PC_RecurringPayments_v2 { // Mutating Functions /// @notice Adds a recurring payment to the manager. + /// @dev Function access controlled by authorizer. /// @dev a new id is created for each Payment. /// @param amount Amount of tokens send to the recipient address. /// @param startEpoch Epoch in which the payment starts. Use getEpochFromTimestamp() or @@ -140,6 +161,7 @@ interface ILM_PC_RecurringPayments_v2 { ) external returns (uint id); /// @notice Removes a recurring Payment. + /// @dev Function access controlled by authorizer. /// @param prevId Id of the previous recurring payment in the payment list. /// @param id Id of the recurring payment that is to be removed. function removeRecurringPayment(uint prevId, uint id) external; diff --git a/src/modules/logicModule/interfaces/ILM_PC_Staking_v2.sol b/src/modules/logicModule/interfaces/ILM_PC_Staking_v3.sol similarity index 84% rename from src/modules/logicModule/interfaces/ILM_PC_Staking_v2.sol rename to src/modules/logicModule/interfaces/ILM_PC_Staking_v3.sol index 23d3c40fb..c037cea1d 100644 --- a/src/modules/logicModule/interfaces/ILM_PC_Staking_v2.sol +++ b/src/modules/logicModule/interfaces/ILM_PC_Staking_v3.sol @@ -1,17 +1,33 @@ pragma solidity ^0.8.0; -interface ILM_PC_Staking_v2 { +/** + * @title Inverter Staking Module Interface + * + * @notice Provides a mechanism for users to stake tokens and earn rewards. + * + * @dev Extends {ERC20PaymentClientBase_v3} and integrates with the Payment Processor + * to enable the distribution of rewards to stakers. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface ILM_PC_Staking_v3 { //-------------------------------------------------------------------------- // Errors /// @notice Given staking token address is invalid. - error Module__LM_PC_Staking_v2__InvalidStakingToken(); + error Module__LM_PC_Staking_v3__InvalidStakingToken(); /// @notice Given Duration is invalid. - error Module__LM_PC_Staking_v2__InvalidDuration(); + error Module__LM_PC_Staking_v3__InvalidDuration(); /// @notice The calculated Reward rate is too low to be used. - error Module__LM_PC_Staking_v2__InvalidRewardRate(); + error Module__LM_PC_Staking_v3__InvalidRewardRate(); //-------------------------------------------------------------------------- // Events @@ -107,22 +123,26 @@ interface ILM_PC_Staking_v2 { // Mutating Functions /// @notice Stake a specified amount of tokens to earn rewards. + /// @dev Function access controlled by authorizer. /// @dev Should tokens already be staked, then the sending address will collect the rewards up until this point. /// @dev Fee on transfer tokens are currently not supported. /// @param amount How much token should be staked. function stake(uint amount) external; /// @notice Unstake a specified amount of tokens and collect rewards. + /// @dev Function access controlled by authorizer. /// @dev Reaps the rewards collected up to this point for the msg.Sender(). /// @dev Fee on transfer tokens are currently not supported. /// @param amount How much token should be unstaked. function unstake(uint amount) external; /// @notice Collects the rewards that are earned up until now. + /// @dev Function access controlled by authorizer. /// @dev Reaps the rewards collected up to this point for the msg.Sender(). function claimRewards() external; /// @notice Sets the rewards that are to be distributed. + /// @dev Function access controlled by authorizer. /// @dev Equally distributes the reward amount over the given time period. /// @param amount How much token should be distributed. /// @param duration How much time it will take to distribute the token. diff --git a/src/modules/paymentProcessor/IPaymentProcessor_v1.sol b/src/modules/paymentProcessor/IPaymentProcessor_v1.sol index ea950bdff..9c1e2e0f6 100644 --- a/src/modules/paymentProcessor/IPaymentProcessor_v1.sol +++ b/src/modules/paymentProcessor/IPaymentProcessor_v1.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -70,21 +70,21 @@ interface IPaymentProcessor_v1 { //-------------------------------------------------------------------------- // Functions - /// @notice Processes all payments from an {IERC20PaymentClientBase_v2} instance. Please note: + /// @notice Processes all payments from an {IERC20PaymentClientBase_v3} instance. Please note: /// this function does not support callbacks on transfer of tokens. /// @dev It's up to the the implementation to keep up with what has been /// paid out or not. /// @dev Currently callback functions on token transfers are not supported and thus not checked. /// This could lead to a failed transaction which could influence the batched processing of /// payments. - /// @param client The {IERC20PaymentClientBase_v2} instance to process its to payments. - function processPayments(IERC20PaymentClientBase_v2 client) external; + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function processPayments(IERC20PaymentClientBase_v3 client) external; - /// @notice Cancels all unfinished payments from an {IERC20PaymentClientBase_v2} instance. + /// @notice Cancels all unfinished payments from an {IERC20PaymentClientBase_v3} instance. /// @dev It's up to the the implementation to keep up with what has been /// paid out or not. - /// @param client The {IERC20PaymentClientBase_v2} instance to process its to payments. - function cancelRunningPayments(IERC20PaymentClientBase_v2 client) + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function cancelRunningPayments(IERC20PaymentClientBase_v3 client) external; /// @notice Getter for the amount of tokens that could not be claimed. @@ -100,7 +100,7 @@ interface IPaymentProcessor_v1 { /// @notice claim every unclaimable amount that the paymentClient owes to the _msgSender and send it to a /// specified receiver. /// @dev This function should be callable if the _msgSender has unclaimedAmounts. - /// @param client The IERC20PaymentClientBase_v2 instance address that processes all claims from _msgSender. + /// @param client The IERC20PaymentClientBase_v3 instance address that processes all claims from _msgSender. /// @param token address of the payment token. /// @param receiver The address that will receive the previously unclaimable amount. function claimPreviouslyUnclaimable( @@ -110,9 +110,9 @@ interface IPaymentProcessor_v1 { ) external; /// @notice Function that checks if the given PaymentOrder was valid. - /// @param order The IERC20PaymentClientBase_v2 Order that needs to be checked. + /// @param order The IERC20PaymentClientBase_v3 Order that needs to be checked. /// @return valid Bool if the Payment Order is valid. function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external returns (bool); } diff --git a/src/modules/paymentProcessor/IPaymentProcessor_v2.sol b/src/modules/paymentProcessor/IPaymentProcessor_v2.sol index 35e4c0d6f..d55fbab73 100644 --- a/src/modules/paymentProcessor/IPaymentProcessor_v2.sol +++ b/src/modules/paymentProcessor/IPaymentProcessor_v2.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -70,21 +70,21 @@ interface IPaymentProcessor_v2 { //-------------------------------------------------------------------------- // Functions - /// @notice Processes all payments from an {IERC20PaymentClientBase_v2} instance. Please note: + /// @notice Processes all payments from an {IERC20PaymentClientBase_v3} instance. Please note: /// this function does not support callbacks on transfer of tokens. /// @dev It's up to the the implementation to keep up with what has been /// paid out or not. /// @dev Currently callback functions on token transfers are not supported and thus not checked. /// This could lead to a failed transaction which could influence the batched processing of /// payments. - /// @param client The {IERC20PaymentClientBase_v2} instance to process its to payments. - function processPayments(IERC20PaymentClientBase_v2 client) external; + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function processPayments(IERC20PaymentClientBase_v3 client) external; - /// @notice Cancels all unfinished payments from an {IERC20PaymentClientBase_v2} instance. + /// @notice Cancels all unfinished payments from an {IERC20PaymentClientBase_v3} instance. /// @dev It's up to the the implementation to keep up with what has been /// paid out or not. - /// @param client The {IERC20PaymentClientBase_v2} instance to process its to payments. - function cancelRunningPayments(IERC20PaymentClientBase_v2 client) + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function cancelRunningPayments(IERC20PaymentClientBase_v3 client) external; /// @notice Getter for the amount of tokens that could not be claimed. @@ -100,7 +100,7 @@ interface IPaymentProcessor_v2 { /// @notice claim every unclaimable amount that the paymentClient owes to the _msgSender and send it to a /// specified receiver. /// @dev This function should be callable if the _msgSender has unclaimedAmounts. - /// @param client The IERC20PaymentClientBase_v2 instance address that processes all claims from _msgSender. + /// @param client The IERC20PaymentClientBase_v3 instance address that processes all claims from _msgSender. /// @param token address of the payment token. /// @param receiver The address that will receive the previously unclaimable amount. function claimPreviouslyUnclaimable( @@ -110,9 +110,9 @@ interface IPaymentProcessor_v2 { ) external; /// @notice Function that checks if the given PaymentOrder was valid. - /// @param order The IERC20PaymentClientBase_v2 Order that needs to be checked. + /// @param order The IERC20PaymentClientBase_v3 Order that needs to be checked. /// @return valid Bool if the Payment Order is valid. function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external returns (bool); } diff --git a/src/modules/paymentProcessor/IPaymentProcessor_v3.sol b/src/modules/paymentProcessor/IPaymentProcessor_v3.sol new file mode 100644 index 000000000..2ee40b878 --- /dev/null +++ b/src/modules/paymentProcessor/IPaymentProcessor_v3.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +// Internal Interfaces +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +interface IPaymentProcessor_v3 { + //-------------------------------------------------------------------------- + // Errors + + /// @notice invalid caller. + error Module__PaymentProcessor__OnlyCallableByModule(); + + /// @notice a client can only execute on its own orders. + error Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); + + /// @notice the paymentReceiver is not owed any money by the paymentClient. + error Module__PaymentProcessor__NothingToClaim( + address paymentClient, address paymentReceiver + ); + + //-------------------------------------------------------------------------- + // Events + + /// @notice Emitted when a payment gets processed for execution. + /// @param paymentClient The payment client that originated the order. + /// @param recipient The address that will receive the payment. + /// @param paymentToken The address of the token that will be used for the payment. + /// @param amount The amount of tokens the payment consists of. + /// @param originChainId The id of the origin chain. + /// @param targetChainId The id of the target chain. + /// @param flags Flags that indicate additional data used by the payment + /// order. + /// @param data Array of additional data regarding the payment order. + event PaymentOrderProcessed( + address indexed paymentClient, + address indexed recipient, + address indexed paymentToken, + uint amount, + uint originChainId, + uint targetChainId, + bytes32 flags, + bytes32[] data + ); + + /// @notice Emitted when an amount of ERC20 tokens gets sent out of the contract. + /// @param recipient The address that will receive the payment. + /// @param token The token address in which the payment should have happened. + /// @param amount The amount of tokens the payment consists of. + event TokensReleased( + address indexed recipient, address indexed token, uint amount + ); + + /// @notice Emitted when a payment was unclaimable due to a token error. + /// @param paymentClient The payment client that originated the order. + /// @param paymentClient The token address in which the payment should have happened. + /// @param recipient The address that should have received the payment. + /// @param amount The amount of tokens that were unclaimable. + event UnclaimableAmountAdded( + address indexed paymentClient, + address indexed token, + address indexed recipient, + uint amount + ); + + //-------------------------------------------------------------------------- + // Functions + + /// @notice Processes all payments from an {IERC20PaymentClientBase_v3} instance. Please note: + /// this function does not support callbacks on transfer of tokens. + /// @dev It's up to the the implementation to keep up with what has been + /// paid out or not. + /// @dev Currently callback functions on token transfers are not supported and thus not checked. + /// This could lead to a failed transaction which could influence the batched processing of + /// payments. + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function processPayments(IERC20PaymentClientBase_v3 client) external; + + /// @notice Cancels all unfinished payments from an {IERC20PaymentClientBase_v3} instance. + /// @dev It's up to the the implementation to keep up with what has been + /// paid out or not. + /// @param client The {IERC20PaymentClientBase_v3} instance to process its to payments. + function cancelRunningPayments(IERC20PaymentClientBase_v3 client) + external; + + /// @notice Getter for the amount of tokens that could not be claimed. + /// @param client address of the payment client. + /// @param token address of the payment token. + /// @param paymentReceiver PaymentReceiver's address. + /// @return amount Amount of tokens that could not be claimed. + function unclaimable(address client, address token, address paymentReceiver) + external + view + returns (uint amount); + + /// @notice claim every unclaimable amount that the paymentClient owes to the _msgSender and send it to a + /// specified receiver. + /// @dev This function should be callable if the _msgSender has unclaimedAmounts. + /// @param client The IERC20PaymentClientBase_v3 instance address that processes all claims from _msgSender. + /// @param token address of the payment token. + /// @param receiver The address that will receive the previously unclaimable amount. + function claimPreviouslyUnclaimable( + address client, + address token, + address receiver + ) external; + + /// @notice Function that checks if the given PaymentOrder was valid. + /// @param order The IERC20PaymentClientBase_v3 Order that needs to be checked. + /// @return valid Bool if the Payment Order is valid. + function validPaymentOrder( + IERC20PaymentClientBase_v3.PaymentOrder memory order + ) external returns (bool); +} diff --git a/src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol b/src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol similarity index 88% rename from src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol rename to src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol index 0cbbdfb4b..120534f28 100644 --- a/src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol +++ b/src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol @@ -8,16 +8,16 @@ import {IEverclear} from import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; -import {IPP_Everclear_CrossChain_v1} from - "src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol"; -import {PP_CrossChainBase_v1} from "@pp/abstracts/PP_CrossChainBase_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; +import {IPP_Everclear_CrossChain_v2} from + "src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol"; +import {PP_CrossChainBase_v2} from "@pp/abstracts/PP_CrossChainBase_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; // Libraries import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; @@ -30,8 +30,8 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * and bridges the payments to the target chain through Everclear's infrastructure. * * @dev Inherits functionality from: - * - IPP_Everclear_CrossChain_v1: Implementation interface. - * - PP_CrossChainBase_v1: Cross-chain Payment Processor Base. + * - IPP_Everclear_CrossChain_v2: Implementation interface. + * - PP_CrossChainBase_v2: Cross-chain Payment Processor Base. * * Key features: * - Cross-chain payment processing @@ -54,9 +54,9 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * * @author 33Audits */ -contract PP_Everclear_CrossChain_v1 is - IPP_Everclear_CrossChain_v1, - PP_CrossChainBase_v1 +contract PP_Everclear_CrossChain_v2 is + IPP_Everclear_CrossChain_v2, + PP_CrossChainBase_v2 { //-------------------------------------------------------------------------- // Libraries @@ -70,10 +70,10 @@ contract PP_Everclear_CrossChain_v1 is public view virtual - override(PP_CrossChainBase_v1) + override(PP_CrossChainBase_v2) returns (bool) { - return interfaceId_ == type(IPP_Everclear_CrossChain_v1).interfaceId + return interfaceId_ == type(IPP_Everclear_CrossChain_v2).interfaceId || super.supportsInterface(interfaceId_); } // ------------------------------------------------------------------------- @@ -110,10 +110,10 @@ contract PP_Everclear_CrossChain_v1 is /// - address: everClearSpoke_: The Everclear spoke contract address for /// cross-chain message passing function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external virtual override(Module_v1) initializer { + ) external virtual override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); (address everClearSpoke_) = abi.decode(configData_, (address)); @@ -123,7 +123,7 @@ contract PP_Everclear_CrossChain_v1 is // ------------------------------------------------------------------------- // View Functions - /// @inheritdoc IPP_Everclear_CrossChain_v1 + /// @inheritdoc IPP_Everclear_CrossChain_v2 function getEverClearSpoke() external view @@ -133,7 +133,7 @@ contract PP_Everclear_CrossChain_v1 is return _everClearSpoke; } - /// @inheritdoc IPP_Everclear_CrossChain_v1 + /// @inheritdoc IPP_Everclear_CrossChain_v2 function getIntentByIntentId(bytes32 intentId_) external view @@ -146,22 +146,22 @@ contract PP_Everclear_CrossChain_v1 is // ------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client_) external virtual onlyModule validClient(address(client_)) { // Get the payment orders from the payment client. - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; (orders,,) = client_.collectPaymentOrders(); // Process each payment order. for (uint i = 0; i < orders.length; i++) { if (!_validPaymentOrder(orders[i])) { revert - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder(); } // Transfer the token for the order from the payment client into @@ -172,9 +172,9 @@ contract PP_Everclear_CrossChain_v1 is } } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) external virtual returns (bool valid_) { return _validPaymentOrder(order_); } @@ -186,7 +186,7 @@ contract PP_Everclear_CrossChain_v1 is /// @param order_ The payment order to validate. /// @return valid_ True if the payment order is valid, false otherwise. function _validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) internal virtual returns (bool valid_) { valid_ = _validPaymentReceiver(order_.recipient) && _validPaymentToken(order_.paymentToken) && _validTotal(order_.amount) @@ -204,8 +204,8 @@ contract PP_Everclear_CrossChain_v1 is /// @notice Execute the cross-chain bridge transfer. /// @param order_ The payment order containing transfer details. function _executeBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ - ) internal virtual override(PP_CrossChainBase_v1) { + IERC20PaymentClientBase_v3.PaymentOrder memory order_ + ) internal virtual override(PP_CrossChainBase_v2) { // Create a new intent. (bytes32 intentId, IEverclear.Intent memory intent_) = _createCrossChainIntent(order_); @@ -229,7 +229,7 @@ contract PP_Everclear_CrossChain_v1 is /// @param order_ The payment order containing transfer details. /// @param client_ The payment client address. function _processFailedBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) internal virtual { // Store failed transfer amount for the recipient. @@ -256,7 +256,7 @@ contract PP_Everclear_CrossChain_v1 is /// @param intentId_ The intent ID. /// @param intent_ The intent data. function _processSuccessfulBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_, bytes32 intentId_, IEverclear.Intent memory intent_ @@ -318,14 +318,14 @@ contract PP_Everclear_CrossChain_v1 is /// @param order_ The payment order details. /// @param client_ The payment client address. function _transferTokenAndApproveToBridge( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) internal virtual { IERC20(order_.paymentToken).safeTransferFrom( client_, address(this), order_.amount ); // Update the amount paid on the payment client side. - IERC20PaymentClientBase_v2(client_).amountPaid( + IERC20PaymentClientBase_v3(client_).amountPaid( order_.paymentToken, order_.amount ); @@ -338,7 +338,7 @@ contract PP_Everclear_CrossChain_v1 is /// @param order_ The payment order details. /// @return intentId_ ID of the created intent. function _createCrossChainIntent( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) internal virtual diff --git a/src/modules/paymentProcessor/PP_Queue_ManualExecution_v1.sol b/src/modules/paymentProcessor/PP_Queue_ManualExecution_v2.sol similarity index 53% rename from src/modules/paymentProcessor/PP_Queue_ManualExecution_v1.sol rename to src/modules/paymentProcessor/PP_Queue_ManualExecution_v2.sol index 11c086150..f4037ea72 100644 --- a/src/modules/paymentProcessor/PP_Queue_ManualExecution_v1.sol +++ b/src/modules/paymentProcessor/PP_Queue_ManualExecution_v2.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.23; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; -import {IPP_Queue_ManualExecution_v1} from - "@pp/interfaces/IPP_Queue_ManualExecution_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; -import {PP_Queue_v1} from "@pp/PP_Queue_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; +import {IPP_Queue_ManualExecution_v2} from + "@pp/interfaces/IPP_Queue_ManualExecution_v2.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; +import {PP_Queue_v2} from "@pp/PP_Queue_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -24,8 +24,8 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; * manual processing of payment orders in the queue. * * @dev This contract inherits from: - * - IPP_Queue_ManualExecution_v1: Implementation interface. - * - PP_Queue_v1: Queue based payment processor. + * - IPP_Queue_ManualExecution_v2: Implementation interface. + * - PP_Queue_v2: Queue based payment processor. * * Key features: * - FIFO queue management for payment orders. @@ -47,57 +47,37 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; * - FAILED: The order has failed due to the transfer failing * (blacklisted address). * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Queue Operators: * - Purpose: Queue operators are authorized to cancel payment * orders in the queue, and claim collateral for * failed payments. - * - How: The OrchestratorAdmin (or - * QUEUE_OPERATOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the queue operator role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getQueueOperatorRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Queue Operator Admin: - * - Purpose: Enables delegation of queue operator management - * to a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRoleAdmin() - * ) - * ); + * 1. Create a Queue operator role + * 2. Add access permission for the + * claimPreviouslyUnclaimableToTreasury() and + * cancelPaymentOrderThroughQueueId() + * functions to the Queue operator role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -contract PP_Queue_ManualExecution_v1 is - IPP_Queue_ManualExecution_v1, - PP_Queue_v1 +contract PP_Queue_ManualExecution_v2 is + IPP_Queue_ManualExecution_v2, + PP_Queue_v2 { // ------------------------------------------------------------------------- // ERC165 @@ -107,11 +87,11 @@ contract PP_Queue_ManualExecution_v1 is public view virtual - override(PP_Queue_v1) + override(PP_Queue_v2) returns (bool) { - return interfaceId_ == type(IPP_Queue_ManualExecution_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v2).interfaceId + return interfaceId_ == type(IPP_Queue_ManualExecution_v2).interfaceId + || interfaceId_ == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId_); } @@ -124,16 +104,16 @@ contract PP_Queue_ManualExecution_v1 is // ------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client_) external virtual - override(PP_Queue_v1, IPaymentProcessor_v2) + override(PP_Queue_v2, IPaymentProcessor_v3) clientIsValid(address(client_)) onlyModule { // Collect outstanding orders and their total token amount. - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; (orders,,) = client_.collectPaymentOrders(); @@ -144,8 +124,8 @@ contract PP_Queue_ManualExecution_v1 is } } - /// @inheritdoc IPP_Queue_ManualExecution_v1 - function executePaymentQueue(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPP_Queue_ManualExecution_v2 + function executePaymentQueue(IERC20PaymentClientBase_v3 client_) external virtual onlyModule diff --git a/src/modules/paymentProcessor/PP_Queue_v1.sol b/src/modules/paymentProcessor/PP_Queue_v2.sol similarity index 88% rename from src/modules/paymentProcessor/PP_Queue_v1.sol rename to src/modules/paymentProcessor/PP_Queue_v2.sol index 93056703d..523b69faf 100644 --- a/src/modules/paymentProcessor/PP_Queue_v1.sol +++ b/src/modules/paymentProcessor/PP_Queue_v2.sol @@ -8,13 +8,13 @@ import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; /** @@ -25,9 +25,9 @@ import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; * payments within the processPayments function. * * @dev This contract inherits from: - * - IPP_Queue_v1: Implementation interface. - * - IPaymentProcessor_v2: Payment processor interface. - * - Module_v1: Base module functionality. + * - IPP_Queue_v2: Implementation interface. + * - IPaymentProcessor_v3: Payment processor interface. + * - Module_v2: Base module functionality. * * Key features: * - FIFO queue management for payment orders. @@ -48,55 +48,35 @@ import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; * - FAILED: The order has failed due to the transfer failing * (blacklisted address). * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Queue Operators: * - Purpose: Queue operators are authorized to cancel payment * orders in the queue, and claim collateral for * failed payments. - * - How: The OrchestratorAdmin (or - * QUEUE_OPERATOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the queue operator role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getQueueOperatorRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Queue Operator Admin: - * - Purpose: Enables delegation of queue operator management - * to a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRoleAdmin() - * ) - * ); + * 1. Create a Queue operator role + * 2. Add access permission for the + * claimPreviouslyUnclaimableToTreasury() and + * cancelPaymentOrderThroughQueueId() + * functions to the Queue operator role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { +contract PP_Queue_v2 is IPP_Queue_v2, Module_v2 { // ------------------------------------------------------------------------- // Libraries @@ -111,27 +91,19 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { public view virtual - override(Module_v1) + override(Module_v2) returns (bool supported_) { - return interfaceId_ == type(IPP_Queue_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v2).interfaceId + return interfaceId_ == type(IPP_Queue_v2).interfaceId + || interfaceId_ == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId_); } // ------------------------------------------------------------------------- // Constants - /// @notice Role identifier for queue operations. - /// @dev This role cancels payments in the queue. - bytes32 internal constant QUEUE_OPERATOR_ROLE = "QUEUE_OPERATOR_ROLE"; - - /// @notice Role identifier for the admin authorized to assign the queue - /// operator role. - /// @dev This role should be set as the role admin for the - /// QUEUE_OPERATOR_ROLE within the Authorizer module. - bytes32 internal constant QUEUE_OPERATOR_ROLE_ADMIN = - "QUEUE_OPERATOR_ROLE_ADMIN"; + /// @notice Flag position in the flags byte. + uint8 internal constant FLAG_ORDER_ID = 0; /// @notice BPS value. uint internal constant BPS = 10_000; @@ -208,10 +180,10 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { /// - address: failedOrdersTreasury: The treasury address which /// receives collateral from failed orders. function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); // Decode config data. (address cancelledOrdersTreasury_, address failedOrdersTreasury_) = @@ -227,7 +199,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { //-------------------------------------------------------------------------- // Public View Functions - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getMaxOrdersPerExecution() external view @@ -237,7 +209,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { return _maxOrdersPerExecution; } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getCanceledOrdersTreasury() external view @@ -247,7 +219,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { treasury_ = _cancelledOrdersTreasury; } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getFailedOrdersTreasury() external view @@ -257,8 +229,8 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { treasury_ = _failedOrdersTreasury; } - /// @inheritdoc IPP_Queue_v1 - function getOrder(uint orderId_, IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPP_Queue_v2 + function getOrder(uint orderId_, IERC20PaymentClientBase_v3 client_) external view virtual @@ -270,7 +242,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { order_ = _orders[address(client_)][orderId_]; } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getOrderQueue(address client_) external view @@ -296,7 +268,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { queue_ = queue; } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getQueueHead(address client_) external view @@ -310,7 +282,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { head_ = _queue[client_].getNextId(LinkedIdList._SENTINEL); } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getQueueTail(address client_) external view @@ -320,7 +292,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { tail_ = _queue[client_].lastId(); } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function getQueueSizeForClient(address client_) external view @@ -330,27 +302,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { size_ = _queue[client_].length(); } - /// @inheritdoc IPP_Queue_v1 - function getQueueOperatorRole() - external - pure - virtual - returns (bytes32 role_) - { - return QUEUE_OPERATOR_ROLE; - } - - /// @inheritdoc IPP_Queue_v1 - function getQueueOperatorRoleAdmin() - external - pure - virtual - returns (bytes32 role_) - { - return QUEUE_OPERATOR_ROLE_ADMIN; - } - - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function unclaimable( address client_, address token_, @@ -360,9 +312,9 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { _unclaimableAmountsForRecipient[client_][token_][paymentReceiver_]; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) external view virtual returns (bool isValid_) { return _validPaymentOrder(order_); } @@ -370,11 +322,11 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { //-------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function setMaxOrdersPerExecution(uint maxOrdersPerExecution_) external virtual - onlyModuleRole(QUEUE_OPERATOR_ROLE) + permissioned { if (maxOrdersPerExecution_ == 0) { revert Module__PP_Queue_ZeroAmount(); @@ -382,32 +334,29 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { _maxOrdersPerExecution = maxOrdersPerExecution_; } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function setCanceledOrdersTreasury(address treasury_) external virtual - onlyOrchestratorAdmin + permissioned { _setCanceledOrdersTreasury(treasury_); } - /// @inheritdoc IPP_Queue_v1 - function setFailedOrdersTreasury(address treasury_) - external - onlyOrchestratorAdmin - { + /// @inheritdoc IPP_Queue_v2 + function setFailedOrdersTreasury(address treasury_) external permissioned { _setFailedOrdersTreasury(treasury_); } - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client_) external virtual clientIsValid(address(client_)) onlyModule { // Collect outstanding orders and their total token amount. - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; (orders,,) = client_.collectPaymentOrders(); @@ -420,8 +369,8 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { _executePaymentQueue(address(client_)); } - /// @inheritdoc IPaymentProcessor_v2 - function cancelRunningPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function cancelRunningPayments(IERC20PaymentClientBase_v3 client_) external view virtual @@ -430,7 +379,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { return; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function claimPreviouslyUnclaimable( address client_, address token_, @@ -443,12 +392,12 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { _claimPreviouslyUnclaimable(client_, token_, receiver_); } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function claimPreviouslyUnclaimableToTreasury( address client_, address token_, address receiver_ - ) external virtual onlyModuleRole(QUEUE_OPERATOR_ROLE) { + ) external virtual permissioned { if (unclaimable(client_, token_, receiver_) == 0) { revert Module__PaymentProcessor__NothingToClaim(client_, receiver_); } @@ -470,16 +419,11 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { ); } - /// @inheritdoc IPP_Queue_v1 + /// @inheritdoc IPP_Queue_v2 function cancelPaymentOrderThroughQueueId( uint orderId_, - IERC20PaymentClientBase_v2 client_ - ) - external - virtual - onlyModuleRole(QUEUE_OPERATOR_ROLE) - returns (bool success_) - { + IERC20PaymentClientBase_v3 client_ + ) external virtual permissioned returns (bool success_) { // Validate that the order exists for the given queue ID and client. if (!_orderExists(orderId_, client_)) { revert Module__PP_Queue_InvalidOrderId(address(client_), orderId_); @@ -713,7 +657,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { } // Update client accounting - IERC20PaymentClientBase_v2(client_).amountPaid(token_, amount_); + IERC20PaymentClientBase_v3(client_).amountPaid(token_, amount_); } /// @notice Executes all pending orders in the queue. @@ -745,7 +689,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { /// @param client_ The client paying for the order. /// @return queueId_ The ID of the added order. function _addPaymentOrderToQueue( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) internal virtual returns (uint queueId_) { if (!_validPaymentOrder(order_)) { @@ -997,7 +941,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { /// @param order_ The order to validate. /// @return valid_ True if the order is valid. function _validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) internal view virtual returns (bool valid_) { (uint orderId_, uint projectFee_) = _getOrderDetailsFromFlagsAndData(order_.flags, order_.data); @@ -1090,7 +1034,7 @@ contract PP_Queue_v1 is IPP_Queue_v1, Module_v1 { /// @param orderId_ ID of the order to check. /// @param client_ Address of the client. /// @return exists_ True if the order exists. - function _orderExists(uint orderId_, IERC20PaymentClientBase_v2 client_) + function _orderExists(uint orderId_, IERC20PaymentClientBase_v3 client_) internal view virtual diff --git a/src/modules/paymentProcessor/PP_Simple_v2.sol b/src/modules/paymentProcessor/PP_Simple_v3.sol similarity index 85% rename from src/modules/paymentProcessor/PP_Simple_v2.sol rename to src/modules/paymentProcessor/PP_Simple_v3.sol index 507ee5365..a2f2e6334 100644 --- a/src/modules/paymentProcessor/PP_Simple_v2.sol +++ b/src/modules/paymentProcessor/PP_Simple_v3.sol @@ -2,15 +2,15 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import { - IPaymentProcessor_v2, - IERC20PaymentClientBase_v2 -} from "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; + IPaymentProcessor_v3, + IERC20PaymentClientBase_v3 +} from "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // Internal Dependencies -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -22,9 +22,9 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * @title Inverter Simple Payment Processor * * @notice Manages ERC20 payment processing for modules within the Inverter Network - * that are compliant with the {IERC20PaymentClientBase_v2} interface. + * that are compliant with the {IERC20PaymentClientBase_v3} interface. * - * @dev Inherits {Module_v1} and implements {IPaymentProcessor_v2} to handle payment + * @dev Inherits {Module_v2} and implements {IPaymentProcessor_v3} to handle payment * orders from registered modules, ensuring only eligible modules can initiate * payments. Utilizes {SafeERC20} for secure token transfers. * @@ -32,18 +32,20 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { +contract PP_Simple_v3 is Module_v2, IPaymentProcessor_v3 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId == type(IPaymentProcessor_v2).interfaceId + return interfaceId == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -61,7 +63,7 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { } /// @dev Checks that the client is calling for itself. - modifier validClient(IERC20PaymentClientBase_v2 client) { + modifier validClient(IERC20PaymentClientBase_v3 client) { if (_msgSender() != address(client)) { revert Module__PaymentProcessor__CannotCallOnOtherClientsOrders(); } @@ -82,30 +84,30 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { //-------------------------------------------------------------------------- // Initializer - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory /*configData*/ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); } //-------------------------------------------------------------------------- - // IPaymentProcessor_v2 Functions + // IPaymentProcessor_v3 Functions - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client) external onlyModule validClient(client) { // Collect outstanding orders and their total token amount. - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; (orders,,) = client.collectPaymentOrders(); - // Transfer tokens from {IERC20PaymentClientBase_v2} to order recipients. + // Transfer tokens from {IERC20PaymentClientBase_v3} to order recipients. address recipient; uint amount; uint len = orders.length; @@ -155,8 +157,8 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { } } - /// @inheritdoc IPaymentProcessor_v2 - function cancelRunningPayments(IERC20PaymentClientBase_v2 client) + /// @inheritdoc IPaymentProcessor_v3 + function cancelRunningPayments(IERC20PaymentClientBase_v3 client) external view onlyModule @@ -166,7 +168,7 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { return; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function unclaimable(address client, address token, address paymentReceiver) public view @@ -175,7 +177,7 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { return unclaimableAmountsForRecipient[client][token][paymentReceiver]; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function claimPreviouslyUnclaimable( address client, address token, @@ -190,9 +192,9 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { _claimPreviouslyUnclaimable(client, token, receiver); } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external returns (bool) { return _validPaymentReceiver(order.recipient) && _validTotal(order.amount) && _validPaymentToken(order.paymentToken) @@ -220,7 +222,7 @@ contract PP_Simple_v2 is Module_v1, IPaymentProcessor_v2 { delete unclaimableAmountsForRecipient[client][token][sender]; // Make sure to let paymentClient know that amount doesnt have to be stored anymore - IERC20PaymentClientBase_v2(client).amountPaid(token, amount); + IERC20PaymentClientBase_v3(client).amountPaid(token, amount); // Call has to succeed otherwise no state change IERC20(token).safeTransferFrom(client, paymentReceiver, amount); diff --git a/src/modules/paymentProcessor/PP_Streaming_v2.sol b/src/modules/paymentProcessor/PP_Streaming_v3.sol similarity index 94% rename from src/modules/paymentProcessor/PP_Streaming_v2.sol rename to src/modules/paymentProcessor/PP_Streaming_v3.sol index 04ea2b739..1a0657a85 100644 --- a/src/modules/paymentProcessor/PP_Streaming_v2.sol +++ b/src/modules/paymentProcessor/PP_Streaming_v3.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.23; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import { - IPP_Streaming_v2, - IPaymentProcessor_v2, - IERC20PaymentClientBase_v2 -} from "@pp/interfaces/IPP_Streaming_v2.sol"; + IPP_Streaming_v3, + IPaymentProcessor_v3, + IERC20PaymentClientBase_v3 +} from "@pp/interfaces/IPP_Streaming_v3.sol"; // Internal Dependencies -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -52,19 +52,21 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v3.0.0 + * * @author Inverter Network */ -contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { +contract PP_Streaming_v3 is Module_v2, IPP_Streaming_v3 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId == type(IPP_Streaming_v2).interfaceId - || interfaceId == type(IPaymentProcessor_v2).interfaceId + return interfaceId == type(IPP_Streaming_v3).interfaceId + || interfaceId == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId); } @@ -145,12 +147,12 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { //-------------------------------------------------------------------------- // External Functions - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); (uint _defaultStart, uint _defaultCliff, uint _defaultEnd) = @@ -159,7 +161,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _setDefaultTimes(_defaultStart, _defaultCliff, _defaultEnd); } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function claimAll(address client) external { if (activeStreams[client][_msgSender()].length == 0) { revert Module__PaymentProcessor__NothingToClaim( @@ -170,7 +172,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _claimAll(client, _msgSender()); } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function claimPreviouslyUnclaimable( address client, address token, @@ -185,7 +187,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _claimPreviouslyUnclaimable(client, token, receiver); } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function claimForSpecificStream(address client, uint streamId) external { if ( activeStreams[client][_msgSender()].length == 0 @@ -206,8 +208,8 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _claimForSpecificStream(client, _msgSender(), streamId); } - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client) external onlyModule validClient(address(client)) @@ -215,7 +217,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { // We check if there are any new paymentOrders, without processing them if (client.paymentOrders().length > 0) { // Collect outstanding orders and their total token amount. - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; address[] memory tokens; uint[] memory totalAmounts; (orders, tokens, totalAmounts) = client.collectPaymentOrders(); @@ -235,7 +237,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { for (uint i; i < numOrders;) { if (!validPaymentOrder(orders[i])) { revert - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder(); } @@ -245,7 +247,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { numStreams[address(client)][orders[i].recipient] + 1 ); - emit IPaymentProcessor_v2.PaymentOrderProcessed( + emit IPaymentProcessor_v3.PaymentOrderProcessed( address(client), orders[i].recipient, orders[i].paymentToken, @@ -263,8 +265,8 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { } } - /// @inheritdoc IPaymentProcessor_v2 - function cancelRunningPayments(IERC20PaymentClientBase_v2 client) + /// @inheritdoc IPaymentProcessor_v3 + function cancelRunningPayments(IERC20PaymentClientBase_v3 client) external onlyModule validClient(address(client)) @@ -272,11 +274,11 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _cancelRunningOrders(address(client)); } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function removeAllPaymentReceiverPayments( address client, address paymentReceiver - ) external onlyOrchestratorAdmin { + ) external permissioned { if ( _findAddressInActiveStreams(client, paymentReceiver) == type(uint).max @@ -288,12 +290,12 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { _removePayment(client, paymentReceiver); } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function removePaymentForSpecificStream( address client, address paymentReceiver, uint streamId - ) external onlyOrchestratorAdmin { + ) external permissioned { // First, we give the streamed funds from this specific streamId to the beneficiary _claimForSpecificStream(client, paymentReceiver, streamId); @@ -311,7 +313,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { //-------------------------------------------------------------------------- // Public Functions - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function isActivePaymentReceiver(address client, address paymentReceiver) public view @@ -320,7 +322,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return activeStreams[client][paymentReceiver].length > 0; } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function startForSpecificStream( address client, address paymentReceiver, @@ -329,7 +331,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return streams[client][paymentReceiver][streamId]._start; } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function cliffForSpecificStream( address client, address paymentReceiver, @@ -338,7 +340,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return streams[client][paymentReceiver][streamId]._cliff; } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function endForSpecificStream( address client, address paymentReceiver, @@ -347,7 +349,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return streams[client][paymentReceiver][streamId]._end; } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function releasedForSpecificStream( address client, address paymentReceiver, @@ -356,7 +358,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return streams[client][paymentReceiver][streamId]._released; } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function streamedAmountForSpecificStream( address client, address paymentReceiver, @@ -368,7 +370,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { ); } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function releasableForSpecificStream( address client, address paymentReceiver, @@ -379,7 +381,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { ) - releasedForSpecificStream(client, paymentReceiver, streamId); } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function unclaimable(address client, address token, address paymentReceiver) public view @@ -397,7 +399,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { } } - /// @inheritdoc IPP_Streaming_v2 + /// @inheritdoc IPP_Streaming_v3 function viewAllPaymentOrders(address client, address paymentReceiver) external view @@ -423,9 +425,9 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { return paymentReceiverStreams; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) public returns (bool) { (uint start, uint cliff, uint end) = _getStreamingDetails(order.flags, order.data); @@ -436,9 +438,10 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { && _validOriginAndTargetChain(order.originChainId, order.targetChainId); } + /// @inheritdoc IPP_Streaming_v3 function setStreamingDefaults(uint newStart_, uint newCliff_, uint newEnd_) external - onlyOrchestratorAdmin + permissioned { _setDefaultTimes(newStart_, newCliff_, newEnd_); } @@ -568,7 +571,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { } /// @notice Deletes all payments related to a `paymentReceiver` & leaves currently streaming tokens in the - /// {IERC20PaymentClientBase_v2}. + /// {IERC20PaymentClientBase_v3}. /// @dev This function calls `_removePayment` which goes through all the payment orders for a `paymentReceiver`. /// For the payment orders that are completely streamed, their details are deleted in the /// `_claimForSpecificStream` function and for others it is deleted in the `_removePayment` function only, @@ -632,7 +635,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { if (remainingReleasable > 0) { // Let PaymentClient know that the amount is not needed to be stored anymore - IERC20PaymentClientBase_v2(client).amountPaid( + IERC20PaymentClientBase_v3(client).amountPaid( _token, remainingReleasable ); } @@ -701,7 +704,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { /// @param _streamId ID of the new stream of the a particular paymentReceiver being added. function _addPayment( address _client, - IERC20PaymentClientBase_v2.PaymentOrder memory _order, + IERC20PaymentClientBase_v3.PaymentOrder memory _order, uint _streamId ) internal { ++numStreams[_client][_order.recipient]; @@ -799,7 +802,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { emit TokensReleased(paymentReceiver, _token, amount); // Make sure to let paymentClient know that amount doesnt have to be stored anymore - IERC20PaymentClientBase_v2(client).amountPaid( + IERC20PaymentClientBase_v3(client).amountPaid( address(_token), amount ); } else { @@ -865,7 +868,7 @@ contract PP_Streaming_v2 is Module_v1, IPP_Streaming_v2 { delete unclaimableStreams[client][token][sender]; // Make sure to let paymentClient know that amount doesnt have to be stored anymore - IERC20PaymentClientBase_v2(client).amountPaid(address(token), amount); + IERC20PaymentClientBase_v3(client).amountPaid(address(token), amount); // Call has to succeed otherwise no state change IERC20(token).safeTransferFrom(client, paymentReceiver, amount); diff --git a/src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.sol b/src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.sol similarity index 87% rename from src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.sol rename to src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.sol index 4897ca06d..03be28cc6 100644 --- a/src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.23; // Internal -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; -import {IPP_CrossChainBase_v1} from "@pp/interfaces/IPP_CrossChainBase_v1.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; +import {IPP_CrossChainBase_v2} from "@pp/interfaces/IPP_CrossChainBase_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -23,9 +23,9 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * processing functionality. * * @dev Inherits functionality from: - * - IPP_CrossChainBase_v1: Implementation interface. - * - IPaymentProcessor_v2: Payment processor interface. - * - Module_v1: Base module functionality. + * - IPP_CrossChainBase_v2: Implementation interface. + * - IPaymentProcessor_v3: Payment processor interface. + * - Module_v2: Base module functionality. * * Key features: * - Bridge Data Management @@ -52,13 +52,13 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version 1.0.0 + * @custom:version 2.0.0 * * @custom:standard-version 1.0.0 * * @author 33Audits */ -abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { +abstract contract PP_CrossChainBase_v2 is IPP_CrossChainBase_v2, Module_v2 { //-------------------------------------------------------------------------- // Libraries @@ -72,11 +72,11 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { - return interfaceId_ == type(IPP_CrossChainBase_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v2).interfaceId + return interfaceId_ == type(IPP_CrossChainBase_v2).interfaceId + || interfaceId_ == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId_); } @@ -123,7 +123,7 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { // ------------------------------------------------------------------------- // View Functions - /// @inheritdoc IPP_CrossChainBase_v1 + /// @inheritdoc IPP_CrossChainBase_v2 function getBridgeDataByPaymentId(uint paymentId_) public view @@ -133,12 +133,12 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { return _paymentIdToBridgeData[paymentId_]; } - /// @inheritdoc IPP_CrossChainBase_v1 + /// @inheritdoc IPP_CrossChainBase_v2 function getPaymentId() external view virtual returns (uint paymentId_) { return _paymentId; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function unclaimable( address client_, address token_, @@ -151,7 +151,7 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { //-------------------------------------------------------------------------- // Public Mutating Functions - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function claimPreviouslyUnclaimable( address client_, address token_, @@ -166,8 +166,8 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { _claimPreviouslyUnclaimable(client_, token_, receiver_); } - /// @inheritdoc IPaymentProcessor_v2 - function cancelRunningPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function cancelRunningPayments(IERC20PaymentClientBase_v3 client_) external virtual onlyModule @@ -184,7 +184,7 @@ abstract contract PP_CrossChainBase_v1 is IPP_CrossChainBase_v1, Module_v1 { /// @dev Override this function to implement specific bridge logic. /// @param order_ The payment order containing all necessary transfer details. function _executeBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) internal virtual; //-------------------------------------------------------------------------- diff --git a/src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v2.sol similarity index 93% rename from src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v1.sol rename to src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v2.sol index ce4f9dee8..b956835f0 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_CrossChainBase_v2.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; // Internal -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; /** * @title Cross-chain Payment Processor Base Contract. @@ -11,8 +11,8 @@ import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; * processing functionality. * * @dev Inherits functionality from: - * - IPP_CrossChainBase_v1: Implementation interface. - * - IPaymentProcessor_v2: Payment processor interface. + * - IPP_CrossChainBase_v2: Implementation interface. + * - IPaymentProcessor_v3: Payment processor interface. * - Module_v1: Base module functionality. * * Key features: @@ -38,13 +38,13 @@ import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version 1.0.0 + * @custom:version 2.0.0 * * @custom:standard-version 1.0.0 * * @author 33Audits */ -interface IPP_CrossChainBase_v1 is IPaymentProcessor_v2 { +interface IPP_CrossChainBase_v2 is IPaymentProcessor_v3 { // Events //-------------------------------------------------------------------------- diff --git a/src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v2.sol similarity index 92% rename from src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v1.sol rename to src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v2.sol index fdbb61cd2..41e403b63 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Everclear_CrossChain_v2.sol @@ -19,8 +19,8 @@ import {IEverclear} from * and bridges the payments to the target chain through Everclear's infrastructure. * * @dev Inherits functionality from: - * - IPP_Everclear_CrossChain_v1: Implementation interface. - * - PP_CrossChainBase_v1: Cross-chain Payment Processor Base. + * - IPP_Everclear_CrossChain_v2: Implementation interface. + * - PP_CrossChainBase_v2: Cross-chain Payment Processor Base. * * Key features: * - Cross-chain payment processing @@ -37,13 +37,13 @@ import {IEverclear} from * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author 33Audits */ -interface IPP_Everclear_CrossChain_v1 { +interface IPP_Everclear_CrossChain_v2 { //-------------------------------------------------------------------------- // View Functions diff --git a/src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v2.sol similarity index 54% rename from src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v1.sol rename to src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v2.sol index 8b7bb2f29..5dd085a80 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Queue_ManualExecution_v2.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; /** * @title Manual Execution Queue Based Payment Processor @@ -14,8 +14,8 @@ import {IERC20PaymentClientBase_v2} from * manual processing of payment orders in the queue. * * @dev This contract inherits from: - * - IPP_Queue_ManualExecution_v1: Implementation interface. - * - PP_Queue_v1: Queue based payment processor. + * - IPP_Queue_ManualExecution_v2: Implementation interface. + * - PP_Queue_v2: Queue based payment processor. * * Key features: * - FIFO queue management for payment orders. @@ -37,55 +37,35 @@ import {IERC20PaymentClientBase_v2} from * - FAILED: The order has failed due to the transfer failing * (blacklisted address). * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Queue Operators: * - Purpose: Queue operators are authorized to cancel payment * orders in the queue, and claim collateral for * failed payments. - * - How: The OrchestratorAdmin (or - * QUEUE_OPERATOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the queue operator role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getQueueOperatorRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Queue Operator Admin: - * - Purpose: Enables delegation of queue operator management - * to a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRoleAdmin() - * ) - * ); + * 1. Create a Queue operator role + * 2. Add access permission for the + * claimPreviouslyUnclaimableToTreasury() and + * cancelPaymentOrderThroughQueueId() + * functions to the Queue operator role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -interface IPP_Queue_ManualExecution_v1 is IPP_Queue_v1 { +interface IPP_Queue_ManualExecution_v2 is IPP_Queue_v2 { // ------------------------------------------------------------------------- // Functions @@ -93,6 +73,6 @@ interface IPP_Queue_ManualExecution_v1 is IPP_Queue_v1 { /// the payment client has funds to cover the orders. /// @dev If the payment client does not have enough funds, the function /// will stop executing orders. - function executePaymentQueue(IERC20PaymentClientBase_v2 paymentClient) + function executePaymentQueue(IERC20PaymentClientBase_v3 paymentClient) external; } diff --git a/src/modules/paymentProcessor/interfaces/IPP_Queue_v1.sol b/src/modules/paymentProcessor/interfaces/IPP_Queue_v2.sol similarity index 81% rename from src/modules/paymentProcessor/interfaces/IPP_Queue_v1.sol rename to src/modules/paymentProcessor/interfaces/IPP_Queue_v2.sol index 07c522708..92c9603b6 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Queue_v1.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Queue_v2.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; /** * @title Queue Based Payment Processor @@ -14,9 +14,9 @@ import {IERC20PaymentClientBase_v2} from * payments within the processPayments function. * * @dev This contract inherits from: - * - IPP_Queue_v1: Implementation interface. - * - IPaymentProcessor_v2: Payment processor interface. - * - Module_v1: Base module functionality. + * - IPP_Queue_v2: Implementation interface. + * - IPaymentProcessor_v3: Payment processor interface. + * - Module_v2: Base module functionality. * * Key features: * - FIFO queue management for payment orders. @@ -37,55 +37,35 @@ import {IERC20PaymentClientBase_v2} from * - FAILED: The order has failed due to the transfer failing * (blacklisted address). * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup OPTIONAL setup steps for enhanced administration: * * 1. Configure Queue Operators: * - Purpose: Queue operators are authorized to cancel payment * orders in the queue, and claim collateral for * failed payments. - * - How: The OrchestratorAdmin (or - * QUEUE_OPERATOR_ROLE_ADMIN if configured) must: - * 1. Retrieve the queue operator role identifier. - * 2. Grant the role to desired addresses. - * - Example: module.grantModuleRole( - * module.getQueueOperatorRole(), - * operatorAddress - * ); - * - * OPTIONAL setup steps for enhanced administration: - * - * 1. Custom Queue Operator Admin: - * - Purpose: Enables delegation of queue operator management - * to a dedicated admin role instead of relying on - * the OrchestratorAdmin. This allows for more - * granular access control and operational - * flexibility. * - How: The OrchestratorAdmin must: - * 1. Generate the role IDs for both roles. - * 2. Transfer admin rights through the Authorizer. - * - Example: authorizer.transferAdminRole( - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRole() - * ), - * authorizer.generateRoleId( - * moduleAddress, - * module.getQueueOperatorRoleAdmin() - * ) - * ); + * 1. Create a Queue operator role + * 2. Add access permission for the + * claimPreviouslyUnclaimableToTreasury() and + * cancelPaymentOrderThroughQueueId() + * functions to the Queue operator role. + * 3. Grant the role to desired addresses. + * - Example: authorizer.createRole(); + * authorizer.addAccessPermission(); + * authorizer.grantRole(); * * @custom:security-contact security@inverter.network * In case of any concerns or findings, please refer to * our Security Policy at security.inverter.network or * email us directly! * - * @custom:version v1.0.0 + * @custom:version v2.0.0 * * @custom:standard-version v1.0.0 * * @author Zealynx Security */ -interface IPP_Queue_v1 is IPaymentProcessor_v2 { +interface IPP_Queue_v2 is IPaymentProcessor_v3 { // ------------------------------------------------------------------------- // Type Declarations @@ -107,7 +87,7 @@ interface IPP_Queue_v1 is IPaymentProcessor_v2 { /// @param timestamp_ Creation timestamp of the payment order. /// @param client_ Address of the client paying for the order. struct QueuedOrder { - IERC20PaymentClientBase_v2.PaymentOrder order_; + IERC20PaymentClientBase_v3.PaymentOrder order_; RedemptionState state_; uint orderId_; uint timestamp_; @@ -285,7 +265,7 @@ interface IPP_Queue_v1 is IPaymentProcessor_v2 { /// @param orderId_ The ID of the payment order. /// @param client_ The client associated with the order. /// @return order_ The payment order data. - function getOrder(uint orderId_, IERC20PaymentClientBase_v2 client_) + function getOrder(uint orderId_, IERC20PaymentClientBase_v3 client_) external view returns (QueuedOrder memory order_); @@ -316,23 +296,12 @@ interface IPP_Queue_v1 is IPaymentProcessor_v2 { view returns (uint size_); - /// @notice Gets the role identifier for the queue operator role. - /// @return role_ The queue operator role identifier. - function getQueueOperatorRole() external pure returns (bytes32 role_); - - /// @notice Gets the role identifier for queue operator admin. - /// @return role_ The queue operator role admin identifier. - function getQueueOperatorRoleAdmin() - external - pure - returns (bytes32 role_); - /// @notice Cancels a payment order by its queue ID and sends the funds /// from the cancelled order to the canceled orders treasury. + /// @dev Function only callable by claim contributors /// @dev This function can only be excuted if the payment client /// has enough collateral to transfer the funds to the - /// canceled orders treasury. Additionally, the caller - /// must have the queue operator role. If the transfer fails, + /// canceled orders treasury. If the transfer fails, /// then the amount is added to the unclaimable amounts for /// the canceled orders treasury. /// @param orderId_ The ID of the order to cancel. @@ -340,7 +309,7 @@ interface IPP_Queue_v1 is IPaymentProcessor_v2 { /// @return success_ True if cancellation was successful. function cancelPaymentOrderThroughQueueId( uint orderId_, - IERC20PaymentClientBase_v2 client_ + IERC20PaymentClientBase_v3 client_ ) external returns (bool success_); /// @notice Get the treasury address for canceled orders. @@ -359,17 +328,19 @@ interface IPP_Queue_v1 is IPaymentProcessor_v2 { /// @notice Set the treasury address which receives the collateral /// of canceled orders. + /// @dev Function only callable by claim contributors /// @param treasury_ The treasury address for canceled orders. function setCanceledOrdersTreasury(address treasury_) external; /// @notice Set the treasury address which receives the collateral /// of failed orders. + /// @dev Function only callable by claim contributors /// @param treasury_ The treasury address for failed orders. function setFailedOrdersTreasury(address treasury_) external; /// @notice Claim previously unclaimable amounts from a receiver to /// the failed orders treasury. - /// @dev This function is only callable by the queue operator. + /// @dev Function only callable by claim contributors /// @param client_ The client address. /// @param token_ The token address. /// @param receiver_ The receiver address. diff --git a/src/modules/paymentProcessor/interfaces/IPP_Streaming_v2.sol b/src/modules/paymentProcessor/interfaces/IPP_Streaming_v3.sol similarity index 81% rename from src/modules/paymentProcessor/interfaces/IPP_Streaming_v2.sol rename to src/modules/paymentProcessor/interfaces/IPP_Streaming_v3.sol index 05a74259a..af7860fe8 100644 --- a/src/modules/paymentProcessor/interfaces/IPP_Streaming_v2.sol +++ b/src/modules/paymentProcessor/interfaces/IPP_Streaming_v3.sol @@ -2,15 +2,49 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -interface IPP_Streaming_v2 is IPaymentProcessor_v2 { +/** + * @title Inverter Linear Streaming Payment Processor + * + * @notice Manages continuous and linear streaming payment streams within the Inverter + * Network, allowing multiple concurrent streams per recipient. Provides tools + * to claim streamed amounts and manage payment schedules dynamically. + * + * @dev Supports complex payment interactions including streaming based on time for + * multiple clients and recipients, integrated with error handling for + * payments and managing active streaming schedules and their cancellations. + * + * DISCLAIMER: Known Limitations + * This contract has a known limitation that could potentially lead to a Denial of Service + * (DoS) attack. The `activePaymentReceivers` array for a client can grow unbounded, + * which may cause gas-intensive operations to exceed block gas limits under certain + * conditions. This could temporarily render some functions inoperable. + * + * While this limitation does not directly risk user funds, it may temporarily prevent + * users from staking, unstaking, or claiming rewards if exploited. The development + * team is aware of this issue and may implement a fix in future upgrades if necessary. + * + * CAUTION: Workflow deployers should be especially careful when using this payment processor + * with contracts that allow users to directly initiate payment streams. Such setups + * are particularly vulnerable to this limitation and could more easily trigger a DoS condition. + * + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v3.0.0 + * + * @author Inverter Network + */ +interface IPP_Streaming_v3 is IPaymentProcessor_v3 { //-------------------------------------------------------------------------- // Structs @@ -144,7 +178,7 @@ interface IPP_Streaming_v2 is IPaymentProcessor_v2 { // Functions /// @notice claim everything that the paymentClient owes to the `_msgSender` till the current timestamp. /// @dev This function should be callable if the `_msgSender` is an `activePaymentReceiver`. - /// @param client The {IERC20PaymentClientBase_v2} instance address that processes all claims from `_msgSender`. + /// @param client The {IERC20PaymentClientBase_v3} instance address that processes all claims from `_msgSender`. function claimAll(address client) external; /// @notice claim the total amount up til block.timestamp from the client for a payment order with id = streamId @@ -152,18 +186,19 @@ interface IPP_Streaming_v2 is IPaymentProcessor_v2 { /// @dev If for a specific streamId, the tokens could not be transferred for some reason, it will added /// to the unclaimableAmounts of the `paymentReceiver`, and the amount would no longer hold /// any co-relation with he specific streamId of the `paymentReceiver`. - /// @param client The {IERC20PaymentClientBase_v2} instance address that processes the `streamId` claim + /// @param client The {IERC20PaymentClientBase_v3} instance address that processes the `streamId` claim /// from `_msgSender`. /// @param streamId The ID of the streaming payment order for which claim is being made. function claimForSpecificStream(address client, uint streamId) external; /// @notice Deletes all payments related to a paymentReceiver & leaves currently streaming tokens in the - /// {IERC20PaymentClientBase_v2}. + /// {IERC20PaymentClientBase_v3}. + /// @dev Function access controlled by authorizer. /// @dev this function calls `_removePayment` which goes through all the payment orders for a `paymentReceiver`. /// For the payment orders that are completely streamed, their details are deleted in the /// `_claimForSpecificStrea` function and for others it is deleted in the `_removePayment` function only, /// leaving the currently streaming tokens as balance of the paymentClient itself. - /// @param client The {IERC20PaymentClientBase_v2} instance address from which we will remove the payments. + /// @param client The {IERC20PaymentClientBase_v3} instance address from which we will remove the payments. /// @param paymentReceiver PaymentReceiver's address. function removeAllPaymentReceiverPayments( address client, @@ -171,10 +206,11 @@ interface IPP_Streaming_v2 is IPaymentProcessor_v2 { ) external; /// @notice Deletes a specific payment with id = streamId for a paymentReceiver & leaves currently streaming - /// tokens in the {IERC20PaymentClientBase_v2}. + /// tokens in the {IERC20PaymentClientBase_v3}. + /// @dev Function access controlled by authorizer. /// @dev the detail of the wallet that is being removed is either deleted in the `_claimForSpecificStream` /// or later down in this function itself depending on the timestamp of when this function was called. - /// @param client The {IERC20PaymentClientBase_v2} instance address from which we will remove the payment. + /// @param client The {IERC20PaymentClientBase_v3} instance address from which we will remove the payment. /// @param paymentReceiver Address of the paymentReceiver whose payment order is to be removed. /// @param streamId The ID of the paymentReceiver's payment order which is to be removed. function removePaymentForSpecificStream( @@ -183,6 +219,15 @@ interface IPP_Streaming_v2 is IPaymentProcessor_v2 { uint streamId ) external; + /// @notice Sets the default start time, cliff and end times for new + /// payment orders. + /// @dev Function access controlled by authorizer. + /// @param newStart_ The new default start time. + /// @param newCliff_ The new default cliff duration. + /// @param newEnd_ The new default end time. + function setStreamingDefaults(uint newStart_, uint newCliff_, uint newEnd_) + external; + /// @notice Getter for the start timestamp of a particular payment order with id = streamId associated /// with a particular paymentReceiver. /// @param client address of the payment client. diff --git a/src/orchestrator/Orchestrator_v1.sol b/src/orchestrator/Orchestrator_v2.sol similarity index 50% rename from src/orchestrator/Orchestrator_v1.sol rename to src/orchestrator/Orchestrator_v2.sol index 381d486c4..79e1414b1 100644 --- a/src/orchestrator/Orchestrator_v1.sol +++ b/src/orchestrator/Orchestrator_v2.sol @@ -3,13 +3,19 @@ pragma solidity 0.8.23; // Internal Interfaces import { - IOrchestrator_v1, - IFundingManager_v1, - IPaymentProcessor_v2, - IAuthorizer_v1, + IOrchestrator_v2, IGovernor_v1 -} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +} from "src/orchestrator/interfaces/IOrchestrator_v2.sol"; + +import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; +import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {IPaymentProcessor_v1} from "@pp/IPaymentProcessor_v1.sol"; +import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; + import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IModuleManagerBase_v1} from "src/orchestrator/interfaces/IModuleManagerBase_v1.sol"; @@ -40,8 +46,8 @@ import {ERC165Checker} from "@oz/utils/introspection/ERC165Checker.sol"; * * The token being accepted for funding is non-changeable and set during * initialization. Authorization is performed via calling a non-changeable - * {IAuthorizer_v1} instance. Payments, initiated by modules, are processed - * via a non-changeable {IPaymentProcessor_v2} instance. + * {IAuthorizer_v2} instance. Payments, initiated by modules, are processed + * via a non-changeable {IPaymentProcessor_v3} instance. * * Each orchestrator has a unique id set during initialization. * @@ -49,9 +55,11 @@ import {ERC165Checker} from "@oz/utils/introspection/ERC165Checker.sol"; * In case of any concerns or findings, please refer to our Security Policy * at security.inverter.network or email us directly! * + * @custom:version v2.0.0 + * * @author Inverter Network */ -contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { +contract Orchestrator_v2 is IOrchestrator_v2, ModuleManagerBase_v1 { /// @inheritdoc ERC165Upgradeable function supportsInterface(bytes4 interfaceId) public @@ -60,21 +68,15 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { override(ModuleManagerBase_v1) returns (bool) { - return interfaceId == type(IOrchestrator_v1).interfaceId + return interfaceId == type(IOrchestrator_v2).interfaceId || super.supportsInterface(interfaceId); } //-------------------------------------------------------------------------- // Modifiers - /// @dev Modifier to guarantee function is only callable by the admin of the workflow - /// address. - modifier onlyOrchestratorAdmin() { - bytes32 adminRole = authorizer.getAdminRole(); - - if (!authorizer.hasRole(adminRole, _msgSender())) { - revert Orchestrator__CallerNotAuthorized(adminRole, _msgSender()); - } + modifier permissioned() { + _checkAuthorization(_msgSender(), _msgData()); _; } @@ -100,20 +102,20 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { //-------------------------------------------------------------------------- // Storage - /// @inheritdoc IOrchestrator_v1 - uint public override(IOrchestrator_v1) orchestratorId; + /// @inheritdoc IOrchestrator_v2 + uint public override(IOrchestrator_v2) orchestratorId; - /// @inheritdoc IOrchestrator_v1 - IFundingManager_v1 public override(IOrchestrator_v1) fundingManager; + /// @inheritdoc IOrchestrator_v2 + IFundingManager_v1 public override(IOrchestrator_v2) fundingManager; - /// @inheritdoc IOrchestrator_v1 - IAuthorizer_v1 public override(IOrchestrator_v1) authorizer; + /// @inheritdoc IOrchestrator_v2 + IAuthorizer_v2 public override(IOrchestrator_v2) authorizer; - /// @inheritdoc IOrchestrator_v1 - IPaymentProcessor_v2 public override(IOrchestrator_v1) paymentProcessor; + /// @inheritdoc IOrchestrator_v2 + IPaymentProcessor_v3 public override(IOrchestrator_v2) paymentProcessor; - /// @inheritdoc IOrchestrator_v1 - IGovernor_v1 public override(IOrchestrator_v1) governor; + /// @inheritdoc IOrchestrator_v2 + IGovernor_v1 public override(IOrchestrator_v2) governor; /// @dev Storage gap for future upgrades. uint[50] private __gap; @@ -127,16 +129,16 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { _disableInitializers(); } - /// @inheritdoc IOrchestrator_v1 + /// @inheritdoc IOrchestrator_v2 function init( uint orchestratorId_, address moduleFactory_, address[] calldata modules, IFundingManager_v1 fundingManager_, - IAuthorizer_v1 authorizer_, - IPaymentProcessor_v2 paymentProcessor_, + IAuthorizer_v2 authorizer_, + IPaymentProcessor_v3 paymentProcessor_, IGovernor_v1 governor_ - ) external override(IOrchestrator_v1) initializer { + ) external override(IOrchestrator_v2) initializer { // Initialize upstream contracts. __ModuleManager_init(moduleFactory_, modules); @@ -152,21 +154,34 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { // Add necessary modules. // Note to not use the public addModule function as the factory // is (most probably) not authorized. + { + bytes4[] memory privilegedInterfaceIds = new bytes4[](1); + privilegedInterfaceIds[0] = type(IFundingManager_v1).interfaceId; - _enforcePrivilegedModuleInterfaceCheck( - address(fundingManager_), type(IFundingManager_v1).interfaceId - ); - __ModuleManager_addModule(address(fundingManager_)); + _enforcePrivilegedModuleInterfaceCheck( + address(fundingManager_), privilegedInterfaceIds + ); + __ModuleManager_addModule(address(fundingManager_)); - _enforcePrivilegedModuleInterfaceCheck( - address(authorizer_), type(IAuthorizer_v1).interfaceId - ); - __ModuleManager_addModule(address(authorizer_)); + privilegedInterfaceIds = new bytes4[](2); + privilegedInterfaceIds[0] = type(IAuthorizer_v1).interfaceId; + privilegedInterfaceIds[1] = type(IAuthorizer_v2).interfaceId; - _enforcePrivilegedModuleInterfaceCheck( - address(paymentProcessor_), type(IPaymentProcessor_v2).interfaceId - ); - __ModuleManager_addModule(address(paymentProcessor_)); + _enforcePrivilegedModuleInterfaceCheck( + address(authorizer_), privilegedInterfaceIds + ); + __ModuleManager_addModule(address(authorizer_)); + + privilegedInterfaceIds = new bytes4[](3); + privilegedInterfaceIds[0] = type(IPaymentProcessor_v1).interfaceId; + privilegedInterfaceIds[1] = type(IPaymentProcessor_v2).interfaceId; + privilegedInterfaceIds[2] = type(IPaymentProcessor_v3).interfaceId; + + _enforcePrivilegedModuleInterfaceCheck( + address(paymentProcessor_), privilegedInterfaceIds + ); + __ModuleManager_addModule(address(paymentProcessor_)); + } emit OrchestratorInitialized( orchestratorId_, @@ -181,30 +196,36 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { //-------------------------------------------------------------------------- // onlyOrchestratorAdmin Functions - /// @inheritdoc IOrchestrator_v1 - function initiateSetAuthorizerWithTimelock(IAuthorizer_v1 newAuthorizer) + /// @inheritdoc IOrchestrator_v2 + function initiateSetAuthorizerWithTimelock(address newAuthorizerAddress) external - onlyOrchestratorAdmin + permissioned { - address newAuthorizerAddress = address(newAuthorizer); + bytes4[] memory privilegedInterfaceIds = new bytes4[](2); + privilegedInterfaceIds[0] = type(IAuthorizer_v1).interfaceId; + privilegedInterfaceIds[1] = type(IAuthorizer_v2).interfaceId; + _enforcePrivilegedModuleInterfaceCheck( - newAuthorizerAddress, type(IAuthorizer_v1).interfaceId + newAuthorizerAddress, privilegedInterfaceIds ); _initiateAddModuleWithTimelock(newAuthorizerAddress); _initiateRemoveModuleWithTimelock(address(authorizer)); } - /// @inheritdoc IOrchestrator_v1 - function executeSetAuthorizer(IAuthorizer_v1 newAuthorizer) + /// @inheritdoc IOrchestrator_v2 + function executeSetAuthorizer(address newAuthorizerAddress) external - onlyOrchestratorAdmin - updatingModuleAlreadyStarted(address(newAuthorizer)) - timelockExpired(address(newAuthorizer)) + permissioned + updatingModuleAlreadyStarted(newAuthorizerAddress) + timelockExpired(newAuthorizerAddress) { - address newAuthorizerAddress = address(newAuthorizer); + bytes4[] memory privilegedInterfaceIds = new bytes4[](2); + privilegedInterfaceIds[0] = type(IAuthorizer_v1).interfaceId; + privilegedInterfaceIds[1] = type(IAuthorizer_v2).interfaceId; + _enforcePrivilegedModuleInterfaceCheck( - newAuthorizerAddress, type(IAuthorizer_v1).interfaceId + newAuthorizerAddress, privilegedInterfaceIds ); _executeRemoveModule(address(authorizer)); @@ -214,33 +235,38 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { // Use _commitAddModule directly as it doesnt need the authorization of the by now none existing Authorizer _commitAddModule(newAuthorizerAddress); - authorizer = newAuthorizer; + authorizer = IAuthorizer_v2(newAuthorizerAddress); emit AuthorizerUpdated(newAuthorizerAddress); } - /// @inheritdoc IOrchestrator_v1 - function cancelAuthorizerUpdate(IAuthorizer_v1 authorizer_) + /// @inheritdoc IOrchestrator_v2 + function cancelAuthorizerUpdate(address authorizer_) external - onlyOrchestratorAdmin + permissioned { _cancelModuleUpdate(address(authorizer)); - _cancelModuleUpdate(address(authorizer_)); + _cancelModuleUpdate(authorizer_); } - /// @inheritdoc IOrchestrator_v1 + /// @inheritdoc IOrchestrator_v2 function initiateSetFundingManagerWithTimelock( - IFundingManager_v1 newFundingManager - ) external onlyOrchestratorAdmin { - address newFundingManagerAddress = address(newFundingManager); + address newFundingManagerAddress + ) external permissioned { + bytes4[] memory privilegedInterfaceIds = new bytes4[](1); + privilegedInterfaceIds[0] = type(IFundingManager_v1).interfaceId; _enforcePrivilegedModuleInterfaceCheck( - newFundingManagerAddress, type(IFundingManager_v1).interfaceId + newFundingManagerAddress, privilegedInterfaceIds ); - if (fundingManager.token() != newFundingManager.token()) { + // Check if the token is the same as the current one + if ( + fundingManager.token() + != IFundingManager_v1(newFundingManagerAddress).token() + ) { revert Orchestrator__MismatchedTokenForFundingManager( address(fundingManager.token()), - address(newFundingManager.token()) + address(IFundingManager_v1(newFundingManagerAddress).token()) ); } else { _initiateAddModuleWithTimelock(newFundingManagerAddress); @@ -248,107 +274,122 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { } } - /// @inheritdoc IOrchestrator_v1 - function executeSetFundingManager(IFundingManager_v1 newFundingManager) + /// @inheritdoc IOrchestrator_v2 + function executeSetFundingManager(address newFundingManagerAddress) external - onlyOrchestratorAdmin + permissioned { - address newFundingManagerAddress = address(newFundingManager); + bytes4[] memory privilegedInterfaceIds = new bytes4[](1); + privilegedInterfaceIds[0] = type(IFundingManager_v1).interfaceId; _enforcePrivilegedModuleInterfaceCheck( - newFundingManagerAddress, type(IFundingManager_v1).interfaceId + newFundingManagerAddress, privilegedInterfaceIds ); _executeRemoveModule(address(fundingManager)); _executeAddModule(newFundingManagerAddress); - fundingManager = newFundingManager; + fundingManager = IFundingManager_v1(newFundingManagerAddress); emit FundingManagerUpdated(newFundingManagerAddress); } - /// @inheritdoc IOrchestrator_v1 - function cancelFundingManagerUpdate(IFundingManager_v1 fundingManager_) + /// @inheritdoc IOrchestrator_v2 + function cancelFundingManagerUpdate(address fundingManager_) external - onlyOrchestratorAdmin + permissioned { _cancelModuleUpdate(address(fundingManager)); - _cancelModuleUpdate(address(fundingManager_)); + _cancelModuleUpdate(fundingManager_); } - /// @inheritdoc IOrchestrator_v1 + /// @inheritdoc IOrchestrator_v2 function initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2 newPaymentProcessor - ) external onlyOrchestratorAdmin { - address newPaymentProcessorAddress = address(newPaymentProcessor); + address newPaymentProcessorAddress + ) external permissioned { + bytes4[] memory privilegedInterfaceIds = new bytes4[](3); + privilegedInterfaceIds[0] = type(IPaymentProcessor_v1).interfaceId; + privilegedInterfaceIds[1] = type(IPaymentProcessor_v2).interfaceId; + privilegedInterfaceIds[2] = type(IPaymentProcessor_v3).interfaceId; _enforcePrivilegedModuleInterfaceCheck( - newPaymentProcessorAddress, type(IPaymentProcessor_v2).interfaceId + newPaymentProcessorAddress, privilegedInterfaceIds ); _initiateAddModuleWithTimelock(newPaymentProcessorAddress); _initiateRemoveModuleWithTimelock(address(paymentProcessor)); } - /// @inheritdoc IOrchestrator_v1 - function executeSetPaymentProcessor( - IPaymentProcessor_v2 newPaymentProcessor - ) external onlyOrchestratorAdmin { - address newPaymentProcessorAddress = address(newPaymentProcessor); + /// @inheritdoc IOrchestrator_v2 + function executeSetPaymentProcessor(address newPaymentProcessorAddress) + external + permissioned + { + bytes4[] memory privilegedInterfaceIds = new bytes4[](3); + privilegedInterfaceIds[0] = type(IPaymentProcessor_v1).interfaceId; + privilegedInterfaceIds[1] = type(IPaymentProcessor_v2).interfaceId; + privilegedInterfaceIds[2] = type(IPaymentProcessor_v3).interfaceId; _enforcePrivilegedModuleInterfaceCheck( - newPaymentProcessorAddress, type(IPaymentProcessor_v2).interfaceId + newPaymentProcessorAddress, privilegedInterfaceIds ); _executeRemoveModule(address(paymentProcessor)); _executeAddModule(newPaymentProcessorAddress); - paymentProcessor = newPaymentProcessor; + + paymentProcessor = IPaymentProcessor_v3(newPaymentProcessorAddress); emit PaymentProcessorUpdated(newPaymentProcessorAddress); } - /// @inheritdoc IOrchestrator_v1 - function cancelPaymentProcessorUpdate( - IPaymentProcessor_v2 paymentProcessor_ - ) external onlyOrchestratorAdmin { + /// @inheritdoc IOrchestrator_v2 + function cancelPaymentProcessorUpdate(address paymentProcessor_) + external + permissioned + { _cancelModuleUpdate(address(paymentProcessor)); - _cancelModuleUpdate(address(paymentProcessor_)); + _cancelModuleUpdate(paymentProcessor_); } - /// @inheritdoc IOrchestrator_v1 - function cancelModuleUpdate(address module_) external { + /// @inheritdoc IOrchestrator_v2 + function initiateAddModuleWithTimelock(address module_) + external + permissioned + { _enforceNonPrivilegedModuleInterfaceCheck(module_); - _cancelModuleUpdate(module_); + _initiateAddModuleWithTimelock(module_); } - /// @inheritdoc IOrchestrator_v1 - function initiateAddModuleWithTimelock(address module_) external { + /// @inheritdoc IOrchestrator_v2 + function executeAddModule(address module_) external permissioned { _enforceNonPrivilegedModuleInterfaceCheck(module_); - _initiateAddModuleWithTimelock(module_); + _executeAddModule(module_); } - /// @inheritdoc IOrchestrator_v1 + /// @inheritdoc IOrchestrator_v2 function initiateRemoveModuleWithTimelock(address module_) external onlyLogicModules(module_) + permissioned { _initiateRemoveModuleWithTimelock(module_); } - /// @inheritdoc IOrchestrator_v1 - function executeAddModule(address module_) external { - _enforceNonPrivilegedModuleInterfaceCheck(module_); - _executeAddModule(module_); - } - - /// @inheritdoc IOrchestrator_v1 + /// @inheritdoc IOrchestrator_v2 function executeRemoveModule(address module_) external onlyLogicModules(module_) + permissioned { _executeRemoveModule(module_); } + /// @inheritdoc IOrchestrator_v2 + function cancelModuleUpdate(address module_) external permissioned { + _enforceNonPrivilegedModuleInterfaceCheck(module_); + _cancelModuleUpdate(module_); + } + //-------------------------------------------------------------------------- // Upstream Function Implementations - /// @dev Only addresses authorized via the {IAuthorizer_v1} instance can manage + /// @dev Only addresses authorized via the {IAuthorizer_v2} instance can manage /// modules. function __ModuleManager_isAuthorized(address who) internal @@ -359,26 +400,66 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { return authorizer.hasRole(authorizer.getAdminRole(), who); } - //-------------------------------------------------------------------------- + // ======================================================================== // Internal Functions + // ------------------------------------------------------------------------ + // Internal - Authorization + + /// @notice Checks if the caller can call the function that implements the locked modifier. + /// @param caller_ The address of the caller. + /// @param data_ The data of the call. + function _checkAuthorization(address caller_, bytes calldata data_) + internal + view + { + // If caller cannot call the function, revert. + if ( + !authorizer.hasPermission(caller_, address(this), bytes4(data_[0:4])) + ) { + revert Orchestrator__NotPermissioned(); + } + } + + // ------------------------------------------------------------------------ + // Internal - Enforce Module Interface Check + /// @notice Enforces that the address is in fact a Module of the required type. /// @dev The function reverts if the given address is not a module of the required type. /// @param _contractAddr The address to be checked. /// @param _privilegedInterfaceId The required interface id. function _enforcePrivilegedModuleInterfaceCheck( address _contractAddr, - bytes4 _privilegedInterfaceId + bytes4[] memory _privilegedInterfaceId ) internal view { - bytes4 moduleInterfaceId = type(IModule_v1).interfaceId; + // If address is not a module, revert if ( - !ERC165Checker.supportsInterface(_contractAddr, moduleInterfaceId) - || !ERC165Checker.supportsInterface( - _contractAddr, _privilegedInterfaceId + !ERC165Checker.supportsInterface( + _contractAddr, type(IModule_v1).interfaceId + ) + && !ERC165Checker.supportsInterface( + _contractAddr, type(IModule_v2).interfaceId ) ) { revert Orchestrator__InvalidModuleType(_contractAddr); } + + bool isInterfaceSupported; + // Check if the module supports the required interface + for (uint i = 0; i < _privilegedInterfaceId.length; i++) { + if ( + ERC165Checker.supportsInterface( + _contractAddr, _privilegedInterfaceId[i] + ) + ) { + isInterfaceSupported = true; + break; + } + } + + if (!isInterfaceSupported) { + revert Orchestrator__InvalidModuleType(_contractAddr); + } } /// @dev Internal function to enforce that the given module is not a privileged module. @@ -387,19 +468,37 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { internal view { - bytes4 moduleInterfaceId = type(IModule_v1).interfaceId; if ( - !ERC165Checker.supportsInterface(_contractAddr, moduleInterfaceId) + // If the given address is not a module + // If the given address is any of the following interfaces + ( + !ERC165Checker.supportsInterface( + _contractAddr, type(IModule_v1).interfaceId + ) + && !ERC165Checker.supportsInterface( + _contractAddr, type(IModule_v2).interfaceId + ) + ) || ERC165Checker.supportsInterface( _contractAddr, type(IAuthorizer_v1).interfaceId ) + || ERC165Checker.supportsInterface( + _contractAddr, type(IAuthorizer_v2).interfaceId + ) || ERC165Checker.supportsInterface( _contractAddr, type(IFundingManager_v1).interfaceId ) + || ERC165Checker.supportsInterface( + _contractAddr, type(IPaymentProcessor_v1).interfaceId + ) || ERC165Checker.supportsInterface( _contractAddr, type(IPaymentProcessor_v2).interfaceId ) + || ERC165Checker.supportsInterface( + _contractAddr, type(IPaymentProcessor_v3).interfaceId + ) ) { + // Then revert revert Orchestrator__InvalidModuleType(_contractAddr); } } @@ -409,7 +508,7 @@ contract Orchestrator_v1 is IOrchestrator_v1, ModuleManagerBase_v1 { /// @inheritdoc IModuleManagerBase_v1 /// @dev Because we want to expose the `isTrustedForwarder` function from the {ERC2771Context} Contract in the - /// {IOrchestrator_v1} we have to override it here as the original openzeppelin version doesnt contain an + /// {IOrchestrator_v2} we have to override it here as the original openzeppelin version doesnt contain an /// interface that we could use to expose it. function isTrustedForwarder(address forwarder) public diff --git a/src/orchestrator/abstracts/ModuleManagerBase_v1.sol b/src/orchestrator/abstracts/ModuleManagerBase_v1.sol index 4cb6cd988..5196a638c 100644 --- a/src/orchestrator/abstracts/ModuleManagerBase_v1.sol +++ b/src/orchestrator/abstracts/ModuleManagerBase_v1.sol @@ -6,6 +6,7 @@ import {IModuleManagerBase_v1} from "src/orchestrator/interfaces/IModuleManagerBase_v1.sol"; import {IModuleFactory_v1} from "src/factories/OrchestratorFactory_v1.sol"; import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; // External Dependencies import {ERC2771ContextUpgradeable} from @@ -15,6 +16,8 @@ import { ERC165Upgradeable } from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; +// External Libraries +import {ERC165Checker} from "@oz/utils/introspection/ERC165Checker.sol"; /** * @title Inverter ModuleManagerBase * @@ -32,6 +35,7 @@ import { * @author Inverter Network * Adapted from Gnosis Safe */ + abstract contract ModuleManagerBase_v1 is IModuleManagerBase_v1, Initializable, @@ -53,14 +57,6 @@ abstract contract ModuleManagerBase_v1 is //-------------------------------------------------------------------------- // Modifiers - /// @dev Modifier to guarantee function is only callable by authorized address. - modifier __ModuleManager_onlyAuthorized() { - if (!__ModuleManager_isAuthorized(_msgSender())) { - revert ModuleManagerBase__CallerNotAuthorized(); - } - _; - } - /// @dev Modifier to guarantee that the caller is a module. modifier onlyModule() { if (!isModule(_msgSender())) { @@ -120,7 +116,7 @@ abstract contract ModuleManagerBase_v1 is //-------------------------------------------------------------------------- // Constants - /// @dev Marks the maximum amount of Modules a {Orchestrator_v1} can have to avoid out-of-gas risk. + /// @dev Marks the maximum amount of Modules a {Orchestrator_v2} can have to avoid out-of-gas risk. uint private constant MAX_MODULE_AMOUNT = 128; /// @dev Timelock used between initiating adding or removing a module and executing it. uint public constant MODULE_UPDATE_TIMELOCK = 72 hours; @@ -134,7 +130,7 @@ abstract contract ModuleManagerBase_v1 is /// @dev List of modules. address[] private _modules; - /// @dev Mapping to keep track of whether a module is used in the {Orchestrator_v1} + /// @dev Mapping to keep track of whether a module is used in the {Orchestrator_v2} /// address => isModule. mapping(address => bool) private _isModule; @@ -232,55 +228,47 @@ abstract contract ModuleManagerBase_v1 is } //-------------------------------------------------------------------------- - // onlyOrchestratorAdmin Functions + // Internal Functions /// @notice Cancels an initiated update for a module. - /// @dev Only callable by authorized address. /// @dev Fails if module update has not been initiated. /// @param module The module address to remove. function _cancelModuleUpdate(address module) internal - __ModuleManager_onlyAuthorized updatingModuleAlreadyStarted(module) { moduleAddressToTimelock[module].timelockActive = false; emit ModuleUpdateCanceled(module); } - /// @notice Initiates adding of a module to the {Orchestrator_v1} on a timelock. - /// @dev Only callable by authorized address. + /// @notice Initiates adding of a module to the {Orchestrator_v2} on a timelock. /// @dev Fails of adding module exeeds max modules limit. /// @dev Fails if address invalid or address already added as module. /// @param module The module address to add. function _initiateAddModuleWithTimelock(address module) internal - __ModuleManager_onlyAuthorized isNotModule(module) validModule(module) { _startModuleUpdateTimelock(module); } - /// @notice Initiates removing of a module from the {Orchestrator_v1} on a timelock. - /// @dev Only callable by authorized address. + /// @notice Initiates removing of a module from the {Orchestrator_v2} on a timelock. /// @dev Fails if address not added as module. /// @param module The module address to remove. function _initiateRemoveModuleWithTimelock(address module) internal - __ModuleManager_onlyAuthorized isModule_(module) { _startModuleUpdateTimelock(module); } - /// @notice Executes adding of a module to the {Orchestrator_v1}. - /// @dev Only callable by authorized address. + /// @notice Executes adding of a module to the {Orchestrator_v2}. /// @dev Fails if adding of module has not been initiated. /// @dev Fails if timelock has not been expired yet. /// @param module The module address to add. function _executeAddModule(address module) internal - __ModuleManager_onlyAuthorized updatingModuleAlreadyStarted(module) timelockExpired(module) { @@ -290,14 +278,12 @@ abstract contract ModuleManagerBase_v1 is __ModuleManager_addModule(module); } - /// @notice Executes removing of a module from the {Orchestrator_v1}. - /// @dev Only callable by authorized address. + /// @notice Executes removing of a module from the {Orchestrator_v2}. /// @dev Fails if removing of module has not been initiated. /// @dev Fails if timelock has not been expired yet. /// @param module The module address to remove. function _executeRemoveModule(address module) internal - __ModuleManager_onlyAuthorized updatingModuleAlreadyStarted(module) timelockExpired(module) { @@ -307,9 +293,6 @@ abstract contract ModuleManagerBase_v1 is _commitRemoveModule(module); } - //-------------------------------------------------------------------------- - // Private Functions - /// @dev Expects `module` to be valid module address. /// @dev Expects `module` to not be enabled module. /// @param module The module address to add. @@ -357,10 +340,16 @@ abstract contract ModuleManagerBase_v1 is /// @param module The module address to check. function _ensureValidModule(address module) private view { if ( + // If the address is not implementing either IModule_v1 or IModule_v2 revert module.code.length == 0 || module == address(0) || module == address(this) - || !ERC165Upgradeable(module).supportsInterface( - type(IModule_v1).interfaceId + || ( + !ERC165Checker.supportsInterface( + module, type(IModule_v1).interfaceId + ) + && !ERC165Checker.supportsInterface( + module, type(IModule_v2).interfaceId + ) ) ) { revert ModuleManagerBase__InvalidModuleAddress(); @@ -393,12 +382,13 @@ abstract contract ModuleManagerBase_v1 is ); } + //-------------------------------------------------------------------------- // IERC2771ContextUpgradeable - // @dev Because we want to expose the isTrustedForwarder function from the ERC2771ContextUpgradeable - // Contract in the IOrchestrator_v1 we have to override it here as the original openzeppelin version - // doesnt contain a interface that we could use to expose it. /// @inheritdoc IModuleManagerBase_v1 + // @dev Because we want to expose the isTrustedForwarder function from the ERC2771ContextUpgradeable + // Contract in the IOrchestrator_v2 we have to override it here as the original openzeppelin version + // doesnt contain a interface that we could use to expose it. function isTrustedForwarder(address forwarder) public view diff --git a/src/orchestrator/interfaces/IModuleManagerBase_v1.sol b/src/orchestrator/interfaces/IModuleManagerBase_v1.sol index 50daf9a02..ffbe2a9f4 100644 --- a/src/orchestrator/interfaces/IModuleManagerBase_v1.sol +++ b/src/orchestrator/interfaces/IModuleManagerBase_v1.sol @@ -19,9 +19,6 @@ interface IModuleManagerBase_v1 is IERC2771Context { //-------------------------------------------------------------------------- // Errors - /// @notice Function is only callable by authorized address. - error ModuleManagerBase__CallerNotAuthorized(); - /// @notice Function is only callable by modules. error ModuleManagerBase__OnlyCallableByModule(); diff --git a/src/orchestrator/interfaces/IOrchestrator_v1.sol b/src/orchestrator/interfaces/IOrchestrator_v2.sol similarity index 67% rename from src/orchestrator/interfaces/IOrchestrator_v1.sol rename to src/orchestrator/interfaces/IOrchestrator_v2.sol index 19a56e397..390bbf02b 100644 --- a/src/orchestrator/interfaces/IOrchestrator_v1.sol +++ b/src/orchestrator/interfaces/IOrchestrator_v2.sol @@ -6,23 +6,47 @@ import {IModuleManagerBase_v1} from "src/orchestrator/interfaces/IModuleManagerBase_v1.sol"; import {IGovernor_v1} from "src/external/governance/interfaces/IGovernor_v1.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -interface IOrchestrator_v1 is IModuleManagerBase_v1 { +/** + * @title Inverter Orchestrator Interface + * + * @dev This Contract is the center and connecting block of all Modules in a + * Inverter Network Workflow. It contains references to the essential contracts + * that make up a workflow. By inheriting the ModuleManager it allows for managing + * which modules make up the workflow. + * + * An orchestrator is composed of a funding mechanism + * and a set of modules. + * + * The token being accepted for funding is non-changeable and set during + * initialization. Authorization is performed via calling a non-changeable + * {IAuthorizer_v2} instance. Payments, initiated by modules, are processed + * via a non-changeable {IPaymentProcessor_v3} instance. + * + * Each orchestrator has a unique id set during initialization. + * + * @custom:security-contact security@inverter.network + * In case of any concerns or findings, please refer to our Security Policy + * at security.inverter.network or email us directly! + * + * @custom:version v2.0.0 + * + * @author Inverter Network + */ +interface IOrchestrator_v2 is IModuleManagerBase_v1 { //-------------------------------------------------------------------------- // Errors /// @notice Function is only callable by authorized caller. - /// @param role The role of the caller. - /// @param caller The caller address. - error Orchestrator__CallerNotAuthorized(bytes32 role, address caller); + error Orchestrator__NotPermissioned(); - /// @notice The given module is not used in the {Orchestrator_v1}. + /// @notice The given module is not used in the {Orchestrator_v2}. /// @param module The module address. error Orchestrator__InvalidModuleType(address module); @@ -33,7 +57,7 @@ interface IOrchestrator_v1 is IModuleManagerBase_v1 { address currentToken, address newToken ); - /// @notice The given module is not used in the {Orchestrator_v1}. + /// @notice The given module is not used in the {Orchestrator_v2}. error Orchestrator__DependencyInjection__ModuleNotUsedInOrchestrator(); /// @notice The Authorizer can not be removed through this function. @@ -60,12 +84,12 @@ interface IOrchestrator_v1 is IModuleManagerBase_v1 { /// @param _address The new address. event PaymentProcessorUpdated(address indexed _address); - /// @notice {Orchestrator_v1} has been initialized with the corresponding modules. - /// @param orchestratorId_ The id of the {Orchestrator_v1}. + /// @notice {Orchestrator_v2} has been initialized with the corresponding modules. + /// @param orchestratorId_ The id of the {Orchestrator_v2}. /// @param fundingManager The address of the funding manager module. /// @param authorizer The address of the authorizer module. /// @param paymentProcessor The address of the payment processor module. - /// @param modules The addresses of the other modules used in the {Orchestrator_v1}. + /// @param modules The addresses of the other modules used in the {Orchestrator_v2}. /// @param governor The address of the {Governor_v1} contract used to reference protocol level interactions. event OrchestratorInitialized( uint indexed orchestratorId_, @@ -77,12 +101,37 @@ interface IOrchestrator_v1 is IModuleManagerBase_v1 { ); //-------------------------------------------------------------------------- - // Functions + // Getter Functions + + /// @notice Returns the {Orchestrator_v2}'s id. + /// @dev Unique id set by the {OrchestratorFactory_v1} during initialization. + /// @return The {Orchestrator_v2}'s id. + function orchestratorId() external view returns (uint); + + /// @notice The {IFundingManager_v1} implementation used to hold and distribute Funds. + /// @return The {IFundingManager_v1} implementation. + function fundingManager() external view returns (IFundingManager_v1); + + /// @notice The {IAuthorizer_v2} implementation used to authorize addresses. + /// @return The {IAuthorizer_v2} implementation. + function authorizer() external view returns (IAuthorizer_v2); + + /// @notice The {IPaymentProcessor_v3} implementation used to process module + /// payments. + /// @return The {IPaymentProcessor_v3} implementation. + function paymentProcessor() external view returns (IPaymentProcessor_v3); + + /// @notice The {IGovernor_v1} implementation used for protocol level interactions. + /// @return The {IGovernor_v1} implementation. + function governor() external view returns (IGovernor_v1); + + //-------------------------------------------------------------------------- + // Initialization /// @notice Initialization function. - /// @param orchestratorId The id of the {Orchestrator_v1}. + /// @param orchestratorId The id of the {Orchestrator_v2}. /// @param moduleFactory_ The address of the module factory. - /// @param modules The addresses of the modules used in the {Orchestrator_v1}. + /// @param modules The addresses of the modules used in the {Orchestrator_v2}. /// @param fundingManager The address of the funding manager module. /// @param authorizer The address of the authorizer module. /// @param paymentProcessor The address of the payment processor module. @@ -92,127 +141,100 @@ interface IOrchestrator_v1 is IModuleManagerBase_v1 { address moduleFactory_, address[] calldata modules, IFundingManager_v1 fundingManager, - IAuthorizer_v1 authorizer, - IPaymentProcessor_v2 paymentProcessor, + IAuthorizer_v2 authorizer, + IPaymentProcessor_v3 paymentProcessor, IGovernor_v1 governor ) external; + //-------------------------------------------------------------------------- + // Mutating Functions + /// @notice Initiates replacing the current authorizer with `_authorizer` on a timelock. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param authorizer_ The address of the new authorizer module. - function initiateSetAuthorizerWithTimelock(IAuthorizer_v1 authorizer_) - external; + function initiateSetAuthorizerWithTimelock(address authorizer_) external; /// @notice Initiates replaces the current funding manager with `fundingManager_` on a timelock. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param fundingManager_ The address of the new funding manager module. - function initiateSetFundingManagerWithTimelock( - IFundingManager_v1 fundingManager_ - ) external; + function initiateSetFundingManagerWithTimelock(address fundingManager_) + external; /// @notice Initiates replaces the current payment processor with `paymentProcessor_` on a timelock. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param paymentProcessor_ The address of the new payment processor module. - function initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2 paymentProcessor_ - ) external; + function initiateSetPaymentProcessorWithTimelock(address paymentProcessor_) + external; /// @notice Cancels the replacement of the current authorizer with `authorizer_`. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param authorizer_ The address of the new authorizer module, for which the update is canceled. - function cancelAuthorizerUpdate(IAuthorizer_v1 authorizer_) external; + function cancelAuthorizerUpdate(address authorizer_) external; /// @notice Cancels the replacement of the current funding manager with `fundingManager_`. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param fundingManager_ The address of the new funding manager module, for which the update is canceled. - function cancelFundingManagerUpdate(IFundingManager_v1 fundingManager_) - external; + function cancelFundingManagerUpdate(address fundingManager_) external; /// @notice Cancels the replacement of the current payment processor with `paymentProcessor_`. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param paymentProcessor_ The address of the new payment processro module, for which the update is canceled. - function cancelPaymentProcessorUpdate( - IPaymentProcessor_v2 paymentProcessor_ - ) external; + function cancelPaymentProcessorUpdate(address paymentProcessor_) external; /// @notice Executes replacing the current authorizer with `_authorizer`. /// @notice !!! IMPORTANT !!! When changing the Authorizer the current set of assigned addresses to Roles are lost. /// Make sure initial owners are set properly. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param authorizer_ The address of the new authorizer module. - function executeSetAuthorizer(IAuthorizer_v1 authorizer_) external; + function executeSetAuthorizer(address authorizer_) external; /// @notice Executes replaces the current funding manager with `fundingManager_`. /// @notice !!! IMPORTANT !!! When changing the FundingManager the current funds still contained in the module might /// not be retrievable. Make sure to clean the FundingManager properly beforehand. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param fundingManager_ The address of the new funding manager module. - function executeSetFundingManager(IFundingManager_v1 fundingManager_) - external; + function executeSetFundingManager(address fundingManager_) external; /// @notice Executes replaces the current payment processor with `paymentProcessor_`. /// @notice !!! IMPORTANT !!! When changing the PaymentProcessor the current ongoing payment orders are lost. /// Make sure to resolve those payments properly beforehand. - /// @dev Only callable by authorized caller. + /// @dev Function access controlled by authorizer. /// @param paymentProcessor_ The address of the new payment processor module. - function executeSetPaymentProcessor(IPaymentProcessor_v2 paymentProcessor_) - external; + function executeSetPaymentProcessor(address paymentProcessor_) external; - /// @notice Initiates the adding of a module to the {Orchestrator_v1} on a timelock. - /// @dev Only callable by authorized address. + /// @notice Initiates the adding of a module to the {Orchestrator_v2} on a timelock. + /// @dev Function access controlled by authorizer. /// @dev Fails of adding module exeeds max modules limit. /// @dev Fails if address invalid or address already added as module. /// @param module The module address to add. function initiateAddModuleWithTimelock(address module) external; - /// @notice Initiate the removal of a module from the {Orchestrator_v1} on a timelock. + /// @notice Initiate the removal of a module from the {Orchestrator_v2} on a timelock. /// @dev Reverts if module to be removed is the current authorizer/fundingManager/paymentProcessor. /// The functions specific to updating these 3 module categories should be used instead. - /// @dev Only callable by authorized address. + /// @dev Function access controlled by authorizer. /// @dev Fails if address not added as module. /// @param module The module address to remove. function initiateRemoveModuleWithTimelock(address module) external; /// @notice Adds address `module` as module. - /// @dev Only callable by authorized address. + /// @dev Function access controlled by authorizer. /// @dev Fails if adding of module has not been initiated. /// @dev Fails if timelock has not been expired yet. /// @param module The module address to add. function executeAddModule(address module) external; /// @notice Removes address `module` as module. - /// @dev Only callable by authorized address. + /// @dev Function access controlled by authorizer. /// @dev Fails if removing of module has not been initiated. /// @dev Fails if timelock has not been expired yet. /// @param module The module address to remove. function executeRemoveModule(address module) external; /// @notice Cancels an initiated update for a module. Can be adding or removing a module - /// from the {Orchestrator_v1}. - /// @dev Only callable by authorized address. + /// from the {Orchestrator_v2}. + /// @dev Function access controlled by authorizer. /// @dev Fails if module update has not been initiated. /// @param module The module address to remove. function cancelModuleUpdate(address module) external; - - /// @notice Returns the {Orchestrator_v1}'s id. - /// @dev Unique id set by the {OrchestratorFactory_v1} during initialization. - /// @return The {Orchestrator_v1}'s id. - function orchestratorId() external view returns (uint); - - /// @notice The {IFundingManager_v1} implementation used to hold and distribute Funds. - /// @return The {IFundingManager_v1} implementation. - function fundingManager() external view returns (IFundingManager_v1); - - /// @notice The {IAuthorizer_v1} implementation used to authorize addresses. - /// @return The {IAuthorizer_v1} implementation. - function authorizer() external view returns (IAuthorizer_v1); - - /// @notice The {IPaymentProcessor_v2} implementation used to process module - /// payments. - /// @return The {IPaymentProcessor_v2} implementation. - function paymentProcessor() external view returns (IPaymentProcessor_v2); - - /// @notice The {IGovernor_v1} implementation used for protocol level interactions. - /// @return The {IGovernor_v1} implementation. - function governor() external view returns (IGovernor_v1); } diff --git a/src/proxies/InverterBeacon_v1.sol b/src/proxies/InverterBeacon_v1.sol index f247453ee..56cc91b4f 100644 --- a/src/proxies/InverterBeacon_v1.sol +++ b/src/proxies/InverterBeacon_v1.sol @@ -181,7 +181,7 @@ contract InverterBeacon_v1 is IInverterBeacon_v1, ERC165, Ownable2Step { function shutDownImplementation() external onlyOwner { // Go into emergency mode _emergencyMode = true; - // Set implementation pointer to _reverterAddress and therefor halting the system + // Set implementation pointer to _reverterAddress and therefore halting the system _implementationPointer = _reverterAddress; emit ShutdownInitiated(); diff --git a/src/templates/modules/FM_Template_v1.sol b/src/templates/modules/FM_Template_v1.sol index 437e7c7d3..a0dca253a 100644 --- a/src/templates/modules/FM_Template_v1.sol +++ b/src/templates/modules/FM_Template_v1.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.23; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; import {IFM_Template_v1} from "./IFM_Template_v1.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; @@ -20,7 +20,7 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * * @dev This contract is used to showcase a basic setup for a funding * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with + * - Inherit from the Module_v2 contract to enable interaction with * the Inverter workflow. * - Use of the IFundingManager_v1 interface to facilitate * interaction as a Funding Manager. @@ -40,7 +40,7 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; * * @author Inverter Network */ -contract FM_Template_v1 is IFM_Template_v1, Module_v1 { +contract FM_Template_v1 is IFM_Template_v1, Module_v2 { // ========================================================================= // Libraries @@ -54,7 +54,7 @@ contract FM_Template_v1 is IFM_Template_v1, Module_v1 { public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { return interfaceId_ == type(IFM_Template_v1).interfaceId @@ -87,12 +87,12 @@ contract FM_Template_v1 is IFM_Template_v1, Module_v1 { // ========================================================================= // Constructor & Init - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); // Decode module specific init data through use of configData bytes. diff --git a/src/templates/modules/IFM_Template_v1.sol b/src/templates/modules/IFM_Template_v1.sol index f644c53df..ded7ddb9f 100644 --- a/src/templates/modules/IFM_Template_v1.sol +++ b/src/templates/modules/IFM_Template_v1.sol @@ -11,7 +11,7 @@ import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; * * @dev This contract is used to showcase a basic setup for a funding * manager. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with + * - Inherit from the Module_v2 contract to enable interaction with * the Inverter workflow. * - Use of the IFundingManager_v1 interface to facilitate * interaction as a Funding Manager. diff --git a/src/templates/modules/ILM_PC_Template_v1.sol b/src/templates/modules/ILM_PC_Template_v1.sol index 8a8899eeb..a75d94476 100644 --- a/src/templates/modules/ILM_PC_Template_v1.sol +++ b/src/templates/modules/ILM_PC_Template_v1.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; // Internal -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; /** * @title Inverter Template Logic Module Payment Client @@ -20,7 +20,7 @@ import {IERC20PaymentClientBase_v2} from * - Interface compliance checks via ERC165 * * Key components: - * - Inherits from ERC20PaymentClientBase_v2 + * - Inherits from ERC20PaymentClientBase_v3 * - Uses DEPOSIT_ADMIN_ROLE for authorized payment processing * - Tracks user deposits in _depositedAmounts mapping * - Enforces maximum deposit limit of 100 ether @@ -50,7 +50,7 @@ import {IERC20PaymentClientBase_v2} from * * @author Inverter Network */ -interface ILM_PC_Template_v1 is IERC20PaymentClientBase_v2 { +interface ILM_PC_Template_v1 is IERC20PaymentClientBase_v3 { // ------------------------------------------------------------------------- // Events @@ -80,10 +80,6 @@ interface ILM_PC_Template_v1 is IERC20PaymentClientBase_v2 { /// @return token_ The address of the payment token. function getPaymentToken() external view returns (address token_); - /// @notice Returns the deposit admin role. - /// @return role_ The address of the deposit admin role. - function getDepositAdminRole() external view returns (bytes32 role_); - /// @notice Returns the maximum deposit amount. /// @return amount_ The maximum deposit amount. function getMaxDepositAmount() external view returns (uint amount_); @@ -96,6 +92,7 @@ interface ILM_PC_Template_v1 is IERC20PaymentClientBase_v2 { function deposit(uint amount_) external; /// @notice Processes a user's deposit. + /// @dev Function access controlled by authorizer. /// @param user_ The address of the user whose deposit to process. /// @param start_ The start timestamp for the payment schedule. /// @param cliff_ The cliff timestamp for the payment schedule. diff --git a/src/templates/modules/IPP_Template_v1.sol b/src/templates/modules/IPP_Template_v1.sol index 281c1b8b4..b9a8be5b3 100644 --- a/src/templates/modules/IPP_Template_v1.sol +++ b/src/templates/modules/IPP_Template_v1.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; // Internal -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; /** * @title Inverter Template Payment Processor @@ -12,9 +12,9 @@ import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; * * @dev This contract is used to showcase a basic setup for a payment * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with + * - Inherit from the Module_v2 contract to enable interaction with * the Inverter workflow. - * - Use of the IPaymentProcessor_v2 interface to facilitate + * - Use of the IPaymentProcessor_v3 interface to facilitate * interaction with a payment client. * - Implement custom interface which has all the public facing * functions, errors, events and structs. @@ -32,7 +32,7 @@ import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; * * @author Inverter Network */ -interface IPP_Template_v1 is IPaymentProcessor_v2 { +interface IPP_Template_v1 is IPaymentProcessor_v3 { //-------------------------------------------------------------------------- // Structs diff --git a/src/templates/modules/LM_PC_Template_v1.sol b/src/templates/modules/LM_PC_Template_v1.sol index 65a4a4a1f..bc0994f5d 100644 --- a/src/templates/modules/LM_PC_Template_v1.sol +++ b/src/templates/modules/LM_PC_Template_v1.sol @@ -2,17 +2,17 @@ pragma solidity 0.8.23; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {ILM_PC_Template_v1} from "src/templates/modules/ILM_PC_Template_v1.sol"; import { - IERC20PaymentClientBase_v2, - IPaymentProcessor_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + IERC20PaymentClientBase_v3, + IPaymentProcessor_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; import { - ERC20PaymentClientBase_v2, - Module_v1 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + Module_v2 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -35,25 +35,31 @@ import {ERC165Upgradeable} from * - Interface compliance checks via ERC165 * * Key components: - * - Inherits from ERC20PaymentClientBase_v2 - * - Uses DEPOSIT_ADMIN_ROLE for authorized payment processing + * - Inherits from ERC20PaymentClientBase_v3 + * - Uses permissioned modifier for authorized payment processing * - Tracks user deposits in _depositedAmounts mapping * - Enforces maximum deposit limit of 100 ether * - Processes payments through Orchestrator's payment processor * - Makes use of payment order flags * - * @custom:setup This module requires the following MANDATORY setup steps: + * @custom:setup This module has the following OPTIONAL setup steps: * - * 1. Configure DEPOSIT_ADMIN_ROLE: + * 1. Configure a role for authorized deposit processing: * - Purpose: Implements access control for processing user - * deposits. Only authorized admins can process + * deposits. Only permissioned role holders can process * deposits into payment orders. - * - How: The OrchestratorAdmin must: - * 1. Retrieve the deposit admin role identifier - * 2. Grant the role to designated admins - * - Example: module.grantModuleRole( - * module.DEPOSIT_ADMIN_ROLE(), - * adminAddress + * - How: The DefaultAdmin must: + * 1. Create a role for the designated admins + * 2. Look up the target function selector + * 3. Add access permission to the role + * - Example: uint roleId = authorizer.createRole( + * "DEPOSIT_ADMIN", + * authorizer.getAdminRole(), + * address[]); + * authorizer.addAccessPermission( + * address(module), + * ILM_PC_Template_v1.processDeposit.selector, + * roleId * ); * * @custom:security-contact security@inverter.network @@ -65,7 +71,7 @@ import {ERC165Upgradeable} from * * @author Inverter Network */ -contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { +contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v3 { // ------------------------------------------------------------------------- // Libraries @@ -79,7 +85,7 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { public view virtual - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { return interfaceId_ == type(ILM_PC_Template_v1).interfaceId @@ -92,9 +98,6 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { /// @notice The maximum deposit amount. uint internal constant MAX_DEPOSIT_AMOUNT = 100 ether; - /// @notice The role that allows processing deposits - bytes32 internal constant DEPOSIT_ADMIN_ROLE = "DEPOSIT_ADMIN"; - /// @notice The payment processor flag for the start timestamp. uint8 internal constant FLAG_START = 1; @@ -137,10 +140,10 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { /// @param configData_ The config data of the module, comprised of: /// - address: paymentToken: The payment token address. function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); // Decode module specific init data through use of configData bytes. @@ -157,7 +160,7 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { flags |= bytes32(1 << FLAG_CLIFF); flags |= bytes32(1 << FLAG_END); - __ERC20PaymentClientBase_v2_init(flags); + __ERC20PaymentClientBase_v3_init(flags); } // ------------------------------------------------------------------------- @@ -173,11 +176,6 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { return address(_paymentToken); } - /// @inheritdoc ILM_PC_Template_v1 - function getDepositAdminRole() external pure returns (bytes32) { - return DEPOSIT_ADMIN_ROLE; - } - /// @inheritdoc ILM_PC_Template_v1 function getMaxDepositAmount() external pure returns (uint) { return MAX_DEPOSIT_AMOUNT; @@ -205,7 +203,7 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { /// @inheritdoc ILM_PC_Template_v1 function processDeposit(address user_, uint start_, uint cliff_, uint end_) external - onlyModuleRole(DEPOSIT_ADMIN_ROLE) + permissioned { uint amount = _depositedAmounts[user_]; @@ -237,7 +235,7 @@ contract LM_PC_Template_v1 is ILM_PC_Template_v1, ERC20PaymentClientBase_v2 { // Process the payment. __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } diff --git a/src/templates/modules/PP_Template_v1.sol b/src/templates/modules/PP_Template_v1.sol index bb1d56586..a4ccd6466 100644 --- a/src/templates/modules/PP_Template_v1.sol +++ b/src/templates/modules/PP_Template_v1.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.23; // Internal -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; import {IPP_Template_v1} from "./IPP_Template_v1.sol"; -import {ERC165Upgradeable, Module_v1} from "src/modules/base/Module_v1.sol"; +import {ERC165Upgradeable, Module_v2} from "src/modules/base/Module_v2.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -21,9 +21,9 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; * * @dev This contract is used to showcase a basic setup for a payment * processor. The contract showcases the following: - * - Inherit from the Module_v1 contract to enable interaction with + * - Inherit from the Module_v2 contract to enable interaction with * the Inverter workflow. - * - Use of the IPaymentProcessor_v2 interface to facilitate + * - Use of the IPaymentProcessor_v3 interface to facilitate * interaction with a payment client. * - Implement custom interface which has all the public facing * functions, errors, events and structs. @@ -41,7 +41,7 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; * * @author Inverter Network */ -contract PP_Template_v1 is IPP_Template_v1, Module_v1 { +contract PP_Template_v1 is IPP_Template_v1, Module_v2 { //-------------------------------------------------------------------------- // Libraries @@ -55,11 +55,11 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { return interfaceId_ == type(IPP_Template_v1).interfaceId - || interfaceId_ == type(IPaymentProcessor_v2).interfaceId + || interfaceId_ == type(IPaymentProcessor_v3).interfaceId || super.supportsInterface(interfaceId_); } @@ -90,12 +90,12 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { //-------------------------------------------------------------------------- // Constructor & Init - /// @inheritdoc Module_v1 + /// @inheritdoc Module_v2 function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata_, bytes memory configData_ - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata_); // Decode module specific init data through use of configData bytes. @@ -122,18 +122,18 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { //-------------------------------------------------------------------------- // Public (Mutating) - /// @inheritdoc IPaymentProcessor_v2 - function processPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function processPayments(IERC20PaymentClientBase_v3 client_) external clientIsValid(address(client_)) { - // The IERC20PaymentClientBase_v2 client should be used to access + // The IERC20PaymentClientBase_v3 client should be used to access // created payment orders in the Logic Module (LM) implementing the // interface. The interface should be referenced to see the different - // functionalities provided by the ERC20PaymentClientBase_v2. + // functionalities provided by the ERC20PaymentClientBase_v3. // Collect orders from the client - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; (orders,,) = client_.collectPaymentOrders(); // Custom logic to proces the payment orders should be implemented @@ -146,7 +146,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { uint amount_ = orders[0].amount * _payoutAmountMultiplier; _paymentId = _paymentId + 1; - // Emit event of the IPaymentProcessor_v2. This is used by Inverter's + // Emit event of the IPaymentProcessor_v3. This is used by Inverter's // Indexer. emit PaymentOrderProcessed( address(client_), @@ -159,7 +159,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { new bytes32[](0) ); - // Transfer tokens from {IERC20PaymentClientBase_v2} to order + // Transfer tokens from {IERC20PaymentClientBase_v3} to order // recipients. // Please note: When processing multiple payment orders and then // letting the call revert as in this example might not be the best @@ -172,13 +172,13 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { // the accounting correct. client_.amountPaid(token_, amount_); - // Emit event of the IPaymentProcessor_v2. This is used by Inverter's + // Emit event of the IPaymentProcessor_v3. This is used by Inverter's // Indexer. emit TokensReleased(recipient_, token_, amount_); } - /// @inheritdoc IPaymentProcessor_v2 - function cancelRunningPayments(IERC20PaymentClientBase_v2 client_) + /// @inheritdoc IPaymentProcessor_v3 + function cancelRunningPayments(IERC20PaymentClientBase_v3 client_) external view clientIsValid(address(client_)) @@ -189,7 +189,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { return; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function unclaimable( address, /*client_*/ address, /*token_*/ @@ -202,7 +202,7 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { return 0; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function claimPreviouslyUnclaimable( address, /*client_*/ address, /*token_*/ @@ -211,9 +211,9 @@ contract PP_Template_v1 is IPP_Template_v1, Module_v1 { return; } - /// @inheritdoc IPaymentProcessor_v2 + /// @inheritdoc IPaymentProcessor_v3 function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) external view returns (bool) { // This function is used to validate the payment order created on the // client side (LM_PC) with the input required by the Payment Processor diff --git a/src/templates/tests/unit/FM_Template_v1.t.sol b/src/templates/tests/unit/FM_Template_v1.t.sol index 81531d964..fddc7fd10 100644 --- a/src/templates/tests/unit/FM_Template_v1.t.sol +++ b/src/templates/tests/unit/FM_Template_v1.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; // Internal import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; @@ -18,9 +18,9 @@ import {FM_Template_v1_Exposed} from "src/templates/tests/unit/FM_Template_v1_Exposed.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // System under Test (SuT) import {IFM_Template_v1} from "src/templates/modules/IFM_Template_v1.sol"; @@ -55,7 +55,7 @@ contract FM_Template_v1_Test is ModuleTest { // Mocks ERC20Mock orchestratorToken; - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; // ========================================================================= // Setup @@ -80,7 +80,7 @@ contract FM_Template_v1_Test is ModuleTest { // Setup other modules needed in the unit tests. // In this case a payment client is needed to test the FM_Template_v1. - paymentClient = new ERC20PaymentClientBaseV2Mock(); + paymentClient = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(paymentClient)); } @@ -101,7 +101,7 @@ contract FM_Template_v1_Test is ModuleTest { } // Test the interface support - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( fundingManager.supportsInterface( type(IFundingManager_v1).interfaceId diff --git a/src/templates/tests/unit/LM_PC_Template_v1.t.sol b/src/templates/tests/unit/LM_PC_Template_v1.t.sol index e61f7f388..dff2e52d1 100644 --- a/src/templates/tests/unit/LM_PC_Template_v1.t.sol +++ b/src/templates/tests/unit/LM_PC_Template_v1.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; // Internal import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; @@ -15,9 +15,9 @@ import {Clones} from "@oz/proxy/Clones.sol"; // Tests and Mocks import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // System under Test (SuT) import {LM_PC_Template_v1_Exposed} from @@ -72,10 +72,8 @@ contract LM_PC_Template_v1_Test is ModuleTest { _orchestrator, _METADATA, abi.encode(address(paymentToken)) ); - // Give test contract the DEPOSIT_ADMIN_ROLE. - paymentClient.grantModuleRole( - paymentClient.getDepositAdminRole(), address(this) - ); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); } // ------------------------------------------------------------------------- @@ -86,10 +84,10 @@ contract LM_PC_Template_v1_Test is ModuleTest { assertEq(paymentClient.getPaymentToken(), address(paymentToken)); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( paymentClient.supportsInterface( - type(IERC20PaymentClientBase_v2).interfaceId + type(IERC20PaymentClientBase_v3).interfaceId ) ); assertTrue( @@ -161,43 +159,28 @@ contract LM_PC_Template_v1_Test is ModuleTest { } /* Test: processDeposit() - ├── Given the caller does not have the DEPOSIT_ADMIN_ROLE + ├── Given the caller is not permissioned │ └── When the function processDeposit() is called │ └── Then it reverts (modifier in place) - └── Given the caller has DEPOSIT_ADMIN_ROLE + └── Given the caller is permissioned └── When the function processDeposit() is called ├── Then the deposit balance clears └── And the payment order processes */ - function testProcessDeposit_revertGivenNotAdmin(address notAdmin_) public { - // Setup - vm.assume(notAdmin_ != address(this) && notAdmin_ != address(0)); - - uint depositAmount = 50 ether; - vm.startPrank(notAdmin_); - paymentToken.mint(notAdmin_, depositAmount); - paymentToken.approve(address(paymentClient), depositAmount); - - uint start = block.timestamp; - uint cliff = block.timestamp + 30 days; - uint end = block.timestamp + 90 days; - paymentClient.deposit(depositAmount); + function testBuyFor_ModifierInPositionChecks() public { + // permissioned - // Test + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _orchestrator.authorizer().generateRoleId( - address(paymentClient), paymentClient.getDepositAdminRole() - ), - notAdmin_ + IModule_v2.Module__CallerNotPermissioned.selector ) ); - paymentClient.processDeposit(notAdmin_, start, cliff, end); - - vm.stopPrank(); + vm.prank(address(0xB0B)); + paymentClient.processDeposit(address(0), 0, 0, 0); } function testProcessDeposit_worksGivenDepositIsProcessed( @@ -264,14 +247,6 @@ contract LM_PC_Template_v1_Test is ModuleTest { assertEq(paymentClient.getPaymentToken(), address(paymentToken)); } - /* Test: getDepositAdminRole() - └── When the function getDepositAdminRole() is called - └── Then it returns the deposit admin role - */ - function testGetDepositAdminRole() public { - assertEq(paymentClient.getDepositAdminRole(), DEPOSIT_ADMIN_ROLE); - } - /* Test: getMaxDepositAmount() └── When the function getMaxDepositAmount() is called └── Then it returns the maximum deposit amount diff --git a/src/templates/tests/unit/PP_Template_v1.t.sol b/src/templates/tests/unit/PP_Template_v1.t.sol index 2013005d7..e229caa18 100644 --- a/src/templates/tests/unit/PP_Template_v1.t.sol +++ b/src/templates/tests/unit/PP_Template_v1.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; // Internal import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; @@ -16,15 +16,15 @@ import {Clones} from "@oz/proxy/Clones.sol"; import {PP_Template_v1_Exposed} from "src/templates/tests/unit/PP_Template_v1_Exposed.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock, + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock, ERC20Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // System under Test (SuT) import { IPP_Template_v1, - IPaymentProcessor_v2 + IPaymentProcessor_v3 } from "src/templates/modules/PP_Template_v1.sol"; /** @@ -58,7 +58,7 @@ contract PP_Template_v1_Test is ModuleTest { // System under test (SuT) PP_Template_v1_Exposed paymentProcessor; // Mocks - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; //-------------------------------------------------------------------------- // Setup @@ -81,8 +81,8 @@ contract PP_Template_v1_Test is ModuleTest { // Setup other modules needed in the unit tests. // In this case a payment client is needed to test the PP_Template_v1. - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); // Adding the payment client is done through a timelock mechanism _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); @@ -104,10 +104,10 @@ contract PP_Template_v1_Test is ModuleTest { } // Test the interface support - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( paymentProcessor.supportsInterface( - type(IPaymentProcessor_v2).interfaceId + type(IPaymentProcessor_v3).interfaceId ) ); assertTrue( @@ -134,8 +134,8 @@ contract PP_Template_v1_Test is ModuleTest { └── Then it should revert */ function testProcessPayments_modifierInPlace() public { - ERC20PaymentClientBaseV2Mock nonRegisteredClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock nonRegisteredClient = + new ERC20PaymentClientBase_v3_Mock(); vm.expectRevert( IPP_Template_v1.Module__PP_Template__ClientNotValid.selector diff --git a/test/e2e/E2EModuleRegistry.sol b/test/e2e/E2EModuleRegistry.sol index e961036b4..146a82052 100644 --- a/test/e2e/E2EModuleRegistry.sol +++ b/test/e2e/E2EModuleRegistry.sol @@ -12,34 +12,34 @@ import {IOrchestratorFactory_v1} from import {Governor_v1} from "@ex/governance/Governor_v1.sol"; // Modules -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {FM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1} from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {FM_BC_Bancor_Redeeming_VirtualSupply_v2} from + "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; +import {FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2} from + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; import {BondingSurface} from "@fm/bondingCurve/formulas/BondingSurface.sol"; -import {FM_EXT_TokenVault_v1} from "@fm/extensions/FM_EXT_TokenVault_v1.sol"; +import {FM_EXT_TokenVault_v2} from "@fm/extensions/FM_EXT_TokenVault_v2.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; -import {PP_Simple_v2} from "src/modules/paymentProcessor/PP_Simple_v2.sol"; -import {PP_Streaming_v2} from "src/modules/paymentProcessor/PP_Streaming_v2.sol"; -import {LM_PC_Bounties_v2} from "@lm/LM_PC_Bounties_v2.sol"; -import {LM_PC_RecurringPayments_v2} from "@lm/LM_PC_RecurringPayments_v2.sol"; -import {LM_PC_PaymentRouter_v2} from "@lm/LM_PC_PaymentRouter_v2.sol"; -import {LM_PC_Staking_v2} from "@lm/LM_PC_Staking_v2.sol"; -import {LM_PC_KPIRewarder_v2} from "@lm/LM_PC_KPIRewarder_v2.sol"; -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; -import {AUT_TokenGated_Roles_v1} from "@aut/role/AUT_TokenGated_Roles_v1.sol"; -import {AUT_EXT_VotingRoles_v1} from - "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol"; -import {PP_Queue_ManualExecution_v1} from "@pp/PP_Queue_ManualExecution_v1.sol"; -import {PP_Queue_v1} from "@pp/PP_Queue_v1.sol"; -import {FM_PC_Oracle_Redeeming_v1} from - "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol"; -import {LM_Oracle_Permissioned_v1} from - "src/modules/logicModule/LM_Oracle_Permissioned_v1.sol"; -import {PP_Everclear_CrossChain_v1} from - "src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol"; +import {PP_Simple_v3} from "src/modules/paymentProcessor/PP_Simple_v3.sol"; +import {PP_Streaming_v3} from "src/modules/paymentProcessor/PP_Streaming_v3.sol"; +import {LM_PC_Bounties_v3} from "@lm/LM_PC_Bounties_v3.sol"; +import {LM_PC_RecurringPayments_v3} from "@lm/LM_PC_RecurringPayments_v3.sol"; +import {LM_PC_PaymentRouter_v3} from "@lm/LM_PC_PaymentRouter_v3.sol"; +import {LM_PC_Staking_v3} from "@lm/LM_PC_Staking_v3.sol"; +import {LM_PC_KPIRewarder_v3} from "@lm/LM_PC_KPIRewarder_v3.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; +import {AUT_TokenGated_Roles_v2} from "@aut/role/AUT_TokenGated_Roles_v2.sol"; +import {AUT_EXT_VotingRoles_v2} from + "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol"; +import {PP_Queue_ManualExecution_v2} from "@pp/PP_Queue_ManualExecution_v2.sol"; +import {PP_Queue_v2} from "@pp/PP_Queue_v2.sol"; +import {FM_PC_Oracle_Redeeming_v2} from + "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol"; +import {LM_Oracle_Permissioned_v2} from + "src/modules/logicModule/LM_Oracle_Permissioned_v2.sol"; +import {PP_Everclear_CrossChain_v2} from + "src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol"; import {Mock_LM_PC_PaymentRouter_Everclear_v1} from "@mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol"; @@ -68,7 +68,7 @@ contract E2EModuleRegistry is Test { // Module moduleImpl; // InverterBeacon_v1 moduleBeacon; // address moduleBeaconOwner = DEFAULT_BEACON_OWNER; - // IModule_v1.Metadata moduleMetadata = IModule_v1.Metadata( + // IModule_v2.Metadata moduleMetadata = IModule_v2.Metadata( // 1, 1, "https://github.com/inverter/module", "ModuleName" // ); // And AS A COMMENT: @@ -88,19 +88,19 @@ contract E2EModuleRegistry is Test { // Funding Managers //-------------------------------------------------------------------------- - IModule_v1.Metadata fundingManagerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata fundingManagerMetadata = IModule_v2.Metadata( 1, // major version 0, // minor version 0, // patch version "https://github.com/inverter/funding-manager", - "FM_PC_Oracle_Redeeming_v1" + "FM_PC_Oracle_Redeeming_v2" ); InverterBeacon_v1 fundingManagerBeacon; - FM_PC_Oracle_Redeeming_v1 fundingManagerExternal; + FM_PC_Oracle_Redeeming_v2 fundingManagerExternal; function setUpPermissionedOracleRedeemingFundingManager() internal { - fundingManagerExternal = new FM_PC_Oracle_Redeeming_v1(); + fundingManagerExternal = new FM_PC_Oracle_Redeeming_v2(); fundingManagerBeacon = new InverterBeacon_v1( moduleFactory.reverter(), @@ -121,36 +121,36 @@ contract E2EModuleRegistry is Test { // Funding Managers //-------------------------------------------------------------------------- - // FM_BC_Bancor_Redeeming_VirtualSupply_v1 + // FM_BC_Bancor_Redeeming_VirtualSupply_v2 BancorFormula formula = new BancorFormula(); - FM_BC_Bancor_Redeeming_VirtualSupply_v1 + FM_BC_Bancor_Redeeming_VirtualSupply_v2 bancorVirtualSupplyBondingCurveFundingManagerImpl; InverterBeacon_v1 bancorVirtualSupplyBondingCurveFundingManagerBeacon; - IModule_v1.Metadata bancorVirtualSupplyBondingCurveFundingManagerMetadata = - IModule_v1.Metadata( + IModule_v2.Metadata bancorVirtualSupplyBondingCurveFundingManagerMetadata = + IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/bonding-curve-funding-manager", - "FM_BC_Bancor_Redeeming_VirtualSupply_v1" + "FM_BC_Bancor_Redeeming_VirtualSupply_v2" ); /* - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.IssuanceToken memory - issuanceToken = IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2.IssuanceToken memory + issuanceToken = IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .IssuanceToken({ name: bytes32(abi.encodePacked("Bonding Curve Token")), symbol: bytes32(abi.encodePacked("BCT")), decimals: uint8(18) }); - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties + IFM_BC_Bancor_Redeeming_VirtualSupply_v2.BondingCurveProperties memory bc_properties = - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .BondingCurveProperties({ formula: address(formula), reserveRatioForBuying: 200_000, @@ -175,7 +175,7 @@ contract E2EModuleRegistry is Test { function setUpBancorVirtualSupplyBondingCurveFundingManager() internal { // Deploy module implementations. bancorVirtualSupplyBondingCurveFundingManagerImpl = - new FM_BC_Bancor_Redeeming_VirtualSupply_v1(); + new FM_BC_Bancor_Redeeming_VirtualSupply_v2(); // Deploy module beacons. bancorVirtualSupplyBondingCurveFundingManagerBeacon = new InverterBeacon_v1( @@ -197,37 +197,37 @@ contract E2EModuleRegistry is Test { ); } - // FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + // FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 BondingSurface bondingSurface = new BondingSurface(); - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 bondingSurfaceRedeemingRestrictedRepayerSeizableImpl; InverterBeacon_v1 bondingSurfaceRedeemingRestrictedRepayerSeizableBeacon; - IModule_v1.Metadata - bondingSurfaceRedeemingRestrictedRepayerSeizableMetadata = IModule_v1 + IModule_v2.Metadata + bondingSurfaceRedeemingRestrictedRepayerSeizableMetadata = IModule_v2 .Metadata( 1, 0, 0, "https://github.com/inverter/contracts", - "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1" + "FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2" ); /* - IFM_BC_BondingSurface_Redeeming_v1.IssuanceToken memory - issuanceToken = IFM_BC_BondingSurface_Redeeming_v1 + IFM_BC_BondingSurface_Redeeming_v2.IssuanceToken memory + issuanceToken = IFM_BC_BondingSurface_Redeeming_v2 .IssuanceToken({ name: bytes32(abi.encodePacked("Bonding Curve Token")), symbol: bytes32(abi.encodePacked("BCT")), decimals: uint8(18) }); - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties = - IFM_BC_BondingSurface_Redeeming_v1 + IFM_BC_BondingSurface_Redeeming_v2 .BondingCurveProperties({ formula: address(bondingSurface), capitalRequired: 1_000_000 * 1e18, // Taken from Topos repo test case @@ -256,8 +256,8 @@ contract E2EModuleRegistry is Test { function setUpBondingSurfaceRedeemingRestrictedRepayerSeizable() internal { // Deploy module implementations. - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - bondigSurfaceRedeemingRestrictedRepayerSeizableImpl = new FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + bondigSurfaceRedeemingRestrictedRepayerSeizableImpl = new FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2( ); // Deploy module beacons. @@ -283,15 +283,15 @@ contract E2EModuleRegistry is Test { ); } - // FM_EXT_TokenVault_v1 + // FM_EXT_TokenVault_v2 - FM_EXT_TokenVault_v1 tokenVaultFundingManagerExtensionImpl; + FM_EXT_TokenVault_v2 tokenVaultFundingManagerExtensionImpl; InverterBeacon_v1 tokenVaultFundingManagerExtensionBeacon; - IModule_v1.Metadata tokenVaultFundingManagerExtensionMetadata = IModule_v1 + IModule_v2.Metadata tokenVaultFundingManagerExtensionMetadata = IModule_v2 .Metadata( - 1, 0, 0, "https://github.com/inverter/contracts", "FM_EXT_TokenVault_v1" + 1, 0, 0, "https://github.com/inverter/contracts", "FM_EXT_TokenVault_v2" ); /* @@ -305,7 +305,7 @@ contract E2EModuleRegistry is Test { function setUpTokenVaultFundingManagerExtension() internal { // Deploy module implementations. - tokenVaultFundingManagerExtensionImpl = new FM_EXT_TokenVault_v1(); + tokenVaultFundingManagerExtensionImpl = new FM_EXT_TokenVault_v2(); // Deploy module beacons. tokenVaultFundingManagerExtensionBeacon = new InverterBeacon_v1( @@ -332,7 +332,7 @@ contract E2EModuleRegistry is Test { InverterBeacon_v1 depositVaultBeacon; - IModule_v1.Metadata depositVaultMetadata = IModule_v1.Metadata( + IModule_v2.Metadata depositVaultMetadata = IModule_v2.Metadata( 1, 0, 0, @@ -367,16 +367,16 @@ contract E2EModuleRegistry is Test { // Role Authorizer - AUT_Roles_v1 roleAuthorizerImpl; + AUT_Roles_v2 roleAuthorizerImpl; InverterBeacon_v1 roleAuthorizerBeacon; - IModule_v1.Metadata roleAuthorizerMetadata = IModule_v1.Metadata( - 1, 0, 0, "https://github.com/inverter/roleAuthorizer", "AUT_Roles_v1" + IModule_v2.Metadata roleAuthorizerMetadata = IModule_v2.Metadata( + 1, 0, 0, "https://github.com/inverter/roleAuthorizer", "AUT_Roles_v2" ); /* - // Note that AUT_Roles_v1 owner and manager are the same + // Note that AUT_Roles_v2 owner and manager are the same IOrchestratorFactory_v1.ModuleConfig roleAuthorizerFactoryConfig = IOrchestratorFactory_v1.ModuleConfig( roleAuthorizerMetadata, @@ -385,7 +385,7 @@ contract E2EModuleRegistry is Test { */ function setUpRoleAuthorizer() internal { // Deploy module implementations. - roleAuthorizerImpl = new AUT_Roles_v1(); + roleAuthorizerImpl = new AUT_Roles_v2(); // Deploy module beacons. roleAuthorizerBeacon = new InverterBeacon_v1( @@ -406,20 +406,20 @@ contract E2EModuleRegistry is Test { // Token Gated Role Authorizer - AUT_TokenGated_Roles_v1 tokenRoleAuthorizerImpl; + AUT_TokenGated_Roles_v2 tokenRoleAuthorizerImpl; InverterBeacon_v1 tokenRoleAuthorizerBeacon; - IModule_v1.Metadata tokenRoleAuthorizerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata tokenRoleAuthorizerMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/tokenRoleAuthorizer", - "AUT_TokenGated_Roles_v1" + "AUT_TokenGated_Roles_v2" ); /* - // Note that AUT_Roles_v1 owner and manager are the same + // Note that AUT_Roles_v2 owner and manager are the same IOrchestratorFactory_v1.ModuleConfig tokenRoleAuthorizerFactoryConfig = IOrchestratorFactory_v1.ModuleConfig( tokenRoleAuthorizerMetadata, @@ -429,7 +429,7 @@ contract E2EModuleRegistry is Test { function setUpTokenGatedRoleAuthorizer() internal { // Deploy module implementations. - tokenRoleAuthorizerImpl = new AUT_TokenGated_Roles_v1(); + tokenRoleAuthorizerImpl = new AUT_TokenGated_Roles_v2(); // Deploy module beacons. tokenRoleAuthorizerBeacon = new InverterBeacon_v1( @@ -453,21 +453,21 @@ contract E2EModuleRegistry is Test { // Payment Processors //-------------------------------------------------------------------------- - // PP_Queue_v1 - PP_Queue_v1 queueBasedPaymentProcessor; + // PP_Queue_v2 + PP_Queue_v2 queueBasedPaymentProcessor; InverterBeacon_v1 queueBasedPaymentProcessorBeacon; - IModule_v1.Metadata queueBasedPaymentProcessorMetadata = IModule_v1.Metadata( + IModule_v2.Metadata queueBasedPaymentProcessorMetadata = IModule_v2.Metadata( 1, // major version 0, // minor version 0, // patch version "https://github.com/inverter/payment-processor", - "PP_Queue_v1" + "PP_Queue_v2" ); function setUpQueueBasedPaymentProcessor() internal { // Deploy module implementations. - queueBasedPaymentProcessor = new PP_Queue_v1(); + queueBasedPaymentProcessor = new PP_Queue_v2(); // Deploy module beacons. queueBasedPaymentProcessorBeacon = new InverterBeacon_v1( @@ -486,23 +486,23 @@ contract E2EModuleRegistry is Test { IInverterBeacon_v1(queueBasedPaymentProcessorBeacon) ); } - // PP_Queue_ManualExecution_v1 + // PP_Queue_ManualExecution_v2 - PP_Queue_ManualExecution_v1 manualQueueBasedPaymentProcessor; + PP_Queue_ManualExecution_v2 manualQueueBasedPaymentProcessor; InverterBeacon_v1 manualQueueBasedPaymentProcessorBeacon; - IModule_v1.Metadata manualQueueBasedPaymentProcessorMetadata = IModule_v1 + IModule_v2.Metadata manualQueueBasedPaymentProcessorMetadata = IModule_v2 .Metadata( 1, // major version 0, // minor version 0, // patch version "https://github.com/inverter/payment-processor", - "PP_Queue_v1" + "PP_Queue_v2" ); function setUpManualQueueBasedPaymentProcessor() internal { // Deploy module implementations. - manualQueueBasedPaymentProcessor = new PP_Queue_ManualExecution_v1(); + manualQueueBasedPaymentProcessor = new PP_Queue_ManualExecution_v2(); // Deploy module beacons. manualQueueBasedPaymentProcessorBeacon = new InverterBeacon_v1( @@ -524,12 +524,12 @@ contract E2EModuleRegistry is Test { // PP_Simple_v1 - PP_Simple_v2 simplePaymentProcessorImpl; + PP_Simple_v3 simplePaymentProcessorImpl; InverterBeacon_v1 simplePaymentProcessorBeacon; - IModule_v1.Metadata simplePaymentProcessorMetadata = IModule_v1.Metadata( - 1, 0, 0, "https://github.com/inverter/payment-processor", "PP_Simple_v2" + IModule_v2.Metadata simplePaymentProcessorMetadata = IModule_v2.Metadata( + 1, 0, 0, "https://github.com/inverter/payment-processor", "PP_Simple_v3" ); /* @@ -541,7 +541,7 @@ contract E2EModuleRegistry is Test { */ function setUpSimplePaymentProcessor() internal { // Deploy module implementations. - simplePaymentProcessorImpl = new PP_Simple_v2(); + simplePaymentProcessorImpl = new PP_Simple_v3(); // Deploy module beacons. simplePaymentProcessorBeacon = new InverterBeacon_v1( @@ -561,18 +561,18 @@ contract E2EModuleRegistry is Test { ); } - // PP_Streaming_v2 + // PP_Streaming_v3 - PP_Streaming_v2 streamingPaymentProcessorImpl; + PP_Streaming_v3 streamingPaymentProcessorImpl; InverterBeacon_v1 streamingPaymentProcessorBeacon; - IModule_v1.Metadata streamingPaymentProcessorMetadata = IModule_v1.Metadata( + IModule_v2.Metadata streamingPaymentProcessorMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/streaming-payment-processor", - "PP_Streaming_v2" + "PP_Streaming_v3" ); /* @@ -585,7 +585,7 @@ contract E2EModuleRegistry is Test { */ function setUpStreamingPaymentProcessor() internal { // Deploy module implementations. - streamingPaymentProcessorImpl = new PP_Streaming_v2(); + streamingPaymentProcessorImpl = new PP_Streaming_v3(); // Deploy module beacons. streamingPaymentProcessorBeacon = new InverterBeacon_v1( @@ -605,19 +605,19 @@ contract E2EModuleRegistry is Test { ); } - // PP_Everclear_CrossChain_v1 - PP_Everclear_CrossChain_v1 ppEverclearCrossChainImpl; + // PP_Everclear_CrossChain_v2 + PP_Everclear_CrossChain_v2 ppEverclearCrossChainImpl; InverterBeacon_v1 ppEverclearCrossChainBeacon; - IModule_v1.Metadata public ppEverclearCrossChainMetadata = IModule_v1 + IModule_v2.Metadata public ppEverclearCrossChainMetadata = IModule_v2 .Metadata( 1, // major version 0, // minor version 0, // patch version - "src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol", // Using file path as URL for local ref - "PP_Everclear_CrossChain_v1" + "src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol", // Using file path as URL for local ref + "PP_Everclear_CrossChain_v2" ); /* - // Example Config for PP_Everclear_CrossChain_v1: + // Example Config for PP_Everclear_CrossChain_v2: // Assumes 'address everclearSpokeAddress' is defined in the test's setUp. IOrchestratorFactory_v1.ModuleConfig ppEverclearConfig = IOrchestratorFactory_v1.ModuleConfig( ppEverclearCrossChainMetadata, @@ -640,7 +640,7 @@ contract E2EModuleRegistry is Test { ); // Deploy module implementation. - ppEverclearCrossChainImpl = new PP_Everclear_CrossChain_v1(); + ppEverclearCrossChainImpl = new PP_Everclear_CrossChain_v2(); // Deploy module beacon. ppEverclearCrossChainBeacon = new InverterBeacon_v1( @@ -660,17 +660,17 @@ contract E2EModuleRegistry is Test { IInverterBeacon_v1(ppEverclearCrossChainBeacon) ) { console.log( - "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v1 SUCCESS" + "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v2 SUCCESS" ); } catch Error(string memory reason) { console.log( - "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v1 FAILED - Error:", + "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v2 FAILED - Error:", reason ); revert(reason); } catch (bytes memory lowLevelData) { console.log( - "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v1 FAILED - LowLevelData:", + "setUpPPEverclearCrossChain: registerMetadataInModuleFactory for PP_Everclear_CrossChain_v2 FAILED - LowLevelData:", string(lowLevelData) ); revert("LowLevelData failure"); @@ -681,21 +681,21 @@ contract E2EModuleRegistry is Test { // logicModules //-------------------------------------------------------------------------- - // LM_Oracle_Permissioned_v1 + // LM_Oracle_Permissioned_v2 - IModule_v1.Metadata oracleMetadata = IModule_v1.Metadata( + IModule_v2.Metadata oracleMetadata = IModule_v2.Metadata( 1, // major version 0, // minor version 0, // patch version "https://github.com/inverter/oracle", - "LM_Oracle_Permissioned_v1" + "LM_Oracle_Permissioned_v2" ); InverterBeacon_v1 oracleBeacon; - LM_Oracle_Permissioned_v1 oracle; + LM_Oracle_Permissioned_v2 oracle; function setUpPermissionedOracle() internal { - oracle = new LM_Oracle_Permissioned_v1(); + oracle = new LM_Oracle_Permissioned_v2(); oracleBeacon = new InverterBeacon_v1( moduleFactory.reverter(), @@ -712,18 +712,18 @@ contract E2EModuleRegistry is Test { ); } - // LM_PC_RecurringPayments_v2 + // LM_PC_RecurringPayments_v3 - LM_PC_RecurringPayments_v2 recurringPaymentManagerImpl; + LM_PC_RecurringPayments_v3 recurringPaymentManagerImpl; InverterBeacon_v1 recurringPaymentManagerBeacon; - IModule_v1.Metadata recurringPaymentManagerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata recurringPaymentManagerMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/recurring-payment-manager", - "LM_PC_RecurringPayments_v2" + "LM_PC_RecurringPayments_v3" ); /* IOrchestratorFactory_v1.ModuleConfig recurringPaymentManagerFactoryConfig = @@ -735,7 +735,7 @@ contract E2EModuleRegistry is Test { function setUpRecurringPaymentManager() internal { // Deploy module implementations. - recurringPaymentManagerImpl = new LM_PC_RecurringPayments_v2(); + recurringPaymentManagerImpl = new LM_PC_RecurringPayments_v3(); // Deploy module beacons. recurringPaymentManagerBeacon = new InverterBeacon_v1( @@ -755,18 +755,18 @@ contract E2EModuleRegistry is Test { ); } - // LM_PC_Bounties_v2 + // LM_PC_Bounties_v3 - LM_PC_Bounties_v2 bountyManagerImpl; + LM_PC_Bounties_v3 bountyManagerImpl; InverterBeacon_v1 bountyManagerBeacon; - IModule_v1.Metadata bountyManagerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata bountyManagerMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/bounty-manager", - "LM_PC_Bounties_v2" + "LM_PC_Bounties_v3" ); /* IOrchestratorFactory_v1.ModuleConfig bountyManagerFactoryConfig = @@ -779,7 +779,7 @@ contract E2EModuleRegistry is Test { function setUpBountyManager() internal { // Deploy module implementations. - bountyManagerImpl = new LM_PC_Bounties_v2(); + bountyManagerImpl = new LM_PC_Bounties_v3(); // Deploy module beacons. bountyManagerBeacon = new InverterBeacon_v1( @@ -798,17 +798,17 @@ contract E2EModuleRegistry is Test { ); } - // LM_PC_PaymentRouter_v2 - LM_PC_PaymentRouter_v2 paymentRouterImpl; + // LM_PC_PaymentRouter_v3 + LM_PC_PaymentRouter_v3 paymentRouterImpl; InverterBeacon_v1 paymentRouterBeacon; - IModule_v1.Metadata public paymentRouterMetadata = IModule_v1.Metadata( + IModule_v2.Metadata public paymentRouterMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/InverterNetwork/contracts", - "LM_PC_PaymentRouter_v2" + "LM_PC_PaymentRouter_v3" ); /* @@ -822,7 +822,7 @@ contract E2EModuleRegistry is Test { function setUpPaymentRouter() internal { // Deploy module implementations. - paymentRouterImpl = new LM_PC_PaymentRouter_v2(); + paymentRouterImpl = new LM_PC_PaymentRouter_v3(); // Deploy module beacons. paymentRouterBeacon = new InverterBeacon_v1( @@ -841,98 +841,98 @@ contract E2EModuleRegistry is Test { ); } - // LM_PC_Staking_v2 + // LM_PC_Staking_v3 - LM_PC_Staking_v2 LM_PC_Staking_v2Impl; + LM_PC_Staking_v3 LM_PC_Staking_v3Impl; - InverterBeacon_v1 LM_PC_Staking_v2Beacon; + InverterBeacon_v1 LM_PC_Staking_v3Beacon; - IModule_v1.Metadata LM_PC_Staking_v2Metadata = IModule_v1.Metadata( + IModule_v2.Metadata LM_PC_Staking_v3Metadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/staking-manager", - "LM_PC_Staking_v2" + "LM_PC_Staking_v3" ); /* - IOrchestratorFactory_v1.ModuleConfig LM_PC_Staking_v2FactoryConfig = + IOrchestratorFactory_v1.ModuleConfig LM_PC_Staking_v3FactoryConfig = IOrchestratorFactory_v1.ModuleConfig( - LM_PC_Staking_v2Metadata, + LM_PC_Staking_v3Metadata, bytes(address(stakingToken)) ); */ - function setUpLM_PC_Staking_v2() internal { + function setUpLM_PC_Staking_v3() internal { // Deploy module implementations. - LM_PC_Staking_v2Impl = new LM_PC_Staking_v2(); + LM_PC_Staking_v3Impl = new LM_PC_Staking_v3(); // Deploy module beacons. - LM_PC_Staking_v2Beacon = new InverterBeacon_v1( + LM_PC_Staking_v3Beacon = new InverterBeacon_v1( moduleFactory.reverter(), DEFAULT_BEACON_OWNER, - LM_PC_Staking_v2Metadata.majorVersion, - address(LM_PC_Staking_v2Impl), - LM_PC_Staking_v2Metadata.minorVersion, - LM_PC_Staking_v2Metadata.patchVersion + LM_PC_Staking_v3Metadata.majorVersion, + address(LM_PC_Staking_v3Impl), + LM_PC_Staking_v3Metadata.minorVersion, + LM_PC_Staking_v3Metadata.patchVersion ); // Register modules at moduleFactory. vm.prank(teamMultisig); gov.registerMetadataInModuleFactory( - LM_PC_Staking_v2Metadata, IInverterBeacon_v1(LM_PC_Staking_v2Beacon) + LM_PC_Staking_v3Metadata, IInverterBeacon_v1(LM_PC_Staking_v3Beacon) ); } - // LM_PC_KPIRewarder_v2 + // LM_PC_KPIRewarder_v3 - LM_PC_KPIRewarder_v2 LM_PC_KPIRewarder_v2Impl; + LM_PC_KPIRewarder_v3 LM_PC_KPIRewarder_v3Impl; - InverterBeacon_v1 LM_PC_KPIRewarder_v2Beacon; + InverterBeacon_v1 LM_PC_KPIRewarder_v3Beacon; - IModule_v1.Metadata LM_PC_KPIRewarder_v2Metadata = IModule_v1.Metadata( + IModule_v2.Metadata LM_PC_KPIRewarder_v3Metadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/KPI-Rewarder", - "LM_PC_KPIRewarder_v2" + "LM_PC_KPIRewarder_v3" ); /* - IOrchestratorFactory_v1.ModuleConfig LM_PC_KPIRewarder_v2FactoryConfig = + IOrchestratorFactory_v1.ModuleConfig LM_PC_KPIRewarder_v3FactoryConfig = IOrchestratorFactory_v1.ModuleConfig( - LM_PC_KPIRewarder_v2Metadata, + LM_PC_KPIRewarder_v3Metadata, abi.encode(address(stakingToken), address(oracleBondToken), address(OptimisticOracleV3Address), uint64(assertionLiveness) ) ); */ - function setUpLM_PC_KPIRewarder_v2() internal { + function setUpLM_PC_KPIRewarder_v3() internal { // Deploy module implementations. - LM_PC_KPIRewarder_v2Impl = new LM_PC_KPIRewarder_v2(); + LM_PC_KPIRewarder_v3Impl = new LM_PC_KPIRewarder_v3(); // Deploy module beacons. - LM_PC_KPIRewarder_v2Beacon = new InverterBeacon_v1( + LM_PC_KPIRewarder_v3Beacon = new InverterBeacon_v1( moduleFactory.reverter(), DEFAULT_BEACON_OWNER, - LM_PC_KPIRewarder_v2Metadata.majorVersion, - address(LM_PC_KPIRewarder_v2Impl), - LM_PC_KPIRewarder_v2Metadata.minorVersion, - LM_PC_KPIRewarder_v2Metadata.patchVersion + LM_PC_KPIRewarder_v3Metadata.majorVersion, + address(LM_PC_KPIRewarder_v3Impl), + LM_PC_KPIRewarder_v3Metadata.minorVersion, + LM_PC_KPIRewarder_v3Metadata.patchVersion ); // Register modules at moduleFactory. vm.prank(teamMultisig); gov.registerMetadataInModuleFactory( - LM_PC_KPIRewarder_v2Metadata, - IInverterBeacon_v1(LM_PC_KPIRewarder_v2Beacon) + LM_PC_KPIRewarder_v3Metadata, + IInverterBeacon_v1(LM_PC_KPIRewarder_v3Beacon) ); } // Mock_LM_PC_PaymentRouter_Everclear_v1 Mock_LM_PC_PaymentRouter_Everclear_v1 mockLmPcPaymentRouterEverclearImpl; InverterBeacon_v1 mockLmPcPaymentRouterEverclearBeacon; - IModule_v1.Metadata public mockLmPcPaymentRouterEverclearMetadata = - IModule_v1.Metadata( + IModule_v2.Metadata public mockLmPcPaymentRouterEverclearMetadata = + IModule_v2.Metadata( 1, // major version 0, // minor version 0, // patch version @@ -1006,18 +1006,18 @@ contract E2EModuleRegistry is Test { //-------------------------------------------------------------------------- // utils - // AUT_EXT_VotingRoles_v1 + // AUT_EXT_VotingRoles_v2 - AUT_EXT_VotingRoles_v1 votingRolesImpl; + AUT_EXT_VotingRoles_v2 votingRolesImpl; InverterBeacon_v1 votingRolesBeacon; - IModule_v1.Metadata votingRolesMetadata = IModule_v1.Metadata( + IModule_v2.Metadata votingRolesMetadata = IModule_v2.Metadata( 1, 0, 0, "https://github.com/inverter/single-vote-governor", - "AUT_EXT_VotingRoles_v1" + "AUT_EXT_VotingRoles_v2" ); /* @@ -1033,7 +1033,7 @@ contract E2EModuleRegistry is Test { function setUpVotingRoles() internal { // Deploy module implementations. - votingRolesImpl = new AUT_EXT_VotingRoles_v1(); + votingRolesImpl = new AUT_EXT_VotingRoles_v2(); // Deploy module beacons. votingRolesBeacon = new InverterBeacon_v1( diff --git a/test/e2e/E2ETest.sol b/test/e2e/E2ETest.sol index 4825b61e6..2d9f7a748 100644 --- a/test/e2e/E2ETest.sol +++ b/test/e2e/E2ETest.sol @@ -26,21 +26,21 @@ import {InverterBeaconProxy_v1} from "src/proxies/InverterBeaconProxy_v1.sol"; import { ModuleFactory_v1, IModuleFactory_v1, - IModule_v1 + IModule_v2 } from "src/factories/ModuleFactory_v1.sol"; import { OrchestratorFactory_v1, IOrchestratorFactory_v1 } from "src/factories/OrchestratorFactory_v1.sol"; -// Orchestrator_v1 +// Orchestrator_v2 import { - Orchestrator_v1, - IOrchestrator_v1 -} from "src/orchestrator/Orchestrator_v1.sol"; + Orchestrator_v2, + IOrchestrator_v2 +} from "src/orchestrator/Orchestrator_v2.sol"; -import {IFM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; +import {IFM_BC_Bancor_Redeeming_VirtualSupply_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; // Mocks @@ -57,10 +57,10 @@ contract E2ETest is E2EModuleRegistry { // Factory instances. OrchestratorFactory_v1 orchestratorFactory; - // Orchestrator_v1 implementation. - Orchestrator_v1 orchestratorImpl; + // Orchestrator_v2 implementation. + Orchestrator_v2 orchestratorImpl; - // Beacon of the Orchestrator_v1 implementation + // Beacon of the Orchestrator_v2 implementation InverterBeacon_v1 orchestratorBeacon; // Mock token for funding. @@ -139,7 +139,7 @@ contract E2ETest is E2EModuleRegistry { // as the governor knows about it. moduleFactory.init( address(gov), - new IModule_v1.Metadata[](0), + new IModule_v2.Metadata[](0), new IInverterBeacon_v1[](0) ); @@ -150,8 +150,8 @@ contract E2ETest is E2EModuleRegistry { token = new ERC20Mock("Mock", "MOCK", 18); - // Deploy Orchestrator_v1 implementation. - orchestratorImpl = new Orchestrator_v1(address(forwarder)); + // Deploy Orchestrator_v2 implementation. + orchestratorImpl = new Orchestrator_v2(address(forwarder)); orchestratorBeacon = new InverterBeacon_v1( address(reverter), address(gov), 1, address(orchestratorImpl), 0, 0 @@ -194,7 +194,7 @@ contract E2ETest is E2EModuleRegistry { function _create_E2E_Orchestrator( IOrchestratorFactory_v1.WorkflowConfig memory _config, IOrchestratorFactory_v1.ModuleConfig[] memory _moduleConfigurations - ) internal virtual returns (IOrchestrator_v1) { + ) internal virtual returns (IOrchestrator_v2) { // Prepare array of optional modules (hopefully can be made more succinct in the future) uint amtOfOptionalModules = _moduleConfigurations.length - 3; diff --git a/test/e2e/ModuleTest_Template.txt b/test/e2e/ModuleTest_Template.txt index a1f482d65..45535abbe 100644 --- a/test/e2e/ModuleTest_Template.txt +++ b/test/e2e/ModuleTest_Template.txt @@ -4,11 +4,11 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // SuT -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // Internal Dependencies import { - E2ETest, IOrchestratorFactory_v1, IOrchestrator_v1 + E2ETest, IOrchestratorFactory_v1, IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; // Import modules that are used in this E2E test @@ -42,7 +42,7 @@ contract RoleAuthorizerE2E is E2ETest { function test_e2e_RoleAuthorizer() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = @@ -51,7 +51,7 @@ contract RoleAuthorizerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); //-------------------------------------------------------------------------- diff --git a/test/e2e/authorizer/RoleAuthorizerE2E.t.sol b/test/e2e/authorizer/RoleAuthorizerE2E.t.sol index 29091f255..c43ed8db4 100644 --- a/test/e2e/authorizer/RoleAuthorizerE2E.t.sol +++ b/test/e2e/authorizer/RoleAuthorizerE2E.t.sol @@ -4,29 +4,30 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // SuT -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // Internal Dependencies import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; // Modules that are used in this E2E test import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; import { - LM_PC_Bounties_v2, - ILM_PC_Bounties_v2, - IERC20PaymentClientBase_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, + ILM_PC_Bounties_v3, + IERC20PaymentClientBase_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; -contract RoleAuthorizerE2E is E2ETest { +contract RoleAuthorizerE2E1 is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. IOrchestratorFactory_v1.ModuleConfig[] moduleConfigurations; // E2E Test Variables - address orchestratorAdmin = makeAddr("orchestratorAdmin"); + address initialAdmin = makeAddr("initialAdmin"); + address bountyIssuer = makeAddr("bountyIssuer"); address bountyVerifier = makeAddr("bountyVerifier"); address bountySubmitter = makeAddr("bountySubmitter"); @@ -54,7 +55,8 @@ contract RoleAuthorizerE2E is E2ETest { setUpRoleAuthorizer(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - roleAuthorizerMetadata, abi.encode(address(this)) + roleAuthorizerMetadata, + abi.encode(initialAdmin) //this sets the given address to be the initialAdmin ) ); @@ -75,9 +77,9 @@ contract RoleAuthorizerE2E is E2ETest { ); } - function test_e2e_RoleAuthorizer() public { + function test_e2e_RoleAuthorizer(address caller_) public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -85,23 +87,23 @@ contract RoleAuthorizerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); FM_DepositVault_v1 fundingManager = FM_DepositVault_v1(address(orchestrator.fundingManager())); - AUT_Roles_v1 authorizer = - AUT_Roles_v1(address(orchestrator.authorizer())); + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); - // Find LM_PC_Bounties_v2 - LM_PC_Bounties_v2 bountyManager; + // Find LM_PC_Bounties_v3 + LM_PC_Bounties_v3 bountyManager; address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { - try ILM_PC_Bounties_v2(modulesList[i]).isExistingBountyId(0) + try ILM_PC_Bounties_v3(modulesList[i]).isExistingBountyId(0) returns (bool) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); + bountyManager = LM_PC_Bounties_v3(modulesList[i]); break; } catch { continue; @@ -109,34 +111,125 @@ contract RoleAuthorizerE2E is E2ETest { } //-------------------------------------------------------------------------- - // Assign Bounty Manager Roles + // Create Bounty Manager Roles //-------------------------------------------------------------------------- - // we authorize the Admin to create bounties - bountyManager.grantModuleRole( - bountyManager.BOUNTY_ISSUER_ROLE(), address(orchestratorAdmin) - ); - - // we authorize the manager to verify bounty claims - bountyManager.grantModuleRole( - bountyManager.VERIFIER_ROLE(), bountyVerifier - ); - - // we authorize the bountySubmitter to submit bounty claims - bountyManager.grantModuleRole( - bountyManager.CLAIMANT_ROLE(), address(bountySubmitter) - ); - - // Since we deploy the orchestrator, with address(this) as admin, - // we now assign them to two external addresses. In production these will be directly set on deployment. - - // we grant admin role to adminAddress - bytes32 adminRole = authorizer.getAdminRole(); - authorizer.grantRole(adminRole, address(orchestratorAdmin)); - authorizer.renounceRole(adminRole, address(this)); - assertTrue(authorizer.hasRole(adminRole, orchestratorAdmin)); - assertEq(authorizer.getRoleMemberCount(adminRole), 1); - + // This subsection divider is to prevent stack too deep errors + { + // First we define new Roles that we want to have in the system + // and assign initial members to them + + // BOUNTY_ISSUER Role + // Lets start with the Bounty Issuer Role + // For that we define a name + string memory roleName = "BOUNTY_ISSUER"; + // We define which role will be the admin of the new role + // (Admin Roles can add and remove members to that role) + // In this case we will use the initial admin role (number 0) + bytes32 roleAdmin = authorizer.getAdminRole(); // Alternatively bytes32(uint(0)) + + // We define the initial members of the role + address[] memory roleMembers = new address[](1); + roleMembers[0] = bountyIssuer; + + // With the above defined values we can now create the role + // We save the Id of the role in a variable + // The function is initally only callable by the initial admin + // but can be adapted like any other permissioned function (see down below) + vm.prank(initialAdmin); + bytes32 bountyIssuerRoleId = + authorizer.createRole(roleName, roleAdmin, roleMembers); + + // Public Role (Bounty Submitter) + // For the Bounty Submitter we choose the public role + // Which allows anyone to join the role + // We fetch the roleId of the public role from the authorizer + bytes32 bountySubmitterRoleId = authorizer.PUBLIC_ROLE(); // Alternatively bytes32(uint(1)) + + //-------------------------------------------------------------------------- + // Define Role Permissions + //-------------------------------------------------------------------------- + + // Now lets define the permissions for the roles + // Starting with the Bounty Issuer Role + // For this we will use the addAccessPermission function in the authorizer + + // First we define the target and function selector + // The target is the address of the bountyManager + address target = address(bountyManager); + // The function selector is the addBounty function + bytes4 selector = bountyManager.addBounty.selector; + // With the previously created role and respective id + // We can now add the permission to the bounty issuer role + vm.prank(initialAdmin); + authorizer.addAccessPermission(target, selector, bountyIssuerRoleId); + + // Lets check that the role holder is permissioned to call the function + assertTrue(authorizer.hasPermission(bountyIssuer, target, selector)); + + // Now lets add the Permission Bounty Submitter Role + // The selector here is the addClaim function + selector = bountyManager.addClaim.selector; + // As we defined before we want to make the function have public access + // so we use the public role as the given roleId + vm.prank(initialAdmin); + authorizer.addAccessPermission( + target, selector, bountySubmitterRoleId + ); + + // Now we check that the address bountySubmitter, which we never set as + // a member of the BOUNTY_ISSUER role, has the permission to call the function + assertTrue( + authorizer.hasPermission(bountySubmitter, target, selector) + ); + + //-------------------------------------------------------------------------- + // Creating Roles and Defining Role Permissions in the same Call + //-------------------------------------------------------------------------- + + // BOUNTY_VERIFIER Role + // For the creation of the BOUNTY_VERIFIER Role we will use + // the combined function createRoleAndAddAccessPermissions + // Which allows us to create and assign roles and permissions + // at the same time + + // We define the role name + roleName = "BOUNTY_VERIFIER"; + // We define the role admin + roleAdmin = authorizer.getAdminRole(); + // We define the initial members of the role + roleMembers = new address[](1); + roleMembers[0] = bountyVerifier; + + // Now we need to create two arrays + // The first one defines the contracts for which we want to assign permissions + address[] memory targets = new address[](1); + // We take the address of the bountyManager here + targets[0] = address(bountyManager); + // The second one defines the selectors for which we want to assign permissions + // This is a two dimensional array, where the first dimension is defines + // the target contract for which we want to assign permissions and the second + // dimension defines the function selectors of the target contract + // In this case we want only the bountyManager functions, so we define the + // array to be of length 1 + bytes4[][] memory selectors = new bytes4[][](1); + // And we define the single bountyManager function verifyClaim(), so we + // define the array to be of length 1 again + selectors[0] = new bytes4[](1); + // In case we wanted to select multiple functions, we would define the array + // to be the size of the number of functions we want to modify permissions for + // With that we can add the selector + selectors[0][0] = bountyManager.verifyClaim.selector; + + vm.prank(initialAdmin); + bytes32 bountyVerifierRoleId = authorizer + .createRoleAndAddAccessPermissions( + roleName, roleAdmin, roleMembers, targets, selectors + ); + + // Now we check that the role has been created + assertTrue(authorizer.hasRole(bountyVerifierRoleId, bountyVerifier)); + } //-------------------------------------------------------------------------- // Set up seed deposit and initial deposit by users //-------------------------------------------------------------------------- @@ -165,13 +258,13 @@ contract RoleAuthorizerE2E is E2ETest { uint maximumPayoutAmount = 500e18; bytes memory details = "This is a test bounty"; - vm.prank(orchestratorAdmin); + vm.prank(bountyIssuer); uint bountyId = bountyManager.addBounty( minimumPayoutAmount, maximumPayoutAmount, details ); // check that the bounty was created - ILM_PC_Bounties_v2.Bounty memory bounty = + ILM_PC_Bounties_v3.Bounty memory bounty = bountyManager.getBountyInformation(1); assertEq(bounty.minimumPayoutAmount, minimumPayoutAmount); assertEq(bounty.maximumPayoutAmount, maximumPayoutAmount); @@ -181,11 +274,11 @@ contract RoleAuthorizerE2E is E2ETest { // Worker submits bounty //-------------------------------------------------------------------------- vm.startPrank(bountySubmitter); - ILM_PC_Bounties_v2.Contributor memory BOB = - ILM_PC_Bounties_v2.Contributor(bountySubmitter, 200e18); + ILM_PC_Bounties_v3.Contributor memory BOB = + ILM_PC_Bounties_v3.Contributor(bountySubmitter, 200e18); - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](1); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](1); contribs[0] = BOB; uint claimId = bountyManager.addClaim( diff --git a/test/e2e/authorizer/TokenGatedRoleAuthorizerE2E.t.sol b/test/e2e/authorizer/TokenGatedRoleAuthorizerE2E.t.sol index 221e6aa1c..ef887275a 100644 --- a/test/e2e/authorizer/TokenGatedRoleAuthorizerE2E.t.sol +++ b/test/e2e/authorizer/TokenGatedRoleAuthorizerE2E.t.sol @@ -5,17 +5,17 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1, + IOrchestrator_v2, ERC20Mock } from "test/e2e/E2ETest.sol"; // SuT -import {AUT_TokenGated_Roles_v1} from "@aut/role/AUT_TokenGated_Roles_v1.sol"; +import {AUT_TokenGated_Roles_v2} from "@aut/role/AUT_TokenGated_Roles_v2.sol"; // Modules that are used in this E2E test import { - LM_PC_Bounties_v2, ILM_PC_Bounties_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, ILM_PC_Bounties_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; contract TokenGatedRoleAuthorizerE2E is E2ETest { @@ -76,7 +76,7 @@ contract TokenGatedRoleAuthorizerE2E is E2ETest { function test_e2e_TokenGatedRoleAuthorizer() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -84,23 +84,23 @@ contract TokenGatedRoleAuthorizerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); - AUT_TokenGated_Roles_v1 authorizer = - AUT_TokenGated_Roles_v1(address(orchestrator.authorizer())); + AUT_TokenGated_Roles_v2 authorizer = + AUT_TokenGated_Roles_v2(address(orchestrator.authorizer())); FM_DepositVault_v1 fundingManager = FM_DepositVault_v1(address(orchestrator.fundingManager())); - // Find LM_PC_Bounties_v2 - LM_PC_Bounties_v2 bountyManager; + // Find LM_PC_Bounties_v3 + LM_PC_Bounties_v3 bountyManager; address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { - try ILM_PC_Bounties_v2(modulesList[i]).isExistingBountyId(0) + try ILM_PC_Bounties_v3(modulesList[i]).isExistingBountyId(0) returns (bool) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); + bountyManager = LM_PC_Bounties_v3(modulesList[i]); break; } catch { continue; @@ -111,44 +111,77 @@ contract TokenGatedRoleAuthorizerE2E is E2ETest { // Set up Bounty Manager Roles with different thresholds //-------------------------------------------------------------------------- - // Give the Orchestrator_v1 Admin the power to change module roles + // Give the Orchestrator_v2 Admin the power to change module roles authorizer.grantRole(authorizer.DEFAULT_ADMIN_ROLE(), orchestratorAdmin); vm.startPrank(orchestratorAdmin); { - // Make the BOUNTY_ADMIN_ROLE token-gated by GATOR token and set the threshold - bytes32 bountyRoleId = authorizer.generateRoleId( - address(bountyManager), bountyManager.BOUNTY_ISSUER_ROLE() + // BOUNTY_ISSUER_ROLE + // Create the role + bytes32 bountyIssuerRoleId = authorizer.createRole( + "BOUNTY_ISSUER_ROLE", + authorizer.DEFAULT_ADMIN_ROLE(), + new address[](0) ); - authorizer.setTokenGated(bountyRoleId, true); - authorizer.setThreshold(bountyRoleId, address(gatingToken), 100); - authorizer.grantRole(bountyRoleId, address(gatingToken)); - // We mint 101 tokens to the orchestrator admin so they can create bounties - gatingToken.mint(orchestratorAdmin, 101); + authorizer.setTokenGated(bountyIssuerRoleId, true); + authorizer.setThreshold( + bountyIssuerRoleId, address(gatingToken), 100 + ); - // Make the VERIFY_ADMIN_ROLE token-gated by GATOR token and set the threshold - bytes32 verifierRoleId = authorizer.generateRoleId( - address(bountyManager), bountyManager.VERIFIER_ROLE() + // Now add the gating Token as a member of the role + // With this any holder of that token with a balance equal or higher than + // 100 will have permission to access the BOUNTY_ISSUER_ROLE functions + // In this case we actually only want the orchestrator admin to be able to call this + // As the default admin is always allowed to call permissioned functions + authorizer.grantRole(bountyIssuerRoleId, address(gatingToken)); + + // VERIFIER_ROLE + // Create the role + bytes32 verifierRoleId = authorizer.createRole( + "VERIFIER_ROLE", + authorizer.DEFAULT_ADMIN_ROLE(), + new address[](0) ); authorizer.setTokenGated(verifierRoleId, true); authorizer.setThreshold(verifierRoleId, address(gatingToken), 50); authorizer.grantRole(verifierRoleId, address(gatingToken)); - // We mint 51 tokens to the orchestrator manager so they can verify bounties - gatingToken.mint(bountyVerifier, 51); + // We mint 50 tokens to the orchestrator manager so they can verify bounties + gatingToken.mint(bountyVerifier, 50); - // Make the CLAIM_ADMIN_ROLE token-gated by GATOR token and set the threshold - bytes32 claimRoleId = authorizer.generateRoleId( - address(bountyManager), bountyManager.CLAIMANT_ROLE() + // CLAIMANT_ROLE + // Create the role + bytes32 claimRoleId = authorizer.createRole( + "CLAIMANT_ROLE", + authorizer.DEFAULT_ADMIN_ROLE(), + new address[](0) ); authorizer.setTokenGated(claimRoleId, true); authorizer.setThreshold(claimRoleId, address(gatingToken), 25); authorizer.grantRole(claimRoleId, address(gatingToken)); - // We mint 26 tokens to the bounty submitter so they can submit bounties - gatingToken.mint(bountySubmitter, 26); + // We mint 25 tokens to the bounty submitter so they can submit bounties + gatingToken.mint(bountySubmitter, 25); + + // Assign the correct permissions to the roles + authorizer.addAccessPermission( + address(bountyManager), + bountyManager.addBounty.selector, + bountyIssuerRoleId + ); + authorizer.addAccessPermission( + address(bountyManager), + bountyManager.addClaim.selector, + claimRoleId + ); + authorizer.addAccessPermission( + address(bountyManager), + bountyManager.verifyClaim.selector, + verifierRoleId + ); } + vm.stopPrank(); //-------------------------------------------------------------------------- @@ -188,7 +221,7 @@ contract TokenGatedRoleAuthorizerE2E is E2ETest { bountyManager.addBounty(100e18, 500e18, "This is a test bounty"); // Validate - ILM_PC_Bounties_v2.Bounty memory bounty = + ILM_PC_Bounties_v3.Bounty memory bounty = bountyManager.getBountyInformation(1); assertEq(bounty.minimumPayoutAmount, 100e18); assertEq(bounty.maximumPayoutAmount, 500e18); @@ -198,11 +231,11 @@ contract TokenGatedRoleAuthorizerE2E is E2ETest { // Worker submits bounty //-------------------------------------------------------------------------- vm.startPrank(bountySubmitter); - ILM_PC_Bounties_v2.Contributor memory BOB = - ILM_PC_Bounties_v2.Contributor(bountySubmitter, 200e18); + ILM_PC_Bounties_v3.Contributor memory BOB = + ILM_PC_Bounties_v3.Contributor(bountySubmitter, 200e18); - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](1); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](1); contribs[0] = BOB; uint claimId = bountyManager.addClaim( diff --git a/test/e2e/authorizer/extensions/VotingRoleManagerE2E.t.sol b/test/e2e/authorizer/extensions/VotingRoleManagerE2E.t.sol index a9af8d70b..12350ed50 100644 --- a/test/e2e/authorizer/extensions/VotingRoleManagerE2E.t.sol +++ b/test/e2e/authorizer/extensions/VotingRoleManagerE2E.t.sol @@ -5,20 +5,20 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; // Modules that are used in this E2E test -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; import { - LM_PC_Bounties_v2, ILM_PC_Bounties_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, ILM_PC_Bounties_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; // SuT import { - AUT_EXT_VotingRoles_v1, - IAUT_EXT_VotingRoles_v1 -} from "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol"; + AUT_EXT_VotingRoles_v2, + IAUT_EXT_VotingRoles_v2 +} from "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol"; contract VotingRoleManagerE2E is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. @@ -51,13 +51,12 @@ contract VotingRoleManagerE2E is E2ETest { ); // Authorizer - setUpTokenGatedRoleAuthorizer(); + setUpRoleAuthorizer(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - tokenRoleAuthorizerMetadata, abi.encode(address(this)) + roleAuthorizerMetadata, abi.encode(address(this)) ) ); - // PaymentProcessor setUpSimplePaymentProcessor(); moduleConfigurations.push( @@ -84,7 +83,7 @@ contract VotingRoleManagerE2E is E2ETest { function test_e2e_VotingRoles() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -92,51 +91,57 @@ contract VotingRoleManagerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); - AUT_Roles_v1 authorizer = - AUT_Roles_v1(address(orchestrator.authorizer())); - - // Find LM_PC_Bounties_v2 - LM_PC_Bounties_v2 bountyManager; + // Find LM_PC_Bounties_v3 + LM_PC_Bounties_v3 bountyManager; address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { - try ILM_PC_Bounties_v2(modulesList[i]).isExistingBountyId(0) + try ILM_PC_Bounties_v3(modulesList[i]).isExistingBountyId(0) returns (bool) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); + bountyManager = LM_PC_Bounties_v3(modulesList[i]); break; } catch { continue; } } - // Find AUT_EXT_VotingRoles_v1 - AUT_EXT_VotingRoles_v1 votingRoles; + // Find AUT_EXT_VotingRoles_v2 + AUT_EXT_VotingRoles_v2 votingRoles; for (uint i; i < modulesList.length; ++i) { - try IAUT_EXT_VotingRoles_v1(modulesList[i]).isVoter(address(0)) + try IAUT_EXT_VotingRoles_v2(modulesList[i]).isVoter(address(0)) returns (bool) { - votingRoles = AUT_EXT_VotingRoles_v1(modulesList[i]); + votingRoles = AUT_EXT_VotingRoles_v2(modulesList[i]); break; } catch { continue; } } - // We make the governor the only admin - bytes32 adminRole = authorizer.getAdminRole(); - authorizer.grantRole(adminRole, address(votingRoles)); - - // we authorize governance to create bounties - bountyManager.grantModuleRole( - bountyManager.BOUNTY_ISSUER_ROLE(), address(votingRoles) + // Create and assign role to create bounties for the governance contract + + // Members of the role + address[] memory roleMembers = new address[](1); + roleMembers[0] = address(votingRoles); + // Target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(bountyManager); + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = bountyManager.addBounty.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "BOUNTY_ISSUER", + orchestrator.authorizer().getAdminRole(), + roleMembers, + targets, + selectors ); - // By having address(this) renounce the Admin Role, all changes from now on need to go through the AUT_EXT_VotingRoles_v1 - authorizer.renounceRole(adminRole, address(this)); - //-------------------------------------------------------------------------- // Set up Vote to create Bounty //-------------------------------------------------------------------------- @@ -151,7 +156,7 @@ contract VotingRoleManagerE2E is E2ETest { bytes32 motionId = votingRoles.createMotion( address(bountyManager), abi.encodeWithSelector( - ILM_PC_Bounties_v2.addBounty.selector, + ILM_PC_Bounties_v3.addBounty.selector, minimumPayoutAmount, maximumPayoutAmount, details @@ -180,7 +185,7 @@ contract VotingRoleManagerE2E is E2ETest { vm.warp(block.timestamp + 2); // check that the bounty was created - ILM_PC_Bounties_v2.Bounty memory bounty = + ILM_PC_Bounties_v3.Bounty memory bounty = bountyManager.getBountyInformation(1); assertEq(bounty.minimumPayoutAmount, minimumPayoutAmount); assertEq(bounty.maximumPayoutAmount, maximumPayoutAmount); @@ -188,8 +193,8 @@ contract VotingRoleManagerE2E is E2ETest { } function _getMotionExecutionResult( - AUT_EXT_VotingRoles_v1 votingRoles, - bytes32 motionId + AUT_EXT_VotingRoles_v2 votingRoles_, + bytes32 motionId_ ) internal view returns (bool, bytes memory) { ( , // address _addr @@ -203,7 +208,7 @@ contract VotingRoleManagerE2E is E2ETest { , // uint _excAt bool _excRes, bytes memory _excData - ) = votingRoles.motions(motionId); + ) = votingRoles_.getMotion(motionId_); return (_excRes, _excData); } diff --git a/test/e2e/fundingManager/BondingCurveFundingManagerE2E.t.sol b/test/e2e/fundingManager/BondingCurveFundingManagerE2E.t.sol index 2ad65686e..ff4651021 100644 --- a/test/e2e/fundingManager/BondingCurveFundingManagerE2E.t.sol +++ b/test/e2e/fundingManager/BondingCurveFundingManagerE2E.t.sol @@ -7,19 +7,19 @@ import "forge-std/console.sol"; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; // SuT import { - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 -} from - "@unitTest/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; + FM_BC_Bancor_Redeeming_VirtualSupply_v2, + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 +} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; contract BondingCurveFundingManagerE2E is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. @@ -55,8 +55,8 @@ contract BondingCurveFundingManagerE2E is E2ETest { ); issuanceToken.setMinter(address(this), true); - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties memory - bc_properties = IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2.BondingCurveProperties memory + bc_properties = IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .BondingCurveProperties({ formula: address(formula), reserveRatioForBuying: 333_333, @@ -109,16 +109,33 @@ contract BondingCurveFundingManagerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); - FM_BC_Bancor_Redeeming_VirtualSupply_v1 fundingManager = - FM_BC_Bancor_Redeeming_VirtualSupply_v1( + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + + FM_BC_Bancor_Redeeming_VirtualSupply_v2 fundingManager = + FM_BC_Bancor_Redeeming_VirtualSupply_v2( address(orchestrator.fundingManager()) ); issuanceToken.setMinter(address(fundingManager), true); + // Set up Roles + // Make buy public + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + // Make sell public + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.sell.selector, + authorizer.PUBLIC_ROLE() + ); + // Mint some tokens to alice and bob in order to fund the fundingmanager. // Alice will perform a very big initial buy. diff --git a/test/e2e/fundingManager/BondingCurveTokenRescueE2E.t.sol b/test/e2e/fundingManager/BondingCurveTokenRescueE2E.t.sol index bcb3df7b6..7c7616f75 100644 --- a/test/e2e/fundingManager/BondingCurveTokenRescueE2E.t.sol +++ b/test/e2e/fundingManager/BondingCurveTokenRescueE2E.t.sol @@ -7,20 +7,21 @@ import "forge-std/console.sol"; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; + import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; -import {LM_PC_PaymentRouter_v2} from "@lm/LM_PC_PaymentRouter_v2.sol"; +import {LM_PC_PaymentRouter_v3} from "@lm/LM_PC_PaymentRouter_v3.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; // SuT import { - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 -} from - "@unitTest/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; + FM_BC_Bancor_Redeeming_VirtualSupply_v2, + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 +} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; contract BondingCurveTokenRescueE2E is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. @@ -28,7 +29,7 @@ contract BondingCurveTokenRescueE2E is E2ETest { ERC20Issuance_v1 issuanceToken; - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties + IFM_BC_Bancor_Redeeming_VirtualSupply_v2.BondingCurveProperties bc_properties; address alice = address(0xA11CE); @@ -56,7 +57,7 @@ contract BondingCurveTokenRescueE2E is E2ETest { ); issuanceToken.setMinter(address(this), true); - bc_properties = IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + bc_properties = IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .BondingCurveProperties({ formula: address(formula), reserveRatioForBuying: 333_333, @@ -101,16 +102,27 @@ contract BondingCurveTokenRescueE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); - FM_BC_Bancor_Redeeming_VirtualSupply_v1 fundingManager = - FM_BC_Bancor_Redeeming_VirtualSupply_v1( + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + + FM_BC_Bancor_Redeeming_VirtualSupply_v2 fundingManager = + FM_BC_Bancor_Redeeming_VirtualSupply_v2( address(orchestrator.fundingManager()) ); issuanceToken.setMinter(address(fundingManager), true); + // Set up Roles + // Make buy public + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + // Mint some tokens to alice in order to fund the fundingmanager. // Alice will perform a very big initial buy. @@ -177,12 +189,7 @@ contract BondingCurveTokenRescueE2E is E2ETest { // Transfer all collateral to the new BC - LM_PC_PaymentRouter_v2(paymentRouter).grantModuleRole( - LM_PC_PaymentRouter_v2(paymentRouter).PAYMENT_PUSHER_ROLE(), - address(this) - ); - - LM_PC_PaymentRouter_v2(paymentRouter).pushPayment( + LM_PC_PaymentRouter_v3(paymentRouter).pushPayment( newBondingCurve, // recipient address(token), // token // This represented the allowed amount of collateral token to be transferred @@ -194,16 +201,12 @@ contract BondingCurveTokenRescueE2E is E2ETest { ); // Initiate setting of new Funding Manager - orchestrator.initiateSetFundingManagerWithTimelock( - IFundingManager_v1(newBondingCurve) - ); + orchestrator.initiateSetFundingManagerWithTimelock(newBondingCurve); // wait for timelock to expire vm.warp(block.timestamp + 1 weeks); - orchestrator.executeSetFundingManager( - IFundingManager_v1(newBondingCurve) - ); + orchestrator.executeSetFundingManager(newBondingCurve); // Enable Minting again for new BC @@ -218,13 +221,21 @@ contract BondingCurveTokenRescueE2E is E2ETest { assertEq(address(orchestrator.fundingManager()), newBondingCurve); fundingManager = - FM_BC_Bancor_Redeeming_VirtualSupply_v1(newBondingCurve); + FM_BC_Bancor_Redeeming_VirtualSupply_v2(newBondingCurve); assertEq(oldIssuanceSupply, fundingManager.getVirtualIssuanceSupply()); assertEq( oldCollateralSupply, fundingManager.getVirtualCollateralSupply() ); + // Assign new Permissions to the new BC + + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + // Bob performs a buy address bob = address(0x606); uint bobBuyAmount = 5000e18; diff --git a/test/e2e/fundingManager/BondingSurfaceFundingManagerE2E.t.sol b/test/e2e/fundingManager/BondingSurfaceFundingManagerE2E.t.sol index 12c0dcb2b..7e09cb708 100644 --- a/test/e2e/fundingManager/BondingSurfaceFundingManagerE2E.t.sol +++ b/test/e2e/fundingManager/BondingSurfaceFundingManagerE2E.t.sol @@ -7,10 +7,12 @@ import "forge-std/console.sol"; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; + +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; @@ -20,15 +22,15 @@ import {ERC165Upgradeable} from // SuT import { - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, - IFM_BC_BondingSurface_Redeeming_v1 + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, + IFM_BC_BondingSurface_Redeeming_v2 } from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {IFM_EXT_TokenVault_v1} from - "@fm/extensions/interfaces/IFM_EXT_TokenVault_v1.sol"; + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; +import {IFM_EXT_TokenVault_v2} from + "@fm/extensions/interfaces/IFM_EXT_TokenVault_v2.sol"; contract BondingSurfaceFundingManagerE2E is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. @@ -70,8 +72,8 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { ); issuanceToken.setMinter(address(this), true); - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory - bc_properties = IFM_BC_BondingSurface_Redeeming_v1 + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory + bc_properties = IFM_BC_BondingSurface_Redeeming_v2 .BondingCurveProperties({ formula: address(bondingSurface), capitalRequired: 1_000_000 * 1e18, // Taken from Topos repo test case @@ -144,12 +146,15 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 fundingManager = - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1( + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2( address(orchestrator.fundingManager()) ); @@ -159,7 +164,7 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(IFM_EXT_TokenVault_v1).interfaceId + type(IFM_EXT_TokenVault_v2).interfaceId ) ) { tokenVault = modulesList[i]; @@ -176,15 +181,84 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { fundingManager.setTokenVault(address(tokenVault)); // Set Roles - fundingManager.grantModuleRole( - fundingManager.RISK_MANAGER_ROLE(), riskManager - ); - fundingManager.grantModuleRole( - fundingManager.COVER_MANAGER_ROLE(), coverManager - ); - fundingManager.grantModuleRole( - fundingManager.CURVE_INTERACTION_ROLE(), curveUser - ); + { + // Create and assign role for the riskManager + + // Members of the role + address[] memory roleMembers = new address[](1); + roleMembers[0] = riskManager; + // Target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(fundingManager); + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](2); + selectors[0][0] = fundingManager.setCapitalRequired.selector; + selectors[0][1] = fundingManager.setBasePriceMultiplier.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "RISK_MANAGER_ROLE", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors + ); + + // Create and assign role for the coverManager + + // Members of the role + roleMembers = new address[](1); + roleMembers[0] = coverManager; + // Target contract and function selectors + targets = new address[](1); + targets[0] = address(fundingManager); + selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = fundingManager.seize.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "COVER_MANAGER_ROLE", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors + ); + + // Create and assign role for the curveUser + + // Members of the role + roleMembers = new address[](1); + roleMembers[0] = curveUser; + // Target contract and function selectors + targets = new address[](1); + targets[0] = address(fundingManager); + selectors = new bytes4[][](1); + selectors[0] = new bytes4[](2); + selectors[0][0] = fundingManager.buy.selector; + selectors[0][1] = fundingManager.sell.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "CURVE_USER_ROLE", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors + ); + + // Buy and sell should be public initially + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.sell.selector, + authorizer.PUBLIC_ROLE() + ); + } //-------------------------------------------------------------------------------- // Setup @@ -201,22 +275,38 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { //-------------------------------------------------------------------------------- // Buy and Sell Restrictions - // Check for that buy and sell is not restricted - assertEq(fundingManager.isBuyAndSellRestricted(), false); + // Check for that buy and sell is public initially + assertEq( + authorizer.hasPermission( + address(0), address(fundingManager), fundingManager.buy.selector + ), + true + ); + assertEq( + authorizer.hasPermission( + address(0), + address(fundingManager), + fundingManager.sell.selector + ), + true + ); - // Restrict Buy and Sell - vm.prank(coverManager); - fundingManager.restrictBuyAndSell(); + // Restrict Buy and Sell by removing the public role + authorizer.removeAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.removeAccessPermission( + address(fundingManager), + fundingManager.sell.selector, + authorizer.PUBLIC_ROLE() + ); // Check that the buy and sell functionalities dont work anymore for a regular user vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - orchestrator.authorizer().generateRoleId( - address(fundingManager), - fundingManager.CURVE_INTERACTION_ROLE() - ), - alice + IModule_v2.Module__CallerNotPermissioned.selector ) ); vm.prank(alice); @@ -224,12 +314,7 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - orchestrator.authorizer().generateRoleId( - address(fundingManager), - fundingManager.CURVE_INTERACTION_ROLE() - ), - alice + IModule_v2.Module__CallerNotPermissioned.selector ) ); vm.prank(alice); @@ -248,9 +333,17 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { vm.prank(curveUser); fundingManager.sell(curveUserSellAmount, 1); - // Open up functions again - vm.prank(coverManager); - fundingManager.unrestrictBuyAndSell(); + // Open up functions again by making them public again + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.buy.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.addAccessPermission( + address(fundingManager), + fundingManager.sell.selector, + authorizer.PUBLIC_ROLE() + ); //-------------------------------------------------------------------------------- // Transfer Repayment @@ -294,8 +387,8 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { // Check that seize cant be triggered again unditl Seize Delay is not reached vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__SeizeTimeout + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__SeizeTimeout .selector, block.timestamp + fundingManager.SEIZE_DELAY() ) @@ -404,7 +497,7 @@ contract BondingSurfaceFundingManagerE2E is E2ETest { assertTrue(feeAmount > 0); // Withdraw fee from token vault - IFM_EXT_TokenVault_v1(tokenVault).withdraw( + IFM_EXT_TokenVault_v2(tokenVault).withdraw( address(token), feeAmount, feeReceiver ); diff --git a/test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.sol b/test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.t.sol similarity index 71% rename from test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.sol rename to test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.t.sol index a7656fb99..1a3040107 100644 --- a/test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.sol +++ b/test/e2e/fundingManager/oracle/OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E.t.sol @@ -5,29 +5,29 @@ import "forge-std/Test.sol"; import {Vm, VmSafe} from "forge-std/Vm.sol"; // SuT -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // Internal Dependencies import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; // Import modules that are used in this E2E test import { - PP_Queue_ManualExecution_v1, - IPP_Queue_ManualExecution_v1 -} from "@pp/PP_Queue_ManualExecution_v1.sol"; + PP_Queue_ManualExecution_v2, + IPP_Queue_ManualExecution_v2 +} from "@pp/PP_Queue_ManualExecution_v2.sol"; import { - FM_PC_Oracle_Redeeming_v1, - IFM_PC_Oracle_Redeeming_v1 -} from "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol"; + FM_PC_Oracle_Redeeming_v2, + IFM_PC_Oracle_Redeeming_v2 +} from "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol"; import { - LM_Oracle_Permissioned_v1, - ILM_Oracle_Permissioned_v1 -} from "src/modules/logicModule/LM_Oracle_Permissioned_v1.sol"; + LM_Oracle_Permissioned_v2, + ILM_Oracle_Permissioned_v2 +} from "src/modules/logicModule/LM_Oracle_Permissioned_v2.sol"; import {ERC20Issuance_Blacklist_v1} from "@ex/token/ERC20Issuance_Blacklist_v1.sol"; @@ -38,8 +38,8 @@ import {InverterBeacon_v1} from "src/proxies/InverterBeacon_v1.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is E2ETest @@ -113,11 +113,11 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is // Contracts ERC20Mock collateralToken; ERC20Issuance_Blacklist_v1 issuanceToken; - FM_PC_Oracle_Redeeming_v1 fundingManager; - PP_Queue_ManualExecution_v1 paymentProcessor; - AUT_Roles_v1 authorizer; - LM_Oracle_Permissioned_v1 permissionedOracle; - IOrchestrator_v1 orchestrator; + FM_PC_Oracle_Redeeming_v2 fundingManager; + PP_Queue_ManualExecution_v2 paymentProcessor; + AUT_Roles_v2 authorizer; + LM_Oracle_Permissioned_v2 permissionedOracle; + IOrchestrator_v2 orchestrator; // Define struct to hold all event parameters struct RedemptionOrderCreatedEventData { @@ -132,7 +132,7 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is uint protocolFeeAmount_; uint finalRedemptionAmount_; address collateralToken_; - IFM_PC_Oracle_Redeeming_v1.RedemptionState state_; + IFM_PC_Oracle_Redeeming_v2.RedemptionState state_; } function setUp() public override { @@ -202,7 +202,7 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is function _init() internal { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -213,12 +213,14 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); + authorizer = AUT_Roles_v2(address(orchestrator.authorizer())); + // Get funding manager fundingManager = - FM_PC_Oracle_Redeeming_v1(address(orchestrator.fundingManager())); + FM_PC_Oracle_Redeeming_v2(address(orchestrator.fundingManager())); // Get payment processor - paymentProcessor = PP_Queue_ManualExecution_v1( + paymentProcessor = PP_Queue_ManualExecution_v2( address(orchestrator.paymentProcessor()) ); @@ -227,10 +229,10 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_Oracle_Permissioned_v1).interfaceId + type(ILM_Oracle_Permissioned_v2).interfaceId ) ) { - permissionedOracle = LM_Oracle_Permissioned_v1(modulesList[i]); + permissionedOracle = LM_Oracle_Permissioned_v2(modulesList[i]); break; } } @@ -245,102 +247,156 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is function _setRoles() internal { //-------------------------------------------------------------------------- - // Set role admins roles in the system + // Set roles and their admin roles in the system - bytes32 roleId; - bytes32 adminRoleId; + { + //Here we create the different roles and set their initial members + // We remember that the role id is just counting up from 1 + // 0 is the default admin role and 1 is the public role + // The create Role function can only be called by permissioned addresses + // In this case only the default admin can call it - // Set role admin for price setter role - roleId = orchestrator.authorizer().generateRoleId( - address(permissionedOracle), permissionedOracle.getPriceSetterRole() - ); - adminRoleId = orchestrator.authorizer().generateRoleId( - address(permissionedOracle), - permissionedOracle.getPriceSetterRoleAdmin() - ); - orchestrator.authorizer().transferAdminRole(roleId, adminRoleId); + string memory roleName; + bytes32 roleAdmin; + address[] memory roleMembers; - // Set role admin for queue operator role - roleId = orchestrator.authorizer().generateRoleId( - address(paymentProcessor), paymentProcessor.getQueueOperatorRole() - ); - adminRoleId = orchestrator.authorizer().generateRoleId( - address(paymentProcessor), - paymentProcessor.getQueueOperatorRoleAdmin() - ); - orchestrator.authorizer().transferAdminRole(roleId, adminRoleId); + //PRICE_SETTER_ROLE_ADMIN + roleName = "PRICE_SETTER_ROLE_ADMIN"; + roleAdmin = bytes32(0); // The default admin + roleMembers = new address[](1); + roleMembers[0] = priceSetterRoleAdmin; - // Set role admin for whitelist role - roleId = orchestrator.authorizer().generateRoleId( - address(fundingManager), fundingManager.getWhitelistRole() - ); - adminRoleId = orchestrator.authorizer().generateRoleId( - address(fundingManager), fundingManager.getWhitelistRoleAdmin() - ); - orchestrator.authorizer().transferAdminRole(roleId, adminRoleId); + authorizer.createRole(roleName, roleAdmin, roleMembers); - // Set role admin for queue executor role - roleId = orchestrator.authorizer().generateRoleId( - address(fundingManager), fundingManager.getQueueExecutorRole() - ); - adminRoleId = orchestrator.authorizer().generateRoleId( - address(fundingManager), fundingManager.getQueueExecutorRoleAdmin() - ); - orchestrator.authorizer().transferAdminRole(roleId, adminRoleId); + //PRICE_SETTER_ROLE + roleName = "PRICE_SETTER_ROLE"; + roleAdmin = bytes32(uint(2)); // The newly created role Price Setter admin + roleMembers = new address[](0); // No initial members as we want for the admins to set them - //-------------------------------------------------------------------------- - // Assign role admin roles + authorizer.createRole(roleName, roleAdmin, roleMembers); - // Assign price setter role admin - permissionedOracle.grantModuleRole( - permissionedOracle.getPriceSetterRoleAdmin(), priceSetterRoleAdmin - ); + //QUEUE_OPERATOR_ROLE_ADMIN + roleName = "QUEUE_OPERATOR_ROLE_ADMIN"; + roleAdmin = bytes32(0); // The default admin + roleMembers = new address[](1); + roleMembers[0] = queueOperatorRoleAdmin; - // Assign queue operator role admin - paymentProcessor.grantModuleRole( - paymentProcessor.getQueueOperatorRoleAdmin(), queueOperatorRoleAdmin - ); + authorizer.createRole(roleName, roleAdmin, roleMembers); - // Assign whitelist role admin - fundingManager.grantModuleRole( - fundingManager.getWhitelistRoleAdmin(), whitelistRoleAdmin - ); + //QUEUE_OPERATOR_ROLE + roleName = "QUEUE_OPERATOR_ROLE"; + roleAdmin = bytes32(uint(4)); // The newly created role Queue Operator admin + roleMembers = new address[](0); // No initial members as we want for the admins to set them - // Assign queue executor role admin - fundingManager.grantModuleRole( - fundingManager.getQueueExecutorRoleAdmin(), queueExecutorRoleAdmin - ); + authorizer.createRole(roleName, roleAdmin, roleMembers); + + //WHITELIST_ROLE_ADMIN + roleName = "WHITELIST_ROLE_ADMIN"; + roleAdmin = bytes32(0); // The default admin + roleMembers = new address[](1); + roleMembers[0] = whitelistRoleAdmin; + + authorizer.createRole(roleName, roleAdmin, roleMembers); + + //WHITELIST_ROLE + roleName = "WHITELIST_ROLE"; + roleAdmin = bytes32(uint(6)); // The newly created role Whitelist admin + roleMembers = new address[](0); // No initial members as we want for the admins to set them + authorizer.createRole(roleName, roleAdmin, roleMembers); + + //QUEUE_EXECUTOR_ROLE_ADMIN + roleName = "QUEUE_EXECUTOR_ROLE_ADMIN"; + roleAdmin = bytes32(0); // The default admin + roleMembers = new address[](1); + roleMembers[0] = queueExecutorRoleAdmin; + + authorizer.createRole(roleName, roleAdmin, roleMembers); + + //QUEUE_EXECUTOR_ROLE + roleName = "QUEUE_EXECUTOR_ROLE"; + roleAdmin = bytes32(uint(8)); // The newly created role Queue Executor admin + roleMembers = new address[](0); // No initial members as we want for the admins to set them + + authorizer.createRole(roleName, roleAdmin, roleMembers); + } + + //-------------------------------------------------------------------------- + // Add Access Permissions to the roles + { + // The addAccessPermission function can only be called by permissioned addresses + // In this case only the default admin can call it + + address target; + bytes4 selector; + bytes32 roleId; + + //PRICE_SETTER_ROLE - setIssuancePrice + target = address(permissionedOracle); + selector = permissionedOracle.setIssuancePrice.selector; + roleId = bytes32(uint(3)); + authorizer.addAccessPermission(target, selector, roleId); + + //PRICE_SETTER_ROLE - setRedemptionPrice + selector = permissionedOracle.setRedemptionPrice.selector; + authorizer.addAccessPermission(target, selector, roleId); + + //PRICE_SETTER_ROLE - setIssuanceAndRedemptionPrice + selector = permissionedOracle.setIssuanceAndRedemptionPrice.selector; + authorizer.addAccessPermission(target, selector, roleId); + + //QUEUE_OPERATOR_ROLE - processPayments + target = address(paymentProcessor); + selector = + paymentProcessor.claimPreviouslyUnclaimableToTreasury.selector; + roleId = bytes32(uint(5)); + authorizer.addAccessPermission(target, selector, roleId); + + //QUEUE_OPERATOR_ROLE - cancelPaymentOrderThroughQueueId + selector = + paymentProcessor.cancelPaymentOrderThroughQueueId.selector; + authorizer.addAccessPermission(target, selector, roleId); + + //WHITELIST_ROLE - buy + target = address(fundingManager); + selector = fundingManager.buy.selector; + authorizer.addAccessPermission(target, selector, bytes32(uint(7))); + + //WHITELIST_ROLE - buyFor + selector = fundingManager.buyFor.selector; + authorizer.addAccessPermission(target, selector, bytes32(uint(7))); + + //WHITELIST_ROLE - sell + selector = fundingManager.sell.selector; + authorizer.addAccessPermission(target, selector, bytes32(uint(7))); + + //WHITELIST_ROLE - sellTo + selector = fundingManager.sellTo.selector; + authorizer.addAccessPermission(target, selector, bytes32(uint(7))); + + //QUEUE_EXECUTOR_ROLE - executeRedemptionQueue + target = address(fundingManager); + selector = fundingManager.executeRedemptionQueue.selector; + authorizer.addAccessPermission(target, selector, bytes32(uint(9))); + } //-------------------------------------------------------------------------- // Assign roles through admins - // Assign price setter role - vm.startPrank(priceSetterRoleAdmin); - permissionedOracle.grantModuleRole( - permissionedOracle.getPriceSetterRole(), priceSetter - ); - vm.stopPrank(); + // Price Setter + vm.prank(priceSetterRoleAdmin); + authorizer.grantRole(bytes32(uint(3)), priceSetter); - // Assign queue operator role - vm.startPrank(queueOperatorRoleAdmin); - paymentProcessor.grantModuleRole( - paymentProcessor.getQueueOperatorRole(), queueOperator - ); - vm.stopPrank(); + // Queue Operator + vm.prank(queueOperatorRoleAdmin); + authorizer.grantRole(bytes32(uint(5)), queueOperator); - // Assign whitelist role - vm.startPrank(whitelistRoleAdmin); - fundingManager.grantModuleRole( - fundingManager.getWhitelistRole(), whitelistedUser - ); - vm.stopPrank(); + // Whitelist + vm.prank(whitelistRoleAdmin); + authorizer.grantRole(bytes32(uint(7)), whitelistedUser); - // Assign queue executor role - vm.startPrank(queueExecutorRoleAdmin); - fundingManager.grantModuleRole( - fundingManager.getQueueExecutorRole(), queueExecutor - ); - vm.stopPrank(); + // Queue Executor + vm.prank(queueExecutorRoleAdmin); + authorizer.grantRole(bytes32(uint(9)), queueExecutor); //-------------------------------------------------------------------------- // Assign other roles in the system @@ -449,9 +505,9 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is uint orderId = data.orderId_; // Get order from payment processor - IPP_Queue_ManualExecution_v1.QueuedOrder memory order = + IPP_Queue_ManualExecution_v2.QueuedOrder memory order = paymentProcessor.getOrder(orderId, fundingManager); - IERC20PaymentClientBase_v2.PaymentOrder memory paymentOrder = + IERC20PaymentClientBase_v3.PaymentOrder memory paymentOrder = order.order_; // verify data from the payment order == data from the event, @@ -579,7 +635,7 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is uint protocolFeeAmount, uint finalRedemptionAmount, address collateralToken_, - IFM_PC_Oracle_Redeeming_v1.RedemptionState state + IFM_PC_Oracle_Redeeming_v2.RedemptionState state ) = abi.decode( entry.data, ( @@ -591,7 +647,7 @@ contract OracleFundingManagerAndManualQueueBasedPaymentProcessorE2E is uint, uint, address, - IFM_PC_Oracle_Redeeming_v1.RedemptionState + IFM_PC_Oracle_Redeeming_v2.RedemptionState ) ); diff --git a/test/e2e/logicModule/BountyManagerE2E.t.sol b/test/e2e/logicModule/BountyManagerE2E.t.sol index e511f1568..40d290e5c 100644 --- a/test/e2e/logicModule/BountyManagerE2E.t.sol +++ b/test/e2e/logicModule/BountyManagerE2E.t.sol @@ -5,13 +5,14 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // SuT import { - LM_PC_Bounties_v2, ILM_PC_Bounties_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, ILM_PC_Bounties_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; @@ -79,7 +80,7 @@ contract BountyManagerE2E is E2ETest { function test_e2e_BountyManagerLifecycle() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -87,30 +88,88 @@ contract BountyManagerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); FM_DepositVault_v1 fundingManager = FM_DepositVault_v1(address(orchestrator.fundingManager())); - LM_PC_Bounties_v2 bountyManager; + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + + LM_PC_Bounties_v3 bountyManager; address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_PC_Bounties_v2).interfaceId + type(ILM_PC_Bounties_v3).interfaceId ) ) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); + bountyManager = LM_PC_Bounties_v3(modulesList[i]); break; } } - // we authorize the deployer of the orchestrator as the bounty admin - bountyManager.grantModuleRole( - bountyManager.BOUNTY_ISSUER_ROLE(), address(this) + // ========= + // Setting up Roles + + // In the upcoming section we will use different permissioned functions + // For which we need to create roles, add function access and assign the roles + // to the different actors + + // The main functions that we will use are: + // - addBounty + // - addClaim + // - verifyClaim + // For demonstration purposes we will set up the roles in reverse order + + // verifyClaim + // for this function we will set up the VERIFIER role + // First we define the Members of the role in an array + // Verifiers approve claim + + address verifier1 = makeAddr("verifier 1"); + + { + address[] memory roleMembers = new address[](1); + roleMembers[0] = verifier1; + + // Then we select the target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(bountyManager); + + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = bountyManager.verifyClaim.selector; + + // Create role, adapt permissions and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "VERIFIER", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors + ); + } + + // addClaim + // Instead of a assigning a role to this function we will make it public + // so that anyone can call it + + authorizer.addAccessPermission( + address(bountyManager), + bountyManager.addClaim.selector, + authorizer.PUBLIC_ROLE() ); + + // addBounty + // addBounty allows the caller to create a new bounty + // This could be a high level admin function so we will only allow the workflow admin to call it + // As the initial admin of the workflow already has access to all permissioned functions + // we wont set up anything for the access to work + // The initial admin in this case is this contract itself, so we dont need to prank the calls + // Funders deposit funds // IMPORTANT @@ -146,18 +205,13 @@ contract BountyManagerE2E is E2ETest { ); // Workers submit bounty - ILM_PC_Bounties_v2.Contributor memory contrib1 = - ILM_PC_Bounties_v2.Contributor(address(0xA11CE), 150e18); - ILM_PC_Bounties_v2.Contributor memory contrib2 = - ILM_PC_Bounties_v2.Contributor(address(0xb0b), 150e18); - - // auth.setIsAuthorized(address(0xA11CE), true); - bountyManager.grantModuleRole( - bountyManager.CLAIMANT_ROLE(), address(0xA11CE) - ); + ILM_PC_Bounties_v3.Contributor memory contrib1 = + ILM_PC_Bounties_v3.Contributor(address(0xA11CE), 150e18); + ILM_PC_Bounties_v3.Contributor memory contrib2 = + ILM_PC_Bounties_v3.Contributor(address(0xb0b), 150e18); - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](2); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](2); contribs[0] = contrib1; contribs[1] = contrib2; @@ -166,13 +220,6 @@ contract BountyManagerE2E is E2ETest { vm.prank(address(0xA11CE)); uint claimId = bountyManager.addClaim(bountyId, contribs, claimDetails); - // Verifiers approve claim - - address verifier1 = makeAddr("verifier 1"); - - // auth.setIsAuthorized(verifier1, true); - bountyManager.grantModuleRole(bountyManager.VERIFIER_ROLE(), verifier1); - vm.prank(verifier1); bountyManager.verifyClaim(claimId, contribs); diff --git a/test/e2e/logicModule/KPIRewarderLifecycle.t.sol b/test/e2e/logicModule/KPIRewarderLifecycle.t.sol index 50195e6db..181eb32db 100644 --- a/test/e2e/logicModule/KPIRewarderLifecycle.t.sol +++ b/test/e2e/logicModule/KPIRewarderLifecycle.t.sol @@ -5,24 +5,24 @@ import {E2ETest} from "test/e2e/E2ETest.sol"; import "forge-std/console.sol"; // Internal Dependencies -import {ModuleTest, IOrchestrator_v1} from "@unitTest/modules/ModuleTest.sol"; -import {IModule_v1, ERC165Upgradeable} from "src/modules/base/Module_v1.sol"; +import {ModuleTest, IOrchestrator_v2} from "@unitTest/modules/ModuleTest.sol"; +import {IModule_v2, ERC165Upgradeable} from "src/modules/base/Module_v2.sol"; import {IOrchestratorFactory_v1} from "src/factories/OrchestratorFactory_v1.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; -import {PP_Simple_v2, IPaymentProcessor_v2} from "@pp/PP_Simple_v2.sol"; +import {PP_Simple_v3, IPaymentProcessor_v3} from "@pp/PP_Simple_v3.sol"; import { - LM_PC_KPIRewarder_v2, - ILM_PC_KPIRewarder_v2, - IOptimisticOracleIntegrator, - ILM_PC_Staking_v2 -} from "src/modules/logicModule/LM_PC_KPIRewarder_v2.sol"; + LM_PC_KPIRewarder_v3, + ILM_PC_KPIRewarder_v3, + IOptimisticOracleIntegrator_v3, + ILM_PC_Staking_v3 +} from "src/modules/logicModule/LM_PC_KPIRewarder_v3.sol"; import {OptimisticOracleV3Interface} from "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/optimistic-oracle-v3/interfaces/OptimisticOracleV3Interface.sol"; @@ -43,7 +43,7 @@ Fork testing necessary. Make sure to have a sepolia rpc configured in foundry.to */ -contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { +contract LM_PC_KPIRewarder_v3Lifecycle is E2ETest { /* - This needs to be a fork test using an actual UMA instance. - Where are the UMA test deployments? => https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks @@ -80,9 +80,10 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { // Module Configurations for the current E2E test. Should be filled during setUp() call. IOrchestratorFactory_v1.ModuleConfig[] moduleConfigurations; - IOrchestrator_v1 orchestrator; + IOrchestrator_v2 orchestrator; FM_DepositVault_v1 fundingManager; - LM_PC_KPIRewarder_v2 kpiRewarder; + AUT_Roles_v2 authorizer; + LM_PC_KPIRewarder_v3 kpiRewarder; ERC20Mock USDC; ERC20Mock rewardToken; @@ -210,10 +211,10 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { // KPI Rewarder - setUpLM_PC_KPIRewarder_v2(); + setUpLM_PC_KPIRewarder_v3(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - LM_PC_KPIRewarder_v2Metadata, + LM_PC_KPIRewarder_v3Metadata, abi.encode( address(stakingToken), USDC_address, @@ -227,7 +228,7 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { ); } - function test_e2e_LM_PC_KPIRewarder_v2Lifecycle() public { + function test_e2e_LM_PC_KPIRewarder_v3Lifecycle() public { // NOTE: Temporary skip if the rpc is failing. if (skipTestsWithFailingRpc) { return; @@ -245,6 +246,8 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); + authorizer = AUT_Roles_v2(address(orchestrator.authorizer())); + fundingManager = FM_DepositVault_v1(address(orchestrator.fundingManager())); @@ -253,10 +256,10 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_PC_KPIRewarder_v2).interfaceId + type(ILM_PC_KPIRewarder_v3).interfaceId ) ) { - kpiRewarder = LM_PC_KPIRewarder_v2(modulesList[i]); + kpiRewarder = LM_PC_KPIRewarder_v3(modulesList[i]); break; } } @@ -279,7 +282,7 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { _setupUSDC(); // give the automation service the rights to post assertions - _prepareLM_PC_KPIRewarder_v2(); + _prepareLM_PC_KPIRewarder_v3(); // Initialize kpiRewarder setup: rewardToken.mint(address(this), REWARD_DEPOSIT_AMOUNT); @@ -408,7 +411,7 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { //-------------------------------------------------------------------------- function _getExpectedRewardAmount( - ILM_PC_KPIRewarder_v2.KPI memory resolvedKPI, + ILM_PC_KPIRewarder_v3.KPI memory resolvedKPI, uint assertedValue ) internal pure returns (uint) { uint rewardAmount; @@ -471,10 +474,46 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { ); } - function _prepareLM_PC_KPIRewarder_v2() internal { - kpiRewarder.grantModuleRole( - kpiRewarder.ASSERTER_ROLE(), AUTOMATION_SERVICE - ); + function _prepareLM_PC_KPIRewarder_v3() internal { + { + address[] memory roleMembers = new address[](1); + roleMembers[0] = AUTOMATION_SERVICE; + + // Then we select the target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(kpiRewarder); + + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = kpiRewarder.postAssertion.selector; + + // Create role, adapt permissions and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "ASSERTER_ROLE", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors + ); + + // make stake, unstake, claimRewards public + authorizer.addAccessPermission( + address(kpiRewarder), + kpiRewarder.stake.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.addAccessPermission( + address(kpiRewarder), + kpiRewarder.unstake.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.addAccessPermission( + address(kpiRewarder), + kpiRewarder.claimRewards.selector, + authorizer.PUBLIC_ROLE() + ); + } + _createDummyContinuousKPI(address(kpiRewarder)); } @@ -488,7 +527,7 @@ contract LM_PC_KPIRewarder_v2Lifecycle is E2ETest { _trancheRewards[i] = trancheRewards[i]; } - ILM_PC_KPIRewarder_v2(kpiManager).createKPI( + ILM_PC_KPIRewarder_v3(kpiManager).createKPI( true, _trancheValues, _trancheRewards ); } diff --git a/test/e2e/logicModule/RecurringPaymentManagerE2E.t.sol b/test/e2e/logicModule/RecurringPaymentManagerE2E.t.sol index 1f8507350..43a5adf0d 100644 --- a/test/e2e/logicModule/RecurringPaymentManagerE2E.t.sol +++ b/test/e2e/logicModule/RecurringPaymentManagerE2E.t.sol @@ -5,22 +5,22 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; // SuT import { - LM_PC_RecurringPayments_v2, - ILM_PC_RecurringPayments_v2, - IERC20PaymentClientBase_v2 -} from "@lm/LM_PC_RecurringPayments_v2.sol"; + LM_PC_RecurringPayments_v3, + ILM_PC_RecurringPayments_v3, + IERC20PaymentClientBase_v3 +} from "@lm/LM_PC_RecurringPayments_v3.sol"; // Modules that are used in this E2E test import { - PP_Streaming_v2, - IPP_Streaming_v2, - IERC20PaymentClientBase_v2 -} from "src/modules/paymentProcessor/PP_Streaming_v2.sol"; + PP_Streaming_v3, + IPP_Streaming_v3, + IERC20PaymentClientBase_v3 +} from "src/modules/paymentProcessor/PP_Streaming_v3.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; @@ -99,10 +99,10 @@ contract RecurringPaymentManagerE2E is E2ETest { function test_e2e_RecurringPayments(uint paymentAmount) public { paymentAmount = bound(paymentAmount, 1, 1e18); - LM_PC_RecurringPayments_v2 recurringPaymentManager; + LM_PC_RecurringPayments_v3 recurringPaymentManager; //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -110,7 +110,7 @@ contract RecurringPaymentManagerE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); FM_DepositVault_v1 fundingManager = @@ -121,11 +121,11 @@ contract RecurringPaymentManagerE2E is E2ETest { for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_PC_RecurringPayments_v2).interfaceId + type(ILM_PC_RecurringPayments_v3).interfaceId ) ) { recurringPaymentManager = - LM_PC_RecurringPayments_v2(modulesList[i]); + LM_PC_RecurringPayments_v3(modulesList[i]); break; } } @@ -173,12 +173,12 @@ contract RecurringPaymentManagerE2E is E2ETest { // 4. Let the paymentReceivers claim their vested tokens /// Let's first find the address of the streamingPaymentProcessor - PP_Streaming_v2 streamingPaymentProcessor; + PP_Streaming_v3 streamingPaymentProcessor; for (uint i; i < modulesList.length; ++i) { - try IPP_Streaming_v2(modulesList[i]).unclaimable( + try IPP_Streaming_v3(modulesList[i]).unclaimable( paymentReceiver1, address(token), paymentReceiver2 ) returns (uint) { - streamingPaymentProcessor = PP_Streaming_v2(modulesList[i]); + streamingPaymentProcessor = PP_Streaming_v3(modulesList[i]); break; } catch { continue; @@ -186,7 +186,7 @@ contract RecurringPaymentManagerE2E is E2ETest { } // Checking whether we got the right address for streamingPaymentProcessor - IPP_Streaming_v2.Stream[] memory streams = streamingPaymentProcessor + IPP_Streaming_v3.Stream[] memory streams = streamingPaymentProcessor .viewAllPaymentOrders( address(recurringPaymentManager), paymentReceiver1 ); diff --git a/test/e2e/logicModule/StakingManagerLifecycle.t.sol b/test/e2e/logicModule/StakingManagerLifecycle.t.sol index efa750755..eb97436d2 100644 --- a/test/e2e/logicModule/StakingManagerLifecycle.t.sol +++ b/test/e2e/logicModule/StakingManagerLifecycle.t.sol @@ -7,11 +7,11 @@ import "forge-std/console.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {IOrchestratorFactory_v1} from "src/factories/OrchestratorFactory_v1.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; @@ -20,14 +20,14 @@ import {ERC165Upgradeable} from import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; // SuT -import {LM_PC_Staking_v2, ILM_PC_Staking_v2} from "@lm/LM_PC_Staking_v2.sol"; +import {LM_PC_Staking_v3, ILM_PC_Staking_v3} from "@lm/LM_PC_Staking_v3.sol"; // Mocks // import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; -contract LM_PC_Staking_v2Lifecycle is E2ETest { +contract LM_PC_Staking_v3Lifecycle is E2ETest { using SafeERC20 for ERC20Mock; // Module Configurations for the current E2E test. Should be filled during setUp() call. @@ -98,15 +98,15 @@ contract LM_PC_Staking_v2Lifecycle is E2ETest { ); // Additional Logic Modules - setUpLM_PC_Staking_v2(); + setUpLM_PC_Staking_v3(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - LM_PC_Staking_v2Metadata, abi.encode(stakingToken) + LM_PC_Staking_v3Metadata, abi.encode(stakingToken) ) ); } - function test_e2e_LM_PC_Staking_v2Lifecycle() public { + function test_e2e_LM_PC_Staking_v3Lifecycle() public { //-------------------------------------------------------------------------- // Orchestrator Initialization //-------------------------------------------------------------------------- @@ -117,32 +117,47 @@ contract LM_PC_Staking_v2Lifecycle is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + FM_DepositVault_v1 fundingManager = FM_DepositVault_v1(address(orchestrator.fundingManager())); - LM_PC_Staking_v2 stakingManager; + LM_PC_Staking_v3 stakingManager; // ------------------ FROM ModuleTest.sol address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_PC_Staking_v2).interfaceId + type(ILM_PC_Staking_v3).interfaceId ) ) { - stakingManager = LM_PC_Staking_v2(modulesList[i]); + stakingManager = LM_PC_Staking_v3(modulesList[i]); break; } } + // Make stake and unstake public + authorizer.addAccessPermission( + address(stakingManager), + stakingManager.stake.selector, + authorizer.PUBLIC_ROLE() + ); + authorizer.addAccessPermission( + address(stakingManager), + stakingManager.unstake.selector, + authorizer.PUBLIC_ROLE() + ); + // Warp to reasonable time vm.warp(52 weeks); // ---------------- - // 1. deopsit some funds to fundingManager + // 1. deposit some funds to fundingManager uint initialDeposit = amount1 + amount2 + amount3 * 2; rewardToken.mint(address(this), initialDeposit); rewardToken.approve(address(fundingManager), initialDeposit); diff --git a/test/e2e/module/MetaTxAndMulticallE2E.t.sol b/test/e2e/module/MetaTxAndMulticallE2E.t.sol index 4b216d983..acd1f0aad 100644 --- a/test/e2e/module/MetaTxAndMulticallE2E.t.sol +++ b/test/e2e/module/MetaTxAndMulticallE2E.t.sol @@ -4,20 +4,20 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // SuT -import {AUT_Roles_v1} from "@aut/role/AUT_Roles_v1.sol"; +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; // Internal Dependencies import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; import { - LM_PC_Bounties_v2, ILM_PC_Bounties_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, ILM_PC_Bounties_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; import { TransactionForwarder_v1, ITransactionForwarder_v1, @@ -49,11 +49,10 @@ contract MetaTxAndMulticallE2E is E2ETest { ); // Authorizer - setUpTokenGatedRoleAuthorizer(); + setUpRoleAuthorizer(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - tokenRoleAuthorizerMetadata, - abi.encode(address(this), address(this)) + roleAuthorizerMetadata, abi.encode(address(this)) ) ); @@ -76,7 +75,7 @@ contract MetaTxAndMulticallE2E is E2ETest { function test_e2e_SendMetaTransaction() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = @@ -85,7 +84,7 @@ contract MetaTxAndMulticallE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); //-------------------------------------------------------------------------- @@ -146,32 +145,79 @@ contract MetaTxAndMulticallE2E is E2ETest { FM_DepositVault_v1(fundingManager).token().balanceOf(fundingManager), depositAmount ); + } + + function test_e2e_SendMetaTransaction_WithRole() public { + //-------------------------------------------------------------------------- + // Orchestrator_v2 Initialization + //-------------------------------------------------------------------------- + + IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = + IOrchestratorFactory_v1.WorkflowConfig({ + independentUpdates: false, + independentUpdateAdmin: address(0) + }); + + IOrchestrator_v2 orchestrator = + _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); + + AUT_Roles_v2 authorizer = + AUT_Roles_v2(address(orchestrator.authorizer())); + + //-------------------------------------------------------------------------- + // Module E2E Test + //-------------------------------------------------------------------------- - //----------------------------------------------------- - // Call Function with role // In this example we're gonna call the bountyManagers createBounty Function // The function needs a role to access it // Lets get the bountyManager address - LM_PC_Bounties_v2 bountyManager; - - address[] memory modulesList = orchestrator.listModules(); - for (uint i; i < modulesList.length; ++i) { - try ILM_PC_Bounties_v2(modulesList[i]).isExistingBountyId(0) - returns (bool) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); - break; - } catch { - continue; + LM_PC_Bounties_v3 bountyManager; + + { + address[] memory modulesList = orchestrator.listModules(); + for (uint i; i < modulesList.length; ++i) { + try ILM_PC_Bounties_v3(modulesList[i]).isExistingBountyId(0) + returns (bool) { + bountyManager = LM_PC_Bounties_v3(modulesList[i]); + break; + } catch { + continue; + } } } - // Give the signer address the according role - bountyManager.grantModuleRole( - bountyManager.BOUNTY_ISSUER_ROLE(), signer + //----------------------------------------------------- + // Create signer + + uint signerPrivateKey = 0xa11ce; + address signer = vm.addr(signerPrivateKey); + + // Create and assign role for the signer + + // Members of the role + address[] memory roleMembers = new address[](1); + roleMembers[0] = signer; + // Target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(bountyManager); + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = bountyManager.addBounty.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "BOUNTY_ISSUER", + authorizer.getAdminRole(), + roleMembers, + targets, + selectors ); + //----------------------------------------------------- // Then we need to create the ForwardRequest - req = ERC2771ForwarderUpgradeable.ForwardRequestData({ + + ERC2771ForwarderUpgradeable.ForwardRequestData memory req = + ERC2771ForwarderUpgradeable.ForwardRequestData({ from: signer, to: address(bountyManager), value: 0, @@ -189,12 +235,12 @@ contract MetaTxAndMulticallE2E is E2ETest { }); // Create the digest needed to create the signature - digest = forwarder.createDigest(req); + bytes32 digest = forwarder.createDigest(req); // Create Signature with digest (This has to be handled by the frontend) vm.prank(signer); - (v, r, s) = vm.sign(signerPrivateKey, digest); - signature = abi.encodePacked(r, s, v); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, digest); + bytes memory signature = abi.encodePacked(r, s, v); req.signature = signature; @@ -205,9 +251,9 @@ contract MetaTxAndMulticallE2E is E2ETest { assertTrue(bountyManager.isExistingBountyId(1)); } - function test_e2e_SendMulticall() public { + function test_e2e_SendMulticall_WithRole() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = @@ -216,7 +262,7 @@ contract MetaTxAndMulticallE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); // lets use this example user @@ -262,21 +308,39 @@ contract MetaTxAndMulticallE2E is E2ETest { // The function needs a role to access it // Lets get the bountyManager address - LM_PC_Bounties_v2 bountyManager; + LM_PC_Bounties_v3 bountyManager; address[] memory modulesList = orchestrator.listModules(); for (uint i; i < modulesList.length; ++i) { - try ILM_PC_Bounties_v2(modulesList[i]).isExistingBountyId(0) + try ILM_PC_Bounties_v3(modulesList[i]).isExistingBountyId(0) returns (bool) { - bountyManager = LM_PC_Bounties_v2(modulesList[i]); + bountyManager = LM_PC_Bounties_v3(modulesList[i]); break; } catch { continue; } } - // Give the signer address the according role - bountyManager.grantModuleRole(bountyManager.BOUNTY_ISSUER_ROLE(), user); + // Create and assign role for the user + + // Members of the role + address[] memory roleMembers = new address[](1); + roleMembers[0] = user; + // Target contract and function selectors + address[] memory targets = new address[](1); + targets[0] = address(bountyManager); + bytes4[][] memory selectors = new bytes4[][](1); + selectors[0] = new bytes4[](1); + selectors[0][0] = bountyManager.addBounty.selector; + + // Create role and set members + orchestrator.authorizer().createRoleAndAddAccessPermissions( + "BOUNTY_ISSUER", + orchestrator.authorizer().getAdminRole(), + roleMembers, + targets, + selectors + ); // We create a call struct containing the call we want to make ITransactionForwarder_v1.SingleCall memory call2 = diff --git a/test/e2e/orchestrator_and_structural/OrchestratorE2E.t.sol b/test/e2e/orchestrator_and_structural/OrchestratorE2E.t.sol index a8967e462..75c0dcfc9 100644 --- a/test/e2e/orchestrator_and_structural/OrchestratorE2E.t.sol +++ b/test/e2e/orchestrator_and_structural/OrchestratorE2E.t.sol @@ -5,24 +5,24 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1, + IOrchestrator_v2, ModuleFactory_v1 } from "test/e2e/E2ETest.sol"; // SuT import { - IOrchestrator_v1, - Orchestrator_v1 -} from "src/orchestrator/Orchestrator_v1.sol"; + IOrchestrator_v2, + Orchestrator_v2 +} from "src/orchestrator/Orchestrator_v2.sol"; // Modules that are used in this E2E test -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; import { - ILM_PC_Bounties_v2, LM_PC_Bounties_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + ILM_PC_Bounties_v3, LM_PC_Bounties_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; // Beacon import {InverterBeacon_v1} from "src/proxies/InverterBeacon_v1.sol"; @@ -70,7 +70,7 @@ contract OrchestratorE2E is E2ETest { ) ); - // We also set up the LM_PC_Bounties_v2, even though we'll add it later + // We also set up the LM_PC_Bounties_v3, even though we'll add it later setUpBountyManager(); } @@ -83,11 +83,11 @@ contract OrchestratorE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); uint timelock = - Orchestrator_v1(address(orchestrator)).MODULE_UPDATE_TIMELOCK(); + Orchestrator_v2(address(orchestrator)).MODULE_UPDATE_TIMELOCK(); //------------------------------------------------------------------------------------------ // Adding Module @@ -154,27 +154,19 @@ contract OrchestratorE2E is E2ETest { // Replace the old modules with the new ones orchestrator.initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2(newPaymentProcessor) + newPaymentProcessor ); vm.warp(block.timestamp + timelock); - orchestrator.executeSetPaymentProcessor( - IPaymentProcessor_v2(newPaymentProcessor) - ); - orchestrator.initiateSetFundingManagerWithTimelock( - IFundingManager_v1(newFundingManager) - ); + orchestrator.executeSetPaymentProcessor(newPaymentProcessor); + orchestrator.initiateSetFundingManagerWithTimelock(newFundingManager); vm.warp(block.timestamp + timelock); - orchestrator.executeSetFundingManager( - IFundingManager_v1(newFundingManager) - ); - orchestrator.initiateSetAuthorizerWithTimelock( - IAuthorizer_v1(newAuthorizer) - ); + orchestrator.executeSetFundingManager(newFundingManager); + orchestrator.initiateSetAuthorizerWithTimelock(newAuthorizer); vm.warp(block.timestamp + timelock); - orchestrator.executeSetAuthorizer(IAuthorizer_v1(newAuthorizer)); + orchestrator.executeSetAuthorizer(newAuthorizer); // Assert post-state assertEq(modulesBefore, orchestrator.modulesSize()); // The orchestrator is back to the original number of modules diff --git a/test/e2e/paymentProcessor/PPEverclearCrossChainE2E.t.sol b/test/e2e/paymentProcessor/PPEverclearCrossChainE2E.t.sol index 39460074a..4ca6b693b 100644 --- a/test/e2e/paymentProcessor/PPEverclearCrossChainE2E.t.sol +++ b/test/e2e/paymentProcessor/PPEverclearCrossChainE2E.t.sol @@ -5,22 +5,22 @@ import {E2ETest} from "test/e2e/E2ETest.sol"; import {console} from "forge-std/console.sol"; // Inverter Core -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import {IOrchestratorFactory_v1} from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; // Added for casting +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; // Added for casting // Modules to be tested and their dependencies -import {PP_Everclear_CrossChain_v1} from "@pp/PP_Everclear_CrossChain_v1.sol"; +import {PP_Everclear_CrossChain_v2} from "@pp/PP_Everclear_CrossChain_v2.sol"; import {Mock_LM_PC_PaymentRouter_Everclear_v1} from "@mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol"; import {IEverclear} from "@pp/interfaces/IEverclear.sol"; import {IERC20PaymentClientBase_v2} from "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IPP_CrossChainBase_v1} from "@pp/interfaces/IPP_CrossChainBase_v1.sol"; +import {IPP_CrossChainBase_v2} from "@pp/interfaces/IPP_CrossChainBase_v2.sol"; import {IFM_DepositVault_v1} from "@fm/depositVault/interfaces/IFM_DepositVault_v1.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; // For interfaceId check @@ -47,8 +47,8 @@ contract PPEverclearCrossChainE2E is E2ETest { //-------------------------------------------------------------------------- IOrchestratorFactory_v1.ModuleConfig[] moduleConfigurations; - IOrchestrator_v1 orchestrator; - PP_Everclear_CrossChain_v1 paymentProcessor; + IOrchestrator_v2 orchestrator; + PP_Everclear_CrossChain_v2 paymentProcessor; Mock_LM_PC_PaymentRouter_Everclear_v1 paymentClient; IFM_DepositVault_v1 fmDepositVault; @@ -144,7 +144,7 @@ contract PPEverclearCrossChainE2E is E2ETest { ) ); - // PaymentProcessor (PP_Everclear_CrossChain_v1) + // PaymentProcessor (PP_Everclear_CrossChain_v2) moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( ppEverclearCrossChainMetadata, // From E2EModuleRegistry, set up by setUpPPEverclearCrossChain @@ -172,11 +172,11 @@ contract PPEverclearCrossChainE2E is E2ETest { vm.label(address(orchestrator), "E2E_Orchestrator"); // 7. Retrieve Deployed Module Instances - paymentProcessor = PP_Everclear_CrossChain_v1( + paymentProcessor = PP_Everclear_CrossChain_v2( payable(address(orchestrator.paymentProcessor())) ); vm.label( - address(paymentProcessor), "PP_Everclear_CrossChain_v1_Instance" + address(paymentProcessor), "PP_Everclear_CrossChain_v2_Instance" ); address[] memory modulesList = orchestrator.listModules(); @@ -186,7 +186,7 @@ contract PPEverclearCrossChainE2E is E2ETest { if (address(paymentClient) == address(0)) { // Only find if not already found string memory currentModuleTitle = - IModule_v1(moduleAddress).title(); + IModule_v2(moduleAddress).title(); if ( keccak256(abi.encodePacked(currentModuleTitle)) == keccak256( @@ -213,8 +213,8 @@ contract PPEverclearCrossChainE2E is E2ETest { if (address(fmDepositVault) == address(0)) { // Only find if not already found // Using supportsInterface for more robust check - // Cast to Module_v1 to access supportsInterface from ERC165Upgradeable - Module_v1 baseModule = Module_v1(payable(moduleAddress)); + // Cast to Module_v2 to access supportsInterface from ERC165Upgradeable + Module_v2 baseModule = Module_v2(payable(moduleAddress)); if ( baseModule.supportsInterface( type(IFundingManager_v1).interfaceId @@ -226,7 +226,7 @@ contract PPEverclearCrossChainE2E is E2ETest { // Further check if it's the one configured with our paymentToken // This assumes depositVaultMetadata was used for its deployment. string memory currentModuleTitle = - IModule_v1(moduleAddress).title(); + IModule_v2(moduleAddress).title(); if ( keccak256(abi.encodePacked(currentModuleTitle)) == keccak256( @@ -272,23 +272,7 @@ contract PPEverclearCrossChainE2E is E2ETest { vm.startPrank(owner); paymentToken.approve(address(paymentClient), type(uint).max); usdc.approve(address(paymentClient), type(uint).max); // If client handles fees - // vm.stopPrank(); // Keep prank active for role granting - - // Grant PAYMENT_PUSHER_ROLE to owner for the paymentClient mock - // The role value is defined in LM_PC_PaymentRouter_v2 - // This needs to be called by an admin of the paymentClient's authorizer (which is 'owner') - bytes32 pusherRole = paymentClient.PAYMENT_PUSHER_ROLE(); // Directly use the constant - paymentClient.grantModuleRole(pusherRole, owner); - - // Grant MODULE_ROLE to paymentClient on the paymentProcessor - // The role value is defined in Module_v1 or specific PP - // This needs to be called by an admin of the orchestrator's authorizer (which is 'owner') - bytes32 MODULE_ROLE = keccak256("MODULE_ROLE"); - // The paymentProcessor's authorizer is the orchestrator's authorizer - IOrchestrator_v1(address(orchestrator)).authorizer().grantRole( - MODULE_ROLE, address(paymentClient) - ); - vm.stopPrank(); // Stop prank after all owner actions + // vm.stopPrank(); // Keep prank active for role granting } //-------------------------------------------------------------------------- @@ -496,7 +480,7 @@ contract PPEverclearCrossChainE2E is E2ETest { // B. paymentClient initiates the cross-chain payment // This sequence of events happens INSIDE paymentClient.pushCrossChainPaymentEverclear(...) - // and the subsequent PP_Everclear_CrossChain_v1.processPayments call. + // and the subsequent PP_Everclear_CrossChain_v2.processPayments call. // B.1. fmDepositVault transfers to paymentClient (triggered by paymentClient) // B.1.a IERC20.Transfer event from the token contract @@ -554,7 +538,7 @@ contract PPEverclearCrossChainE2E is E2ETest { // B.7. paymentProcessor emits BridgeTransferCompleted (inside processPayments) // We don't check intentId (topic2) as it's generated dynamically. vm.expectEmit(true, false, true, false, address(paymentProcessor)); // paymentId (topic1), recipient (topic3) are indexed. Data not checked. - emit IPP_CrossChainBase_v1.BridgeTransferCompleted( + emit IPP_CrossChainBase_v2.BridgeTransferCompleted( initialState.paymentProcessorPaymentId, // Expected paymentId bytes32(0), // Placeholder for intentId - not checked recipientAddressOnTargetChain, diff --git a/test/e2e/paymentProcessor/StreamingPaymentProcessorE2E.t.sol b/test/e2e/paymentProcessor/StreamingPaymentProcessorE2E.t.sol index 79ef5de11..826a7babd 100644 --- a/test/e2e/paymentProcessor/StreamingPaymentProcessorE2E.t.sol +++ b/test/e2e/paymentProcessor/StreamingPaymentProcessorE2E.t.sol @@ -5,19 +5,19 @@ pragma solidity ^0.8.0; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1 + IOrchestrator_v2 } from "test/e2e/E2ETest.sol"; import {FM_DepositVault_v1} from "@fm/depositVault/FM_DepositVault_v1.sol"; // SuT import { - LM_PC_RecurringPayments_v2, - ILM_PC_RecurringPayments_v2 -} from "@lm/LM_PC_RecurringPayments_v2.sol"; + LM_PC_RecurringPayments_v3, + ILM_PC_RecurringPayments_v3 +} from "@lm/LM_PC_RecurringPayments_v3.sol"; -import {PP_Streaming_v2} from "src/modules/paymentProcessor/PP_Streaming_v2.sol"; +import {PP_Streaming_v3} from "src/modules/paymentProcessor/PP_Streaming_v3.sol"; -import {IPP_Streaming_v2} from "@pp/interfaces/IPP_Streaming_v2.sol"; +import {IPP_Streaming_v3} from "@pp/interfaces/IPP_Streaming_v3.sol"; import {ERC165Upgradeable} from "@oz-up/utils/introspection/ERC165Upgradeable.sol"; @@ -45,10 +45,10 @@ contract StreamingPaymentProcessorE2E is E2ETest { uint defaultEnd = 30; // Modules, for reference between functions - IOrchestrator_v1 orchestrator; + IOrchestrator_v2 orchestrator; FM_DepositVault_v1 fundingManager; - LM_PC_RecurringPayments_v2 recurringPaymentManager; - PP_Streaming_v2 streamingPaymentProcessor; + LM_PC_RecurringPayments_v3 recurringPaymentManager; + PP_Streaming_v3 streamingPaymentProcessor; function setUp() public override { // Setup common E2E framework @@ -98,7 +98,7 @@ contract StreamingPaymentProcessorE2E is E2ETest { function init() private { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ @@ -116,11 +116,11 @@ contract StreamingPaymentProcessorE2E is E2ETest { for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(ILM_PC_RecurringPayments_v2).interfaceId + type(ILM_PC_RecurringPayments_v3).interfaceId ) ) { recurringPaymentManager = - LM_PC_RecurringPayments_v2(modulesList[i]); + LM_PC_RecurringPayments_v3(modulesList[i]); break; } } @@ -131,10 +131,10 @@ contract StreamingPaymentProcessorE2E is E2ETest { for (uint i; i < modulesList.length; ++i) { if ( ERC165Upgradeable(modulesList[i]).supportsInterface( - type(IPP_Streaming_v2).interfaceId + type(IPP_Streaming_v3).interfaceId ) ) { - streamingPaymentProcessor = PP_Streaming_v2(modulesList[i]); + streamingPaymentProcessor = PP_Streaming_v3(modulesList[i]); break; } } @@ -153,7 +153,7 @@ contract StreamingPaymentProcessorE2E is E2ETest { init(); // --------------------------------------------------------------------------------------------------- - // User side of the PP_Streaming_v2 + // User side of the PP_Streaming_v3 // Create 3 different Payments @@ -177,7 +177,7 @@ contract StreamingPaymentProcessorE2E is E2ETest { // Check Payments // viewAllPaymentOrders // Lets see all avaialable orders - IPP_Streaming_v2.Stream[] memory streams = streamingPaymentProcessor + IPP_Streaming_v3.Stream[] memory streams = streamingPaymentProcessor .viewAllPaymentOrders(address(recurringPaymentManager), alice); assertTrue(streams.length == 3); @@ -292,7 +292,7 @@ contract StreamingPaymentProcessorE2E is E2ETest { recurringPaymentManager.trigger(); // Check if everyone has a running payment active - IPP_Streaming_v2.Stream[] memory streams = streamingPaymentProcessor + IPP_Streaming_v3.Stream[] memory streams = streamingPaymentProcessor .viewAllPaymentOrders(address(recurringPaymentManager), alice); assertTrue(streams.length == 3); diff --git a/test/e2e/proxies/InverterBeaconE2E.t.sol b/test/e2e/proxies/InverterBeaconE2E.t.sol index 013bce1da..4f6a75dff 100644 --- a/test/e2e/proxies/InverterBeaconE2E.t.sol +++ b/test/e2e/proxies/InverterBeaconE2E.t.sol @@ -6,12 +6,12 @@ import "forge-std/Test.sol"; import { E2ETest, IOrchestratorFactory_v1, - IOrchestrator_v1, + IOrchestrator_v2, ModuleFactory_v1 } from "test/e2e/E2ETest.sol"; import { IModuleFactory_v1, - IModule_v1 + IModule_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; import {InverterBeacon_v1} from "src/proxies/InverterBeacon_v1.sol"; @@ -45,7 +45,7 @@ contract InverterBeaconE2E is E2ETest { string constant URL = "https://github.com/organization/module"; string constant TITLE = "Module"; - IModule_v1.Metadata DATA = IModule_v1.Metadata( + IModule_v2.Metadata DATA = IModule_v2.Metadata( MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, TITLE ); @@ -75,10 +75,10 @@ contract InverterBeaconE2E is E2ETest { ); // Authorizer - setUpTokenGatedRoleAuthorizer(); + setUpRoleAuthorizer(); moduleConfigurations.push( IOrchestratorFactory_v1.ModuleConfig( - tokenRoleAuthorizerMetadata, abi.encode(address(this)) + roleAuthorizerMetadata, abi.encode(address(this)) ) ); @@ -123,7 +123,7 @@ contract InverterBeaconE2E is E2ETest { function test_e2e_InverterBeaconUpgrade() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = @@ -132,7 +132,7 @@ contract InverterBeaconE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); //-------------------------------------------------------------------------- @@ -170,7 +170,7 @@ contract InverterBeaconE2E is E2ETest { function test_e2e_InverterBeaconShutdown() public { //-------------------------------------------------------------------------- - // Orchestrator_v1 Initialization + // Orchestrator_v2 Initialization //-------------------------------------------------------------------------- IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig = @@ -179,7 +179,7 @@ contract InverterBeaconE2E is E2ETest { independentUpdateAdmin: address(0) }); - IOrchestrator_v1 orchestrator = + IOrchestrator_v2 orchestrator = _create_E2E_Orchestrator(workflowConfig, moduleConfigurations); //-------------------------------------------------------------------------- // Module E2E Test @@ -243,7 +243,7 @@ contract InverterBeaconE2E is E2ETest { //-------------------------------------------------------------------------- // Internal Helper Functions - function _assumeValidMetadata(IModule_v1.Metadata memory metadata) + function _assumeValidMetadata(IModule_v2.Metadata memory metadata) public pure { diff --git a/test/mocks/external/governance/GovernorV1Mock.sol b/test/mocks/external/governance/GovernorV1Mock.sol index c4ed6abd6..200827054 100644 --- a/test/mocks/external/governance/GovernorV1Mock.sol +++ b/test/mocks/external/governance/GovernorV1Mock.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {IGovernor_v1} from "@ex/governance/interfaces/IGovernor_v1.sol"; import {IFeeManager_v1} from "@ex/fees/interfaces/IFeeManager_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {IModuleFactory_v1} from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; @@ -98,7 +98,7 @@ contract GovernorV1Mock is IGovernor_v1 { // Factory Functions function registerMetadataInModuleFactory( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, IInverterBeacon_v1 beacon ) external {} diff --git a/test/mocks/factories/ModuleFactoryV1Mock.sol b/test/mocks/factories/ModuleFactoryV1Mock.sol index 874b48a14..c241987ca 100644 --- a/test/mocks/factories/ModuleFactoryV1Mock.sol +++ b/test/mocks/factories/ModuleFactoryV1Mock.sol @@ -6,19 +6,20 @@ import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; import { IModuleFactory_v1, IInverterBeacon_v1, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IOrchestratorFactory_v1} from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; import {FundingManagerV1Mock} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; import {Clones} from "@oz/proxy/Clones.sol"; @@ -32,20 +33,20 @@ contract ModuleFactoryV1Mock is IModuleFactory_v1 { IOrchestratorFactory_v1.WorkflowConfig public givenWorkflowConfig; - IModule_v1.Metadata fundingManagerMetadata = IModule_v1.Metadata( + IModule_v2.Metadata fundingManagerMetadata = IModule_v2.Metadata( 1, 0, 0, "https://fundingmanager.com", "FundingManager" ); - IModule_v1.Metadata authorizerMetadata = - IModule_v1.Metadata(1, 0, 0, "https://authorizer.com", "Authorizer"); + IModule_v2.Metadata authorizerMetadata = + IModule_v2.Metadata(1, 0, 0, "https://authorizer.com", "Authorizer"); - IModule_v1.Metadata paymentProcessorMetadata = IModule_v1.Metadata( - 1, 1, 0, "https://paymentprocessor.com", "PP_Simple_v2" + IModule_v2.Metadata paymentProcessorMetadata = IModule_v2.Metadata( + 1, 1, 0, "https://paymentprocessor.com", "PP_Simple_v3" ); function createAndInitModule( - IModule_v1.Metadata memory metadata, - IOrchestrator_v1, + IModule_v2.Metadata memory metadata, + IOrchestrator_v2, bytes memory, IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig ) external returns (address) { @@ -59,26 +60,26 @@ contract ModuleFactoryV1Mock is IModuleFactory_v1 { LibMetadata.identifier(metadata) == LibMetadata.identifier(authorizerMetadata) ) { - return address(new AuthorizerV1Mock()); + return address(new Authorizer_v2_Mock()); } else if ( LibMetadata.identifier(metadata) == LibMetadata.identifier(paymentProcessorMetadata) ) { - return address(new PaymentProcessorV1Mock()); + return address(new PaymentProcessor_v3_Mock()); } else { - return address(new ModuleV1Mock()); + return address(new Module_v2_Mock()); } } function createModuleProxy( - IModule_v1.Metadata memory, - IOrchestrator_v1, + IModule_v2.Metadata memory, + IOrchestrator_v2, IOrchestratorFactory_v1.WorkflowConfig memory ) external returns (address) { - return Clones.clone(address(new ModuleV1Mock())); + return Clones.clone(address(new Module_v2_Mock())); } - function getBeaconAndId(IModule_v1.Metadata memory metadata) + function getBeaconAndId(IModule_v2.Metadata memory metadata) external view returns (IInverterBeacon_v1, bytes32) @@ -99,7 +100,7 @@ contract ModuleFactoryV1Mock is IModuleFactory_v1 { return msg.sender; } - function registerMetadata(IModule_v1.Metadata memory, IInverterBeacon_v1) + function registerMetadata(IModule_v2.Metadata memory, IInverterBeacon_v1) external { howManyCalls++; diff --git a/test/mocks/modules/authorizer/AUT_Roles_v2_Exposed.sol b/test/mocks/modules/authorizer/AUT_Roles_v2_Exposed.sol new file mode 100644 index 000000000..5bfb15539 --- /dev/null +++ b/test/mocks/modules/authorizer/AUT_Roles_v2_Exposed.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; +// Internal Dependencies + +import {AUT_Roles_v2} from "@aut/role/AUT_Roles_v2.sol"; + +contract AUT_Roles_v2_Exposed is AUT_Roles_v2 { + //========================================================================== + // State Access Functions + + function changeLastAssignedRoleId(uint newLastAssignedRoleIdValue_) + external + { + _lastAssignedRoleId = newLastAssignedRoleIdValue_; + } + + function addAccessPermission_unrestricted( + address target_, + bytes4 selector_, + bytes32 roleId_ + ) external { + _permissions[target_][selector_].push(roleId_); + } + + //========================================================================== + // Modifiers + + function idNotDefaultAdminModifier_exposed(bytes32 roleId_) + public + idNotDefaultAdmin(roleId_) + {} + + function idExistsModifier_exposed(bytes32 roleId_) + public + idExists(roleId_) + {} + + //========================================================================== + // Helper Functions +} diff --git a/test/mocks/modules/authorizer/AUT_TokenGated_Roles_v2_Exposed.sol b/test/mocks/modules/authorizer/AUT_TokenGated_Roles_v2_Exposed.sol new file mode 100644 index 000000000..cb65a740b --- /dev/null +++ b/test/mocks/modules/authorizer/AUT_TokenGated_Roles_v2_Exposed.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; +// Internal Dependencies + +import {AUT_TokenGated_Roles_v2} from "@aut/role/AUT_TokenGated_Roles_v2.sol"; +import {AccessControlUpgradeable} from + "@oz-up/access/AccessControlUpgradeable.sol"; + +contract AUT_TokenGated_Roles_v2_Exposed is AUT_TokenGated_Roles_v2 { + //========================================================================== + // Modifier + + function onlyEmptyRoleModifier_exposed(bytes32 roleId_) + public + onlyEmptyRole(roleId_) + {} + + function notPublicRoleModifier_exposed(bytes32 roleId_) + public + notPublicRole(roleId_) + {} + + function onlyTokenGatedModifier_exposed(bytes32 roleId_) + public + onlyTokenGated(roleId_) + {} + function validThresholdModifier_exposed(uint threshold_) + public + validThreshold(threshold_) + {} + + //========================================================================== + // Internal Exposed Functions + + function exposed_setThreshold( + bytes32 roleId_, + address token_, + uint threshold_ + ) public { + _setThreshold(roleId_, token_, threshold_); + } + + function exposed_hasTokenRole(bytes32 roleId_, address who_) + public + view + returns (bool hasTokenRole_) + { + return _hasTokenRole(roleId_, who_); + } + + function exposed_AccessControlUpgradeable_hasRole( + bytes32 roleId_, + address who_ + ) public view returns (bool hasRole_) { + return AccessControlUpgradeable.hasRole(roleId_, who_); + } + + //========================================================================== + // Helper Functions + + function setTokenGated_unrestricted(bytes32 roleId_, bool to_) public { + _isTokenGated[roleId_] = to_; + } + + function setThreshold_unrestricted( + bytes32 roleId_, + address token_, + uint threshold_ + ) public { + bytes32 thresholdId = keccak256(abi.encodePacked(roleId_, token_)); + _thresholdMap[thresholdId] = threshold_; + } +} diff --git a/test/mocks/modules/authorizer/AuthorizerV1Mock.sol b/test/mocks/modules/authorizer/AuthorizerV1Mock.sol deleted file mode 100644 index 2e0899930..000000000 --- a/test/mocks/modules/authorizer/AuthorizerV1Mock.sol +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import "forge-std/console.sol"; - -import { - Module_v1, - IModule_v1, - IOrchestrator_v1 -} from "src/modules/base/Module_v1.sol"; - -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; - -contract AuthorizerV1Mock is IAuthorizer_v1, Module_v1 { - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(Module_v1) - returns (bool) - { - bytes4 interfaceId_IAuthorizer = type(IAuthorizer_v1).interfaceId; - return interfaceId == interfaceId_IAuthorizer - || super.supportsInterface(interfaceId); - } - - mapping(address => bool) private _authorized; - mapping(bytes32 => mapping(address => bool)) private _roleAuthorized; - - bool private _allAuthorized; - - function setIsAuthorized(address who, bool to) external { - _authorized[who] = to; - } - - function setAllAuthorized(bool to) external { - _allAuthorized = to; - } - - //-------------------------------------------------------------------------- - // IModule_v1 Functions - - function init( - IOrchestrator_v1 orchestrator_, - Metadata memory metadata, - bytes memory configData - ) public override(Module_v1) initializer { - __Module_init(orchestrator_, metadata); - - // Read first authorized address from configData. - address authorized = abi.decode(configData, (address)); - require(authorized != address(0), "Zero address can not be authorized"); - - _authorized[authorized] = true; - - _roleAuthorized["0x00"][msg.sender] = true; - _roleAuthorized["0x02"][msg.sender] = true; - } - - function mockInit(bytes memory configData) public { - // Read first authorized address from configData. - address authorized = abi.decode(configData, (address)); - require(authorized != address(0), "Zero address can not be authorized"); - - _authorized[authorized] = true; - } - - //-------------------------------------------------------------------------- - // IAuthorizer_v1 Functions - - function generateRoleId(address module, bytes32 role) - public - pure - returns (bytes32) - { - return keccak256(abi.encodePacked(module, role)); - } - - function grantRoleFromModule(bytes32 role, address target) external { - _roleAuthorized[generateRoleId(_msgSender(), role)][target] = true; - } - - function grantRoleFromModuleBatched( - bytes32 role, - address[] calldata targets - ) external { - for (uint i = 0; i < targets.length; i++) { - _roleAuthorized[generateRoleId(_msgSender(), role)][targets[i]] = - true; - } - } - - function revokeRoleFromModule(bytes32 role, address target) external { - _roleAuthorized[generateRoleId(_msgSender(), role)][target] = false; - } - - function revokeRoleFromModuleBatched( - bytes32 role, - address[] calldata targets - ) external { - for (uint i = 0; i < targets.length; i++) { - _roleAuthorized[generateRoleId(_msgSender(), role)][targets[i]] = - false; - } - } - - function grantRole(bytes32 role, address who) public { - _roleAuthorized[role][who] = true; - } - - function hasRole(bytes32 role, address who) external view returns (bool) { - return _authorized[who] || _roleAuthorized[role][who] || _allAuthorized; - } - - function checkForRole(bytes32 role, address who) - external - view - returns (bool) - { - return _authorized[who] || _roleAuthorized[role][who] || _allAuthorized; - } - - function checkRoleMembership(bytes32 role, address who) - external - view - returns (bool) - { - return _roleAuthorized[role][who]; - } - - function revokeRole(bytes32 role, address who) public { - _roleAuthorized[role][who] = false; - } - - function getAdminRole() external pure returns (bytes32) { - return "0x00"; - } - - function grantGlobalRole(bytes32 role, address target) external { - bytes32 roleID = generateRoleId(address(orchestrator()), role); - grantRole(roleID, target); - } - - function revokeGlobalRole(bytes32 role, address target) external { - bytes32 roleID = generateRoleId(address(orchestrator()), role); - revokeRole(roleID, target); - } - - //-------------------------------------------------------------------------- - // Functions left empty - - function grantGlobalRoleBatched(bytes32, address[] calldata) - external - pure - { - revert("Not implemented in Authorizer Mock"); - } - - function revokeGlobalRoleBatched(bytes32, address[] calldata) - external - pure - { - revert("Not implemented in Authorizer Mock"); - } - - function renounceRole(bytes32, address) external pure { - revert("Not implemented in Authorizer Mock"); - } - - function transferAdminRole(bytes32, bytes32) external pure { - revert("Not implemented in Authorizer Mock"); - } - - function burnAdminFromModuleRole(bytes32) external pure { - revert("Not implemented in Authorizer Mock"); - } - - function getRoleAdmin(bytes32) external pure returns (bytes32) { - return "0x00"; // In this mock, all roles have the owner as admin - } - - function getRoleMember(bytes32, uint) external pure returns (address) { - revert("Not implemented in Authorizer Mock"); - } - - function getRoleMemberCount(bytes32) external pure returns (uint) { - revert("Not implemented in Authorizer Mock"); - } -} diff --git a/test/mocks/modules/authorizer/Authorizer_v1_Mock.sol b/test/mocks/modules/authorizer/Authorizer_v1_Mock.sol new file mode 100644 index 000000000..0e3a752f9 --- /dev/null +++ b/test/mocks/modules/authorizer/Authorizer_v1_Mock.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; +import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; + +import {ERC165Upgradeable} from + "@oz-up/utils/introspection/ERC165Upgradeable.sol"; + +contract Authorizer_v1_Mock is IAuthorizer_v1, IModule_v1, ERC165Upgradeable { + /// @inheritdoc ERC165Upgradeable + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC165Upgradeable) + returns (bool) + { + return interfaceId == type(IAuthorizer_v1).interfaceId + || interfaceId == type(IModule_v1).interfaceId + || super.supportsInterface(interfaceId); + } + // ------------------------------------------------------------------------ + // IAuthorizer_v1 Functions + + function checkForRole(bytes32, address) external view returns (bool) { + return false; + } + + function generateRoleId(address, bytes32) external pure returns (bytes32) { + return bytes32(0); + } + + function grantRoleFromModule(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantRoleFromModuleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function revokeRoleFromModule(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function revokeRoleFromModuleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function transferAdminRole(bytes32, bytes32) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function burnAdminFromModuleRole(bytes32) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantGlobalRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantGlobalRoleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function revokeGlobalRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function revokeGlobalRoleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function getAdminRole() external pure returns (bytes32) { + return bytes32(0); + } + + // ------------------------------------------------------------------------ + // IAccessControlEnumerable Functions + + function getRoleMember(bytes32, uint) external pure returns (address) { + return address(0); + } + + function getRoleMemberCount(bytes32) external pure returns (uint) { + return 0; + } + + // ------------------------------------------------------------------------ + // IAccessControl Functions + + function hasRole(bytes32, address) external view returns (bool) { + return false; + } + + function getRoleAdmin(bytes32) external pure returns (bytes32) { + return bytes32(0); + } + + function grantRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function revokeRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function renounceRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + // ------------------------------------------------------------------------ + // IModule_v1 Functions + + function init(IOrchestrator_v2, Metadata memory, bytes memory) + public + virtual + {} + + function identifier() external view returns (bytes32) { + return bytes32(0); + } + + function version() external view returns (uint, uint, uint) { + return (0, 0, 0); + } + + function url() external view returns (string memory) { + return ""; + } + + /// @notice Returns the module's title. + /// @return The module's title. + function title() external view returns (string memory) { + return ""; + } + + function orchestrator() external view returns (IOrchestrator_v2) { + return IOrchestrator_v2(address(0)); + } + + function grantModuleRole(bytes32, address) external { + revert("Not implemented in Authorizer Mock"); + } + + function grantModuleRoleBatched(bytes32, address[] calldata) external { + revert("Not implemented in Authorizer Mock"); + } + + function revokeModuleRole(bytes32, address) external { + revert("Not implemented in Authorizer Mock"); + } + + function revokeModuleRoleBatched(bytes32, address[] calldata) external { + revert("Not implemented in Authorizer Mock"); + } +} diff --git a/test/mocks/modules/authorizer/Authorizer_v2_Mock.sol b/test/mocks/modules/authorizer/Authorizer_v2_Mock.sol new file mode 100644 index 000000000..2a113eddc --- /dev/null +++ b/test/mocks/modules/authorizer/Authorizer_v2_Mock.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import "forge-std/console.sol"; + +import { + Module_v2, + IModule_v2, + IOrchestrator_v2 +} from "src/modules/base/Module_v2.sol"; + +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; + +contract Authorizer_v2_Mock is IAuthorizer_v2, Module_v2 { + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Module_v2) + returns (bool) + { + bytes4 interfaceId_IAuthorizer = type(IAuthorizer_v2).interfaceId; + return interfaceId == interfaceId_IAuthorizer + || super.supportsInterface(interfaceId); + } + + mapping(address => bool) private _authorized; + mapping(bytes32 => mapping(address => bool)) private _roleAuthorized; + + mapping( + address caller + => mapping( + address target + => mapping(bytes4 functionSelector => bool permission) + ) + ) internal _permissions; + + bool private _allAuthorized; + address _defaultAdmin; + + function setDefaultAdmin(address who) external { + _defaultAdmin = who; + } + + function setIsAuthorized(address who, bool to) external { + _authorized[who] = to; + } + + function setAllAuthorized(bool to) external { + _allAuthorized = to; + } + + //-------------------------------------------------------------------------- + // IModule_v2 Functions + + function init( + IOrchestrator_v2 orchestrator_, + Metadata memory metadata, + bytes memory configData + ) public override(Module_v2) initializer { + __Module_init(orchestrator_, metadata); + + // Read first authorized address from configData. + address authorized = abi.decode(configData, (address)); + require(authorized != address(0), "Zero address can not be authorized"); + + _authorized[authorized] = true; + + _roleAuthorized[0x00][msg.sender] = true; + + _defaultAdmin = authorized; + } + + function mockInit(bytes memory configData) public { + // Read first authorized address from configData. + address authorized = abi.decode(configData, (address)); + require(authorized != address(0), "Zero address can not be authorized"); + + _authorized[authorized] = true; + } + + // ======================================================================== + // Mock Overrides + + function grantRole(bytes32 role, address who) public { + _roleAuthorized[role][who] = true; + } + + function hasRole(bytes32 role, address who) external view returns (bool) { + return _authorized[who] || _roleAuthorized[role][who] || _allAuthorized; + } + + function checkRoleMembership(bytes32 role, address who) + external + view + returns (bool) + { + return _roleAuthorized[role][who]; + } + + function revokeRole(bytes32 role, address who) public { + _roleAuthorized[role][who] = false; + } + + function renounceRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function getRoleAdmin(bytes32) external pure returns (bytes32) { + return 0x00; // In this mock, all roles have the owner as admin + } + + function getRoleMember(bytes32, uint) external pure returns (address) { + revert("Not implemented in Authorizer Mock"); + } + + function getRoleMemberCount(bytes32) external pure returns (uint) { + revert("Not implemented in Authorizer Mock"); + } + + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Authorization + + function getPermissions(address, bytes4) + external + view + returns (bytes32[] memory) + {} + + function getLastAssignedRoleId() + external + view + returns (uint lastAssignedRoleId_) + {} + + function isRolePermissioned(address, bytes4, bytes32) + external + view + returns (bool) + {} + + function hasPermission( + address caller_, + address target_, + bytes4 functionSelector_ + ) external view returns (bool) { + if (_allAuthorized) { + return true; + } + if (caller_ == _defaultAdmin) { + return true; + } + return _permissions[caller_][target_][functionSelector_]; + } + + function setHasPermission( + address caller_, + address target_, + bytes4 functionSelector_, + bool to + ) external { + _permissions[caller_][target_][functionSelector_] = to; + } + + // ------------------------------------------------------------------------ + // Getter - Role Management + + function getAdminRole() external pure returns (bytes32) { + return 0x00; + } + + // ------------------------------------------------------------------------ + // Getter - Out of Order + + function checkForRole(bytes32 role, address who) + external + view + returns (bool) + { + return _authorized[who] || _roleAuthorized[role][who] || _allAuthorized; + } + + function generateRoleId(address module, bytes32 role) + public + pure + returns (bytes32) + { + return keccak256(abi.encodePacked(module, role)); + } + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - Authorization + + function addAccessPermission(address, bytes4, bytes32) external {} + + function removeAccessPermission(address, bytes4, bytes32) external {} + + // ------------------------------------------------------------------------ + // Mutating - Role Management + + function createRole(string memory, bytes32, address[] memory) + external + returns (bytes32) + {} + + function labelRole(bytes32, string memory) external {} + + function transferAdminRole(bytes32, bytes32) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function burnRoleAdmin(bytes32) external pure { + revert("Not implemented in Authorizer Mock"); + } + + // ------------------------------------------------------------------------ + // Mutating - Mixed Utility + + function createRoleAndAddAccessPermissions( + string memory, + bytes32, + address[] memory, + address[] memory, + bytes4[][] memory + ) external returns (bytes32) {} + + // ------------------------------------------------------------------------ + // Mutating - Out of Order + + function grantRoleFromModule(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantRoleFromModuleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function revokeRoleFromModule(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function revokeRoleFromModuleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function burnAdminFromModuleRole(bytes32) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantGlobalRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function grantGlobalRoleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } + + function revokeGlobalRole(bytes32, address) external pure { + revert("Not implemented in Authorizer Mock"); + } + + function revokeGlobalRoleBatched(bytes32, address[] calldata) + external + pure + { + revert("Not implemented in Authorizer Mock"); + } +} diff --git a/test/mocks/modules/authorizer/TokenInterfaceMock.sol b/test/mocks/modules/authorizer/TokenInterfaceMock.sol new file mode 100644 index 000000000..57e60d4d6 --- /dev/null +++ b/test/mocks/modules/authorizer/TokenInterfaceMock.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.23; + +import {TokenInterface} from "@aut/role/AUT_TokenGated_Roles_v2.sol"; + +contract TokenInterfaceMock is TokenInterface { + //========================================================================== + // Storage + mapping(address => uint) public tokenBalances; + + //========================================================================== + // Public Getter Functions + + function balanceOf(address _owner) external view returns (uint balance) { + return tokenBalances[_owner]; + } + + //========================================================================== + // Public Setter Functions + + function setTokenBalance(address _owner, uint _balance) external { + tokenBalances[_owner] = _balance; + } +} diff --git a/test/mocks/modules/base/ModuleV1Mock.sol b/test/mocks/modules/base/Module_v2_Mock.sol similarity index 57% rename from test/mocks/modules/base/ModuleV1Mock.sol rename to test/mocks/modules/base/Module_v2_Mock.sol index 066d8d364..d607fb9fc 100644 --- a/test/mocks/modules/base/ModuleV1Mock.sol +++ b/test/mocks/modules/base/Module_v2_Mock.sol @@ -2,29 +2,48 @@ pragma solidity ^0.8.0; import { - Module_v1, - IModule_v1, - IOrchestrator_v1 -} from "src/modules/base/Module_v1.sol"; + Module_v2, + IModule_v2, + IOrchestrator_v2 +} from "src/modules/base/Module_v2.sol"; + +contract Module_v2_Mock is Module_v2 { + // ======================================================================== + // Modifier Access + + function modifierPermissionedCheck() external view permissioned {} + + // Empty function used to test the modifier `onlyPaymentClient` + function modifierOnlyPaymentClientCheck() external view onlyPaymentClient {} + + function modifierOnlyValidAddressCheck(address to) + external + view + validAddress(to) + {} + + // ======================================================================== + // Initialization -contract ModuleV1Mock is Module_v1 { function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory - ) public virtual override(Module_v1) initializer { + ) public virtual override(Module_v2) initializer { __Module_init(orchestrator_, metadata); } // Note that the `initializer` modifier is missing. function initNoInitializer( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory ) external { __Module_init(orchestrator_, metadata); } + // ======================================================================== + // Internal Function Access function original_msgSender() external view @@ -43,7 +62,7 @@ contract ModuleV1Mock is Module_v1 { return _msgData(); } - function original_getFeeManagerCollateralFeeData(bytes4 functionSelector) + function _getFeeManagerCollateralFeeData_exposed(bytes4 functionSelector) external view returns (uint, address) @@ -51,7 +70,7 @@ contract ModuleV1Mock is Module_v1 { return _getFeeManagerCollateralFeeData(functionSelector); } - function original_getFeeManagerIssuanceFeeData(bytes4 functionSelector) + function _getFeeManagerIssuanceFeeData_exposed(bytes4 functionSelector) external view returns (uint, address) @@ -59,12 +78,10 @@ contract ModuleV1Mock is Module_v1 { return _getFeeManagerIssuanceFeeData(functionSelector); } - // Empty function used to test the modifier `onlyPaymentClient` - function modifierOnlyPaymentClientCheck() external view onlyPaymentClient {} - - function modifierOnlyValidAddressCheck(address to) + function _checkAuthorization_exposed(address caller_, bytes calldata data_) external view - validAddress(to) - {} + { + _checkAuthorization(caller_, data_); + } } diff --git a/test/mocks/modules/fundingManager/FundingManagerV1Mock.sol b/test/mocks/modules/fundingManager/FundingManagerV1Mock.sol index d2fc6bffc..ba12d2d29 100644 --- a/test/mocks/modules/fundingManager/FundingManagerV1Mock.sol +++ b/test/mocks/modules/fundingManager/FundingManagerV1Mock.sol @@ -6,21 +6,21 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; // Internal Dependencies import { - Module_v1, - IModule_v1, - IOrchestrator_v1 -} from "src/modules/base/Module_v1.sol"; + Module_v2, + IModule_v2, + IOrchestrator_v2 +} from "src/modules/base/Module_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; // External Libraries import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; -contract FundingManagerV1Mock is IFundingManager_v1, Module_v1 { +contract FundingManagerV1Mock is IFundingManager_v1, Module_v2 { function supportsInterface(bytes4 interfaceId) public view virtual - override(Module_v1) + override(Module_v2) returns (bool) { bytes4 interfaceId_IFundingManager = @@ -34,10 +34,10 @@ contract FundingManagerV1Mock is IFundingManager_v1, Module_v1 { IERC20 private _token; function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory - ) public override(Module_v1) initializer { + ) public override(Module_v2) initializer { __Module_init(orchestrator_, metadata); } diff --git a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol index 356ad4754..4bd749916 100644 --- a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol +++ b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol @@ -4,23 +4,23 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT import { - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, + FM_BC_Bancor_Redeeming_VirtualSupply_v2, + IFM_BC_Bancor_Redeeming_VirtualSupply_v2, FM_BC_Tools -} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; +} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; import {IBancorFormula} from "@fm/bondingCurve/interfaces/IBancorFormula.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock is - FM_BC_Bancor_Redeeming_VirtualSupply_v1 + FM_BC_Bancor_Redeeming_VirtualSupply_v2 { // ------------------------------------------------------------------------- - // The FM_BC_Bancor_Redeeming_VirtualSupply_v1 is not abstract, so all the necessary functions are already implemented + // The FM_BC_Bancor_Redeeming_VirtualSupply_v2 is not abstract, so all the necessary functions are already implemented // The goal of this mock is to provide direct access to internal functions for testing purposes. // ------------------------------------------------------------------------- diff --git a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_RedeemingV1_Exposed.sol b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_RedeemingV1_Exposed.sol index 31ab746a4..827214b2a 100644 --- a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_RedeemingV1_Exposed.sol +++ b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_RedeemingV1_Exposed.sol @@ -4,16 +4,16 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT -import {FM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {FM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; contract FM_BC_BondingSurface_RedeemingV1_Exposed is - FM_BC_BondingSurface_Redeeming_v1 + FM_BC_BondingSurface_Redeeming_v2 { // ------------------------------------------------------------------------- // The goal of this mock is to provide direct access to internal functions for testing purposes. diff --git a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed.sol b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed.sol index 43e04bc52..42fc71d20 100644 --- a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed.sol +++ b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed.sol @@ -4,19 +4,19 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT import { - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 } from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed is - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 { // ------------------------------------------------------------------------- // The goal of this mock is to provide direct access to internal functions for testing purposes. @@ -32,10 +32,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed is // ------------------------------------------------------------------------- // Mock access for internal functions - function exposed_onlyIfNotBuyAndSellRestrictedModifier() external view { - _onlyIfNotBuyAndSellRestrictedModifier(); - } - function exposed_getRepayableAmount() external view diff --git a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock.sol b/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock.sol deleted file mode 100644 index 235ea4740..000000000 --- a/test/mocks/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock.sol +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import "forge-std/console.sol"; - -// Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -// SuT -import {FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol"; - -import { - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, - FM_BC_Tools -} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {IBancorFormula} from "@fm/bondingCurve/interfaces/IBancorFormula.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; - -contract FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock is - FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1 -{ - // ------------------------------------------------------------------------- - // The FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1 is not abstract, so all the necessary functions are already implemented - // The goal of this mock is to provide direct access to internal functions for testing purposes. - - // ------------------------------------------------------------------------- - // Mock access for internal functions - - function call_BPS() external pure returns (uint) { - return BPS; - } - - function call_PPM() external pure returns (uint32) { - return PPM; - } - - function call_reserveRatioForBuying() external view returns (uint32) { - return reserveRatioForBuying; - } - - function call_reserveRatioForSelling() external view returns (uint32) { - return reserveRatioForSelling; - } - - function call_collateralTokenDecimals() external view returns (uint8) { - return collateralTokenDecimals; - } - - function call_issuanceTokenDecimals() external view returns (uint8) { - return issuanceTokenDecimals; - } - - // Since the init calls are not registered for coverage, we call expose setIssuanceToken to get to 100% test coverage. - function call_setIssuanceToken(address _newIssuanceToken) external { - _setIssuanceToken(_newIssuanceToken); - } - /* - function call_staticPricePPM( - uint _issuanceSupply, - uint _collateralSupply, - uint32 _reserveRatio - ) external pure returns (uint) { - return - _staticPricePPM(_issuanceSupply, _collateralSupply, _reserveRatio); - } - */ - - function call_convertAmountToRequiredDecimal( - uint _amount, - uint8 _tokenDecimals, - uint8 _requiredDecimals - ) external pure returns (uint) { - return FM_BC_Tools._convertAmountToRequiredDecimal( - _amount, _tokenDecimals, _requiredDecimals - ); - } - - // Note: this function returns the virtual token supply in the same format it will be fed to the Bancor formula - function call_getFormulaVirtualIssuanceSupply() - external - view - returns (uint) - { - uint decimalConvertedVirtualIssuanceSupply = FM_BC_Tools - ._convertAmountToRequiredDecimal( - virtualIssuanceSupply, issuanceTokenDecimals, 18 - ); - return decimalConvertedVirtualIssuanceSupply; - } - - function call_setVirtualIssuanceSupply(uint _newSupply) external { - _setVirtualIssuanceSupply(_newSupply); - } - - // Note: this function returns the virtual collateral supply in the same format it will be fed to the Bancor formula - function call_getFormulaVirtualCollateralSupply() - external - view - returns (uint) - { - uint decimalConvertedVirtualCollateralSupply = FM_BC_Tools - ._convertAmountToRequiredDecimal( - virtualCollateralSupply, collateralTokenDecimals, 18 - ); - return decimalConvertedVirtualCollateralSupply; - } - - function call_processCollateralTokensForBuyOperation(uint _amount) - external - { - _processCollateralTokensForBuyOperation(_amount); - } - - function call_handleIssuanceTokensAfterBuy(address _receiver, uint _amount) - external - { - _handleIssuanceTokensAfterBuy(_receiver, _amount); - } - - function call_handleCollateralTokensAfterSell( - address _receiver, - uint _amount - ) external { - _handleCollateralTokensAfterSell(_receiver, _amount); - } - - // ------------------------------------------------------------------------- - // Helper Functions - - function setProjectCollateralFeeCollectedHelper(uint _amount) external { - projectCollateralFeeCollected = _amount; - } -} diff --git a/test/mocks/modules/fundingManager/bondingCurve/abstracts/BondingCurveBaseV1Mock.sol b/test/mocks/modules/fundingManager/bondingCurve/abstracts/BondingCurveBaseV1Mock.sol index 6eb9b93d0..5fe7e49ea 100644 --- a/test/mocks/modules/fundingManager/bondingCurve/abstracts/BondingCurveBaseV1Mock.sol +++ b/test/mocks/modules/fundingManager/bondingCurve/abstracts/BondingCurveBaseV1Mock.sol @@ -4,28 +4,28 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT import { - BondingCurveBase_v1, - IBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; + BondingCurveBase_v2, + IBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import {IBancorFormula} from "@fm/bondingCurve/interfaces/IBancorFormula.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -contract BondingCurveBaseV1Mock is BondingCurveBase_v1 { +contract BondingCurveBaseV1Mock is BondingCurveBase_v2 { IBancorFormula public formula; function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); ( @@ -47,7 +47,7 @@ contract BondingCurveBaseV1Mock is BondingCurveBase_v1 { function _issueTokensFormulaWrapper(uint _depositAmount) internal pure - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (uint) { // Since this is a mock, we will always mint the same amount of tokens as have been deposited @@ -58,7 +58,7 @@ contract BondingCurveBaseV1Mock is BondingCurveBase_v1 { function getStaticPriceForBuying() external view - override(BondingCurveBase_v1) + override(BondingCurveBase_v2) returns (uint) {} diff --git a/test/mocks/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBaseV1Mock.sol b/test/mocks/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBaseV1Mock.sol index b7ec50f42..b3c38e334 100644 --- a/test/mocks/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBaseV1Mock.sol +++ b/test/mocks/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBaseV1Mock.sol @@ -4,36 +4,36 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // Internal Dependencies -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT import { - RedeemingBondingCurveBase_v1, - IRedeemingBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; + RedeemingBondingCurveBase_v2, + IRedeemingBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; import { - BondingCurveBase_v1, - IBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; + BondingCurveBase_v2, + IBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import {IBancorFormula} from "@fm/bondingCurve/interfaces/IBancorFormula.sol"; -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; // External Interfaces import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -contract RedeemingBondingCurveBaseV1Mock is RedeemingBondingCurveBase_v1 { +contract RedeemingBondingCurveBaseV1Mock is RedeemingBondingCurveBase_v2 { IBancorFormula public formula; // ------------------------------------------------------------------------- // Override Functions function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); ( @@ -69,7 +69,7 @@ contract RedeemingBondingCurveBaseV1Mock is RedeemingBondingCurveBase_v1 { function _redeemTokensFormulaWrapper(uint _depositAmount) internal pure - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint) { // Since this is a mock, we will always redeem the same amount of tokens as have been deposited @@ -110,14 +110,14 @@ contract RedeemingBondingCurveBaseV1Mock is RedeemingBondingCurveBase_v1 { function getStaticPriceForSelling() external view - override(RedeemingBondingCurveBase_v1) + override(RedeemingBondingCurveBase_v2) returns (uint) {} function getStaticPriceForBuying() external view - override(BondingCurveBase_v1, IBondingCurveBase_v1) + override(BondingCurveBase_v2, IBondingCurveBase_v2) returns (uint) {} diff --git a/test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v1_Exposed.sol b/test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v2_Exposed.sol similarity index 55% rename from test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v1_Exposed.sol rename to test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v2_Exposed.sol index d9193c917..49bf68cdc 100644 --- a/test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v1_Exposed.sol +++ b/test/mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v2_Exposed.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {FM_EXT_TokenVault_v1} from "@fm/extensions/FM_EXT_TokenVault_v1.sol"; +import {FM_EXT_TokenVault_v2} from "@fm/extensions/FM_EXT_TokenVault_v2.sol"; -contract FM_EXT_TokenVault_v1_Exposed is FM_EXT_TokenVault_v1 { +contract FM_EXT_TokenVault_v2_Exposed is FM_EXT_TokenVault_v2 { function exposed_amountIsValid(uint amt_) external pure { _ensureAmountIsValid(amt_); } diff --git a/test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1_Exposed.sol b/test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2_Exposed.sol similarity index 95% rename from test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1_Exposed.sol rename to test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2_Exposed.sol index 10182e5ef..88dc24883 100644 --- a/test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1_Exposed.sol +++ b/test/mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2_Exposed.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.23; -import {FM_PC_Oracle_Redeeming_v1} from - "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.sol"; +import {FM_PC_Oracle_Redeeming_v2} from + "src/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.sol"; -contract FM_PC_Oracle_Redeeming_v1_Exposed is FM_PC_Oracle_Redeeming_v1 { +contract FM_PC_Oracle_Redeeming_v2_Exposed is FM_PC_Oracle_Redeeming_v2 { function exposed_setProjectTreasury(address projectTreasury_) public { _setProjectTreasury(projectTreasury_); } diff --git a/test/mocks/modules/logicModule/InvalidOraclePrice_Mock.sol b/test/mocks/modules/logicModule/InvalidOraclePrice_Mock.sol index fb231e6a7..418874255 100644 --- a/test/mocks/modules/logicModule/InvalidOraclePrice_Mock.sol +++ b/test/mocks/modules/logicModule/InvalidOraclePrice_Mock.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; -import "src/modules/base/Module_v1.sol"; +import "src/modules/base/Module_v2.sol"; -contract InvalidOraclePrice_Mock is Module_v1 { +contract InvalidOraclePrice_Mock is Module_v2 { function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory /* configData */ - ) public override(Module_v1) initializer { + ) public override(Module_v2) initializer { __Module_init(orchestrator_, metadata); } } diff --git a/test/mocks/modules/logicModule/LM_Oracle_Permissioned_v1_Exposed.sol b/test/mocks/modules/logicModule/LM_Oracle_Permissioned_v2_Exposed.sol similarity index 64% rename from test/mocks/modules/logicModule/LM_Oracle_Permissioned_v1_Exposed.sol rename to test/mocks/modules/logicModule/LM_Oracle_Permissioned_v2_Exposed.sol index 56ebebfe3..bc7df1817 100644 --- a/test/mocks/modules/logicModule/LM_Oracle_Permissioned_v1_Exposed.sol +++ b/test/mocks/modules/logicModule/LM_Oracle_Permissioned_v2_Exposed.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.23; -import {LM_Oracle_Permissioned_v1} from "@lm/LM_Oracle_Permissioned_v1.sol"; +import {LM_Oracle_Permissioned_v2} from "@lm/LM_Oracle_Permissioned_v2.sol"; -contract LM_Oracle_Permissioned_v1_Exposed is LM_Oracle_Permissioned_v1 { +contract LM_Oracle_Permissioned_v2_Exposed is LM_Oracle_Permissioned_v2 { function exposed_setIssuancePrice(uint price_) public { _setIssuancePrice(price_); } diff --git a/test/mocks/modules/logicModule/LM_PC_Bounties_v2_Exposed.sol b/test/mocks/modules/logicModule/LM_PC_Bounties_v3_Exposed.sol similarity index 90% rename from test/mocks/modules/logicModule/LM_PC_Bounties_v2_Exposed.sol rename to test/mocks/modules/logicModule/LM_PC_Bounties_v3_Exposed.sol index 9ad50b489..b7851bffc 100644 --- a/test/mocks/modules/logicModule/LM_PC_Bounties_v2_Exposed.sol +++ b/test/mocks/modules/logicModule/LM_PC_Bounties_v3_Exposed.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal Dependencies -import {LM_PC_Bounties_v2} from "@lm/LM_PC_Bounties_v2.sol"; +import {LM_PC_Bounties_v3} from "@lm/LM_PC_Bounties_v3.sol"; -contract LM_PC_Bounties_v2_Exposed is LM_PC_Bounties_v2 { +contract LM_PC_Bounties_v3_Exposed is LM_PC_Bounties_v3 { //-------------------------------------------------------------------------- // Modifier Access diff --git a/test/mocks/modules/logicModule/LM_PC_PaymentRouter_v2_Exposed.sol b/test/mocks/modules/logicModule/LM_PC_PaymentRouter_v3_Exposed.sol similarity index 75% rename from test/mocks/modules/logicModule/LM_PC_PaymentRouter_v2_Exposed.sol rename to test/mocks/modules/logicModule/LM_PC_PaymentRouter_v3_Exposed.sol index 1762b725b..a0c932c9f 100644 --- a/test/mocks/modules/logicModule/LM_PC_PaymentRouter_v2_Exposed.sol +++ b/test/mocks/modules/logicModule/LM_PC_PaymentRouter_v3_Exposed.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal Dependencies -import {LM_PC_PaymentRouter_v2} from "@lm/LM_PC_PaymentRouter_v2.sol"; +import {LM_PC_PaymentRouter_v3} from "@lm/LM_PC_PaymentRouter_v3.sol"; -contract LM_PC_PaymentRouter_v2_Exposed is LM_PC_PaymentRouter_v2 { +contract LM_PC_PaymentRouter_v3_Exposed is LM_PC_PaymentRouter_v3 { //-------------------------------------------------------------------------- // Internal Functions diff --git a/test/mocks/modules/logicModule/LM_PC_Staking_v2_Exposed.sol b/test/mocks/modules/logicModule/LM_PC_Staking_v3_Exposed.sol similarity index 93% rename from test/mocks/modules/logicModule/LM_PC_Staking_v2_Exposed.sol rename to test/mocks/modules/logicModule/LM_PC_Staking_v3_Exposed.sol index 15fde58c2..950c0d5f0 100644 --- a/test/mocks/modules/logicModule/LM_PC_Staking_v2_Exposed.sol +++ b/test/mocks/modules/logicModule/LM_PC_Staking_v3_Exposed.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; // Internal Dependencies -import {LM_PC_Staking_v2} from "@lm/LM_PC_Staking_v2.sol"; +import {LM_PC_Staking_v3} from "@lm/LM_PC_Staking_v3.sol"; -contract LM_PC_Staking_v2_Exposed is LM_PC_Staking_v2 { +contract LM_PC_Staking_v3_Exposed is LM_PC_Staking_v3 { //-------------------------------------------------------------------------- // Getter Functions diff --git a/test/mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol b/test/mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol index 276f6cf2c..dd72241db 100644 --- a/test/mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol +++ b/test/mocks/modules/logicModule/Mock_LM_PC_PaymentRouter_Everclear_v1.sol @@ -1,24 +1,24 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; -import {LM_PC_PaymentRouter_v2} from - "src/modules/logicModule/LM_PC_PaymentRouter_v2.sol"; -import {PP_Everclear_CrossChain_v1} from - "src/modules/paymentProcessor/PP_Everclear_CrossChain_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol"; +import {LM_PC_PaymentRouter_v3} from + "src/modules/logicModule/LM_PC_PaymentRouter_v3.sol"; +import {PP_Everclear_CrossChain_v2} from + "src/modules/paymentProcessor/PP_Everclear_CrossChain_v2.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol"; -contract Mock_LM_PC_PaymentRouter_Everclear_v1 is LM_PC_PaymentRouter_v2 { - // Local constants mirroring PP_Everclear_CrossChain_v1 +contract Mock_LM_PC_PaymentRouter_Everclear_v1 is LM_PC_PaymentRouter_v3 { + // Local constants mirroring PP_Everclear_CrossChain_v2 uint8 public constant LOCAL_FLAG_MAX_FEE = 5; uint8 public constant LOCAL_FLAG_TTL = 6; function init( - IOrchestrator_v1 orchestrator_, - IModule_v1.Metadata memory metadata, + IOrchestrator_v2 orchestrator_, + IModule_v2.Metadata memory metadata, bytes memory /* configData */ ) external override initializer { __Module_init(orchestrator_, metadata); @@ -29,7 +29,7 @@ contract Mock_LM_PC_PaymentRouter_Everclear_v1 is LM_PC_PaymentRouter_v2 { | uint(1 << LOCAL_FLAG_TTL) ); - __ERC20PaymentClientBase_v2_init(combinedFlags); + __ERC20PaymentClientBase_v3_init(combinedFlags); } function pushCrossChainPaymentEverclear( @@ -39,7 +39,7 @@ contract Mock_LM_PC_PaymentRouter_Everclear_v1 is LM_PC_PaymentRouter_v2 { uint targetChainId, uint24 maxFee, uint48 ttl - ) public onlyModuleRole(PAYMENT_PUSHER_ROLE) { + ) public permissioned { // Prepare payment parameters array for Everclear-specific data bytes32[] memory paymentParamsForEverclear = new bytes32[](3); paymentParamsForEverclear[0] = bytes32(block.timestamp); // For FLAG_START @@ -81,7 +81,7 @@ contract Mock_LM_PC_PaymentRouter_Everclear_v1 is LM_PC_PaymentRouter_v2 { // Call the payment processor to process the payments __Module_orchestrator.paymentProcessor().processPayments( - IERC20PaymentClientBase_v2(address(this)) + IERC20PaymentClientBase_v3(address(this)) ); } diff --git a/test/mocks/modules/logicModule/OraclePrice_Mock.sol b/test/mocks/modules/logicModule/OraclePrice_Mock.sol index 3715a83e3..fe983cb13 100644 --- a/test/mocks/modules/logicModule/OraclePrice_Mock.sol +++ b/test/mocks/modules/logicModule/OraclePrice_Mock.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.23; import "@lm/interfaces/IOraclePrice_v1.sol"; -import "src/modules/base/Module_v1.sol"; +import "src/modules/base/Module_v2.sol"; -contract OraclePrice_Mock is IOraclePrice_v1, Module_v1 { +contract OraclePrice_Mock is IOraclePrice_v1, Module_v2 { uint private _priceForIssuance; uint private _priceForRedemption; @@ -20,10 +20,10 @@ contract OraclePrice_Mock is IOraclePrice_v1, Module_v1 { } function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory /* configData */ - ) public override(Module_v1) initializer { + ) public override(Module_v2) initializer { __Module_init(orchestrator_, metadata); _priceForIssuance = 1e6; // Default price 1:1 _priceForRedemption = 1e6; // Default price 1:1 diff --git a/test/mocks/modules/logicModule/oracle/OptimisiticOracleIntegratorMock.sol b/test/mocks/modules/logicModule/oracle/OptimisticOracleIntegrator_v3_Mock.sol similarity index 75% rename from test/mocks/modules/logicModule/oracle/OptimisiticOracleIntegratorMock.sol rename to test/mocks/modules/logicModule/oracle/OptimisticOracleIntegrator_v3_Mock.sol index 70382ec95..38adf909c 100644 --- a/test/mocks/modules/logicModule/oracle/OptimisiticOracleIntegratorMock.sol +++ b/test/mocks/modules/logicModule/oracle/OptimisticOracleIntegrator_v3_Mock.sol @@ -2,17 +2,17 @@ pragma solidity 0.8.23; // Internal Dependencies -import {Module_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2} from "src/modules/base/Module_v2.sol"; // Internal Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import { - OptimisticOracleIntegrator, - IOptimisticOracleIntegrator + OptimisticOracleIntegrator_v3, + IOptimisticOracleIntegrator_v3 } from - "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol"; + "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol"; // External Dependencies import {OptimisticOracleV3CallbackRecipientInterface} from @@ -22,7 +22,7 @@ import {OptimisticOracleV3Interface} from import {ClaimData} from "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/optimistic-oracle-v3/ClaimData.sol"; -contract OptimisticOracleIntegratorMock is OptimisticOracleIntegrator { +contract OptimisticOracleIntegrator_v3_Mock is OptimisticOracleIntegrator_v3 { function assertionResolvedCallback( bytes32 assertionId, bool assertedTruthfully diff --git a/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2_Exposed.sol b/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2_Exposed.sol index e22eb9aa9..541b7c775 100644 --- a/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2_Exposed.sol +++ b/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2_Exposed.sol @@ -1,41 +1,41 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; import { - Module_v1, - IModule_v1, - IOrchestrator_v1 -} from "src/modules/base/Module_v1.sol"; + Module_v2, + IModule_v2, + IOrchestrator_v2 +} from "src/modules/base/Module_v2.sol"; // SuT import { - ERC20PaymentClientBase_v2, - IERC20PaymentClientBase_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + IERC20PaymentClientBase_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // Internal Interfaces -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // Mocks import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -contract ERC20PaymentClientBaseV2_Exposed is ERC20PaymentClientBase_v2 { +contract ERC20PaymentClientBaseV2_Exposed is ERC20PaymentClientBase_v3 { mapping(address => bool) authorized; function init( - IOrchestrator_v1 orchestrator_, + IOrchestrator_v2 orchestrator_, Metadata memory metadata, bytes memory // configData - ) external override(Module_v1) initializer { + ) external override(Module_v2) initializer { __Module_init(orchestrator_, metadata); } //-------------------------------------------------------------------------- - // IERC20PaymentClientBase_v2 Wrapper Functions + // IERC20PaymentClientBase_v3 Wrapper Functions function exposed_addPaymentOrder(PaymentOrder memory order) external { _addPaymentOrder(order); @@ -71,14 +71,14 @@ contract ERC20PaymentClientBaseV2_Exposed is ERC20PaymentClientBase_v2 { } function exposed_ensureTokenAllowance( - IPaymentProcessor_v2 spender, + IPaymentProcessor_v3 spender, address token ) external { return _ensureTokenAllowance(spender, token); } function exposed_isAuthorizedPaymentProcessor( - IPaymentProcessor_v2 processor + IPaymentProcessor_v3 processor ) external view returns (bool) { return _isAuthorizedPaymentProcessor(processor); } diff --git a/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol b/test/mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol similarity index 75% rename from test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol rename to test/mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol index 8a28148bf..b4c3f6df1 100644 --- a/test/mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol +++ b/test/mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol @@ -1,23 +1,23 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; // SuT import { - ERC20PaymentClientBase_v2, - IERC20PaymentClientBase_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; + ERC20PaymentClientBase_v3, + IERC20PaymentClientBase_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; // Internal Interfaces -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; // Mocks import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { +contract ERC20PaymentClientBase_v3_Mock is ERC20PaymentClientBase_v3 { ERC20Mock token; mapping(address => uint) public amountPaidCounter; @@ -30,7 +30,7 @@ contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { authorized[who] = to; } - function setOrchestrator(IOrchestrator_v1 orchestrator) external { + function setOrchestrator(IOrchestrator_v2 orchestrator) external { __Module_orchestrator = orchestrator; } @@ -38,7 +38,7 @@ contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { token = token_; } //-------------------------------------------------------------------------- - // IERC20PaymentClientBase_v2 Wrapper Functions + // IERC20PaymentClientBase_v3 Wrapper Functions function exposed_addPaymentOrder(PaymentOrder memory order) external { _addPaymentOrder(order); @@ -74,11 +74,11 @@ contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { } //-------------------------------------------------------------------------- - // IERC20PaymentClientBase_v2 Overriden Functions + // IERC20PaymentClientBase_v3 Overriden Functions function _ensureTokenBalance(address token_) internal - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) { uint amount = _outstandingTokenAmounts[token_]; @@ -90,17 +90,17 @@ contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { } } - function _ensureTokenAllowance(IPaymentProcessor_v2 spender, address _token) + function _ensureTokenAllowance(IPaymentProcessor_v3 spender, address _token) internal - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) { token.approve(address(spender), _outstandingTokenAmounts[_token]); } - function _isAuthorizedPaymentProcessor(IPaymentProcessor_v2) + function _isAuthorizedPaymentProcessor(IPaymentProcessor_v3) internal view - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) returns (bool) { return authorized[_msgSender()]; @@ -108,7 +108,7 @@ contract ERC20PaymentClientBaseV2Mock is ERC20PaymentClientBase_v2 { function amountPaid(address _token, uint amount) public - override(ERC20PaymentClientBase_v2) + override(ERC20PaymentClientBase_v3) { amountPaidCounter[_token] += amount; diff --git a/test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v1_Exposed.sol b/test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v2_Exposed.sol similarity index 81% rename from test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v1_Exposed.sol rename to test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v2_Exposed.sol index d549a9ca9..a9133c6a0 100644 --- a/test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v1_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v2_Exposed.sol @@ -1,29 +1,29 @@ pragma solidity 0.8.23; // Internal -import {PP_Everclear_CrossChain_v1} from "@pp/PP_Everclear_CrossChain_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {PP_Everclear_CrossChain_v2} from "@pp/PP_Everclear_CrossChain_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; import {IEverclear} from "@pp/interfaces/IEverclear.sol"; // External import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -contract PP_Everclear_CrossChain_v1_Exposed is PP_Everclear_CrossChain_v1 { +contract PP_Everclear_CrossChain_v2_Exposed is PP_Everclear_CrossChain_v2 { // Expose internal _executeBridgeTransfer function function exposed_executeBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external { _executeBridgeTransfer(order); } function exposed_validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external returns (bool) { return _validPaymentOrder(order); } function exposed_transferTokenAndApproveToBridge( - IERC20PaymentClientBase_v2.PaymentOrder memory order, + IERC20PaymentClientBase_v3.PaymentOrder memory order, address client ) external { _transferTokenAndApproveToBridge(order, client); @@ -31,7 +31,7 @@ contract PP_Everclear_CrossChain_v1_Exposed is PP_Everclear_CrossChain_v1 { // Expose internal xcall function function exposed_createCrossChainIntent( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external returns (bytes32 intentId_, IEverclear.Intent memory intent_) { return _createCrossChainIntent(order); } @@ -67,7 +67,7 @@ contract PP_Everclear_CrossChain_v1_Exposed is PP_Everclear_CrossChain_v1 { } function exposed_processSuccessfulBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_, bytes32 intentId_, IEverclear.Intent memory intent_ @@ -76,7 +76,7 @@ contract PP_Everclear_CrossChain_v1_Exposed is PP_Everclear_CrossChain_v1 { } function exposed_processFailedBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) external { _processFailedBridgeTransfer(order_, client_); diff --git a/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Exposed.sol b/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Exposed.sol similarity index 74% rename from test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Exposed.sol rename to test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Exposed.sol index bf10a9e28..c4580be7c 100644 --- a/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Exposed.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; -import {PP_Queue_ManualExecution_v1} from "@pp/PP_Queue_ManualExecution_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; +import {PP_Queue_ManualExecution_v2} from "@pp/PP_Queue_ManualExecution_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; -contract PP_Queue_ManualExecution_v1_Exposed is PP_Queue_ManualExecution_v1 { +contract PP_Queue_ManualExecution_v2_Exposed is PP_Queue_ManualExecution_v2 { using LinkedIdList for LinkedIdList.List; function exposed_addPaymentOrderToQueue( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) external returns (uint) { return _addPaymentOrderToQueue(order_, client_); diff --git a/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Mock.sol b/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Mock.sol similarity index 65% rename from test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Mock.sol rename to test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Mock.sol index 3d4b40120..8b50feca7 100644 --- a/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Mock.sol +++ b/test/mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Mock.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; -contract PP_Queue_ManualExecution_v1_Mock is PaymentProcessorV1Mock { +contract PP_Queue_ManualExecution_v2_Mock is PaymentProcessor_v3_Mock { //-------------------------------------------------------------------------- - // PP_Queue_ManualExecution_v1_Mock Functions + // PP_Queue_ManualExecution_v2_Mock Functions function executePaymentQueue(address /*client_*/ ) external { emit PaymentOrderProcessed( diff --git a/test/mocks/modules/paymentProcessor/PP_Queue_v1_Exposed.sol b/test/mocks/modules/paymentProcessor/PP_Queue_v2_Exposed.sol similarity index 91% rename from test/mocks/modules/paymentProcessor/PP_Queue_v1_Exposed.sol rename to test/mocks/modules/paymentProcessor/PP_Queue_v2_Exposed.sol index a113dabd1..952083f55 100644 --- a/test/mocks/modules/paymentProcessor/PP_Queue_v1_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/PP_Queue_v2_Exposed.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.23; -import {PP_Queue_v1} from "@pp/PP_Queue_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; +import {PP_Queue_v2} from "@pp/PP_Queue_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; -contract PP_Queue_v1_Exposed is PP_Queue_v1 { +contract PP_Queue_v2_Exposed is PP_Queue_v2 { using LinkedIdList for LinkedIdList.List; // Override _msgSender para simplificar testing @@ -40,7 +40,7 @@ contract PP_Queue_v1_Exposed is PP_Queue_v1 { } function exposed_addPaymentOrderToQueue( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) external returns (uint) { return _addPaymentOrderToQueue(order_, client_); @@ -132,13 +132,13 @@ contract PP_Queue_v1_Exposed is PP_Queue_v1 { function exposed_orderExists( uint orderId_, - IERC20PaymentClientBase_v2 client_ + IERC20PaymentClientBase_v3 client_ ) external view returns (bool) { return _orderExists(orderId_, client_); } function exposed_addUnclaimableOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) external { _addToUnclaimableAmount( @@ -147,7 +147,7 @@ contract PP_Queue_v1_Exposed is PP_Queue_v1 { } function exposed_validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order_ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ ) external view returns (bool) { return _validPaymentOrder(order_); } diff --git a/test/mocks/modules/paymentProcessor/PP_Simple_v2_Exposed.sol b/test/mocks/modules/paymentProcessor/PP_Simple_v3_Exposed.sol similarity index 80% rename from test/mocks/modules/paymentProcessor/PP_Simple_v2_Exposed.sol rename to test/mocks/modules/paymentProcessor/PP_Simple_v3_Exposed.sol index 39e947e9a..8a681f8fa 100644 --- a/test/mocks/modules/paymentProcessor/PP_Simple_v2_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/PP_Simple_v3_Exposed.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // Internal Dependencies -import {PP_Simple_v2} from "@pp/PP_Simple_v2.sol"; +import {PP_Simple_v3} from "@pp/PP_Simple_v3.sol"; -contract PP_Simple_v2_Exposed is PP_Simple_v2 { +contract PP_Simple_v3_Exposed is PP_Simple_v3 { function exposed_validPaymentReceiver(address addr) external view diff --git a/test/mocks/modules/paymentProcessor/PP_Streaming_v2_Exposed.sol b/test/mocks/modules/paymentProcessor/PP_Streaming_v3_Exposed.sol similarity index 88% rename from test/mocks/modules/paymentProcessor/PP_Streaming_v2_Exposed.sol rename to test/mocks/modules/paymentProcessor/PP_Streaming_v3_Exposed.sol index bac2cb369..cfd10097e 100644 --- a/test/mocks/modules/paymentProcessor/PP_Streaming_v2_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/PP_Streaming_v3_Exposed.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // Internal Dependencies -import {PP_Streaming_v2} from "@pp/PP_Streaming_v2.sol"; +import {PP_Streaming_v3} from "@pp/PP_Streaming_v3.sol"; -contract PP_Streaming_v2_Exposed is PP_Streaming_v2 { +contract PP_Streaming_v3_Exposed is PP_Streaming_v3 { //-------------------------------------------------------------------------- // Getter Functions diff --git a/test/mocks/modules/paymentProcessor/PaymentProcessor_v1_Mock.sol b/test/mocks/modules/paymentProcessor/PaymentProcessor_v1_Mock.sol new file mode 100644 index 000000000..094970774 --- /dev/null +++ b/test/mocks/modules/paymentProcessor/PaymentProcessor_v1_Mock.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +import {ERC165} from "@oz/utils/introspection/ERC165.sol"; + +import {IPaymentProcessor_v1} from + "src/modules/paymentProcessor/IPaymentProcessor_v1.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IModule_v2} from "src/modules/base/Module_v2.sol"; + +contract PaymentProcessor_v1_Mock is IPaymentProcessor_v1, ERC165 { + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC165) + returns (bool) + { + return interfaceId == type(IPaymentProcessor_v1).interfaceId + || interfaceId == type(IModule_v2).interfaceId + || super.supportsInterface(interfaceId); + } + + uint public processPaymentsTriggered; + bool public validOrder = true; + + //-------------------------------------------------------------------------- + // IPaymentProcessor_v1 Functions + + function processPayments(IERC20PaymentClientBase_v3 /*client*/ ) external { + emit PaymentOrderProcessed( + address(0), + address(0), + address(0), + 0, + 0, + 0, + bytes32(0), + new bytes32[](0) + ); + processPaymentsTriggered += 1; + } + + function cancelRunningPayments(IERC20PaymentClientBase_v3) external {} + + function token() external pure returns (IERC20) { + return IERC20(address(0)); + } + + function deleteAllPayments(IERC20PaymentClientBase_v3 client) external { + client.collectPaymentOrders(); + } + + function unclaimable(address, address, address) + external + view + returns (uint) + {} + + function claimPreviouslyUnclaimable(address, address, address) external {} + + function validPaymentOrder(IERC20PaymentClientBase_v3.PaymentOrder memory) + external + view + returns (bool) + { + return validOrder; + } + + // Mock Functions + + function flipValidOrder() external { + validOrder = !validOrder; + } +} diff --git a/test/mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol b/test/mocks/modules/paymentProcessor/PaymentProcessor_v2_Mock.sol similarity index 67% rename from test/mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol rename to test/mocks/modules/paymentProcessor/PaymentProcessor_v2_Mock.sol index c30ca4459..ef27d8825 100644 --- a/test/mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol +++ b/test/mocks/modules/paymentProcessor/PaymentProcessor_v2_Mock.sol @@ -7,11 +7,11 @@ import {ERC165} from "@oz/utils/introspection/ERC165.sol"; import {IPaymentProcessor_v2} from "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; -import {IModule_v1} from "src/modules/base/Module_v1.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IModule_v2} from "src/modules/base/Module_v2.sol"; -contract PaymentProcessorV1Mock is IPaymentProcessor_v2, ERC165 { +contract PaymentProcessor_v2_Mock is IPaymentProcessor_v2, ERC165 { function supportsInterface(bytes4 interfaceId) public view @@ -19,11 +19,8 @@ contract PaymentProcessorV1Mock is IPaymentProcessor_v2, ERC165 { override(ERC165) returns (bool) { - bytes4 interfaceId_IPaymentProcessor = - type(IPaymentProcessor_v2).interfaceId; - bytes4 interfaceId_IModule = type(IModule_v1).interfaceId; - return interfaceId == interfaceId_IPaymentProcessor - || interfaceId == interfaceId_IModule + return interfaceId == type(IPaymentProcessor_v2).interfaceId + || interfaceId == type(IModule_v2).interfaceId || super.supportsInterface(interfaceId); } @@ -33,7 +30,7 @@ contract PaymentProcessorV1Mock is IPaymentProcessor_v2, ERC165 { //-------------------------------------------------------------------------- // IPaymentProcessor_v2 Functions - function processPayments(IERC20PaymentClientBase_v2 /*client*/ ) external { + function processPayments(IERC20PaymentClientBase_v3 /*client*/ ) external { emit PaymentOrderProcessed( address(0), address(0), @@ -47,13 +44,13 @@ contract PaymentProcessorV1Mock is IPaymentProcessor_v2, ERC165 { processPaymentsTriggered += 1; } - function cancelRunningPayments(IERC20PaymentClientBase_v2) external {} + function cancelRunningPayments(IERC20PaymentClientBase_v3) external {} function token() external pure returns (IERC20) { return IERC20(address(0)); } - function deleteAllPayments(IERC20PaymentClientBase_v2 client) external { + function deleteAllPayments(IERC20PaymentClientBase_v3 client) external { client.collectPaymentOrders(); } @@ -65,7 +62,7 @@ contract PaymentProcessorV1Mock is IPaymentProcessor_v2, ERC165 { function claimPreviouslyUnclaimable(address, address, address) external {} - function validPaymentOrder(IERC20PaymentClientBase_v2.PaymentOrder memory) + function validPaymentOrder(IERC20PaymentClientBase_v3.PaymentOrder memory) external view returns (bool) diff --git a/test/mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol b/test/mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol new file mode 100644 index 000000000..c1704a840 --- /dev/null +++ b/test/mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +import {ERC165} from "@oz/utils/introspection/ERC165.sol"; + +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; +import {IModule_v2} from "src/modules/base/Module_v2.sol"; + +contract PaymentProcessor_v3_Mock is IPaymentProcessor_v3, ERC165 { + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC165) + returns (bool) + { + return interfaceId == type(IPaymentProcessor_v3).interfaceId + || interfaceId == type(IModule_v2).interfaceId + || super.supportsInterface(interfaceId); + } + + uint public processPaymentsTriggered; + bool public validOrder = true; + + //-------------------------------------------------------------------------- + // IPaymentProcessor_v3 Functions + + function processPayments(IERC20PaymentClientBase_v3 /*client*/ ) external { + emit PaymentOrderProcessed( + address(0), + address(0), + address(0), + 0, + 0, + 0, + bytes32(0), + new bytes32[](0) + ); + processPaymentsTriggered += 1; + } + + function cancelRunningPayments(IERC20PaymentClientBase_v3) external {} + + function token() external pure returns (IERC20) { + return IERC20(address(0)); + } + + function deleteAllPayments(IERC20PaymentClientBase_v3 client) external { + client.collectPaymentOrders(); + } + + function unclaimable(address, address, address) + external + view + returns (uint) + {} + + function claimPreviouslyUnclaimable(address, address, address) external {} + + function validPaymentOrder(IERC20PaymentClientBase_v3.PaymentOrder memory) + external + view + returns (bool) + { + return validOrder; + } + + // Mock Functions + + function flipValidOrder() external { + validOrder = !validOrder; + } +} diff --git a/test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1_Exposed.sol b/test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2_Exposed.sol similarity index 79% rename from test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1_Exposed.sol rename to test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2_Exposed.sol index 17a3ad5cc..3ceea08ff 100644 --- a/test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1_Exposed.sol +++ b/test/mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2_Exposed.sol @@ -2,25 +2,25 @@ pragma solidity 0.8.23; // Internal -import {PP_CrossChainBase_v1} from "@pp/abstracts/PP_CrossChainBase_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {PP_CrossChainBase_v2} from "@pp/abstracts/PP_CrossChainBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -contract PP_CrossChainBase_v1_Exposed is PP_CrossChainBase_v1 { +contract PP_CrossChainBase_v2_Exposed is PP_CrossChainBase_v2 { // ========================================================================= // Implement interface and abstract functions - function processPayments(IERC20PaymentClientBase_v2 client) external {} + function processPayments(IERC20PaymentClientBase_v3 client) external {} function validPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory /* order */ + IERC20PaymentClientBase_v3.PaymentOrder memory /* order */ ) external pure returns (bool valid_) { return true; } function _executeBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) internal override {} // ========================================================================= @@ -51,7 +51,7 @@ contract PP_CrossChainBase_v1_Exposed is PP_CrossChainBase_v1 { } function exposed_executeBridgeTransfer( - IERC20PaymentClientBase_v2.PaymentOrder memory order + IERC20PaymentClientBase_v3.PaymentOrder memory order ) external { return _executeBridgeTransfer(order); } diff --git a/test/mocks/orchestrator/OrchestratorV1Mock.sol b/test/mocks/orchestrator/OrchestratorV1Mock.sol index e96b8becd..322a3ff8a 100644 --- a/test/mocks/orchestrator/OrchestratorV1Mock.sol +++ b/test/mocks/orchestrator/OrchestratorV1Mock.sol @@ -5,13 +5,13 @@ import { ModuleManagerBase_v1, IModuleManagerBase_v1 } from "src/orchestrator/abstracts/ModuleManagerBase_v1.sol"; -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; -contract OrchestratorV1Mock is Orchestrator_v1 { +contract OrchestratorV1Mock is Orchestrator_v2 { bool connectToTrustedForwarder = false; bool public interceptData; - constructor(address _trustedForwarder) Orchestrator_v1(_trustedForwarder) {} + constructor(address _trustedForwarder) Orchestrator_v2(_trustedForwarder) {} function flipConnectToTrustedForwarder() external { connectToTrustedForwarder = !connectToTrustedForwarder; @@ -21,7 +21,7 @@ contract OrchestratorV1Mock is Orchestrator_v1 { public view virtual - override(Orchestrator_v1) + override(Orchestrator_v2) returns (bool) { if (connectToTrustedForwarder) { diff --git a/test/mocks/orchestrator/OrchestratorV1_Exposed.sol b/test/mocks/orchestrator/OrchestratorV1_Exposed.sol deleted file mode 100644 index c7a888314..000000000 --- a/test/mocks/orchestrator/OrchestratorV1_Exposed.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -// Internal Interfaces -import {IModuleManagerBase_v1} from - "src/orchestrator/interfaces/IModuleManagerBase_v1.sol"; -import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; -import {IGovernor_v1} from "@ex/governance/interfaces/IGovernor_v1.sol"; - -contract OrchestratorV1AccessMock is IOrchestrator_v1 { - IERC20 public token; - IPaymentProcessor_v2 public paymentProcessor; - IFundingManager_v1 public fundingManager; - IGovernor_v1 public governor; - - function cancelAuthorizerUpdate(IAuthorizer_v1 authorizer_) external {} - - function cancelPaymentProcessorUpdate( - IPaymentProcessor_v2 paymentProcessor_ - ) external {} - - function cancelFundingManagerUpdate(IFundingManager_v1 fundingManager_) - external - {} - - function cancelModuleUpdate(address module) external {} - - function initiateAddModuleWithTimelock(address module) external {} - - function initiateRemoveModuleWithTimelock(address module) external {} - - function executeAddModule(address module) external {} - - function executeRemoveModule(address module) external {} - - function isModule(address module) external view returns (bool) {} - - function listModules() external view returns (address[] memory) {} - - function modulesSize() external view returns (uint8) {} - - function grantRole(bytes32 role, address account) external {} - - function revokeRole(bytes32 role, address account) external {} - - function renounceRole(address module, bytes32 role) external {} - - function hasRole(address module, bytes32 role, address account) - external - returns (bool) - {} - - function init( - uint, - address, - address[] calldata, - IFundingManager_v1, - IAuthorizer_v1, - IPaymentProcessor_v2, - IGovernor_v1 - ) external {} - - function initiateSetAuthorizerWithTimelock(IAuthorizer_v1 authorizer_) - external - {} - - function initiateSetFundingManagerWithTimelock( - IFundingManager_v1 fundingManager_ - ) external {} - - function initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2 paymentProcessor_ - ) external {} - - function executeSetAuthorizer(IAuthorizer_v1 authorizer_) external {} - - function executeSetFundingManager(IFundingManager_v1 fundingManager_) - external - { - fundingManager = fundingManager_; - } - - function executeSetPaymentProcessor(IPaymentProcessor_v2 paymentProcessor_) - external - { - paymentProcessor = paymentProcessor_; - } - - function orchestratorId() external view returns (uint) {} - - function authorizer() external view returns (IAuthorizer_v1) {} - - function version() external pure returns (string memory) {} - - function manager() external view returns (address) {} - - function verifyAddressIsPaymentProcessor(address paymentProcessorAddress) - external - view - returns (bool) - {} - - function verifyAddressIsRecurringPaymentManager( - address recurringPaymentManager - ) external view returns (bool) {} - - function verifyAddressIsFundingManager(address fundingManagerAddress) - external - view - returns (bool) - {} - - function verifyAddressIsAuthorizerModule(address authModule) - external - view - returns (bool) - {} - - function isTrustedForwarder(address forwarder) - external - view - returns (bool) - {} - - function trustedForwarder() external view returns (address) {} - - //------------------------------------------------------------------- - // Mock Helper Functions - function setToken(IERC20 token_) external { - token = token_; - } -} diff --git a/test/mocks/orchestrator/Orchestrator_v2_Exposed.sol b/test/mocks/orchestrator/Orchestrator_v2_Exposed.sol new file mode 100644 index 000000000..6d597a025 --- /dev/null +++ b/test/mocks/orchestrator/Orchestrator_v2_Exposed.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; + +import {ModuleManagerBase_v1} from + "src/orchestrator/abstracts/ModuleManagerBase_v1.sol"; + +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; + +contract Orchestrator_v2_Exposed is Orchestrator_v2 { + //========================================================================== + // Setup + constructor(address _trustedForwarder) Orchestrator_v2(_trustedForwarder) {} + + function setup_authorizer(address authorizer_) external { + authorizer = IAuthorizer_v2(authorizer_); + } + + //========================================================================== + // Modifier Access + + function modifierPermissionedCheck() external view permissioned {} + + //========================================================================== + // Internal Function Access + + function _checkAuthorization_exposed(address caller_, bytes calldata data_) + external + view + { + _checkAuthorization(caller_, data_); + } + + function _enforcePrivilegedModuleInterfaceCheck_exposed( + address _contractAddr, + bytes4[] memory _privilegedInterfaceId + ) external view { + _enforcePrivilegedModuleInterfaceCheck( + _contractAddr, _privilegedInterfaceId + ); + } + + function _enforceNonPrivilegedModuleInterfaceCheck_exposed( + address _contractAddr + ) external { + _enforceNonPrivilegedModuleInterfaceCheck(_contractAddr); + } +} diff --git a/test/mocks/proxies/IModuleImplementationMock.sol b/test/mocks/proxies/IModuleImplementationMock.sol index 38dfdd318..2cd40469b 100644 --- a/test/mocks/proxies/IModuleImplementationMock.sol +++ b/test/mocks/proxies/IModuleImplementationMock.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; // Internal Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; interface IModuleImplementationMock { diff --git a/test/mocks/proxies/ModuleImplementationV1Mock.sol b/test/mocks/proxies/ModuleImplementationV1Mock.sol index 65f5899bb..a837a87c5 100644 --- a/test/mocks/proxies/ModuleImplementationV1Mock.sol +++ b/test/mocks/proxies/ModuleImplementationV1Mock.sol @@ -1,11 +1,11 @@ pragma solidity ^0.8.0; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; import {IModuleImplementationMock} from "@mocks/proxies/IModuleImplementationMock.sol"; contract ModuleImplementationV1Mock is - ModuleV1Mock, + Module_v2_Mock, IModuleImplementationMock { uint public data; diff --git a/test/mocks/proxies/ModuleImplementationV2Mock.sol b/test/mocks/proxies/ModuleImplementationV2Mock.sol index 9d0f8c298..eaec6ec70 100644 --- a/test/mocks/proxies/ModuleImplementationV2Mock.sol +++ b/test/mocks/proxies/ModuleImplementationV2Mock.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.0; import "@oz/proxy/utils/Initializable.sol"; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; import {IModuleImplementationMock} from "@mocks/proxies/IModuleImplementationMock.sol"; contract ModuleImplementationV2Mock is - ModuleV1Mock, + Module_v2_Mock, IModuleImplementationMock { uint public data; diff --git a/test/testUtilities/CallIntercepter.sol b/test/testUtilities/CallIntercepter.sol index b15ab8ed1..6c812635f 100644 --- a/test/testUtilities/CallIntercepter.sol +++ b/test/testUtilities/CallIntercepter.sol @@ -12,7 +12,7 @@ contract CallIntercepter is Test { } // ERC2771Context - // @dev Because we want to expose the isTrustedForwarder function from the ERC2771Context Contract in the IOrchestrator_v1 + // @dev Because we want to expose the isTrustedForwarder function from the ERC2771Context Contract in the IOrchestrator_v2 // we have to override it here as the original openzeppelin version doesnt contain a interface that we could use to expose it. function isTrustedForwarder(address) public view virtual returns (bool) { return isTrusted; diff --git a/test/testUtilities/ERC165InterfaceMock.sol b/test/testUtilities/ERC165InterfaceMock.sol new file mode 100644 index 000000000..6fe2fcd75 --- /dev/null +++ b/test/testUtilities/ERC165InterfaceMock.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {ERC165Upgradeable} from + "@oz-up/utils/introspection/ERC165Upgradeable.sol"; + +contract ERC165InterfaceMock is ERC165Upgradeable { + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC165Upgradeable) + returns (bool) + { + for (uint i = 0; i < interfaceIds.length; i++) { + if (interfaceIds[i] == interfaceId) { + return true; + } + } + return super.supportsInterface(interfaceId); + } + + bytes4[] public interfaceIds; + + function registerInterface(bytes4 interfaceId) external { + interfaceIds.push(interfaceId); + } + + function unregisterInterface(bytes4 interfaceId) external { + for (uint i = 0; i < interfaceIds.length; i++) { + if (interfaceIds[i] == interfaceId) { + interfaceIds[i] = interfaceIds[interfaceIds.length - 1]; + interfaceIds.pop(); + return; + } + } + } +} diff --git a/test/testUtilities/TypeSanityHelper.sol b/test/testUtilities/TypeSanityHelper.sol index ea1ff8ff4..907b33b59 100644 --- a/test/testUtilities/TypeSanityHelper.sol +++ b/test/testUtilities/TypeSanityHelper.sol @@ -23,8 +23,8 @@ contract TypeSanityHelper is Test { } //-------------------------------------------------------------------------- - // Types for Orchestrator_v1 - // Contract: Orchestrator_v1.sol + // Types for Orchestrator_v2 + // Contract: Orchestrator_v2.sol function assumeValidOrchestratorId(uint id) public pure { vm.assume(id != 0); diff --git a/test/tools/CallIntercepter.sol b/test/tools/CallIntercepter.sol new file mode 100644 index 000000000..6c812635f --- /dev/null +++ b/test/tools/CallIntercepter.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +contract CallIntercepter is Test { + bool isTrusted; + bool public callShouldBreak; + + constructor() { + isTrusted = true; + } + + // ERC2771Context + // @dev Because we want to expose the isTrustedForwarder function from the ERC2771Context Contract in the IOrchestrator_v2 + // we have to override it here as the original openzeppelin version doesnt contain a interface that we could use to expose it. + function isTrustedForwarder(address) public view virtual returns (bool) { + return isTrusted; + } + + function flipIsTrusted() external { + isTrusted = !isTrusted; + } + + function flipCallShouldBreak() external { + callShouldBreak = !callShouldBreak; + } + + event CallReceived(address intercepterAddress, bytes data, address sender); + + error CallReceivedButBroke( + address intercepterAddress, bytes data, address sender + ); + + fallback(bytes calldata) external virtual returns (bytes memory) { + if (callShouldBreak) { + revert CallReceivedButBroke(address(this), msg.data, msg.sender); + } + emit CallReceived(address(this), msg.data, msg.sender); + return (abi.encode("Call Successful")); + } + + receive() external payable { + revert(); + } +} diff --git a/test/tools/OZErrors.sol b/test/tools/OZErrors.sol new file mode 100644 index 000000000..e922f02e7 --- /dev/null +++ b/test/tools/OZErrors.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +/** + * @dev Library providing error types for OpenZeppelin contracts. + */ +library OZErrors { + // Contract: Initializable + bytes4 public constant Initializable__NotInitializing = + bytes4(keccak256("NotInitializing()")); + bytes4 internal constant Initializable__InvalidInitialization = + bytes4(keccak256("InvalidInitialization()")); + + // Contract: Ownable + bytes4 internal constant Ownable__UnauthorizedAccount = + bytes4(keccak256("OwnableUnauthorizedAccount(address)")); +} diff --git a/test/tools/TypeSanityHelper.sol b/test/tools/TypeSanityHelper.sol new file mode 100644 index 000000000..907b33b59 --- /dev/null +++ b/test/tools/TypeSanityHelper.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {Test} from "forge-std/Test.sol"; + +contract TypeSanityHelper is Test { + address private _self; + + constructor(address self) { + _self = self; + } + + //-------------------------------------------------------------------------- + // Helpers + + function assumeElemNotInSet(address[] memory set, address elem) + public + pure + { + for (uint i; i < set.length; ++i) { + vm.assume(elem != set[i]); + } + } + + //-------------------------------------------------------------------------- + // Types for Orchestrator_v2 + // Contract: Orchestrator_v2.sol + + function assumeValidOrchestratorId(uint id) public pure { + vm.assume(id != 0); + } + + //-------------------------------------------------------------------------- + // Types for Module + // Contract: base/ModuleManagerBase_v1.sol + + uint8 private constant MAX_MODULES = 128; + + mapping(address => bool) moduleCache; + + function assumeValidModules(address[] memory modules) public { + vm.assume(modules.length <= MAX_MODULES); + for (uint i; i < modules.length; ++i) { + assumeValidModule(modules[i]); + + // Assume module unique. + vm.assume(!moduleCache[modules[i]]); + + // Add module to cache. + moduleCache[modules[i]] = true; + } + } + + function assumeValidModule(address module) public view { + address[] memory invalids = createInvalidModules(); + + for (uint i; i < invalids.length; ++i) { + vm.assume(module != invalids[i]); + } + } + + function createInvalidModules() public view returns (address[] memory) { + address[] memory invalids = new address[](3); + + invalids[0] = address(0); + invalids[1] = _self; + + return invalids; + } + + //-------------------------------------------------------------------------- + // Types for Funder + // Contract: base/FunderManager.sol + + function assumeValidFunders(address[] memory funders) public {} +} diff --git a/test/unit/external/governance/Governor_v1.t.sol b/test/unit/external/governance/Governor_v1.t.sol index eee4e7e09..7a8622fd0 100644 --- a/test/unit/external/governance/Governor_v1.t.sol +++ b/test/unit/external/governance/Governor_v1.t.sol @@ -18,7 +18,7 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; import {IInverterBeacon_v1} from "src/proxies/interfaces/IInverterBeacon_v1.sol"; import { IModuleFactory_v1, - IModule_v1 + IModule_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; import {InverterBeaconV1OwnableMock} from @@ -118,7 +118,7 @@ contract GovernorV1Test is Test { } function testLinkedBeaconsEmpty(bool empty) public { - IModule_v1.Metadata memory metadata; + IModule_v2.Metadata memory metadata; if (!empty) { vm.prank(communityMultisig); gov.registerMetadataInModuleFactory(metadata, ownedBeaconMock); @@ -432,7 +432,7 @@ contract GovernorV1Test is Test { gov.moduleFactoryInitCallback(newBeacons); // linkedBeaconsEmpty - IModule_v1.Metadata memory metadata; + IModule_v2.Metadata memory metadata; vm.prank(communityMultisig); gov.registerMetadataInModuleFactory(metadata, ownedBeaconMock); vm.expectRevert( @@ -609,7 +609,7 @@ contract GovernorV1Test is Test { // Test: Register Beacons function testRegisterMetadataInModuleFactory() public { - IModule_v1.Metadata memory metadata; + IModule_v2.Metadata memory metadata; vm.prank(communityMultisig); gov.registerMetadataInModuleFactory( @@ -631,7 +631,7 @@ contract GovernorV1Test is Test { ) ); - IModule_v1.Metadata memory metadata; + IModule_v2.Metadata memory metadata; gov.registerMetadataInModuleFactory( metadata, IInverterBeacon_v1(ownedBeaconMock) ); diff --git a/test/unit/factories/ModuleFactory_v1.t.sol b/test/unit/factories/ModuleFactory_v1.t.sol index 34e66ac05..4185f9276 100644 --- a/test/unit/factories/ModuleFactory_v1.t.sol +++ b/test/unit/factories/ModuleFactory_v1.t.sol @@ -12,8 +12,8 @@ import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; // Internal Interfaces import { IModuleFactory_v1, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "src/factories/interfaces/IModuleFactory_v1.sol"; import {IOrchestratorFactory_v1} from @@ -61,14 +61,14 @@ contract ModuleFactoryV1Test is Test { /// @notice Event emitted when new beacon registered for metadata. event MetadataRegistered( - IModule_v1.Metadata metadata, IInverterBeacon_v1 indexed beacon + IModule_v2.Metadata metadata, IInverterBeacon_v1 indexed beacon ); /// @notice Event emitted when new module created for a orchestrator. event ModuleCreated( address indexed orchestrator, address indexed module, - IModule_v1.Metadata metadata + IModule_v2.Metadata metadata ); event GovernorSet(address indexed governor); @@ -80,7 +80,7 @@ contract ModuleFactoryV1Test is Test { string constant URL = "https://github.com/organization/module"; string constant TITLE = "Payment Processor"; - IModule_v1.Metadata DATA = IModule_v1.Metadata( + IModule_v2.Metadata DATA = IModule_v2.Metadata( MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, TITLE ); @@ -96,7 +96,7 @@ contract ModuleFactoryV1Test is Test { emit GovernorSet(address(governor)); factory.init( address(governor), - new IModule_v1.Metadata[](0), + new IModule_v2.Metadata[](0), new IInverterBeacon_v1[](0) ); } @@ -116,15 +116,15 @@ contract ModuleFactoryV1Test is Test { factory = ModuleFactory_v1(Clones.clone(impl)); metadataSets = bound(metadataSets, 1, 10); - IModule_v1.Metadata[] memory metadata = - new IModule_v1.Metadata[](metadataSets); + IModule_v2.Metadata[] memory metadata = + new IModule_v2.Metadata[](metadataSets); IInverterBeacon_v1[] memory beacons = new IInverterBeacon_v1[](metadataSets); InverterBeaconV1OwnableMock beaconI; for (uint i = 0; i < metadataSets; i++) { - metadata[i] = IModule_v1.Metadata( + metadata[i] = IModule_v2.Metadata( i + 1, MINOR_VERSION, PATCH_VERSION, URL, TITLE ); @@ -165,9 +165,9 @@ contract ModuleFactoryV1Test is Test { ); } - IModule_v1.Metadata[] memory metadata = - new IModule_v1.Metadata[](number1); - metadata[0] = IModule_v1.Metadata( + IModule_v2.Metadata[] memory metadata = + new IModule_v2.Metadata[](number1); + metadata[0] = IModule_v2.Metadata( MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, "" ); @@ -194,7 +194,7 @@ contract ModuleFactoryV1Test is Test { factory.registerMetadata(DATA, beacon); } - function testRegisterMetadata(IModule_v1.Metadata memory metadata) public { + function testRegisterMetadata(IModule_v2.Metadata memory metadata) public { _assumeValidMetadata(metadata); beacon.overrideImplementation(address(module)); @@ -220,7 +220,7 @@ contract ModuleFactoryV1Test is Test { ); vm.prank(address(governor)); factory.registerMetadata( - IModule_v1.Metadata( + IModule_v2.Metadata( MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, "", TITLE ), beacon @@ -232,7 +232,7 @@ contract ModuleFactoryV1Test is Test { ); vm.prank(address(governor)); factory.registerMetadata( - IModule_v1.Metadata( + IModule_v2.Metadata( MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, "" ), beacon @@ -301,7 +301,7 @@ contract ModuleFactoryV1Test is Test { function testCreateAndInitModule( IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig, - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, address orchestrator, bytes memory configData ) public { @@ -311,15 +311,15 @@ contract ModuleFactoryV1Test is Test { beacon.overrideImplementation(address(module)); - // Register ModuleV1Mock for given metadata. + // Register Module_v2_Mock for given metadata. vm.prank(address(governor)); factory.registerMetadata(metadata, beacon); // Create new module instance. - IModule_v1 newModule = IModule_v1( + IModule_v2 newModule = IModule_v2( factory.createAndInitModule( metadata, - IOrchestrator_v1(orchestrator), + IOrchestrator_v2(orchestrator), configData, workflowConfig ) @@ -336,7 +336,7 @@ contract ModuleFactoryV1Test is Test { function testCreateModuleProxy( IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig, - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, address orchestrator ) public { _assumeValidMetadata(metadata); @@ -345,7 +345,7 @@ contract ModuleFactoryV1Test is Test { beacon.overrideImplementation(address(module)); - // Register ModuleV1Mock for given metadata. + // Register Module_v2_Mock for given metadata. vm.prank(address(governor)); factory.registerMetadata(metadata, beacon); @@ -356,9 +356,9 @@ contract ModuleFactoryV1Test is Test { emit ModuleCreated(orchestrator, address(0), metadata); // Create new module instance. - IModule_v1 newModule = IModule_v1( + IModule_v2 newModule = IModule_v2( factory.createModuleProxy( - metadata, IOrchestrator_v1(orchestrator), workflowConfig + metadata, IOrchestrator_v2(orchestrator), workflowConfig ) ); @@ -389,7 +389,7 @@ contract ModuleFactoryV1Test is Test { function testCreateModuleReorgResilience( IOrchestratorFactory_v1.WorkflowConfig memory workflowConfig, - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, address orchestrator ) public { address alice = address(0xA11CE); @@ -401,7 +401,7 @@ contract ModuleFactoryV1Test is Test { beacon.overrideImplementation(address(module)); - // Register ModuleV1Mock for given metadata. + // Register Module_v2_Mock for given metadata. vm.prank(address(governor)); factory.registerMetadata(metadata, beacon); @@ -414,13 +414,13 @@ contract ModuleFactoryV1Test is Test { // We emit the event we expect to see. emit ModuleCreated(orchestrator, address(0), metadata); - IModule_v1 originalModule; + IModule_v2 originalModule; vm.startPrank(alice); { // Create new module instance. - originalModule = IModule_v1( + originalModule = IModule_v2( factory.createModuleProxy( - metadata, IOrchestrator_v1(orchestrator), workflowConfig + metadata, IOrchestrator_v2(orchestrator), workflowConfig ) ); } @@ -457,13 +457,13 @@ contract ModuleFactoryV1Test is Test { // We emit the event we expect to see. emit ModuleCreated(orchestrator, address(0), metadata); - IModule_v1 redeployedModule_bob; + IModule_v2 redeployedModule_bob; vm.startPrank(bob); { // Create new module instance. - redeployedModule_bob = IModule_v1( + redeployedModule_bob = IModule_v2( factory.createModuleProxy( - metadata, IOrchestrator_v1(orchestrator), workflowConfig + metadata, IOrchestrator_v2(orchestrator), workflowConfig ) ); } @@ -479,13 +479,13 @@ contract ModuleFactoryV1Test is Test { // We emit the event we expect to see. emit ModuleCreated(orchestrator, address(0), metadata); - IModule_v1 redeployedModule_alice; + IModule_v2 redeployedModule_alice; vm.startPrank(alice); { // Create new module instance. - redeployedModule_alice = IModule_v1( + redeployedModule_alice = IModule_v2( factory.createModuleProxy( - metadata, IOrchestrator_v1(orchestrator), workflowConfig + metadata, IOrchestrator_v2(orchestrator), workflowConfig ) ); } @@ -498,7 +498,7 @@ contract ModuleFactoryV1Test is Test { } function testCreateModuleFailsIfMetadataUnregistered( - IModule_v1.Metadata memory metadata, + IModule_v2.Metadata memory metadata, address orchestrator, bytes memory configData ) public { @@ -510,7 +510,7 @@ contract ModuleFactoryV1Test is Test { ); factory.createAndInitModule( metadata, - IOrchestrator_v1(orchestrator), + IOrchestrator_v2(orchestrator), configData, workflowConfigNoIndependentUpdates ); @@ -519,7 +519,7 @@ contract ModuleFactoryV1Test is Test { //-------------------------------------------------------------------------- // Internal Helper Functions - function _assumeValidMetadata(IModule_v1.Metadata memory metadata) + function _assumeValidMetadata(IModule_v2.Metadata memory metadata) public pure { diff --git a/test/unit/factories/OrchestratorFactory_v1.t.sol b/test/unit/factories/OrchestratorFactory_v1.t.sol index c9a5375d3..d7179ee3b 100644 --- a/test/unit/factories/OrchestratorFactory_v1.t.sol +++ b/test/unit/factories/OrchestratorFactory_v1.t.sol @@ -13,11 +13,11 @@ import {Ownable} from "@oz/access/Ownable.sol"; // Internal Interfaces import { IOrchestratorFactory_v1, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; // Mocks import {ModuleImplementationV1Mock} from @@ -38,7 +38,7 @@ contract OrchestratorFactoryV1Test is Test { // SuT OrchestratorFactory_v1 factory; - Orchestrator_v1 target; + Orchestrator_v2 target; InverterBeaconV1OwnableMock beacon; @@ -69,7 +69,7 @@ contract OrchestratorFactoryV1Test is Test { IOrchestratorFactory_v1.ModuleConfig fundingManagerConfig = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( + IModule_v2.Metadata( 1, 0, 0, "https://fundingmanager.com", "FundingManager" ), bytes("data") @@ -77,28 +77,28 @@ contract OrchestratorFactoryV1Test is Test { IOrchestratorFactory_v1.ModuleConfig authorizerConfig = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata(1, 0, 0, "https://authorizer.com", "Authorizer"), + IModule_v2.Metadata(1, 0, 0, "https://authorizer.com", "Authorizer"), abi.encode(address(this), address(this)) ); IOrchestratorFactory_v1.ModuleConfig paymentProcessorConfig = IOrchestratorFactory_v1.ModuleConfig( - IModule_v1.Metadata( - 1, 1, 0, "https://paymentprocessor.com", "PP_Simple_v2" + IModule_v2.Metadata( + 1, 1, 0, "https://paymentprocessor.com", "PP_Simple_v3" ), bytes("data") ); IOrchestratorFactory_v1.ModuleConfig moduleConfig = IOrchestratorFactory_v1 .ModuleConfig( - IModule_v1.Metadata(1, 0, 0, "https://module.com", "Module_v1"), + IModule_v2.Metadata(1, 0, 0, "https://module.com", "Module_v2"), bytes("") ); function setUp() public { moduleFactory = new ModuleFactoryV1Mock(); - target = new Orchestrator_v1(address(0)); + target = new Orchestrator_v2(address(0)); beacon = new InverterBeaconV1OwnableMock(governanceContract); beacon.overrideImplementation(address(target)); @@ -153,8 +153,8 @@ contract OrchestratorFactoryV1Test is Test { vm.expectEmit(true, false, false, false); emit OrchestratorCreated(1, address(0)); // Since we don't know the address of the orchestrator - // Deploy Orchestrator_v1 with id=1 - IOrchestrator_v1 orchestrator = factory.createOrchestrator( + // Deploy Orchestrator_v2 with id=1 + IOrchestrator_v2 orchestrator = factory.createOrchestrator( workflowConfig, fundingManagerConfig, authorizerConfig, @@ -187,7 +187,7 @@ contract OrchestratorFactoryV1Test is Test { vm.expectEmit(true, false, false, false); emit OrchestratorCreated(2, address(0)); // since we don't know the address of the orchestrator - // Deploy Orchestrator_v1 with id=2 + // Deploy Orchestrator_v2 with id=2 orchestrator = factory.createOrchestrator( workflowConfig, fundingManagerConfig, @@ -257,7 +257,7 @@ contract OrchestratorFactoryV1Test is Test { emit OrchestratorCreated(1, address(0)); // Alice deploys original orchestrator - IOrchestrator_v1 orchestrator; + IOrchestrator_v2 orchestrator; vm.startPrank(alice); { orchestrator = factory.createOrchestrator( @@ -303,7 +303,7 @@ contract OrchestratorFactoryV1Test is Test { // after the reorg vm.expectEmit(true, false, false, false); emit OrchestratorCreated(1, address(0)); - IOrchestrator_v1 orchestrator_retry_bob; + IOrchestrator_v2 orchestrator_retry_bob; vm.startPrank(bob); { orchestrator_retry_bob = factory.createOrchestrator( @@ -323,7 +323,7 @@ contract OrchestratorFactoryV1Test is Test { // Now Alice deploys the workflow again, after the reorg vm.expectEmit(true, false, false, false); emit OrchestratorCreated(2, address(0)); - IOrchestrator_v1 orchestrator_retry_alice; + IOrchestrator_v2 orchestrator_retry_alice; vm.startPrank(alice); { orchestrator_retry_alice = factory.createOrchestrator( @@ -350,8 +350,8 @@ contract OrchestratorFactoryV1Test is Test { vm.expectEmit(false, false, false, false); emit OrchestratorCreated(0, address(0)); // Since we don't know the id/address of the orchestrator - // Deploy Orchestrator_v1 - IOrchestrator_v1 orchestrator = factory.createOrchestrator( + // Deploy Orchestrator_v2 + IOrchestrator_v2 orchestrator = factory.createOrchestrator( workflowConfigNoIndependentUpdates, fundingManagerConfig, authorizerConfig, diff --git a/test/unit/factories/workflow-specific/PIM_WorkflowFactory_v1.t.sol b/test/unit/factories/workflow-specific/PIM_WorkflowFactory_v1.t.sol deleted file mode 100644 index c4cb5514f..000000000 --- a/test/unit/factories/workflow-specific/PIM_WorkflowFactory_v1.t.sol +++ /dev/null @@ -1,572 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -// Internal Dependencies -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IOrchestratorFactory_v1} from - "src/factories/interfaces/IOrchestratorFactory_v1.sol"; -import {IPIM_WorkflowFactory_v1} from - "src/factories/interfaces/IPIM_WorkflowFactory_v1.sol"; -import {IERC20Issuance_v1} from - "src/external/token/interfaces/IERC20Issuance_v1.sol"; -import {ERC20Issuance_v1} from "src/external/token/ERC20Issuance_v1.sol"; -import {IFM_BC_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {PIM_WorkflowFactory_v1} from - "src/factories/workflow-specific/PIM_WorkflowFactory_v1.sol"; -import {E2ETest} from "test/e2e/E2ETest.sol"; -import {Ownable} from "@oz/access/Ownable.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IRedeemingBondingCurveBase_v1.sol"; - -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -import {ERC20} from "@oz/token/ERC20/ERC20.sol"; - -contract PIM_WorkflowFactory_v1Test is E2ETest { - // SuT - PIM_WorkflowFactory_v1 factory; - - // Deployment Parameters - IOrchestratorFactory_v1.WorkflowConfig workflowConfig; - IOrchestratorFactory_v1.ModuleConfig authorizerConfig; - IOrchestratorFactory_v1.ModuleConfig paymentProcessorConfig; - IOrchestratorFactory_v1.ModuleConfig[] logicModuleConfigs; - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties bcProperties; - IPIM_WorkflowFactory_v1.IssuanceTokenParams issuanceTokenParams; - - address factoryDeployer = vm.addr(1); - address workflowDeployer = vm.addr(2); - address mockTrustedForwarder = vm.addr(3); - address alice = vm.addr(0xA11CE); - - uint initialIssuuanceSupply = 122_727_272_727_272_727_272_727; - uint initialCollateralSupply = 3_163_408_614_166_851_161; - uint firstCollateralIn = 100_000_000; - uint32 reserveRatio = 160_000; - - event PIMWorkflowCreated(address indexed issuanceToken); - - function setUp() public override { - super.setUp(); - - // deploy new factory - factory = new PIM_WorkflowFactory_v1( - address(orchestratorFactory), factoryDeployer, mockTrustedForwarder - ); - assert(factory.owner() == factoryDeployer); - - // Orchestrator/Workflow config - workflowConfig = IOrchestratorFactory_v1.WorkflowConfig({ - independentUpdates: false, - independentUpdateAdmin: address(0) - }); - - // Authorizer - setUpRoleAuthorizer(); - authorizerConfig = IOrchestratorFactory_v1.ModuleConfig( - roleAuthorizerMetadata, abi.encode(address(factory)) - ); - - // PaymentProcessor - setUpSimplePaymentProcessor(); - paymentProcessorConfig = IOrchestratorFactory_v1.ModuleConfig( - simplePaymentProcessorMetadata, bytes("") - ); - - // Additional Logic Modules: bounty manager - setUpBountyManager(); - logicModuleConfigs.push( - IOrchestratorFactory_v1.ModuleConfig( - bountyManagerMetadata, bytes("") - ) - ); - - // Funding Manager: Bancor Virtual Supply - setUpBancorVirtualSupplyBondingCurveFundingManager(); - bcProperties = IFM_BC_Bancor_Redeeming_VirtualSupply_v1 - .BondingCurveProperties({ - formula: address(formula), - reserveRatioForBuying: reserveRatio, - reserveRatioForSelling: reserveRatio, - buyFee: 0, - sellFee: 0, - buyIsOpen: true, - sellIsOpen: true, - initialIssuanceSupply: initialIssuuanceSupply, - initialCollateralSupply: initialCollateralSupply - }); - - // Deploy Issuance Token - issuanceTokenParams = IPIM_WorkflowFactory_v1.IssuanceTokenParams({ - name: "Bonding Curve Token", - symbol: "BCT", - decimals: 18, - maxSupply: type(uint).max - 1 - }); - - // mint collateral token to deployer and approve to factory - token.mint(address(this), type(uint).max); - token.approve(address(factory), type(uint).max); - } - - /* Test testCreatePIMWorkflow - ├── given the default config - │ └── when called - │ └── then it deploys, cleans up minting rights and makes first purchase - └── given withInitialLiquidity flag is set to true - | └── when called - | │ └── then the curve receives initial collateral supply and initial issuance supply is minted to recipient - └── given withInitialLiquidity flag is set to false - | └── when called - | │ └── then the curve doesn't receive initial collateral supply and burns initial issuance supply - └── given both renounce flags are set - | └── when called - | └── then the issuance token doesn't have owner and factory remains workflow admin - └── given only isRenouncedIssuanceToken flag is set - | └── when called - | └── then issuance token doesn't have owner and admin (from params) is workflow admin - └── given only isRenouncedWorkflow flag is set - | └── when called - | └── then admin (from params) is owner of issuance token and factory is workflow admin - └── given msg.sender hasn't approved collateral token for factory - └── when called - └── then it reverts - */ - - function testCreatePIMWorkflow() public { - // CHECK: event is emitted - vm.expectEmit(false, false, false, false); - emit IPIM_WorkflowFactory_v1.PIMWorkflowCreated( - address(0), address(0), address(0), address(0), true, true - ); - // get default config - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - // CHECK: factory DOES NOT have minting rights on token anymore - bool isFactoryStillMinter = - issuanceToken.allowedMinters(address(factory)); - assertFalse(isFactoryStillMinter); - // CHECK: bonding curve module HAS minting rights on token - bool isBcMinter = - issuanceToken.allowedMinters(address(orchestrator.fundingManager())); - assertTrue(isBcMinter); - // CHECK: deployer uses firstCollateralIn (amount) to make first purchase - assertTrue(issuanceToken.balanceOf(pimConfig.recipient) > 0); - } - - function testCreatePIMWorkflow_WithInitialLiquidity() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.withInitialLiquidity = true; // just to highlight what is being tested - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - address fundingManager = address(orchestrator.fundingManager()); - - // CHECK: curve HAS received initial collateral supply and firstCollateralIn - assertTrue( - token.balanceOf(fundingManager) - == pimConfig.bcProperties.initialCollateralSupply - + pimConfig.firstCollateralIn - ); - // CHECK: recipient receives initialIssuanceSupply plus first purchase amount - assertGt( - issuanceToken.balanceOf(pimConfig.recipient), - bcProperties.initialIssuanceSupply - ); - // CHECK: if recipient SELLS complete stack and BUYS BACK they get same amount of issuanceToken - vm.startPrank(pimConfig.recipient); - // get issuance balance before sale and rebuy - uint balanceBefore = issuanceToken.balanceOf(pimConfig.recipient); - // get static price before sale - uint staticPriceBefore = - IBondingCurveBase_v1(fundingManager).getStaticPriceForBuying(); - // get how many issuance tokens are sold - uint firstPurchaseVolume = - balanceBefore - pimConfig.bcProperties.initialIssuanceSupply; - issuanceToken.approve(fundingManager, firstPurchaseVolume); - uint collateralAmountOut = IRedeemingBondingCurveBase_v1(fundingManager) - .calculateSaleReturn(firstPurchaseVolume); - // sell all tokens from initial purchase (the one that happened atomically in the factory) - IRedeemingBondingCurveBase_v1(fundingManager).sell( - firstPurchaseVolume, collateralAmountOut - ); - // alice only has initial issuance supply left? - assert( - issuanceToken.balanceOf(pimConfig.recipient) - == pimConfig.bcProperties.initialIssuanceSupply - ); - token.approve(fundingManager, collateralAmountOut); - uint issuanceAmountOut = IBondingCurveBase_v1(fundingManager) - .calculatePurchaseReturn(collateralAmountOut); - // now use the collateral from the sale to buy back - IBondingCurveBase_v1(fundingManager).buy( - collateralAmountOut, issuanceAmountOut - ); - // get the static price after the re-buy - uint staticPriceAfter = - IBondingCurveBase_v1(fundingManager).getStaticPriceForBuying(); - // CHECK: if recipient SELLS complete stack and BUYS BACK they end up with same balance of issuanceToken - assertApproxEqRel( - balanceBefore, - issuanceToken.balanceOf(pimConfig.recipient), - 0.00001e18 - ); - // CHECK: if recipient SELLS complete stack and BUYS BACK the static price is the same again - assertEq(staticPriceBefore, staticPriceAfter); - vm.stopPrank(); - } - - function testCreatePIMWorkflow_WithoutInitialLiquidity() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.withInitialLiquidity = false; - - uint preCollateralBalance = token.balanceOf(address(this)); - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - uint postCollateralBalance = token.balanceOf(address(this)); - - address fundingManager = address(orchestrator.fundingManager()); - - // CHECK: deployer DID NOT send initial collateral supply to curve, ONLY did first purchase - assertEq( - preCollateralBalance - postCollateralBalance, - pimConfig.firstCollateralIn - ); - // CHECK: initialIssuanceSupply is BURNT (sent to 0xDEAD) - assertEq( - issuanceToken.balanceOf(address(0xDEAD)), - bcProperties.initialIssuanceSupply - ); - // CHECK: recipient receives some amount of issuanceToken (due to first purchase) - assertTrue(issuanceToken.balanceOf(pimConfig.recipient) > 0); - - vm.startPrank(pimConfig.recipient); - // get issuance balance before sale and rebuy - uint balanceBefore = issuanceToken.balanceOf(pimConfig.recipient); - // get static price before sale - uint staticPriceBefore = - IBondingCurveBase_v1(fundingManager).getStaticPriceForBuying(); - issuanceToken.approve(fundingManager, balanceBefore); - uint collateralAmountOut = IRedeemingBondingCurveBase_v1(fundingManager) - .calculateSaleReturn(balanceBefore); - // use complete issuance balance to sell for collateral - IRedeemingBondingCurveBase_v1(fundingManager).sell( - balanceBefore, collateralAmountOut - ); - // alice doesn't have any issuance token left - assert(issuanceToken.balanceOf(pimConfig.recipient) == 0); - token.approve(fundingManager, collateralAmountOut); - uint issuanceAmountOut = IBondingCurveBase_v1(fundingManager) - .calculatePurchaseReturn(collateralAmountOut); - // now use the collateral from the sale to buy back - IBondingCurveBase_v1(fundingManager).buy( - collateralAmountOut, issuanceAmountOut - ); - uint staticPriceAfter = - IBondingCurveBase_v1(fundingManager).getStaticPriceForBuying(); - // CHECK: if recipient SELLS complete stack and BUYS BACK they end up with same balance of issuanceToken - assertApproxEqRel( - balanceBefore, - issuanceToken.balanceOf(pimConfig.recipient), - 0.00001e18 - ); - // CHECK: if recipient SELLS complete stack and BUYS BACK the static price is the same again - assertEq(staticPriceBefore, staticPriceAfter); - vm.stopPrank(); - } - - function testCreatePIMWorkflow_IfFullyRenounced() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.isRenouncedIssuanceToken = true; - pimConfig.isRenouncedWorkflow = true; - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - // CHECK: the token DOES NOT have an owner anymore - address owner = issuanceToken.owner(); - assertEq(owner, address(0)); - // CHECK: the deployer DOES NOT get admin rights over workflow - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - bool isDeployerAdmin = orchestrator.authorizer().hasRole( - adminRole, address(workflowDeployer) - ); - assertFalse(isDeployerAdmin); - // CHECK: the factory HAS admin rights over workflow - bool isFactoryAdmin = - orchestrator.authorizer().hasRole(adminRole, address(factory)); - assertTrue(isFactoryAdmin); - } - - function testCreatePIMWorkflow_IfNotRenounced() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.isRenouncedIssuanceToken = false; - pimConfig.isRenouncedWorkflow = false; - pimConfig.recipient = alice; - pimConfig.admin = workflowDeployer; - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - // CHECK: the deployer IS owner of the token - assertEq(issuanceToken.owner(), workflowDeployer); - // CHECK: the deployer IS admin of the workflow - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - bool isDeployerAdmin = orchestrator.authorizer().hasRole( - adminRole, address(workflowDeployer) - ); - assertTrue(isDeployerAdmin); - } - - function testCreatePIMWorkflow_IfTokenRenounced() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.isRenouncedIssuanceToken = true; - pimConfig.isRenouncedWorkflow = false; - pimConfig.recipient = alice; - pimConfig.admin = workflowDeployer; - - (IOrchestrator_v1 orchestrator, ERC20Issuance_v1 issuanceToken) = - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - // CHECK: the token DOES NOT have an owner anymore - assertEq(issuanceToken.owner(), address(0)); - // CHECK: the deployer IS admin of the workflow - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - bool isDeployerAdmin = orchestrator.authorizer().hasRole( - adminRole, address(workflowDeployer) - ); - assertTrue(isDeployerAdmin); - } - - function testCreatePIMWorkflow_IfWorkflowRenounced() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - pimConfig.isRenouncedIssuanceToken = false; - pimConfig.isRenouncedWorkflow = true; - - (IOrchestrator_v1 orchestrator,) = factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - // CHECK: the deployer IS owner of the token - // assertEq(issuanceToken.owner(), workflowDeployer); - // CHECK: the deployer DOES NOT have admin rights over workflow - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - bool isDeployerAdmin = orchestrator.authorizer().hasRole( - adminRole, address(workflowDeployer) - ); - assertFalse(isDeployerAdmin); - // CHECK: the factory DOES have admin rights over workflow - bool isFactoryAdmin = - orchestrator.authorizer().hasRole(adminRole, address(factory)); - assertTrue(isFactoryAdmin); - } - - function testCreatePIMWorkflow_FailsWithoutCollateralTokenApproval() - public - { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - address deployer = address(0xB0B); - vm.prank(deployer); - - vm.expectRevert(); - - factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - } - - /* Test testWithdrawPimFee - ├── given the msg.sender is the fee recipient - | └── when called - | └── then it emits fee claim events on bc and factory - └── given the msg.sender is NOT the fee recipient - └── when called - └── then it reverts - */ - - function testWithdrawPimFee() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - - (IOrchestrator_v1 orchestrator,) = factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - address fundingManager = address(orchestrator.fundingManager()); - - // CHECK: bonding curve EMITS event for fee withdrawal - vm.expectEmit(true, true, true, false); - emit IBondingCurveBase_v1.ProjectCollateralFeeWithdrawn( - address(this), 0 - ); - vm.expectEmit(true, true, true, true); - emit IPIM_WorkflowFactory_v1.PimFeeClaimed(address(this), 0); - factory.withdrawPimFee(fundingManager, alice); - } - - function testWithdrawPimFee__FailsIfCallerIsNotPimFeeRecipient() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - - (IOrchestrator_v1 orchestrator,) = factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - address fundingManager = address(orchestrator.fundingManager()); - // CHECK: withdrawal REVERTS if caller IS NOT the fee recipient - vm.expectRevert( - abi.encodeWithSelector( - IPIM_WorkflowFactory_v1 - .PIM_WorkflowFactory__OnlyPimFeeRecipient - .selector - ) - ); - vm.prank(alice); - factory.withdrawPimFee(fundingManager, alice); - } - - /* Test testTransferPimFeeEligibility - ├── given the msg.sender is the fee recipient - | └── when called - | └── then it emits an event indicating role update and lets new recipient withdraw fee - └── given the msg.sender is NOT the fee recipient - └── when called - └── then it reverts - */ - - function testTransferPimFeeEligibility() public { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - - (IOrchestrator_v1 orchestrator,) = factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - address fundingManager = address(orchestrator.fundingManager()); - // CHECK: when fee recipient is updated event is emitted - vm.expectEmit(true, true, true, true); - emit IPIM_WorkflowFactory_v1.PimFeeRecipientUpdated( - address(this), alice - ); - factory.transferPimFeeEligibility(fundingManager, alice); - - // CHECK: new recipient (alice) CAN withdraw fee - vm.prank(alice); - vm.expectEmit(true, true, true, false); - emit IBondingCurveBase_v1.ProjectCollateralFeeWithdrawn( - address(this), 0 - ); - factory.withdrawPimFee(fundingManager, alice); - } - - function testTransferPimFeeEligibility_FailsIfCallerIsNotPimFeeRecipient() - public - { - IPIM_WorkflowFactory_v1.PIMConfig memory pimConfig = - getDefaultPIMConfig(); - - (IOrchestrator_v1 orchestrator,) = factory.createPIMWorkflow( - workflowConfig, - paymentProcessorConfig, - logicModuleConfigs, - pimConfig - ); - - address fundingManager = address(orchestrator.fundingManager()); - // CHECK: withdrawal REVERTS if caller IS NOT the fee recipient - vm.expectRevert( - abi.encodeWithSelector( - IPIM_WorkflowFactory_v1 - .PIM_WorkflowFactory__OnlyPimFeeRecipient - .selector - ) - ); - vm.prank(alice); - factory.transferPimFeeEligibility(fundingManager, address(0xB0B)); - } - - // UTILS - function getDefaultPIMConfig() - internal - view - returns (IPIM_WorkflowFactory_v1.PIMConfig memory) - { - return IPIM_WorkflowFactory_v1.PIMConfig({ - fundingManagerMetadata: bancorVirtualSupplyBondingCurveFundingManagerMetadata, - authorizerMetadata: roleAuthorizerMetadata, - bcProperties: bcProperties, - issuanceTokenParams: issuanceTokenParams, - collateralToken: address(token), - firstCollateralIn: firstCollateralIn, - admin: address(this), - recipient: alice, - isRenouncedIssuanceToken: true, - isRenouncedWorkflow: true, - withInitialLiquidity: true - }); - } -} diff --git a/test/unit/modules/ModuleTest.sol b/test/unit/modules/ModuleTest.sol index e6a08338f..9c8e9951b 100644 --- a/test/unit/modules/ModuleTest.sol +++ b/test/unit/modules/ModuleTest.sol @@ -18,18 +18,19 @@ import {TransactionForwarder_v1} from import {ModuleFactoryV1Mock} from "@mocks/factories/ModuleFactoryV1Mock.sol"; // Internal Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; // Mocks import {OrchestratorV1Mock} from "@mocks/orchestrator/OrchestratorV1Mock.sol"; import {FundingManagerV1Mock} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import { - PaymentProcessorV1Mock, - IPaymentProcessor_v2 -} from "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; + PaymentProcessor_v3_Mock, + IPaymentProcessor_v3 +} from "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; // External Dependencies import {TransparentUpgradeableProxy} from "@oz/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -42,9 +43,9 @@ abstract contract ModuleTest is Test { // Mocks FundingManagerV1Mock _fundingManager; - AuthorizerV1Mock _authorizer; + Authorizer_v2_Mock _authorizer; ERC20Mock _token = new ERC20Mock("Mock Token", "MOCK", 18); - PaymentProcessorV1Mock _paymentProcessor = new PaymentProcessorV1Mock(); + PaymentProcessor_v3_Mock _paymentProcessor = new PaymentProcessor_v3_Mock(); GovernorV1Mock governor = new GovernorV1Mock(); ModuleFactoryV1Mock moduleFactory = new ModuleFactoryV1Mock(); @@ -55,23 +56,23 @@ abstract contract ModuleTest is Test { // Deploy a forwarder used to enable metatransactions TransactionForwarder_v1 _forwarder = new TransactionForwarder_v1(); - // Orchestrator_v1 Constants + // Orchestrator_v2 Constants uint constant _ORCHESTRATOR_ID = 1; - // Module_v1 Constants + // Module_v2 Constants uint constant _MAJOR_VERSION = 1; uint constant _MINOR_VERSION = 0; uint constant _PATCH_VERSION = 0; string constant _URL = "https://github.com/organization/module"; - string constant _TITLE = "Module_v1"; + string constant _TITLE = "Module_v2"; - IModule_v1.Metadata _METADATA = IModule_v1.Metadata( + IModule_v2.Metadata _METADATA = IModule_v2.Metadata( _MAJOR_VERSION, _MINOR_VERSION, _PATCH_VERSION, _URL, _TITLE ); //-------------------------------------------------------------------------- // Setup - function _setUpOrchestrator(IModule_v1 module) internal virtual { + function _setUpOrchestrator(IModule_v2 module) internal virtual { // Needs to be a proxy for the notInitialized Check feeManager = FeeManager_v1( address( @@ -94,8 +95,49 @@ abstract contract ModuleTest is Test { impl = address(new FundingManagerV1Mock()); _fundingManager = FundingManagerV1Mock(Clones.clone(impl)); - impl = address(new AuthorizerV1Mock()); - _authorizer = AuthorizerV1Mock(Clones.clone(impl)); + impl = address(new Authorizer_v2_Mock()); + _authorizer = Authorizer_v2_Mock(Clones.clone(impl)); + + _orchestrator.init( + _ORCHESTRATOR_ID, + address(moduleFactory), + modules, + _fundingManager, + _authorizer, + _paymentProcessor, + governor + ); + + _authorizer.init(_orchestrator, _METADATA, abi.encode(address(this))); + + _fundingManager.init(_orchestrator, _METADATA, abi.encode("")); + _fundingManager.setToken(IERC20(address(_token))); + } + + function _setUpOrchestrator() internal virtual { + // Needs to be a proxy for the notInitialized Check + feeManager = FeeManager_v1( + address( + new TransparentUpgradeableProxy( // based on openzeppelins TransparentUpgradeableProxy + address(new FeeManager_v1()), // Implementation Address + address(this), // Admin + bytes("") // data field that could have been used for calls, but not necessary + ) + ) + ); + feeManager.init(address(this), treasury, 0, 0); + governor.setFeeManager(address(feeManager)); + + address[] memory modules = new address[](0); + + address impl = address(new OrchestratorV1Mock(address(_forwarder))); + _orchestrator = OrchestratorV1Mock(Clones.clone(impl)); + + impl = address(new FundingManagerV1Mock()); + _fundingManager = FundingManagerV1Mock(Clones.clone(impl)); + + impl = address(new Authorizer_v2_Mock()); + _authorizer = Authorizer_v2_Mock(Clones.clone(impl)); _orchestrator.init( _ORCHESTRATOR_ID, @@ -122,6 +164,8 @@ abstract contract ModuleTest is Test { function testReinitFails() public virtual; + function testSupportsInterface() public virtual; + //-------------------------------------------------------------------------- // Assertion Helper Functions // @@ -257,11 +301,9 @@ abstract contract ModuleTest is Test { internal { _orchestrator.initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2(paymentProcessor_) + address(paymentProcessor_) ); vm.warp(block.timestamp + 73 hours); - _orchestrator.executeSetPaymentProcessor( - IPaymentProcessor_v2(paymentProcessor_) - ); + _orchestrator.executeSetPaymentProcessor(address(paymentProcessor_)); } } diff --git a/test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.t.sol b/test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.t.sol similarity index 84% rename from test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.t.sol rename to test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.t.sol index 37aa652f0..8bb8fd4f7 100644 --- a/test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.t.sol +++ b/test/unit/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.t.sol @@ -6,9 +6,9 @@ import "forge-std/console.sol"; // SuT import { - AUT_EXT_VotingRoles_v1, - IAUT_EXT_VotingRoles_v1 -} from "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v1.sol"; + AUT_EXT_VotingRoles_v2, + IAUT_EXT_VotingRoles_v2 +} from "src/modules/authorizer/extensions/AUT_EXT_VotingRoles_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; @@ -16,35 +16,36 @@ import {Clones} from "@oz/proxy/Clones.sol"; import {IERC165} from "@oz/utils/introspection/IERC165.sol"; import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Internal Dependencies -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; // Interfaces -import {IOrchestrator_v1} from - "src/orchestrator/interfaces/IOrchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IOrchestrator_v2} from + "src/orchestrator/interfaces/IOrchestrator_v2.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; // Mocks -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import {FundingManagerV1Mock} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; -contract AUT_EXT_VotingRoles_v1Test is ModuleTest { +contract AUT_EXT_VotingRoles_v2Test is ModuleTest { // SuT - AUT_EXT_VotingRoles_v1 _votingRoles; + AUT_EXT_VotingRoles_v2 _votingRoles; - // Orchestrator_v1 _orchestrator; + // Orchestrator_v2 _orchestrator; address[] initialVoters; address[] currentVoters; @@ -56,7 +57,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { address internal constant ALBA = address(0xa1ba); address internal constant BOB = address(0xb0b); address internal constant COBIE = address(0xc0b1e); - IAUT_EXT_VotingRoles_v1.Motion _bufMotion; + IAUT_EXT_VotingRoles_v2.Motion _bufMotion; //-------------------------------------------------------------------------- // Events @@ -93,8 +94,8 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { function setUp() public { // Set up a orchestrator - address authImpl = address(new AUT_EXT_VotingRoles_v1()); - _votingRoles = AUT_EXT_VotingRoles_v1(Clones.clone(authImpl)); + address authImpl = address(new AUT_EXT_VotingRoles_v2()); + _votingRoles = AUT_EXT_VotingRoles_v2(Clones.clone(authImpl)); _setUpOrchestrator(_votingRoles); @@ -103,33 +104,31 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _authorizer.grantRole(adminRole, address(_votingRoles)); // _authorizer.setIsAuthorized(address(_votingRoles), true); - // Initialize the votingRoles with 3 users + // Initialize the votingRoles with 2 users - initialVoters = new address[](3); + initialVoters = new address[](2); initialVoters[0] = ALBA; initialVoters[1] = BOB; - initialVoters[2] = COBIE; uint _startingThreshold = DEFAULT_QUORUM; uint _startingDuration = DEFAULT_DURATION; _votingRoles.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(initialVoters, _startingThreshold, _startingDuration) ); currentVoters.push(ALBA); currentVoters.push(BOB); - currentVoters.push(COBIE); // validation of the initial state happens in testInit() } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( _votingRoles.supportsInterface( - type(IAUT_EXT_VotingRoles_v1).interfaceId + type(IAUT_EXT_VotingRoles_v2).interfaceId ) ); } @@ -140,8 +139,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { public returns (bytes32) { - bytes32 countID = - keccak256(abi.encodePacked(_addr, _msg, _votingRoles.motionCount())); + bytes32 countID = keccak256( + abi.encodePacked(_addr, _msg, _votingRoles.getMotionCount()) + ); vm.prank(callingUser); vm.expectEmit(true, true, true, true); @@ -205,7 +205,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { } // the voting time passes - vm.warp(block.timestamp + _votingRoles.voteDuration() + 1); + vm.warp(block.timestamp + _votingRoles.getVoteDuration() + 1); return _voteID; } @@ -220,7 +220,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { } bytes32 _voteID = createVote(_voters[0], _target, _action); - for (uint i = 1; i < _votingRoles.threshold(); ++i) { + for (uint i = 1; i < _votingRoles.getThreshold(); ++i) { if (i < _voters.length) { vm.expectEmit(true, true, true, true); emit VoteCast(_voteID, _voters[(i - 1)], 0); @@ -229,7 +229,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { } // the voting time passes - vm.warp(block.timestamp + _votingRoles.voteDuration() + 1); + vm.warp(block.timestamp + _votingRoles.getVoteDuration() + 1); return _voteID; } @@ -243,7 +243,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { function getFullMotionData(bytes32 voteId) internal - returns (IAUT_EXT_VotingRoles_v1.Motion storage) + returns (IAUT_EXT_VotingRoles_v2.Motion storage) { ( address _addr, @@ -257,7 +257,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { uint _excAt, bool _excRes, bytes memory _excData - ) = _votingRoles.motions(voteId); + ) = _votingRoles.getMotion(voteId); _bufMotion.target = _addr; _bufMotion.action = _act; @@ -290,13 +290,12 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { assertEq(_authorizer.hasRole(admin, address(_votingRoles)), true); // Admin role assertEq(_votingRoles.isVoter(ALBA), true); assertEq(_votingRoles.isVoter(BOB), true); - assertEq(_votingRoles.isVoter(COBIE), true); assertEq(_authorizer.hasRole(admin, address(this)), true); assertEq(_authorizer.hasRole(admin, address(_orchestrator)), false); assertEq(_votingRoles.isVoter(address(this)), false); assertEq(_votingRoles.isVoter(address(_orchestrator)), false); - assertEq(_votingRoles.voterCount(), 3); + assertEq(_votingRoles.getVoterCount(), 2); } function testInitWithInitialVoters(address[] memory testVoters) public { @@ -306,9 +305,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.assume(testVoters.length >= 2); _validateUserList(testVoters); - address authImpl = address(new AUT_EXT_VotingRoles_v1()); - AUT_EXT_VotingRoles_v1 testAuthorizer = - AUT_EXT_VotingRoles_v1(Clones.clone(authImpl)); + address authImpl = address(new AUT_EXT_VotingRoles_v2()); + AUT_EXT_VotingRoles_v2 testAuthorizer = + AUT_EXT_VotingRoles_v2(Clones.clone(authImpl)); // Since the authorizer we are working with is not the default one, // we must manually control that the fuzzer doesn't feed us its address @@ -317,7 +316,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { } testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); @@ -328,7 +327,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { assertEq(testAuthorizer.isVoter(testVoters[i]), true); } assertEq(testAuthorizer.isVoter(address(this)), false); - assertEq(testAuthorizer.voterCount(), testVoters.length); + assertEq(testAuthorizer.getVoterCount(), testVoters.length); } function testInitWithDuplicateInitialVotersFails( @@ -341,9 +340,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.assume(testVoters.length >= 2); position = uint8(bound(position, 1, testVoters.length - 1)); - address authImpl = address(new AUT_EXT_VotingRoles_v1()); - AUT_EXT_VotingRoles_v1 testAuthorizer = - AUT_EXT_VotingRoles_v1(Clones.clone(authImpl)); + address authImpl = address(new AUT_EXT_VotingRoles_v2()); + AUT_EXT_VotingRoles_v2 testAuthorizer = + AUT_EXT_VotingRoles_v2(Clones.clone(authImpl)); _validateUserList(testVoters); @@ -357,13 +356,13 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__IsAlreadyVoter .selector ) ); testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); @@ -377,20 +376,20 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { function testInitWithInvalidInitialVotersFails() public { // We "reuse" the orchestrator created in the setup, but the orchestrator doesn't know about this new authorizer. - address authImpl = address(new AUT_EXT_VotingRoles_v1()); - AUT_EXT_VotingRoles_v1 testAuthorizer = - AUT_EXT_VotingRoles_v1(Clones.clone(authImpl)); + address authImpl = address(new AUT_EXT_VotingRoles_v2()); + AUT_EXT_VotingRoles_v2 testAuthorizer = + AUT_EXT_VotingRoles_v2(Clones.clone(authImpl)); address[] memory testVoters; vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__EmptyVoters .selector ) ); testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); @@ -400,13 +399,13 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidVoterAddress .selector ) ); testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); @@ -414,13 +413,13 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { testVoters[0] = address(testAuthorizer); vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidVoterAddress .selector ) ); testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); @@ -428,19 +427,19 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { testVoters[0] = address(_orchestrator); vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidVoterAddress .selector ) ); testAuthorizer.init( - IOrchestrator_v1(_orchestrator), + IOrchestrator_v2(_orchestrator), _METADATA, abi.encode(testVoters, DEFAULT_QUORUM, DEFAULT_DURATION) ); assertEq(address(testAuthorizer.orchestrator()), address(0)); - assertEq(testAuthorizer.voterCount(), 0); + assertEq(testAuthorizer.getVoterCount(), 0); } //-------------------------------------------------------------------------- @@ -453,7 +452,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < initialVoters.length; ++i) { bytes32 _voteID = createVote(ALBA, _moduleAddress, _msg); - IAUT_EXT_VotingRoles_v1.Motion storage _motion = + IAUT_EXT_VotingRoles_v2.Motion storage _motion = getFullMotionData(_voteID); // assertEq(_votingRoles.motionCount(), (_voteID + 1)); @@ -480,7 +479,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { assertEq(_votingRoles.isVoter(users[i]), false); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__CallerNotVoter .selector ); @@ -495,11 +494,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - bytes32("onlySelf"), - users[i] - ) + IAUT_EXT_VotingRoles_v2 + .Module__VotingRoleManager__OnlySelfCallAllowed + .selector ); vm.prank(users[i]); // authorized, but not Module if (i % 3 == 0) { @@ -516,11 +513,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - bytes32("onlySelf"), - users[i] - ) + IAUT_EXT_VotingRoles_v2 + .Module__VotingRoleManager__OnlySelfCallAllowed + .selector ); vm.prank(users[i]); // authorized, but not Module if (i % 3 == 0) { @@ -566,7 +561,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { (address _moduleAddress, bytes memory _msg) = getMockValidVote(); bytes32 _voteID = createVote(ALBA, _moduleAddress, _msg); - IAUT_EXT_VotingRoles_v1.Motion storage _motion = + IAUT_EXT_VotingRoles_v2.Motion storage _motion = getFullMotionData(_voteID); uint _votesBefore = _motion.forVotes; @@ -610,7 +605,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { // fail to vote as unauthorized address vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__CallerNotVoter .selector ); @@ -627,7 +622,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { (address _moduleAddress, bytes memory _msg) = getMockValidVote(); bytes32 _voteID = createVote(ALBA, _moduleAddress, _msg); - IAUT_EXT_VotingRoles_v1.Motion storage _motion = + IAUT_EXT_VotingRoles_v2.Motion storage _motion = getFullMotionData(_voteID); uint _votesBefore = _motion.againstVotes; @@ -676,7 +671,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { // fail to vote as unauthorized address vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__CallerNotVoter .selector ); @@ -693,7 +688,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { (address _moduleAddress, bytes memory _msg) = getMockValidVote(); bytes32 _voteID = createVote(ALBA, _moduleAddress, _msg); - IAUT_EXT_VotingRoles_v1.Motion storage _motion = + IAUT_EXT_VotingRoles_v2.Motion storage _motion = getFullMotionData(_voteID); uint _votesBefore = _motion.abstainVotes; @@ -716,7 +711,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { emit VoteCast(_voteID, BOB, 2); voteAbstain(BOB, _voteID); - IAUT_EXT_VotingRoles_v1.Receipt memory _r = + IAUT_EXT_VotingRoles_v2.Receipt memory _r = _votingRoles.getReceipt(_voteID, ALBA); assertEq(_r.hasVoted, true); assertEq(_r.support, 2); @@ -757,7 +752,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { for (uint i; i < users.length; ++i) { // fail to vote as unauthorized address vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__CallerNotVoter .selector ); @@ -777,7 +772,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // For vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionVotingPhaseClosed .selector ); @@ -786,7 +781,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // Against vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionVotingPhaseClosed .selector ); @@ -794,7 +789,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // Abstain vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionVotingPhaseClosed .selector ); @@ -814,7 +809,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // For vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidMotionId .selector ); @@ -823,7 +818,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // Against vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidMotionId .selector ); @@ -832,7 +827,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // Abstain vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidMotionId .selector ); @@ -848,7 +843,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { bytes32 _voteID = createVote(ALBA, _moduleAddress, _msg); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidSupport .selector ); @@ -869,21 +864,21 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { voteAgainst(users[i], _voteID); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__AttemptedDoubleVote .selector ); voteInFavor(users[i], _voteID); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__AttemptedDoubleVote .selector ); voteAgainst(users[i], _voteID); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__AttemptedDoubleVote .selector ); @@ -911,20 +906,20 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // 3) The vote gets executed (by anybody) vm.expectEmit(true, true, true, true); - uint _oldDuration = _votingRoles.voteDuration(); + uint _oldDuration = _votingRoles.getVoteDuration(); emit VoteDurationUpdated(_oldDuration, _newDuration); emit MotionExecuted(_voteID); _votingRoles.executeMotion(_voteID); // 4) The module state has changed - assertEq(_votingRoles.voteDuration(), _newDuration); + assertEq(_votingRoles.getVoteDuration(), _newDuration); } // Fail to execute vote that didn't pass function testExecuteInexistentVote(bytes32 wrongId) public { // No votes exist yet, everyting should fail vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidMotionId .selector ); @@ -942,7 +937,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // No prank address needed vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__ThresholdNotReached .selector ); @@ -961,7 +956,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.expectRevert( abi.encodePacked( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionInVotingPhase .selector ) @@ -969,11 +964,11 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _votingRoles.executeMotion(_voteID); // we wait and try again in the last block of voting time - vm.warp(block.timestamp + _votingRoles.voteDuration()); + vm.warp(block.timestamp + _votingRoles.getVoteDuration()); vm.expectRevert( abi.encodePacked( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionInVotingPhase .selector ) @@ -995,12 +990,12 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _votingRoles.executeMotion(_voteID); // 3) the module state has changed - assertEq(_votingRoles.voteDuration(), _newDuration); + assertEq(_votingRoles.getVoteDuration(), _newDuration); // 4) Now we test that we can't execute again: vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__MotionAlreadyExecuted .selector ) @@ -1015,7 +1010,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.startPrank(address(_votingRoles)); for (uint i; i < users.length; ++i) { - uint before = _votingRoles.threshold(); + uint before = _votingRoles.getThreshold(); vm.expectEmit(); emit VoterAdded(users[i]); @@ -1025,10 +1020,10 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.expectEmit(); emit ThresholdUpdated(before, before + 1); _votingRoles.addVoterAndUpdateThreshold(users[i], before + 1); - assertEq(_votingRoles.threshold(), before + 1); + assertEq(_votingRoles.getThreshold(), before + 1); } else { _votingRoles.addVoter(users[i]); - assertEq(_votingRoles.threshold(), before); + assertEq(_votingRoles.getThreshold(), before); } } @@ -1047,6 +1042,34 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.stopPrank(); } + function testAddVoters_ValidatesThreshold() public { + // To check the validate threshold function we lower the threshold to 1 + vm.prank(address(_votingRoles)); + _votingRoles.setThreshold(1); + + // Afterwards we remove one of the voters + // As the threshold check is tied to having the specific amount of voters of 2 + // after the addVoter function call + vm.prank(address(_votingRoles)); + _votingRoles.removeVoter(BOB); + + // Now we add two voters again, as the condition only triggers on 3+ voters + vm.prank(address(_votingRoles)); + _votingRoles.addVoter(BOB); + + // Here we expect the revert + vm.expectRevert( + abi.encodeWithSelector( + IAUT_EXT_VotingRoles_v2 + .Module__VotingRoleManager__InvalidThreshold + .selector + ) + ); + + vm.prank(address(_votingRoles)); + _votingRoles.addVoter(address(0xBEEF)); + } + function testRemoveVoter(address[] memory users) public { _validateUserList(users); batchAddAuthorized(users); @@ -1081,7 +1104,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // this call would leave a 1 person list with a threshold of 2 vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidThreshold .selector ); @@ -1100,23 +1123,23 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // regular remove would fail as it would bring it to // 2 out of 1 with the threshold of 2 vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidThreshold .selector ); _votingRoles.removeVoter(BOB); - assertEq(_votingRoles.threshold(), 2); + assertEq(_votingRoles.getThreshold(), 2); // try to remove again, this time including a reduction // of the threshold to 1 vm.expectEmit(); emit ThresholdUpdated(2, 1); _votingRoles.removeVoterAndUpdateThreshold(BOB, 1); - assertEq(_votingRoles.threshold(), 1); + assertEq(_votingRoles.getThreshold(), 1); // this call would leave a 1 person list with a threshold of 2 vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__EmptyVoters .selector ); @@ -1129,14 +1152,14 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // TEST: QUORUM // Get correct threshold - function testGetThreshold() public { - assertEq(_votingRoles.threshold(), DEFAULT_QUORUM); + function testGetgetThreshold() public { + assertEq(_votingRoles.getThreshold(), DEFAULT_QUORUM); } // Set a new threshold - function testMotionSetThreshold() public { - uint oldThreshold = _votingRoles.threshold(); - uint newThreshold = 3; + function testMotionSetgetThreshold() public { + uint oldThreshold = _votingRoles.getThreshold(); + uint newThreshold = 2; vm.prank(address(_votingRoles)); @@ -1145,39 +1168,44 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _votingRoles.setThreshold(newThreshold); - assertEq(_votingRoles.threshold(), newThreshold); + assertEq(_votingRoles.getThreshold(), newThreshold); } // Fail to set a threshold that's too damn high or too damn low function testSetInvalidThreshold(uint newThreshold) public { // Test too high - vm.assume(newThreshold > _votingRoles.voterCount()); + vm.assume(newThreshold > 3); vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidThreshold .selector ); vm.prank(address(_votingRoles)); _votingRoles.setThreshold(newThreshold); - // Test too low + // Test too if amount of voters is less than 3 vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidThreshold .selector ); vm.prank(address(_votingRoles)); - _votingRoles.setThreshold(1); + _votingRoles.setThreshold(0); + + // Should fail if voters are equal or more than 3 and threshold is less than 2 + + // Add voter + vm.prank(address(_votingRoles)); + _votingRoles.addVoter(address(makeAddr("voter"))); - // Test too low with zero vm.expectRevert( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidThreshold .selector ); vm.prank(address(_votingRoles)); - _votingRoles.setThreshold(0); + _votingRoles.setThreshold(1); } // Fail to change threshold when not the module itself @@ -1188,11 +1216,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { uint _newQ = 1; for (uint i; i < users.length; ++i) { vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - bytes32("onlySelf"), - users[i] - ) + IAUT_EXT_VotingRoles_v2 + .Module__VotingRoleManager__OnlySelfCallAllowed + .selector ); vm.prank(users[i]); // authorized, but not orchestrator _votingRoles.setThreshold(_newQ); @@ -1201,7 +1227,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // Change the threshold by going through governance function testGovernanceThresholdChange() public { - uint _newThreshold = 3; + uint _newThreshold = 2; // 1) Create and approve a vote bytes memory _encodedAction = @@ -1212,7 +1238,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { // 2) The vote gets executed by anybody - uint _oldThreshold = _votingRoles.threshold(); + uint _oldThreshold = _votingRoles.getThreshold(); vm.expectEmit(true, true, true, true); emit ThresholdUpdated(_oldThreshold, _newThreshold); @@ -1221,20 +1247,20 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _votingRoles.executeMotion(_voteID); // 3) The orchestrator state has changed - assertEq(_votingRoles.threshold(), _newThreshold); + assertEq(_votingRoles.getThreshold(), _newThreshold); } //-------------------------------------------------------------------------- // TEST: VOTE DURATION // Get correct vote duration - function testGetVoteDuration() public { - assertEq(_votingRoles.voteDuration(), DEFAULT_DURATION); + function testGetgetVoteDuration() public { + assertEq(_votingRoles.getVoteDuration(), DEFAULT_DURATION); } // Set new vote duration - function testMotionSetVoteDuration() public { - uint _oldDuration = _votingRoles.voteDuration(); + function testMotionSetgetVoteDuration() public { + uint _oldDuration = _votingRoles.getVoteDuration(); uint _newDuration = 3 days; vm.prank(address(_votingRoles)); @@ -1244,17 +1270,17 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { _votingRoles.setVotingDuration(_newDuration); - assertEq(_votingRoles.voteDuration(), _newDuration); + assertEq(_votingRoles.getVoteDuration(), _newDuration); } // Fail to set vote durations out of bounds - function testMotionSetInvalidVoteDuration() public { - uint _oldDur = _votingRoles.voteDuration(); + function testMotionSetInvalidgetVoteDuration() public { + uint _oldDur = _votingRoles.getVoteDuration(); uint _newDur = 3 weeks; vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidVotingDuration .selector ) @@ -1266,7 +1292,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IAUT_EXT_VotingRoles_v1 + IAUT_EXT_VotingRoles_v2 .Module__VotingRoleManager__InvalidVotingDuration .selector ) @@ -1274,7 +1300,7 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { vm.prank(address(_votingRoles)); _votingRoles.setVotingDuration(_newDur); - assertEq(_votingRoles.voteDuration(), _oldDur); + assertEq(_votingRoles.getVoteDuration(), _oldDur); } // Set new duration bygoing through governance @@ -1293,11 +1319,9 @@ contract AUT_EXT_VotingRoles_v1Test is ModuleTest { uint _newDuration = 5 days; for (uint i; i < users.length; ++i) { vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - bytes32("onlySelf"), - users[i] - ) + IAUT_EXT_VotingRoles_v2 + .Module__VotingRoleManager__OnlySelfCallAllowed + .selector ); vm.prank(users[i]); // authorized, but not orchestrator _votingRoles.setVotingDuration(_newDuration); diff --git a/test/unit/modules/authorizer/role/AUT_Roles_v1.t.sol b/test/unit/modules/authorizer/role/AUT_Roles_v1.t.sol deleted file mode 100644 index 7a70b10c2..000000000 --- a/test/unit/modules/authorizer/role/AUT_Roles_v1.t.sol +++ /dev/null @@ -1,1147 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// SuT -import {Test} from "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import { - AUT_Roles_v1, - IAuthorizer_v1, - IModule_v1 -} from "@aut/role/AUT_Roles_v1.sol"; -// External Libraries -import {Clones} from "@oz/proxy/Clones.sol"; - -import {IERC165} from "@oz/interfaces/IERC165.sol"; - -import {IAccessControlEnumerable} from - "@oz/access/extensions/IAccessControlEnumerable.sol"; - -import {IAccessControl} from "@oz/access/IAccessControl.sol"; -// Internal Dependencies -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; -import {TransactionForwarder_v1} from - "src/external/forwarder/TransactionForwarder_v1.sol"; -// Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; -// Mocks -import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; -import {FundingManagerV1Mock} from - "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; -import {GovernorV1Mock} from "@mocks/external/governance/GovernorV1Mock.sol"; -import {ModuleFactoryV1Mock} from "@mocks/factories/ModuleFactoryV1Mock.sol"; - -contract AUT_RolesV1Test is Test { - // Mocks - AUT_Roles_v1 _authorizer; - Orchestrator_v1 internal _orchestrator = new Orchestrator_v1(address(0)); - ERC20Mock internal _token = new ERC20Mock("Mock Token", "MOCK", 18); - FundingManagerV1Mock _fundingManager = new FundingManagerV1Mock(); - PaymentProcessorV1Mock _paymentProcessor = new PaymentProcessorV1Mock(); - GovernorV1Mock internal _governor = new GovernorV1Mock(); - ModuleFactoryV1Mock internal _moduleFactory = new ModuleFactoryV1Mock(); - TransactionForwarder_v1 _forwarder = new TransactionForwarder_v1(); - address ALBA = address(0xa1ba); // default authorized person - address BOB = address(0xb0b); // example person to add - - bytes32 immutable ROLE_0 = "ROLE_0"; - bytes32 immutable ROLE_1 = "ROLE_1"; - - // Orchestrator_v1 Constants - uint internal constant _ORCHESTRATOR_ID = 1; - // Module Constants - uint constant MAJOR_VERSION = 1; - uint constant MINOR_VERSION = 0; - uint constant PATCH_VERSION = 0; - string constant URL = "https://github.com/organization/module"; - string constant TITLE = "Module"; - - IModule_v1.Metadata _METADATA = IModule_v1.Metadata( - MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, TITLE - ); - - //-------------------------------------------------------------------------- - // Events - - /** - * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` - * - * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - * {RoleAdminChanged} not being emitted signaling this. - * - * _Available since v3.1._ - */ - event RoleAdminChanged( - bytes32 indexed role, - bytes32 indexed previousAdminRole, - bytes32 indexed newAdminRole - ); - - /** - * @dev Emitted when `account` is granted `role`. - * - * `sender` is the account that originated the contract call, an admin role - * bearer except when using {AccessControl-_setupRole}. - */ - event RoleGranted( - bytes32 indexed role, address indexed account, address indexed sender - ); - - /** - * @dev Emitted when `account` is revoked `role`. - * - * `sender` is the account that originated the contract call: - * - if using `revokeRole`, it is the admin role bearer - * - if using `renounceRole`, it is the role bearer (i.e. `account`) - */ - event RoleRevoked( - bytes32 indexed role, address indexed account, address indexed sender - ); - - function setUp() public virtual { - address authImpl = address(new AUT_Roles_v1()); - _authorizer = AUT_Roles_v1(Clones.clone(authImpl)); - address propImpl = address(new Orchestrator_v1(address(_forwarder))); - _orchestrator = Orchestrator_v1(Clones.clone(propImpl)); - ModuleV1Mock module = new ModuleV1Mock(); - address[] memory modules = new address[](1); - modules[0] = address(module); - _orchestrator.init( - _ORCHESTRATOR_ID, - address(_moduleFactory), - modules, - _fundingManager, - _authorizer, - _paymentProcessor, - _governor - ); - - address initialAuth = ALBA; - - _authorizer.init( - IOrchestrator_v1(_orchestrator), _METADATA, abi.encode(initialAuth) - ); - - // console.log(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA)); - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA), true); - // console.log(_authorizer.hasRole(_authorizer.getAdminRole(), address(this))); - assertEq( - _authorizer.hasRole(_authorizer.getAdminRole(), address(this)), - false - ); - } - - //-------------------------------------------------------------------------------- - // Tests Initialization - - function testSupportsInterface() public { - assertTrue( - _authorizer.supportsInterface(type(IAuthorizer_v1).interfaceId) - ); - } - - function testInitWithInitialAdmin(address initialAuth) public { - // Checks that address list gets correctly stored on initialization - // We "reuse" the orchestrator created in the setup, but the orchestrator doesn't know about this new authorizer. - - address authImpl = address(new AUT_Roles_v1()); - AUT_Roles_v1 testAuthorizer = AUT_Roles_v1(Clones.clone(authImpl)); - - vm.assume(initialAuth != address(0)); - vm.assume(initialAuth != address(this)); - vm.assume(initialAuth != address(_orchestrator)); - - testAuthorizer.init( - IOrchestrator_v1(_orchestrator), - _METADATA, - abi.encode(initialAuth, address(this)) - ); - - assertEq( - testAuthorizer.getRoleAdmin(testAuthorizer.BURN_ADMIN_ROLE()), - testAuthorizer.BURN_ADMIN_ROLE() - ); - - assertEq(address(testAuthorizer.orchestrator()), address(_orchestrator)); - - assertEq( - testAuthorizer.hasRole(testAuthorizer.getAdminRole(), initialAuth), - true - ); - - assertEq( - testAuthorizer.hasRole(testAuthorizer.getAdminRole(), address(this)), - false - ); - assertEq( - testAuthorizer.getRoleMemberCount(testAuthorizer.getAdminRole()), 1 - ); - } - - function testInitWithoutInitialAdmins() public { - // Checks that address list gets correctly stored on initialization if there are no admins given - // We "reuse" the orchestrator created in the setup, but the orchestrator doesn't know about this new authorizer. - - address authImpl = address(new AUT_Roles_v1()); - AUT_Roles_v1 testAuthorizer = AUT_Roles_v1(Clones.clone(authImpl)); - - address initialAuth = address(0); - - vm.expectRevert( - IAuthorizer_v1.Module__Authorizer__InvalidInitialAdmin.selector - ); - - testAuthorizer.init( - IOrchestrator_v1(_orchestrator), - _METADATA, - abi.encode(initialAuth, address(this)) - ); - - assertEq( - testAuthorizer.getRoleMemberCount(testAuthorizer.getAdminRole()), 0 - ); - } - - function testInitWithInitialAdminSameAsDeployer() public { - // Checks that address list gets correctly stored on initialization - // We "reuse" the orchestrator created in the setup, but the orchestrator doesn't know about this new authorizer. - - address authImpl = address(new AUT_Roles_v1()); - AUT_Roles_v1 testAuthorizer = AUT_Roles_v1(Clones.clone(authImpl)); - - address initialAuth = address(this); - - testAuthorizer.init( - IOrchestrator_v1(_orchestrator), - _METADATA, - abi.encode(initialAuth, address(this)) - ); - - assertEq( - testAuthorizer.getRoleAdmin(testAuthorizer.BURN_ADMIN_ROLE()), - testAuthorizer.BURN_ADMIN_ROLE() - ); - - assertEq(address(testAuthorizer.orchestrator()), address(_orchestrator)); - - assertEq( - testAuthorizer.hasRole(testAuthorizer.getAdminRole(), initialAuth), - true - ); - - assertEq( - testAuthorizer.getRoleMemberCount(testAuthorizer.getAdminRole()), 1 - ); - } - - function testReinitFails() public { - // Create a mock new orchestrator - Orchestrator_v1 newOrchestrator = Orchestrator_v1( - Clones.clone(address(new Orchestrator_v1(address(0)))) - ); - - address initialAdmin = address(this); - - vm.expectRevert(); - _authorizer.init( - IOrchestrator_v1(newOrchestrator), - _METADATA, - abi.encode(initialAdmin) - ); - assertEq( - _authorizer.hasRole(_authorizer.getAdminRole(), address(this)), - false - ); - assertEq(address(_authorizer.orchestrator()), address(_orchestrator)); - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA), true); - assertEq(_authorizer.getRoleMemberCount(_authorizer.getAdminRole()), 1); - } - - // Test Register Roles - - //-------------------------------------------------------------------------------- - // Test manually granting and revoking roles as orchestrator-defined Admin - - function testGrantAdminRole(address[] memory newAuthorized) public { - uint amountAuth = - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()); - - _validateAuthorizedList(newAuthorized); - - vm.startPrank(address(ALBA)); - for (uint i; i < newAuthorized.length; ++i) { - vm.expectEmit(true, true, true, true); - emit RoleGranted( - _authorizer.getAdminRole(), newAuthorized[i], address(ALBA) - ); - - _authorizer.grantRole(_authorizer.getAdminRole(), newAuthorized[i]); - } - vm.stopPrank(); - - for (uint i; i < newAuthorized.length; ++i) { - assertEq( - _authorizer.hasRole( - _authorizer.getAdminRole(), newAuthorized[i] - ), - true - ); - } - assertEq( - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()), - (amountAuth + newAuthorized.length) - ); - } - - function testGrantAdminRoleFailsIfOrchestratorWillBeAdmin() public { - vm.startPrank(address(ALBA)); - - bytes32 adminRole = _authorizer.getAdminRole(); - - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1 - .Module__Authorizer__OrchestratorCannotHaveAdminRole - .selector - ) - ); - - _authorizer.grantRole(adminRole, address(_orchestrator)); - - vm.stopPrank(); - } - - function testRevokeAdminRole() public { - // Add Bob as admin - vm.startPrank(address(ALBA)); - _authorizer.grantRole(_authorizer.getAdminRole(), BOB); // Meet your new Manager - vm.stopPrank(); - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), BOB), true); - - uint amountAuth = - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()); - - vm.startPrank(address(ALBA)); - - vm.expectEmit(true, true, true, true); - emit RoleRevoked( - _authorizer.getAdminRole(), address(ALBA), address(ALBA) - ); - - _authorizer.revokeRole(_authorizer.getAdminRole(), ALBA); - vm.stopPrank(); - - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA), false); - assertEq( - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()), - amountAuth - 1 - ); - } - - function testRemoveLastAdminFails() public { - uint amountAuth = - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()); - bytes32 adminRole = _authorizer.getAdminRole(); // To correctly time the vm.expectRevert - - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1 - .Module__Authorizer__AdminRoleCannotBeEmpty - .selector - ) - ); - vm.prank(address(ALBA)); - _authorizer.revokeRole(adminRole, ALBA); - - assertEq(_authorizer.hasRole(adminRole, ALBA), true); - assertEq( - _authorizer.getRoleMemberCount(_authorizer.getAdminRole()), - amountAuth - ); - } - - // Test grantRoleFromModule - // - Should revert if caller is not a module - // - Should not revert if role is already granted, but not emit events either - /// forge-config: default.allow_internal_expect_revert = true - function testGrantRoleFromModule() public { - address newModule = _setupMockSelfManagedModule(); - bytes32 role0_module = _authorizer.generateRoleId(newModule, ROLE_0); - - assertEq(_authorizer.hasRole(role0_module, ALBA), false); - - vm.prank(newModule); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(role0_module, ALBA, newModule); - - _authorizer.grantRoleFromModule(ROLE_0, ALBA); - - assertEq(_authorizer.hasRole(role0_module, ALBA), true); - } - - function testGrantRoleFromModuleFailsIfCalledByNonModule() public { - address newModule = _setupMockSelfManagedModule(); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.grantRoleFromModule(ROLE_0, ALBA); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - false - ); - } - - event hm(uint test); - - function testGrantRoleFromModuleFailsIfModuleNotInOrchestrator() public { - address newModule = _setupMockSelfManagedModule(); - - vm.startPrank(ALBA); - - _orchestrator.initiateRemoveModuleWithTimelock(newModule); - - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeRemoveModule(newModule); - - vm.stopPrank(); - - vm.prank(newModule); - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1.Module__Authorizer__NotActiveModule.selector, - newModule - ) - ); - _authorizer.grantRoleFromModule(ROLE_0, ALBA); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - false - ); - } - - function testGrantRoleFromModuleIdempotence() public { - address newModule = _setupMockSelfManagedModule(); - - vm.startPrank(newModule); - - _authorizer.grantRoleFromModule(ROLE_0, ALBA); - - _authorizer.grantRoleFromModule(ROLE_0, ALBA); - // No reverts happen - - vm.stopPrank(); - - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - true - ); - } - - // Test grantRoleFromModuleBatched - // - Should revert if caller is not a module - // - Should not revert if role is already granted, but not emit events either - // - Should not revert if address list is empty - - function testGrantRoleFromModuleBatched(address[] memory newAuthorized) - public - { - _validateAuthorizedList(newAuthorized); - - address newModule = _setupMockSelfManagedModule(); - bytes32 role0_module = _authorizer.generateRoleId(newModule, ROLE_0); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(role0_module, newAuthorized[i]), false); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(role0_module, newAuthorized[i], newModule); - } - - vm.prank(newModule); - _authorizer.grantRoleFromModuleBatched(ROLE_0, newAuthorized); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(role0_module, newAuthorized[i]), true); - } - } - - function testGrantRoleFromModuleBatchedFailsIfCalledByNonModule() public { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.grantRoleFromModuleBatched(ROLE_0, targets); - (ROLE_0, ALBA); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - false - ); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testGrantRoleFromModuleBatchedFailsIfModuleNotInOrchestrator() - public - { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.startPrank(ALBA); - _orchestrator.initiateRemoveModuleWithTimelock(newModule); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeRemoveModule(newModule); - vm.stopPrank(); - - vm.prank(newModule); - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1.Module__Authorizer__NotActiveModule.selector, - newModule - ) - ); - _authorizer.grantRoleFromModuleBatched(ROLE_0, targets); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - false - ); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testGrantRoleFromModuleBatchedIdempotenceOnEmptyList() public { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](0); - - vm.prank(newModule); - _authorizer.grantRoleFromModuleBatched(ROLE_0, targets); - } - - // Test revokeRoleFromModule - // - Should revert if caller is not a module - // - Should revert if role does not exist - // - Should not revert if target doesn't have role. - - function testRevokeRoleFromModule() public { - address newModule = _setupMockSelfManagedModule(); - bytes32 role0_module = _authorizer.generateRoleId(newModule, ROLE_0); - - assertEq(_authorizer.hasRole(role0_module, BOB), false); - - vm.prank(newModule); - - _authorizer.grantRoleFromModule(ROLE_0, address(BOB)); - - assertEq(_authorizer.hasRole(role0_module, BOB), true); - - vm.prank(newModule); - - vm.expectEmit(true, true, true, true); - emit RoleRevoked(role0_module, BOB, newModule); - - _authorizer.revokeRoleFromModule(ROLE_0, address(BOB)); - - assertEq(_authorizer.hasRole(role0_module, BOB), false); - } - - function testRevokeRoleFromModuleFailsIfCalledByNonModule() public { - address newModule = _setupMockSelfManagedModule(); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.revokeRoleFromModule(ROLE_0, BOB); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testRevokeRoleFromModuleFailsIfModuleNotInOrchestrator() public { - address newModule = _setupMockSelfManagedModule(); - - vm.startPrank(ALBA); - _orchestrator.initiateRemoveModuleWithTimelock(newModule); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeRemoveModule(newModule); - vm.stopPrank(); - - vm.prank(newModule); - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1.Module__Authorizer__NotActiveModule.selector, - newModule - ) - ); - _authorizer.revokeRoleFromModule(ROLE_0, BOB); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testRevokeRoleFromModuleIdempotence() public { - address newModule = _setupMockSelfManagedModule(); - - vm.startPrank(newModule); - - _authorizer.revokeRoleFromModule(ROLE_0, BOB); - - _authorizer.revokeRoleFromModule(ROLE_0, BOB); - // No reverts happen - - vm.stopPrank(); - - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - // Test revokeRoleFromModuleBatched - // - Should revert if caller is not a module - // - Should not revert if target doesn't have role. - // - Should not revert if address list is empty - - function testRevokeRoleFromModuleBatched(address[] memory newAuthorized) - public - { - address newModule = _setupMockSelfManagedModule(); - newAuthorized = _validateAuthorizedList(newAuthorized); - bytes32 role0_module = _authorizer.generateRoleId(newModule, ROLE_0); - - // grant role to the addresses - for (uint i = 0; i < newAuthorized.length; i++) { - vm.prank(newModule); - _authorizer.grantRoleFromModule(ROLE_0, newAuthorized[i]); - assertEq(_authorizer.hasRole(role0_module, newAuthorized[i]), true); - } - - for (uint i = 0; i < newAuthorized.length; i++) { - vm.expectEmit(true, true, true, true); - emit RoleRevoked(role0_module, newAuthorized[i], newModule); - } - - vm.prank(newModule); - _authorizer.revokeRoleFromModuleBatched(ROLE_0, newAuthorized); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(role0_module, newAuthorized[i]), false); - } - } - - function testRevokeRoleFromModuleBatchedFailsIfCalledByNonModule() public { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.revokeRoleFromModuleBatched(ROLE_0, targets); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testRevokeRoleFromModuleBatchedFailsIfModuleNotInOrchestrator() - public - { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.startPrank(ALBA); - _orchestrator.initiateRemoveModuleWithTimelock(newModule); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeRemoveModule(newModule); - vm.stopPrank(); - - vm.prank(newModule); - vm.expectRevert( - abi.encodeWithSelector( - IAuthorizer_v1.Module__Authorizer__NotActiveModule.selector, - newModule - ) - ); - _authorizer.revokeRoleFromModuleBatched(ROLE_0, targets); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testRevokeRoleFromModuleBatchedIdempotence() public { - address newModule = _setupMockSelfManagedModule(); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.startPrank(newModule); - - _authorizer.revokeRoleFromModuleBatched(ROLE_0, targets); - _authorizer.revokeRoleFromModuleBatched(ROLE_0, targets); - - // No reverts happen - - vm.stopPrank(); - - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), ALBA - ), - false - ); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - // Test grant and revoke global roles - - // Grant global roles - function testGrantGlobalRole() public { - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - vm.prank(ALBA); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(globalRole, BOB, ALBA); - - _authorizer.grantGlobalRole(bytes32("0x03"), BOB); - assertTrue(_authorizer.hasRole(globalRole, BOB)); - } - - function testGrantGlobalRoleFailsIfNotAdmin() public { - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - vm.prank(BOB); - vm.expectRevert(); - _authorizer.grantGlobalRole(bytes32("0x03"), ALBA); - assertFalse(_authorizer.hasRole(globalRole, ALBA)); - } - - // Test grantGlobalRoleBatched - // - Should revert if caller is not admin - // - Should not revert if address list is empty - - function testGrantGlobalRoleBatched(address[] memory newAuthorized) - public - { - _validateAuthorizedList(newAuthorized); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(globalRole, newAuthorized[i]), false); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(globalRole, newAuthorized[i], ALBA); - } - - vm.prank(ALBA); - _authorizer.grantGlobalRoleBatched(bytes32("0x03"), newAuthorized); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(globalRole, newAuthorized[i]), true); - } - } - - function testGrantGlobalRoleBatchedFailsIfCalledByNonAdmin() public { - address newModule = _setupMockSelfManagedModule(); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.grantGlobalRoleBatched(globalRole, targets); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, globalRole), ALBA - ), - false - ); - assertEq( - _authorizer.hasRole( - _authorizer.generateRoleId(newModule, ROLE_0), BOB - ), - false - ); - } - - function testGrantGlobalRoleBatchedIdempotenceOnEmptyList() public { - _setupMockSelfManagedModule(); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - address[] memory targets = new address[](0); - - vm.prank(ALBA); - _authorizer.grantGlobalRoleBatched(globalRole, targets); - } - - // Revoke global roles - function testRevokeGlobalRole() public { - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - vm.startPrank(ALBA); - _authorizer.grantGlobalRole(bytes32("0x03"), BOB); - assertTrue(_authorizer.hasRole(globalRole, BOB)); - - vm.expectEmit(true, true, true, true); - emit RoleRevoked(globalRole, BOB, ALBA); - - _authorizer.revokeGlobalRole(bytes32("0x03"), BOB); - assertEq(_authorizer.hasRole(globalRole, BOB), false); - - vm.stopPrank(); - } - - function testRevokeGlobalRoleFailsIfNotAdmin() public { - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - vm.prank(ALBA); - _authorizer.grantGlobalRole(bytes32("0x03"), BOB); - assertTrue(_authorizer.hasRole(globalRole, BOB)); - - vm.prank(BOB); - vm.expectRevert(); - _authorizer.revokeGlobalRole(bytes32("0x03"), BOB); - assertTrue(_authorizer.hasRole(globalRole, BOB)); - } - - // Test revokeGlobalRoleBatched - // - Should revert if caller is not admin - // - Should not revert if address list is empty - - function testRevokeGlobalRoleBatched(address[] memory newAuthorized) - public - { - _validateAuthorizedList(newAuthorized); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - vm.startPrank(ALBA); - _authorizer.grantGlobalRoleBatched(bytes32("0x03"), newAuthorized); - - for (uint i = 0; i < newAuthorized.length; i++) { - vm.expectEmit(true, true, true, true); - emit RoleRevoked(globalRole, newAuthorized[i], ALBA); - } - - _authorizer.revokeGlobalRoleBatched(bytes32("0x03"), newAuthorized); - - for (uint i = 0; i < newAuthorized.length; i++) { - assertEq(_authorizer.hasRole(globalRole, newAuthorized[i]), false); - } - - vm.stopPrank(); - } - - function testRevokeGlobalRoleBatchedFailsIfNotAdmin() public { - _setupMockSelfManagedModule(); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - address[] memory targets = new address[](2); - targets[0] = address(ALBA); - targets[1] = address(BOB); - - vm.prank(ALBA); - _authorizer.grantGlobalRoleBatched(bytes32("0x03"), targets); - - assertEq(_authorizer.hasRole(globalRole, ALBA), true); - assertEq(_authorizer.hasRole(globalRole, BOB), true); - - vm.prank(address(BOB)); - vm.expectRevert(); - _authorizer.revokeGlobalRoleBatched(globalRole, targets); - - assertEq(_authorizer.hasRole(globalRole, ALBA), true); - assertEq(_authorizer.hasRole(globalRole, BOB), true); - } - - function testRevokeGlobalRoleBatchedIdempotenceOnEmptyList() public { - _setupMockSelfManagedModule(); - - bytes32 globalRole = - _authorizer.generateRoleId(address(_orchestrator), bytes32("0x03")); - - address[] memory targets = new address[](0); - - vm.prank(ALBA); - _authorizer.revokeGlobalRoleBatched(globalRole, targets); - } - - // ========================================================================= - // Test granting and revoking ADMIN control, and test admin control over module roles - - function testGrantAdminRole() public { - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - vm.prank(ALBA); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(adminRole, BOB, ALBA); - - _authorizer.grantRole(adminRole, BOB); - assertTrue(_authorizer.hasRole(adminRole, BOB)); - } - - function testGrantAdminRoleFailsIfNotAdmin() public { - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - address COBIE = address(0xC0B1E); - - vm.prank(BOB); - vm.expectRevert(); - _authorizer.grantRole(adminRole, COBIE); - assertFalse(_authorizer.hasRole(adminRole, COBIE)); - } - - // Test that only Admin can change admin - function testChangeRoleAdminOnModuleRole() public { - // First, we make BOB admin - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - vm.prank(ALBA); - _authorizer.grantRole(adminRole, BOB); - assertTrue(_authorizer.hasRole(adminRole, BOB)); - - // Then we set up a mock module - address newModule = _setupMockSelfManagedModule(); - bytes32 roleId = _authorizer.generateRoleId(newModule, ROLE_0); - - // Now we set the OWNER as Role admin - vm.startPrank(BOB); - _authorizer.transferAdminRole(roleId, _authorizer.getAdminRole()); - vm.stopPrank(); - - // ALBA can now freely grant and revoke roles - assertEq(_authorizer.hasRole(roleId, BOB), false); - vm.startPrank(ALBA); - _authorizer.grantRole(roleId, BOB); - assertEq(_authorizer.hasRole(roleId, BOB), true); - _authorizer.revokeRole(roleId, BOB); - assertEq(_authorizer.hasRole(roleId, BOB), false); - } - - function testChangeRoleAdminOnModuleRoleFailsIfNotAdmin() public { - // We set up a mock module - address newModule = _setupMockSelfManagedModule(); - - bytes32 roleId = _authorizer.generateRoleId(newModule, ROLE_0); - bytes32 adminRole = _authorizer.getAdminRole(); // Buffer this to time revert - - // BOB is not allowed to do this - vm.startPrank(BOB); - vm.expectRevert(); - _authorizer.transferAdminRole(roleId, adminRole); - vm.stopPrank(); - } - - // Test that ADMIN cannot change module roles if admin role was burned - - function testAdminCannotModifyRoleIfAdminBurned() public { - // First, we make BOB admin - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - vm.prank(ALBA); - _authorizer.grantRole(adminRole, BOB); - assertTrue(_authorizer.hasRole(adminRole, BOB)); - - // Then we set up a mock module and buffer the role with burned admin - address newModule = _setupMockSelfManagedModule(); - bytes32 roleId = _authorizer.generateRoleId(newModule, ROLE_1); - - // BOB can NOT grant and revoke roles even though he's admin - assertEq(_authorizer.hasRole(roleId, BOB), false); - vm.startPrank(BOB); - vm.expectRevert(); - _authorizer.grantRole(roleId, BOB); - assertEq(_authorizer.hasRole(roleId, BOB), false); - vm.expectRevert(); - _authorizer.revokeRole(roleId, BOB); - assertEq(_authorizer.hasRole(roleId, BOB), false); - vm.stopPrank(); - } - - // Test the burnAdminFromModuleRole - // -> Test burnAdmin changes state - function testBurnAdminChangesRoleState() public { - // _setupMockSelfManagedModule implicitly test this - } - // -> Test a role with burnt admin cannot be modified by admin - - function testModifyRoleByAdminFailsIfAdminBurned() public { - // First, we make BOB admin - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - vm.prank(ALBA); - _authorizer.grantRole(adminRole, BOB); - assertTrue(_authorizer.hasRole(adminRole, BOB)); - - // Then we set up a mock module and buffer both roles - address newModule = _setupMockSelfManagedModule(); - bytes32 roleId_0 = _authorizer.generateRoleId(newModule, ROLE_0); - bytes32 roleId_1 = _authorizer.generateRoleId(newModule, ROLE_1); - - vm.startPrank(BOB); - - // BOB can modify role 0 - assertEq(_authorizer.hasRole(roleId_0, ALBA), false); - _authorizer.grantRole(roleId_0, ALBA); - assertEq(_authorizer.hasRole(roleId_0, ALBA), true); - _authorizer.revokeRole(roleId_0, ALBA); - assertEq(_authorizer.hasRole(roleId_0, ALBA), false); - - // But not role 1 - vm.expectRevert(); - _authorizer.grantRole(roleId_1, ALBA); - assertEq(_authorizer.hasRole(roleId_1, ALBA), false); - vm.expectRevert(); - _authorizer.revokeRole(roleId_1, ALBA); - assertEq(_authorizer.hasRole(roleId_1, ALBA), false); - vm.stopPrank(); - } - - // ========================================================================= - // Test Helper Functions - - // SetUp ModuleWith Roles. - // Creates a Mock module and adds it to the orchestrator with 2 roles: - // - 1 with default Admin - // - 1 with burnt admin - // BOB is member of both roles. - function _setupMockSelfManagedModule() internal returns (address) { - ModuleV1Mock mockModule = new ModuleV1Mock(); - - vm.startPrank(ALBA); // We assume ALBA is admin - _orchestrator.initiateAddModuleWithTimelock(address(mockModule)); - vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); - emit hm(_orchestrator.MODULE_UPDATE_TIMELOCK()); - _orchestrator.executeAddModule(address(mockModule)); - vm.stopPrank(); - vm.startPrank(address(mockModule)); - - vm.expectEmit(true, true, true, true); - emit RoleAdminChanged( - _authorizer.generateRoleId(address(mockModule), ROLE_1), - bytes32(0x00), - _authorizer.BURN_ADMIN_ROLE() - ); - - _authorizer.burnAdminFromModuleRole(ROLE_1); - - vm.stopPrank(); - - bytes32 burntAdmin = _authorizer.getRoleAdmin( - _authorizer.generateRoleId(address(mockModule), ROLE_1) - ); - assertTrue(burntAdmin == _authorizer.BURN_ADMIN_ROLE()); - - return address(mockModule); - } - - function _validateAuthorizedList(address[] memory auths) - internal - returns (address[] memory) - { - vm.assume(auths.length != 0); - vm.assume(auths.length < 20); - assumeValidAuths(auths); - - return auths; - } - // Adapted from orchestrator/helper/TypeSanityHelper.sol - - mapping(address => bool) authorizedCache; - - function assumeValidAuths(address[] memory addrs) public { - for (uint i; i < addrs.length; ++i) { - assumeValidAuth(addrs[i]); - - // Assume authorized address unique. - vm.assume(!authorizedCache[addrs[i]]); - - // Add contributor address to cache. - authorizedCache[addrs[i]] = true; - } - } - - function assumeValidAuth(address a) public view { - address[] memory invalids = createInvalidAuthorized(); - - for (uint i; i < invalids.length; ++i) { - vm.assume(a != invalids[i]); - } - } - - function createInvalidAuthorized() public view returns (address[] memory) { - address[] memory invalids = new address[](8); - - invalids[0] = address(0); - invalids[1] = address(_orchestrator); - invalids[2] = address(_authorizer); - invalids[3] = address(_paymentProcessor); - invalids[4] = address(_token); - invalids[5] = address(this); - invalids[6] = ALBA; - invalids[7] = BOB; - - return invalids; - } - // ========================================================================= -} diff --git a/test/unit/modules/authorizer/role/AUT_Roles_v2.t.sol b/test/unit/modules/authorizer/role/AUT_Roles_v2.t.sol new file mode 100644 index 000000000..1c7e4e414 --- /dev/null +++ b/test/unit/modules/authorizer/role/AUT_Roles_v2.t.sol @@ -0,0 +1,1180 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// External Libraries +import {Clones} from "@oz/proxy/Clones.sol"; + +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +import {IERC165} from "@oz/utils/introspection/IERC165.sol"; + +// Internal Dependencies +import { + ModuleTest, + IModule_v2, + IOrchestrator_v2 +} from "@unitTest/modules/ModuleTest.sol"; + +// Internal Libraries +import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; + +// Internal Interfaces +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; + +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; + +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; + +// SuT +import {AUT_Roles_v2_Exposed} from + "@mocks/modules/authorizer/AUT_Roles_v2_Exposed.sol"; + +// Mocks +import {FundingManagerV1Mock} from + "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; +import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; + +// Errors +import {OZErrors} from "@testUtilities/OZErrors.sol"; + +// External Dependencies +import {IAccessControl} from "@oz/access/IAccessControl.sol"; + +contract AUT_Roles_v2_Test is ModuleTest { + /////////////////////////////////////////////////////////////////////////// + // State + + // SuT + AUT_Roles_v2_Exposed _authSuT; + + // Constants + address _initialAdmin = makeAddr("initialAdmin"); + address _bob = makeAddr("Bob"); + address _alice = makeAddr("Alice"); + + // Addresses + + // Bob and Alice can Access + bytes4 _selector1 = bytes4(keccak256("selector1()")); + // Alice can access + bytes4 _selector2 = bytes4(keccak256("selector2()")); + // No Permissions + bytes4 _selector3 = bytes4(keccak256("selector3()")); + // Public Role can access + bytes4 _selector4 = bytes4(keccak256("selector4()")); + + /////////////////////////////////////////////////////////////////////////// + // Setup + + function setUp() public { + address impl = address(new AUT_Roles_v2_Exposed()); + _authSuT = AUT_Roles_v2_Exposed(Clones.clone(impl)); + + // initiate orchestrator without extra Module + _setUpOrchestrator(); + + _authSuT.init(_orchestrator, _METADATA, abi.encode(_initialAdmin)); + + // Change Authorizer of Module Test to SuT + _orchestrator.initiateSetAuthorizerWithTimelock(address(_authSuT)); + vm.warp(72 hours + 1); + _orchestrator.executeSetAuthorizer(address(_authSuT)); + } + + /////////////////////////////////////////////////////////////////////////// + // Test Initialization + + /* + Test: SupportsInterface + └── Given: The interfaceId is IAuthorizer_v2 + └── When: the function supportsInterface is called + └── Then: the function should return true + */ + function testSupportsInterface() public override(ModuleTest) { + assertTrue(_authSuT.supportsInterface(type(IAuthorizer_v2).interfaceId)); + } + + /* + Test: Init + └── When: the function init is called + └── Then: the function should set the initial admin + */ + function testInit() public override { + // Check that the initial Admin is set + assertTrue(_authSuT.hasRole(_authSuT.getAdminRole(), _initialAdmin)); + } + + /* + Test: ReinitFails + └── When: the function init is called after the contract has been initialized + └── Then: the function should revert + */ + function testReinitFails() public override { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + _authSuT.init(_orchestrator, _METADATA, abi.encode(_initialAdmin)); + } + ///////////////////////////////////////////////////////////////////////////// + // Test Modifier + + //idNotDefaultAdmin + /* + Test: idNotDefaultAdmin Modifier + └── Given: Role ID is the default admin role + └── When: function with idNotDefaultAdmin modifier is called + └── Then: the function should revert + */ + function testIdNotDefaultAdminModifier(bytes32 _givenRoleId) public { + if (_givenRoleId == _authSuT.DEFAULT_ADMIN_ROLE()) { + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2 + .Module__Authorizer__CannotModifyAdminRoleAccess + .selector + ) + ); + } + _authSuT.idNotDefaultAdminModifier_exposed(_givenRoleId); + } + + /* + Test: idExists Modifier + └── Given: Role ID is not existing and is not Public Role + └── When: function with idExists modifier is called + └── Then: the function should revert + */ + function testidExistsModifier( + uint _lastAssignedRoleIdValue, + bytes32 _givenRoleId + ) public { + _authSuT.changeLastAssignedRoleId(_lastAssignedRoleIdValue); + if ( + _givenRoleId != _authSuT.PUBLIC_ROLE() + && uint(_givenRoleId) > _lastAssignedRoleIdValue + ) { + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2 + .Module__Authorizer__RoleIdNotExisting + .selector + ) + ); + } + _authSuT.idExistsModifier_exposed(_givenRoleId); + } + + /////////////////////////////////////////////////////////////////////////// + // Test External Functions + + // ======================================================================== + // Public Getter Functions + + // ------------------------------------------------------------------------ + // Getter - Authorization + + /* + Test: getPermissions + └── When: getPermissions is called + └── Then: Return all Role ids that are listed for that function + */ + function testGetPermissions( + address target_, + bytes4 selector_, + bytes32[] memory permissions_ + ) public { + for (uint i = 0; i < permissions_.length; i++) { + _authSuT.addAccessPermission_unrestricted( + target_, selector_, permissions_[i] + ); + } + bytes32[] memory returnedPermissions = + _authSuT.getPermissions(target_, selector_); + assertEq(returnedPermissions.length, permissions_.length); + for (uint i = 0; i < permissions_.length; i++) { + assertEq(returnedPermissions[i], permissions_[i]); + } + } + + /* + Test: isRolePermissioned + └── When: isRolePermissioned is called + └── Then: Return true if the roleId is listed for that function + */ + function testisRolePermissioned( + bytes32 roleId_, + address target_, + bytes4 selector_, + bool isRolePermissioned_ + ) public { + if (isRolePermissioned_) { + _authSuT.addAccessPermission_unrestricted( + target_, selector_, roleId_ + ); + } + assertEq( + _authSuT.isRolePermissioned(target_, selector_, roleId_), + isRolePermissioned_ + ); + } + + /* + Test: hasPermission + ├── Given: Caller has the Default Admin Role + │ └── When: hasPermission is called + │ └── Then: Return true + ├── Given: There are no roleId permissions for the function + │ └── When: hasPermission is called + │ └── Then: Return false + ├── Given: The permissions contain the public role + │ └── When: hasPermission is called + │ └── Then: Return true + └── Given: The caller inhabits one of the roles that have permission + └── When: hasPermission is called + └── Then: Return true + */ + + function testHasPermission_CallerHasDefaultAdminRole(uint seed_) public { + // Create Setup with predetermined roles and function restrictions + address target = address(uint160(seed_)); + createSetup(target); + + // Select function selector from setup based on seed + bytes4 selector = selectFunctionSelectorBasedOnSeed(seed_); + + // Check that if the caller has the default admin role, they can call any function + assertTrue(_authSuT.hasPermission(_initialAdmin, target, selector)); + } + + function testHasPermission_NoPermissionsForFunction( + uint seed_, + address caller_ + ) public { + // Make sure calle does not have the default admin role + vm.assume(caller_ != _initialAdmin); + + // Create Setup with predetermined roles and function restrictions + address target = address(uint160(seed_)); + createSetup(target); + + // Select function selector from setup based on seed + bytes4 selector = selectFunctionSelectorBasedOnSeed(seed_); + + // Check that the function has no permissions + if (_authSuT.getPermissions(target, selector).length == 0) { + // If the function has no permissions, the caller should not be able to call the function + assertFalse(_authSuT.hasPermission(caller_, target, selector)); + } + } + + function testHasPermission_PermissionIsPublicRole( + uint seed_, + address caller_ + ) public { + // Make sure caller does not have the default admin role + vm.assume(caller_ != _initialAdmin); + + // Create Setup with predetermined roles and function restrictions + address target = address(uint160(seed_)); + createSetup(target); + + // Select function selector from setup based on seed + bytes4 selector = selectFunctionSelectorBasedOnSeed(seed_); + + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + for (uint i = 0; i < permissions.length; i++) { + if (permissions[i] == _authSuT.PUBLIC_ROLE()) { + assertTrue(_authSuT.hasPermission(caller_, target, selector)); + } + } + } + + function testHasPermission_IsNotPublicRole(uint seed_, uint callerSeed_) + public + { + // Fetch caller from seed + address caller = selectCallerBasedOnSeed(callerSeed_); + // Make sure caller is not the default admin or Bob or Alice + vm.assume(caller != _initialAdmin || caller != _bob || caller != _alice); + + // Create Setup with predetermined roles and function restrictions + address target = address(uint160(seed_)); + createSetup(target); + + // Select function selector from setup based on seed + bytes4 selector = selectFunctionSelectorBasedOnSeed(seed_); + + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + for (uint i = 0; i < permissions.length; i++) { + // If the caller has the role they should be able to call the function + if (_authSuT.hasRole(permissions[i], caller)) { + assertTrue(_authSuT.hasPermission(caller, target, selector)); + } + } + } + + // ------------------------------------------------------------------------ + // Getter - Role Management + + /* + Test: getAdminRole + └── When: getAdminRole is called + └── Then: Return the Admin Role + */ + function testGetAdminRole() public { + assertEq(_authSuT.getAdminRole(), _authSuT.DEFAULT_ADMIN_ROLE()); + } + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - Authorization + + /* + Test: addAccessPermission + ├── Given: Caller does not inhabit the permissioned Role + │ └── When: addAccessPermission is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is the default admin role + │ └── When: addAccessPermission is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is not existing + │ └── When: addAccessPermission is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is existing and not the default admin role + ├── And: The given roleId has already permission + │ └── When: addAccessPermission is called + │ └── Then: Nothing happens + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is existing and not the default admin role + └── And: The given roleId does not have permission yet + └── When: addAccessPermission is called + └── Then: The roleId gains permission + └── And: An event is emitted + */ + + function testAddAccessPermission_ModifierInPostionChecks() public { + //permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + _authSuT.addAccessPermission(address(this), bytes4(0), bytes32(uint(0))); + + //idNotDefaultAdmin(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2 + .Module__Authorizer__CannotModifyAdminRoleAccess + .selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.addAccessPermission( + address(this), + bytes4(0), + bytes32(uint(0)) //Default Admin Id + ); + + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.addAccessPermission(address(this), bytes4(0), bytes32(uint(2))); + } + + function testAddAccessPermission_PermissionAlreadyExisting(uint seed_) + public + { + // Create All Role Ids + _authSuT.changeLastAssignedRoleId(type(uint).max); + + // Create a random lock setup + (address target, bytes4 selector) = + createRandomLockRestrictions(seed_, 1); + + // Fetch permission array for comparison + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + + // Fetch one of the permissions from the lock + bytes32 roleIdPermission = permissions[seed_ % permissions.length]; + + // Try it again + vm.prank(_initialAdmin); + _authSuT.addAccessPermission(target, selector, roleIdPermission); + + // Fetch permission array for comparison + bytes32[] memory permissionsAfter = + _authSuT.getPermissions(target, selector); + + // Check that the array length is the same + assertEq(permissions.length, permissionsAfter.length); + // Check that the array stayed the same + for (uint i = 0; i < permissions.length; i++) { + assertEq(permissions[i], permissionsAfter[i]); + } + } + + function testAddAccessPermission_PermissionNotAlreadyExisting( + uint seed_, + bytes32 roleId_ + ) public { + // Make sure roleId_ is not the default admin role or the Public Role + vm.assume(roleId_ != _authSuT.DEFAULT_ADMIN_ROLE()); + vm.assume(roleId_ != _authSuT.PUBLIC_ROLE()); + + // Create All Role Ids + _authSuT.changeLastAssignedRoleId(type(uint).max); + + // Create a random lock setup + (address target, bytes4 selector) = + createRandomLockRestrictions(seed_, 0); + + // Make sure roleId_ is not part of the function lock + vm.assume(!_authSuT.isRolePermissioned(target, selector, roleId_)); + + // Fetch permission array for comparison + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + + // Check that event is emitted + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.AccessPermissionAdded(target, selector, roleId_); + + // Add permission to roleId to function lock + vm.prank(_initialAdmin); + _authSuT.addAccessPermission(target, selector, roleId_); + + // Fetch permission array for comparison + bytes32[] memory permissionsAfter = + _authSuT.getPermissions(target, selector); + + // Check that array length was adapted + assertEq(permissionsAfter.length, permissions.length + 1); + + // Check that the role is permissioned + assertTrue(_authSuT.isRolePermissioned(target, selector, roleId_)); + } + + /* + Test: removeAccessPermission + ├── Given: Caller does not inhabit the permissioned Role + │ └── When: removeAccessPermission is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId not a permissioned + │ └── When: removeAccessPermission is called + │ └── Then: Nothing happens + ├── Given: Caller inhabits the default admin role + └── And: The given roleId is permissioned + └── When: removeAccessPermission is called + └── Then: The permission is removed + └── And: An event is emitted + + */ + function testRemoveAccessPermission_ModifierInPostionChecks() public { + //permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + _authSuT.removeAccessPermission( + address(this), bytes4(0), bytes32(uint(0)) + ); + } + + function testRemoveAccessPermission_PermissionNotExisting( + uint seed_, + bytes32 roleId_ + ) public { + // Create All Role Ids + _authSuT.changeLastAssignedRoleId(type(uint).max); + + // Create a random lock setup + (address target, bytes4 selector) = + createRandomLockRestrictions(seed_, 0); + + // Make sure roleId_ is not part of the function lock + vm.assume(!_authSuT.isRolePermissioned(target, selector, roleId_)); + + // Fetch permission array for comparison + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + + // Remove any permission from function lock + vm.prank(_initialAdmin); + _authSuT.removeAccessPermission(target, selector, roleId_); + + // Fetch permission array for comparison + bytes32[] memory permissionsAfter = + _authSuT.getPermissions(target, selector); + + // Check that array length is the same + assertEq(permissions.length, permissionsAfter.length); + + // Check that values stayed the same + for (uint i = 0; i < permissions.length; i++) { + assertEq(permissions[i], permissionsAfter[i]); + } + } + + function testRemoveAccessPermission_PermissionExisting(uint seed_) public { + // Create All Role Ids + _authSuT.changeLastAssignedRoleId(type(uint).max); + + // Create a random lock setup + (address target, bytes4 selector) = + createRandomLockRestrictions(seed_, 1); + + // Fetch permission array for comparison + bytes32[] memory permissions = _authSuT.getPermissions(target, selector); + + // Fetch one of the permissions from the lock + bytes32 roleIdPermission = permissions[uint(seed_) % permissions.length]; + + // Check that event is emitted + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.AccessPermissionRemoved( + target, selector, roleIdPermission + ); + + // Remove permission from function lock + vm.prank(_initialAdmin); + _authSuT.removeAccessPermission(target, selector, roleIdPermission); + + // Fetch permission array for comparison + bytes32[] memory permissionsAfter = + _authSuT.getPermissions(target, selector); + + // Check that array length is one less + assertEq(permissions.length - 1, permissionsAfter.length); + + // Check that roleId is not permissioned + assertFalse( + _authSuT.isRolePermissioned(target, selector, roleIdPermission) + ); + } + + // ------------------------------------------------------------------------ + // Mutating - Role Management + + /* + Test: createRole + ├── Given: Caller does not inhabit the permissioned Role + │ └── When: createRole is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is not existing + │ └── When: createRole is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + └── And: The given roleId is existing + └── When: createRole is called + ├── Then: The role id counter is incremented + ├── And: The admin for the role is set + ├── And: An event is emitted + └── And: The intial members of the role are set + */ + + function testCreateRole_ModifierInPostionChecks() public { + //permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + _authSuT.createRole("RoleName", bytes32(uint(0)), new address[](0)); + + //idExists(respectiveAdminRole_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.createRole("RoleName", bytes32(uint(2)), new address[](0)); + } + + function testCreateRole_RoleIdIsExisting( + string memory roleName_, + uint seed_, + address[] memory members_ + ) public { + // Check that members_ is reasonably sized + vm.assume(members_.length < 2500); + + // Create random number of permissions between 0 and half uint max + _authSuT.changeLastAssignedRoleId(bound(seed_, 0, type(uint).max / 2)); + + uint currentLastAssignedRoleId = _authSuT.getLastAssignedRoleId(); + bytes32 expectedRoleId = bytes32(currentLastAssignedRoleId + 1); + + // Expect event + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.RoleCreated(expectedRoleId, roleName_); + + // Create role + vm.prank(_initialAdmin); + bytes32 roleId = _authSuT.createRole( + roleName_, + bytes32(bound(seed_, 0, currentLastAssignedRoleId)), + members_ + ); + + // Check that roleId is the expected roleId + assertEq(roleId, expectedRoleId); + + // Check that each member has the role + for (uint i = 0; i < members_.length; i++) { + assertTrue(_authSuT.hasRole(roleId, members_[i])); + } + } + + /* + Test: labelRole + ├── Given: Caller does not inhabit the default admin role + │ └── When: labelRole is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is not existing + │ └── When: labelRole is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + └── And: The given roleId is existing + └── When: labelRole is called + └── Then: An event is emitted + */ + function testLabelRole_ModifierInPositionChecks() public { + //permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + _authSuT.labelRole(bytes32(uint(0)), "RoleName"); + + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.labelRole(bytes32(uint(2)), "RoleName"); + } + + function testLabelRole_idExists(string memory newRoleName_) public { + // Create Role + vm.prank(_initialAdmin); + bytes32 id = + _authSuT.createRole("RoleName", bytes32(0), new address[](0)); + + // Check that event is emitted + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.RoleLabeled(id, newRoleName_); + + // Label role + vm.prank(_initialAdmin); + _authSuT.labelRole(id, newRoleName_); + } + + /* + Test: transferAdminRole + ├── Given: Caller is not the admin ot the role for which the admin is being transferred + │ └── When: transferAdminRole is called + │ └── Then: The function should revert + ├── Given: Caller is the admin of the role for which the admin is being transferred + ├── And: The given roleId is not existing + │ └── When: transferAdminRole is called + │ └── Then: The function should revert (modifier in position check) + ├── Given: Caller is the admin of the role for which the admin is being transferred + ├── And: The given adminRoleId is not existing + │ └── When: transferAdminRole is called + │ └── Then: The function should revert (modifier in position check) + ├── Given: Caller is the admin of the role for which the admin is being transferred + ├── And: The given roleId is existing + └── When: transferAdminRole is called + └── Then: The Admin should be transferred to the new Admin + */ + + function testTransferAdminRole_OnlyRoleAdmin( + uint seed_, + bytes32 roleId_, + bytes32 roleAdmin_ + ) public { + // Make sure roleId_ is not the default admin or the public role + vm.assume(uint(roleId_) > 1); + // make sure that roleAdmin was created before roleId + vm.assume(uint(roleId_) > uint(roleAdmin_)); + + // Create Setup + // Create random amount of Roles making the next created Role have the given RoleId + _authSuT.changeLastAssignedRoleId(uint(roleId_) - 1); + // Create new Role with given RoleAdmin + vm.prank(_initialAdmin); + _authSuT.createRole("RoleName", roleAdmin_, new address[](0)); + + // randomize if caller has the admin role or not + if (seed_ % 2 == 0) { + vm.prank(_initialAdmin); + _authSuT.grantRole(roleAdmin_, _bob); + } else { + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + address(_bob), + roleAdmin_ + ) + ); + } + vm.prank(_bob); + _authSuT.transferAdminRole(roleId_, roleAdmin_); + } + + function testTransferAdminRole_ModifierInPositionChecks() public { + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.transferAdminRole(bytes32(uint(2)), bytes32(uint(0))); + + //idExists(newAdminRoleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.transferAdminRole(bytes32(uint(0)), bytes32(uint(2))); + } + + function testTransferAdminRole_idExists( + bytes32 roleId_, + bytes32 newAdminRoleId_ + ) public { + // make sure that roleAdmin was created before roleId + vm.assume(uint(roleId_) > uint(newAdminRoleId_)); + // Create Setup + // Create random amount of Roles making the next created Role have the given RoleId + _authSuT.changeLastAssignedRoleId(uint(roleId_) - 1); + // Create new Role with given RoleAdmin + vm.prank(_initialAdmin); + _authSuT.createRole("RoleName", bytes32(0), new address[](0)); + + // Call transferAdminRole + vm.prank(_initialAdmin); + _authSuT.transferAdminRole(roleId_, newAdminRoleId_); + + // Check that the new Admin Role is the new Admin + assertEq(_authSuT.getRoleAdmin(roleId_), newAdminRoleId_); + } + + // burnRoleAdmin + + /* + Test: burnRoleAdmin + ├── Given: Caller is not the admin of the role for which the admin is being burned + │ └── When: burnRoleAdmin is called + │ └── Then: The function should revert + ├── Given: Caller is the admin of the role for which the admin is being burned + ├── And: The given roleId is not existing + │ └── When: burnRoleAdmin is called + │ └── Then: The function should revert (modifier in position check) + ├── Given: Caller is the admin of the role for which the admin is being burned + └── And: The given roleId is existing + └── When: burnRoleAdmin is called + └── Then: The Admin should be burned + */ + + function testBurnRoleAdmin_OnlyRoleAdmin( + uint seed_, + bytes32 roleId_, + bytes32 roleAdmin_ + ) public { + // Make sure roleId_ is not the default admin or the public role + vm.assume(uint(roleId_) > 1); + // make sure that roleAdmin was created before roleId + vm.assume(uint(roleId_) > uint(roleAdmin_)); + + // Create Setup + // Create random amount of Roles making the next created Role have the given RoleId + _authSuT.changeLastAssignedRoleId(uint(roleId_) - 1); + // Create new Role with given RoleAdmin + vm.prank(_initialAdmin); + _authSuT.createRole("RoleName", roleAdmin_, new address[](0)); + + // randomize if caller has the admin role or not + if (seed_ % 2 == 0) { + vm.prank(_initialAdmin); + _authSuT.grantRole(roleAdmin_, _bob); + } else { + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + address(_bob), + roleAdmin_ + ) + ); + } + vm.prank(_bob); + _authSuT.burnRoleAdmin(roleId_); + } + + function testBurnRoleAdmin_ModifierInPositionChecks() public { + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.burnRoleAdmin(bytes32(uint(2))); + } + + function testBurnRoleAdmin_idExists(bytes32 roleId_, bytes32 adminRoleId_) + public + { + // make sure that roleAdmin was created before roleId + vm.assume(uint(roleId_) > uint(adminRoleId_)); + // Create Setup + // Create random amount of Roles making the next created Role have the given RoleId + _authSuT.changeLastAssignedRoleId(uint(roleId_) - 1); + // Create new Role with given RoleAdmin + vm.prank(_initialAdmin); + _authSuT.createRole("RoleName", adminRoleId_, new address[](0)); + + // Give Caller the Admin Role + vm.prank(_initialAdmin); + _authSuT.grantRole(adminRoleId_, _bob); + + // Expect event + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.RoleAdminBurned(roleId_); + + // Call transferAdminRole + vm.prank(_bob); + _authSuT.burnRoleAdmin(roleId_); + + // Check that the new Admin Role is the Burned Admin role + assertEq(_authSuT.getRoleAdmin(roleId_), _authSuT.BURN_ADMIN_ROLE()); + } + + // ------------------------------------------------------------------------ + // Mutating - Mixed Utility + + /* + Test: createRoleAndAddAccessPermissions + ├── Given: Caller does not inhabit the permissioned Role + │ └── When: createRoleAndAddAccessPermissions is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is not existing + │ └── When: createRoleAndAddAccessPermissions is called + │ └── Then: Then it should revert (modifier in position check) + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is existing + ├── And: The given targets array and the selectors array do not have the same length + │ └── When: createRoleAndAddAccessPermissions is called + │ └── Then: Then it should revert + ├── Given: Caller inhabits the default admin role + ├── And: The given roleId is existing + └── And: The given targets array and the selectors array have the same length + └── When: createRoleAndAddAccessPermissions is called + └── Then: The role is created + └── And: The permissions are added to the according function locks + */ + function testCreateRoleAndAddAccessPermissions_ModifierInPositionCheck() + public + { + //permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + _authSuT.createRoleAndAddAccessPermissions( + "RoleName", + bytes32(uint(0)), + new address[](0), + new address[](0), + new bytes4[][](0) + ); + + //idExists(respectiveAdminRole_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.createRoleAndAddAccessPermissions( + "RoleName", + bytes32(uint(2)), + new address[](0), + new address[](0), + new bytes4[][](0) + ); + } + + function testCreateRoleAndAddAccessPermissions_RevertWhenArrayLengthsAreDifferent( + address[] memory targets_, + bytes4[][] memory selectors_ + ) public { + // Make sure the arrays have different lengths + vm.assume(targets_.length != selectors_.length); + + // Invalid Input Length + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__InvalidInputLength.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.createRoleAndAddAccessPermissions( + "RoleName", bytes32(uint(0)), new address[](0), targets_, selectors_ + ); + } + + //@note This test alone takes up as much time as the others combined. Restricted the number of runs to 20 + /// forge-config: default.fuzz.runs = 20 + function testCreateRoleAndAddAccessPermissions_idExists( + string memory roleName_, + address[] memory initialMembers_, + address[] memory targets_, + bytes4[][] memory selectors_ + ) public { + // Downsize arrays to reasonable size + vm.assume(initialMembers_.length < 800); //800 + vm.assume(targets_.length < 75); //75 + vm.assume(selectors_.length <= targets_.length); + + uint selectorLength = selectors_.length; + for (uint i = 0; i < selectorLength; i++) { + vm.assume(selectors_[i].length < 50); //50 + } + + // Make sure that selector length and target length are the same + if (selectorLength < targets_.length) { + address[] memory temp = new address[](selectorLength); + for (uint i = 0; i < selectorLength; i++) { + temp[i] = targets_[i]; + } + targets_ = temp; + } + + // Check that the role is created + vm.expectEmit(true, true, true, true); + emit IAuthorizer_v2.RoleCreated(bytes32(uint(2)), roleName_); + + vm.prank(_initialAdmin); + bytes32 roleId = _authSuT.createRoleAndAddAccessPermissions( + roleName_, bytes32(0), initialMembers_, targets_, selectors_ + ); + + // Check that the permissions are added to the function locks + uint targetLength = targets_.length; + for (uint i = 0; i < targetLength; i++) { + for (uint j = 0; j < selectors_[i].length; j++) { + assertTrue( + _authSuT.isRolePermissioned( + targets_[i], selectors_[i][j], roleId + ) + ); + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // Test Internal Functions + + // ======================================================================== + // Internal Functions + + // ------------------------------------------------------------------------ + // Internal - Upstream Function Implementations + + // function _grantRole( + + /* + Test: grantRole + └── Given: The role id is not existing + └── When: grantRole is called + └── Then: The call reverts (modifier in position check) + */ + function testGrantRole_ModifierInPositionCheck() public { + // idExists(role) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + vm.prank(_initialAdmin); + _authSuT.grantRole(bytes32(uint(2)), _bob); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper Functions + + // Bob and Alice can Access selctor1 + // Alice can access selector2 + // No permissions in selector3 + // Public Role can access selector46("selector4()")); + function createSetup(address target_) internal { + vm.startPrank(_initialAdmin); + + address[] memory targetArray = new address[](1); + targetArray[0] = target_; + + address[] memory selector1Members = new address[](2); + selector1Members[0] = _bob; + selector1Members[1] = _alice; + + address[] memory selector2Members = new address[](1); + selector2Members[0] = _alice; + + address[] memory selector3Members = new address[](0); + + address[] memory selector4Members = new address[](0); + + bytes4[][] memory selectorArray = new bytes4[][](1); + bytes4[] memory selector1Array2D = new bytes4[](1); + + selector1Array2D[0] = _selector1; + selectorArray[0] = selector1Array2D; + + _authSuT.createRoleAndAddAccessPermissions( + "Selector1Role", + _authSuT.getAdminRole(), + selector1Members, + targetArray, + selectorArray + ); + + bytes4[] memory selector2Array2D = new bytes4[](1); + + selector2Array2D[0] = _selector2; + selectorArray[0] = selector2Array2D; + + _authSuT.createRoleAndAddAccessPermissions( + "Selector2Role", + _authSuT.getAdminRole(), + selector2Members, + targetArray, + selectorArray + ); + + bytes4[] memory selector3Array2D = new bytes4[](1); + + selector3Array2D[0] = _selector3; + selectorArray[0] = selector3Array2D; + + _authSuT.createRoleAndAddAccessPermissions( + "Selector3Role", + _authSuT.getAdminRole(), + selector3Members, + targetArray, + selectorArray + ); + + bytes4[] memory selector4Array2D = new bytes4[](1); + + selector4Array2D[0] = _selector4; + selectorArray[0] = selector4Array2D; + + _authSuT.createRoleAndAddAccessPermissions( + "Selector4Role", + _authSuT.getAdminRole(), + selector4Members, + targetArray, + selectorArray + ); + + _authSuT.addAccessPermission( + target_, _selector4, _authSuT.PUBLIC_ROLE() + ); + + vm.stopPrank(); + } + + function selectFunctionSelectorBasedOnSeed(uint seed_) + internal + view + returns (bytes4 selector_) + { + if (seed_ % 5 == 0) { + selector_ = _selector1; + } else if (seed_ % 5 == 1) { + selector_ = _selector2; + } else if (seed_ % 5 == 2) { + selector_ = _selector3; + } else if (seed_ % 5 == 3) { + selector_ = _selector4; + } else { + selector_ = bytes4(bytes32(seed_)); + } + } + + /// @dev 3 Possible callers + /// Bob, Alice, or a random address + /// Random address can be Bob or Alice or Initial Admin + function selectCallerBasedOnSeed(uint seed_) + internal + view + returns (address caller_) + { + if (seed_ % 3 == 0) { + caller_ = _bob; + } else if (seed_ % 3 == 1) { + caller_ = _alice; + } else { + caller_ = address(uint160(seed_)); + } + } + + /// @dev needs all permissions to be unlocked via _authSuT.changeLastAssignedRoleId(type(uint).max); + function createRandomLockRestrictions( + uint seed_, + uint minimumPermissionLength_ + ) internal returns (address target_, bytes4 selector_) { + target_ = address(uint160(seed_)); + selector_ = bytes4(bytes32(seed_)); + + uint permissionLength = seed_ % 50; + if (permissionLength <= minimumPermissionLength_) { + permissionLength = minimumPermissionLength_; + } + uint currentPermissionRoleId = seed_; + for (uint i = 0; i < permissionLength; i++) { + // Increment key in a non regular way + unchecked { + currentPermissionRoleId = currentPermissionRoleId + i * i; + } + // Key cannot be Default Admin Role + if (currentPermissionRoleId == 0) { + currentPermissionRoleId = 1; + } + // Key cannot be Public Role + if (currentPermissionRoleId == type(uint).max) { + currentPermissionRoleId = type(uint).max - 1; + } + vm.prank(_initialAdmin); + _authSuT.addAccessPermission(target_, selector_, bytes32(uint(1))); + } + } +} diff --git a/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v1.t.sol b/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v1.t.sol deleted file mode 100644 index 60a6c5505..000000000 --- a/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v1.t.sol +++ /dev/null @@ -1,787 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -// SuT -import {Test} from "forge-std/Test.sol"; - -import {AUT_RolesV1Test} from - "@unitTest/modules/authorizer/role/AUT_Roles_v1.t.sol"; - -// SuT -import { - AUT_TokenGated_Roles_v1, - IAUT_TokenGated_Roles_v1 -} from "@aut/role/AUT_TokenGated_Roles_v1.sol"; - -import {AUT_Roles_v1, IAuthorizer_v1} from "@aut/role/AUT_Roles_v1.sol"; -import {IAuthorizer_v1} from "@aut/IAuthorizer_v1.sol"; -// External Libraries -import {Clones} from "@oz/proxy/Clones.sol"; -import {IERC165} from "@oz/utils/introspection/IERC165.sol"; -import {IAccessControl} from "@oz/access/IAccessControl.sol"; -import {IAccessControlEnumerable} from - "@oz/access/extensions/IAccessControlEnumerable.sol"; - -// Internal Dependencies -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; -// Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; -// Mocks -import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -import {ERC721Mock} from "@mocks/external/token/ERC721Mock.sol"; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; -import {FundingManagerV1Mock} from - "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; -import {GovernorV1Mock} from "@mocks/external/governance/GovernorV1Mock.sol"; -import {ModuleFactoryV1Mock} from "@mocks/factories/ModuleFactoryV1Mock.sol"; - -// Run through the AUT_Roles_v1 tests with the AUT_TokenGated_Roles_v1 -contract AUT_TokenGated_RolesV1Test is AUT_RolesV1Test { - function setUp() public override { - //==== We use the AUT_TokenGated_Roles_v1 as a regular AUT_Roles_v1 ===== - address authImpl = address(new AUT_TokenGated_Roles_v1()); - _authorizer = AUT_Roles_v1(Clones.clone(authImpl)); - //========================================================================== - - address propImpl = address(new Orchestrator_v1(address(0))); - _orchestrator = Orchestrator_v1(Clones.clone(propImpl)); - ModuleV1Mock module = new ModuleV1Mock(); - address[] memory modules = new address[](1); - modules[0] = address(module); - _orchestrator.init( - _ORCHESTRATOR_ID, - address(_moduleFactory), - modules, - _fundingManager, - _authorizer, - _paymentProcessor, - _governor - ); - - address initialAuth = ALBA; - - _authorizer.init( - IOrchestrator_v1(_orchestrator), _METADATA, abi.encode(initialAuth) - ); - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA), true); - assertEq( - _authorizer.hasRole(_authorizer.getAdminRole(), address(this)), - false - ); - } -} - -contract TokenGatedAUT_RoleV1Test is Test { - // Mocks - AUT_TokenGated_Roles_v1 _authorizer; - Orchestrator_v1 internal _orchestrator = new Orchestrator_v1(address(0)); - ERC20Mock internal _token = new ERC20Mock("Mock Token", "MOCK", 18); - FundingManagerV1Mock _fundingManager = new FundingManagerV1Mock(); - PaymentProcessorV1Mock _paymentProcessor = new PaymentProcessorV1Mock(); - GovernorV1Mock internal _governor = new GovernorV1Mock(); - ModuleFactoryV1Mock internal _moduleFactory = new ModuleFactoryV1Mock(); - - ModuleV1Mock mockModule = new ModuleV1Mock(); - - address ALBA = address(0xa1ba); // default authorized person - address BOB = address(0xb0b); // example person - address CLOE = address(0xc10e); // example person - - ERC20Mock internal roleToken = - new ERC20Mock("Inverters With Benefits", "IWB", 18); - ERC721Mock internal roleNft = - new ERC721Mock("detrevnI epA thcaY bulC", "EPA"); - - bytes32 immutable ROLE_TOKEN = "ROLE_TOKEN"; - bytes32 immutable ROLE_NFT = "ROLE_NFT"; - - // Orchestrator_v1 Constants - uint internal constant _ORCHESTRATOR_ID = 1; - // Module Constants - uint constant MAJOR_VERSION = 1; - uint constant MINOR_VERSION = 0; - uint constant PATCH_VERSION = 0; - string constant URL = "https://github.com/organization/module"; - string constant TITLE = "Module"; - - IModule_v1.Metadata _METADATA = IModule_v1.Metadata( - MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, URL, TITLE - ); - - //-------------------------------------------------------------------------- - // Events - - /// @notice Event emitted when the token-gating of a role changes. - /// @param role The role that was modified. - /// @param newValue The new value of the role. - event ChangedTokenGating(bytes32 role, bool newValue); - - /// @notice Event emitted when the threshold of a token-gated role changes. - /// @param role The role that was modified. - /// @param token The token for which the threshold was modified. - /// @param newValue The new value of the threshold. - event ChangedTokenThreshold(bytes32 role, address token, uint newValue); - - /// @notice Event emitted when `account` is revoked of `role`. - /// @param role The role that was revoked. - /// @param account The account that has the role revoked. - /// @param sender The account that performed the revocation. - event RoleRevoked( - bytes32 indexed role, address indexed account, address indexed sender - ); - - function setUp() public { - address authImpl = address(new AUT_TokenGated_Roles_v1()); - _authorizer = AUT_TokenGated_Roles_v1(Clones.clone(authImpl)); - address propImpl = address(new Orchestrator_v1(address(0))); - _orchestrator = Orchestrator_v1(Clones.clone(propImpl)); - address[] memory modules = new address[](1); - modules[0] = address(mockModule); - _orchestrator.init( - _ORCHESTRATOR_ID, - address(_moduleFactory), - modules, - _fundingManager, - _authorizer, - _paymentProcessor, - _governor - ); - - address initialAuth = ALBA; - - _authorizer.init( - IOrchestrator_v1(_orchestrator), _METADATA, abi.encode(initialAuth) - ); - assertEq(_authorizer.hasRole(_authorizer.getAdminRole(), ALBA), true); - assertEq( - _authorizer.hasRole(_authorizer.getAdminRole(), address(this)), - false - ); - - // We mint some tokens: First, two different amounts of ERC20 - roleToken.mint(BOB, 1000); - roleToken.mint(CLOE, 10); - - // Then, a ERC721 for BOB - roleNft.mint(BOB); - } - - function testSupportsInterface() public { - assertTrue( - _authorizer.supportsInterface( - type(IAUT_TokenGated_Roles_v1).interfaceId - ) - ); - } - - //------------------------------------------------- - // Helper Functions - - // function set up tokenGated role with threshold - function setUpTokenGatedRole( - address module, - bytes32 role, - address token, - uint threshold - ) internal returns (bytes32) { - bytes32 roleId = _authorizer.generateRoleId(module, role); - vm.startPrank(module); - - vm.expectEmit(); - emit ChangedTokenGating(roleId, true); - emit ChangedTokenThreshold(roleId, address(token), threshold); - - _authorizer.makeRoleTokenGatedFromModule(role); - _authorizer.grantTokenRoleFromModule(role, address(token), threshold); - vm.stopPrank(); - return roleId; - } - - // function set up nftGated role - function setUpNFTGatedRole(address module, bytes32 role, address nft) - internal - returns (bytes32) - { - bytes32 roleId = _authorizer.generateRoleId(module, role); - vm.startPrank(module); - - _authorizer.makeRoleTokenGatedFromModule(role); - _authorizer.grantTokenRoleFromModule(role, address(nft), 1); - vm.stopPrank(); - return roleId; - } - - function makeAddressDefaultAdmin(address who) public { - bytes32 adminRole = _authorizer.DEFAULT_ADMIN_ROLE(); - vm.prank(ALBA); - _authorizer.grantRole(adminRole, who); - assertTrue(_authorizer.hasRole(adminRole, who)); - } - - // ------------------------------------- - // State change and validation tests - - // test make role token gated - - function testMakeRoleTokenGated() public { - bytes32 roleId_1 = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - assertTrue(_authorizer.isTokenGated(roleId_1)); - - bytes32 roleId_2 = - setUpNFTGatedRole(address(mockModule), ROLE_NFT, address(roleNft)); - assertTrue(_authorizer.isTokenGated(roleId_2)); - } - - // test admin setTokenGating - function testSetTokenGatingByAdmin() public { - // we set CLOE as admin - makeAddressDefaultAdmin(CLOE); - - // we set and unset on an empty role - - bytes32 roleId = _authorizer.generateRoleId(address(mockModule), "0x00"); - - // now we make it tokengated as admin - vm.prank(CLOE); - - vm.expectEmit(); - emit ChangedTokenGating(roleId, true); - - _authorizer.setTokenGated(roleId, true); - - assertTrue(_authorizer.isTokenGated(roleId)); - - // and revert the change - vm.prank(CLOE); - - vm.expectEmit(); - emit ChangedTokenGating(roleId, false); - - _authorizer.setTokenGated(roleId, false); - - assertFalse(_authorizer.isTokenGated(roleId)); - } - - // test makeTokenGated fails if not empty - function testMakingFunctionTokenGatedFailsIfAlreadyInUse() public { - bytes32 roleId = - _authorizer.generateRoleId(address(mockModule), ROLE_TOKEN); - - // we switch on self-management and whitelist an address - vm.startPrank(address(mockModule)); - _authorizer.grantRoleFromModule(ROLE_TOKEN, CLOE); - - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotEmpty - .selector - ) - ); - _authorizer.makeRoleTokenGatedFromModule(ROLE_TOKEN); - assertFalse(_authorizer.isTokenGated(roleId)); - - // we revoke the whitelist - _authorizer.revokeRoleFromModule(ROLE_TOKEN, CLOE); - - // now it works: - _authorizer.makeRoleTokenGatedFromModule(ROLE_TOKEN); - assertTrue(_authorizer.isTokenGated(roleId)); - } - // smae but with admin - - function testSetTokenGatedFailsIfRoleAlreadyInUse() public { - // we set BOB as admin - makeAddressDefaultAdmin(BOB); - - bytes32 roleId = - _authorizer.generateRoleId(address(mockModule), ROLE_TOKEN); - - // we switch on self-management and whitelist an address - vm.prank(address(mockModule)); - _authorizer.grantRoleFromModule(ROLE_TOKEN, CLOE); - - vm.startPrank(BOB); - - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotEmpty - .selector - ) - ); - _authorizer.setTokenGated(roleId, true); - - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotEmpty - .selector - ) - ); - _authorizer.setTokenGated(roleId, false); - - // we revoke the whitelist - _authorizer.revokeRole(roleId, CLOE); - - // now it works: - _authorizer.setTokenGated(roleId, true); - assertTrue(_authorizer.isTokenGated(roleId)); - - _authorizer.setTokenGated(roleId, false); - assertFalse(_authorizer.isTokenGated(roleId)); - } - - // test interface enforcement when granting role - // -> yes case - function testCanAddTokenWhenTokenGated() public { - setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - setUpNFTGatedRole(address(mockModule), ROLE_NFT, address(roleNft)); - } - // -> no case - - /// forge-config: default.allow_internal_expect_revert = true - function testCannotAddNonTokenWhenTokenGated() public { - setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - - vm.prank(address(mockModule)); - // First, the call to the interface reverts without reason - vm.expectRevert(); - // Then the contract handles the reversion and sends the correct error message - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__InvalidToken - .selector, - CLOE - ) - ); - _authorizer.grantRoleFromModule(ROLE_TOKEN, CLOE); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testAdminCannotAddNonTokenWhenTokenGated() public { - // we set BOB as admin - makeAddressDefaultAdmin(BOB); - - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - - vm.prank(BOB); - // First, the call to the interface reverts without reason - vm.expectRevert(); - // Then the contract handles the reversion and sends the correct error message - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__InvalidToken - .selector, - CLOE - ) - ); - _authorizer.grantRole(roleId, CLOE); - } - - // Check setting the threshold - // yes case - function testSetThreshold() public { - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - assertEq(_authorizer.getThresholdValue(roleId, address(roleToken)), 500); - } - - // invalid threshold from module - - function testSetThresholdFailsIfInvalid() public { - bytes32 role = ROLE_TOKEN; - vm.startPrank(address(mockModule)); - _authorizer.makeRoleTokenGatedFromModule(role); - - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__InvalidThreshold - .selector, - 0 - ) - ); - _authorizer.grantTokenRoleFromModule(role, address(roleToken), 0); - - vm.stopPrank(); - } - // invalid threshold from admin - - function testSetThresholdFromAdminFailsIfInvalid() public { - // we set BOB as admin - makeAddressDefaultAdmin(BOB); - // First we set up a valid role - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - - // and we try to break it - vm.prank(BOB); - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__InvalidThreshold - .selector, - 0 - ) - ); - _authorizer.setThreshold(roleId, address(roleToken), 0); - } - - function testSetThresholdFailsIfNotTokenGated() public { - // we set BOB as admin - makeAddressDefaultAdmin(BOB); - - vm.prank(address(mockModule)); - // We didn't make the role token-gated beforehand - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotTokenGated - .selector - ) - ); - _authorizer.grantTokenRoleFromModule( - ROLE_TOKEN, address(roleToken), 500 - ); - - // also fails for the admin - bytes32 roleId = - _authorizer.generateRoleId(address(mockModule), ROLE_TOKEN); - - vm.prank(BOB); - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotTokenGated - .selector - ) - ); - _authorizer.setThreshold(roleId, address(roleToken), 500); - } - - // Test setThresholdFromModule - - function testSetThresholdFromModule() public { - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), 500 - ); - vm.prank(address(mockModule)); - _authorizer.setThresholdFromModule(ROLE_TOKEN, address(roleToken), 1000); - assertEq( - _authorizer.getThresholdValue(roleId, address(roleToken)), 1000 - ); - } - - // invalid threshold from module - - function testSetThresholdFromModuleFailsIfInvalid() public { - bytes32 role = ROLE_TOKEN; - vm.startPrank(address(mockModule)); - _authorizer.makeRoleTokenGatedFromModule(role); - - _authorizer.grantTokenRoleFromModule(role, address(roleToken), 100); - - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__InvalidThreshold - .selector, - 0 - ) - ); - _authorizer.setThresholdFromModule(ROLE_TOKEN, address(roleToken), 0); - - vm.stopPrank(); - } - - function testSetThresholdFromModuleFailsIfNotTokenGated() public { - // we set BOB as admin - makeAddressDefaultAdmin(BOB); - - vm.prank(address(mockModule)); - // We didn't make the role token-gated beforehand - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__RoleNotTokenGated - .selector - ) - ); - _authorizer.setThresholdFromModule(ROLE_TOKEN, address(roleToken), 500); - } - - // Threshold state checks: - // Cannot grant role if threshold is set to zero - - function testGrantTokenRoleFailsIfThresholdWouldBeZero() public { - bytes32 role = ROLE_TOKEN; - - // Make the role token-gated, but don't set a token with grantRoleFromModule() - vm.prank(address(mockModule)); - _authorizer.makeRoleTokenGatedFromModule(role); - - bytes32 storedRoleId = - _authorizer.generateRoleId(address(mockModule), role); - - // Now we make BOB admin of the role - makeAddressDefaultAdmin(BOB); - - vm.startPrank(BOB); - vm.expectRevert( - abi.encodeWithSelector( - IAUT_TokenGated_Roles_v1 - .Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold - .selector, - storedRoleId, - address(roleToken) - ) - ); - _authorizer.grantRole(storedRoleId, address(roleToken)); // BOB tries to circumvent setting a threshold - - vm.stopPrank(); - } - - // Threshold is zero after revoking role - - function testThresholdStateGetsDeletedOnRevoke() public { - bytes32 role = ROLE_TOKEN; - bytes32 moduleRoleId = - _authorizer.generateRoleId(address(mockModule), role); - - assertEq( - _authorizer.getThresholdValue(moduleRoleId, address(roleToken)), 0 - ); - - vm.startPrank(address(mockModule)); - - // Make the role token-gated with a threshold of 500 - _authorizer.makeRoleTokenGatedFromModule(role); - _authorizer.grantTokenRoleFromModule(role, address(roleToken), 500); - - assertEq( - _authorizer.getThresholdValue(moduleRoleId, address(roleToken)), 500 - ); - assertEq(true, _authorizer.hasRole(moduleRoleId, address(roleToken))); - - assertEq( - false, _authorizer.hasTokenRole(moduleRoleId, address(roleToken)) - ); - assertEq( - false, _authorizer.checkForRole(moduleRoleId, address(roleToken)) - ); - - _authorizer.revokeRoleFromModule(role, address(roleToken)); - - assertEq( - _authorizer.getThresholdValue(moduleRoleId, address(roleToken)), 0 - ); - - assertEq(false, _authorizer.hasRole(moduleRoleId, address(roleToken))); - - // Grant the same role again, with different Threshold - _authorizer.grantTokenRoleFromModule(role, address(roleToken), 250); - - assertEq( - _authorizer.getThresholdValue(moduleRoleId, address(roleToken)), 250 - ); - - vm.stopPrank(); - } - - // Test Authorization - - // Test token authorization - // -> yes case - function testFuzzTokenAuthorization( - uint threshold, - address[] calldata callers, - uint[] calldata amounts - ) public { - vm.assume(callers.length <= amounts.length); - vm.assume(threshold != 0); - - // This implcitly confirms ERC20 compatibility - - // We burn the tokens created on setup - roleToken.burn(BOB, 1000); - roleToken.burn(CLOE, 10); - - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), threshold - ); - - for (uint i = 0; i < callers.length; i++) { - if (callers[i] == address(0)) { - // cannot mint to 0 address - continue; - } - - roleToken.mint(callers[i], amounts[i]); - - // we ensure both ways to check give the same result - vm.prank(address(mockModule)); - bool result = _authorizer.checkForRole(roleId, callers[i]); - assertEq(result, _authorizer.hasTokenRole(roleId, callers[i])); - - // we verify the result ir correct - if (amounts[i] >= threshold) { - assertTrue(result); - } else { - assertFalse(result); - } - - // we burn the minted tokens to avoid overflows - roleToken.burn(callers[i], amounts[i]); - } - } - - // Test NFT authorization - // -> yes case - // -> no case - function testFuzzNFTAuthorization( - address[] calldata callers, - bool[] calldata hasNFT - ) public { - vm.assume(callers.length < 50); - vm.assume(callers.length <= hasNFT.length); - - // This is similar to the function above, but in this case we just do a yes/no check - // This implcitly confirms ERC721 compatibility - - // We burn the token created on setup - roleNft.burn(roleNft.idCounter() - 1); - - bytes32 roleId = - setUpNFTGatedRole(address(mockModule), ROLE_NFT, address(roleNft)); - - for (uint i = 0; i < callers.length; i++) { - if (callers[i] == address(0)) { - // cannot mint to 0 address - continue; - } - if (hasNFT[i]) { - roleNft.mint(callers[i]); - } - - // we ensure both ways to check give the same result - vm.prank(address(mockModule)); - bool result = _authorizer.checkForRole(roleId, callers[i]); - assertEq(result, _authorizer.hasTokenRole(roleId, callers[i])); - - // we verify the result ir correct - if (hasNFT[i]) { - assertTrue(result); - } else { - assertFalse(result); - } - - // If we minted a token we burn it to guarantee a clean slate in case of address repetition - if (hasNFT[i]) { - roleNft.burn(roleNft.idCounter() - 1); - } - } - } - - function testFuzzTokenAuthorizationAndRevoke( - uint threshold, - address[] calldata callers, - uint[] calldata amounts - ) public { - vm.assume(callers.length <= amounts.length); - vm.assume(threshold != 0); - - // This implcitly confirms ERC20 compatibility - - // We burn the tokens created on setup - roleToken.burn(BOB, 1000); - roleToken.burn(CLOE, 10); - - bytes32 roleId = setUpTokenGatedRole( - address(mockModule), ROLE_TOKEN, address(roleToken), threshold - ); - - assertEq(true, _authorizer.hasRole(roleId, address(roleToken))); // The token has been added to the core authorizer mapping - - assertEq(false, _authorizer.checkForRole(roleId, address(roleToken))); // The token itself does not have the role - assertEq( - _authorizer.checkForRole(roleId, address(roleToken)), - _authorizer.hasTokenRole(roleId, address(roleToken)) - ); // We ensure both ways to check give the same result - - for (uint i = 0; i < callers.length; i++) { - if (callers[i] == address(0)) { - // cannot mint to 0 address - continue; - } - - roleToken.mint(callers[i], amounts[i]); - - // we ensure both ways to check give the same result - vm.prank(address(mockModule)); - bool result = _authorizer.checkForRole(roleId, callers[i]); - assertEq(result, _authorizer.hasTokenRole(roleId, callers[i])); - - // we verify the result is correct - if (amounts[i] >= threshold) { - assertTrue(result); - } else { - assertFalse(result); - } - - // we burn the minted tokens to avoid overflows - roleToken.burn(callers[i], amounts[i]); - } - - // Now we revoke the token from the role - vm.startPrank(address(mockModule)); - vm.expectEmit(); - emit ChangedTokenThreshold(roleId, address(roleToken), 0); - emit RoleRevoked(roleId, address(roleToken), address(mockModule)); - - _authorizer.revokeRoleFromModule(ROLE_TOKEN, address(roleToken)); - vm.stopPrank(); - - assertEq(false, _authorizer.hasRole(roleId, address(roleToken))); // The token has been revoked from the core authorizer mapping - - assertEq(false, _authorizer.checkForRole(roleId, address(roleToken))); // The token itsef still does not have the role - assertEq( - _authorizer.checkForRole(roleId, address(roleToken)), - _authorizer.hasTokenRole(roleId, address(roleToken)) - ); // We ensure both ways to check give the same result - - for (uint i = 0; i < callers.length; i++) { - if (callers[i] == address(0)) { - // cannot mint to 0 address - continue; - } - - roleToken.mint(callers[i], amounts[i]); - - // we ensure both ways to check give the same result - vm.prank(address(mockModule)); - bool result = _authorizer.checkForRole(ROLE_TOKEN, callers[i]); - assertEq(result, _authorizer.hasTokenRole(roleId, callers[i])); - - // we verify the user is not authorized - assertFalse(result); - - // we burn the minted tokens to avoid overflows - roleToken.burn(callers[i], amounts[i]); - } - } -} diff --git a/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v2.t.sol b/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v2.t.sol new file mode 100644 index 000000000..b082dcc1e --- /dev/null +++ b/test/unit/modules/authorizer/role/AUT_TokenGated_Roles_v2.t.sol @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// External Libraries +import {Clones} from "@oz/proxy/Clones.sol"; + +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +import {IERC165} from "@oz/utils/introspection/IERC165.sol"; + +// Internal Dependencies +import { + ModuleTest, + IModule_v2, + IOrchestrator_v2 +} from "@unitTest/modules/ModuleTest.sol"; + +// Internal Libraries +import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; + +// Internal Interfaces +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; + +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; + +import {IAuthorizer_v2} from "@aut/IAuthorizer_v2.sol"; +import {IAUT_TokenGated_Roles_v2} from + "@aut/role/interfaces/IAUT_TokenGated_Roles_v2.sol"; + +import {TokenInterface} from "@aut/role/AUT_TokenGated_Roles_v2.sol"; + +// SuT +import {AUT_TokenGated_Roles_v2_Exposed} from + "@mocks/modules/authorizer/AUT_TokenGated_Roles_v2_Exposed.sol"; + +// Mocks +import {FundingManagerV1Mock} from + "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; +import {TokenInterfaceMock} from + "@mocks/modules/authorizer/TokenInterfaceMock.sol"; + +// Errors +import {OZErrors} from "@testUtilities/OZErrors.sol"; + +// External Dependencies +import {IAccessControl} from "@oz/access/IAccessControl.sol"; + +contract AUT_TokenGated_Roles_v2_Test is ModuleTest { + /////////////////////////////////////////////////////////////////////////// + // State + + // SuT + AUT_TokenGated_Roles_v2_Exposed _authSuT; + + // Constants + address _bob = makeAddr("Bob"); + + // Addresses + + // Bob and Alice can Access + bytes4 _selector1 = bytes4(keccak256("selector1()")); + // Alice can access + bytes4 _selector2 = bytes4(keccak256("selector2()")); + // No Permissions + bytes4 _selector3 = bytes4(keccak256("selector3()")); + // Public Role can access + bytes4 _selector4 = bytes4(keccak256("selector4()")); + + /////////////////////////////////////////////////////////////////////////// + // Setup + + function setUp() public { + address impl = address(new AUT_TokenGated_Roles_v2_Exposed()); + _authSuT = AUT_TokenGated_Roles_v2_Exposed(Clones.clone(impl)); + + // initiate orchestrator without extra Module + _setUpOrchestrator(); + + // Init SuT + // Initial Admin is this contract + _authSuT.init(_orchestrator, _METADATA, abi.encode(address(this))); + + // Change Authorizer of Module Test to SuT + _orchestrator.initiateSetAuthorizerWithTimelock(address(_authSuT)); + vm.warp(72 hours + 1); + _orchestrator.executeSetAuthorizer(address(_authSuT)); + } + + /////////////////////////////////////////////////////////////////////////// + // Test Initialization + + /* + Test: SupportsInterface + └── Given: The interfaceId is IAuthorizer_v2 + └── When: the function supportsInterface is called + └── Then: the function should return true + */ + function testSupportsInterface() public override(ModuleTest) { + assertTrue( + _authSuT.supportsInterface( + type(IAUT_TokenGated_Roles_v2).interfaceId + ) + ); + } + + /* + Test: Init + └── When: the function init is called + └── Then: the function should set the initial admin + */ + function testInit() public override { + // Check that the initial Admin is set + assertTrue(_authSuT.hasRole(_authSuT.getAdminRole(), address(this))); + } + + /* + Test: ReinitFails + └── When: the function init is called after the contract has been initialized + └── Then: the function should revert + */ + function testReinitFails() public override { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + _authSuT.init(_orchestrator, _METADATA, abi.encode(address(this))); + } + ///////////////////////////////////////////////////////////////////////////// + // Test Modifier + + /* + Test: onlyEmptyRole Modifier + └── Given: Role is not empty + └── When: function with onlyEmptyRole modifier is called + └── Then: the function should revert + */ + function testOnlyEmptyRoleModifier(uint seed_) public { + // Create address array + address[] memory members = new address[](seed_ % 20); + for (uint i; i < members.length; ++i) { + members[i] = address(uint160(i)); + } + + // Create Role + bytes32 roleId = + _authSuT.createRole("Role", _authSuT.DEFAULT_ADMIN_ROLE(), members); + + if (members.length != 0) { + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleNotEmpty + .selector + ) + ); + } + _authSuT.onlyEmptyRoleModifier_exposed(roleId); + } + + /* + Test notPublicRole Modifier + └── Given: Role is public + └── When: function with notPublicRole modifier is called + └── Then: the function should revert + */ + function testNotPublicRoleModifier() public { + bytes32 roleId = _authSuT.PUBLIC_ROLE(); + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleIsPublic + .selector + ) + ); + _authSuT.notPublicRoleModifier_exposed(roleId); + } + + /* + Test: onlyTokenGated Modifier + └── Given: Role is not token-gated + └── When: function with onlyTokenGated modifier is called + └── Then: the function should revert + */ + function testOnlyTokenGatedModifier(bool isTokenGated_) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Set token gated + if (isTokenGated_) { + _authSuT.setTokenGated(roleId, true); + } else { + // If not token gated, then the function should revert + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleNotTokenGated + .selector + ) + ); + } + _authSuT.onlyTokenGatedModifier_exposed(roleId); + } + + /* + Test: validThreshold Modifier + └── Given: Threshold is invalid + └── When: function with validThreshold modifier is called + └── Then: the function should revert + */ + function testValidThresholdModifier(uint threshold_) public { + if (threshold_ == 0) { + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__InvalidThreshold + .selector, + threshold_ + ) + ); + } + _authSuT.validThresholdModifier_exposed(threshold_); + } + + /////////////////////////////////////////////////////////////////////////// + // Test External Functions + + // ======================================================================== + // Public Getter Functions + + /* + Test: isTokenGated + └── When: isTokenGated is called + └── Then: Return if the role is token gated + */ + function testIsTokenGated(bool isTokenGated_, bytes32 roleId_) public { + // Public role cannot be token gated + vm.assume(roleId_ != _authSuT.PUBLIC_ROLE()); + + // Set token gated + if (isTokenGated_) { + _authSuT.setTokenGated_unrestricted(roleId_, true); + } + assertEq(_authSuT.isTokenGated(roleId_), isTokenGated_); + } + + /* + Test: hasTokenRole + ├── Given: Role is not token gated + │ └── When: hasTokenRole is called + │ └── Then: It should revert (modifier in position check) + └── Given: Role is token gated + └── When: hasTokenRole is called + └── Then: It should call the internal function + */ + function testHasTokenRole_ModifierInPositionChecks() public { + // onlyTokenGated + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleNotTokenGated + .selector + ) + ); + _authSuT.hasTokenRole(bytes32(uint(0)), address(0)); + } + + function testHasTokenRole_TokenGated_CallsInternalFunction( + address who_, + bool hasTokenRole_ + ) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Create Token Interface Mock + address token = address(new TokenInterfaceMock()); + + // Set threshold + _authSuT.setThreshold(roleId, token, 1); + + // Grant Role + _authSuT.grantRole(roleId, token); + + if (hasTokenRole_) { + // Give the address some tokens + TokenInterfaceMock(token).setTokenBalance(who_, 1); + } + + // Check that the role is granted + assertEq(_authSuT.hasTokenRole(roleId, who_), hasTokenRole_); + } + + /* + Test: getThresholdValue + └── When: getThresholdValue is called + └── Then: Return the threshold value + */ + function testGetThresholdValue( + uint threshold_, + bytes32 roleId_, + address token_ + ) public { + // Set threshold + _authSuT.setThreshold_unrestricted(roleId_, token_, threshold_); + + assertEq(_authSuT.getThresholdValue(roleId_, token_), threshold_); + } + + // ======================================================================== + // Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - TokenGated Settings + + /* + Test: setTokenGated + ├── Given: Caller is not permissioned + │ └── When: setTokenGated is called + │ └── Then: The call reverts (modifier in position check) + ├── Given: Caller is permissioned + ├── And: Role is not empty + │ └── When: setTokenGated is called + │ └── Then: The call reverts (modifier in position check) + ├── Given: Caller is permissioned + ├── And: Role is empty + ├── And: Role is Public Role + │ └── When: setTokenGated is called + │ └── Then: The call reverts (modifier in position check) + ├── Given: Caller is permissioned + ├── And: Role is empty + └── And: Role is not Public Role + └── When: setTokenGated is called + └── Then: The role becomes token gated + └── And: An event is emitted + */ + function testSetTokenGated_ModifierInPositionChecks() public { + // permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(_bob); + _authSuT.setTokenGated(bytes32(uint(0)), true); + + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + _authSuT.setTokenGated(bytes32(uint(2)), true); + + // onlyEmptyRole(roleId_) + + // Create Role that is not empty + address[] memory members = new address[](1); + members[0] = _bob; + bytes32 roleId = + _authSuT.createRole("Role", _authSuT.DEFAULT_ADMIN_ROLE(), members); + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleNotEmpty + .selector + ) + ); + _authSuT.setTokenGated(roleId, true); + + // notPublicRole(roleId_) + roleId = _authSuT.PUBLIC_ROLE(); + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleIsPublic + .selector + ) + ); + _authSuT.setTokenGated(roleId, true); + } + + function testSetTokenGated_Functionality() public { + // Create Role that is empty + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Expect event + vm.expectEmit(true, true, true, true); + emit IAUT_TokenGated_Roles_v2.ChangedTokenGating(roleId, true); + + // Set token gated + _authSuT.setTokenGated(roleId, true); + assertTrue(_authSuT.isTokenGated(roleId)); + } + + /* + Test: setThreshold + ├── Given: Caller is not permissioned + │ └── When: setThreshold is called + │ └── Then: The call reverts (modifier in position check) + └── Given: Caller is permissioned + └── When: setThreshold is called + └── Then: The underlying function is called (Check via event) + */ + function testSetThreshold_ModifierInPositionChecks() public { + // permissioned + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(_bob); + _authSuT.setThreshold(bytes32(uint(0)), address(0), 0); + + //idExists(roleId_) + vm.expectRevert( + abi.encodeWithSelector( + IAuthorizer_v2.Module__Authorizer__RoleIdNotExisting.selector + ) + ); + _authSuT.setThreshold(bytes32(uint(2)), address(0), 0); + } + + function testSetThreshold_Functionality() public { + // Create Role that is empty + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make it token gated + _authSuT.setTokenGated(roleId, true); + + // Expect event + vm.expectEmit(true, true, true, true); + emit IAUT_TokenGated_Roles_v2.ChangedTokenThreshold( + roleId, address(0), 1 + ); + + // Set threshold + _authSuT.setThreshold(roleId, address(0), 1); + } + + /////////////////////////////////////////////////////////////////////////// + // Test Override Functions + + /* + Test: hasRole + ├── Given: Role is not token gated + ├── And: The given address does not have the role + │ └── When: hasRole is called + │ └── Then: hasRole works like base contract + ├── Given: Role is token gated + ├── And: The given address has the role + │ └── When: hasRole is called + │ └── Then: hasRole works like base contract + └── Given: Role is token gated + └── When: hasRole is called + └── Then: It should use the internal _hasTokenRole function + */ + function testHasRole_NotTokenGated_AddressDoesNotHaveRole(address who_) + public + { + // Check that address is not initial admin + vm.assume(who_ != address(this)); + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + assertFalse(_authSuT.hasRole(roleId, who_)); + } + + function testHasRole_NotTokenGated_AddressHasRole(address who_) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Add address to role + _authSuT.grantRole(roleId, who_); + assertTrue(_authSuT.hasRole(roleId, who_)); + } + + function testHasRole_TokenGated_CallsInternalFunction( + address who_, + bool hasTokenRole_ + ) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Create Token Interface Mock + address token = address(new TokenInterfaceMock()); + + // Set threshold + _authSuT.setThreshold(roleId, token, 1); + + // Grant Role + _authSuT.grantRole(roleId, token); + + if (hasTokenRole_) { + // Give the address some tokens + TokenInterfaceMock(token).setTokenBalance(who_, 1); + } + + // Check that the role is granted + assertEq(_authSuT.hasRole(roleId, who_), hasTokenRole_); + } + /* + Test: grantRole + ├── Given: Role is not token gated + │ └── When: grantRole is called + │ └── Then: Grant Role works like base contract + ├── Given: Role is token gated + ├── And: The given address has code size 0 + │ └── When: grantRole is called + │ └── Then: The function should revert + ├── Given: Role is token gated + ├── And: The given address has code size > 0 + ├── And: The Threshold is 0 for the given address + │ └── When: grantRole is called + │ └── Then: The function should revert + ├── Given: Role is token gated + ├── And: The given address has code size > 0 + ├── And: The Threshold is > 0 for the given address + ├── And: The given address does not implement the TokenInterface + │ └── When: grantRole is called + │ └── Then: The function should revert + ├── Given: Role is token gated + ├── And: the given address has code size > 0 + ├── And: The Threshold is > 0 for the given address + └── And: The given address implements the TokenInterface + └── When: grantRole is called + └── Then: Grant Role works like base contract + */ + + function test_grantRole_NotTokenGated(address who_) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Grant Role + _authSuT.grantRole(roleId, who_); + + // Check that the role is granted + assertTrue( + _authSuT.exposed_AccessControlUpgradeable_hasRole(roleId, who_) + ); + } + + function test_grantRole_CodeSizeZero(address who_) public { + uint32 size; + assembly { + size := extcodesize(who_) + } + vm.assume(size == 0); + + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Grant Role + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__InvalidToken + .selector, + address(who_) + ) + ); + _authSuT.grantRole(roleId, who_); + } + + function test_grantRole_TokenInterfaceThresholdZero() public { + // Create Mock Token Interface + TokenInterfaceMock tokenInterfaceMock = new TokenInterfaceMock(); + + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Grant Role + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__TokenRoleMustHaveThreshold + .selector, + roleId, + address(tokenInterfaceMock) + ) + ); + + _authSuT.grantRole(roleId, address(tokenInterfaceMock)); + } + + function test_grantRole_TokenInterfaceNotImplemented() public { + // We pick a contract that definetly does not implement the interface + address who_ = address(_orchestrator); + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Set threshold + _authSuT.setThreshold(roleId, who_, 1); + + // Grant Role + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__InvalidToken + .selector, + who_ + ) + ); + _authSuT.grantRole(roleId, who_); + } + + function test_grantRole_TokenInterfaceImplemented() public { + // Create Mock Token Interface + TokenInterfaceMock tokenInterfaceMock = new TokenInterfaceMock(); + + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Set threshold + _authSuT.setThreshold(roleId, address(tokenInterfaceMock), 1); + + // Grant Role + _authSuT.grantRole(roleId, address(tokenInterfaceMock)); + + // Check that the role is granted + assertTrue( + _authSuT.exposed_AccessControlUpgradeable_hasRole( + roleId, address(tokenInterfaceMock) + ) + ); + } + + /* + Test: _revokeRole + ├── Given: Role is not token gated + │ └── When: revokeRole is called + │ └── Then: Revoke Role works like base contract + └── Given: Role is token gated + └── When: revokeRole is called + └── Then: The Threshold is set to 0 + └── And: An event is emitted + └── And: Revoke Role works like base contract + */ + function test_revokeRole_NotTokenGated(address who_) public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Add address to role + _authSuT.grantRole(roleId, who_); + + // Revoke Role + _authSuT.revokeRole(roleId, who_); + + // Check that the role is revoked + assertFalse( + _authSuT.exposed_AccessControlUpgradeable_hasRole(roleId, who_) + ); + } + + function test_revokeRole_TokenGated() public { + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + // Make Role token gated + _authSuT.setTokenGated(roleId, true); + + // Create Token Interface Mock + address who = address(new TokenInterfaceMock()); + + // Set threshold + _authSuT.setThreshold(roleId, who, 1); + + // Grant Role + _authSuT.grantRole(roleId, who); + + // Expect event + vm.expectEmit(true, true, true, true); + emit IAUT_TokenGated_Roles_v2.ChangedTokenThreshold(roleId, who, 0); + + // Revoke Role + _authSuT.revokeRole(roleId, who); + + // Check that threshold is set to 0 + assertEq(_authSuT.getThresholdValue(roleId, who), 0); + + // Check that the role is revoked + assertFalse( + _authSuT.exposed_AccessControlUpgradeable_hasRole(roleId, who) + ); + } + /////////////////////////////////////////////////////////////////////////// + // Test Internal Functions + + // ------------------------------------------------------------------------ + // Internal - Upstream Function Implementations + + /* + Test: _setThreshold + ├── Given: The given roleId is not token gated + │ └── When: _setThreshold is called + │ └── Then: The call reverts (modifier in position check) + ├── Given: The given roleId is token gated + ├── And: the given threshold is invalid + │ └── When: _setThreshold is called + │ └── Then: The call reverts (modifier in position check) + ├── Given: The given roleId is token gated + └── And: the given threshold is valid + └── When: _setThreshold is called + └── Then: the threshold map is updated + └── And: A event is emitted + */ + function test_setThreshold_ModifierInPositionChecks() public { + // onlyTokenGated(roleId_) + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__RoleNotTokenGated + .selector + ) + ); + _authSuT.exposed_setThreshold(roleId, address(0), 0); + + // validThreshold(threshold_) + + // Make role token gated + _authSuT.setTokenGated(roleId, true); + + vm.expectRevert( + abi.encodeWithSelector( + IAUT_TokenGated_Roles_v2 + .Module__AUT_TokenGated_Roles__InvalidThreshold + .selector, + 0 + ) + ); + _authSuT.exposed_setThreshold(roleId, address(0), 0); + } + + function test_setThreshold_Functionality(address token_, uint threshold_) + public + { + // Make sure threshold_ is not zero + vm.assume(threshold_ != 0); + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Make it TokenGated + _authSuT.setTokenGated(roleId, true); + + vm.expectEmit(true, true, true, true); + emit IAUT_TokenGated_Roles_v2.ChangedTokenThreshold( + roleId, token_, threshold_ + ); + // Set Threshold + _authSuT.setThreshold(roleId, token_, threshold_); + + assertEq(_authSuT.getThresholdValue(roleId, token_), threshold_); + } + + /* + Test: _hasTokenRole + Invariant: Role can only contain TokenInterface addresses + ├── Given: Role contains TokenInterface mock addresses + ├── And: The token amount is less than the threshold + │ └── When: _hasTokenRole is called + │ └── Then: It should return false + ├── Given: Role contains TokenInterface mock addresses + └── And: The token amount of at least one of them is equal or higher than the threshold + └── When: _hasTokenRole is called + └── Then: It should return true + */ + function test_hasTokenRole_TokenInterfacesWrongThreshold( + uint[] memory thresholdAmounts_, + address who_ + ) public { + // Assume realistic number of token interface Mocks + vm.assume(thresholdAmounts_.length < 50); + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Set token gated + _authSuT.setTokenGated(roleId, true); + + // Create token interface mocks + address[] memory tokenInterfaceMocks = + new address[](thresholdAmounts_.length); + + for (uint i; i < thresholdAmounts_.length; ++i) { + // Should the threshold be zero then set it to 1 + if (thresholdAmounts_[i] == 0) { + thresholdAmounts_[i] = 1; + } + tokenInterfaceMocks[i] = + _createTokenInterfaceMock_SetThresholdAmount_AddToMembers( + roleId, thresholdAmounts_[i] + ); + } + + // Should return false as target has no tokens in any of the mocks + assertFalse(_authSuT.exposed_hasTokenRole(roleId, who_)); + } + + function test_hasTokenRole_WhoHasTokens( + uint seed_, + uint[] memory thresholdAmounts_, + uint[] memory tokenAmounts_, + address who_ + ) public { + // Assume realistic number of token interface Mocks + vm.assume(thresholdAmounts_.length > 0); + + // Cap the array to 50 elements + if (thresholdAmounts_.length > 50) { + uint[] memory cappedArr = new uint[](50); // Create new memory array + + for (uint i = 0; i < 50; i++) { + cappedArr[i] = thresholdAmounts_[i]; // Copy elements into new array + } + + thresholdAmounts_ = cappedArr; + } + + vm.assume(tokenAmounts_.length <= thresholdAmounts_.length); + // Create Role + bytes32 roleId = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Set token gated + _authSuT.setTokenGated(roleId, true); + + // Create token interface mocks + address[] memory tokenInterfaceMocks = + new address[](thresholdAmounts_.length); + + for (uint i; i < thresholdAmounts_.length; ++i) { + // Should the threshold be zero then set it to 1 + if (thresholdAmounts_[i] == 0) { + thresholdAmounts_[i] = 1; + } + tokenInterfaceMocks[i] = + _createTokenInterfaceMock_SetThresholdAmount_AddToMembers( + roleId, thresholdAmounts_[i] + ); + } + + // Give the target some tokens + for (uint i; i < tokenAmounts_.length; ++i) { + TokenInterfaceMock(tokenInterfaceMocks[i]).setTokenBalance( + who_, tokenAmounts_[i] + ); + } + + // Make sure that the target has at least more or equal tokens in one of the mocks + // Pick a random token interface mock + address randomTokenInterfaceAddress = + tokenInterfaceMocks[seed_ % thresholdAmounts_.length]; + // Pick the respective threshold amount + uint thresholdAmountOfTokenInterfaceMock = + thresholdAmounts_[seed_ % thresholdAmounts_.length]; + + // Set the balance of the target to the threshold amount of the token interface mock + TokenInterfaceMock(randomTokenInterfaceAddress).setTokenBalance( + who_, thresholdAmountOfTokenInterfaceMock + ); + + // Should return true as target has thethreshold in at least one contract + assertTrue(_authSuT.exposed_hasTokenRole(roleId, who_)); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper Functions + + /// @notice Creates a role with token gated set to true + /// @return roleId_ The id of the created role + function _createTokenGatedRole() internal returns (bytes32 roleId_) { + // Create Role + roleId_ = _authSuT.createRole( + "Role", _authSuT.DEFAULT_ADMIN_ROLE(), new address[](0) + ); + // Set token gated + _authSuT.setTokenGated(roleId_, true); + } + + /// @notice Creates a token interface mock, sets the threshold and adds it to the role + /// @param roleId_ The id of the role to add the token interface mock to + /// @param thresholdAmount_ The threshold amount to set + /// @return tokenInterfaceMock_ The address of the token interface mock + function _createTokenInterfaceMock_SetThresholdAmount_AddToMembers( + bytes32 roleId_, + uint thresholdAmount_ + ) internal returns (address tokenInterfaceMock_) { + // Create token interface mock + tokenInterfaceMock_ = address(new TokenInterfaceMock()); + + // Set threshold + _authSuT.setThreshold(roleId_, tokenInterfaceMock_, thresholdAmount_); + + // Add token interface mock to role + _authSuT.grantRole(roleId_, tokenInterfaceMock_); + } +} diff --git a/test/unit/modules/base/Module_v1.t.sol b/test/unit/modules/base/Module_v2.t.sol similarity index 54% rename from test/unit/modules/base/Module_v1.t.sol rename to test/unit/modules/base/Module_v2.t.sol index 99cead1bc..409f939ad 100644 --- a/test/unit/modules/base/Module_v1.t.sol +++ b/test/unit/modules/base/Module_v2.t.sol @@ -13,69 +13,72 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Internal Libraries import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; // Internal Interfaces -import {IModule_v1, IOrchestrator_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2, IOrchestrator_v2} from "src/modules/base/IModule_v2.sol"; -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; +import {Orchestrator_v2} from "src/orchestrator/Orchestrator_v2.sol"; // Mocks -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; import {FundingManagerV1Mock} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; contract ModuleBaseV1Test is ModuleTest { + /////////////////////////////////////////////////////////////////////////// + // State + // SuT - ModuleV1Mock module; + Module_v2_Mock module; bytes _CONFIGDATA = bytes(""); - //-------------------------------------------------------------------------- - // Events - - /// @notice Module has been initialized. - /// @param parentOrchestrator The address of the orchestrator the module is linked to. - /// @param metadata The metadata of the module. - event ModuleInitialized( - address indexed parentOrchestrator, IModule_v1.Metadata metadata - ); + /////////////////////////////////////////////////////////////////////////// + // Setup function setUp() public { - address impl = address(new ModuleV1Mock()); - module = ModuleV1Mock(Clones.clone(impl)); + address impl = address(new Module_v2_Mock()); + module = Module_v2_Mock(Clones.clone(impl)); _setUpOrchestrator(module); vm.expectEmit(true, true, true, false); - emit ModuleInitialized(address(_orchestrator), _METADATA); + emit IModule_v2.ModuleInitialized(address(_orchestrator), _METADATA); module.init(_orchestrator, _METADATA, _CONFIGDATA); } - //-------------------------------------------------------------------------- - // Tests: Initialization + /////////////////////////////////////////////////////////////////////////// + // Test Initialization - function testSupportsInterface() public { - assertTrue(module.supportsInterface(type(IModule_v1).interfaceId)); + /* + Test: SupportsInterface + └── Given: The interfaceId is IModule_v2 + └── When: the function supportsInterface is called + └── Then: the function should return true + */ + function testSupportsInterface() public override(ModuleTest) { + assertTrue(module.supportsInterface(type(IModule_v2).interfaceId)); } function testInit() public override { - // Orchestrator_v1 correctly written to storage. + // Orchestrator_v2 correctly written to storage. assertEq(address(module.orchestrator()), address(_orchestrator)); // Identifier correctly computed. @@ -98,8 +101,8 @@ contract ModuleBaseV1Test is ModuleTest { } function testInitFailsForNonInitializerFunction() public { - address impl = address(new ModuleV1Mock()); - module = ModuleV1Mock(Clones.clone(impl)); + address impl = address(new Module_v2_Mock()); + module = Module_v2_Mock(Clones.clone(impl)); vm.expectRevert(OZErrors.Initializable__NotInitializing); module.initNoInitializer(_orchestrator, _METADATA, _CONFIGDATA); @@ -111,179 +114,160 @@ contract ModuleBaseV1Test is ModuleTest { } function testInitFailsForInvalidOrchestrator() public { - address impl = address(new ModuleV1Mock()); - module = ModuleV1Mock(Clones.clone(impl)); + address impl = address(new Module_v2_Mock()); + module = Module_v2_Mock(Clones.clone(impl)); - vm.expectRevert(IModule_v1.Module__InvalidOrchestratorAddress.selector); - module.init(IOrchestrator_v1(address(0)), _METADATA, _CONFIGDATA); + vm.expectRevert(IModule_v2.Module__InvalidOrchestratorAddress.selector); + module.init(IOrchestrator_v2(address(0)), _METADATA, _CONFIGDATA); } function testInitFailsIfMetadataInvalid() public { - address impl = address(new ModuleV1Mock()); - module = ModuleV1Mock(Clones.clone(impl)); + address impl = address(new Module_v2_Mock()); + module = Module_v2_Mock(Clones.clone(impl)); // Invalid if _URL empty. - vm.expectRevert(IModule_v1.Module__InvalidMetadata.selector); + vm.expectRevert(IModule_v2.Module__InvalidMetadata.selector); module.init( _orchestrator, - IModule_v1.Metadata( + IModule_v2.Metadata( _MAJOR_VERSION, _MINOR_VERSION, _PATCH_VERSION, "", _TITLE ), _CONFIGDATA ); // Invalid if _TITLE empty. - vm.expectRevert(IModule_v1.Module__InvalidMetadata.selector); + vm.expectRevert(IModule_v2.Module__InvalidMetadata.selector); module.init( _orchestrator, - IModule_v1.Metadata( + IModule_v2.Metadata( _MAJOR_VERSION, _MINOR_VERSION, _PATCH_VERSION, _URL, "" ), _CONFIGDATA ); } - //-------------------------------------------------------------------------- - // Role Functions + ///////////////////////////////////////////////////////////////////////////// + // Test Modifier - function testGrantModuleRole(bytes32 role, address addr) public { - vm.assume(addr != address(0)); - - vm.startPrank(address(this)); - - module.grantModuleRole(role, addr); - - bytes32 roleId = _authorizer.generateRoleId(address(module), role); - bool isAuthorized = _authorizer.checkRoleMembership(roleId, addr); - assertTrue(isAuthorized); - - vm.stopPrank(); - } - - function testGrantModuleRoleBatched(bytes32 role, address[] memory addrs) + /* + Test: permissioned + ├── Given: modifierPermissionedCheck is executed via call with a valid selector, but random data + ├── And: The call sender is randomised + └── And: The Caller is permissioned to call the function + └── When: The function modifierPermissionedCheck is called + └── Then: the function should not revert, because the sender and only the function selector were correctly passed + */ + function testPermissioned_modifier(address caller_, bytes memory data_) public { - vm.startPrank(address(this)); + // Assume that the calldata is at least 4 bytes long + vm.assume(data_.length >= 4); - for (uint i = 0; i < addrs.length; i++) { - vm.assume(addrs[i] != address(0)); - } + bytes4 targetSelector = + Module_v2_Mock.modifierPermissionedCheck.selector; - module.grantModuleRoleBatched(role, addrs); + // Proof + _authorizer.setHasPermission( + caller_, address(module), targetSelector, true + ); - for (uint i = 0; i < addrs.length; i++) { - bytes32 roleId = _authorizer.generateRoleId(address(module), role); - bool isAuthorized = - _authorizer.checkRoleMembership(roleId, addrs[i]); - assertTrue(isAuthorized); + // Replace the msg.data function selector with the correct one + for (uint i = 0; i < 4; i++) { + data_[i] = targetSelector[i]; } - vm.stopPrank(); + // Expect no revert + vm.prank(caller_); + address(module).call(data_); } - function testRevokeModuleRole(bytes32 role, address addr) public { - vm.assume(addr != address(0)); - - vm.startPrank(address(this)); - - module.grantModuleRole(role, addr); - - bytes32 roleId = _authorizer.generateRoleId(address(module), role); - bool isAuthorizedBefore = _authorizer.checkRoleMembership(roleId, addr); - assertTrue(isAuthorizedBefore); - - module.revokeModuleRole(role, addr); - - bool isAuthorizedAfter = _authorizer.checkRoleMembership(roleId, addr); - assertFalse(isAuthorizedAfter); + /* + Test modifier onlyPaymentClient + ├── given the caller is not a PaymentClient + │ └── when the function modifierOnlyPaymentClientCheck() gets called + │ └── then it should revert + └── given the caller is a PaymentClient module + └── and the PaymentClient module is not registered in the Orchestrator + └── when the function modifierOnlyPaymentClientCheck() gets called + └── then it should revert + */ - vm.stopPrank(); + function testOnlyPaymentClientModifier_worksGivenCallerIsNotPaymentClient( + address _notPaymentClient + ) public { + vm.prank(address(_notPaymentClient)); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); + module.modifierOnlyPaymentClientCheck(); } - function testRevokeModuleRoleBatched(bytes32 role, address[] memory addrs) - public - { - vm.startPrank(address(this)); - - for (uint i = 0; i < addrs.length; i++) { - vm.assume(addrs[i] != address(0)); - } - - module.grantModuleRoleBatched(role, addrs); - - bytes32 roleId = _authorizer.generateRoleId(address(module), role); - - for (uint i = 0; i < addrs.length; i++) { - bool isAuthorizedBefore = - _authorizer.checkRoleMembership(roleId, addrs[i]); - assertTrue(isAuthorizedBefore); - } - - module.revokeModuleRoleBatched(role, addrs); - - for (uint i = 0; i < addrs.length; i++) { - bool isAuthorizedAfter = - _authorizer.checkRoleMembership(roleId, addrs[i]); - assertFalse(isAuthorizedAfter); - } + function testOnlyPaymentClientModifier_worksGivenCallerIsPaymentClientButNotRegisteredModule( + ) public { + ERC20PaymentClientBase_v3_Mock _erc20PaymentClientMock = + new ERC20PaymentClientBase_v3_Mock(); - vm.stopPrank(); + vm.prank(address(_erc20PaymentClientMock)); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); + module.modifierOnlyPaymentClientCheck(); } - //-------------------------------------------------------------------------- - // FeeManager - - function testGetFeeManagerCollateralFeeData(bytes4 functionSelector) - public - { - uint setFee = 100; - address treasury = makeAddr("customTreasury"); - - // Set treasury - feeManager.setWorkflowTreasury(address(_orchestrator), treasury); - - // set fee - feeManager.setCollateralWorkflowFee( - address(_orchestrator), - address(module), - functionSelector, - true, - setFee - ); - - (uint returnFee, address returnTreasury) = - module.original_getFeeManagerCollateralFeeData(functionSelector); + /* + Test: validAddress + └── Given: The address is either the zero address or the module address + └── When: validAddress is called + └── Then: The function should revert + */ - assertEq(returnFee, setFee); - assertEq(returnTreasury, treasury); + function testValidAddress(address adr) public { + if (adr == address(0) || adr == address(module)) { + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); + } + module.modifierOnlyValidAddressCheck(adr); } - function testGetFeeManagerIssuanceFeeData(bytes4 functionSelector) public { - uint setFee = 100; - address treasury = makeAddr("customTreasury"); - - // Set treasury - feeManager.setWorkflowTreasury(address(_orchestrator), treasury); + /////////////////////////////////////////////////////////////////////////// + // Test External Functions - // set fee - feeManager.setIssuanceWorkflowFee( - address(_orchestrator), - address(module), - functionSelector, - true, - setFee - ); + // ======================================================================== + // Public Getter Functions - (uint returnFee, address returnTreasury) = - module.original_getFeeManagerIssuanceFeeData(functionSelector); + // ------------------------------------------------------------------------ + // Getter - Module State - assertEq(returnFee, setFee); - assertEq(returnTreasury, treasury); - } + /* + Test: identifier + └── When: the function identifier is called + └── Then: the function should return the identifier + */ + // Trivial + /* + Test: version + └── When: the function version is called + └── Then: the function should return the version + */ + // Trivial + /* + Test: url + └── When: the function url is called + └── Then: the function should return the url + */ + // Trivial + /* + Test: title + └── When: the function title is called + └── Then: the function should return the title + */ + // Trivial + /* + Test: orchestrator + └── When: the function orchestrator is called + └── Then: the function should return the orchestrator + */ + // Trivial //-------------------------------------------------------------------------- - // ERC2771 + // Getter - ERC2771 Context Upgradeable Overrides + //@todo This test is weird and probably needs to be moved to a different test file function test_msgSender(address signer, address sender, bool fromForwarder) public { @@ -321,6 +305,7 @@ contract ModuleBaseV1Test is ModuleTest { } } + //@todo This test is weird and probably needs to be moved to a different test file function test_msgData(address signer, address sender, bool fromForwarder) public { @@ -358,41 +343,88 @@ contract ModuleBaseV1Test is ModuleTest { } } - //-------------------------------------------------------------------------- - // Modifier + // ======================================================================== + // Internal Functions - /* Test modifier onlyPaymentClient - ├── given the caller is not a PaymentClient - │ └── when the function modifierOnlyPaymentClientCheck() gets called - │ └── then it should revert - └── given the caller is a PaymentClient module - └── and the PaymentClient module is not registered in the Orchestrator - └── when the function modifierOnlyPaymentClientCheck() gets called - └── then it should revert - */ + // ------------------------------------------------------------------------ + // Internal - Fees - function testOnlyPaymentClientModifier_worksGivenCallerIsNotPaymentClient( - address _notPaymentClient - ) public { - vm.prank(address(_notPaymentClient)); - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); - module.modifierOnlyPaymentClientCheck(); + function testGetFeeManagerCollateralFeeData(bytes4 functionSelector) + public + { + uint setFee = 100; + address treasury = makeAddr("customTreasury"); + + // Set treasury + feeManager.setWorkflowTreasury(address(_orchestrator), treasury); + + // set fee + feeManager.setCollateralWorkflowFee( + address(_orchestrator), + address(module), + functionSelector, + true, + setFee + ); + + (uint returnFee, address returnTreasury) = + module._getFeeManagerCollateralFeeData_exposed(functionSelector); + + assertEq(returnFee, setFee); + assertEq(returnTreasury, treasury); } - function testOnlyPaymentClientModifier_worksGivenCallerIsPaymentClientButNotRegisteredModule( - ) public { - ERC20PaymentClientBaseV2Mock _erc20PaymentClientMock = - new ERC20PaymentClientBaseV2Mock(); + function testGetFeeManagerIssuanceFeeData(bytes4 functionSelector) public { + uint setFee = 100; + address treasury = makeAddr("customTreasury"); - vm.prank(address(_erc20PaymentClientMock)); - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); - module.modifierOnlyPaymentClientCheck(); + // Set treasury + feeManager.setWorkflowTreasury(address(_orchestrator), treasury); + + // set fee + feeManager.setIssuanceWorkflowFee( + address(_orchestrator), + address(module), + functionSelector, + true, + setFee + ); + + (uint returnFee, address returnTreasury) = + module._getFeeManagerIssuanceFeeData_exposed(functionSelector); + + assertEq(returnFee, setFee); + assertEq(returnTreasury, treasury); } - function testValidAddress(address adr) public { - if (adr == address(0) || adr == address(module)) { - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + // ------------------------------------------------------------------------ + // Internal - Authorization + + /* + Test: _checkAuthorization_ + └── Given: Authorizer hasPermission() is mocked + ├── When: _checkAuthorization_ is called + └── And: Authorizer hasPermission() returns false + ├── Then: It should forward the function selector properly + └── And: The function should revert + */ + function test_checkAuthorization_hasPermissionMocked( + bool hasPermission_, + address caller_, + bytes calldata data_ + ) public { + vm.assume(data_.length >= 4); + // Assume that caller is not the module as it is the default admin + vm.assume(caller_ != address(this)); + + _authorizer.setHasPermission( + caller_, address(module), bytes4(data_[0:4]), hasPermission_ + ); + + if (!hasPermission_) { + vm.expectRevert(IModule_v2.Module__CallerNotPermissioned.selector); } - module.modifierOnlyValidAddressCheck(adr); + + module._checkAuthorization_exposed(caller_, data_); } } diff --git a/test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.t.sol similarity index 84% rename from test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol rename to test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.t.sol index 6c53fa11c..6cb863177 100644 --- a/test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol +++ b/test/unit/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.t.sol @@ -5,46 +5,45 @@ import "forge-std/console.sol"; // SuT import { - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, - FM_BC_Bancor_Redeeming_VirtualSupply_v1, + IFM_BC_Bancor_Redeeming_VirtualSupply_v2, + FM_BC_Bancor_Redeeming_VirtualSupply_v2, IFundingManager_v1 -} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; +} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; import {IERC165} from "@oz/utils/introspection/IERC165.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; import {IVirtualIssuanceSupplyBase_v1} from "@fm/bondingCurve/interfaces/IVirtualIssuanceSupplyBase_v1.sol"; import {IVirtualCollateralSupplyBase_v1} from "@fm/bondingCurve/interfaces/IVirtualCollateralSupplyBase_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; import { - IRedeemingBondingCurveBase_v1, - IRedeemingBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; + IRedeemingBondingCurveBase_v2, + IRedeemingBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; // Mocks import {FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock} from "@mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; - -import {RedeemingBondingCurveBaseV1Test} from - "@unitTest/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.t.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; /* Since the following functions just wrap the Bancor formula contract, their content is assumed to be tested in the original formula tests, not here: @@ -53,7 +52,7 @@ import {RedeemingBondingCurveBaseV1Test} from - _redeemTokensFormulaWrapper(uint _depositAmount) */ -contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { +contract FM_BC_Bancor_Redeeming_VirtualSupplyV2Test is ModuleTest { string internal constant NAME = "Bonding Curve Token"; string internal constant SYMBOL = "BCT"; uint8 internal constant DECIMALS = 18; @@ -72,44 +71,11 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { address formula; ERC20Issuance_v1 issuanceToken; - ERC20PaymentClientBaseV2Mock _erc20PaymentClientMock; + ERC20PaymentClientBase_v3_Mock _erc20PaymentClientMock; address admin_address = address(0xA1BA); address non_admin_address = address(0xB0B); - event Transfer(address indexed from, address indexed to, uint value); - - event TokensBought( - address indexed receiver, - uint depositAmount, - uint receivedAmount, - address buyer - ); - event VirtualCollateralAmountAdded(uint amountAdded, uint newSupply); - event VirtualCollateralAmountSubtracted( - uint amountSubtracted, uint newSupply - ); - event VirtualIssuanceAmountSubtracted( - uint amountSubtracted, uint newSupply - ); - event VirtualIssuanceAmountAdded(uint amountAdded, uint newSupply); - event TokensSold( - address indexed receiver, - uint depositAmount, - uint receivedAmount, - address seller - ); - event BuyReserveRatioSet( - uint32 newBuyReserveRatio, uint32 oldBuyReserveRatio - ); - event SellReserveRatioSet( - uint32 newSellReserveRatio, uint32 oldSellReserveRatio - ); - event VirtualIssuanceSupplySet(uint newSupply, uint oldSupply); - event VirtualCollateralSupplySet(uint newSupply, uint oldSupply); - event TransferOrchestratorToken(address indexed to, uint amount); - event OrchestratorTokenSet(address indexed token, uint8 decimals); - function setUp() public virtual { // Deploy contracts issuanceToken = new ERC20Issuance_v1(NAME, SYMBOL, DECIMALS, MAX_SUPPLY); @@ -118,7 +84,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { BancorFormula bancorFormula = new BancorFormula(); formula = address(bancorFormula); - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties memory + IFM_BC_Bancor_Redeeming_VirtualSupply_v2.BondingCurveProperties memory bc_properties; bc_properties.formula = formula; @@ -138,10 +104,11 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { _setUpOrchestrator(bondingCurveFundingManager); - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); vm.expectEmit(true, true, true, true); - emit OrchestratorTokenSet(address(_token), DECIMALS); + emit IFundingManager_v1.OrchestratorTokenSet(address(_token), DECIMALS); // Init Module bondingCurveFundingManager.init( @@ -158,10 +125,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { issuanceToken.setMinter(address(bondingCurveFundingManager), true); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bondingCurveFundingManager.supportsInterface( - type(IFM_BC_Bancor_Redeeming_VirtualSupply_v1).interfaceId + type(IFM_BC_Bancor_Redeeming_VirtualSupply_v2).interfaceId ) ); } @@ -249,6 +216,104 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { //-------------------------------------------------------------------------- // Public Functions + /* + Test: buyFor Modifier Checks + ├── Given: buyer is not permissioned + │ └── When: buyFor is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is not enabled + │ └── When: buyFor is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is enabled + └── And: receiver is invalid + └── When: buyFor is called + └── Then: it should revert (modifier in position check) + */ + + function testBuyFor_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeBuy(); + + vm.expectRevert( + IBondingCurveBase_v2 + .Module__BondingCurveBase__BuyingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + + // Open up Buy again + bondingCurveFundingManager.openBuy(); + + // validReceiver + vm.expectRevert( + abi.encodeWithSelector( + IBondingCurveBase_v2 + .Module__BondingCurveBase__InvalidRecipient + .selector + ) + ); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + } + + /* + Test: buy Modifier Checks + ├── Given: buyer is not permissioned + │ └── When: buy is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is not enabled + └── When: buy is called + └── Then: it should revert (modifier in position check) + */ + + function testBuy_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.buy(0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeBuy(); + + vm.expectRevert( + IBondingCurveBase_v2 + .Module__BondingCurveBase__BuyingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.buy(0, 0); + } + /* Test buy and _virtualSupplyBuyOrder function ├── when the deposit amount is 0 │ └── it should revert @@ -276,7 +341,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { */ function testBuyOrder_FailsIfDepositAmountIsZero() public { - // Test covered in BondingCurveBase_v1 + // Test covered in BondingCurveBase_v2 } function testBuyOrder_FailsIfDepositAmountOverflowsVirtualCollateralSupply( @@ -387,23 +452,25 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { // Execution vm.prank(buyer); vm.expectEmit(true, true, true, true, address(_token)); - emit Transfer(buyer, address(bondingCurveFundingManager), amount); + emit IERC20.Transfer(buyer, address(bondingCurveFundingManager), amount); vm.expectEmit(true, true, true, true, address(issuanceToken)); - emit Transfer(address(0), buyer, formulaReturn); + emit IERC20.Transfer(address(0), buyer, formulaReturn); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensBought(buyer, amount, formulaReturn, buyer); + emit IBondingCurveBase_v2.TokensBought( + buyer, amount, formulaReturn, buyer + ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualIssuanceAmountAdded( + emit IVirtualIssuanceSupplyBase_v1.VirtualIssuanceAmountAdded( formulaReturn, (INITIAL_ISSUANCE_SUPPLY + formulaReturn) ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualCollateralAmountAdded( + emit IVirtualCollateralSupplyBase_v1.VirtualCollateralAmountAdded( amount, (INITIAL_COLLATERAL_SUPPLY + amount) ); bondingCurveFundingManager.buy(amount, formulaReturn); @@ -465,23 +532,25 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { // Execution vm.prank(buyer); vm.expectEmit(true, true, true, true, address(_token)); - emit Transfer(buyer, address(bondingCurveFundingManager), amount); + emit IERC20.Transfer(buyer, address(bondingCurveFundingManager), amount); vm.expectEmit(true, true, true, true, address(issuanceToken)); - emit Transfer(address(0), buyer, formulaReturn); + emit IERC20.Transfer(address(0), buyer, formulaReturn); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensBought(buyer, amount, formulaReturn, buyer); + emit IBondingCurveBase_v2.TokensBought( + buyer, amount, formulaReturn, buyer + ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualIssuanceAmountAdded( + emit IVirtualIssuanceSupplyBase_v1.VirtualIssuanceAmountAdded( formulaReturn, (INITIAL_ISSUANCE_SUPPLY + formulaReturn) ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualCollateralAmountAdded( + emit IVirtualCollateralSupplyBase_v1.VirtualCollateralAmountAdded( buyAmountMinusFee, (INITIAL_COLLATERAL_SUPPLY + buyAmountMinusFee) ); bondingCurveFundingManager.buy(amount, formulaReturn); @@ -558,6 +627,105 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { assertEq(issuanceToken.balanceOf(to), formulaReturn); } + /* + Test: sellTo Modifier Checks + ├── Given: seller is not permissioned + │ └── When: sellTo is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + ├── And: buysellinging is not enabled + │ └── When: sellTo is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + ├── And: selling is enabled + └── And: receiver is invalid + └── When: sellTo is called + └── Then: it should revert (modifier in position check) + */ + + function testsellTo_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeSell(); + + vm.expectRevert( + IRedeemingBondingCurveBase_v2 + .Module__RedeemingBondingCurveBase__SellingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + + // Open up Buy again + bondingCurveFundingManager.openSell(); + + // validReceiver + vm.expectRevert( + abi.encodeWithSelector( + IBondingCurveBase_v2 + .Module__BondingCurveBase__InvalidRecipient + .selector + ) + ); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + } + + /* + Test: sell Modifier Checks + ├── Given: seller is not permissioned + │ └── When: sell is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + └── And: buysellinging is not enabled + └── When: sell is called + └── Then: it should revert (modifier in position check) + + */ + + function testsell_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.sell(0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeSell(); + + vm.expectRevert( + IRedeemingBondingCurveBase_v2 + .Module__RedeemingBondingCurveBase__SellingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.sell(0, 0); + } + /* Test sell and _virtualSupplySellOrder function ├── when the sell amount is 0 │ └── it should revert @@ -596,7 +764,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { */ function testSellOrder_FailsIfDepositAmountIsZero() public { - // Test covered in RedeemingBondingCurveBase_v1 + // Test covered in RedeemingBondingCurveBase_v2 } function testSellOrder_FailsIfBurnAmountExceedsVirtualIssuanceSupply( @@ -728,7 +896,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.startPrank(seller); { vm.expectEmit(true, true, true, true, address(_token)); - emit Transfer( + emit IERC20.Transfer( address(bondingCurveFundingManager), address(seller), normalized_formulaReturn @@ -736,19 +904,20 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensSold( + emit IRedeemingBondingCurveBase_v2.TokensSold( seller, userSellAmount, normalized_formulaReturn, seller ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualIssuanceAmountSubtracted( + emit IVirtualIssuanceSupplyBase_v1.VirtualIssuanceAmountSubtracted( userSellAmount, newVirtualIssuanceSupply - userSellAmount ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualCollateralAmountSubtracted( + emit IVirtualCollateralSupplyBase_v1 + .VirtualCollateralAmountSubtracted( normalized_formulaReturn, newVirtualCollateral - normalized_formulaReturn ); @@ -837,7 +1006,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.startPrank(seller); { vm.expectEmit(true, true, true, true, address(_token)); - emit Transfer( + emit IERC20.Transfer( address(bondingCurveFundingManager), address(seller), sellAmountMinusFee @@ -845,17 +1014,20 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensSold(seller, userSellAmount, sellAmountMinusFee, seller); + emit IRedeemingBondingCurveBase_v2.TokensSold( + seller, userSellAmount, sellAmountMinusFee, seller + ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualIssuanceAmountSubtracted( + emit IVirtualIssuanceSupplyBase_v1.VirtualIssuanceAmountSubtracted( userSellAmount, newVirtualIssuanceSupply - userSellAmount ); vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit VirtualCollateralAmountSubtracted( + emit IVirtualCollateralSupplyBase_v1 + .VirtualCollateralAmountSubtracted( normalized_formulaReturn, newVirtualCollateral - normalized_formulaReturn ); @@ -1172,10 +1344,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { // OnlyOrchestrator Functions /* Test setVirtualIssuanceSupply and _setVirtualIssuanceSupply function - ├── given caller is not the Orchestrator_v1 admin + ├── given caller is not permissioned │ └── when the function setVirtualIssuanceSupply() is called │ └── then it should revert (test modifier is in place. Modifier test itself is tested in base Module tests) - └── given the caller is the Orchestrator_v1 admin + └── given the caller is permissioned ├── and the buy | sell curve are still open (modifier test) │ └── when the function_setVirtualIssuanceSupply() is called │ └── then it should revert @@ -1191,19 +1363,20 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { └── and it should emit an event */ - function testSetVirtualIssuanceSupply_WorksGivenOnlyOrchestratorAdminModifierInPlace( - uint _newSupply - ) public { - vm.assume(_newSupply != 0); + function testSetVirtualIssuanceSupply_PermissionedModifierInPlace() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - non_admin_address + IModule_v2.Module__CallerNotPermissioned.selector ) ); - vm.prank(non_admin_address); - bondingCurveFundingManager.setVirtualIssuanceSupply(_newSupply); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setVirtualIssuanceSupply(0); } function testSetVirtualIssuanceSupply_WorksGivenOnlyWhenCurveInteractionsAreClosedModifierInPosition( @@ -1211,7 +1384,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { ) public callerIsOrchestratorAdmin { vm.assume(_newSupply != 0); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1254,7 +1427,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit VirtualIssuanceSupplySet(_newSupply, INITIAL_ISSUANCE_SUPPLY); + emit IVirtualIssuanceSupplyBase_v1.VirtualIssuanceSupplySet( + _newSupply, INITIAL_ISSUANCE_SUPPLY + ); bondingCurveFundingManager.call_setVirtualIssuanceSupply(_newSupply); assertEq( bondingCurveFundingManager.getVirtualIssuanceSupply(), _newSupply @@ -1262,10 +1437,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { } /* Test setVirtualCollateralSupply and _setVirtualCollateralSupply function - ├── given caller is not the Orchestrator_v1 admin + ├── given caller is not permissioned │ └── when the function setVirtualCollateralSupply() is called │ └── then it should revert (test modifier is in place. Modifier test itself is tested in base Module tests) - └── given the caller is the Orchestrator_v1 admin + └── given the caller is permissioned ├── and the buy | sell curve are still open (modifier test) │ └── when the setVirtualCollateralSupply() is called │ └── then it should revert @@ -1278,19 +1453,20 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { └── and it should emit an event */ - function testSetVirtualCollateralSupply_WorksGivenOnlyOrchestratorAdminModifierInPlace( - uint _newSupply - ) public { - vm.assume(_newSupply != 0); + function testSetVirtualCollateralSupply_permissionedModifierInPlace() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - non_admin_address + IModule_v2.Module__CallerNotPermissioned.selector ) ); - vm.prank(non_admin_address); - bondingCurveFundingManager.setVirtualCollateralSupply(_newSupply); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setVirtualCollateralSupply(0); } function testSetVirtualCollateralSupply_WorksGivenOnlyWhenCurveInteractionsAreClosedModifierInPosition( @@ -1298,7 +1474,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { ) public callerIsOrchestratorAdmin { vm.assume(_newSupply != 0); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1330,7 +1506,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit VirtualCollateralSupplySet(_newSupply, INITIAL_COLLATERAL_SUPPLY); + emit IVirtualCollateralSupplyBase_v1.VirtualCollateralSupplySet( + _newSupply, INITIAL_COLLATERAL_SUPPLY + ); bondingCurveFundingManager.setVirtualCollateralSupply(_newSupply); assertEq( bondingCurveFundingManager.getVirtualCollateralSupply(), _newSupply @@ -1338,9 +1516,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { } /* Test setReserveRatioForBuying and _setReserveRatioForBuying function - ├── when caller is not the Orchestrator_v1 admin - │ └── it should revert (tested in base Module tests) - └── when caller is the Orchestrator_v1 admin + ├── when caller is not permissioned + │ └── it should revert (modifier in position test) + └── when caller is the Orchestrator_v2 admin ├── when buy | sell is still open (modifier test) │ └── it should revert ├── when reserve ratio is 0% @@ -1355,10 +1533,26 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { └── it should revert */ + function testSetReserveRatioForBuying_permissionedModifierInPosition() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setReserveRatioForBuying(0); + } + function testSetReserveRatioForBuying_WorksGivenOnlyWhenCurveInteractionsAreClosedModifierInPosition( ) public { vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1371,7 +1565,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { { _closeCurveInteractions(); // Close interactions to enable setting of ratio vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidReserveRatio .selector ); @@ -1384,7 +1578,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.assume(_newRatio > bondingCurveFundingManager.call_PPM()); _closeCurveInteractions(); // Close interactions to enable setting of ratio vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidReserveRatio .selector ); @@ -1401,7 +1595,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit BuyReserveRatioSet(_newRatio, RESERVE_RATIO_FOR_BUYING); + emit IFM_BC_Bancor_Redeeming_VirtualSupply_v2.BuyReserveRatioSet( + _newRatio, RESERVE_RATIO_FOR_BUYING + ); bondingCurveFundingManager.setReserveRatioForBuying(_newRatio); assertEq( bondingCurveFundingManager.call_reserveRatioForBuying(), _newRatio @@ -1412,9 +1608,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { // Test reserve ratio changes /* Test setReserveRatioForSelling and _setReserveRatioForSelling function - ├── when caller is not the Orchestrator_v1 admin - │ └── it should revert (tested in base Module tests) - └── when caller is the Orchestrator_v1 admin + ├── when caller is not permissioned + │ └── it should revert (modifier in position) + └── when caller is permissioned ├── when buy | sell is still open (modifier test) │ └── it should revert ├── when reserve ratio is 0% @@ -1429,10 +1625,26 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { └── it should revert */ + function testSetReserveRatioForSelling_permissionedModifierInPosition() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setReserveRatioForSelling(0); + } + function testSetReserveRatioForSelling_WorksGivenOnlyWhenCurveInteractionsAreClosedModifierInPosition( ) public { vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1445,7 +1657,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { { _closeCurveInteractions(); // Close interactions to enable setting of ratio vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidReserveRatio .selector ); @@ -1458,7 +1670,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.assume(_newRatio > bondingCurveFundingManager.call_PPM()); _closeCurveInteractions(); // Close interactions to enable setting of ratio vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidReserveRatio .selector ); @@ -1475,7 +1687,9 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit SellReserveRatioSet(_newRatio, RESERVE_RATIO_FOR_SELLING); + emit IFM_BC_Bancor_Redeeming_VirtualSupply_v2.SellReserveRatioSet( + _newRatio, RESERVE_RATIO_FOR_SELLING + ); bondingCurveFundingManager.setReserveRatioForSelling(_newRatio); assertEq( bondingCurveFundingManager.call_reserveRatioForSelling(), _newRatio @@ -1510,7 +1724,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { newIssuanceToken.setMinter(address(this), true); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidTokenDecimal .selector ); @@ -1535,7 +1749,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { newIssuanceToken.setMinter(address(this), true); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__InvalidTokenDecimal .selector ); @@ -1584,7 +1798,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { │ └── it should return the amount without change ├── when the token decimals are higher than the required decimals │ └── it should cut the excess decimals from the amount and return it - └── when caller is the Orchestrator_v1 admin + └── when caller is the Orchestrator_v2 admin └── it should pad the amount by the missing decimals and return it */ @@ -1643,7 +1857,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { // OnlyOrchestrator Mutating Functions /* Test transferOrchestratorToken - ├── given the onlyPaymentClient modifier is set (individual modifier tests are done in Module_v1.t.sol) + ├── given the onlyPaymentClient modifier is set (individual modifier tests are done in Module_v2.t.sol) │ └── and the conditions of the modifier are not met │ └── when the function transferOrchestratorToken() gets called │ └── then it should revert @@ -1663,10 +1877,10 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { address to, uint amount ) public { - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); vm.prank(caller); - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); bondingCurveFundingManager.transferOrchestratorToken(to, amount); } @@ -1695,7 +1909,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { assertEq(_token.balanceOf(address(bondingCurveFundingManager)), amount); // Add logic module to workflow to pass modifier - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(_erc20PaymentClientMock)); vm.startPrank(address(_erc20PaymentClientMock)); { @@ -1721,12 +1935,12 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { assertEq(_token.balanceOf(address(bondingCurveFundingManager)), amount); // Add logic module to workflow to pass modifier - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(_erc20PaymentClientMock)); vm.startPrank(address(_erc20PaymentClientMock)); { vm.expectEmit(true, true, true, true); - emit TransferOrchestratorToken(to, amount); + emit IFundingManager_v1.TransferOrchestratorToken(to, amount); bondingCurveFundingManager.transferOrchestratorToken(to, amount); } @@ -1759,7 +1973,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { uint virtualSupply = 100e18; vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1772,7 +1986,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { bondingCurveFundingManager.closeSell(); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); @@ -1785,7 +1999,7 @@ contract FM_BC_Bancor_Redeeming_VirtualSupplyV1Test is ModuleTest { bondingCurveFundingManager.closeBuy(); vm.expectRevert( - IFM_BC_Bancor_Redeeming_VirtualSupply_v1 + IFM_BC_Bancor_Redeeming_VirtualSupply_v2 .Module__FM_BC_Bancor_Redeeming_VirtualSupply__CurveInteractionsMustBeClosed .selector ); diff --git a/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.t.sol similarity index 74% rename from test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.t.sol rename to test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.t.sol index 4c3c931af..986b588be 100644 --- a/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.t.sol +++ b/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.t.sol @@ -5,15 +5,15 @@ import "forge-std/console.sol"; // SuT import { - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1, + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2, IFundingManager_v1, - IBondingCurveBase_v1 + IBondingCurveBase_v2 } from - "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; + "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; -import {IFM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; +import {IFM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; @@ -24,21 +24,21 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {BondingSurface} from "@fm/bondingCurve/formulas/BondingSurface.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; import { - IRedeemingBondingCurveBase_v1, - IRedeemingBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; + IRedeemingBondingCurveBase_v2, + IRedeemingBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; import {IBondingSurface} from "@fm/bondingCurve/interfaces/IBondingSurface.sol"; -import {IFM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; -import {IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1.sol"; +import {IFM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; +import {IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2.sol"; import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; import {FixedPointMathLib} from "src/modules/lib/FixedPointMathLib.sol"; @@ -55,7 +55,7 @@ import {FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed} - buy() & buyOrderFor() - sell() & sellOrderFor() */ -contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is +contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2_Test is ModuleTest { string private constant NAME = "Topos Token"; @@ -71,9 +71,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is bool private constant BUY_AND_SELL_IS_RESTRICTED = false; uint32 private constant BPS = 10_000; - bytes32 private constant RISK_MANAGER_ROLE = "RISK_MANAGER"; - bytes32 private constant COVER_MANAGER_ROLE = "COVER_MANAGER"; - uint private MIN_RESERVE = 10 ** _token.decimals(); uint64 private constant MAX_SEIZE = 100; uint64 private constant MAX_SELL_FEE = 100; @@ -92,17 +89,14 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is address buyer = makeAddr("buyer"); address seller = makeAddr("seller"); address burner = makeAddr("burner"); - address coverManager = address(0xa1bc); - address riskManager = address(0xb0b); address tokenVault = makeAddr("tokenVault"); - bytes32 CURVE_INTERACTION_ROLE = "CURVE_USER"; function setUp() public { // Deploy contracts issuanceToken = new ERC20Issuance_v1(NAME, SYMBOL, DECIMALS, MAX_SUPPLY); issuanceToken.setMinter(address(this), true); - FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 .BondingCurveProperties memory bc_properties; // Deploy formula and cast to address for encoding @@ -132,7 +126,9 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is ); _setUpOrchestrator(bondingCurveFundingManager); - _authorizer.setIsAuthorized(address(this), true); + + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); // Set Minter issuanceToken.setMinter(address(bondingCurveFundingManager), true); @@ -155,14 +151,6 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is address(bondingCurveFundingManager), bondingCurveFundingManager.MIN_RESERVE() ); - - address[] memory targets = new address[](2); - targets[0] = buyer; - targets[1] = seller; - - bondingCurveFundingManager.grantModuleRoleBatched( - CURVE_INTERACTION_ROLE, targets - ); } // ------------------------------------------------------------------------- @@ -267,7 +255,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is _token.setDecimals(decimals_); // Setup bondingCurve properties - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties; // Deploy formula and cast to address for encoding @@ -336,8 +324,8 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is } else { vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidLiquidityVaultController + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidLiquidityVaultController .selector, caller_ ) @@ -349,8 +337,8 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is ); } - // ------------------------------------------------------------------------- - // Public Functions + // ======================================================================== + // Init Functions /* Test: Init fails for invalid formula @@ -359,7 +347,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is */ function testInitFailsForInvalidFormula() public { - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties; bc_properties.formula = address( new FM_BC_BondingSurface_Redeeming_Restricted_Repayer_SeizableV1_Exposed( @@ -378,8 +366,8 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidBondingSurfaceFormula + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidBondingSurfaceFormula .selector ) ); @@ -401,11 +389,11 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is // ------------------------------------------------------------------------- // Tests: Supports Interface - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bondingCurveFundingManager.supportsInterface( type( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 ).interfaceId ) ); @@ -416,183 +404,70 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is ); } - // ------------------------------------------------------------------------- - // Public Functions - - /* Test buy() & buyFor() functions - Please Note: The functions have been extensively tested in the BondingCurveBase_v1.t contract. These - tests only check for the placement of the onlyIfNotBuyAndSellRestricted() modifier - ├── Given the modifier onlyIfNotBuyAndSellRestricted() is in place - tests only check for the placement of the onlyIfNotBuyAndSellRestricted() modifier - ├── Given the modifier onlyIfNotBuyAndSellRestricted() is in place - │ └── And the modifier condition isn't met - │ ├── When the function buy() is called - │ └── Then it should revert - └── Given the modifier onlyIfNotBuyAndSellRestricted() is in place - └── Given the modifier onlyIfNotBuyAndSellRestricted() is in place - └── And the modifier condition isn't met - └── When the function buyFor() is called - └── Then it should revert - */ - - function testBuy_modifierInPlace() public { - // Set buyAndSellIsRestricted to true - bondingCurveFundingManager.restrictBuyAndSell(); - assertEq(bondingCurveFundingManager.isBuyAndSellRestricted(), true); - - // Check for modifier - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.CURVE_INTERACTION_ROLE() - ), - nonAuthorizedBuyer - ) - ); - vm.prank(nonAuthorizedBuyer); - bondingCurveFundingManager.buy(1, 1); - } - - function testBuyFor_modifierInPlace() public { - // Set buyAndSellIsRestricted to true - bondingCurveFundingManager.restrictBuyAndSell(); - assertEq(bondingCurveFundingManager.isBuyAndSellRestricted(), true); + // ======================================================================== + // Public Getter Functions - // Check for modifier - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.CURVE_INTERACTION_ROLE() - ), - nonAuthorizedBuyer - ) - ); - vm.prank(nonAuthorizedBuyer); - bondingCurveFundingManager.buyFor(seller, 1, 1); - } + // ------------------------------------------------------------------------- + // Implementation Specific Public Functions - /* Test unrestrictBuyAndSell() function - ├── Given the onlyModuleRole(COVER_MANAGER_ROLE) modifier is in place - │ └── And the modifier condition isn't met - │ └── When the function unrestrictBuyAndSell() is called - │ └── Then it should revert - └── Given the msg.sender is the COVER_MANAGER_ROLE - └── When the function unrestrictBuyAndSell() is called - └── Then it should set the buyAndSellIsRestricted to false - └── And it should emit an event + /* Test seizable() + └── When: the function seizable() gets called + └── Then: it should return the correct seizable amount */ - function testBuyAndSellIsUnrestricted_modifierInPlace() public { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - nonAuthorizedBuyer - ) - ); - vm.prank(nonAuthorizedBuyer); - bondingCurveFundingManager.unrestrictBuyAndSell(); - } - - function testBuyAndSellIsUnrestricted_worksGivenCallerHasCoverManagerRole() - public - { + function testSeizable_works(uint tokenBalance_, uint64 seize_) public { + tokenBalance_ = + bound(tokenBalance_, 1, (UINT256_MAX - MIN_RESERVE) / 1000); // to protect agains overflow if max balance * max seize + seize_ = + uint64(bound(seize_, 1, bondingCurveFundingManager.MAX_SEIZE())); // Setup - bondingCurveFundingManager.restrictBuyAndSell(); - assertEq(bondingCurveFundingManager.isBuyAndSellRestricted(), true); - - // Test condition - vm.expectEmit( - true, true, true, true, address(bondingCurveFundingManager) + // Get balance before test + uint tokenBalanceFundingMangerBaseline = + _token.balanceOf(address(bondingCurveFundingManager)); + // mint collateral to funding manager + _mintCollateralTokenToAddressHelper( + address(bondingCurveFundingManager), tokenBalance_ ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .BuyAndSellIsUnrestricted(); - bondingCurveFundingManager.unrestrictBuyAndSell(); - } - - /* Test restrictBuyAndSell() function - ├── Given the onlyModuleRole(COVER_MANAGER_ROLE) modifier is in place - │ └── And the modifier condition isn't met - │ └── When the function restrictBuyAndSell() is called - │ └── Then it should revert - └── Given the msg.sender is the COVER_MANAGER_ROLE - └── When the function restrictBuyAndSell() is called - └── Then it should set the buyAndSellIsRestricted to true - └── And it should emit an event - */ + // set seize in contract + bondingCurveFundingManager.adjustSeize(seize_); - function testRestrictBuyAndSell_modifierInPlace() public { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - nonAuthorizedBuyer - ) - ); - vm.prank(nonAuthorizedBuyer); - bondingCurveFundingManager.restrictBuyAndSell(); - } + // calculate return value + uint expectedReturnValue = + ((tokenBalance_ + tokenBalanceFundingMangerBaseline) * seize_) / BPS; - function testUnrestrictBuyAndSell_worksGivenCallerHasCoverManagerRole() - public - { - // Setup - bondingCurveFundingManager.unrestrictBuyAndSell(); - assertEq(bondingCurveFundingManager.isBuyAndSellRestricted(), false); + // Execute tx + uint returnValue = bondingCurveFundingManager.getSeizableAmount(); - // Test condition - vm.expectEmit( - true, true, true, true, address(bondingCurveFundingManager) - ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .BuyAndSellIsRestricted(); - bondingCurveFundingManager.restrictBuyAndSell(); + // Assert right return value + assertEq(returnValue, expectedReturnValue); } - /* Test internal _onlyIfNotBuyAndSellRestrictedModifier() function - /* Test internal _onlyIfNotBuyAndSellRestrictedModifier() function - └── Given buy and selling is restricted - └── And the msg.sender does not have the CURVE_INTERACTION_ROLE - └── When the function _onlyIfNotBuyAndSellRestrictedModifier() is called - └── When the function _onlyIfNotBuyAndSellRestrictedModifier() is called - └── Then it should revert + /* Test getRepayableAmount() + └── When: the function getRepayableAmount() gets called + └── Then: it should return the return value of _getRepayableAmount() */ - function testInternalonlyIfNotBuyAndSellRestrictedModifier_revertGivenCallerHasNotCoverManagerRole( - ) public { - // Setup - bondingCurveFundingManager.restrictBuyAndSell(); - assertEq(bondingCurveFundingManager.isBuyAndSellRestricted(), true); - - // Test condition - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.CURVE_INTERACTION_ROLE() - ), - nonAuthorizedBuyer - ) - ); - vm.prank(nonAuthorizedBuyer); - bondingCurveFundingManager.exposed_onlyIfNotBuyAndSellRestrictedModifier( - ); - bondingCurveFundingManager.exposed_onlyIfNotBuyAndSellRestrictedModifier( - ); + function testPublicGetRepayableAmount_works() public { + // get return value from internal function + uint internalFunctionResult = + bondingCurveFundingManager.exposed_getRepayableAmount(); + // Get return value from public function + uint publicFunctionResult = + bondingCurveFundingManager.getRepayableAmount(); + // Assert they are equal + assertEq(internalFunctionResult, publicFunctionResult); } + // ======================================================================== + // Public Mutating Functions + + // ------------------------------------------------------------------------ + // Mutating - Permissioned Functions + /* Test burnIssuanceToken() + ├── Given: caller is not permissioned + │ └── When: the function burnIssuanceToken() gets called + │ └── Then: it should revert (modifier in position) ├── Given: amount_ > msg.sender balance of issuance token │ └── When: the function burnIssuanceToken() gets called │ └── Then: it should revert @@ -600,6 +475,19 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is └── When: the function burnIssuanceToken() gets called └── Then: it should burn amount_ from msg.sender's balance */ + function testBurnIssuanceToken_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.burnIssuanceToken(0); + } function testBurnIssuanceToken_revertGivenAmountBiggerThanMsgSenderBalance( uint amount_ @@ -643,6 +531,9 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is } /* Test burnIssuanceTokenFor() + ├── Given: caller is not permissioned + │ └── When: the function burnIssuanceToken() gets called + │ └── Then: it should revert (modifier in position) ├── Given: _owner != msg.sender │ ├── And: the allowance < amount_ │ │ └── When: the function burnIssuanceTokenFor() gets called @@ -662,6 +553,19 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is └── When: the function burnIssuanceToken() gets called └── Then: it should burn amount_ from _owner's balance */ + function testBurnIssuanceTokenFor_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.burnIssuanceTokenFor(address(0), 0); + } function testBurnIssuanceTokenFor_revertGivenAmountHigherThanOwnerAllowance( uint amount_ @@ -780,114 +684,386 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is assertEq(issuanceToken.balanceOf(burner), burnerTokenBalance - amount_); } - // ------------------------------------------------------------------------- - // Implementation Specific Public Functions - - /* Test seizable() - └── When: the function seizable() gets called - └── Then: it should return the correct seizable amount + /* Test seize() + ├── Given: the calleris not permissioned + │ └── When: the function seize() gets called + │ └── Then: it should revert (modifier in position) + └── Given: the caller_ is permissioned + ├── And: the parameter amount_ > the seizable amount + │ └── When: the function seize() gets called + │ └── Then: it should revert + ├── And: the lastSeizeTimestamp + SEIZE_DELAY > block.timestamp + │ └── When: the function seize() gets called + │ └── Then: it should revert + ├── And: the capital available - amount_ < MIN_RESERVE + │ └── When: the function seize() gets called + │ └── Then: it should transfer the value of capitalAvailable - MIN_RESERVE tokens to the msg.sender + │ ├── And: it should set the current timeStamp to lastSeizeTimestamp + │ └── And: it should emit an event + └── And: the capital available - amount_ > MIN_RESERVE + └── And: the lastSeizeTimestamp + SEIZE_DELAY < block.timestamp + └── When: the function seize() gets called + └── Then: it should transfer the value of amount_ tokens to the msg.sender + ├── And: it should set the current timeStamp to lastSeizeTimestamp + └── And: it should emit an event */ + function testSeize_ModifierInPosition() public { + // permissioned - function testSeizable_works(uint tokenBalance_, uint64 seize_) public { - tokenBalance_ = - bound(tokenBalance_, 1, (UINT256_MAX - MIN_RESERVE) / 1000); // to protect agains overflow if max balance * max seize - seize_ = - uint64(bound(seize_, 1, bondingCurveFundingManager.MAX_SEIZE())); - // Setup - // Get balance before test - uint tokenBalanceFundingMangerBaseline = - _token.balanceOf(address(bondingCurveFundingManager)); - // mint collateral to funding manager - _mintCollateralTokenToAddressHelper( - address(bondingCurveFundingManager), tokenBalance_ + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) ); - // set seize in contract - bondingCurveFundingManager.adjustSeize(seize_); - - // calculate return value - uint expectedReturnValue = - ((tokenBalance_ + tokenBalanceFundingMangerBaseline) * seize_) / BPS; - - // Execute tx - uint returnValue = bondingCurveFundingManager.getSeizableAmount(); - - // Assert right return value - assertEq(returnValue, expectedReturnValue); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.seize(0); } - /* Test getRepayableAmount() - └── When: the function getRepayableAmount() gets called - └── Then: it should return the return value of _getRepayableAmount() - */ + function testSeize_revertGivenAmountBiggerThanSeizableAmount(uint amount_) + public + { + uint currentSeizable = bondingCurveFundingManager.getSeizableAmount(); + vm.assume(amount_ > currentSeizable); - function testPublicGetRepayableAmount_works() public { - // get return value from internal function - uint internalFunctionResult = - bondingCurveFundingManager.exposed_getRepayableAmount(); - // Get return value from public function - uint publicFunctionResult = - bondingCurveFundingManager.getRepayableAmount(); - // Assert they are equal - assertEq(internalFunctionResult, publicFunctionResult); + vm.expectRevert( + abi.encodeWithSelector( + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeizeAmount + .selector, + currentSeizable + ) + ); + bondingCurveFundingManager.seize(amount_); } - // ------------------------------------------------------------------------- - // onlyLiquidityVaultController Functions + function testSeize_revertGivenLastSeizeTimerNotReset() public { + uint seizeAmount = 1 ether; + // Setup + // Mint collateral for enough capital available + _mintCollateralTokenToAddressHelper( + address(bondingCurveFundingManager), seizeAmount * 100 + ); + // Check Seize timestamp before calling function + uint seizeTimestampBefore = + bondingCurveFundingManager.getLastSeizeTimestamp(); - /* Test transferRepayment() - ├── Given the caller_ is not the liquidityVaultController - │ └── When the function transferRepayment() is called - │ └── Then it should revert - ├── Given modifier validReceiver(to_) is in place: Please Note: Modifier test can be found in BondingCurveBase_v1.t - │ └── When the function transferRepayment() is called - │ └── Then it should revert if receiver is invalid - └── Given: the caller_ is the liquidityVaultController - └── And: the to_ address is valid - ├── And: the amount_ > the repayable amount available - │ └── When: the function transferRepayment() gets called - │ └── Then: it should revert - ├── And: the amount_ > the repayable amount available - │ └── When: the function transferRepayment() gets called - │ └── Then: it should revert - └── And: amount_ <= repayable amount available - └── When: the function transferRepayment() gets called - └── Then: it should transfer amount_ to the to_ address - └── And: it should emit an event - */ + // Assert expected fail. block.timestamp == 1 without setting it in vm.warp + assertGt((seizeTimestampBefore + SEIZE_DELAY), block.timestamp); - function testTransferPayment_revertGivenCallerIsNotLiquidityVaultController( - address to_, - uint amount_ - ) public { - // Valid to_ address - vm.assume( - to_ != liquidityVaultController - && to_ != address(bondingCurveFundingManager) && to_ != address(0) - ); - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidLiquidityVaultController - .selector, - seller + // Execute Tx expecting it to revert + vm.expectRevert( + abi.encodeWithSelector( + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__SeizeTimeout + .selector, + ( + seizeTimestampBefore + + bondingCurveFundingManager.SEIZE_DELAY() ) - ); - bondingCurveFundingManager.transferRepayment(to_, amount_); - } + ) + ); + bondingCurveFundingManager.seize(seizeAmount); } - function testTransferPayment_modifierInPlace(uint amount_) public { - address to = address(0); - amount_ = bound(amount_, 2, UINT256_MAX - 1); - - // Execute Tx - vm.startPrank(liquidityVaultController); - { - vm.expectRevert( - IBondingCurveBase_v1 + function testSeize_worksGivenCapitalAvailableMinusMinReserveIsReturned() + public + { + // Setup + // Set block.timestamp to valid time + vm.warp(SEIZE_DELAY + 1); + // Amount has to be smaller than seizable amount which is (currentBalance * currentSeize) / BPS + // i.e. (1 ether * 200 ) / 10_000 + uint amount = 1e16; // 0.01 ether + // Return value check for emit. Expected return is 0. Capital available - MIN_RESERVE + uint expectedReturnValue = bondingCurveFundingManager + .exposed_getCapitalAvailable() - MIN_RESERVE; + assertEq(expectedReturnValue, 0); + + //Get balance before seize + uint balanceBeforeBuy = _token.balanceOf(address(this)); + + // Execute Tx + vm.expectEmit( + true, true, true, true, address(bondingCurveFundingManager) + ); + emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .CollateralSeized(expectedReturnValue); + bondingCurveFundingManager.seize(amount); + + // Assert that no tokens have been sent + assertEq(balanceBeforeBuy, balanceBeforeBuy); + } + + function testSeize_worksGivenCapitalAmountIsReturnd(uint amount_) public { + // Setup + // Set block.timestamp to valid time + vm.warp(SEIZE_DELAY + 1); + // Bound seizable value + amount_ = bound(amount_, 1, type(uint128).max); + // Mint enough surplus so seizing can happen + _mintCollateralTokenToAddressHelper( + address(bondingCurveFundingManager), amount_ * 10_000 + ); + //Get balance before seize + uint balanceBeforeBuy = _token.balanceOf(address(this)); + + // Execute Tx + vm.expectEmit( + true, true, true, true, address(bondingCurveFundingManager) + ); + emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .CollateralSeized(amount_); + bondingCurveFundingManager.seize(amount_); + + // Get balance after buying + uint balanceAfterBuy = _token.balanceOf(address(this)); + // Assert that no tokens have been sent + assertEq(balanceAfterBuy, balanceBeforeBuy + amount_); + } + + /* Test adjust size + ├── Given: the caller_ is not permissioned + │ └── When: the function adjustSeize() gets called + │ └── Then: it should revert (modifier in position) + └── Given: the caller_ is permissioned + └── When: the function adjustSeize() gets called + └── Then: it should call the internal function and set the state + */ + + function testAdjustSeize_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + + bondingCurveFundingManager.adjustSeize(0); + } + + function testAdjustSeize_permissioned(uint64 seize_) public { + vm.assume(seize_ != bondingCurveFundingManager.getCurrentSeize()); + seize_ = uint64(bound(seize_, 1, MAX_SEIZE)); + + // Execute Tx + bondingCurveFundingManager.adjustSeize(seize_); + + assertEq(bondingCurveFundingManager.getCurrentSeize(), seize_); + } + + /* Test setliquidityVaultControllerContract() + ├── Given: the caller_ is not permissioned + │ └── When: the function setliquidityVaultControllerContract() gets called + │ └── Then: it should revert (modifier in position) + └── Given: the caller_ is permissioned + ├── And: _lp == address(0) + │ └── When: the function setliquidityVaultController() gets called + │ └── Then: it should revert + └── And: _lp != address(0) + └── When: the function setliquidityVaultController() gets called + └── Then: it should set the state liquidityVaultController to _lp + └── And: it should emit an event + */ + function testSetliquidityVaultControllerContract_ModifierInPosition() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setLiquidityVaultControllerContract( + address(0) + ); + } + + function testSetliquidityVaultControllerContract_revertGivenAddressIsZero() + public + { + address lvc_ = address(0); + + // Expect Revert + vm.expectRevert( + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidInputAddress + .selector + ); + bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc_); + } + + function testSetliquidityVaultControllerContract_revertGivenAddressIsEqualToFM( + ) public { + address lvc = address(bondingCurveFundingManager); + + // Expect Revert + vm.expectRevert( + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidInputAddress + .selector + ); + bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc); + } + + function testSetliquidityVaultControllerContract_permissioned(address lvc_) + public + { + vm.assume( + lvc_ != address(0) && lvc_ != address(bondingCurveFundingManager) + ); + + vm.expectEmit( + true, true, true, true, address(bondingCurveFundingManager) + ); + emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .LiquidityVaultControllerChanged( + address(lvc_), liquidityVaultController + ); + bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc_); + } + + /* Test setRepayableAmount() + ├── Given: the caller_ is not permissioned + │ └── When: the function setRepayableAmount() gets called + │ └── Then: it should revert (modifier in position) + └── Given: the caller_ is permissioned + ├── And: amount_ > either capitalAvailable or capitalRequirements + │ └── When: the function setRepayableAmount() gets called + │ └── Then: it should revert + └── And: amount_ <= either capitalAvailable or capitalRequirements + └── When: the function setRepayableAmount() gets called + └── Then: it should set the state of repayableAmount to amount_ + └── And: it should emit an event + */ + function testSetRepayableAmount_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setRepayableAmount(0); + } + + function testSetRepayableAmount_permissioned(uint amount_) public { + amount_ = bound( + amount_, + bondingCurveFundingManager.exposed_getSmallerCaCr() + 1, + type(uint).max + ); + + // Execute Tx + vm.expectRevert( + abi.encodeWithSelector( + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount + .selector + ) + ); + bondingCurveFundingManager.setRepayableAmount(amount_); + } + + /* Test: setTokenVault() modifier in position + └── Given: the caller_ is permissioned + └── When: the function setTokenVault() gets called + └── Then: it should revert (modifier in position) + └── Given: the caller_ is permissioned + └── When: the function setTokenVault() gets called + └── Then: it should change the _tokenVault address to the given address + */ + function testSetTokenVault_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setTokenVault(address(0)); + } + + function testSetTokenVault_permissioned(address _tokenVault) public { + vm.assume(_tokenVault != address(0)); + vm.assume(_tokenVault != address(bondingCurveFundingManager)); + bondingCurveFundingManager.setTokenVault(_tokenVault); + + assertEq(_tokenVault, bondingCurveFundingManager.getTokenVault()); + } + + // ------------------------------------------------------------------------- + // onlyLiquidityVaultController Functions + + /* Test transferRepayment() + ├── Given the caller_ is not the liquidityVaultController + │ └── When the function transferRepayment() is called + │ └── Then it should revert + ├── Given modifier validReceiver(to_) is in place: Please Note: Modifier test can be found in BondingCurveBase_v2.t + │ └── When the function transferRepayment() is called + │ └── Then it should revert if receiver is invalid + └── Given: the caller_ is the liquidityVaultController + └── And: the to_ address is valid + ├── And: the amount_ > the repayable amount available + │ └── When: the function transferRepayment() gets called + │ └── Then: it should revert + ├── And: the amount_ > the repayable amount available + │ └── When: the function transferRepayment() gets called + │ └── Then: it should revert + └── And: amount_ <= repayable amount available + └── When: the function transferRepayment() gets called + └── Then: it should transfer amount_ to the to_ address + └── And: it should emit an event + */ + + function testTransferPayment_revertGivenCallerIsNotLiquidityVaultController( + address to_, + uint amount_ + ) public { + // Valid to_ address + vm.assume( + to_ != liquidityVaultController + && to_ != address(bondingCurveFundingManager) && to_ != address(0) + ); + // Execute Tx + vm.startPrank(seller); + { + vm.expectRevert( + abi.encodeWithSelector( + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidLiquidityVaultController + .selector, + seller + ) + ); + bondingCurveFundingManager.transferRepayment(to_, amount_); + } + } + + function testTransferPayment_modifierInPlace(uint amount_) public { + address to = address(0); + amount_ = bound(amount_, 2, UINT256_MAX - 1); + + // Execute Tx + vm.startPrank(liquidityVaultController); + { + vm.expectRevert( + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); @@ -963,8 +1139,8 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is // Execute Tx vm.prank(liquidityVaultController); vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__MinReserveReached + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__MinReserveReached .selector ); bondingCurveFundingManager.transferRepayment(to_, amount_ + MIN_RESERVE); @@ -1020,506 +1196,18 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is } // ------------------------------------------------------------------------- - // OnlyCoverManager Functions - - /* Test seize() - ├── Given: the caller_ has not the COVER_MANAGER_ROLE - │ └── When: the function seize() gets called - │ └── Then: it should revert - └── Given: the caller_ has the COVER_MANAGER_ROLE - ├── And: the parameter amount_ > the seizable amount - │ └── When: the function seize() gets called - │ └── Then: it should revert - ├── And: the lastSeizeTimestamp + SEIZE_DELAY > block.timestamp - │ └── When: the function seize() gets called - │ └── Then: it should revert - ├── And: the capital available - amount_ < MIN_RESERVE - │ └── When: the function seize() gets called - │ └── Then: it should transfer the value of capitalAvailable - MIN_RESERVE tokens to the msg.sender - │ ├── And: it should set the current timeStamp to lastSeizeTimestamp - │ └── And: it should emit an event - └── And: the capital available - amount_ > MIN_RESERVE - └── And: the lastSeizeTimestamp + SEIZE_DELAY < block.timestamp - └── When: the function seize() gets called - └── Then: it should transfer the value of amount_ tokens to the msg.sender - ├── And: it should set the current timeStamp to lastSeizeTimestamp - └── And: it should emit an event - */ - - function testSeize_revertGivenCallerHasNotCoverManagerRole() public { - uint amount_ = 1; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.seize(amount_); - } - } - - function testSeize_revertGivenAmountBiggerThanSeizableAmount(uint amount_) - public - { - uint currentSeizable = bondingCurveFundingManager.getSeizableAmount(); - vm.assume(amount_ > currentSeizable); - - vm.expectRevert( - abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeizeAmount - .selector, - currentSeizable - ) - ); - bondingCurveFundingManager.seize(amount_); - } - - function testSeize_revertGivenLastSeizeTimerNotReset() public { - uint seizeAmount = 1 ether; - // Setup - // Mint collateral for enough capital available - _mintCollateralTokenToAddressHelper( - address(bondingCurveFundingManager), seizeAmount * 100 - ); - // Check Seize timestamp before calling function - uint seizeTimestampBefore = - bondingCurveFundingManager.getLastSeizeTimestamp(); - - // Assert expected fail. block.timestamp == 1 without setting it in vm.warp - assertGt((seizeTimestampBefore + SEIZE_DELAY), block.timestamp); - - // Execute Tx expecting it to revert - vm.expectRevert( - abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__SeizeTimeout - .selector, - ( - seizeTimestampBefore - + bondingCurveFundingManager.SEIZE_DELAY() - ) - ) - ); - bondingCurveFundingManager.seize(seizeAmount); - } - - function testSeize_worksGivenCapitalAvailableMinusMinReserveIsReturned() - public - { - // Setup - // Set block.timestamp to valid time - vm.warp(SEIZE_DELAY + 1); - // Amount has to be smaller than seizable amount which is (currentBalance * currentSeize) / BPS - // i.e. (1 ether * 200 ) / 10_000 - uint amount = 1e16; // 0.01 ether - // Return value check for emit. Expected return is 0. Capital available - MIN_RESERVE - uint expectedReturnValue = bondingCurveFundingManager - .exposed_getCapitalAvailable() - MIN_RESERVE; - assertEq(expectedReturnValue, 0); - - //Get balance before seize - uint balanceBeforeBuy = _token.balanceOf(address(this)); - - // Execute Tx - vm.expectEmit( - true, true, true, true, address(bondingCurveFundingManager) - ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .CollateralSeized(expectedReturnValue); - bondingCurveFundingManager.seize(amount); - - // Assert that no tokens have been sent - assertEq(balanceBeforeBuy, balanceBeforeBuy); - } - - function testSeize_worksGivenCapitalAmountIsReturnd(uint amount_) public { - // Setup - // Set block.timestamp to valid time - vm.warp(SEIZE_DELAY + 1); - // Bound seizable value - amount_ = bound(amount_, 1, type(uint128).max); - // Mint enough surplus so seizing can happen - _mintCollateralTokenToAddressHelper( - address(bondingCurveFundingManager), amount_ * 10_000 - ); - //Get balance before seize - uint balanceBeforeBuy = _token.balanceOf(address(this)); - - // Execute Tx - vm.expectEmit( - true, true, true, true, address(bondingCurveFundingManager) - ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .CollateralSeized(amount_); - bondingCurveFundingManager.seize(amount_); - - // Get balance after buying - uint balanceAfterBuy = _token.balanceOf(address(this)); - // Assert that no tokens have been sent - assertEq(balanceAfterBuy, balanceBeforeBuy + amount_); - } - - /* Test adjust - ├── Given: the caller_ has not the COVER_MANAGER_ROLE - │ └── When: the function adjustSeize() gets called - │ └── Then: it should revert - └── Given: the caller_ has the COVER_MANAGER_ROLE - └── When: the function adjustSeize() gets called - └── Then: it should call the internal function and set the state - */ - - function testAdjustSeize_revertGivenCallerHasNotCoverManagerRole() public { - uint64 seize_ = 10_000; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.adjustSeize(seize_); - } - } - - function testAdjustSeize_worksGivenCallerHasCoverManagerRole(uint64 seize_) - public - { - vm.assume(seize_ != bondingCurveFundingManager.getCurrentSeize()); - seize_ = uint64(bound(seize_, 1, MAX_SEIZE)); - - // Execute Tx - bondingCurveFundingManager.adjustSeize(seize_); - - assertEq(bondingCurveFundingManager.getCurrentSeize(), seize_); - } - - /* Test setSellFee() - ├── Given: the caller_ has not the COVER_MANAGER_ROLE - │ └── When: the function setSellFee() gets called - │ └── Then: it should revert - └── Given: the caller_ has the COVER_MANAGER_ROLE - ├── And: fee_ > MAX_SELL_FEE - │ └── When: the function setSellFee() getrs called - │ └── Then: it should revert - └── And: fee_ <= MAX_SELL_FEE - └── When: the function setSellFee() gets called - └── Then: it should set the state of sellFee to fee_ - */ - - function testSetSellFee_revertGivenCallerHasNotCoverManagerRole() public { - uint sellFee = 100; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.setSellFee(sellFee); - } - } - - function testSetSellFee_revertGivenSeizeBiggerThanMaxSeize(uint fee_) - public - { - vm.assume(fee_ > MAX_SELL_FEE); - - // Execute Tx - vm.expectRevert( - abi.encodeWithSelector( - IBondingCurveBase_v1 - .Module__BondingCurveBase__InvalidFeePercentage - .selector - ) - ); - bondingCurveFundingManager.setSellFee(fee_); - } - - function testAdjustSeize_worksGivenCallerHasRoleAndSeizeIsValid(uint fee_) - public - { - vm.assume(fee_ != bondingCurveFundingManager.sellFee()); - fee_ = bound(fee_, 1, MAX_SELL_FEE); - - // Execute Tx - bondingCurveFundingManager.setSellFee(fee_); - - assertEq(bondingCurveFundingManager.sellFee(), fee_); - } - - /* Test setRepayableAmount() - ├── Given: the caller_ has not the COVER_MANAGER_ROLE - │ └── When: the function setRepayableAmount() gets called - │ └── Then: it should revert - └── Given: the caller_ has the COVER_MANAGER_ROLE - ├── And: amount_ > either capitalAvailable or capitalRequirements - │ └── When: the function setRepayableAmount() gets called - │ └── Then: it should revert - └── And: amount_ <= either capitalAvailable or capitalRequirements - └── When: the function setRepayableAmount() gets called - └── Then: it should set the state of repayableAmount to amount_ - └── And: it should emit an event - */ - - function testSetRepayableAmount_revertGivenCallerHasNotCoverManagerRole() - public - { - uint amount = 1000; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.setRepayableAmount(amount); - } - } - - function testSetRepayableAmount_revertGivenCallerHasNotCoverManagerRole( - uint amount_ - ) public { - amount_ = bound( - amount_, - bondingCurveFundingManager.exposed_getSmallerCaCr() + 1, - type(uint).max - ); - - // Execute Tx - vm.expectRevert( - abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount - .selector - ) - ); - bondingCurveFundingManager.setRepayableAmount(amount_); - } - - /* Test setliquidityVaultControllerContract() - ├── Given: the caller_ has not the COVER_MANAGER_ROLE - │ └── When: the function setliquidityVaultControllerContract() gets called - │ └── Then: it should revert - └── Given: the caller_ has the COVER_MANAGER_ROLE - ├── And: _lp == address(0) - │ └── When: the function setliquidityVaultController() gets called - │ └── Then: it should revert - └── And: _lp != address(0) - └── When: the function setliquidityVaultController() gets called - └── Then: it should set the state liquidityVaultController to _lp - └── And: it should emit an event - */ - - function testSetliquidityVaultControllerContract_revertGivenCallerHasNotCoverManagerRole( - address lvc_ - ) public { - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.COVER_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc_); - } - } - - function testSetliquidityVaultControllerContract_revertGivenAddressIsZero() - public - { - address lvc_ = address(0); - - // Expect Revert - vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidInputAddress - .selector - ); - bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc_); - } - - function testSetliquidityVaultControllerContract_revertGivenAddressIsEqualToFM( - ) public { - address lvc = address(bondingCurveFundingManager); - - // Expect Revert - vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidInputAddress - .selector - ); - bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc); - } - - function testSetliquidityVaultControllerContract_worksGivenCallerHasRoleAndAddressValid( - address lvc_ - ) public { - vm.assume( - lvc_ != address(0) && lvc_ != address(bondingCurveFundingManager) - ); - - vm.expectEmit( - true, true, true, true, address(bondingCurveFundingManager) - ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .LiquidityVaultControllerChanged( - address(lvc_), liquidityVaultController - ); - bondingCurveFundingManager.setLiquidityVaultControllerContract(lvc_); - } - - // ------------------------------------------------------------------------- - // OnlyCoverManager Functions - - /* Test setCapitalRequired() - ├── Given: the caller_ has not the RISK_MANAGER_ROLE - │ └── When: the function setCapitalRequired() is called - │ └── Then: it should revert - */ - function testSetCapitalRequired_modifierInPosition() public { - uint newCapitalRequired = 1 ether; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.RISK_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.setCapitalRequired(newCapitalRequired); - } - } - - /* Test setBaseMultiplier() - ├── Given: the caller_ has not the RISK_MANAGER_ROLE - │ └── When: the function setBaseMultiplier() is called - │ └── Then: it should revert - */ - - function testSetBasePriceMultiplier_modifierInPosition() public { - uint newBaseMultiplier = 1; - - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bondingCurveFundingManager), - bondingCurveFundingManager.RISK_MANAGER_ROLE() - ), - seller - ) - ); - bondingCurveFundingManager.setBasePriceMultiplier(newBaseMultiplier); - } - } - - // ------------------------------------------------------------------------- - // OnlyOrchestratorAdmin Functions - - /* Test: setTokenVault() modifier in position - └── Given: the caller_ is not the OrchestratorAdmin - └── When: the function setTokenVault() gets called - └── Then: it should revert - └── Given: the caller_ is the OrchestratorAdmin - └── When: the function setTokenVault() gets called - └── Then: it should change the _tokenVault address to the given address - */ - function testSetTokenVault_ModifierInPosition() public { - // onlyOrchestratorAdmin - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0) - ) - ); - vm.prank(address(0)); - bondingCurveFundingManager.setTokenVault(address(0)); - } - - function testSetTokenVault_worksGivenCallerIsOrchestratorAdmin( - address _tokenVault - ) public { - vm.assume(_tokenVault != address(0)); - vm.assume(_tokenVault != address(bondingCurveFundingManager)); - bondingCurveFundingManager.setTokenVault(_tokenVault); - - assertEq(_tokenVault, bondingCurveFundingManager.getTokenVault()); - } + // Mutating - Out of Order /* Test: withdrawProjectCollateralFee - └── Given: the caller_ is not the OrchestratorAdmin - └── When: the function withdrawProjectCollateralFee() gets called - └── Then: it should revert - └── Given: the caller_ is the OrchestratorAdmin - └── When: the function withdrawProjectCollateralFee() gets called - └── Then: it should revert - */ - - function testWithdrawProjectCollateralFee_ModifierInPosition() public { - // onlyOrchestratorAdmin - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0) - ) - ); - vm.prank(address(0)); - bondingCurveFundingManager.withdrawProjectCollateralFee(address(0), 0); - } + └── When: the function withdrawProjectCollateralFee() gets called + └── Then: it should revert - function testWithdrawProjectCollateralFee_revertsGivenCallerIsOrchestratorAdmin( - ) public { + */ + function testWithdrawProjectCollateralFee_reverts() public { vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidFunctionality + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidFunctionality .selector ) ); @@ -1547,8 +1235,8 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is // Execute Tx vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 - .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1__InvalidSeize + IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 + .FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2__InvalidSeize .selector, seize_ ) @@ -1567,7 +1255,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1 + emit IFM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v2 .SeizeChanged(currentSeize, seize_); bondingCurveFundingManager.adjustSeize(seize_); @@ -1585,12 +1273,12 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is */ function testSetTokenVault_revertGivenAddressIsZero() public { - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); bondingCurveFundingManager.exposed_setTokenVault(address(0)); } function testSetTokenVault_revertGivenAddressIsSameContract() public { - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); bondingCurveFundingManager.exposed_setTokenVault( address(bondingCurveFundingManager) ); @@ -1784,7 +1472,7 @@ contract FM_BC_BondingSurface_Redeeming_Restricted_Repayer_Seizable_v1_Test is _token.mint(address(bondingCurveFundingManager), amount_); vm.expectEmit(true, true, true, true); - emit IBondingCurveBase_v1.ProjectCollateralFeeWithdrawn( + emit IBondingCurveBase_v2.ProjectCollateralFeeWithdrawn( tokenVault, amount_ ); diff --git a/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.t.sol similarity index 89% rename from test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.t.sol rename to test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.t.sol index 970532e31..f5a879daf 100644 --- a/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.t.sol +++ b/test/unit/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.t.sol @@ -5,15 +5,15 @@ import "forge-std/console.sol"; // SuT import { - IFM_BC_BondingSurface_Redeeming_v1, - FM_BC_BondingSurface_Redeeming_v1, - IBondingCurveBase_v1 -} from "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v1.sol"; + IFM_BC_BondingSurface_Redeeming_v2, + FM_BC_BondingSurface_Redeeming_v2, + IBondingCurveBase_v2 +} from "@fm/bondingCurve/FM_BC_BondingSurface_Redeeming_v2.sol"; import { - IFM_BC_BondingSurface_Redeeming_v1, + IFM_BC_BondingSurface_Redeeming_v2, IFundingManager_v1 -} from "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; +} from "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; // External Libraries import {Clones} from "@oz/proxy/Clones.sol"; @@ -24,22 +24,22 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {BondingSurface} from "@fm/bondingCurve/formulas/BondingSurface.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {IRedeemingBondingCurveBase_v1} from - "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; +import {IRedeemingBondingCurveBase_v2} from + "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; import {IBondingSurface} from "@fm/bondingCurve/interfaces/IBondingSurface.sol"; -import {IFM_BC_BondingSurface_Redeeming_v1} from - "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v1.sol"; +import {IFM_BC_BondingSurface_Redeeming_v2} from + "@fm/bondingCurve/interfaces/IFM_BC_BondingSurface_Redeeming_v2.sol"; import {IRepayer_v1} from "@fm/bondingCurve/interfaces/IRepayer_v1.sol"; import {FixedPointMathLib} from "src/modules/lib/FixedPointMathLib.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; @@ -47,7 +47,7 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; import {FM_BC_BondingSurface_RedeemingV1_Exposed} from "@mocks/modules/fundingManager/bondingCurve/FM_BC_BondingSurface_RedeemingV1_Exposed.sol"; -contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { +contract FM_BC_BondingSurface_Redeeming_v2_Test is ModuleTest { string private constant NAME = "Bonding Surface Token"; string private constant SYMBOL = "BST"; uint8 internal constant DECIMALS = 18; @@ -66,7 +66,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { FM_BC_BondingSurface_RedeemingV1_Exposed bondingCurveFundingManager; address formula; ERC20Issuance_v1 issuanceToken; - ERC20PaymentClientBaseV2Mock _erc20PaymentClientMock; + ERC20PaymentClientBase_v3_Mock _erc20PaymentClientMock; // Addresses address owner_address = address(0xA1BA); @@ -80,7 +80,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { issuanceToken = new ERC20Issuance_v1(NAME, SYMBOL, DECIMALS, MAX_SUPPLY); issuanceToken.setMinter(address(this), true); - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties; // Deploy formula and cast to address for encoding @@ -104,7 +104,9 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { FM_BC_BondingSurface_RedeemingV1_Exposed(Clones.clone(impl)); _setUpOrchestrator(bondingCurveFundingManager); - _authorizer.setIsAuthorized(address(this), true); + + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); // Set Minter issuanceToken.setMinter(address(bondingCurveFundingManager), true); @@ -207,9 +209,9 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { */ function testInitFailsForInvalidFormula() public { - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties; - bc_properties.formula = address(new FM_BC_BondingSurface_Redeeming_v1()); + bc_properties.formula = address(new FM_BC_BondingSurface_Redeeming_v2()); address impl = address(new FM_BC_BondingSurface_RedeemingV1_Exposed()); @@ -218,8 +220,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidBondingSurfaceFormula + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidBondingSurfaceFormula .selector ) ); @@ -250,7 +252,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { _token.setDecimals(decimals_); // Setup bondingCurve properties - IFM_BC_BondingSurface_Redeeming_v1.BondingCurveProperties memory + IFM_BC_BondingSurface_Redeeming_v2.BondingCurveProperties memory bc_properties; // Deploy formula and cast to address for encoding @@ -294,10 +296,10 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // ------------------------------------------------------------------------- // Tests: Supports Interface - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bondingCurveFundingManager.supportsInterface( - type(IFM_BC_BondingSurface_Redeeming_v1).interfaceId + type(IFM_BC_BondingSurface_Redeeming_v2).interfaceId ) ); assertTrue( @@ -411,50 +413,45 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // OnlyOrchestratorAdmin Functions /* Test setCapitalRequired() - ├── Given: the caller_ is not the OrchestratorAdmin + ├── Given: the caller_ is permissioned │ └── When: the function setCapitalRequired() is called - │ └── Then: it should revert + │ └── Then: it should revert (modifier in position test) ├── Given: the amount is invalid │ └── When: the function setCapitalRequired() is called - │ └── Then: it should revert - └── Given: the caller_ is the OrchestratorAdmin + │ └── Then: it should revert + └── Given: the caller_ is permissioned └── When: the function setCapitalRequired() is called └── Then: it should call the internal function and set the state */ - function testSetCapitalRequired_revertGivenCallerHasNotRiskManagerRole() - public - { - uint newCapitalRequired = 1 ether; + function testSetCapitalRequired_ModifierInPosition() public { + // permissioned - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - seller - ) - ); - bondingCurveFundingManager.setCapitalRequired(newCapitalRequired); - } + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setCapitalRequired(0); } function testSetCapitalRequired_revertGivenAmountIsInvalid() public { vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount .selector ) ); bondingCurveFundingManager.setCapitalRequired(0); vm.expectRevert( abi.encodeWithSelector( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount .selector ) ); @@ -481,31 +478,26 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { } /* Test setBaseMultiplier() - ├── Given: the caller_ is not the OrchestratorAdmin + ├── Given: the caller_ is not permissioned │ └── When: the function setBaseMultiplier() is called - │ └── Then: it should revert - └── Given: the caller_ is the OrchestratorAdmin + │ └── Then: it should revert (modifier in position test) + └── Given: the caller_ is permissioned └── When: the function setBaseMultiplier() is called └── Then: it should call the internal function and set the state */ - function testSetBaseMultiplier_revertGivenCallerHasNotRiskManagerRole() - public - { - uint newBaseMultiplier = 1; + function testSetBaseMultiplier_modifierInPosition() public { + // permissioned - // Execute Tx - vm.startPrank(seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - seller - ) - ); - bondingCurveFundingManager.setBasePriceMultiplier(newBaseMultiplier); - } + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setBasePriceMultiplier(0); } function testSetBaseMultiplier_worksGivenCallerHasRiskManagerRole( @@ -531,7 +523,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // OnlyPaymentClient Functions /* Test transferOrchestratorToken - ├── given the onlyPaymentClient modifier is set (individual modifier tests are done in Module_v1.t.sol) + ├── given the onlyPaymentClient modifier is set (individual modifier tests are done in Module_v2.t.sol) │ └── and the conditions of the modifier are not met │ └── when the function transferOrchestratorToken() gets called │ └── then it should revert @@ -542,7 +534,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { │ └── then it should revert ├── and FM collateral token balance < MIN_RESERVE │ └── when the function transferOrchestratorToken() gets called - │ └── then it should revert with FM_BC_BondingSurface_Redeeming_v1__MinReserveReached + │ └── then it should revert with FM_BC_BondingSurface_Redeeming_v2__MinReserveReached └── and the FM has enough collateral token for amount to be transferred when the function transferOrchestratorToken() gets called └── then is should send the funds to the specified address @@ -554,10 +546,10 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { address to_, uint amount_ ) public { - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); vm.prank(caller_); - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); bondingCurveFundingManager.transferOrchestratorToken(to_, amount_); } @@ -589,7 +581,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { ); // Add logic module to workflow to pass modifier - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(_erc20PaymentClientMock)); vm.startPrank(address(_erc20PaymentClientMock)); { @@ -622,12 +614,12 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { ); // Add logic module to workflow to pass modifier - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(_erc20PaymentClientMock)); vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__MinReserveReached + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__MinReserveReached .selector ); vm.prank(address(_erc20PaymentClientMock)); @@ -652,7 +644,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { ); // Add logic module to workflow to pass modifier - _erc20PaymentClientMock = new ERC20PaymentClientBaseV2Mock(); + _erc20PaymentClientMock = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(_erc20PaymentClientMock)); vm.startPrank(address(_erc20PaymentClientMock)); { @@ -692,8 +684,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // Execute Tx vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__NoCapitalAvailable + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__NoCapitalAvailable .selector ); bondingCurveFundingManager.exposed_issueTokensFormulaWrapper( @@ -732,8 +724,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { │ └── Then: it should revert ├── Given: (capitalAvailable - redeemAmount) < MIN_RESERVE │ └── When: the function _redeemTokensFormulaWrapper() gets called - │ └── Then: it should revert with FM_BC_BondingSurface_Redeeming_v1__MinReserveReached - │ └── Then: it should revert with FM_BC_BondingSurface_Redeeming_v1__MinReserveReached + │ └── Then: it should revert with FM_BC_BondingSurface_Redeeming_v2__MinReserveReached + │ └── Then: it should revert with FM_BC_BondingSurface_Redeeming_v2__MinReserveReached └── Given: (capitalAvailable - redeemAmount) >= MIN_RESERVE └── When: the function _redeemTokensFormulaWrapper() gets called └── Then: it should return redeemAmount @@ -749,8 +741,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // Execute Tx vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__NoCapitalAvailable + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__NoCapitalAvailable .selector ); bondingCurveFundingManager.exposed_redeemTokensFormulaWrapper( @@ -780,8 +772,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { // Execute Tx vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__MinReserveReached + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__MinReserveReached .selector ); @@ -937,8 +929,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { uint capitalRequirements = 0; // Expect Revert vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount .selector ); bondingCurveFundingManager.exposed_setBasePriceMultiplier( @@ -959,7 +951,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IFM_BC_BondingSurface_Redeeming_v1.CapitalRequiredChanged( + emit IFM_BC_BondingSurface_Redeeming_v2.CapitalRequiredChanged( currentCapitalRequirements, capitalRequirements_ ); bondingCurveFundingManager.exposed_setCapitalRequired( @@ -1001,8 +993,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { uint basePriceMultiplier = 0; // Expect Revert vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount .selector ); bondingCurveFundingManager.exposed_setBasePriceMultiplier( @@ -1028,7 +1020,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IFM_BC_BondingSurface_Redeeming_v1.BasePriceMultiplierChanged( + emit IFM_BC_BondingSurface_Redeeming_v2.BasePriceMultiplierChanged( currentBasePriceMultiplier, basePriceMultiplier_ ); bondingCurveFundingManager.exposed_setBasePriceMultiplier( @@ -1068,8 +1060,8 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { basePriceMultiplier_ = bound(basePriceMultiplier_, 1e37, 1e38); // Higher minimum bound vm.expectRevert( - IFM_BC_BondingSurface_Redeeming_v1 - .FM_BC_BondingSurface_Redeeming_v1__InvalidInputAmount + IFM_BC_BondingSurface_Redeeming_v2 + .FM_BC_BondingSurface_Redeeming_v2__InvalidInputAmount .selector ); bondingCurveFundingManager.exposed_calculateBasePriceToCapitalRatio( @@ -1129,7 +1121,7 @@ contract FM_BC_BondingSurface_Redeeming_v1_Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IFM_BC_BondingSurface_Redeeming_v1.BasePriceToCapitalRatioChanged( + emit IFM_BC_BondingSurface_Redeeming_v2.BasePriceToCapitalRatioChanged( currentBasePriceToCapitalRatio, expectedReturnValue ); bondingCurveFundingManager.exposed_updateVariables(); diff --git a/test/unit/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.t.sol deleted file mode 100644 index cb24b5209..000000000 --- a/test/unit/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.t.sol +++ /dev/null @@ -1,352 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import "forge-std/console.sol"; - -// SuT -import {FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1} from - "@fm/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupply_v1.sol"; - -import {Clones} from "@oz/proxy/Clones.sol"; - -import {IERC165} from "@oz/utils/introspection/IERC165.sol"; -import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; - -import { - IFM_BC_Bancor_Redeeming_VirtualSupply_v1, - FM_BC_Bancor_Redeeming_VirtualSupply_v1, - IFundingManager_v1 -} from "@fm/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.sol"; -import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; - -import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; - -import { - FM_BC_Bancor_Redeeming_VirtualSupplyV1Test, - ModuleTest, - IModule_v1 -} from - "@unitTest/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupply_v1.t.sol"; -import {FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock} from - "@mocks/modules/fundingManager/bondingCurve/FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock.sol"; -import {FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock} from - "@mocks/modules/fundingManager/bondingCurve/FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock.sol"; -import {OZErrors} from "@testUtilities/OZErrors.sol"; - -contract FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1UpstreamTests is - FM_BC_Bancor_Redeeming_VirtualSupplyV1Test -{ - function setUp() public override { - // Deploy contracts - issuanceToken = new ERC20Issuance_v1(NAME, SYMBOL, DECIMALS, MAX_SUPPLY); - issuanceToken.setMinter(address(this), true); - - BancorFormula bancorFormula = new BancorFormula(); - formula = address(bancorFormula); - - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties memory - bc_properties; - - bc_properties.formula = formula; - bc_properties.reserveRatioForBuying = RESERVE_RATIO_FOR_BUYING; - bc_properties.reserveRatioForSelling = RESERVE_RATIO_FOR_SELLING; - bc_properties.buyFee = BUY_FEE; - bc_properties.sellFee = SELL_FEE; - bc_properties.buyIsOpen = BUY_IS_OPEN; - bc_properties.sellIsOpen = SELL_IS_OPEN; - bc_properties.initialIssuanceSupply = INITIAL_ISSUANCE_SUPPLY; - bc_properties.initialCollateralSupply = INITIAL_COLLATERAL_SUPPLY; - - address impl = - address(new FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock()); - - bondingCurveFundingManager = - FM_BC_Bancor_Redeeming_VirtualSupplyV1Mock(Clones.clone(impl)); - - _setUpOrchestrator(bondingCurveFundingManager); - - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); - - // Init Module - bondingCurveFundingManager.init( - _orchestrator, - _METADATA, - abi.encode( - address(issuanceToken), - bc_properties, - _token // fetching from ModuleTest.sol (specifically after the _setUpOrchestrator function call) - ) - ); - - // we grant minting rights to the bonding curve - issuanceToken.setMinter(address(bondingCurveFundingManager), true); - - // Grant necessary roles for the Upstream tests to pass - - bytes32 CURVE_INTERACTION_ROLE = "CURVE_USER"; - address buyer = makeAddr("buyer"); - address seller = makeAddr("seller"); - - address[] memory targets = new address[](3); - targets[0] = buyer; - targets[1] = seller; - targets[2] = admin_address; - - bondingCurveFundingManager.grantModuleRoleBatched( - CURVE_INTERACTION_ROLE, targets - ); - } - - function testTransferOrchestratorToken_FailsGivenNotEnoughCollateralInFM( - address to, - uint amount, - uint projectCollateralFeeCollected - ) public override { - // Temp test override, as in dev branch we have already removed the restriction - // to call the transferOrchestratorToken() function - } -} - -contract FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Tests is - ModuleTest -{ - string internal constant NAME = "Bonding Curve Token"; - string internal constant SYMBOL = "BCT"; - uint8 internal constant DECIMALS = 18; - uint internal constant MAX_SUPPLY = type(uint).max; - - uint internal constant INITIAL_ISSUANCE_SUPPLY = 10; - uint internal constant INITIAL_COLLATERAL_SUPPLY = 30; - uint32 internal constant RESERVE_RATIO_FOR_BUYING = 333_333; - uint32 internal constant RESERVE_RATIO_FOR_SELLING = 333_333; - uint internal constant BUY_FEE = 0; - uint internal constant SELL_FEE = 0; - bool internal constant BUY_IS_OPEN = true; - bool internal constant SELL_IS_OPEN = true; - - FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock - bondingCurveFundingManager; - address formula; - - ERC20Issuance_v1 issuanceToken; - - address admin_address = address(0xA1BA); - address non_admin_address = address(0xB0B); - - function setUp() public { - // Deploy contracts - issuanceToken = new ERC20Issuance_v1(NAME, SYMBOL, DECIMALS, MAX_SUPPLY); - issuanceToken.setMinter(address(this), true); - - BancorFormula bancorFormula = new BancorFormula(); - formula = address(bancorFormula); - - IFM_BC_Bancor_Redeeming_VirtualSupply_v1.BondingCurveProperties memory - bc_properties; - - bc_properties.formula = formula; - bc_properties.reserveRatioForBuying = RESERVE_RATIO_FOR_BUYING; - bc_properties.reserveRatioForSelling = RESERVE_RATIO_FOR_SELLING; - bc_properties.buyFee = BUY_FEE; - bc_properties.sellFee = SELL_FEE; - bc_properties.buyIsOpen = BUY_IS_OPEN; - bc_properties.sellIsOpen = SELL_IS_OPEN; - bc_properties.initialIssuanceSupply = INITIAL_ISSUANCE_SUPPLY; - bc_properties.initialCollateralSupply = INITIAL_COLLATERAL_SUPPLY; - - address impl = - address(new FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock()); - - bondingCurveFundingManager = - FM_BC_Restricted_Bancor_Redeeming_VirtualSupplyV1Mock( - Clones.clone(impl) - ); - - _setUpOrchestrator(bondingCurveFundingManager); - - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); - - // Init Module - bondingCurveFundingManager.init( - _orchestrator, - _METADATA, - abi.encode( - address(issuanceToken), - bc_properties, - _token // fetching from ModuleTest.sol (specifically after the _setUpOrchestrator function call) - ) - ); - - // we grant minting rights to the bonding curve - issuanceToken.setMinter(address(bondingCurveFundingManager), true); - - // Since we tested the success case in the Upstream tests, we now only need to verify revert on unauthorized calls - } - - function testInit() public override { - assertEq( - issuanceToken.name(), - string(abi.encodePacked(NAME)), - "Name has not been set correctly" - ); - assertEq( - issuanceToken.symbol(), - string(abi.encodePacked(SYMBOL)), - "Symbol has not been set correctly" - ); - assertEq( - issuanceToken.decimals(), - DECIMALS, - "Decimals has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.call_collateralTokenDecimals(), - _token.decimals(), - "Collateral token decimals has not been set correctly" - ); - assertEq( - address(bondingCurveFundingManager.formula()), - formula, - "Formula has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.getVirtualIssuanceSupply(), - INITIAL_ISSUANCE_SUPPLY, - "Virtual token supply has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.getVirtualCollateralSupply(), - INITIAL_COLLATERAL_SUPPLY, - "Virtual collateral supply has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.call_reserveRatioForBuying(), - RESERVE_RATIO_FOR_BUYING, - "Reserve ratio for buying has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.call_reserveRatioForSelling(), - RESERVE_RATIO_FOR_SELLING, - "Reserve ratio for selling has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.buyFee(), - BUY_FEE, - "Buy fee has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.buyIsOpen(), - BUY_IS_OPEN, - "Buy-is-open has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.buyFee(), - SELL_FEE, - "Sell fee has not been set correctly" - ); - assertEq( - bondingCurveFundingManager.buyIsOpen(), - SELL_IS_OPEN, - "Sell-is-open has not been set correctly" - ); - } - - function testReinitFails() public override { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - bondingCurveFundingManager.init(_orchestrator, _METADATA, abi.encode()); - } - - function testBuyFor_FailsIfCallerNotAuthorized() public { - address _buyer = makeAddr("buyer"); - address _receiver = makeAddr("receiver"); - uint _depositAmount = 1; - - bytes32 _roleId = _authorizer.generateRoleId( - address(bondingCurveFundingManager), "CURVE_USER" - ); - - vm.startPrank(_buyer); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _roleId, - _buyer - ) - ); - bondingCurveFundingManager.buyFor( - _receiver, _depositAmount, _depositAmount - ); - } - } - - function testBuy_FailsIfCallerNotAuthorized() public { - address _buyer = makeAddr("buyer"); - uint _depositAmount = 1; - - bytes32 _roleId = _authorizer.generateRoleId( - address(bondingCurveFundingManager), "CURVE_USER" - ); - - vm.startPrank(_buyer); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _roleId, - _buyer - ) - ); - bondingCurveFundingManager.buy(_depositAmount, _depositAmount); - } - } - - function testsellTo_FailsIfCallerNotAuthorized() public { - address _seller = makeAddr("seller"); - address _receiver = makeAddr("receiver"); - uint _sellAmount = 1; - - bytes32 _roleId = _authorizer.generateRoleId( - address(bondingCurveFundingManager), "CURVE_USER" - ); - - vm.startPrank(_seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _roleId, - _seller - ) - ); - bondingCurveFundingManager.sellTo( - _receiver, _sellAmount, _sellAmount - ); - } - } - - function testSell_FailsIfCallerNotAuthorized() public { - address _seller = makeAddr("seller"); - uint _sellAmount = 1; - - bytes32 _roleId = _authorizer.generateRoleId( - address(bondingCurveFundingManager), "CURVE_USER" - ); - - vm.startPrank(_seller); - { - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _roleId, - _seller - ) - ); - bondingCurveFundingManager.sell(_sellAmount, _sellAmount); - } - } -} diff --git a/test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.t.sol similarity index 84% rename from test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.t.sol rename to test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.t.sol index 8f2301aa1..5d6207b91 100644 --- a/test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v1.t.sol +++ b/test/unit/modules/fundingManager/bondingCurve/abstracts/BondingCurveBase_v2.t.sol @@ -14,8 +14,8 @@ import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; @@ -25,14 +25,14 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // Mocks import { BondingCurveBaseV1Mock, - IBondingCurveBase_v1 + IBondingCurveBase_v2 } from "@mocks/modules/fundingManager/bondingCurve/abstracts/BondingCurveBaseV1Mock.sol"; import {IssuanceTokenWrapperV1Mock} from "@mocks/modules/fundingManager/bondingCurve/abstracts/IssuanceTokenWrapperV1Mock.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -contract BondingCurveBaseV1Test is ModuleTest { +contract BondingCurveBaseV2Test is ModuleTest { string private constant NAME = "Bonding Curve Token"; string private constant SYMBOL = "BCT"; uint8 private constant DECIMALS = 18; @@ -47,22 +47,6 @@ contract BondingCurveBaseV1Test is ModuleTest { address admin_address = makeAddr("alice"); address non_admin_address = makeAddr("bob"); - event BuyingEnabled(); - event BuyingDisabled(); - event BuyFeeUpdated(uint newBuyFee, uint oldBuyFee); - event TokensBought( - address indexed receiver, - uint depositAmount, - uint receivedAmount, - address buyer - ); - event IssuanceTokenSet(address indexed token, uint8 decimals); - - event ProtocolFeeMinted( - address indexed token, address indexed treasury, uint feeAmount - ); - event ProjectCollateralFeeWithdrawn(address receiver, uint amount); - function setUp() public { // Deploy contracts address impl = address(new BondingCurveBaseV1Mock()); @@ -78,7 +62,8 @@ contract BondingCurveBaseV1Test is ModuleTest { issuanceToken.setMinter(address(this), true); _setUpOrchestrator(bondingCurveFundingManager); - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); // Set max fee of feeManager to 100% for testing purposes vm.prank(address(governor)); @@ -92,10 +77,10 @@ contract BondingCurveBaseV1Test is ModuleTest { ); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bondingCurveFundingManager.supportsInterface( - type(IBondingCurveBase_v1).interfaceId + type(IBondingCurveBase_v2).interfaceId ) ); } @@ -165,7 +150,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.prank(non_admin_address); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__BuyingFunctionaltiesClosed .selector ); @@ -185,7 +170,7 @@ contract BondingCurveBaseV1Test is ModuleTest { // Test for address(0) vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); @@ -193,7 +178,7 @@ contract BondingCurveBaseV1Test is ModuleTest { // Test for its own address) vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); @@ -204,7 +189,105 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.stopPrank(); } - /* Test buy and _buyOrder function + /* + Test: buyFor Modifier Checks + ├── Given: buyer is not permissioned + │ └── When: buyFor is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is not enabled + │ └── When: buyFor is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is enabled + └── And: receiver is invalid + └── When: buyFor is called + └── Then: it should revert (modifier in position check) + */ + + function testBuyFor_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeBuy(); + + vm.expectRevert( + IBondingCurveBase_v2 + .Module__BondingCurveBase__BuyingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + + // Open up Buy again + bondingCurveFundingManager.openBuy(); + + // validReceiver + vm.expectRevert( + abi.encodeWithSelector( + IBondingCurveBase_v2 + .Module__BondingCurveBase__InvalidRecipient + .selector + ) + ); + bondingCurveFundingManager.buyFor(address(0), 0, 0); + } + + /* + Test: buy Modifier Checks + ├── Given: buyer is not permissioned + │ └── When: buy is called + │ └── Then: it should revert (modifier in position check) + ├── Given: buyer is permissioned + ├── And: buying is not enabled + └── When: buy is called + └── Then: it should revert (modifier in position check) + */ + + function testBuy_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.buy(0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeBuy(); + + vm.expectRevert( + IBondingCurveBase_v2 + .Module__BondingCurveBase__BuyingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.buy(0, 0); + } + + /* Test _buyOrder function ├── when the deposit amount is 0 │ └── it should revert └── when the deposit amount is not 0 @@ -228,7 +311,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.startPrank(non_admin_address); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); @@ -248,7 +331,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.startPrank(buyer); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InsufficientOutputAmount .selector ); @@ -270,7 +353,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensBought(buyer, amount, amount, buyer); + emit IBondingCurveBase_v2.TokensBought(buyer, amount, amount, buyer); // Execution vm.prank(buyer); @@ -364,7 +447,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IBondingCurveBase_v1.ProjectCollateralFeeAdded( + emit IBondingCurveBase_v2.ProjectCollateralFeeAdded( projectCollateralFeeAmount ); } @@ -373,7 +456,9 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensBought(buyer, amount, finalAmount, buyer); // since the fee gets taken before interacting with the bonding curve, we expect the event to already have the fee substracted + emit IBondingCurveBase_v2.TokensBought( + buyer, amount, finalAmount, buyer + ); // since the fee gets taken before interacting with the bonding curve, we expect the event to already have the fee substracted // Execution vm.prank(buyer); @@ -473,7 +558,7 @@ contract BondingCurveBaseV1Test is ModuleTest { address _treasury = address(0); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); @@ -514,7 +599,9 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit ProtocolFeeMinted(address(issuanceToken), treasury, _feeAmount); + emit IBondingCurveBase_v2.ProtocolFeeMinted( + address(issuanceToken), treasury, _feeAmount + ); // Function call bondingCurveFundingManager.call_processProtocolFeeViaMinting( treasury, _feeAmount @@ -547,7 +634,7 @@ contract BondingCurveBaseV1Test is ModuleTest { address _treasury = address(0); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); @@ -589,7 +676,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(_token), treasury, _feeAmount ); // Function call @@ -649,7 +736,7 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.assume(protocolFee + workflowFee >= _bps); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__FeeAmountToHigh .selector ); @@ -665,7 +752,7 @@ contract BondingCurveBaseV1Test is ModuleTest { uint totalAmount = 100; vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__TradeAmountTooLow .selector ); @@ -681,7 +768,7 @@ contract BondingCurveBaseV1Test is ModuleTest { uint totalAmount = 100; vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__TradeAmountTooLow .selector ); @@ -761,9 +848,9 @@ contract BondingCurveBaseV1Test is ModuleTest { } /* Test openBuy and _openBuy function - ├── when caller is not the Orchestrator_v1 admin - │ └── it should revert (tested in base Module modifier tests) - └── when caller is the Orchestrator_v1 admin + ├── when caller is not permissioned + │ └── it should revert (modifier in position) + └── when caller is the Orchestrator_v2 admin └── when buy functionality is already open │ └── it should stay as is │ └── it should emit an event @@ -771,18 +858,33 @@ contract BondingCurveBaseV1Test is ModuleTest { └── it should open the buy functionality └── it should emit an event */ - function testOpenBuy_Idempotence() public callerIsOrchestratorAdmin { + + function testOpenBuy_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.openBuy(); + } + + function testOpenBuy_Idempotence() public { assertEq(bondingCurveFundingManager.buyIsOpen(), true); vm.expectEmit(address(bondingCurveFundingManager)); - emit BuyingEnabled(); + emit IBondingCurveBase_v2.BuyingEnabled(); bondingCurveFundingManager.openBuy(); assertEq(bondingCurveFundingManager.buyIsOpen(), true); } - function testOpenBuy() public callerIsOrchestratorAdmin { + function testOpenBuy() public { assertEq(bondingCurveFundingManager.buyIsOpen(), true); bondingCurveFundingManager.closeBuy(); @@ -790,7 +892,7 @@ contract BondingCurveBaseV1Test is ModuleTest { assertEq(bondingCurveFundingManager.buyIsOpen(), false); vm.expectEmit(address(bondingCurveFundingManager)); - emit BuyingEnabled(); + emit IBondingCurveBase_v2.BuyingEnabled(); bondingCurveFundingManager.openBuy(); @@ -798,9 +900,9 @@ contract BondingCurveBaseV1Test is ModuleTest { } /* Test closeBuy and _closeBuy function - ├── when caller is not the Orchestrator_v1 admin - │ └── it should revert (tested in base Module tests) - └── when caller is the Orchestrator_v1 admin + ├── when caller is not permissioned + │ └── it should revert (modifier in position check) + └── when caller is permissioned └── when buy functionality is already closed │ └── it should stay as is │ └── it should emit an event @@ -808,30 +910,41 @@ contract BondingCurveBaseV1Test is ModuleTest { ├── it should close the buy functionality └── it should emit an event */ - function testCloseBuy_FailsIfAlreadyClosed() - public - callerIsOrchestratorAdmin - { + function testCloseBuy_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.closeBuy(); + } + + function testCloseBuy_FailsIfAlreadyClosed() public { vm.expectEmit(address(bondingCurveFundingManager)); - emit BuyingDisabled(); + emit IBondingCurveBase_v2.BuyingDisabled(); bondingCurveFundingManager.closeBuy(); assertEq(bondingCurveFundingManager.buyIsOpen(), false); vm.expectEmit(address(bondingCurveFundingManager)); - emit BuyingDisabled(); + emit IBondingCurveBase_v2.BuyingDisabled(); bondingCurveFundingManager.closeBuy(); assertEq(bondingCurveFundingManager.buyIsOpen(), false); } - function testCloseBuy() public callerIsOrchestratorAdmin { + function testCloseBuy() public { assertEq(bondingCurveFundingManager.buyIsOpen(), true); vm.expectEmit(address(bondingCurveFundingManager)); - emit BuyingDisabled(); + emit IBondingCurveBase_v2.BuyingDisabled(); bondingCurveFundingManager.closeBuy(); @@ -839,9 +952,9 @@ contract BondingCurveBaseV1Test is ModuleTest { } /* Test setBuyFee and _setBuyFee function - ├── when caller is not the Orchestrator_v1 admin - │ └── it should revert (tested in base Module tests) - └── when caller is the Orchestrator_v1 admin + ├── when caller is not permissioned + │ └── it should revert (modifier in postion) + └── when caller is permissioned └── when fee is over 100% │ └── it should revert ├── when fee is 100% @@ -850,26 +963,37 @@ contract BondingCurveBaseV1Test is ModuleTest { ├── it should set the new fee └── it should emit an event */ - function testSetBuyFee_FailsIfFee100PercentOrMore(uint _fee) - public - callerIsOrchestratorAdmin - { + function testSetBuyFee_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setBuyFee(0); + } + + function testSetBuyFee_FailsIfFee100PercentOrMore(uint _fee) public { vm.assume(_fee > bondingCurveFundingManager.call_BPS()); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidFeePercentage .selector ); bondingCurveFundingManager.setBuyFee(_fee); } - function testSetBuyFee(uint newFee) public callerIsOrchestratorAdmin { + function testSetBuyFee(uint newFee) public { vm.assume(newFee < bondingCurveFundingManager.call_BPS()); vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit BuyFeeUpdated(newFee, BUY_FEE); + emit IBondingCurveBase_v2.BuyFeeUpdated(newFee, BUY_FEE); bondingCurveFundingManager.setBuyFee(newFee); @@ -942,7 +1066,9 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IssuanceTokenSet(address(newIssuanceToken), _newDecimals); + emit IBondingCurveBase_v2.IssuanceTokenSet( + address(newIssuanceToken), _newDecimals + ); bondingCurveFundingManager.call_setIssuanceToken( address(newIssuanceToken) ); @@ -973,7 +1099,7 @@ contract BondingCurveBaseV1Test is ModuleTest { uint depositAmount = 0; vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); @@ -1067,35 +1193,40 @@ contract BondingCurveBaseV1Test is ModuleTest { assertEq(internalFunctionReturnValue, functionReturnValue); } - /* Test withdrawProjectCollateralFee function - └── Given the receiver is address zero or equal to bonding curve address - └── When the function withdrawProjectCollateralFee function gets called - └── Then it should revert with invalid receiver + /* + Test: withdrawProjectCollateralFee modifier in postion + ├── Given: buyer is not permissioned + │ └── When: withdrawProjectCollateralFee is called + │ └── Then: it should revert (modifier in position check) + ├── Given: the receiver is address zero or equal to bonding curve address + └── And: buyer is permissioned + └── When: withdrawProjectCollateralFee is called + └── Then: it should revert (modifier in position check) */ - function testWithdrawProjectCollateralFee_revertGivenInvalidReceiver( - uint _amount - ) public { - address receiver = address(0); + function testWithdrawProjectCollateralFee_ModifierInPostion() public { + // permissioned + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( - IBondingCurveBase_v1 - .Module__BondingCurveBase__InvalidRecipient - .selector - ); - bondingCurveFundingManager.withdrawProjectCollateralFee( - receiver, _amount + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.withdrawProjectCollateralFee(address(0), 0); - receiver = address(bondingCurveFundingManager); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // validReceiver vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidRecipient .selector ); - bondingCurveFundingManager.withdrawProjectCollateralFee( - receiver, _amount - ); + bondingCurveFundingManager.withdrawProjectCollateralFee(address(0), 0); } /* Test internal _withdrawProjectCollateralFee function @@ -1121,7 +1252,7 @@ contract BondingCurveBaseV1Test is ModuleTest { ); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidWithdrawAmount .selector ); @@ -1157,7 +1288,9 @@ contract BondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit ProjectCollateralFeeWithdrawn(receiver, _amount); + emit IBondingCurveBase_v2.ProjectCollateralFeeWithdrawn( + receiver, _amount + ); // Execute function bondingCurveFundingManager.call_withdrawProjectCollateralFee( receiver, _amount @@ -1193,7 +1326,7 @@ contract BondingCurveBaseV1Test is ModuleTest { _minAmountOut = bound(_minAmountOut, 1, type(uint128).max); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); @@ -1209,7 +1342,7 @@ contract BondingCurveBaseV1Test is ModuleTest { _depositAmount = bound(_depositAmount, 1, type(uint128).max); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidMinAmountOut .selector ); @@ -1230,7 +1363,7 @@ contract BondingCurveBaseV1Test is ModuleTest { bondingCurveFundingManager.projectCollateralFeeCollected(); vm.expectEmit(true, true, true, true); - emit IBondingCurveBase_v1.ProjectCollateralFeeAdded(_projectFeeAmount); + emit IBondingCurveBase_v2.ProjectCollateralFeeAdded(_projectFeeAmount); // Execute Tx bondingCurveFundingManager.exposed_projectFeeCollected( @@ -1250,13 +1383,6 @@ contract BondingCurveBaseV1Test is ModuleTest { //-------------------------------------------------------------------------- // Helper functions - // Modifier to ensure the caller has the admin role - modifier callerIsOrchestratorAdmin() { - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); - vm.startPrank(admin_address); - _; - } - // Helper function that mints enough collateral tokens to a buyer and approves the bonding curve to spend them function _prepareBuyConditions(address buyer, uint amount) internal { _token.mint(buyer, amount); diff --git a/test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.t.sol b/test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.t.sol similarity index 79% rename from test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.t.sol rename to test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.t.sol index c412b5189..2844c5128 100644 --- a/test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.t.sol +++ b/test/unit/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.t.sol @@ -13,13 +13,13 @@ import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {BancorFormula} from "@fm/bondingCurve/formulas/BancorFormula.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; -import {IBondingCurveBase_v1} from - "@fm/bondingCurve/interfaces/IBondingCurveBase_v1.sol"; +import {IBondingCurveBase_v2} from + "@fm/bondingCurve/interfaces/IBondingCurveBase_v2.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; @@ -28,11 +28,11 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import { RedeemingBondingCurveBaseV1Mock, - IRedeemingBondingCurveBase_v1 + IRedeemingBondingCurveBase_v2 } from "@mocks/modules/fundingManager/bondingCurve/abstracts/RedeemingBondingCurveBaseV1Mock.sol"; -contract RedeemingBondingCurveBaseV1Test is ModuleTest { +contract RedeemingBondingCurveBaseV2Test is ModuleTest { string private constant NAME = "Bonding Curve Token"; string private constant SYMBOL = "BCT"; uint8 private constant DECIMALS = 18; @@ -51,16 +51,6 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { address admin_address = address(0xA1BA); address non_admin_address = address(0xB0B); - event SellingEnabled(); - event SellingDisabled(); - event SellFeeUpdated(uint newSellFee, uint oldSellFee); - event TokensSold( - address indexed receiver, - uint depositAmount, - uint receivedAmount, - address seller - ); - function setUp() public { // Deploy contracts address impl = address(new RedeemingBondingCurveBaseV1Mock()); @@ -78,7 +68,8 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { _setUpOrchestrator(bondingCurveFundingManager); - _authorizer.grantRole(_authorizer.getAdminRole(), admin_address); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); // Set max fee of feeManager to 100% for testing purposes vm.prank(address(governor)); @@ -98,10 +89,10 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { ); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bondingCurveFundingManager.supportsInterface( - type(IRedeemingBondingCurveBase_v1).interfaceId + type(IRedeemingBondingCurveBase_v2).interfaceId ) ); } @@ -174,7 +165,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.prank(non_admin_address); vm.expectRevert( - IRedeemingBondingCurveBase_v1 + IRedeemingBondingCurveBase_v2 .Module__RedeemingBondingCurveBase__SellingFunctionaltiesClosed .selector ); @@ -214,6 +205,105 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { assertEq(_token.balanceOf(seller), 0); } + /* + Test: sellTo Modifier Checks + ├── Given: seller is not permissioned + │ └── When: sellTo is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + ├── And: buysellinging is not enabled + │ └── When: sellTo is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + ├── And: selling is enabled + └── And: receiver is invalid + └── When: sellTo is called + └── Then: it should revert (modifier in position check) + */ + + function testsellTo_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeSell(); + + vm.expectRevert( + IRedeemingBondingCurveBase_v2 + .Module__RedeemingBondingCurveBase__SellingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + + // Open up Buy again + bondingCurveFundingManager.openSell(); + + // validReceiver + vm.expectRevert( + abi.encodeWithSelector( + IBondingCurveBase_v2 + .Module__BondingCurveBase__InvalidRecipient + .selector + ) + ); + bondingCurveFundingManager.sellTo(address(0), 0, 0); + } + + /* + Test: sell Modifier Checks + ├── Given: seller is not permissioned + │ └── When: sell is called + │ └── Then: it should revert (modifier in position check) + ├── Given: seller is permissioned + └── And: buysellinging is not enabled + └── When: sell is called + └── Then: it should revert (modifier in position check) + + */ + + function testsell_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.sell(0, 0); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // buyingIsEnabled + + // Close buy to check for + bondingCurveFundingManager.closeSell(); + + vm.expectRevert( + IRedeemingBondingCurveBase_v2 + .Module__RedeemingBondingCurveBase__SellingFunctionaltiesClosed + .selector + ); + bondingCurveFundingManager.sell(0, 0); + } + /* Test sell and _sellOrder function ├── when the sell amount is 0 │ └── it should revert @@ -243,7 +333,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.startPrank(non_admin_address); { vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); @@ -265,7 +355,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.startPrank(seller); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InsufficientOutputAmount .selector ); @@ -299,7 +389,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.startPrank(seller); { vm.expectRevert( - IRedeemingBondingCurveBase_v1 + IRedeemingBondingCurveBase_v2 .Module__RedeemingBondingCurveBase__InsufficientCollateralForProjectFee .selector ); @@ -368,7 +458,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit IBondingCurveBase_v1.ProjectCollateralFeeAdded( + emit IBondingCurveBase_v2.ProjectCollateralFeeAdded( projectCollateralFeeAmount ); } @@ -377,7 +467,9 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, true, true, address(bondingCurveFundingManager) ); - emit TokensSold(seller, amount, finalAmount, seller); + emit IRedeemingBondingCurveBase_v2.TokensSold( + seller, amount, finalAmount, seller + ); // Execution vm.prank(seller); @@ -405,8 +497,8 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { } /* Test openSell function - ├── when caller is not the Orchestrator admin - │ └── it should revert (tested in base Module modifier tests) + ├── when caller is not permissioned + │ └── it should revert (modifier in position check) └── when caller is the Orchestrator admin └── when sell functionality is already open │ └── it should stay as is @@ -415,10 +507,25 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { ├── it should open the sell functionality └── it should emit an event */ + + function testOpenSell_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.openSell(); + } + function testOpenSell_Idempotence() public callerIsOrchestratorAdmin { assertEq(bondingCurveFundingManager.sellIsOpen(), true); vm.expectEmit(address(bondingCurveFundingManager)); - emit SellingEnabled(); + emit IRedeemingBondingCurveBase_v2.SellingEnabled(); bondingCurveFundingManager.openSell(); } @@ -431,7 +538,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { assertEq(bondingCurveFundingManager.sellIsOpen(), false); vm.expectEmit(address(bondingCurveFundingManager)); - emit SellingEnabled(); + emit IRedeemingBondingCurveBase_v2.SellingEnabled(); bondingCurveFundingManager.openSell(); @@ -439,8 +546,8 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { } /* Test closeSell function - ├── when caller is not the Orchestrator admin - │ └── it should revert (tested in base Module tests) + ├── when caller is permissioned + │ └── it should revert (modifier in position check) └── when caller is the Orchestrator admin └── when sell functionality is already closed │ └── it should stay as is @@ -449,6 +556,19 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { ├── it should close the sell functionality └── it should emit an event */ + function testCloseSell_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.closeSell(); + } function testCloseSell_FailsIfAlreadyClosed() public @@ -457,13 +577,13 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { assertEq(bondingCurveFundingManager.sellIsOpen(), true); vm.expectEmit(address(bondingCurveFundingManager)); - emit SellingDisabled(); + emit IRedeemingBondingCurveBase_v2.SellingDisabled(); bondingCurveFundingManager.closeSell(); assertEq(bondingCurveFundingManager.sellIsOpen(), false); vm.expectEmit(address(bondingCurveFundingManager)); - emit SellingDisabled(); + emit IRedeemingBondingCurveBase_v2.SellingDisabled(); bondingCurveFundingManager.closeSell(); assertEq(bondingCurveFundingManager.sellIsOpen(), false); @@ -473,15 +593,15 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { assertEq(bondingCurveFundingManager.sellIsOpen(), true); vm.expectEmit(address(bondingCurveFundingManager)); - emit SellingDisabled(); + emit IRedeemingBondingCurveBase_v2.SellingDisabled(); bondingCurveFundingManager.closeSell(); assertEq(bondingCurveFundingManager.sellIsOpen(), false); } /* Test setSellFee and _setSellFee function - ├── when caller is not the Orchestrator admin - │ └── it should revert (tested in base Module tests) + ├── when caller is not permissioned + │ └── it should revert (modifier in position check) └── when caller is the Orchestrator admin └── when fee is over 100% │ └── it should revert @@ -493,13 +613,27 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { └── it should emit an event? */ + function testSetSellFee_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bondingCurveFundingManager.setSellFee(0); + } + function testSetSellFee_FailsIfFeeIsOver100Percent(uint _fee) public callerIsOrchestratorAdmin { vm.assume(_fee > bondingCurveFundingManager.call_BPS()); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidFeePercentage .selector ); @@ -514,7 +648,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { vm.expectEmit( true, true, false, false, address(bondingCurveFundingManager) ); - emit SellFeeUpdated(_fee, oldSellFee); + emit IRedeemingBondingCurveBase_v2.SellFeeUpdated(_fee, oldSellFee); bondingCurveFundingManager.setSellFee(_fee); assertEq(bondingCurveFundingManager.sellFee(), _fee); @@ -536,7 +670,7 @@ contract RedeemingBondingCurveBaseV1Test is ModuleTest { uint depositAmount = 0; vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); diff --git a/test/unit/modules/fundingManager/depositVault/FM_DepositVault_v1.t.sol b/test/unit/modules/fundingManager/depositVault/FM_DepositVault_v1.t.sol index 4e5b32849..d7a0aff15 100644 --- a/test/unit/modules/fundingManager/depositVault/FM_DepositVault_v1.t.sol +++ b/test/unit/modules/fundingManager/depositVault/FM_DepositVault_v1.t.sol @@ -10,16 +10,16 @@ import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import {IFM_DepositVault_v1} from "@fm/depositVault/interfaces/IFM_DepositVault_v1.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {FM_DepositVault_v1_Exposed} from "@mocks/modules/fundingManager/depositVault/FM_DepositVault_v1_Exposed.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Errors @@ -31,7 +31,7 @@ import {Clones} from "@oz/proxy/Clones.sol"; contract FM_DepositVaultV1Test is ModuleTest { // SuT FM_DepositVault_v1_Exposed vault; - ERC20PaymentClientBaseV2Mock client; + ERC20PaymentClientBase_v3_Mock client; uint internal constant BPS = 10_000; @@ -44,14 +44,14 @@ contract FM_DepositVaultV1Test is ModuleTest { // Init Module vault.init(_orchestrator, _METADATA, abi.encode(address(_token))); - client = new ERC20PaymentClientBaseV2Mock(); + client = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(client)); vm.prank(address(governor)); feeManager.setMaxFee(feeManager.BPS()); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( vault.supportsInterface(type(IFM_DepositVault_v1).interfaceId) ); @@ -109,7 +109,7 @@ contract FM_DepositVaultV1Test is ModuleTest { // Deposit if (expectedFeeAmount != 0) { vm.expectEmit(true, true, true, true); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(_token), feeManager.getDefaultProtocolTreasury(), expectedFeeAmount @@ -144,10 +144,10 @@ contract FM_DepositVaultV1Test is ModuleTest { } function testTransferOrchestratorTokenModifierInPosition() public { - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); vault.transferOrchestratorToken(address(this), 0); - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); vm.prank(address(client)); vault.transferOrchestratorToken(address(0), 0); } @@ -166,7 +166,7 @@ contract FM_DepositVaultV1Test is ModuleTest { assertEq(_token.balanceOf(address(vault)), amount_); vm.expectEmit(true, true, true, true); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(_token), treasury_, amount_ ); diff --git a/test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.t.sol b/test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.t.sol similarity index 77% rename from test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.t.sol rename to test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.t.sol index 56acbc049..fe6f44034 100644 --- a/test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v1.t.sol +++ b/test/unit/modules/fundingManager/extensions/FM_EXT_TokenVault_v2.t.sol @@ -6,10 +6,10 @@ import "forge-std/Test.sol"; // Internal import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; import { IFundingManager_v1, FundingManagerV1Mock @@ -24,29 +24,31 @@ import "@oz/utils/Strings.sol"; // SuT import { - FM_EXT_TokenVault_v1, - IFM_EXT_TokenVault_v1 -} from "@fm/extensions/FM_EXT_TokenVault_v1.sol"; -import {FM_EXT_TokenVault_v1_Exposed} from - "@mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v1_Exposed.sol"; + FM_EXT_TokenVault_v2, + IFM_EXT_TokenVault_v2 +} from "@fm/extensions/FM_EXT_TokenVault_v2.sol"; +import {FM_EXT_TokenVault_v2_Exposed} from + "@mocks/modules/fundingManager/extensions/FM_EXT_TokenVault_v2_Exposed.sol"; -contract FM_EXT_TokenVault_v1_Test is ModuleTest { +contract FM_EXT_TokenVault_v2_Test is ModuleTest { // SuT - FM_EXT_TokenVault_v1_Exposed vault; + FM_EXT_TokenVault_v2_Exposed vault; function setUp() public virtual { - // Add Module to Mock Orchestrator_v1 - address impl = address(new FM_EXT_TokenVault_v1_Exposed()); - vault = FM_EXT_TokenVault_v1_Exposed(Clones.clone(impl)); + // Add Module to Mock Orchestrator_v2 + address impl = address(new FM_EXT_TokenVault_v2_Exposed()); + vault = FM_EXT_TokenVault_v2_Exposed(Clones.clone(impl)); _setUpOrchestrator(vault); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); vault.init(_orchestrator, _METADATA, bytes("")); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( - vault.supportsInterface(type(IFM_EXT_TokenVault_v1).interfaceId) + vault.supportsInterface(type(IFM_EXT_TokenVault_v2).interfaceId) ); } @@ -62,7 +64,7 @@ contract FM_EXT_TokenVault_v1_Test is ModuleTest { // Modifiers /* Test withdraw() function modifiers in place - ├── Given the caller is not the Orchestrator Admin + ├── Given the caller is not permissioned │ └── And the modifier onlyOrchestratorAdmin is in position │ └── When the function withdraw() is called │ └── Then it should revert @@ -80,26 +82,28 @@ contract FM_EXT_TokenVault_v1_Test is ModuleTest { └── Then it should revert */ - function testWithdraw_onlyOrchestratorAdminModifierInPosition() public { + function testWithdraw_permissionedModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0) + IModule_v2.Module__CallerNotPermissioned.selector ) ); - vm.prank(address(0)); + vm.prank(address(0xB0B)); vault.withdraw(address(0), 0, address(0)); } function testWithdraw_validAddressTokModifierInPosition() public { - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); vault.withdraw(address(0), 1, address(1)); } function testWithdraw_validAmountModifierInPosition() public { vm.expectRevert( - IFM_EXT_TokenVault_v1 + IFM_EXT_TokenVault_v2 .Module__FM_EXT_TokenVault__InvalidAmount .selector ); @@ -107,7 +111,7 @@ contract FM_EXT_TokenVault_v1_Test is ModuleTest { } function testWithdraw_validAddressDstModifierInPosition() public { - vm.expectRevert(IModule_v1.Module__InvalidAddress.selector); + vm.expectRevert(IModule_v2.Module__InvalidAddress.selector); vault.withdraw(address(1), 1, address(0)); } @@ -134,7 +138,7 @@ contract FM_EXT_TokenVault_v1_Test is ModuleTest { // Test condition vm.expectEmit(true, true, true, true); - emit IFM_EXT_TokenVault_v1.TokensWithdrawn(address(_token), dst, amount); + emit IFM_EXT_TokenVault_v2.TokensWithdrawn(address(_token), dst, amount); vault.withdraw(address(_token), amount, dst); assertEq(_token.balanceOf(address(vault)), 0); @@ -155,7 +159,7 @@ contract FM_EXT_TokenVault_v1_Test is ModuleTest { function testInternalOnlyValidAmount_revertGivenZeroAmount() public { vm.expectRevert( - IFM_EXT_TokenVault_v1 + IFM_EXT_TokenVault_v2 .Module__FM_EXT_TokenVault__InvalidAmount .selector ); diff --git a/test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.t.sol b/test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.t.sol similarity index 80% rename from test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.t.sol rename to test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.t.sol index 89aaf6465..6cfeee66c 100644 --- a/test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1.t.sol +++ b/test/unit/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2.t.sol @@ -3,23 +3,23 @@ pragma solidity ^0.8.0; // Internal imports import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; -import {IFM_PC_Oracle_Redeeming_v1} from - "@fm/oracle/interfaces/IFM_PC_Oracle_Redeeming_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IFM_PC_Oracle_Redeeming_v2} from + "@fm/oracle/interfaces/IFM_PC_Oracle_Redeeming_v2.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; import { - BondingCurveBase_v1, - IBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/BondingCurveBase_v1.sol"; + BondingCurveBase_v2, + IBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/BondingCurveBase_v2.sol"; import {IFundingManager_v1} from "@fm/IFundingManager_v1.sol"; import { - RedeemingBondingCurveBase_v1, - IRedeemingBondingCurveBase_v1 -} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v1.sol"; + RedeemingBondingCurveBase_v2, + IRedeemingBondingCurveBase_v2 +} from "@fm/bondingCurve/abstracts/RedeemingBondingCurveBase_v2.sol"; import {ERC20Issuance_v1} from "@ex/token/ERC20Issuance_v1.sol"; import {FM_BC_Tools} from "@fm/bondingCurve/FM_BC_Tools.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // External imports import {Clones} from "@oz/proxy/Clones.sol"; @@ -28,22 +28,22 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; // Tests and Mocks import {ModuleTest} from "@unitTest/modules/ModuleTest.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; import {OraclePrice_Mock} from "@mocks/modules/logicModule/OraclePrice_Mock.sol"; import {InvalidOraclePrice_Mock} from "@mocks/modules/logicModule/InvalidOraclePrice_Mock.sol"; -import {PP_Queue_ManualExecution_v1_Mock} from - "@mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Mock.sol"; +import {PP_Queue_ManualExecution_v2_Mock} from + "@mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Mock.sol"; // System under testing (SUT) -import {FM_PC_Oracle_Redeeming_v1_Exposed} from - "@mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v1_Exposed.sol"; +import {FM_PC_Oracle_Redeeming_v2_Exposed} from + "@mocks/modules/fundingManager/oracle/FM_PC_Oracle_Redeeming_v2_Exposed.sol"; /** * @title FM_PC_ExternalPrice_Redeeming_v1_Test - * @notice Test contract for FM_PC_Oracle_Redeeming_v1 + * @notice Test contract for FM_PC_Oracle_Redeeming_v2 */ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // ============================================================================ @@ -80,10 +80,10 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // State // Contracts - FM_PC_Oracle_Redeeming_v1_Exposed fundingManager; + FM_PC_Oracle_Redeeming_v2_Exposed fundingManager; ERC20Issuance_v1 issuanceToken; OraclePrice_Mock oracle; - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; address impl; // Test addresses @@ -121,8 +121,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { ); // Setup funding manager - impl = address(new FM_PC_Oracle_Redeeming_v1_Exposed()); - fundingManager = FM_PC_Oracle_Redeeming_v1_Exposed(Clones.clone(impl)); + impl = address(new FM_PC_Oracle_Redeeming_v2_Exposed()); + fundingManager = FM_PC_Oracle_Redeeming_v2_Exposed(Clones.clone(impl)); _setUpOrchestrator(fundingManager); // Initialize the funding manager @@ -133,14 +133,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // set oracle address in FM fundingManager.setOracleAddress(address(oracle)); - // Grant whitelist role to test contract - fundingManager.grantModuleRole( - fundingManager.getWhitelistRole(), address(this) - ); - // Grant queue executor role to test contract - fundingManager.grantModuleRole( - fundingManager.getQueueExecutorRole(), address(this) - ); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); // Open buy and sell fundingManager.openBuy(); @@ -224,13 +218,13 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { ├── Then it should return true for supported interfaces └── Then it should return false for unsupported interfaces */ - function testSupportsInterface_worksGivenDifferentInterfaces() public { + function testSupportsInterface() public override(ModuleTest) { // Test - Verify supported interfaces assertTrue( fundingManager.supportsInterface( - type(IFM_PC_Oracle_Redeeming_v1).interfaceId + type(IFM_PC_Oracle_Redeeming_v2).interfaceId ), - "Should support IFM_PC_Oracle_Redeeming_v1" + "Should support IFM_PC_Oracle_Redeeming_v2" ); assertTrue( @@ -314,71 +308,6 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { ); } - /* Test: Function getWhitelistRole() - └── Given we want to get the whitelist role - └── When the function getWhitelistRole() is called - └── Then it should return the correct whitelist role identifier - */ - function testGetWhitelistRole_worksGivenWhitelistRoleRetrieved() public { - // Test - Verify whitelist role - bytes32 expectedRole = bytes32("WHITELIST_ROLE"); - assertEq( - fundingManager.getWhitelistRole(), - expectedRole, - "Incorrect whitelist role identifier" - ); - } - - /* Test: Function: getWhitelistRoleAdmin() - └── Given we want to get the whitelist role admin - └── When the function getWhitelistRoleAdmin() is called - └── Then it should return the correct whitelist role admin identifier - */ - function testGetWhitelistRoleAdmin_worksGivenWhitelistRoleAdminRetrieved() - public - { - // Test - Verify whitelist role admin - bytes32 expectedRole = bytes32("WHITELIST_ROLE_ADMIN"); - assertEq( - fundingManager.getWhitelistRoleAdmin(), - expectedRole, - "Incorrect whitelist role admin identifier" - ); - } - - /* Test: Function getQueueExecutorRole() - └── Given we want to get the queue executor role - └── When the function getQueueExecutorRole() is called - └── Then it should return the correct queue executor role identifier - */ - function testGetQueueExecutorRole_worksGivenQueueExecutorRoleRetrieved() - public - { - // Test - Verify queue executor role - bytes32 expectedRole = bytes32("QUEUE_EXECUTOR_ROLE"); - assertEq( - fundingManager.getQueueExecutorRole(), - expectedRole, - "Incorrect queue executor role identifier" - ); - } - - /* Test: Function getQueueExecutorRoleAdmin() - └── Given we want to get the queue executor role admin - └── When the function getQueueExecutorRoleAdmin() is called - └── Then it should return the correct queue executor role admin identifier - */ - function testGetQueueExecutorRoleAdmin_worksGivenQueueExecutorRoleAdminRetrieved( - ) public { - // Test - Verify queue executor role admin - bytes32 expectedRole = bytes32("QUEUE_EXECUTOR_ROLE_ADMIN"); - assertEq( - fundingManager.getQueueExecutorRoleAdmin(), - expectedRole, - "Incorrect queue executor role admin identifier" - ); - } - /* Test: Function getStaticPriceForBuying() ├── Given we want to get the static price for buying └── When the function getStaticPriceForBuying() is called @@ -532,7 +461,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test - Expect ReserveDeposited event vm.expectEmit(true, true, true, true); - emit IFM_PC_Oracle_Redeeming_v1.ReserveDeposited(address(this), amount_); + emit IFM_PC_Oracle_Redeeming_v2.ReserveDeposited(address(this), amount_); // Test - Deposit reserve fundingManager.depositReserve(amount_); @@ -558,247 +487,41 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { function testDepositReserve_revertGivenZeroAmount() public { // Test - Expect revert on zero amount vm.expectRevert( - IFM_PC_Oracle_Redeeming_v1 + IFM_PC_Oracle_Redeeming_v2 .Module__FM_PC_ExternalPrice_Redeeming_InvalidAmount .selector ); fundingManager.depositReserve(0); } - /* Test: Function buy() - └── Given a user with WHITELIST_ROLE and buying is open - └── When buy() is called with valid minAmountOut - └── Then it should execute successfully - */ - function testBuy_worksGivenWhitelistedUser() public { - // Setup - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(_token), amount_, address(this), address(fundingManager) - ); - - // Setup - Calculate minimum amount out - uint minAmountOut_ = fundingManager.calculatePurchaseReturn(amount_); - - // Test - Should not revert - fundingManager.buy(amount_, minAmountOut_); - } - - /* Test: Function buy() - └── Given a user without WHITELIST_ROLE but buying is open - └── When buy() is called with valid minAmountOut - └── Then it should revert with Module__CallerNotAuthorized error - */ - function testBuy_revertGivenNonWhitelistedUser() public { - // Setup - address nonWhitelisted_ = makeAddr("nonWhitelisted"); - uint amount_ = 1e18; - // Get role for revert - bytes32 roleId = _authorizer.generateRoleId( - address(fundingManager), fundingManager.getWhitelistRole() - ); - - // Test - Switch to non-whitelisted user and expect revert - vm.prank(nonWhitelisted_); - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - nonWhitelisted_ - ) - ); - fundingManager.buy(amount_, amount_); - } - /* Test: Function buyFor() - └── Given a user with WHITELIST_ROLE and Third Party Operations (TPO) enabled + └── Given Third Party Operations (TPO) disabled └── When buyFor() is called - └── Then it should execute successfully - */ - function testBuyFor_worksGivenWhitelistedUserAndTPOEnabled() public { - // Setup - address receiver_ = makeAddr("receiver"); - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(_token), amount_, address(this), address(fundingManager) - ); - - fundingManager.exposed_setIsDirectOperationsOnly(false); - - // Setup - Calculate minimum amount out - uint minAmountOut_ = fundingManager.calculatePurchaseReturn(amount_); - - // Test - Should not revert - fundingManager.buyFor(receiver_, amount_, minAmountOut_); - } - - /* Test: Function buyFor() - └── Given a user without WHITELIST_ROLE but Third Party Operations (TPO) enabled - └── When buyFor() is called - └── Then it should revert - */ - function testBuyFor_revertGivenNonWhitelistedUserAndTPOEnabled() public { - // Setup - address nonWhitelisted_ = makeAddr("nonWhitelisted"); - address receiver_ = makeAddr("receiver"); - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(_token), amount_, nonWhitelisted_, address(fundingManager) - ); - - fundingManager.exposed_setIsDirectOperationsOnly(false); - - // Setup - Calculate minimum amount out - uint minAmountOut_ = fundingManager.calculatePurchaseReturn(amount_); - - // Test - Switch to non-whitelisted user and expect revert - vm.startPrank(nonWhitelisted_); - vm.expectRevert(); - fundingManager.buyFor(receiver_, amount_, minAmountOut_); - vm.stopPrank(); - } - - /* Test: Function buyFor() - └── Given a whitelisted user but Third Party Operations (TPO) disabled - └── When buyFor() is called - └── Then it should revert + └── Then it should revert (Modifier in place test) */ function testBuyFor_revertGivenTPODisabled() public { - // Setup - address receiver_ = makeAddr("receiver"); - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(_token), amount_, address(this), address(fundingManager) - ); - - // Setup - Calculate minimum amount out - uint minAmountOut_ = fundingManager.calculatePurchaseReturn(amount_); - // Test - Should revert as TPO is disabled - vm.expectRevert(); - fundingManager.buyFor(receiver_, amount_, minAmountOut_); - } - - /* Test: Function sell() - └── Given a user with WHITELIST_ROLE and selling is open - └── When sell() is called - └── Then it should execute successfully - */ - function testSell_worksGivenWhitelistedUser() public { - // Setup - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(_token), amount_, address(this), address(fundingManager) - ); - - // Setup - Calculate minimum amount out - uint minBuyAmountOut_ = fundingManager.calculatePurchaseReturn(amount_); - - // Test - Should not revert - fundingManager.buy(amount_, minBuyAmountOut_); - - assertEq( - issuanceToken.balanceOf(address(this)), - minBuyAmountOut_, - "Sender balance not decreased correctly" - ); - - uint minSellAmountOut_ = - fundingManager.calculateSaleReturn(minBuyAmountOut_); - - // Test - Should not revert - sell the tokens we received from buying - fundingManager.sell(minBuyAmountOut_, minSellAmountOut_); - - // Test - Verify balances - assertEq(issuanceToken.balanceOf(address(fundingManager)), 0); - } - - /* Test: Function sell() - └── Given a user without WHITELIST_ROLE but selling is open - └── When sell() is called - └── Then it should revert (modifier in place test) - */ - function testSell_revertGivenNonWhitelistedUser() public { - // Setup - address nonWhitelisted_ = makeAddr("nonWhitelisted"); - uint amount_ = 1e18; - // Get role for revert - bytes32 roleId = _authorizer.generateRoleId( - address(fundingManager), fundingManager.getWhitelistRole() - ); - - // Setup - Calculate minimum amount out - uint minAmountOut_ = fundingManager.calculateSaleReturn(amount_); - - // Test - Switch to non-whitelisted user and expect revert - vm.prank(nonWhitelisted_); vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - nonWhitelisted_ - ) - ); - fundingManager.sell(amount_, minAmountOut_); - } - /* Test: Function sellTo() - └── Given selling is open - └── And Third Party Operations (TPO) enabled - └── And the caller has WHITELIST_ROLE - └── When sellTo() is called - └── Then it should execute successfully - */ - - function testSellTo_worksGivenWhitelistedUser() public { - // Setup - address receiver_ = makeAddr("receiver"); - uint amount_ = 1e18; - _prepareBuyOrSellConditions( - address(issuanceToken), - amount_, - address(this), - address(fundingManager) + IFM_PC_Oracle_Redeeming_v2 + .Module__FM_PC_ExternalPrice_Redeeming_ThirdPartyOperationsDisabled + .selector ); - fundingManager.exposed_setIsDirectOperationsOnly(false); - - uint minSellAmountOut_ = fundingManager.calculateSaleReturn(amount_); - - // Test - Should not revert - sell the tokens we received from buying - fundingManager.sellTo(receiver_, amount_, minSellAmountOut_); - - // Test - Verify balances - assertEq(issuanceToken.balanceOf(address(fundingManager)), 0); + fundingManager.buyFor(address(0), 0, 0); } /* Test: Function sellTo() - └── Given selling is open - └── And Third Party Operations (TPO) enabled - └── And the caller has no WHITELIST_ROLE - └── When sellTo() is called - └── Then it should revert (modifier in place test) + └── Given Third Party Operations (TPO) disabled + └── When sellTo() is called + └── Then it should revert (Modifier in place test) */ - function testSellTo_revertGivenNonWhitelistedUser() public { - // Setup - address nonWhitelisted_ = makeAddr("nonWhitelisted"); - address receiver_ = makeAddr("receiver"); - uint amount_ = 1e18; - // Get role for revert - bytes32 roleId = _authorizer.generateRoleId( - address(fundingManager), fundingManager.getWhitelistRole() - ); - // Enable TPO - fundingManager.exposed_setIsDirectOperationsOnly(false); - - // Test - Switch to non-whitelisted user and expect revert - vm.prank(nonWhitelisted_); + function testSellTo_revertGivenTPODisabled() public { + // Test - Should revert as TPO is disabled vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - nonWhitelisted_ - ) + IFM_PC_Oracle_Redeeming_v2 + .Module__FM_PC_ExternalPrice_Redeeming_ThirdPartyOperationsDisabled + .selector ); - fundingManager.sellTo(receiver_, amount_, amount_); + fundingManager.sellTo(address(0), 0, 0); } /* Test: Function transferOrchestratorToken() @@ -814,7 +537,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { uint amount_ = 100; // Test - Should revert if not called by payment client - vm.expectRevert(IModule_v1.Module__OnlyCallableByPaymentClient.selector); + vm.expectRevert(IModule_v2.Module__OnlyCallableByPaymentClient.selector); fundingManager.transferOrchestratorToken(receiver_, amount_); } @@ -835,7 +558,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { ); // Setup - Create and register payment client - paymentClient = new ERC20PaymentClientBaseV2Mock(); + paymentClient = new ERC20PaymentClientBase_v3_Mock(); _addLogicModuleToOrchestrator(address(paymentClient)); // Setup - Mock payment client call @@ -945,10 +668,28 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { } /* Test: Function setProjectTreasury() - ├── Given the project treasury is a valid address - │ └── When the function setProjectTreasury() is called - │ └── Then it should set the project treasury correctly + ├── Given: Caller is not permissioned + | └── When the function setProjectTreasury() is called + | └── Then it should revert (modifier in place test) + ├── Given: Caller is permissioned + ├── And: the project treasury is a valid address + └── When the function setProjectTreasury() is called + └── Then it should set the project treasury correctly */ + function testSetProjectTreasury_modifierInPlace() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + fundingManager.setProjectTreasury(address(0)); + } + function testSetProjectTreasury_worksGivenValidAddress( address projectTreasury_ ) public { @@ -963,13 +704,33 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { } /* Test: Function setOracleAddress() - ├── Given the oracle supports the IOraclePrice_v1 interface - │ └── When the function _setOracleAddress() is called - │ └── Then it should set the oracle address correctly - └── Given the oracle does not support the IOraclePrice_v1 interface - └── When the function _setOracleAddress() is called + ├── Given: Caller is not permissioned + | └── When the function setOracleAddress() is called + | └── Then it should revert (modifier in place test) + ├── Given: Caller is permissioned + ├── And: the oracle supports the IOraclePrice_v1 interface + | └── When the function setOracleAddress() is called + | └── Then it should set the oracle address correctly + ├── Given: Caller is permissioned + ├── And: the oracle does not support the IOraclePrice_v1 interface + └── When the function setOracleAddress() is called └── Then it should revert */ + + function testSetOracleAddress_modifierInPlace() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + fundingManager.setOracleAddress(address(0)); + } + function testSetOracleAddress_worksGivenValidOracle(address _oracle) public { @@ -990,10 +751,29 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { } /* Test: Function setIsDirectOperationsOnly() - └── Given a valid value + ├── Given: Caller is not permissioned + | └── When the function setIsDirectOperationsOnly() is called + | └── Then it should revert (modifier in place test) + ├── Given: Caller is permissioned + └── And: Called with a valid value └── When the function exposed_setIsDirectOperationsOnly() is called └── Then the value should be set correctly */ + + function testSetIsDirectOperationsOnly_modifierInPlace() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + fundingManager.setIsDirectOperationsOnly(false); + } + function testSetIsDirectOperationsOnly_worksGivenValidValue( bool _isDirectOperationsOnly ) public { @@ -1009,34 +789,26 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { } /* Test: Function executeRedemptionQueue() - └── Given caller does not have QUEUE_EXECUTOR_ROLE + └── Given caller is not permissioned └── When executeRedemptionQueue() is called └── Then it should revert (modifier in place test) */ - function testExecuteRedemptionQueue_revertGivenCallerDoesNotHaveQueueExecutorRole( - ) public { - // Setup - address nonExecutorRole = makeAddr("nonExecutorRole"); - bytes32 roleId = _authorizer.generateRoleId( - address(fundingManager), fundingManager.getQueueExecutorRole() - ); + function testExecuteRedemptionQueue_modifierInPlace() public { + // permissioned - // Test - Switch to non-executor role user and expect revert - vm.prank(nonExecutorRole); + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - nonExecutorRole + IModule_v2.Module__CallerNotPermissioned.selector ) ); - - // Test + vm.prank(address(0xB0B)); fundingManager.executeRedemptionQueue(); } /* Test: Function executeRedemptionQueue() - ├── Given caller has QUEUE_EXECUTOR_ROLE + ├── Given caller is permissioned └── And the Payment Processor does not have the correct interface └── When executeRedemptionQueue() is called └── Then it should revert @@ -1049,7 +821,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // which means we test the low level call failure. vm.expectRevert( abi.encodeWithSelector( - IFM_PC_Oracle_Redeeming_v1 + IFM_PC_Oracle_Redeeming_v2 .Module__FM_PC_ExternalPrice_Redeeming_QueueExecutionFailed .selector, bytes("") @@ -1059,7 +831,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { } /* Test: Function executeRedemptionQueue() - ├── Given caller has QUEUE_EXECUTOR_ROLE + ├── Given caller is permissioned ├── And there are redemption orders in the queue └── And the payment processor has the correct interface └── When executeRedemptionQueue() is called @@ -1077,8 +849,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { uint protocolSellFeeAmount_ = 1e17; // Setup payment processor with the correct interface, so the low level call // does not fail - PP_Queue_ManualExecution_v1_Mock paymentProcessor = - new PP_Queue_ManualExecution_v1_Mock(); + PP_Queue_ManualExecution_v2_Mock paymentProcessor = + new PP_Queue_ManualExecution_v2_Mock(); _addPaymentProcessorToOrchestrator(address(paymentProcessor)); // Setup - Create order @@ -1117,7 +889,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectRevert( abi.encodeWithSelector( - IFM_PC_Oracle_Redeeming_v1 + IFM_PC_Oracle_Redeeming_v2 .Module__FM_PC_ExternalPrice_Redeeming_InvalidProjectTreasury .selector ) @@ -1133,7 +905,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectEmit(true, true, true, true); - emit IFM_PC_Oracle_Redeeming_v1.ProjectTreasuryUpdated( + emit IFM_PC_Oracle_Redeeming_v2.ProjectTreasuryUpdated( projectTreasury, projectTreasury_ ); @@ -1163,7 +935,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectEmit(true, true, true, true); - emit IFM_PC_Oracle_Redeeming_v1.RedemptionAmountUpdated( + emit IFM_PC_Oracle_Redeeming_v2.RedemptionAmountUpdated( openRedemptionAmount_ - amount_ ); @@ -1194,7 +966,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectEmit(true, true, true, true); - emit IFM_PC_Oracle_Redeeming_v1.RedemptionAmountUpdated( + emit IFM_PC_Oracle_Redeeming_v2.RedemptionAmountUpdated( openRedemptionAmount_ + amount_ ); @@ -1235,7 +1007,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectEmit(true, true, true, true); - emit IFM_PC_Oracle_Redeeming_v1.OracleUpdated( + emit IFM_PC_Oracle_Redeeming_v2.OracleUpdated( currentOracle, address(newOracle) ); @@ -1349,7 +1121,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.expectEmit(true, true, true, true); - emit IBondingCurveBase_v1.IssuanceTokenSet( + emit IBondingCurveBase_v2.IssuanceTokenSet( address(newIssuanceToken), decimals_ ); @@ -1380,8 +1152,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Convert amount to issuance token decimals amount_ = amount_ * 10 ** issuanceTokenDecimals_; - FM_PC_Oracle_Redeeming_v1_Exposed newFundingManager = - FM_PC_Oracle_Redeeming_v1_Exposed( + FM_PC_Oracle_Redeeming_v2_Exposed newFundingManager = + FM_PC_Oracle_Redeeming_v2_Exposed( _initializeFundingManagerWithDifferentTokenDecimals( issuanceTokenDecimals_, collateralTokenDecimals_ ) @@ -1434,8 +1206,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { issuanceTokenDecimals_ = uint8(bound(issuanceTokenDecimals_, 1, 18)); collateralTokenDecimals_ = uint8(bound(collateralTokenDecimals_, 1, 18)); - FM_PC_Oracle_Redeeming_v1_Exposed newFundingManager = - FM_PC_Oracle_Redeeming_v1_Exposed( + FM_PC_Oracle_Redeeming_v2_Exposed newFundingManager = + FM_PC_Oracle_Redeeming_v2_Exposed( _initializeFundingManagerWithDifferentTokenDecimals( issuanceTokenDecimals_, collateralTokenDecimals_ ) @@ -1666,7 +1438,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test - Expect event emission vm.expectEmit(true, true, true, true); - emit IBondingCurveBase_v1.ProjectCollateralFeeAdded(projectFeeAmount_); + emit IBondingCurveBase_v2.ProjectCollateralFeeAdded(projectFeeAmount_); // Execute fundingManager.exposed_projectFeeCollected(projectFeeAmount_); @@ -1702,7 +1474,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test - Expect event emission vm.expectEmit(true, true, true, true, address(fundingManager)); - emit IFM_PC_Oracle_Redeeming_v1.RedemptionOrderCreated( + emit IFM_PC_Oracle_Redeeming_v2.RedemptionOrderCreated( address(fundingManager), // paymentClient_ 1, // orderId_ (first order) address(this), // seller_ @@ -1714,7 +1486,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { protocolSellFeeAmount_, // protocolFeeAmount_ collateralRedeemAmount_, // finalRedemptionAmount_ address(_token), // collateralToken_ - IFM_PC_Oracle_Redeeming_v1.RedemptionState.PENDING // state_ + IFM_PC_Oracle_Redeeming_v2.RedemptionState.PENDING // state_ ); // Execute @@ -1726,8 +1498,8 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { protocolSellFeeAmount_ ); - IERC20PaymentClientBase_v2.PaymentOrder[] memory localPaymentOrders = - IERC20PaymentClientBase_v2(address(fundingManager)).paymentOrders(); + IERC20PaymentClientBase_v3.PaymentOrder[] memory localPaymentOrders = + IERC20PaymentClientBase_v3(address(fundingManager)).paymentOrders(); assertEq( localPaymentOrders.length, 1, "Payment orders length should be 1" @@ -1798,7 +1570,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { address receiver_ = makeAddr("receiver"); // Test vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidDepositAmount .selector ); @@ -1820,7 +1592,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { address receiver_ = makeAddr("receiver"); // Test vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InvalidMinAmountOut .selector ); @@ -1856,7 +1628,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { // Test vm.prank(receiver_); vm.expectRevert( - IBondingCurveBase_v1 + IBondingCurveBase_v2 .Module__BondingCurveBase__InsufficientOutputAmount .selector ); @@ -1916,11 +1688,11 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { vm.prank(receiver_); // Expect events vm.expectEmit(true, true, true, true, address(fundingManager)); - emit IBondingCurveBase_v1.ProjectCollateralFeeAdded( + emit IBondingCurveBase_v2.ProjectCollateralFeeAdded( expectedProjectCollateralFeeAmount_ ); vm.expectEmit(true, true, true, true, address(fundingManager)); - emit IRedeemingBondingCurveBase_v1.TokensSold( + emit IRedeemingBondingCurveBase_v2.TokensSold( receiver_, sellAmount_, expectedNetCollateralRedeemAmount_, @@ -1992,7 +1764,7 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { vm.prank(receiver_); // Expect events vm.expectEmit(true, true, true, true, address(fundingManager)); - emit IRedeemingBondingCurveBase_v1.TokensSold( + emit IRedeemingBondingCurveBase_v2.TokensSold( receiver_, sellAmount_, expectedNetCollateralRedeemAmount_, @@ -2153,9 +1925,9 @@ contract FM_PC_ExternalPrice_Redeeming_v1_Test is ModuleTest { ); // Setup funding manager address implementation = - address(new FM_PC_Oracle_Redeeming_v1_Exposed()); - FM_PC_Oracle_Redeeming_v1_Exposed newFundingManager = - FM_PC_Oracle_Redeeming_v1_Exposed(Clones.clone(implementation)); + address(new FM_PC_Oracle_Redeeming_v2_Exposed()); + FM_PC_Oracle_Redeeming_v2_Exposed newFundingManager = + FM_PC_Oracle_Redeeming_v2_Exposed(Clones.clone(implementation)); // Initialize funding manager newFundingManager.init(_orchestrator, _METADATA, newConfigData); diff --git a/test/unit/modules/lib/LibMetadata.t.sol b/test/unit/modules/lib/LibMetadata.t.sol index 5db811621..79fabe016 100644 --- a/test/unit/modules/lib/LibMetadata.t.sol +++ b/test/unit/modules/lib/LibMetadata.t.sol @@ -7,14 +7,14 @@ import {Test} from "forge-std/Test.sol"; import {LibMetadata} from "src/modules/lib/LibMetadata.sol"; // Internal Interfaces -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; contract LibMetadataTest is Test { function setUp() public {} /// @dev The identifier is defined as the hash of the major version, url /// and title. - function testIdentifier(IModule_v1.Metadata memory data) public { + function testIdentifier(IModule_v2.Metadata memory data) public { bytes32 got = LibMetadata.identifier(data); bytes32 want = keccak256(abi.encode(data.majorVersion, data.url, data.title)); @@ -33,7 +33,7 @@ contract LibMetadataTest is Test { vm.assume(bytes(url).length != 0); vm.assume(bytes(title).length != 0); - IModule_v1.Metadata memory data = IModule_v1.Metadata( + IModule_v2.Metadata memory data = IModule_v2.Metadata( majorVersion, minorVersion, patchVersion, url, title ); @@ -46,7 +46,7 @@ contract LibMetadataTest is Test { uint patchVersion ) public { vm.assume(majorVersion != 0 || minorVersion != 0 || patchVersion != 0); - IModule_v1.Metadata memory data = IModule_v1.Metadata( + IModule_v2.Metadata memory data = IModule_v2.Metadata( majorVersion, minorVersion, patchVersion, "", "title" ); @@ -60,7 +60,7 @@ contract LibMetadataTest is Test { ) public { vm.assume(majorVersion != 0 || minorVersion != 0 || patchVersion != 0); - IModule_v1.Metadata memory data = IModule_v1.Metadata( + IModule_v2.Metadata memory data = IModule_v2.Metadata( majorVersion, minorVersion, patchVersion, "url", "" ); @@ -72,7 +72,7 @@ contract LibMetadataTest is Test { uint minorVersion, uint patchVersion ) public { - IModule_v1.Metadata memory data = IModule_v1.Metadata( + IModule_v2.Metadata memory data = IModule_v2.Metadata( majorVersion, minorVersion, patchVersion, "url", "title" ); if (majorVersion == 0 && minorVersion == 0 && patchVersion == 0) { diff --git a/test/unit/modules/logicModule/LM_Oracle_Permissioned_v1.t.sol b/test/unit/modules/logicModule/LM_Oracle_Permissioned_v2.t.sol similarity index 76% rename from test/unit/modules/logicModule/LM_Oracle_Permissioned_v1.t.sol rename to test/unit/modules/logicModule/LM_Oracle_Permissioned_v2.t.sol index 96a8fd70e..1e9571a16 100644 --- a/test/unit/modules/logicModule/LM_Oracle_Permissioned_v1.t.sol +++ b/test/unit/modules/logicModule/LM_Oracle_Permissioned_v2.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; // Internal import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; import {IOraclePrice_v1} from "@lm/interfaces/IOraclePrice_v1.sol"; @@ -15,22 +15,22 @@ import {Clones} from "@oz/proxy/Clones.sol"; // Tests and Mocks import {Test} from "forge-std/Test.sol"; -import {LM_Oracle_Permissioned_v1_Exposed} from - "@mocks/modules/logicModule/LM_Oracle_Permissioned_v1_Exposed.sol"; +import {LM_Oracle_Permissioned_v2_Exposed} from + "@mocks/modules/logicModule/LM_Oracle_Permissioned_v2_Exposed.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; // System under testing import { - LM_Oracle_Permissioned_v1, - ILM_Oracle_Permissioned_v1 -} from "@lm/LM_Oracle_Permissioned_v1.sol"; + LM_Oracle_Permissioned_v2, + ILM_Oracle_Permissioned_v2 +} from "@lm/LM_Oracle_Permissioned_v2.sol"; /** - * @title LM_Oracle_Permissioned_v1_Test - * @dev Test contract for LM_Oracle_Permissioned_v1 + * @title LM_Oracle_Permissioned_v2_Test + * @dev Test contract for LM_Oracle_Permissioned_v2 * @author Zealynx Security */ -contract LM_Oracle_Permissioned_v1_Test is ModuleTest { +contract LM_Oracle_Permissioned_v2_Test is ModuleTest { // ================================================================================ // Constants uint8 constant TOKEN_DECIMALS = 6; @@ -39,7 +39,7 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { // ================================================================================ // State - LM_Oracle_Permissioned_v1_Exposed manualExternalPriceSetter; + LM_Oracle_Permissioned_v2_Exposed manualExternalPriceSetter; ERC20Mock collateralToken; // ================================================================================ @@ -50,22 +50,17 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { new ERC20Mock(TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS); // Setup manual external price setter - address impl = address(new LM_Oracle_Permissioned_v1_Exposed()); + address impl = address(new LM_Oracle_Permissioned_v2_Exposed()); manualExternalPriceSetter = - LM_Oracle_Permissioned_v1_Exposed(Clones.clone(impl)); + LM_Oracle_Permissioned_v2_Exposed(Clones.clone(impl)); _setUpOrchestrator(manualExternalPriceSetter); // Init module bytes memory configData = abi.encode(address(collateralToken)); manualExternalPriceSetter.init(_orchestrator, _METADATA, configData); - // Grant PRICE_SETTER_ROLE and PRICE_SETTER_ROLE_ADMIN to the test contract - manualExternalPriceSetter.grantModuleRole( - manualExternalPriceSetter.getPriceSetterRole(), address(this) - ); - manualExternalPriceSetter.grantModuleRole( - manualExternalPriceSetter.getPriceSetterRoleAdmin(), address(this) - ); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); } // ================================================================================ @@ -87,10 +82,10 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { ); } - function testSupportsInterface_GivenValidInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( manualExternalPriceSetter.supportsInterface( - type(ILM_Oracle_Permissioned_v1).interfaceId + type(ILM_Oracle_Permissioned_v2).interfaceId ) ); } @@ -99,36 +94,26 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { // Test External (public + external) /* Test: Function SetIssuancePrice() - ├── Given the caller has not PRICE_SETTER_ROLE + ├── Given the caller is not permissioned │ └── When the function setIssuancePrice() is called │ └── Then the function should revert (Modifier in place test) - └── Given the caller has PRICE_SETTER_ROLE + └── Given the caller is permissioned └── When the function setIssuancePrice() is called └── Then the price should be set correctly (redirects to internal func) */ - function testSetIssuancePrice_worksGivenModifierInPlace( - address unauthorized_, - uint price_ - ) public { - // Setup - vm.assume(unauthorized_ != address(this)); - vm.assume(price_ > 0); - bytes32 roleId = _authorizer.generateRoleId( - address(manualExternalPriceSetter), - manualExternalPriceSetter.getPriceSetterRole() - ); + function testSetIssuancePrice_ModifierInPlace() public { + // permissioned - // Test - vm.startPrank(unauthorized_); + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - unauthorized_ + IModule_v2.Module__CallerNotPermissioned.selector ) ); - manualExternalPriceSetter.setIssuancePrice(price_); + vm.prank(address(0xB0B)); + manualExternalPriceSetter.setIssuancePrice(0); } function testSetIssuancePrice_worksGivenPriceIsSet( @@ -159,36 +144,25 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { } /* Test: Function: SetRedemptionPrice() - ├── Given the caller has not PRICE_SETTER_ROLE + ├── Given the caller is not permissioned │ └── When the function setRedemptionPrice() is called │ └── Then the function should revert (Modifier in place test) - └── Given the caller has PRICE_SETTER_ROLE + └── Given the caller is permissioned └── When the function setRedemptionPrice() is called └── Then the price should be set correctly (redirects to internal func) */ - function testSetRedemptionPrice_worksGivenModifierInPlace( - address unauthorized_, - uint price_ - ) public { - // Setup - vm.assume(unauthorized_ != address(this)); - vm.assume(price_ > 0); - bytes32 roleId = _authorizer.generateRoleId( - address(manualExternalPriceSetter), - manualExternalPriceSetter.getPriceSetterRole() - ); - - // Test - vm.startPrank(unauthorized_); + function testSetRedemptionPrice_ModifierInPlace() public { + // permissioned + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - unauthorized_ + IModule_v2.Module__CallerNotPermissioned.selector ) ); - manualExternalPriceSetter.setRedemptionPrice(price_); + vm.prank(address(0xB0B)); + manualExternalPriceSetter.setRedemptionPrice(0); } function testSetRedemptionPrice_worksGivenPriceIsSet( @@ -219,39 +193,26 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { } /* Test: Function: SetIssuanceAndRedemptionPrice() - ├── Given the caller has not PRICE_SETTER_ROLE + ├── Given the caller is not permissioned │ └── When the function setIssuanceAndRedemptionPrice() is called │ └── Then the function should revert (Modifier in place test) - └── Given the caller has PRICE_SETTER_ROLE + └── Given the caller is permissioned └── When the function setIssuanceAndRedemptionPrice() is called └── Then the price should be set correctly (redirects to internal funcs) */ - function testSetIssuanceAndRedemptionPrice_worksGivenModifierInPlace( - address unauthorized_, - uint issuancePrice_, - uint redemptionPrice_ - ) public { - // Setup - vm.assume(unauthorized_ != address(this)); - vm.assume(issuancePrice_ > 0 && redemptionPrice_ > 0); - bytes32 roleId = _authorizer.generateRoleId( - address(manualExternalPriceSetter), - manualExternalPriceSetter.getPriceSetterRole() - ); + function testSetIssuanceAndRedemptionPrice_ModifierInPlace() public { + // permissioned - // Test - vm.startPrank(unauthorized_); + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - unauthorized_ + IModule_v2.Module__CallerNotPermissioned.selector ) ); - manualExternalPriceSetter.setIssuanceAndRedemptionPrice( - issuancePrice_, redemptionPrice_ - ); + vm.prank(address(0xB0B)); + manualExternalPriceSetter.setIssuanceAndRedemptionPrice(0, 0); } function testSetIssuanceAndRedemptionPrice_worksGivenPricesAreSet( @@ -358,7 +319,7 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { // Test vm.expectRevert( abi.encodeWithSelector( - ILM_Oracle_Permissioned_v1 + ILM_Oracle_Permissioned_v2 .Module__LM_ExternalPriceSetter__InvalidPrice .selector ) @@ -398,7 +359,7 @@ contract LM_Oracle_Permissioned_v1_Test is ModuleTest { // Test vm.expectRevert( abi.encodeWithSelector( - ILM_Oracle_Permissioned_v1 + ILM_Oracle_Permissioned_v2 .Module__LM_ExternalPriceSetter__InvalidPrice .selector ) diff --git a/test/unit/modules/logicModule/LM_PC_Bounties_v2.t.sol b/test/unit/modules/logicModule/LM_PC_Bounties_v3.t.sol similarity index 79% rename from test/unit/modules/logicModule/LM_PC_Bounties_v2.t.sol rename to test/unit/modules/logicModule/LM_PC_Bounties_v3.t.sol index 1316ec034..f5e01d6f0 100644 --- a/test/unit/modules/logicModule/LM_PC_Bounties_v2.t.sol +++ b/test/unit/modules/logicModule/LM_PC_Bounties_v3.t.sol @@ -11,8 +11,8 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Errors @@ -20,63 +20,38 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // SuT import { - LM_PC_Bounties_v2, - ILM_PC_Bounties_v2, - IERC20PaymentClientBase_v2 -} from "@lm/LM_PC_Bounties_v2.sol"; + LM_PC_Bounties_v3, + ILM_PC_Bounties_v3, + IERC20PaymentClientBase_v3 +} from "@lm/LM_PC_Bounties_v3.sol"; -import {LM_PC_Bounties_v2_Exposed} from - "@mocks/modules/logicModule/LM_PC_Bounties_v2_Exposed.sol"; +import {LM_PC_Bounties_v3_Exposed} from + "@mocks/modules/logicModule/LM_PC_Bounties_v3_Exposed.sol"; contract LM_PC_BountiesV1Test is ModuleTest { // SuT - LM_PC_Bounties_v2_Exposed bountyManager; + LM_PC_Bounties_v3_Exposed bountyManager; uint private constant _SENTINEL = type(uint).max; - ILM_PC_Bounties_v2.Contributor ALICE = - ILM_PC_Bounties_v2.Contributor(address(0xA11CE), 50_000_000); - ILM_PC_Bounties_v2.Contributor BOB = - ILM_PC_Bounties_v2.Contributor(address(0x606), 50_000_000); - ILM_PC_Bounties_v2.Contributor BEEF = - ILM_PC_Bounties_v2.Contributor(address(0xBEEF), 0); - ILM_PC_Bounties_v2.Contributor[] DEFAULT_CONTRIBUTORS; - ILM_PC_Bounties_v2.Contributor[] INVALID_CONTRIBUTORS; - - event BountyAdded( - uint indexed bountyId, - uint minimumPayoutAmount, - uint maximumPayoutAmount, - bytes details - ); - - event BountyUpdated(uint indexed bountyId, bytes details); - - event BountyLocked(uint indexed bountyId); - - event ClaimAdded( - uint indexed claimId, - uint indexed bountyId, - ILM_PC_Bounties_v2.Contributor[] contributors, - bytes details - ); - - event ClaimContributorsUpdated( - uint indexed claimId, ILM_PC_Bounties_v2.Contributor[] contributors - ); - - event ClaimDetailsUpdated(uint indexed claimId, bytes details); - - event ClaimVerified(uint indexed claimId); + ILM_PC_Bounties_v3.Contributor ALICE = + ILM_PC_Bounties_v3.Contributor(address(0xA11CE), 50_000_000); + ILM_PC_Bounties_v3.Contributor BOB = + ILM_PC_Bounties_v3.Contributor(address(0x606), 50_000_000); + ILM_PC_Bounties_v3.Contributor BEEF = + ILM_PC_Bounties_v3.Contributor(address(0xBEEF), 0); + ILM_PC_Bounties_v3.Contributor[] DEFAULT_CONTRIBUTORS; + ILM_PC_Bounties_v3.Contributor[] INVALID_CONTRIBUTORS; function setUp() public { - // Add Module to Mock Orchestrator_v1 - address impl = address(new LM_PC_Bounties_v2_Exposed()); - bountyManager = LM_PC_Bounties_v2_Exposed(Clones.clone(impl)); + // Add Module to Mock Orchestrator_v2 + address impl = address(new LM_PC_Bounties_v3_Exposed()); + bountyManager = LM_PC_Bounties_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(bountyManager); - _authorizer.setIsAuthorized(address(this), true); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); DEFAULT_CONTRIBUTORS.push(ALICE); DEFAULT_CONTRIBUTORS.push(BOB); @@ -89,10 +64,10 @@ contract LM_PC_BountiesV1Test is ModuleTest { //-------------------------------------------------------------------------- // Test: Initialization - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( bountyManager.supportsInterface( - type(ILM_PC_Bounties_v2).interfaceId + type(ILM_PC_Bounties_v3).interfaceId ) ); } @@ -124,7 +99,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { amounts = cutAmounts(20_000_000_000_000, amounts); // => maxAmount = 20_000_000_000_000 * 50 = 1_000_000_000_000_000 uint maxAmount = 1_000_000_000_000_000; - ILM_PC_Bounties_v2.Contributor[] memory contribs = + ILM_PC_Bounties_v3.Contributor[] memory contribs = createValidContributors(addrs, amounts); bountyManager.addBounty(1, maxAmount, bytes("")); @@ -133,7 +108,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (!contains(contribs, addr)) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__OnlyClaimContributor .selector ); @@ -145,7 +120,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { function testValidPayoutAmounts() public { //Check that internal function is in position vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidPayoutAmounts .selector ); @@ -165,7 +140,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { || detailArrayLength != minimumPayoutAmountLength ) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidArrayLengths .selector ); @@ -187,7 +162,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (id > usedIds || id == 0) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidBountyId .selector ); @@ -207,7 +182,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (id > usedIds + bountyId || id == 0 || id == bountyId) { vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidClaimId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidClaimId.selector ); } @@ -236,12 +211,12 @@ contract LM_PC_BountiesV1Test is ModuleTest { minimumPayoutAmount, maximumPayoutAmount, bytes("") ); - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](length); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](length); if (length == 0) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidContributorsLength .selector ); @@ -249,14 +224,14 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.addClaim(1, contribs, bytes("")); } else { for (uint i; i < length; i++) { - contribs[i] = ILM_PC_Bounties_v2.Contributor({ + contribs[i] = ILM_PC_Bounties_v3.Contributor({ addr: addrs[i], claimAmount: amounts[i] }); } uint totalAmount; - ILM_PC_Bounties_v2.Contributor memory currentContrib; + ILM_PC_Bounties_v3.Contributor memory currentContrib; // Check if it reached the end -> ClaimExceedsGivenPayoutAmounts will only be checked if it ran through everything bool reachedEnd; @@ -267,7 +242,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (currentContrib.claimAmount == 0) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidContributorAmount .selector ); @@ -280,7 +255,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { || currentContrib.addr == address(_orchestrator) ) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidContributorAddress .selector ); @@ -300,7 +275,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { ) ) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__ClaimExceedsGivenPayoutAmounts .selector ); @@ -320,7 +295,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (isClaimed) { bountyManager.verifyClaim(claimId, DEFAULT_CONTRIBUTORS); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__AlreadyClaimed.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__AlreadyClaimed.selector ); } bountyManager.verifyClaim(claimId, DEFAULT_CONTRIBUTORS); @@ -336,7 +311,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { if (isLocked) { bountyManager.lockBounty(bountyId); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); } bountyManager.verifyClaim(claimId, DEFAULT_CONTRIBUTORS); @@ -360,16 +335,16 @@ contract LM_PC_BountiesV1Test is ModuleTest { uint claimId = bountyManager.addClaim(bountyId, DEFAULT_CONTRIBUTORS, bytes("")); - ILM_PC_Bounties_v2.Contributor[] memory changedContributors = + ILM_PC_Bounties_v3.Contributor[] memory changedContributors = DEFAULT_CONTRIBUTORS; - changedContributors[0] = ILM_PC_Bounties_v2.Contributor({ + changedContributors[0] = ILM_PC_Bounties_v3.Contributor({ addr: changeAddress, claimAmount: changeAmount }); bountyManager.updateClaimContributors(claimId, changedContributors); vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__ContributorsChanged .selector ); @@ -397,7 +372,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { // Append element to contributor array to change the length DEFAULT_CONTRIBUTORS.push( - ILM_PC_Bounties_v2.Contributor({ + ILM_PC_Bounties_v3.Contributor({ addr: changeAddress, claimAmount: changeAmount }) @@ -408,7 +383,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { // Remove appended element to check with original array DEFAULT_CONTRIBUTORS.pop(); vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__ContributorsChanged .selector ); @@ -421,14 +396,14 @@ contract LM_PC_BountiesV1Test is ModuleTest { function testGetBountyInformationModifierInPosition() public { vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidBountyId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidBountyId.selector ); bountyManager.getBountyInformation(0); } function testGetClaimInformationModifierInPosition() public { vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidClaimId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidClaimId.selector ); bountyManager.getClaimInformation(0); } @@ -451,7 +426,9 @@ contract LM_PC_BountiesV1Test is ModuleTest { //Check that internal function is in position vm.expectEmit(true, true, true, true); - emit BountyAdded(1, minimumPayoutAmount, maximumPayoutAmount, details); + emit ILM_PC_Bounties_v3.BountyAdded( + 1, minimumPayoutAmount, maximumPayoutAmount, details + ); bountyManager.addBounty( minimumPayoutAmount, maximumPayoutAmount, details @@ -459,26 +436,26 @@ contract LM_PC_BountiesV1Test is ModuleTest { } function testAddBountyModifierInPosition() public { - // validPayoutAmounts + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( - ILM_PC_Bounties_v2 - .Module__LM_PC_Bounty__InvalidPayoutAmounts - .selector + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) ); + vm.prank(address(0xB0B)); bountyManager.addBounty(0, 0, bytes("")); - // Set this address to not authorized to test the roles correctly - _authorizer.setIsAuthorized(address(this), false); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); - // onlyBountyAdmin + // validPayoutAmounts vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.BOUNTY_ISSUER_ROLE() - ), - address(this) - ) + ILM_PC_Bounties_v3 + .Module__LM_PC_Bounty__InvalidPayoutAmounts + .selector ); bountyManager.addBounty(0, 0, bytes("")); } @@ -508,7 +485,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { for (uint i = 0; i < batchSize; i++) { vm.expectEmit(true, true, true, true); - emit BountyAdded( + emit ILM_PC_Bounties_v3.BountyAdded( 1 + i, minimumPayoutAmount, maximumPayoutAmount, details ); } @@ -531,9 +508,26 @@ contract LM_PC_BountiesV1Test is ModuleTest { bytes[] memory details = new bytes[](1); details[0] = bytes(""); + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bountyManager.addBountyBatch( + new uint[](0), maximumPayoutAmounts, details + ); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + // validArrayLengths vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidArrayLengths .selector ); @@ -543,33 +537,109 @@ contract LM_PC_BountiesV1Test is ModuleTest { // validPayoutAmounts vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidPayoutAmounts .selector ); bountyManager.addBountyBatch( minimumPayoutAmounts, maximumPayoutAmounts, details ); + } + + //----------------------------------------- + // UpdateBounty + + function testUpdateBounty(bytes calldata details) public { + uint id = bountyManager.addBounty(1, 1, bytes("")); + + vm.expectEmit(true, true, true, true); + emit ILM_PC_Bounties_v3.BountyUpdated(1, details); + + bountyManager.updateBounty(id, details); + + assertEqualBounty(id, 1, 1, details, false); + } + + function testUpdateBountyModifierInPosition() public { + bountyManager.addBounty(1, 1, bytes("")); + + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bountyManager.updateBounty(1, bytes("")); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // validBountyId + vm.expectRevert( + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidBountyId.selector + ); + bountyManager.updateBounty(0, bytes("")); // Set this address to not authorized to test the roles correctly _authorizer.setIsAuthorized(address(this), false); - // Set maximumPayoutAmounts[0] correctly - maximumPayoutAmounts[0] = 2; + // notLocked + bountyManager.lockBounty(1); - // onlyBountyAdmin + vm.expectRevert( + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector + ); + bountyManager.updateBounty(1, bytes("")); + } + + //----------------------------------------- + // LockBounty + + function testLockBounty() public { + uint id = bountyManager.addBounty(1, 1, bytes("")); + + vm.expectEmit(true, true, true, true); + emit ILM_PC_Bounties_v3.BountyLocked(1); + + bountyManager.lockBounty(1); + + assertEqualBounty(id, 1, 1, bytes(""), true); + } + + function testLockBountyModifierInPosition() public { + bountyManager.addBounty(1, 1, bytes("")); + + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.BOUNTY_ISSUER_ROLE() - ), - address(this) + IModule_v2.Module__CallerNotPermissioned.selector ) ); - bountyManager.addBountyBatch( - minimumPayoutAmounts, maximumPayoutAmounts, details + vm.prank(address(0xB0B)); + bountyManager.lockBounty(1); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + + // validBountyId + vm.expectRevert( + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidBountyId.selector + ); + bountyManager.lockBounty(0); + + // NotLocked + bountyManager.lockBounty(1); + vm.expectRevert( + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); + bountyManager.lockBounty(1); } //----------------------------------------- @@ -590,7 +660,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { amounts = cutAmounts(20_000_000_000_000, amounts); // => maxAmount = 20_000_000_000_000 * 50 = 1_000_000_000_000_000 uint maxAmount = 1_000_000_000_000_000; - ILM_PC_Bounties_v2.Contributor[] memory contribs = + ILM_PC_Bounties_v3.Contributor[] memory contribs = createValidContributors(addrs, amounts); bountyManager.addBounty(1, maxAmount, bytes("")); @@ -600,7 +670,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { for (uint i = 0; i < times; i++) { vm.expectEmit(true, true, true, true); // id starts at 2 because the id counter starts at 1 and addBounty increases it by 1 again - emit ClaimAdded(i + 2, 1, contribs, details); + emit ILM_PC_Bounties_v3.ClaimAdded(i + 2, 1, contribs, details); id = bountyManager.addClaim(1, contribs, details); assertEqualClaim(id, 1, contribs, details, false); @@ -615,137 +685,42 @@ contract LM_PC_BountiesV1Test is ModuleTest { function testAddClaimModifierInPosition() public { bountyManager.addBounty(1, 1, bytes("")); - // validBountyId - vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidBountyId.selector - ); - bountyManager.addClaim(0, DEFAULT_CONTRIBUTORS, bytes("")); + // permissioned - // _validContributorsForBounty - vm.expectRevert( - ILM_PC_Bounties_v2 - .Module__LM_PC_Bounty__InvalidContributorAmount - .selector - ); - bountyManager.addClaim(1, INVALID_CONTRIBUTORS, bytes("")); - - // notLocked - bountyManager.lockBounty(1); - - vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector - ); - bountyManager.addClaim(1, DEFAULT_CONTRIBUTORS, bytes("")); - - // Set this address to not authorized to test the roles correctly - _authorizer.setIsAuthorized(address(this), false); - - // onlyClaimAdmin + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.CLAIMANT_ROLE() - ), - address(this) + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); bountyManager.addClaim(0, DEFAULT_CONTRIBUTORS, bytes("")); - } - //----------------------------------------- - // UpdateBounty - - function testUpdateBounty(bytes calldata details) public { - uint id = bountyManager.addBounty(1, 1, bytes("")); - - vm.expectEmit(true, true, true, true); - emit BountyUpdated(1, details); - - bountyManager.updateBounty(id, details); - - assertEqualBounty(id, 1, 1, details, false); - } - - function testUpdateBountyModifierInPosition() public { - bountyManager.addBounty(1, 1, bytes("")); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); // validBountyId vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidBountyId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidBountyId.selector ); - bountyManager.updateBounty(0, bytes("")); - - // Set this address to not authorized to test the roles correctly - _authorizer.setIsAuthorized(address(this), false); + bountyManager.addClaim(0, DEFAULT_CONTRIBUTORS, bytes("")); - // onlyBountyAdmin + // _validContributorsForBounty vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.BOUNTY_ISSUER_ROLE() - ), - address(this) - ) + ILM_PC_Bounties_v3 + .Module__LM_PC_Bounty__InvalidContributorAmount + .selector ); - bountyManager.updateBounty(1, bytes("")); - // Reset this address to authorized - _authorizer.setIsAuthorized(address(this), true); + bountyManager.addClaim(1, INVALID_CONTRIBUTORS, bytes("")); // notLocked bountyManager.lockBounty(1); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector - ); - bountyManager.updateBounty(1, bytes("")); - } - - //----------------------------------------- - // UpdateBounty - - function testLockBounty() public { - uint id = bountyManager.addBounty(1, 1, bytes("")); - - vm.expectEmit(true, true, true, true); - emit BountyLocked(1); - - bountyManager.lockBounty(1); - - assertEqualBounty(id, 1, 1, bytes(""), true); - } - - function testLockBountyModifierInPosition() public { - bountyManager.addBounty(1, 1, bytes("")); - - // validBountyId - vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidBountyId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); - bountyManager.lockBounty(0); - - // NotLocked - bountyManager.lockBounty(1); - vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector - ); - bountyManager.lockBounty(1); - - // Set this address to not authorized to test the roles correctly - _authorizer.setIsAuthorized(address(this), false); - - // onlyBountyAdmin - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.BOUNTY_ISSUER_ROLE() - ), - address(this) - ) - ); - bountyManager.lockBounty(0); + bountyManager.addClaim(1, DEFAULT_CONTRIBUTORS, bytes("")); } //----------------------------------------- @@ -764,14 +739,14 @@ contract LM_PC_BountiesV1Test is ModuleTest { // => maxAmount = 20_000_000_000_000 * 50 = 1_000_000_000_000_000 uint maxAmount = 1_000_000_000_000_000; - ILM_PC_Bounties_v2.Contributor[] memory contribs = + ILM_PC_Bounties_v3.Contributor[] memory contribs = createValidContributors(addrs, amounts); bountyManager.addBounty(1, maxAmount, bytes("")); uint id = bountyManager.addClaim(1, DEFAULT_CONTRIBUTORS, bytes("")); vm.expectEmit(true, true, true, true); - emit ClaimContributorsUpdated(id, contribs); + emit ILM_PC_Bounties_v3.ClaimContributorsUpdated(id, contribs); bountyManager.updateClaimContributors(id, contribs); @@ -803,43 +778,40 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.addBounty(1, 100_000_000, bytes("")); // Id 3 bountyManager.addClaim(3, DEFAULT_CONTRIBUTORS, bytes("")); // Id 4 + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + bountyManager.updateClaimContributors(2, DEFAULT_CONTRIBUTORS); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + // validClaimId vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidClaimId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidClaimId.selector ); bountyManager.updateClaimContributors(0, DEFAULT_CONTRIBUTORS); // _validContributorsForBounty vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidContributorAmount .selector ); bountyManager.updateClaimContributors(2, INVALID_CONTRIBUTORS); - // onlyClaimAdmin - _authorizer.setIsAuthorized(address(this), false); // No access address - vm.expectRevert( - abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.CLAIMANT_ROLE() - ), - address(this) - ) - ); - bountyManager.updateClaimContributors(2, DEFAULT_CONTRIBUTORS); - // Reset this address to authorized - _authorizer.setIsAuthorized(address(this), true); - - // Reset this address to be authorized to test correctly - _authorizer.setIsAuthorized(address(this), true); - bountyManager.lockBounty(1); // notLocked vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); bountyManager.updateClaimContributors(2, DEFAULT_CONTRIBUTORS); @@ -849,7 +821,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.verifyClaim(4, DEFAULT_CONTRIBUTORS); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__AlreadyClaimed.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__AlreadyClaimed.selector ); bountyManager.updateClaimContributors(4, DEFAULT_CONTRIBUTORS); } @@ -862,7 +834,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.addClaim(1, DEFAULT_CONTRIBUTORS, bytes("")); vm.expectEmit(true, true, true, true); - emit ClaimDetailsUpdated(2, details); + emit ILM_PC_Bounties_v3.ClaimDetailsUpdated(2, details); vm.prank(DEFAULT_CONTRIBUTORS[0].addr); bountyManager.updateClaimDetails(2, details); @@ -878,13 +850,13 @@ contract LM_PC_BountiesV1Test is ModuleTest { // validClaimId vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidClaimId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidClaimId.selector ); bountyManager.updateClaimDetails(0, bytes("")); // onlyClaimContributor vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__OnlyClaimContributor .selector ); @@ -894,7 +866,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.lockBounty(1); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); vm.prank(DEFAULT_CONTRIBUTORS[0].addr); bountyManager.updateClaimDetails(2, bytes("")); @@ -904,7 +876,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.verifyClaim(4, DEFAULT_CONTRIBUTORS); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__AlreadyClaimed.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__AlreadyClaimed.selector ); vm.prank(DEFAULT_CONTRIBUTORS[0].addr); bountyManager.updateClaimDetails(4, bytes("")); @@ -928,31 +900,27 @@ contract LM_PC_BountiesV1Test is ModuleTest { uint maxAmount = 1_000_000_000_000_000; _token.mint(address(_fundingManager), maxAmount); - ILM_PC_Bounties_v2.Contributor[] memory contribs = + ILM_PC_Bounties_v3.Contributor[] memory contribs = createValidContributors(addrs, amounts); uint bountyId = bountyManager.addBounty(1, maxAmount, details); uint claimId = bountyManager.addClaim(bountyId, contribs, details); vm.expectEmit(true, true, true, true); - emit ClaimVerified(claimId); + emit ILM_PC_Bounties_v3.ClaimVerified(claimId); bountyManager.verifyClaim(claimId, contribs); - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = bountyManager.paymentOrders(); assertEq(length, orders.length); - // Amount of tokens that should be in the LM_PC_RecurringPayments_v2 - uint totalAmount; - // Amount of tokens in a single order uint claimAmount; for (uint i = 0; i < length; i++) { claimAmount = contribs[i].claimAmount; - totalAmount += claimAmount; assertEq(orders[i].recipient, contribs[i].addr); @@ -980,34 +948,31 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.addBounty(1, 100_000_000, bytes("")); // Id 3 bountyManager.addClaim(3, DEFAULT_CONTRIBUTORS, bytes("")); // Id 4 - // Set this address to not authorized to test the roles correctly - _authorizer.setIsAuthorized(address(this), false); + // permissioned - // onlyVerifyAdmin + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(bountyManager), bountyManager.VERIFIER_ROLE() - ), - address(this) + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); bountyManager.verifyClaim(0, DEFAULT_CONTRIBUTORS); - // Reset this address to authorized - _authorizer.setIsAuthorized(address(this), true); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); // validClaimId vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__InvalidClaimId.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__InvalidClaimId.selector ); bountyManager.verifyClaim(0, DEFAULT_CONTRIBUTORS); // _contributorsNotChanged vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__ContributorsChanged .selector ); @@ -1018,7 +983,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.verifyClaim(2, DEFAULT_CONTRIBUTORS); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__AlreadyClaimed.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__AlreadyClaimed.selector ); bountyManager.verifyClaim(2, DEFAULT_CONTRIBUTORS); @@ -1026,7 +991,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bountyManager.lockBounty(3); vm.expectRevert( - ILM_PC_Bounties_v2.Module__LM_PC_Bounty__BountyLocked.selector + ILM_PC_Bounties_v3.Module__LM_PC_Bounty__BountyLocked.selector ); bountyManager.verifyClaim(4, DEFAULT_CONTRIBUTORS); } @@ -1043,7 +1008,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { || maximumPayoutAmount < minimumPayoutAmount ) { vm.expectRevert( - ILM_PC_Bounties_v2 + ILM_PC_Bounties_v3 .Module__LM_PC_Bounty__InvalidPayoutAmounts .selector ); @@ -1068,7 +1033,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { uint id; for (uint i; i < testAmount; i++) { vm.expectEmit(true, true, true, true); - emit BountyAdded( + emit ILM_PC_Bounties_v3.BountyAdded( i + 1, minimumPayoutAmount, maximumPayoutAmount, details ); @@ -1123,7 +1088,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { function createPotentiallyInvalidContributors( address[] memory addrs, uint[] memory amounts - ) internal view returns (ILM_PC_Bounties_v2.Contributor[] memory) { + ) internal view returns (ILM_PC_Bounties_v3.Contributor[] memory) { uint length = addrs.length; assert(length <= amounts.length); address a; @@ -1146,10 +1111,10 @@ contract LM_PC_BountiesV1Test is ModuleTest { } } - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](length); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](length); for (uint i; i < length; i++) { - contribs[i] = ILM_PC_Bounties_v2.Contributor({ + contribs[i] = ILM_PC_Bounties_v3.Contributor({ addr: addrs[i], claimAmount: amounts[i] }); @@ -1160,7 +1125,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { function createValidContributors( address[] memory addrs, uint[] memory amounts - ) internal view returns (ILM_PC_Bounties_v2.Contributor[] memory) { + ) internal view returns (ILM_PC_Bounties_v3.Contributor[] memory) { uint length = addrs.length; assert(length <= amounts.length); address a; @@ -1179,10 +1144,10 @@ contract LM_PC_BountiesV1Test is ModuleTest { } } - ILM_PC_Bounties_v2.Contributor[] memory contribs = - new ILM_PC_Bounties_v2.Contributor[](length); + ILM_PC_Bounties_v3.Contributor[] memory contribs = + new ILM_PC_Bounties_v3.Contributor[](length); for (uint i; i < length; i++) { - contribs[i] = ILM_PC_Bounties_v2.Contributor({ + contribs[i] = ILM_PC_Bounties_v3.Contributor({ addr: addrs[i], claimAmount: amounts[i] }); @@ -1197,7 +1162,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { bytes memory detailsToTest, bool lockedToTest ) internal { - ILM_PC_Bounties_v2.Bounty memory currentBounty = + ILM_PC_Bounties_v3.Bounty memory currentBounty = bountyManager.getBountyInformation(idToProve); assertEq(currentBounty.minimumPayoutAmount, minimumPayoutAmountToTest); @@ -1209,14 +1174,14 @@ contract LM_PC_BountiesV1Test is ModuleTest { function assertEqualClaim( uint idToProve, uint bountyidToTest, - ILM_PC_Bounties_v2.Contributor[] memory contribsToTest, + ILM_PC_Bounties_v3.Contributor[] memory contribsToTest, bytes memory detailsToTest, bool claimedToTest ) internal { - ILM_PC_Bounties_v2.Claim memory currentClaim = + ILM_PC_Bounties_v3.Claim memory currentClaim = bountyManager.getClaimInformation(idToProve); - ILM_PC_Bounties_v2.Contributor[] memory currentContribs = + ILM_PC_Bounties_v3.Contributor[] memory currentContribs = currentClaim.contributors; uint length = currentContribs.length; @@ -1268,7 +1233,7 @@ contract LM_PC_BountiesV1Test is ModuleTest { } function contains( - LM_PC_Bounties_v2.Contributor[] memory searchThrough, + LM_PC_Bounties_v3.Contributor[] memory searchThrough, address addr ) internal pure returns (bool) { uint lengthSearchFor = searchThrough.length; diff --git a/test/unit/modules/logicModule/LM_PC_KPIRewarder_v2.t.sol b/test/unit/modules/logicModule/LM_PC_KPIRewarder_v3.t.sol similarity index 83% rename from test/unit/modules/logicModule/LM_PC_KPIRewarder_v2.t.sol rename to test/unit/modules/logicModule/LM_PC_KPIRewarder_v3.t.sol index 1f32b2e02..9e0bfd071 100644 --- a/test/unit/modules/logicModule/LM_PC_KPIRewarder_v2.t.sol +++ b/test/unit/modules/logicModule/LM_PC_KPIRewarder_v3.t.sol @@ -12,24 +12,24 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; -import {IERC20PaymentClientBase_v2} from - "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "src/modules/logicModule/interfaces/IERC20PaymentClientBase_v3.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; // SuT import { - LM_PC_KPIRewarder_v2, - ILM_PC_KPIRewarder_v2, - IOptimisticOracleIntegrator, - ILM_PC_Staking_v2, + LM_PC_KPIRewarder_v3, + ILM_PC_KPIRewarder_v3, + IOptimisticOracleIntegrator_v3, + ILM_PC_Staking_v3, OptimisticOracleV3CallbackRecipientInterface -} from "src/modules/logicModule/LM_PC_KPIRewarder_v2.sol"; +} from "src/modules/logicModule/LM_PC_KPIRewarder_v3.sol"; import { OptimisticOracleV3Mock, @@ -39,9 +39,9 @@ import { // Mocks import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -contract LM_PC_KPIRewarder_v2Test is ModuleTest { +contract LM_PC_KPIRewarder_v3Test is ModuleTest { // SuT - LM_PC_KPIRewarder_v2 kpiManager; + LM_PC_KPIRewarder_v3 kpiManager; OptimisticOracleV3Mock ooV3; @@ -61,55 +61,6 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { ERC20Mock feeToken = new ERC20Mock("OOV3 Fee Mock Token", "FEE MOCK", 18); uint feeTokenBond; - //========================================================================================= - // Events for emission testing - - event Staked(address indexed user, uint amount); - event DataAsserted( - bytes32 indexed dataId, - bytes32 data, - address indexed asserter, - bytes32 indexed assertionId - ); - event DataAssertionResolved( - bool assertedTruthfully, - bytes32 indexed dataId, - bytes32 data, - address indexed asserter, - bytes32 indexed assertionId - ); - event RewardSet( - uint amount, uint duration, uint rewardRate, uint rewardsEnd - ); - - event KPICreated( - uint indexed KPI_Id, - uint numOfTranches, - uint totalKPIRewards, - bool continuous, - uint[] trancheValues, - uint[] trancheRewards - ); - - event RewardRoundConfigured( - bytes32 indexed assertionId, - uint creationTime, - uint assertedValue, - uint indexed KpiToUse - ); - - event PaymentOrderAdded( - address indexed recipient, - address indexed token, - uint amount, - uint originChainId, - uint targetChainId, - bytes32 flags, - bytes32[] data - ); - - event DeletedStuckAssertion(bytes32 indexed assertionId); - //========================================================================================= // Setup @@ -120,12 +71,13 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { feeTokenBond = ooV3.getMinimumBond(address(feeToken)); // Add Module to Mock Orchestrator - address impl = address(new LM_PC_KPIRewarder_v2()); - kpiManager = LM_PC_KPIRewarder_v2(Clones.clone(impl)); + address impl = address(new LM_PC_KPIRewarder_v3()); + kpiManager = LM_PC_KPIRewarder_v3(Clones.clone(impl)); _setUpOrchestrator(kpiManager); - _authorizer.setIsAuthorized(address(this), true); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); bytes memory configData = abi.encode( address(stakingToken), @@ -150,8 +102,8 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { // Test: Initialization function testInit() public override(ModuleTest) { - address impl = address(new LM_PC_KPIRewarder_v2()); - kpiManager = LM_PC_KPIRewarder_v2(Clones.clone(impl)); + address impl = address(new LM_PC_KPIRewarder_v3()); + kpiManager = LM_PC_KPIRewarder_v3(Clones.clone(impl)); _setUpOrchestrator(kpiManager); @@ -160,13 +112,13 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { ); // Init Module wrongly - vm.expectRevert(IModule_v1.Module__InvalidOrchestratorAddress.selector); - kpiManager.init(IOrchestrator_v1(address(0)), _METADATA, configData); + vm.expectRevert(IModule_v2.Module__InvalidOrchestratorAddress.selector); + kpiManager.init(IOrchestrator_v2(address(0)), _METADATA, configData); // Test invalid staking token vm.expectRevert( - ILM_PC_Staking_v2 - .Module__LM_PC_Staking_v2__InvalidStakingToken + ILM_PC_Staking_v3 + .Module__LM_PC_Staking_v3__InvalidStakingToken .selector ); kpiManager.init( @@ -183,8 +135,8 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { // Test invalid reward token vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__InvalidDefaultCurrency + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__InvalidDefaultCurrency .selector ); kpiManager.init( @@ -201,8 +153,8 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { // Test invalid token bond vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__CurrencyBondTooLow + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__CurrencyBondTooLow .selector ); kpiManager.init( @@ -231,16 +183,26 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { kpiManager.init(_orchestrator, _METADATA, bytes("")); } - function test_InterfaceInheritanceTree() public view { - kpiManager.supportsInterface(type(ILM_PC_KPIRewarder_v2).interfaceId); - kpiManager.supportsInterface(type(ILM_PC_Staking_v2).interfaceId); - kpiManager.supportsInterface( - type(IOptimisticOracleIntegrator).interfaceId + function testSupportsInterface() public override(ModuleTest) { + assertTrue( + kpiManager.supportsInterface( + type(ILM_PC_KPIRewarder_v3).interfaceId + ) + ); + assertTrue( + kpiManager.supportsInterface(type(ILM_PC_Staking_v3).interfaceId) ); - kpiManager.supportsInterface( - type(OptimisticOracleV3CallbackRecipientInterface).interfaceId + assertTrue( + kpiManager.supportsInterface( + type(IOptimisticOracleIntegrator_v3).interfaceId + ) + ); + assertTrue( + kpiManager.supportsInterface( + type(OptimisticOracleV3CallbackRecipientInterface).interfaceId + ) ); - kpiManager.supportsInterface(type(IModule_v1).interfaceId); + assertTrue(kpiManager.supportsInterface(type(IModule_v2).interfaceId)); } // Creates dummy incontinuous KPI with 3 tranches, a max value of 300 and 300e18 tokens for rewards @@ -313,7 +275,7 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { vm.startPrank(cappedUsers[i]); stakingToken.approve(address(kpiManager), cappedAmounts[i]); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit Staked(cappedUsers[i], cappedAmounts[i]); + emit ILM_PC_Staking_v3.Staked(cappedUsers[i], cappedAmounts[i]); kpiManager.stake(cappedAmounts[i]); totalUserFunds += cappedAmounts[i]; vm.stopPrank(); @@ -344,10 +306,6 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { else createDummyIncontinuousKPI(); // prepare bond and asserter authorization - kpiManager.grantModuleRole( - kpiManager.ASSERTER_ROLE(), MOCK_ASSERTER_ADDRESS - ); - feeToken.mint( address(MOCK_ASSERTER_ADDRESS), ooV3.getMinimumBond(address(feeToken)) @@ -362,7 +320,7 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { // SuT vm.expectEmit(true, false, false, false, address(kpiManager)); - emit DataAsserted( + emit IOptimisticOracleIntegrator_v3.DataAsserted( MOCK_ASSERTION_DATA_ID, bytes32(valueToAssert), MOCK_ASSERTER_ADDRESS, @@ -380,6 +338,8 @@ contract LM_PC_KPIRewarder_v2Test is ModuleTest { /* postAssertionTest +├── when the caller is not permissioned +│ └── it should revert (modifier in position check) ├── when the Asserter is the Module itself │ └── when the default currency is the same as the staking token │ └── it should revert @@ -397,13 +357,29 @@ postAssertionTest └── it should return a correct assertionId */ -contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { +contract LM_PC_KPIRewarder_v3_postAssertionTest is LM_PC_KPIRewarder_v3Test { + function test_ModifierInPositionCheck() external { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + kpiManager.postAssertion( + MOCK_ASSERTION_DATA_ID, 100, MOCK_ASSERTER_ADDRESS, 0 + ); + } + function test_RevertWhen_TheBondConfigurationIsInvalid() external { // Since the setup has a correct KPI MAnager, we create a new one with stakingToken == FeeToken - address impl = address(new LM_PC_KPIRewarder_v2()); - LM_PC_KPIRewarder_v2 alt_kpiManager = - LM_PC_KPIRewarder_v2(Clones.clone(impl)); + address impl = address(new LM_PC_KPIRewarder_v3()); + LM_PC_KPIRewarder_v3 alt_kpiManager = + LM_PC_KPIRewarder_v3(Clones.clone(impl)); bytes memory configData = abi.encode( address(feeToken), @@ -418,8 +394,8 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { // it should revert vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__ModuleCannotUseStakingTokenAsBond + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__ModuleCannotUseStakingTokenAsBond .selector ); alt_kpiManager.postAssertion( @@ -430,8 +406,8 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { function test_RevertWhen_ThereAreNoKPIs() external { // it should revert vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidKPINumber + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidKPINumber .selector ); kpiManager.postAssertion( @@ -444,8 +420,8 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { // it should revert vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidKPINumber + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidKPINumber .selector ); kpiManager.postAssertion( @@ -458,9 +434,6 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { createDummyIncontinuousKPI(); // prepare bond and asserter authorization - kpiManager.grantModuleRole( - kpiManager.ASSERTER_ROLE(), MOCK_ASSERTER_ADDRESS - ); feeToken.mint( address(MOCK_ASSERTER_ADDRESS), ooV3.getMinimumBond(address(feeToken)) @@ -473,7 +446,7 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { // SuT vm.expectEmit(true, false, false, false, address(kpiManager)); - emit DataAsserted( + emit IOptimisticOracleIntegrator_v3.DataAsserted( MOCK_ASSERTION_DATA_ID, bytes32(MOCK_ASSERTED_VALUE), MOCK_ASSERTER_ADDRESS, @@ -492,8 +465,8 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { // Posting another assertion should now fail vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__UnresolvedAssertionExists + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__UnresolvedAssertionExists .selector ); vm.prank(address(MOCK_ASSERTER_ADDRESS)); @@ -520,9 +493,6 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { createDummyIncontinuousKPI(); // prepare bond and asserter authorization - kpiManager.grantModuleRole( - kpiManager.ASSERTER_ROLE(), MOCK_ASSERTER_ADDRESS - ); feeToken.mint( address(MOCK_ASSERTER_ADDRESS), ooV3.getMinimumBond(address(feeToken)) @@ -534,7 +504,7 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { // SuT vm.expectEmit(true, false, false, false, address(kpiManager)); - emit DataAsserted( + emit IOptimisticOracleIntegrator_v3.DataAsserted( MOCK_ASSERTION_DATA_ID, bytes32(MOCK_ASSERTED_VALUE), MOCK_ASSERTER_ADDRESS, @@ -542,7 +512,9 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { ); // we don't know the last one vm.expectEmit(false, true, true, true, address(kpiManager)); - emit RewardRoundConfigured(0x0, block.timestamp, 100, 0); // we don't know the generated ID + emit ILM_PC_KPIRewarder_v3.RewardRoundConfigured( + 0x0, block.timestamp, 100, 0 + ); // we don't know the generated ID bytes32 assertionId = kpiManager.postAssertion( MOCK_ASSERTION_DATA_ID, 100, MOCK_ASSERTER_ADDRESS, 0 @@ -557,9 +529,9 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { assertEq(feeToken.balanceOf(MOCK_ASSERTER_ADDRESS), 0); // check mock for stored data - IOptimisticOracleIntegrator.DataAssertion memory assertion = + IOptimisticOracleIntegrator_v3.DataAssertion memory assertion = kpiManager.getAssertion(assertionId); - ILM_PC_KPIRewarder_v2.RewardRoundConfiguration memory rewardRoundConfig = + ILM_PC_KPIRewarder_v3.RewardRoundConfiguration memory rewardRoundConfig = kpiManager.getAssertionConfig(assertionId); assertEq(assertion.dataId, MOCK_ASSERTION_DATA_ID); @@ -585,9 +557,6 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { createDummyIncontinuousKPI(); // prepare bond and asserter authorization - kpiManager.grantModuleRole( - kpiManager.ASSERTER_ROLE(), MOCK_ASSERTER_ADDRESS - ); feeToken.mint( address(MOCK_ASSERTER_ADDRESS), ooV3.getMinimumBond(address(feeToken)) - 1 @@ -615,6 +584,8 @@ contract LM_PC_KPIRewarder_v2_postAssertionTest is LM_PC_KPIRewarder_v2Test { /* createKPITest +├── when the caller is not permissioned +│ └── it should revert (modifier in position check) ├── when the number of tranches is 0 │ └── it should revert ├── when the number of tranches is bigger than 20 @@ -629,7 +600,21 @@ createKPITest */ -contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { +contract LM_PC_KPIRewarder_v3_createKPITest is LM_PC_KPIRewarder_v3Test { + function test_ModifierInPositionCheck() external { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + kpiManager.createKPI(true, new uint[](0), new uint[](0)); + } + function test_RevertWhen_TheNumberOfTranchesIs0() external { // it should revert @@ -637,8 +622,8 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { uint[] memory trancheRewards; vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidTrancheNumber + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidTrancheNumber .selector ); kpiManager.createKPI(true, trancheValues, trancheRewards); @@ -652,8 +637,8 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { vm.assume(trancheValues.length >= 21); vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidTrancheNumber + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidTrancheNumber .selector ); kpiManager.createKPI(true, trancheValues, trancheRewards); @@ -671,8 +656,8 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { if (rewardLength != valueLength) { vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidKPIValueLengths + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidKPIValueLengths .selector ); kpiManager.createKPI( @@ -706,8 +691,8 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { // Guarantee wrong value in the last tranche. valuesCapped[length - 1] = valuesCapped[length - 2] / 2; vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__InvalidKPITrancheValues + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__InvalidKPITrancheValues .selector ); kpiManager.createKPI(true, valuesCapped, rewardsCapped); @@ -737,7 +722,7 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { } vm.expectEmit(true, true, true, true, address(kpiManager)); - emit KPICreated( + emit ILM_PC_KPIRewarder_v3.KPICreated( 0, numOfTranches, totalRewards, @@ -749,7 +734,7 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { uint kpiNum = kpiManager.createKPI(continuous, trancheValues, trancheRewards); - ILM_PC_KPIRewarder_v2.KPI memory generatedKPI = + ILM_PC_KPIRewarder_v3.KPI memory generatedKPI = kpiManager.getKPI(kpiNum); assertEq(generatedKPI.trancheValues.length, numOfTranches); @@ -765,6 +750,8 @@ contract LM_PC_KPIRewarder_v2_createKPITest is LM_PC_KPIRewarder_v2Test { /* stakeTest +├── when the caller is not permissioned +│ └── it should revert (modifier in position check) ├── when the staked amount is 0 │ └── it should revert ├── when there is an unresolved assertion live @@ -775,7 +762,21 @@ stakeTest ├── it should take the funds from the user └── it should stake the funds */ -contract LM_PC_KPIRewarder_v2_stakeTest is LM_PC_KPIRewarder_v2Test { +contract LM_PC_KPIRewarder_v3_stakeTest is LM_PC_KPIRewarder_v3Test { + function test_ModifierInPositionCheck() external { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + kpiManager.stake(0); + } + function test_RevertWhen_TheStakedAmountIs0() external { // it should revert @@ -783,7 +784,7 @@ contract LM_PC_KPIRewarder_v2_stakeTest is LM_PC_KPIRewarder_v2Test { vm.startPrank(USER_1); stakingToken.approve(address(kpiManager), 1000e18); vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); @@ -802,9 +803,6 @@ contract LM_PC_KPIRewarder_v2_stakeTest is LM_PC_KPIRewarder_v2Test { createDummyIncontinuousKPI(); // prepare bond and asserter authorization - kpiManager.grantModuleRole( - kpiManager.ASSERTER_ROLE(), MOCK_ASSERTER_ADDRESS - ); feeToken.mint( address(MOCK_ASSERTER_ADDRESS), ooV3.getMinimumBond(address(feeToken)) @@ -817,7 +815,7 @@ contract LM_PC_KPIRewarder_v2_stakeTest is LM_PC_KPIRewarder_v2Test { // SuT vm.expectEmit(true, false, false, false, address(kpiManager)); - emit DataAsserted( + emit IOptimisticOracleIntegrator_v3.DataAsserted( MOCK_ASSERTION_DATA_ID, bytes32(MOCK_ASSERTED_VALUE), MOCK_ASSERTER_ADDRESS, @@ -838,8 +836,8 @@ contract LM_PC_KPIRewarder_v2_stakeTest is LM_PC_KPIRewarder_v2Test { vm.startPrank(USER_1); stakingToken.approve(address(kpiManager), stakeAmount); vm.expectRevert( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__CannotStakeWhenAssertionPending + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__CannotStakeWhenAssertionPending .selector ); kpiManager.stake(stakeAmount); @@ -899,8 +897,8 @@ assertionresolvedCallbackTest └── it should emit an event */ -contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is - LM_PC_KPIRewarder_v2Test +contract LM_PC_KPIRewarder_v3_assertionresolvedCallbackTest is + LM_PC_KPIRewarder_v3Test { function test_WhenTheAssertionResolvedToFalse( address[] memory users, @@ -920,7 +918,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is vm.expectEmit(true, true, true, true, address(kpiManager)); // vm.expectEmit(false, false, false, false); - emit DataAssertionResolved( + emit IOptimisticOracleIntegrator_v3.DataAssertionResolved( false, MOCK_ASSERTION_DATA_ID, bytes32(assertedIntermediateValue), @@ -964,7 +962,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is vm.startPrank(address(ooV3)); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit DataAssertionResolved( + emit IOptimisticOracleIntegrator_v3.DataAssertionResolved( true, MOCK_ASSERTION_DATA_ID, bytes32(assertedIntermediateValue), @@ -973,7 +971,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is ); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit RewardSet(250e32, 1, 250e32, block.timestamp + 1); + emit ILM_PC_Staking_v3.RewardSet(250e32, 1, 250e32, block.timestamp + 1); kpiManager.assertionResolvedCallback(createdID, true); vm.stopPrank(); @@ -1015,7 +1013,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is if (earnedReward > 0) { vm.expectEmit(true, true, true, true, address(kpiManager)); - emit PaymentOrderAdded( + emit IERC20PaymentClientBase_v3.PaymentOrderAdded( users[i], address(_token), earnedReward, @@ -1056,7 +1054,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is vm.startPrank(address(ooV3)); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit DataAssertionResolved( + emit IOptimisticOracleIntegrator_v3.DataAssertionResolved( true, MOCK_ASSERTION_DATA_ID, bytes32(assertedIntermediateValue), @@ -1065,7 +1063,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is ); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit RewardSet(200e32, 1, 200e32, block.timestamp + 1); + emit ILM_PC_Staking_v3.RewardSet(200e32, 1, 200e32, block.timestamp + 1); kpiManager.assertionResolvedCallback(createdID, true); vm.stopPrank(); @@ -1106,7 +1104,7 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is if (earnedReward > 0) { vm.expectEmit(true, true, true, true, address(kpiManager)); - emit PaymentOrderAdded( + emit IERC20PaymentClientBase_v3.PaymentOrderAdded( users[i], address(_token), earnedReward, @@ -1139,8 +1137,8 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is setUpStateForAssertionResolution(users, amounts, 250, false); vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__CallerNotOO + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__CallerNotOO .selector ); kpiManager.assertionResolvedCallback(createdID, true); @@ -1192,8 +1190,8 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is vm.prank(address(ooV3)); vm.expectRevert( abi.encodeWithSelector( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__NonExistentAssertionId + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__NonExistentAssertionId .selector, fake_ID ) @@ -1210,6 +1208,8 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is /* testDeleteStuckAssertion + ├── When the caller is not permissioned + │ └── It should revert (modifier in position check) ├── When the assertion isn't stored locally │ └── It should revert |── When the assertion hasn't expired yet @@ -1223,9 +1223,23 @@ contract LM_PC_KPIRewarder_v2_assertionresolvedCallbackTest is └── It should emit an event */ -contract LM_PC_KPIRewarder_v2_deleteStuckAssertionTest is - LM_PC_KPIRewarder_v2Test +contract LM_PC_KPIRewarder_v3_deleteStuckAssertionTest is + LM_PC_KPIRewarder_v3Test { + function test_ModifierInPositionCheck() external { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + kpiManager.deleteStuckAssertion(0); + } + function test_RevertWhen_TheAssertionIsntStoredLocally(bytes32 assertionId) external { @@ -1233,8 +1247,8 @@ contract LM_PC_KPIRewarder_v2_deleteStuckAssertionTest is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__NonExistentAssertionId + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__NonExistentAssertionId .selector, assertionId ) @@ -1264,8 +1278,8 @@ contract LM_PC_KPIRewarder_v2_deleteStuckAssertionTest is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__AssertionNotStuck + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__AssertionNotStuck .selector, createdID ) @@ -1295,8 +1309,8 @@ contract LM_PC_KPIRewarder_v2_deleteStuckAssertionTest is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_KPIRewarder_v2 - .Module__LM_PC_KPIRewarder_v2__AssertionNotStuck + ILM_PC_KPIRewarder_v3 + .Module__LM_PC_KPIRewarder_v3__AssertionNotStuck .selector, createdID ) @@ -1328,7 +1342,7 @@ contract LM_PC_KPIRewarder_v2_deleteStuckAssertionTest is feeToken.burn(address(ooV3), ooV3.getMinimumBond(address(feeToken))); vm.expectEmit(true, true, true, true, address(kpiManager)); - emit DeletedStuckAssertion(createdID); + emit ILM_PC_KPIRewarder_v3.DeletedStuckAssertion(createdID); kpiManager.deleteStuckAssertion(createdID); // Check assertion data is deleted diff --git a/test/unit/modules/logicModule/LM_PC_PaymentRouter_v2.t.sol b/test/unit/modules/logicModule/LM_PC_PaymentRouter_v3.t.sol similarity index 74% rename from test/unit/modules/logicModule/LM_PC_PaymentRouter_v2.t.sol rename to test/unit/modules/logicModule/LM_PC_PaymentRouter_v3.t.sol index 364f7aa23..f6f08fc2f 100644 --- a/test/unit/modules/logicModule/LM_PC_PaymentRouter_v2.t.sol +++ b/test/unit/modules/logicModule/LM_PC_PaymentRouter_v3.t.sol @@ -13,24 +13,24 @@ import "@oz/utils/Strings.sol"; import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // SuT -import {LM_PC_PaymentRouter_v2_Exposed} from - "@mocks/modules/logicModule/LM_PC_PaymentRouter_v2_Exposed.sol"; -import {ILM_PC_PaymentRouter_v2} from - "@lm/interfaces/ILM_PC_PaymentRouter_v2.sol"; +import {LM_PC_PaymentRouter_v3_Exposed} from + "@mocks/modules/logicModule/LM_PC_PaymentRouter_v3_Exposed.sol"; +import {ILM_PC_PaymentRouter_v3} from + "@lm/interfaces/ILM_PC_PaymentRouter_v3.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBase_v2 -} from "@lm/abstracts/ERC20PaymentClientBase_v2.sol"; -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3 +} from "@lm/abstracts/ERC20PaymentClientBase_v3.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; import {OrchestratorV1Mock} from "@mocks/orchestrator/OrchestratorV1Mock.sol"; -import {PP_Simple_v2, IPaymentProcessor_v2} from "@pp/PP_Simple_v2.sol"; +import {PP_Simple_v3, IPaymentProcessor_v3} from "@pp/PP_Simple_v3.sol"; import { IFundingManager_v1, @@ -40,9 +40,9 @@ import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; -contract LM_PC_PaymentRouter_v2_Test is ModuleTest { +contract LM_PC_PaymentRouter_v3_Test is ModuleTest { // SuT - LM_PC_PaymentRouter_v2_Exposed paymentRouter; + LM_PC_PaymentRouter_v3_Exposed paymentRouter; address paymentPusher_user = makeAddr("paymentPusher_user"); @@ -66,35 +66,19 @@ contract LM_PC_PaymentRouter_v2_Test is ModuleTest { ); function setUp() public virtual { - // Add Module to Mock Orchestrator_v1 - address impl = address(new LM_PC_PaymentRouter_v2_Exposed()); - paymentRouter = LM_PC_PaymentRouter_v2_Exposed(Clones.clone(impl)); + // Add Module to Mock Orchestrator_v2 + address impl = address(new LM_PC_PaymentRouter_v3_Exposed()); + paymentRouter = LM_PC_PaymentRouter_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(paymentRouter); paymentRouter.init(_orchestrator, _METADATA, bytes("")); - bytes32 roleId = _authorizer.generateRoleId( - address(paymentRouter), paymentRouter.PAYMENT_PUSHER_ROLE() - ); - - _authorizer.grantRole(roleId, paymentPusher_user); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); } function testInit() public override(ModuleTest) { - bytes32 roleId = _authorizer.generateRoleId( - address(paymentRouter), paymentRouter.PAYMENT_PUSHER_ROLE() - ); - - assertEq(_authorizer.hasRole(roleId, paymentPusher_user), true); - assertEq( - _authorizer.checkRoleMembership(roleId, paymentPusher_user), true - ); - - vm.startPrank(address(paymentRouter)); - assertEq(_authorizer.checkForRole(roleId, paymentPusher_user), true); - vm.stopPrank(); - assertEq(paymentRouter.getFlagCount(), 3); bytes32 _START_END_CLIFF_FLAG = 0x000000000000000000000000000000000000000000000000000000000000000e; @@ -105,12 +89,20 @@ contract LM_PC_PaymentRouter_v2_Test is ModuleTest { vm.expectRevert(OZErrors.Initializable__InvalidInitialization); paymentRouter.init(_orchestrator, _METADATA, bytes("")); } + + function testSupportsInterface() public override(ModuleTest) { + assertTrue( + paymentRouter.supportsInterface( + type(ILM_PC_PaymentRouter_v3).interfaceId + ) + ); + } } /* test_pushPayment - ├── When the caller doesn't have the PAYMENT_PUSHER_ROLE - │ └── It should revert + ├── When the caller is not permissioned + │ └── It should revert (modifier in position check) └── When the caller has the PAYMENT_PUSHER_ROLE ├── When the Payment Order is incorrect │ ├── When the recipient is incorrect @@ -125,34 +117,36 @@ contract LM_PC_PaymentRouter_v2_Test is ModuleTest { ├── It should call processPayments └── It should emit an event */ -contract LM_PC_PaymentRouter_v2_Test_pushPayment is - LM_PC_PaymentRouter_v2_Test +contract LM_PC_PaymentRouter_v3_Test_pushPayment is + LM_PC_PaymentRouter_v3_Test { - function test_WhenTheCallerDoesntHaveThePAYMENT_PUSHER_ROLE(address caller) - external - { - // It should revert - _assumeValidAddress(caller); - vm.assume(caller != paymentPusher_user); - vm.startPrank(caller); + function test_ModifierInPositionCheck() external { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(paymentRouter), paymentRouter.PAYMENT_PUSHER_ROLE() - ), - caller + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); paymentRouter.pushPayment(address(0), address(0), 0, 0, 0, 0); - vm.stopPrank(); } } +/* @todo Where is this? @0xNuggan + └── When the Payment Order is correct + ├── It should add the Payment Order to the array of Payment Orders + ├── It should emit an event + ├── It should call processPayments + └── It should emit an event +*/ + /* pushPaymentBatched ├── When the caller doesn't have the PAYMENT_PUSHER_ROLE - │ └── It should revert + │ └── It should revert (modifier in position check) └── When the caller has the PAYMENT_PUSHER_ROLE ├── When the Payment Order arrays are incorrect │ ├── When the array lengths are mismatched @@ -165,8 +159,8 @@ contract LM_PC_PaymentRouter_v2_Test_pushPayment is ├── It should call processPayments └── It should emit an event for each Payment Order */ -contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is - LM_PC_PaymentRouter_v2_Test +contract LM_PC_PaymentRouter_v3_Test_pushPaymentBatched is + LM_PC_PaymentRouter_v3_Test { uint8 numOfOrders = 2; address[] recipients = new address[](2); @@ -192,43 +186,29 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is ends[1] = po_end + 500; } - modifier whenTheCallerHasThePAYMENT_PUSHER_ROLE() { - vm.startPrank(paymentPusher_user); - _; - } + function test_ModifierInPositionCheck() external { + // permissioned - function test_WhenTheCallerDoesntHaveThePAYMENT_PUSHER_ROLE(address caller) - external - { - // It should revert - _assumeValidAddress(caller); - vm.assume(caller != paymentPusher_user); - vm.startPrank(caller); + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.generateRoleId( - address(paymentRouter), paymentRouter.PAYMENT_PUSHER_ROLE() - ), - caller + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); paymentRouter.pushPaymentBatched( 0, new address[](0), new address[](0), new uint[](0), 0, 0, 0 ); - vm.stopPrank(); } - function test_WhenTheArrayLengthsAreMismatched() - external - whenTheCallerHasThePAYMENT_PUSHER_ROLE - { + function test_WhenTheArrayLengthsAreMismatched() external { // It should revert with the corresponding error message vm.expectRevert( abi.encodeWithSelector( - ILM_PC_PaymentRouter_v2 - .Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch + ILM_PC_PaymentRouter_v3 + .Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch .selector ) ); @@ -238,8 +218,8 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_PaymentRouter_v2 - .Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch + ILM_PC_PaymentRouter_v3 + .Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch .selector ) ); @@ -255,8 +235,8 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_PaymentRouter_v2 - .Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch + ILM_PC_PaymentRouter_v3 + .Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch .selector ) ); @@ -266,8 +246,8 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is vm.expectRevert( abi.encodeWithSelector( - ILM_PC_PaymentRouter_v2 - .Module__LM_PC_PaymentRouter_v2__ArrayLengthMismatch + ILM_PC_PaymentRouter_v3 + .Module__LM_PC_PaymentRouter_v3__ArrayLengthMismatch .selector ) ); @@ -284,15 +264,11 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is function test_WhenTheParamatersOfASpecificPaymentOrderAreIncorrect() external - whenTheCallerHasThePAYMENT_PUSHER_ROLE { // It was tested upstream } - function test_WhenThePaymentOrdersAreCorrect(uint8 _numOfOrders) - external - whenTheCallerHasThePAYMENT_PUSHER_ROLE - { + function test_WhenThePaymentOrdersAreCorrect(uint8 _numOfOrders) external { vm.assume(_numOfOrders < 20); // It should add all Payment Orders // It should emit an event for each Payment Order @@ -333,7 +309,7 @@ contract LM_PC_PaymentRouter_v2_Test_pushPaymentBatched is ); } vm.expectEmit(true, false, false, false); - emit IPaymentProcessor_v2.PaymentOrderProcessed( + emit IPaymentProcessor_v3.PaymentOrderProcessed( address(0), address(0), address(0), diff --git a/test/unit/modules/logicModule/LM_PC_RecurringPayments_v2.t.sol b/test/unit/modules/logicModule/LM_PC_RecurringPayments_v3.t.sol similarity index 86% rename from test/unit/modules/logicModule/LM_PC_RecurringPayments_v2.t.sol rename to test/unit/modules/logicModule/LM_PC_RecurringPayments_v3.t.sol index f83e7c65e..f3383ae0b 100644 --- a/test/unit/modules/logicModule/LM_PC_RecurringPayments_v2.t.sol +++ b/test/unit/modules/logicModule/LM_PC_RecurringPayments_v3.t.sol @@ -11,8 +11,8 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Errors @@ -20,46 +20,36 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // SuT import { - LM_PC_RecurringPayments_v2, - ILM_PC_RecurringPayments_v2, - IERC20PaymentClientBase_v2 -} from "@lm/LM_PC_RecurringPayments_v2.sol"; + LM_PC_RecurringPayments_v3, + ILM_PC_RecurringPayments_v3, + IERC20PaymentClientBase_v3 +} from "@lm/LM_PC_RecurringPayments_v3.sol"; contract LM_PC_RecurringV1Test is ModuleTest { // SuT - LM_PC_RecurringPayments_v2 recurringPaymentManager; + LM_PC_RecurringPayments_v3 recurringPaymentManager; uint private constant _SENTINEL = type(uint).max; bytes32 private constant _FLAGS_SET = 0x000000000000000000000000000000000000000000000000000000000000000a; - event RecurringPaymentAdded( - uint indexed recurringPaymentId, - uint amount, - uint startEpoch, - uint lastTriggeredEpoch, - address recipient - ); - event RecurringPaymentRemoved(uint indexed recurringPaymentId); - event RecurringPaymentsTriggered(uint indexed currentEpoch); - event EpochLengthSet(uint epochLength); - function setUp() public { - // Add Module to Mock Orchestrator_v1 - address impl = address(new LM_PC_RecurringPayments_v2()); - recurringPaymentManager = LM_PC_RecurringPayments_v2(Clones.clone(impl)); + // Add Module to Mock Orchestrator_v2 + address impl = address(new LM_PC_RecurringPayments_v3()); + recurringPaymentManager = LM_PC_RecurringPayments_v3(Clones.clone(impl)); _setUpOrchestrator(recurringPaymentManager); - _authorizer.setIsAuthorized(address(this), true); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); } //-------------------------------------------------------------------------- // Test: Initialization - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( recurringPaymentManager.supportsInterface( - type(ILM_PC_RecurringPayments_v2).interfaceId + type(ILM_PC_RecurringPayments_v3).interfaceId ) ); } @@ -67,13 +57,13 @@ contract LM_PC_RecurringV1Test is ModuleTest { // This function also tests all the getters function testInit() public override(ModuleTest) { vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidEpochLength .selector ); vm.expectEmit(true, true, true, true); - emit EpochLengthSet(1 weeks); + emit ILM_PC_RecurringPayments_v3.EpochLengthSet(1 weeks); // Init Module wrongly recurringPaymentManager.init( @@ -81,7 +71,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { ); vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidEpochLength .selector ); @@ -129,7 +119,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { if (id > usedIds || id == 0) { vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidRecurringPaymentId .selector ); @@ -145,7 +135,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { if (currentEpoch > startEpoch) { vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidStartEpoch .selector ); @@ -162,7 +152,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { function testGetRecurringPaymentInformationModifierInPosition() public { vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidRecurringPaymentId .selector ); @@ -206,7 +196,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { startEpoch = bound(startEpoch, currentEpoch, type(uint).max); vm.expectEmit(true, true, true, true); - emit RecurringPaymentAdded( + emit ILM_PC_RecurringPayments_v3.RecurringPaymentAdded( 1, // Id starts at 1 amount, startEpoch, @@ -226,7 +216,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { uint length = bound(amount, 1, 30); // Reasonable amount for (uint i = 2; i < length + 2; i++) { vm.expectEmit(true, true, true, true); - emit RecurringPaymentAdded( + emit ILM_PC_RecurringPayments_v3.RecurringPaymentAdded( i, // Id starts at 1 1, currentEpoch, @@ -252,20 +242,24 @@ contract LM_PC_RecurringV1Test is ModuleTest { // Warp to a reasonable time vm.warp(2 weeks); - // onlyOrchestratorAdmin + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0xBEEF) + IModule_v2.Module__CallerNotPermissioned.selector ) ); - vm.prank(address(0xBEEF)); // Not Authorized + vm.prank(address(0xB0B)); recurringPaymentManager.addRecurringPayment(1, 2 weeks, address(0xBEEF)); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + // validAmount vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); @@ -274,7 +268,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { // validStartEpoch vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidStartEpoch .selector ); @@ -283,7 +277,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { // validRecipient vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidRecipient .selector ); @@ -315,7 +309,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { uint id = i + 1; // Note that id's start at 1. vm.expectEmit(true, true, true, true); - emit RecurringPaymentRemoved(id); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentRemoved(id); recurringPaymentManager.removeRecurringPayment(_SENTINEL, id); assertEq( @@ -329,7 +323,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { // Delete all payments for easier testing _paymentProcessor.deleteAllPayments( - IERC20PaymentClientBase_v2(address(recurringPaymentManager)) + IERC20PaymentClientBase_v3(address(recurringPaymentManager)) ); // Fund Fundingmanager @@ -357,10 +351,12 @@ contract LM_PC_RecurringV1Test is ModuleTest { // Check if trigger was called vm.expectEmit(true, true, true, true); - emit RecurringPaymentsTriggered(currentEpoch); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentsTriggered( + currentEpoch + ); vm.expectEmit(true, true, true, true); - emit RecurringPaymentRemoved(id); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentRemoved(id); recurringPaymentManager.removeRecurringPayment(prevId, id); assertEq( @@ -379,15 +375,16 @@ contract LM_PC_RecurringV1Test is ModuleTest { _orchestrator, _METADATA, abi.encode(1 weeks) ); - // onlyOrchestratorAdmin + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0xBEEF) + IModule_v2.Module__CallerNotPermissioned.selector ) ); - vm.prank(address(0xBEEF)); // Not Authorized + vm.prank(address(0xB0B)); recurringPaymentManager.removeRecurringPayment(0, 1); } @@ -415,15 +412,17 @@ contract LM_PC_RecurringV1Test is ModuleTest { _token.mint(address(_fundingManager), 10_000); // Copy Payments for later comparison - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory recurringPaymentsToBeChecked = fetchRecurringPayments(); // Payout created Payments via trigger vm.expectEmit(true, true, true, true); - emit RecurringPaymentsTriggered(currentEpoch); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentsTriggered( + currentEpoch + ); recurringPaymentManager.trigger(); - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory currentRecurringPayments = fetchRecurringPayments(); // compare that Orders were placed and lastTriggered got updated accordingly @@ -433,7 +432,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { // remove tokens and orders from recurringPaymentManager for easier testing _paymentProcessor.deleteAllPayments( - IERC20PaymentClientBase_v2(address(recurringPaymentManager)) + IERC20PaymentClientBase_v3(address(recurringPaymentManager)) ); _token.burn( address(recurringPaymentManager), @@ -455,7 +454,9 @@ contract LM_PC_RecurringV1Test is ModuleTest { ); currentEpoch = recurringPaymentManager.getCurrentEpoch(); vm.expectEmit(true, true, true, true); - emit RecurringPaymentsTriggered(currentEpoch); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentsTriggered( + currentEpoch + ); recurringPaymentManager.trigger(); currentRecurringPayments = fetchRecurringPayments(); @@ -469,7 +470,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { // remove tokens and orders from recurringPaymentManager for easier testing _paymentProcessor.deleteAllPayments( - IERC20PaymentClientBase_v2(address(recurringPaymentManager)) + IERC20PaymentClientBase_v3(address(recurringPaymentManager)) ); _token.burn( address(recurringPaymentManager), @@ -503,7 +504,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { _token.mint(address(_fundingManager), 500); // Copy Payments for later comparison - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory filteredRecurringPaymentsToBeChecked = filterPayments(fetchRecurringPayments(), startId, endId); @@ -518,11 +519,13 @@ contract LM_PC_RecurringV1Test is ModuleTest { uint currentEpoch = recurringPaymentManager.getCurrentEpoch(); vm.expectEmit(true, true, true, true); - emit RecurringPaymentsTriggered(currentEpoch); + emit ILM_PC_RecurringPayments_v3.RecurringPaymentsTriggered( + currentEpoch + ); recurringPaymentManager.triggerFor(startId, endId); // Get currentPayments and filter them - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory currentRecurringPayments = filterPayments(fetchRecurringPayments(), startId, endId); @@ -546,21 +549,21 @@ contract LM_PC_RecurringV1Test is ModuleTest { ); vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidRecurringPaymentId .selector ); recurringPaymentManager.triggerFor(0, 1); vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__InvalidRecurringPaymentId .selector ); recurringPaymentManager.triggerFor(1, 0); vm.expectRevert( - ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3 .Module__LM_PC_RecurringPayments__StartIdNotBeforeEndId .selector ); @@ -611,7 +614,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { uint lastTriggeredEpoch, address recipient ) internal { - ILM_PC_RecurringPayments_v2.RecurringPayment memory payment = + ILM_PC_RecurringPayments_v3.RecurringPayment memory payment = recurringPaymentManager.getRecurringPaymentInformation(idToProve); assertEq(payment.amount, amount); @@ -648,13 +651,13 @@ contract LM_PC_RecurringV1Test is ModuleTest { function fetchRecurringPayments() internal view - returns (ILM_PC_RecurringPayments_v2.RecurringPayment[] memory) + returns (ILM_PC_RecurringPayments_v3.RecurringPayment[] memory) { uint[] memory ids = recurringPaymentManager.listRecurringPaymentIds(); uint length = ids.length; - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory recurringPayments = - new ILM_PC_RecurringPayments_v2.RecurringPayment[](length); + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory recurringPayments = + new ILM_PC_RecurringPayments_v3.RecurringPayment[](length); for (uint i = 0; i < length; i++) { recurringPayments[i] = @@ -664,16 +667,16 @@ contract LM_PC_RecurringV1Test is ModuleTest { } function filterPayments( - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory paymentsToFilter, + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory paymentsToFilter, uint startId, uint endId ) internal pure - returns (ILM_PC_RecurringPayments_v2.RecurringPayment[] memory) + returns (ILM_PC_RecurringPayments_v3.RecurringPayment[] memory) { uint filterArrayLength = endId - startId + 1; // even if endId and startId are the same its at least one order - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory returnArray = new ILM_PC_RecurringPayments_v2 + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory returnArray = new ILM_PC_RecurringPayments_v3 .RecurringPayment[](filterArrayLength); for (uint i = 0; i < filterArrayLength; i++) { returnArray[i] = paymentsToFilter[startId - 1 + i]; // because ids start at 1 substract 1 to get appropriate array position @@ -683,28 +686,28 @@ contract LM_PC_RecurringV1Test is ModuleTest { // Note: this needs the old version of the orders before the trigger function was called to work function recurringPaymentsAreCorrect( - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory recurringPaymentsToBeChecked, - ILM_PC_RecurringPayments_v2.RecurringPayment[] memory + ILM_PC_RecurringPayments_v3.RecurringPayment[] memory currentRecurringPayments, uint currentEpoch ) internal { uint length = recurringPaymentsToBeChecked.length; - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = recurringPaymentManager.paymentOrders(); assertEq(length, currentRecurringPayments.length); // prediction of how many orders have to be created for this recurring payment uint epochsTriggered; - // Amount of tokens that should be in the LM_PC_RecurringPayments_v2 + // Amount of tokens that should be in the LM_PC_RecurringPayments_v3 uint totalAmount; // Amount of tokens in a single order uint orderAmount; - ILM_PC_RecurringPayments_v2.RecurringPayment memory + ILM_PC_RecurringPayments_v3.RecurringPayment memory currentRecurringPaymentToBeChecked; // Because some of the RecurringPaymentOrders start only in the future we have to have a seperate index for that @@ -759,7 +762,7 @@ contract LM_PC_RecurringV1Test is ModuleTest { } function assertOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order, + IERC20PaymentClientBase_v3.PaymentOrder memory order, address recipient, uint amount, uint start, diff --git a/test/unit/modules/logicModule/LM_PC_Staking_v2.t.sol b/test/unit/modules/logicModule/LM_PC_Staking_v3.t.sol similarity index 86% rename from test/unit/modules/logicModule/LM_PC_Staking_v2.t.sol rename to test/unit/modules/logicModule/LM_PC_Staking_v3.t.sol index 8e4fbd706..bd6dde872 100644 --- a/test/unit/modules/logicModule/LM_PC_Staking_v2.t.sol +++ b/test/unit/modules/logicModule/LM_PC_Staking_v3.t.sol @@ -9,8 +9,8 @@ import {Clones} from "@oz/proxy/Clones.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Errors @@ -18,21 +18,21 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // SuT import { - LM_PC_Staking_v2, - ILM_PC_Staking_v2, + LM_PC_Staking_v3, + ILM_PC_Staking_v3, ReentrancyGuardUpgradeable, - IERC20PaymentClientBase_v2 -} from "@lm/LM_PC_Staking_v2.sol"; + IERC20PaymentClientBase_v3 +} from "@lm/LM_PC_Staking_v3.sol"; -import {LM_PC_Staking_v2_Exposed} from - "@mocks/modules/logicModule/LM_PC_Staking_v2_Exposed.sol"; +import {LM_PC_Staking_v3_Exposed} from + "@mocks/modules/logicModule/LM_PC_Staking_v3_Exposed.sol"; // Mocks import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; -contract LM_PC_Staking_v2Test is ModuleTest { +contract LM_PC_Staking_v3Test is ModuleTest { // SuT - LM_PC_Staking_v2_Exposed stakingManager; + LM_PC_Staking_v3_Exposed stakingManager; ERC20Mock stakingToken = new ERC20Mock("Staking Mock Token", "STAKE MOCK", 18); @@ -41,31 +41,17 @@ contract LM_PC_Staking_v2Test is ModuleTest { uint internal initialStakerMaxAmount = 100; uint internal tokenMultiplicator = 1e18; - // Events - - event RewardSet( - uint rewardAmount, uint duration, uint newRewardRate, uint newRewardsEnd - ); - event Staked(address indexed user, uint amount); - event Unstaked(address indexed user, uint amount); - event RewardsDistributed(address indexed user, uint amount); - event Updated( - address indexed triggerAddress, - uint rewardValue, - uint lastUpdate, - uint earnedRewards - ); - event StakingTokenSet(address indexed token); - function setUp() public { // Add Module to Mock Orchestrator - address impl = address(new LM_PC_Staking_v2_Exposed()); - stakingManager = LM_PC_Staking_v2_Exposed(Clones.clone(impl)); + address impl = address(new LM_PC_Staking_v3_Exposed()); + stakingManager = LM_PC_Staking_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(stakingManager); - _authorizer.setIsAuthorized(address(this), true); + // Every caller has permission for every permissioned function + _authorizer.setAllAuthorized(true); + vm.expectEmit(true, true, true, true); - emit StakingTokenSet(address(stakingToken)); + emit ILM_PC_Staking_v3.StakingTokenSet(address(stakingToken)); stakingManager.init( _orchestrator, _METADATA, abi.encode(address(stakingToken)) ); @@ -87,14 +73,14 @@ contract LM_PC_Staking_v2Test is ModuleTest { _orchestrator, _METADATA, abi.encode(address(stakingToken)) ); - address impl = address(new LM_PC_Staking_v2_Exposed()); - stakingManager = LM_PC_Staking_v2_Exposed(Clones.clone(impl)); + address impl = address(new LM_PC_Staking_v3_Exposed()); + stakingManager = LM_PC_Staking_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(stakingManager); _authorizer.setIsAuthorized(address(this), true); vm.expectRevert( - ILM_PC_Staking_v2 - .Module__LM_PC_Staking_v2__InvalidStakingToken + ILM_PC_Staking_v3 + .Module__LM_PC_Staking_v3__InvalidStakingToken .selector ); stakingManager.init( @@ -102,6 +88,14 @@ contract LM_PC_Staking_v2Test is ModuleTest { ); } + function testSupportsInterface() public override(ModuleTest) { + assertTrue( + stakingManager.supportsInterface( + type(ILM_PC_Staking_v3).interfaceId + ) + ); + } + //-------------------------------------------------------------------------- // Modifier @@ -109,8 +103,8 @@ contract LM_PC_Staking_v2Test is ModuleTest { duration = bound(duration, 0, 31_536_000_000); // 31536000000 = 1000 years in seconds if (duration == 0) { vm.expectRevert( - ILM_PC_Staking_v2 - .Module__LM_PC_Staking_v2__InvalidDuration + ILM_PC_Staking_v3 + .Module__LM_PC_Staking_v3__InvalidDuration .selector ); } @@ -193,7 +187,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { function testEstimateRewardModifierInPosition() public { // validAmount vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); @@ -202,7 +196,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { // validDuration vm.expectRevert( - ILM_PC_Staking_v2.Module__LM_PC_Staking_v2__InvalidDuration.selector + ILM_PC_Staking_v3.Module__LM_PC_Staking_v3__InvalidDuration.selector ); stakingManager.getEstimatedReward(1, 0); @@ -248,7 +242,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { uint expectedEarnings = stakingManager.getEarned(staker); vm.expectEmit(true, true, true, true); - emit Staked(staker, stakeAmount); + emit ILM_PC_Staking_v3.Staked(staker, stakeAmount); vm.prank(staker); stakingManager.stake(stakeAmount); @@ -269,13 +263,28 @@ contract LM_PC_Staking_v2Test is ModuleTest { function testStakeModifierInPosition() public { // validAmount vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); stakingManager.stake(0); + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + stakingManager.stake(1); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + // Check for reentrancy // Set it so that the stakingToken does a reentrancy call on the stakingManager @@ -345,7 +354,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { uint expectedEarnings = stakingManager.getEarned(staker); vm.expectEmit(true, true, true, true); - emit Unstaked(staker, unstakeAmount); + emit ILM_PC_Staking_v3.Unstaked(staker, unstakeAmount); // Withdraw vm.prank(staker); @@ -369,13 +378,28 @@ contract LM_PC_Staking_v2Test is ModuleTest { function testUnstakeModifierInPosition() public { // validAmount vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); stakingManager.unstake(0); + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + stakingManager.unstake(1); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); + // Check for reentrancy // Set it so that the stakingToken does a reentrancy call on the stakingManager @@ -444,8 +468,8 @@ contract LM_PC_Staking_v2Test is ModuleTest { if (expectedRewardRate == 0) { vm.expectRevert( - ILM_PC_Staking_v2 - .Module__LM_PC_Staking_v2__InvalidRewardRate + ILM_PC_Staking_v3 + .Module__LM_PC_Staking_v3__InvalidRewardRate .selector ); stakingManager.setRewards(amount, duration); @@ -454,7 +478,9 @@ contract LM_PC_Staking_v2Test is ModuleTest { } vm.expectEmit(true, true, true, true); - emit RewardSet(amount, duration, expectedRewardRate, expectedRewardsEnd); + emit ILM_PC_Staking_v3.RewardSet( + amount, duration, expectedRewardRate, expectedRewardsEnd + ); stakingManager.setRewards(amount, duration); @@ -473,8 +499,8 @@ contract LM_PC_Staking_v2Test is ModuleTest { if (expectedRewardRate == 0) { vm.expectRevert( - ILM_PC_Staking_v2 - .Module__LM_PC_Staking_v2__InvalidRewardRate + ILM_PC_Staking_v3 + .Module__LM_PC_Staking_v3__InvalidRewardRate .selector ); stakingManager.setRewards(secondAmount, secondDuration); @@ -483,7 +509,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { } vm.expectEmit(true, true, true, true); - emit RewardSet( + emit ILM_PC_Staking_v3.RewardSet( secondAmount, secondDuration, expectedRewardRate, expectedRewardsEnd ); @@ -494,21 +520,21 @@ contract LM_PC_Staking_v2Test is ModuleTest { } function testSetRewardsModifierInPosition() public { - // onlyOrchestratorAdmin + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - _authorizer.getAdminRole(), - address(0xBEEF) + IModule_v2.Module__CallerNotPermissioned.selector ) ); - - vm.prank(address(0xBEEF)); - stakingManager.setRewards(1, 1); + vm.prank(address(0xB0B)); + stakingManager.stake(1); // validAmount vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidAmount .selector ); @@ -517,7 +543,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { // validDuration vm.expectRevert( - ILM_PC_Staking_v2.Module__LM_PC_Staking_v2__InvalidDuration.selector + ILM_PC_Staking_v3.Module__LM_PC_Staking_v3__InvalidDuration.selector ); stakingManager.setRewards(1, 0); @@ -544,7 +570,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { stakingManager.direct_calculateRewardValue(); } vm.expectEmit(true, true, true, false); - emit Updated( + emit ILM_PC_Staking_v3.Updated( trigger, expectedRewards, stakingManager.getLastUpdate(), 0 ); @@ -552,7 +578,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { // Check that values changed assertEq( - stakingManager.direct_calculateRewardValue(), // works because time between last update and RewardDistributionTimestamp value is 0 and therefor just returns the older rewardValue + stakingManager.direct_calculateRewardValue(), // works because time between last update and RewardDistributionTimestamp value is 0 and therefore just returns the older rewardValue stakingManager.getRewardValue() ); assertEq(stakingManager.getLastUpdate(), stakingManager.getLastUpdate()); @@ -641,7 +667,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { stakingManager.direct_update(user); vm.expectEmit(true, true, true, true); - emit RewardsDistributed(user, expectedPayout); + emit ILM_PC_Staking_v3.RewardsDistributed(user, expectedPayout); stakingManager.direct_distributeRewards(user); @@ -649,7 +675,7 @@ contract LM_PC_Staking_v2Test is ModuleTest { assertEq(0, stakingManager.getUserRewards(user)); // Expect paymentOrder to be correct - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = stakingManager.paymentOrders(); assertEq(1, orders.length); diff --git a/test/unit/modules/logicModule/abstract/ERCPaymentClientBase_v2.t.sol b/test/unit/modules/logicModule/abstract/ERCPaymentClientBase_v2.t.sol index b4f561003..160d97ee3 100644 --- a/test/unit/modules/logicModule/abstract/ERCPaymentClientBase_v2.t.sol +++ b/test/unit/modules/logicModule/abstract/ERCPaymentClientBase_v2.t.sol @@ -10,23 +10,23 @@ import {Clones} from "@oz/proxy/Clones.sol"; import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // SuT import { ERC20PaymentClientBaseV2_Exposed, - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 } from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2_Exposed.sol"; -import {Module_v1, IModule_v1} from "src/modules/base/Module_v1.sol"; +import {Module_v2, IModule_v2} from "src/modules/base/Module_v2.sol"; import {OrchestratorV1Mock} from "@mocks/orchestrator/OrchestratorV1Mock.sol"; import { - PaymentProcessorV1Mock, - IPaymentProcessor_v2 -} from "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; + PaymentProcessor_v3_Mock, + IPaymentProcessor_v3 +} from "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; import { IFundingManager_v1, FundingManagerV1Mock @@ -75,10 +75,10 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { function testReinitFails() public override {} - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( paymentClient.supportsInterface( - type(IERC20PaymentClientBase_v2).interfaceId + type(IERC20PaymentClientBase_v3).interfaceId ) ); } @@ -100,7 +100,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { _assumeValidAmount(amount); for (uint i; i < orderAmount; ++i) { - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createPaymentOrder(address(_token), recipient, amount, end); vm.expectEmit(); @@ -117,7 +117,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { paymentClient.exposed_addPaymentOrder(order); } - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = paymentClient.paymentOrders(); assertEq(orders.length, orderAmount); @@ -142,12 +142,12 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { _paymentProcessor.flipValidOrder(); vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ); paymentClient.exposed_addPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0), paymentToken: address(_token), amount: 1, @@ -163,10 +163,10 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { // Test: addPaymentOrders() function testAddPaymentOrders() public { - IERC20PaymentClientBase_v2.PaymentOrder[] memory ordersToAdd = - new IERC20PaymentClientBase_v2.PaymentOrder[](3); + IERC20PaymentClientBase_v3.PaymentOrder[] memory ordersToAdd = + new IERC20PaymentClientBase_v3.PaymentOrder[](3); - ordersToAdd[0] = IERC20PaymentClientBase_v2.PaymentOrder({ + ordersToAdd[0] = IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xCAFE1), paymentToken: address(_token), amount: 100e18, @@ -184,7 +184,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { data[0] = bytes32(block.timestamp); data[1] = bytes32(block.timestamp + 1); - ordersToAdd[1] = IERC20PaymentClientBase_v2.PaymentOrder({ + ordersToAdd[1] = IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xCAFE2), paymentToken: address(_token), amount: 100e18, @@ -198,7 +198,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { data[0] = bytes32(block.timestamp); data[1] = bytes32(block.timestamp + 2); - ordersToAdd[2] = IERC20PaymentClientBase_v2.PaymentOrder({ + ordersToAdd[2] = IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xCAFE3), paymentToken: address(_token), amount: 100e18, @@ -238,7 +238,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { ); paymentClient.exposed_addPaymentOrders(ordersToAdd); - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = paymentClient.paymentOrders(); assertEq(orders.length, 3); for (uint i; i < 3; ++i) { @@ -275,7 +275,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { ); } - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; address[] memory tokens; uint[] memory totalOutstandingAmounts; vm.prank(address(_paymentProcessor)); @@ -298,8 +298,8 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { assertEq(totalOutstandingAmounts.length, 1); assertEq(totalOutstandingAmounts[0], orderAmount * amount); - // Check that orders in ERC20PaymentClientBase_v2 got reset. - IERC20PaymentClientBase_v2.PaymentOrder[] memory updatedOrders; + // Check that orders in ERC20PaymentClientBase_v3 got reset. + IERC20PaymentClientBase_v3.PaymentOrder[] memory updatedOrders; updatedOrders = paymentClient.paymentOrders(); assertEq(updatedOrders.length, 0); @@ -309,7 +309,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { totalOutstandingAmounts[0] ); - // Check that we received allowance to fetch tokens from ERC20PaymentClientBase_v2. + // Check that we received allowance to fetch tokens from ERC20PaymentClientBase_v3. assertTrue( _token.allowance(address(paymentClient), address(_paymentProcessor)) >= totalOutstandingAmounts[0] @@ -317,7 +317,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { } function testCollectPaymentOrders_IfThereAreNoOrders() public { - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders; address[] memory tokens; uint[] memory totalOutstandingAmounts; vm.prank(address(_paymentProcessor)); @@ -330,14 +330,14 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { assertEq(totalOutstandingAmounts.length, 0); // Check that there are no orders in the paymentClient - IERC20PaymentClientBase_v2.PaymentOrder[] memory updatedOrders; + IERC20PaymentClientBase_v3.PaymentOrder[] memory updatedOrders; updatedOrders = paymentClient.paymentOrders(); assertEq(updatedOrders.length, 0); } function testCollectPaymentOrdersFailsCallerNotAuthorized() public { vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__CallerNotAuthorized .selector ); @@ -368,7 +368,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { if (caller != address(_paymentProcessor)) { vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__CallerNotAuthorized .selector ); @@ -389,7 +389,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { _token.mint(address(paymentClient), currentFunds); // create paymentOrder with required amount - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createPaymentOrder( address(_token), address(0xA11CE), amountRequired, block.timestamp ); @@ -431,7 +431,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { ); // we add the first paymentOrder to increase the outstanding amount - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createPaymentOrder( address(_token), address(0xA11CE), firstAmount, block.timestamp ); @@ -466,7 +466,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { function testIsAuthorizedPaymentProcessor(address addr) public { bool isAuthorized = paymentClient.exposed_isAuthorizedPaymentProcessor( - IPaymentProcessor_v2(addr) + IPaymentProcessor_v3(addr) ); if (addr == address(_paymentProcessor)) { @@ -501,7 +501,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__MismatchBetweenFlagCountAndArrayLength .selector, paymentClient.getFlagCount(), @@ -582,7 +582,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { address recipient, uint amount, uint end - ) internal view returns (IERC20PaymentClientBase_v2.PaymentOrder memory) { + ) internal view returns (IERC20PaymentClientBase_v3.PaymentOrder memory) { bytes32[] memory data = new bytes32[](end != 0 ? 3 : 2); data[0] = bytes32(block.timestamp); data[1] = bytes32(0); @@ -590,7 +590,7 @@ contract ERC20PaymentClientBaseV2Test is ModuleTest { data[2] = bytes32(end); } - return IERC20PaymentClientBase_v2.PaymentOrder({ + return IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, paymentToken: paymentToken, amount: amount, diff --git a/test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator.t.sol b/test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator_v3.t.sol similarity index 80% rename from test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator.t.sol rename to test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator_v3.t.sol index 711deebf8..8a8b1b8bf 100644 --- a/test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator.t.sol +++ b/test/unit/modules/logicModule/abstract/oracle/OptimisticOracleIntegrator_v3.t.sol @@ -4,8 +4,11 @@ pragma solidity ^0.8.0; import "forge-std/console.sol"; // SuT -import {OptimisticOracleIntegratorMock} from - "@mocks/modules/logicModule/oracle/OptimisiticOracleIntegratorMock.sol"; +import {OptimisticOracleIntegrator_v3_Mock} from + "@mocks/modules/logicModule/oracle/OptimisticOracleIntegrator_v3_Mock.sol"; + +import {OptimisticOracleV3CallbackRecipientInterface} from + "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/optimistic-oracle-v3/interfaces/OptimisticOracleV3CallbackRecipientInterface.sol"; import {OptimisticOracleV3Mock} from "@mocks/modules/logicModule/oracle/OptimisiticOracleV3Mock.sol"; @@ -16,8 +19,8 @@ import {Clones} from "@oz/proxy/Clones.sol"; // Internal Dependencies import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // Errors @@ -25,14 +28,14 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // SuT import { - OptimisticOracleIntegrator, - IOptimisticOracleIntegrator + OptimisticOracleIntegrator_v3, + IOptimisticOracleIntegrator_v3 } from - "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator.sol"; + "@lm/abstracts/oracleIntegrations/UMA_OptimisticOracleV3/OptimisticOracleIntegrator_v3.sol"; -contract OptimisticOracleIntegratorTest is ModuleTest { +contract OptimisticOracleIntegrator_v3_Test is ModuleTest { address ooIntegratorImplementation; - OptimisticOracleIntegratorMock ooIntegrator; + OptimisticOracleIntegrator_v3_Mock ooIntegrator; OptimisticOracleV3Mock ooV3; uint64 immutable DEFAULT_LIVENESS = 25_000; @@ -51,8 +54,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { // Add Module to Mock Orchestrator ooIntegratorImplementation = - address(new OptimisticOracleIntegratorMock()); - ooIntegrator = OptimisticOracleIntegratorMock( + address(new OptimisticOracleIntegrator_v3_Mock()); + ooIntegrator = OptimisticOracleIntegrator_v3_Mock( Clones.clone(ooIntegratorImplementation) ); @@ -85,8 +88,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { function testInit() public override(ModuleTest) { // set up new orchestrator ooIntegratorImplementation = - address(new OptimisticOracleIntegratorMock()); - ooIntegrator = OptimisticOracleIntegratorMock( + address(new OptimisticOracleIntegrator_v3_Mock()); + ooIntegrator = OptimisticOracleIntegrator_v3_Mock( Clones.clone(ooIntegratorImplementation) ); _setUpOrchestrator(ooIntegrator); @@ -95,8 +98,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { abi.encode(address(_token), address(ooV3), DEFAULT_LIVENESS); // Init Module wrongly - vm.expectRevert(IModule_v1.Module__InvalidOrchestratorAddress.selector); - ooIntegrator.init(IOrchestrator_v1(address(0)), _METADATA, _configData); + vm.expectRevert(IModule_v2.Module__InvalidOrchestratorAddress.selector); + ooIntegrator.init(IOrchestrator_v2(address(0)), _METADATA, _configData); // Test invalid token vm.expectRevert(); @@ -120,6 +123,19 @@ contract OptimisticOracleIntegratorTest is ModuleTest { ooIntegrator.init(_orchestrator, _METADATA, bytes("")); } + function testSupportsInterface() public override(ModuleTest) { + assertTrue( + ooIntegrator.supportsInterface( + type(IOptimisticOracleIntegrator_v3).interfaceId + ) + ); + assertTrue( + ooIntegrator.supportsInterface( + type(OptimisticOracleV3CallbackRecipientInterface).interfaceId + ) + ); + } + // Tests /* @@ -161,8 +177,9 @@ contract OptimisticOracleIntegratorTest is ModuleTest { // Setter Functions /* - When the caller is not the admin - reverts (tested in module tests) + Test: setDefaultCurrencyAndBond + When the caller is not permissioned + It should revert (modifier in position check) When the caller is the admin when the address is 0 reverts @@ -177,12 +194,26 @@ contract OptimisticOracleIntegratorTest is ModuleTest { */ + function testSetDefaultCurrencyAndBond_modifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + ooIntegrator.setDefaultCurrencyAndBond(address(0), 0); + } + function testsetDefaultCurrencyAndBondFails_whenNewCurrencyIsZero() public { vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__InvalidDefaultCurrency + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__InvalidDefaultCurrency .selector ); ooIntegrator.setDefaultCurrencyAndBond(address(0), 0); @@ -200,8 +231,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { ooV3.whitelistCurrency(whitelisted, minimumBond); vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__CurrencyBondTooLow + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__CurrencyBondTooLow .selector ); ooIntegrator.setDefaultCurrencyAndBond(whitelisted, proposedBond); @@ -221,8 +252,9 @@ contract OptimisticOracleIntegratorTest is ModuleTest { } /* - When the caller is not the admin - reverts (tested in module tests) + Test: setOptimisticOracle + When the caller is not permissioned + It should revert (modifier in position check) When the caller is the admin when the address is 0 reverts @@ -232,10 +264,25 @@ contract OptimisticOracleIntegratorTest is ModuleTest { sets the new address as optimistic oracle emits an event */ + + function testSetOptimisticOracle_modifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + ooIntegrator.setOptimisticOracle(address(0)); + } + function testSetOptimisticOracleFails_WhenNewOracleIsZero() public { vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__InvalidOOInstance + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__InvalidOOInstance .selector ); ooIntegrator.setOptimisticOracle(address(0)); @@ -259,8 +306,9 @@ contract OptimisticOracleIntegratorTest is ModuleTest { } /* - When the caller is not the admin - reverts (tested in module tests) + Test: setDefaultAssertionLiveness + When the caller is not permissioned + It should revert (modifier in position check) When the caller is the admin when the liveness is below 6 hours reverts @@ -268,14 +316,27 @@ contract OptimisticOracleIntegratorTest is ModuleTest { sets the new liveness emits an event */ + function testSetDefaultAssertionLiveness_modifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + ooIntegrator.setDefaultAssertionLiveness(0); + } function testSetDefaultAssertionLivenessFails_whenLivenessLessThanSixHours( uint64 newLiveness ) public { vm.assume(newLiveness < 21_600); vm.expectRevert( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__InvalidDefaultLiveness + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__InvalidDefaultLiveness .selector ); ooIntegrator.setDefaultAssertionLiveness(newLiveness); @@ -288,8 +349,9 @@ contract OptimisticOracleIntegratorTest is ModuleTest { } /* - When the caller does not have asserter role - reverts + Test: assertDataFor + When the caller is not permissioned + It should revert (modifier in position check) when the caller has the asserter role when the asserter address is 0 it uses msgSender as asserter address @@ -311,21 +373,17 @@ contract OptimisticOracleIntegratorTest is ModuleTest { */ - function testAssertDataForFails_whenCallerDoesNotHaveAsserterRole() - public - { - bytes32 roleId = _authorizer.generateRoleId( - address(ooIntegrator), ooIntegrator.ASSERTER_ROLE() - ); + function testAssertDataFor_modifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions _authorizer.setAllAuthorized(false); - vm.prank(address(0xBEEF)); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - address(0xBEEF) + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); ooIntegrator.assertDataFor( MOCK_ASSERTION_DATA_ID, MOCK_ASSERTION_DATA, MOCK_ASSERTER_ADDRESS ); @@ -346,8 +404,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { // Since we are using the mockauthorizer, the asserter has the asserter role vm.expectRevert( abi.encodeWithSelector( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond .selector ) ); @@ -377,8 +435,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { vm.prank(prankUser); vm.expectRevert( abi.encodeWithSelector( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond .selector ) ); @@ -407,8 +465,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { vm.prank(prankUser); vm.expectRevert( abi.encodeWithSelector( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator_InsufficientFundsToPayForBond + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3_InsufficientFundsToPayForBond .selector ) ); @@ -515,8 +573,8 @@ contract OptimisticOracleIntegratorTest is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IOptimisticOracleIntegrator - .Module__OptimisticOracleIntegrator__CallerNotOO + IOptimisticOracleIntegrator_v3 + .Module__OptimisticOracleIntegrator_v3__CallerNotOO .selector ) ); diff --git a/test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v1.t.sol b/test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v2.t.sol similarity index 91% rename from test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v1.t.sol rename to test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v2.t.sol index e51a285c7..a101f7254 100644 --- a/test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v1.t.sol +++ b/test/unit/modules/paymentProcessor/PP_Everclear_Crosschain_v2.t.sol @@ -3,12 +3,12 @@ pragma solidity ^0.8.0; // Internal Imports import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IPP_CrossChainBase_v1} from "@pp/interfaces/IPP_CrossChainBase_v1.sol"; +import {IPP_CrossChainBase_v2} from "@pp/interfaces/IPP_CrossChainBase_v2.sol"; import {IModule_v1} from "src/modules/base/IModule_v1.sol"; -import {IPP_Everclear_CrossChain_v1} from - "@pp/interfaces/IPP_Everclear_CrossChain_v1.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IPP_Everclear_CrossChain_v2} from + "@pp/interfaces/IPP_Everclear_CrossChain_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // External Imports import {Clones} from "@oz/proxy/Clones.sol"; @@ -19,14 +19,14 @@ import {IEverclear} from "@pp/interfaces/IEverclear.sol"; import {ModuleTest} from "@unitTest/modules/ModuleTest.sol"; import {EverclearPaymentMock} from "@mocks/external/integrations/EverclearPaymentMock.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // SuT -import {PP_Everclear_CrossChain_v1_Exposed} from - "@mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v1_Exposed.sol"; +import {PP_Everclear_CrossChain_v2_Exposed} from + "@mocks/modules/paymentProcessor/PP_Everclear_CrossChain_v2_Exposed.sol"; -contract PP_Everclear_CrossChain_v1_Test is ModuleTest { +contract PP_Everclear_CrossChain_v2_Test is ModuleTest { // ======================================================================== // Constants @@ -37,10 +37,10 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { // ======================================================================== // State - PP_Everclear_CrossChain_v1_Exposed public paymentProcessor; + PP_Everclear_CrossChain_v2_Exposed public paymentProcessor; EverclearPaymentMock public everclearPaymentMock; - ERC20PaymentClientBaseV2Mock paymentClient; - IPP_CrossChainBase_v1 public CrossChainBase; + ERC20PaymentClientBase_v3_Mock paymentClient; + IPP_CrossChainBase_v2 public CrossChainBase; // Bridge-related storage address public mockConnextBridge; @@ -63,13 +63,13 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { mockEverClearSpoke = address(everclearPaymentMock); // Deploy and setup the payment client for testing SUT - address impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + address impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); // Deploy and init the SUT - impl = address(new PP_Everclear_CrossChain_v1_Exposed()); + impl = address(new PP_Everclear_CrossChain_v2_Exposed()); paymentProcessor = - PP_Everclear_CrossChain_v1_Exposed(Clones.clone(impl)); + PP_Everclear_CrossChain_v2_Exposed(Clones.clone(impl)); // Setup the mock workflow contracts and token _setUpOrchestrator(paymentClient); @@ -100,17 +100,17 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { ); } - function testSupportsInterface() public { - // Test for IPP_CrossChainBase_v1 interface + function testSupportsInterface() public override { + // Test for IPP_CrossChainBase_v2 interface assertTrue( paymentProcessor.supportsInterface( - type(IPP_CrossChainBase_v1).interfaceId + type(IPP_CrossChainBase_v2).interfaceId ) ); - // Test for IPP_Everclear_CrossChain_v1 interface + // Test for IPP_Everclear_CrossChain_v2 interface assertTrue( paymentProcessor.supportsInterface( - type(IPP_Everclear_CrossChain_v1).interfaceId + type(IPP_Everclear_CrossChain_v2).interfaceId ) ); // Test for IPaymentProcessor_v2 interface @@ -207,7 +207,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.prank(address(paymentClient)); // Execute the transaction paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq(_token.balanceOf(address(mockEverClearSpoke)), testAmount); bytes32 intentId = bytes32(paymentProcessor.getBridgeDataByPaymentId(0)); @@ -229,8 +229,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount, EMPTY_EXECUTION_DATA); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); assertEq(client.outstandingTokenAmount(address(_token)), testAmount); vm.prank(address(paymentClient)); @@ -268,8 +268,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { ); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); bytes32[] memory executionData = _getExecutionData(); @@ -337,8 +337,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { ); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); assertEq( client.outstandingTokenAmount(address(_token)), OutstandingAmount @@ -360,7 +360,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { // Process payments and verify _paymentIdToBridgeData mapping is not updated vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); assertTrue( keccak256(paymentProcessor.getBridgeDataByPaymentId(0)) @@ -389,13 +389,13 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _setupSinglePayment(testRecipient, testAmount, customExecutionData); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); // Process payments vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -417,8 +417,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { bytes32[] memory customExecutionData = _getExecutionData(); // Create payment order with fuzzed time range - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: testRecipient, paymentToken: address(_token), amount: testAmount, @@ -431,13 +431,13 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { paymentClient.exposed_addPaymentOrder(order); vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ); vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -453,12 +453,12 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _setupSinglePayment(address(0), testAmount, EMPTY_EXECUTION_DATA); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); // Process payments vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ); @@ -478,12 +478,12 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _setupSinglePayment(testRecipient, 0, EMPTY_EXECUTION_DATA); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); // Process payments vm.expectRevert( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ); @@ -505,8 +505,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _assumeValidRecipientAndAmount(testRecipient, testAmount); _setupSinglePayment(testRecipient, testAmount, EMPTY_EXECUTION_DATA); // Get the client interface - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); vm.prank(address(paymentClient)); // Process payments and verify _paymentIdToBridgeData mapping is updated paymentProcessor.processPayments(client); @@ -528,8 +528,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { └── Then it should return empty bytes */ function testProcessPayments_succeedsGivenEmptyBridgeData() public { - IERC20PaymentClientBase_v2 client = - IERC20PaymentClientBase_v2(address(paymentClient)); + IERC20PaymentClientBase_v3 client = + IERC20PaymentClientBase_v3(address(paymentClient)); // Process payments and verify _paymentIdToBridgeData mapping is updated vm.prank(address(paymentClient)); paymentProcessor.processPayments(client); @@ -565,7 +565,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { // Action - Process payments vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -583,14 +583,14 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup the payment and process it - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = _setupSinglePayment(testRecipient, testAmount, EMPTY_EXECUTION_DATA); uint balanceBefore = _token.balanceOf(address(paymentProcessor)); everclearPaymentMock.setMockBridgeToFail(true); //Force the bridge transfer to fail vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); uint balanceAfter = _token.balanceOf(address(paymentProcessor)); assertEq(balanceAfter, balanceBefore + testAmount); @@ -640,7 +640,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.prank(address(paymentClient)); // Process payment to create intent paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); // Prank as non-recipient @@ -676,7 +676,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _setupSinglePayment(testRecipient, testAmount, EMPTY_EXECUTION_DATA); vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); // Cancel the transfer @@ -712,14 +712,14 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) ); vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -735,8 +735,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _assumeValidRecipientAndAmount(testRecipient, testAmount); // Setup payment with unsupported token - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: testRecipient, paymentToken: address(0xDEADBEEF), // Unsupported token address amount: testAmount, @@ -752,7 +752,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.expectRevert(); vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -782,7 +782,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.prank(address(paymentClient)); // Process payments paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); // Verify final intent ID exists @@ -811,8 +811,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { bytes32[] memory customExecutionData = _getExecutionData(); customExecutionData[3] = bytes32(uint(block.timestamp + timeOffset)); // Create payment order with fuzzed time range - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: testRecipient, paymentToken: address(_token), amount: testAmount, @@ -826,7 +826,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { vm.prank(address(paymentClient)); paymentProcessor.processPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); bytes32 intentId = bytes32( @@ -846,8 +846,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { └── Then it should return false */ function testValidPaymentOrder_revertsGivenInvalidRecipient() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0), paymentToken: address(_token), amount: 10 ether, @@ -865,8 +865,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { └── Then it should return false */ function testValidPaymentOrder_revertsGivenInvalidToken() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xBEEF), paymentToken: address(0), amount: 1, @@ -884,8 +884,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { └── Then it should return false */ function testValidPaymentOrder_revertsGivenInvalidAmount() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xBEEF), paymentToken: address(_token), amount: 0, @@ -903,8 +903,8 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { └── Then it should return true */ function testValidPaymentOrder_succeedsGivenValidPaymentOrder() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0xBEEF), paymentToken: address(_token), amount: 10 ether, @@ -930,7 +930,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { (bytes32 intentId, IEverclear.Intent memory intent) = _getValidIntentIdAndIntent(); // Create valid payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( address(paymentClient), 10 ether, @@ -959,7 +959,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { order.flags, order.data ); - emit IPP_CrossChainBase_v1.BridgeTransferCompleted( + emit IPP_CrossChainBase_v2.BridgeTransferCompleted( paymentProcessor.getPaymentId(), intentId, order.recipient, @@ -998,7 +998,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { // Create valid intent ID and intent (bytes32 intentId,) = _getValidIntentIdAndIntent(); // Create valid payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( address(paymentClient), 10 ether, @@ -1024,7 +1024,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { // Test function call and emit event vm.expectEmit(true, true, true, true); - emit IPP_CrossChainBase_v1.BridgeTransferFailed( + emit IPP_CrossChainBase_v2.BridgeTransferFailed( address(paymentClient), order.recipient, order.paymentToken, @@ -1189,7 +1189,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { function testInternalValidPaymentOrder_worksGivenValidPaymentOrder() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( address(0xBEEF), 10 ether, address(_token), _getExecutionData() ); @@ -1220,7 +1220,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { everclearPaymentMock.setMockBridgeToFail(true); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( testRecipient, testAmount, address(_token), _getExecutionData() ); @@ -1259,7 +1259,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _token.approve(address(everclearPaymentMock), testAmount); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( testRecipient, testAmount, address(_token), _getExecutionData() ); @@ -1298,7 +1298,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { _token.approve(address(paymentProcessor), testAmount); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = _createTestPaymentOrder( testRecipient, testAmount, address(_token), _getExecutionData() ); @@ -1367,13 +1367,13 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { address recipient_, uint amount_, bytes32[] memory executionData_ - ) internal returns (IERC20PaymentClientBase_v2.PaymentOrder[] memory) { + ) internal returns (IERC20PaymentClientBase_v3.PaymentOrder[] memory) { address[] memory setupRecipients = new address[](1); setupRecipients[0] = recipient_; uint[] memory setupAmounts = new uint[](1); setupAmounts[0] = amount_; - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = _createPaymentOrders(1, setupRecipients, setupAmounts, executionData_); return orders; } @@ -1383,9 +1383,9 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { address[] memory recipients, uint[] memory amounts, bytes32[] memory executionData - ) internal returns (IERC20PaymentClientBase_v2.PaymentOrder[] memory) { - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = - new IERC20PaymentClientBase_v2.PaymentOrder[](orderCount); + ) internal returns (IERC20PaymentClientBase_v3.PaymentOrder[] memory) { + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = + new IERC20PaymentClientBase_v3.PaymentOrder[](orderCount); for (uint i = 0; i < orderCount; i++) { orders[i] = _createTestPaymentOrder( @@ -1404,7 +1404,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { uint amount, address token, bytes32[] memory executionData - ) internal view returns (IERC20PaymentClientBase_v2.PaymentOrder memory) { + ) internal view returns (IERC20PaymentClientBase_v3.PaymentOrder memory) { bytes32[] memory data = new bytes32[](7); if (executionData.length == 7 && executionData[0] == bytes32(0)) { @@ -1413,7 +1413,7 @@ contract PP_Everclear_CrossChain_v1_Test is ModuleTest { data = executionData; } - return IERC20PaymentClientBase_v2.PaymentOrder({ + return IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, paymentToken: token, amount: amount, diff --git a/test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v1.t.sol b/test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v2.t.sol similarity index 84% rename from test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v1.t.sol rename to test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v2.t.sol index dab73ac61..2fb5b7abd 100644 --- a/test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v1.t.sol +++ b/test/unit/modules/paymentProcessor/PP_Queue_ManualExecution_v2.t.sol @@ -15,39 +15,38 @@ import "forge-std/console.sol"; // Tests and Mocks import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; -import {PP_Queue_v1_Exposed} from - "@mocks/modules/paymentProcessor/PP_Queue_v1_Exposed.sol"; +import {PP_Queue_v2_Exposed} from + "@mocks/modules/paymentProcessor/PP_Queue_v2_Exposed.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock, + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock, ERC20Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {NonStandardTokenMock} from "@mocks/external/token/NonStandardTokenMock.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; // System under testing -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; -import {PP_Queue_ManualExecution_v1} from "@pp/PP_Queue_ManualExecution_v1.sol"; -import {PP_Queue_ManualExecution_v1_Exposed} from - "@mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v1_Exposed.sol"; -import {PP_Queue_v1_Test} from "./PP_Queue_v1.t.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; +import {PP_Queue_ManualExecution_v2} from "@pp/PP_Queue_ManualExecution_v2.sol"; +import {PP_Queue_ManualExecution_v2_Exposed} from + "@mocks/modules/paymentProcessor/PP_Queue_ManualExecution_v2_Exposed.sol"; +import {PP_Queue_v2_Test} from "./PP_Queue_v2.t.sol"; -contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { +contract PP_Queue_ManualExecution_v2_Test is PP_Queue_v2_Test { // SuT - PP_Queue_ManualExecution_v1_Exposed queueManualExecution; + PP_Queue_ManualExecution_v2_Exposed queueManualExecution; function init() public { - address impl = address(new PP_Queue_ManualExecution_v1_Exposed()); + address impl = address(new PP_Queue_ManualExecution_v2_Exposed()); queueManualExecution = - PP_Queue_ManualExecution_v1_Exposed(Clones.clone(impl)); + PP_Queue_ManualExecution_v2_Exposed(Clones.clone(impl)); // Setup orchestrator once _setUpOrchestrator(queueManualExecution); - _authorizer.setIsAuthorized(address(this), true); // Initialize queue manual execution queueManualExecution.init( @@ -57,8 +56,8 @@ contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { ); // Setup payment client - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); // Register payment client as module in the same orchestrator _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); @@ -68,6 +67,9 @@ contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { paymentClient.init(_orchestrator, _METADATA, bytes("")); paymentClient.setIsAuthorized(address(queueManualExecution), true); paymentClient.setToken(_token); + + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); } /* Test testPublicProcessPayments_succeedsGivenValidSetupAndPaymentOrder() function @@ -91,8 +93,8 @@ contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory orders = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory orders = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, amount: amount, paymentToken: paymentToken, @@ -155,8 +157,8 @@ contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { function testPublicProcessPayments_failsGivenUnregisteredClient() public { init(); // Create another payment client that is not registered - ERC20PaymentClientBaseV2Mock otherPaymentClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock otherPaymentClient = + new ERC20PaymentClientBase_v3_Mock(); // Try to call processPayments with unregistered client vm.prank(address(paymentClient)); @@ -188,8 +190,8 @@ contract PP_Queue_ManualExecution_v1_Test is PP_Queue_v1_Test { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, amount: amount, paymentToken: paymentToken, diff --git a/test/unit/modules/paymentProcessor/PP_Queue_v1.t.sol b/test/unit/modules/paymentProcessor/PP_Queue_v2.t.sol similarity index 88% rename from test/unit/modules/paymentProcessor/PP_Queue_v1.t.sol rename to test/unit/modules/paymentProcessor/PP_Queue_v2.t.sol index 3337778c6..bf282eed3 100644 --- a/test/unit/modules/paymentProcessor/PP_Queue_v1.t.sol +++ b/test/unit/modules/paymentProcessor/PP_Queue_v2.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; // Internal import {LinkedIdList} from "src/modules/lib/LinkedIdList.sol"; -import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from "@pp/IPaymentProcessor_v3.sol"; // External import {Test} from "forge-std/Test.sol"; @@ -15,32 +15,32 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // Tests and Mocks import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; -import {PP_Queue_v1_Exposed} from - "@mocks/modules/paymentProcessor/PP_Queue_v1_Exposed.sol"; +import {PP_Queue_v2_Exposed} from + "@mocks/modules/paymentProcessor/PP_Queue_v2_Exposed.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock, + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock, ERC20Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {NonStandardTokenMock} from "@mocks/external/token/NonStandardTokenMock.sol"; import {OZErrors} from "@testUtilities/OZErrors.sol"; // System under testing -import {IPP_Queue_v1} from "@pp/interfaces/IPP_Queue_v1.sol"; +import {IPP_Queue_v2} from "@pp/interfaces/IPP_Queue_v2.sol"; -contract PP_Queue_v1_Test is ModuleTest { +contract PP_Queue_v2_Test is ModuleTest { // ================================================================================ // Storage // SuT - PP_Queue_v1_Exposed queue; + PP_Queue_v2_Exposed queue; // Mocks - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; // ================================================================================ // Events @@ -81,11 +81,11 @@ contract PP_Queue_v1_Test is ModuleTest { canceledOrdersTreasury = makeAddr("canceledOrdersTreasury"); failedOrdersTreasury = makeAddr("failedOrdersTreasury"); - address impl = address(new PP_Queue_v1_Exposed()); - queue = PP_Queue_v1_Exposed(Clones.clone(impl)); + address impl = address(new PP_Queue_v2_Exposed()); + queue = PP_Queue_v2_Exposed(Clones.clone(impl)); - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); _setUpOrchestrator(paymentClient); // initiate SuT @@ -100,8 +100,8 @@ contract PP_Queue_v1_Test is ModuleTest { paymentClient.setIsAuthorized(address(queue), true); paymentClient.setToken(_token); - // Authorize test contract - _authorizer.setIsAuthorized(address(this), true); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); } // ================================================================================ @@ -122,11 +122,11 @@ contract PP_Queue_v1_Test is ModuleTest { ); } - function testSupportsInterface() public { + function testSupportsInterface() public override(ModuleTest) { assertTrue( - queue.supportsInterface(type(IPaymentProcessor_v2).interfaceId) + queue.supportsInterface(type(IPaymentProcessor_v3).interfaceId) ); - assertTrue(queue.supportsInterface(type(IPP_Queue_v1).interfaceId)); + assertTrue(queue.supportsInterface(type(IPP_Queue_v2).interfaceId)); assertTrue(queue.supportsInterface(type(IERC165).interfaceId)); } @@ -167,8 +167,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -201,7 +201,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -416,28 +416,22 @@ contract PP_Queue_v1_Test is ModuleTest { // Test Set Max Orders Per Execution /* Test: Function setMaxOrdersPerExecution() - └── Given the caller does not have the QUEUE_OPERATOR_ROLE_ADMIN role - └── When the function setMaxOrdersPerExecution is called - └── Then it should revert + └── Given: Caller is not permissioned + └── When: the function setMaxOrdersPerExecution() is called + └── Then: it should revert (modifier in place test) */ - function testSetMaxOrdersPerExecution_revertGivenNonQueueOperator( - address nonQueueOperator_ - ) public { - // Setup - vm.assume(nonQueueOperator_ != address(this)); - bytes32 roleId = _authorizer.generateRoleId( - address(queue), queue.getQueueOperatorRole() - ); + function testSetMaxOrdersPerExecution_ModifierInPlace() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( abi.encodeWithSelector( - IModule_v1.Module__CallerNotAuthorized.selector, - roleId, - nonQueueOperator_ + IModule_v2.Module__CallerNotPermissioned.selector ) ); - // Test - vm.prank(nonQueueOperator_); + vm.prank(address(0xB0B)); queue.setMaxOrdersPerExecution(100); } @@ -450,7 +444,7 @@ contract PP_Queue_v1_Test is ModuleTest { function testSetMaxOrdersPerExecution_revertGivenZeroAmount() public { vm.expectRevert( abi.encodeWithSelector( - IPP_Queue_v1.Module__PP_Queue_ZeroAmount.selector + IPP_Queue_v2.Module__PP_Queue_ZeroAmount.selector ) ); queue.setMaxOrdersPerExecution(0); @@ -499,8 +493,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: makeAddr("recipient"), amount: 100, paymentToken: address(_token), @@ -525,7 +519,7 @@ contract PP_Queue_v1_Test is ModuleTest { ); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -560,8 +554,8 @@ contract PP_Queue_v1_Test is ModuleTest { for (uint8 i = 0; i < numOrders_; i++) { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(i + 1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: makeAddr(string.concat("recipient", vm.toString(i))), amount: 100, paymentToken: address(_token), @@ -596,7 +590,7 @@ contract PP_Queue_v1_Test is ModuleTest { vm.stopPrank(); queue.cancelPaymentOrderThroughQueueId( - orderIds_[i], IERC20PaymentClientBase_v2(address(paymentClient)) + orderIds_[i], IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -626,7 +620,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -638,7 +632,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(this), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -667,7 +661,7 @@ contract PP_Queue_v1_Test is ModuleTest { vm.assume(address(paymentClient) != recipient_); amount_ = uint96(bound(uint(amount_), 1, 1e30)); - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -681,7 +675,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -698,7 +692,7 @@ contract PP_Queue_v1_Test is ModuleTest { 1 ) ); - queue.getOrder(1, IERC20PaymentClientBase_v2(address(this))); + queue.getOrder(1, IERC20PaymentClientBase_v3(address(this))); } /* Test testGetOrder_GivenCancelledOrder() @@ -715,8 +709,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -741,7 +735,7 @@ contract PP_Queue_v1_Test is ModuleTest { _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); helper_assertOrderMatch( @@ -749,7 +743,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.CANCELLED + IPP_Queue_v2.RedemptionState.CANCELLED ); } @@ -766,8 +760,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -785,7 +779,7 @@ contract PP_Queue_v1_Test is ModuleTest { queue.exposed_updateOrderState( orderId_, address(paymentClient), - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); queue.exposed_removeFromQueue(orderId_, address(paymentClient)); @@ -794,7 +788,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); } @@ -817,7 +811,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -844,7 +838,7 @@ contract PP_Queue_v1_Test is ModuleTest { makeAddr(string.concat("recipient", vm.toString(i_))); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder( recipient_, amount_, i_ + 1, address(_token) ); @@ -886,8 +880,8 @@ contract PP_Queue_v1_Test is ModuleTest { └── Then it should revert with Module__PP_Queue_QueueOperationFailed. */ function testAddPaymentOrderToQueue_RevertGivenInvalidOrder() public { - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0), amount: 100, paymentToken: address(_token), @@ -920,8 +914,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -948,7 +942,7 @@ contract PP_Queue_v1_Test is ModuleTest { vm.prank(address(paymentClient)); _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); uint[] memory queueAfter_ = queue.getOrderQueue(address(paymentClient)); @@ -982,8 +976,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1017,8 +1011,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1049,14 +1043,14 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1094,8 +1088,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1119,8 +1113,8 @@ contract PP_Queue_v1_Test is ModuleTest { // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1142,7 +1136,7 @@ contract PP_Queue_v1_Test is ModuleTest { vm.stopPrank(); queue.cancelPaymentOrderThroughQueueId( - firstOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + firstOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -1156,7 +1150,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -1172,8 +1166,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1197,8 +1191,8 @@ contract PP_Queue_v1_Test is ModuleTest { // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1222,13 +1216,13 @@ contract PP_Queue_v1_Test is ModuleTest { vm.prank(address(paymentClient)); _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - firstOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + firstOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); vm.prank(address(paymentClient)); _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - secondOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + secondOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -1261,8 +1255,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1296,8 +1290,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1319,8 +1313,8 @@ contract PP_Queue_v1_Test is ModuleTest { // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1351,7 +1345,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -1367,8 +1361,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1392,8 +1386,8 @@ contract PP_Queue_v1_Test is ModuleTest { // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1415,7 +1409,7 @@ contract PP_Queue_v1_Test is ModuleTest { vm.stopPrank(); queue.cancelPaymentOrderThroughQueueId( - firstOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + firstOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -1429,7 +1423,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient_, amount_, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -1445,8 +1439,8 @@ contract PP_Queue_v1_Test is ModuleTest { // First order (bytes32 flags1_, bytes32[] memory data1_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order1_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order1_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1470,8 +1464,8 @@ contract PP_Queue_v1_Test is ModuleTest { // Second order with different flags/data (bytes32 flags2_, bytes32[] memory data2_) = helper__encodePaymentOrderData(2, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order2_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order2_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1495,12 +1489,12 @@ contract PP_Queue_v1_Test is ModuleTest { vm.prank(address(paymentClient)); _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - firstOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + firstOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); vm.prank(address(paymentClient)); _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - secondOrderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + secondOrderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( @@ -1510,20 +1504,6 @@ contract PP_Queue_v1_Test is ModuleTest { ); } - /* Test testGetQueueOperatorRole_GivenValidRole() - └── Given a queue operator role - └── When getting the role - └── Then it should return correct role hash. - */ - function testGetQueueOperatorRole_GivenValidRole() public { - bytes32 expectedRole_ = bytes32("QUEUE_OPERATOR_ROLE"); - assertEq( - queue.getQueueOperatorRole(), - expectedRole_, - "Role hash should match." - ); - } - // ================================================================================ // Test Process Next Order @@ -1543,7 +1523,7 @@ contract PP_Queue_v1_Test is ModuleTest { // Setup // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); // Setup tokens using helper helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -1552,14 +1532,14 @@ contract PP_Queue_v1_Test is ModuleTest { queue.exposed_addPaymentOrderToQueue(order, address(paymentClient)); // Get value for pre-assertions - IPP_Queue_v1.QueuedOrder memory queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder_ = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); // Pre-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PENDING) + uint(IPP_Queue_v2.RedemptionState.PENDING) ); assertEq( _token.balanceOf(recipient_), 0, "Recipient should have no balance" @@ -1577,12 +1557,12 @@ contract PP_Queue_v1_Test is ModuleTest { // Get value for post-assertions queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); // Post-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PROCESSED) + uint(IPP_Queue_v2.RedemptionState.PROCESSED) ); assertEq(_token.balanceOf(recipient_), amount_); assertEq( @@ -1602,8 +1582,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1643,7 +1623,7 @@ contract PP_Queue_v1_Test is ModuleTest { uint queueId = 1; // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder( recipient_, amount_, queueId, address(_token) ); @@ -1654,14 +1634,14 @@ contract PP_Queue_v1_Test is ModuleTest { uint orderId_ = helper_addPaymentOrderToQueue(order, address(paymentClient)); uint queueSize_ = queue.getQueueSizeForClient(address(paymentClient)); - IPP_Queue_v1.QueuedOrder memory queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder_ = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); // pre-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PENDING) + uint(IPP_Queue_v2.RedemptionState.PENDING) ); assertEq(_token.balanceOf(recipient_), 0); assertEq(queueSize_, 1); @@ -1671,14 +1651,14 @@ contract PP_Queue_v1_Test is ModuleTest { // Get values for post-assertions queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); queueSize_ = queue.getQueueSizeForClient(address(paymentClient)); // post-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PROCESSED) + uint(IPP_Queue_v2.RedemptionState.PROCESSED) ); assertEq(_token.balanceOf(recipient_), amount_); assertEq(queueSize_, 0); @@ -1706,7 +1686,7 @@ contract PP_Queue_v1_Test is ModuleTest { nonStandardToken.setFailTransferTo(recipient_); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder( recipient_, amount_, queueId, address(nonStandardToken) ); @@ -1719,14 +1699,14 @@ contract PP_Queue_v1_Test is ModuleTest { uint orderId_ = helper_addPaymentOrderToQueue(order, address(paymentClient)); uint queueSize_ = queue.getQueueSizeForClient(address(paymentClient)); - IPP_Queue_v1.QueuedOrder memory queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder_ = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); // pre-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PENDING) + uint(IPP_Queue_v2.RedemptionState.PENDING) ); assertEq(nonStandardToken.balanceOf(recipient_), 0); assertEq(nonStandardToken.balanceOf(address(paymentClient)), amount_); @@ -1737,13 +1717,13 @@ contract PP_Queue_v1_Test is ModuleTest { // Get values for post-assertions queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); queueSize_ = queue.getQueueSizeForClient(address(paymentClient)); // post-assertions assertEq( - uint(queuedOrder_.state_), uint(IPP_Queue_v1.RedemptionState.FAILED) + uint(queuedOrder_.state_), uint(IPP_Queue_v2.RedemptionState.FAILED) ); assertEq(nonStandardToken.balanceOf(recipient_), 0); assertEq(nonStandardToken.balanceOf(address(queue)), amount_); @@ -1761,7 +1741,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); // Setup tokens using helper @@ -1794,8 +1774,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(0, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1828,8 +1808,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1856,11 +1836,11 @@ contract PP_Queue_v1_Test is ModuleTest { success, "Processing should fail due to insufficient balance" ); - IPP_Queue_v1.QueuedOrder memory order = + IPP_Queue_v2.QueuedOrder memory order = queue.getOrder(orderId_, paymentClient); assertEq( uint(order.state_), - uint(IPP_Queue_v1.RedemptionState.PENDING), + uint(IPP_Queue_v2.RedemptionState.PENDING), "Order should remain in PENDING state" ); } @@ -1875,8 +1855,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1893,7 +1873,7 @@ contract PP_Queue_v1_Test is ModuleTest { assertTrue( queue.exposed_orderExists( - orderId_, IERC20PaymentClientBase_v2(address(this)) + orderId_, IERC20PaymentClientBase_v3(address(this)) ), "Order should exist." ); @@ -1907,7 +1887,7 @@ contract PP_Queue_v1_Test is ModuleTest { function testOrderExists_GivenInvalidOrder() public { assertFalse( queue.exposed_orderExists( - 999, IERC20PaymentClientBase_v2(address(this)) + 999, IERC20PaymentClientBase_v3(address(this)) ), "Invalid order should not exist." ); @@ -1923,8 +1903,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1942,7 +1922,7 @@ contract PP_Queue_v1_Test is ModuleTest { address invalidClient_ = makeAddr("invalidClient"); assertFalse( queue.exposed_orderExists( - orderId_, IERC20PaymentClientBase_v2(invalidClient_) + orderId_, IERC20PaymentClientBase_v3(invalidClient_) ), "Order should not exist for invalid client." ); @@ -1958,8 +1938,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -1994,7 +1974,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -2004,15 +1984,15 @@ contract PP_Queue_v1_Test is ModuleTest { queue.exposed_updateOrderState( orderId_, address(paymentClient), - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); - IPP_Queue_v1.QueuedOrder memory queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder_ = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PROCESSED), + uint(IPP_Queue_v2.RedemptionState.PROCESSED), "State should be PROCESSED." ); } @@ -2030,7 +2010,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); helper_setupPaymentTokenBalanceAndApproval(amount_, _token); @@ -2288,13 +2268,13 @@ contract PP_Queue_v1_Test is ModuleTest { ); vm.expectEmit(true, true, true, true, address(queue)); - emit IPaymentProcessor_v2.TokensReleased( + emit IPaymentProcessor_v3.TokensReleased( validRecipient_, address(_token), netAmount ); - emit IPaymentProcessor_v2.TokensReleased( + emit IPaymentProcessor_v3.TokensReleased( protocolTreasury_, address(_token), protocolFeeAmount ); - emit IModule_v1.ProtocolFeeTransferred( + emit IModule_v2.ProtocolFeeTransferred( address(_token), protocolTreasury_, protocolFeeAmount ); @@ -2347,7 +2327,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); _token.mint(address(paymentClient), amount_ * 2); @@ -2400,8 +2380,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(i_ + 1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipients_[i_], amount: amounts_[i_], paymentToken: address(_token), @@ -2513,8 +2493,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(i_ + 1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipients_[i_], amount: amounts_[i_], paymentToken: address(_token), @@ -2554,8 +2534,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -2581,11 +2561,11 @@ contract PP_Queue_v1_Test is ModuleTest { vm.stopPrank(); // Verify order is in PROCESSED state - IPP_Queue_v1.QueuedOrder memory queuedOrder = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); require( - queuedOrder.state_ == IPP_Queue_v1.RedemptionState.PROCESSED, + queuedOrder.state_ == IPP_Queue_v2.RedemptionState.PROCESSED, "Order should be in PROCESSED state" ); @@ -2594,8 +2574,8 @@ contract PP_Queue_v1_Test is ModuleTest { abi.encodeWithSignature( "Module__PP_Queue_InvalidStateTransition(uint256,uint8,uint8)", orderId_, - uint8(IPP_Queue_v1.RedemptionState.PROCESSED), - uint8(IPP_Queue_v1.RedemptionState.CANCELLED) + uint8(IPP_Queue_v2.RedemptionState.PROCESSED), + uint8(IPP_Queue_v2.RedemptionState.CANCELLED) ) ); @@ -2603,7 +2583,7 @@ contract PP_Queue_v1_Test is ModuleTest { queue.exposed_updateOrderState( orderId_, address(paymentClient), - IPP_Queue_v1.RedemptionState.CANCELLED + IPP_Queue_v2.RedemptionState.CANCELLED ); } @@ -2618,8 +2598,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -2640,7 +2620,7 @@ contract PP_Queue_v1_Test is ModuleTest { _token.approve(address(queue), amount_); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); vm.expectRevert( @@ -2680,7 +2660,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); // Add payment order to queue, creating queue order uint orderId_ = @@ -2728,7 +2708,7 @@ contract PP_Queue_v1_Test is ModuleTest { uint orderId_ = 1; // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = helper_createTestPaymentOrder( recipient_, amount_, orderId_, address(_token) ); @@ -2738,7 +2718,7 @@ contract PP_Queue_v1_Test is ModuleTest { queue.exposed_updateOrderState( orderId_, address(paymentClient), - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); // Test function call @@ -2763,7 +2743,7 @@ contract PP_Queue_v1_Test is ModuleTest { uint orderId_ = 1; // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = helper_createTestPaymentOrder( recipient_, amount_, orderId_, address(_token) ); @@ -2791,7 +2771,7 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; uint orderId_ = 1; // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = helper_createTestPaymentOrder( recipient_, amount_, orderId_, address(_token) ); @@ -2818,8 +2798,8 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_ = 100; (bytes32 flags_, bytes32[] memory data_) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(_token), @@ -2839,14 +2819,14 @@ contract PP_Queue_v1_Test is ModuleTest { abi.encodeWithSignature( "Module__PP_Queue_InvalidStateTransition(uint256,uint8,uint8)", orderId_, - uint8(IPP_Queue_v1.RedemptionState.PROCESSED), - uint8(IPP_Queue_v1.RedemptionState.PENDING) + uint8(IPP_Queue_v2.RedemptionState.PROCESSED), + uint8(IPP_Queue_v2.RedemptionState.PENDING) ) ); queue.exposed_updateOrderState( orderId_, address(paymentClient), - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -2878,7 +2858,7 @@ contract PP_Queue_v1_Test is ModuleTest { ) ); queue.cancelPaymentOrderThroughQueueId( - nonExistentOrderId_, IERC20PaymentClientBase_v2(address(this)) + nonExistentOrderId_, IERC20PaymentClientBase_v3(address(this)) ); } @@ -2896,7 +2876,29 @@ contract PP_Queue_v1_Test is ModuleTest { ) ); queue.cancelPaymentOrderThroughQueueId( - 0, IERC20PaymentClientBase_v2(address(this)) + 0, IERC20PaymentClientBase_v3(address(this)) + ); + } + + /* + Test: cancelPaymentOrderThroughQueueId + └── Given: Caller is not permissioned + └── When the function cancelPaymentOrderThroughQueueId() is called + └── Then it should revert (modifier in place test) + */ + function testCancelPaymentOrderThroughQueueId_ModifierInPlace() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + queue.cancelPaymentOrderThroughQueueId( + 0, IERC20PaymentClientBase_v3(address(0)) ); } @@ -2920,7 +2922,7 @@ contract PP_Queue_v1_Test is ModuleTest { nonStandardToken.setFailTransferTo(canceledOrdersTreasury); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder( recipient_, amount_, queueId, address(nonStandardToken) ); @@ -2933,14 +2935,14 @@ contract PP_Queue_v1_Test is ModuleTest { uint orderId_ = helper_addPaymentOrderToQueue(order, address(paymentClient)); uint queueSize_ = queue.getQueueSizeForClient(address(paymentClient)); - IPP_Queue_v1.QueuedOrder memory queuedOrder_ = queue.getOrder( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + IPP_Queue_v2.QueuedOrder memory queuedOrder_ = queue.getOrder( + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); // pre-assertions assertEq( uint(queuedOrder_.state_), - uint(IPP_Queue_v1.RedemptionState.PENDING) + uint(IPP_Queue_v2.RedemptionState.PENDING) ); assertEq(nonStandardToken.balanceOf(recipient_), 0); assertEq(nonStandardToken.balanceOf(address(paymentClient)), amount_); @@ -2957,7 +2959,7 @@ contract PP_Queue_v1_Test is ModuleTest { ) ); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -2995,7 +2997,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient = makeAddr("recipient"); uint96 amount = 1000; - IERC20PaymentClientBase_v2.PaymentOrder memory orders = + IERC20PaymentClientBase_v3.PaymentOrder memory orders = helper_createTestPaymentOrder(recipient, amount, 1, address(_token)); // Setup initial state _token.mint(address(paymentClient), amount); @@ -3035,7 +3037,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient, amount, - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); } @@ -3055,7 +3057,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient = makeAddr("recipient"); uint96 amount = 1000; - IERC20PaymentClientBase_v2.PaymentOrder memory orders = + IERC20PaymentClientBase_v3.PaymentOrder memory orders = helper_createTestPaymentOrder(recipient, amount, 1, address(_token)); // Setup initial state @@ -3097,7 +3099,7 @@ contract PP_Queue_v1_Test is ModuleTest { address(paymentClient), recipient, amount, - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PROCESSED ); vm.expectRevert( @@ -3106,57 +3108,37 @@ contract PP_Queue_v1_Test is ModuleTest { vm.prank(address(this)); queue.cancelPaymentOrderThroughQueueId( - orderId_, IERC20PaymentClientBase_v2(address(paymentClient)) + orderId_, IERC20PaymentClientBase_v3(address(paymentClient)) ); } - /* Test testPublicClaimPreviouslyUnclaimableToTreasury_revertsGivenUnauthorizedCaller() function - ├── Given an unclaimable payment order has been added - │ └── And tokens have been transferred to the queue - │ └── When claimPreviouslyUnclaimableToTreasury is called by an unauthorized caller - │ └── Then the transaction should revert with "Module__CallerNotAuthorized" + /* Test claimPreviouslyUnclaimableToTreasury + └── Given caller is not permissioned + └── When claimPreviouslyUnclaimableToTreasury is called + └── Then it should revert (modifier in place test) */ - function testPublicClaimPreviouslyUnclaimableToTreasury_revertsGivenUnauthorizedCaller( - ) public { - paymentClient.exposed_addToOutstandingTokenAmounts(address(_token), 200); - - address recipient_ = makeAddr("recipient"); - uint96 amount_ = 100; - - IERC20PaymentClientBase_v2.PaymentOrder memory order = - helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); - - _token.mint(address(paymentClient), amount_ * 2); - - vm.startPrank(address(paymentClient)); - _token.approve(address(queue), amount_ * 2); - queue.exposed_addUnclaimableOrder(order, address(paymentClient)); - _token.transfer(address(queue), amount_); - vm.stopPrank(); - - vm.prank(address(queue)); - _token.approve(address(queue), amount_); - - //@todo => calculate role - // bytes32 role = _authorizer.generateRoleId(address(this), QUEUE_OPERATOR_ROLE); + function testClaimPreviouslyUnclaimableToTreasury_modifierInPlace() + public + { + // permissioned - vm.prank(address(paymentClient)); + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); vm.expectRevert( - abi.encodeWithSignature( - "Module__CallerNotAuthorized(bytes32,address)", - 0x5d97d4d42ba6fe9c9c1a1451385e8b1e735e94d33a05d1e7fae480933f0db4e0, - address(paymentClient) + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector ) ); + vm.prank(address(0xB0B)); queue.claimPreviouslyUnclaimableToTreasury( - address(paymentClient), address(_token), recipient_ + address(0), address(0), address(0) ); } /* Test testPublicClaimPreviouslyUnclaimableToTreasury_succeedsGivenValidConditions() function ├── Given an unclaimable payment order has been added │ └── And tokens have been transferred to the queue - │ └── When claimPreviouslyUnclaimableToTreasury is called by an authorized caller + │ └── When claimPreviouslyUnclaimableToTreasury is called by an permissioned caller │ └── Then the unclaimable amount should be zero │ └── And the tokens should be transferred to the failed orders treasury │ └── And the canceled orders treasury should not receive any tokens @@ -3168,7 +3150,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr("recipient"); uint96 amount_ = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder memory order = helper_createTestPaymentOrder(recipient_, amount_, 1, address(_token)); _token.mint(address(paymentClient), amount_ * 2); @@ -3208,14 +3190,29 @@ contract PP_Queue_v1_Test is ModuleTest { ); } - /* Test testPublicSetCanceledOrdersTreasury_succeedsGivenAuthorizedCaller() function + /* Test testPublicSetCanceledOrdersTreasury ├── Given a new treasury address │ └── And the current treasury address is different - │ └── When setCanceledOrdersTreasury is called by an unauthorized caller - │ └── Then it should revert with "Module__CallerNotAuthorized" - │ └── And when called by an authorized caller + │ └── When setCanceledOrdersTreasury is called by non permissioned caller + │ └── Then it should revert (Modifier in place test) + │ └── And when called by a permissioned caller │ └── Then the treasury address should be updated */ + + function testPublicSetCanceledOrdersTreasury_ModifierInPosition() public { + //permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + queue.setCanceledOrdersTreasury(address(0)); + } + function testPublicSetCanceledOrdersTreasury_succeedsGivenAuthorizedCaller() public { @@ -3227,20 +3224,6 @@ contract PP_Queue_v1_Test is ModuleTest { "New treasury should be different from current" ); - bytes32 ORCHESTRATOR_ADMIN_ROLE = - 0x3078303000000000000000000000000000000000000000000000000000000000; - address unauthorized = makeAddr("unauthorized"); - vm.prank(unauthorized); - vm.expectRevert( - abi.encodeWithSignature( - "Module__CallerNotAuthorized(bytes32,address)", - ORCHESTRATOR_ADMIN_ROLE, - unauthorized - ) - ); - queue.setCanceledOrdersTreasury(newTreasury); - - vm.prank(address(this)); queue.setCanceledOrdersTreasury(newTreasury); assertEq( @@ -3250,14 +3233,29 @@ contract PP_Queue_v1_Test is ModuleTest { ); } - /* Test testPublicSetFailedOrdersTreasury_succeedsGivenAuthorizedCaller() function + /* Test testPublicSetFailedOrdersTreasury ├── Given a new failed orders treasury address │ └── And the current failed orders treasury address is different - │ └── When setFailedOrdersTreasury is called by an unauthorized caller - │ └── Then it should revert with "Module__CallerNotAuthorized" - │ └── And when called by an authorized caller + │ └── When setFailedOrdersTreasury is called by a permissioned caller + │ └── Then it should revert (Modifier in place test) + │ └── And when called by a permissioned caller │ └── Then the failed orders treasury address should be updated */ + + function testPublicSetFailedOrdersTreasury_ModifierInPosition() public { + //permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + queue.setFailedOrdersTreasury(address(0)); + } + function testPublicSetFailedOrdersTreasury_succeedsGivenAuthorizedCaller() public { @@ -3269,19 +3267,6 @@ contract PP_Queue_v1_Test is ModuleTest { "New treasury should be different from current" ); - bytes32 ORCHESTRATOR_ADMIN_ROLE = - 0x3078303000000000000000000000000000000000000000000000000000000000; - address unauthorized = makeAddr("unauthorized"); - vm.prank(unauthorized); - vm.expectRevert( - abi.encodeWithSignature( - "Module__CallerNotAuthorized(bytes32,address)", - ORCHESTRATOR_ADMIN_ROLE, - unauthorized - ) - ); - queue.setFailedOrdersTreasury(newTreasury); - vm.prank(address(this)); queue.setFailedOrdersTreasury(newTreasury); @@ -3307,7 +3292,7 @@ contract PP_Queue_v1_Test is ModuleTest { { address recipient = makeAddr("recipient"); uint96 amount = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory validOrder = + IERC20PaymentClientBase_v3.PaymentOrder memory validOrder = helper_createTestPaymentOrder(recipient, amount, 1, address(_token)); assertTrue( @@ -3315,7 +3300,7 @@ contract PP_Queue_v1_Test is ModuleTest { "Valid payment order should return true" ); - IERC20PaymentClientBase_v2.PaymentOrder memory invalidOrder = + IERC20PaymentClientBase_v3.PaymentOrder memory invalidOrder = helper_createTestPaymentOrder(address(0), amount, 1, address(_token)); assertFalse( @@ -3340,98 +3325,13 @@ contract PP_Queue_v1_Test is ModuleTest { ); } - /* Test testPublicGetQueueOperatorRoleAdmin_succeedsGivenCorrectAdmin() function - ├── When getQueueOperatorRoleAdmin is called - │ └── Then it should return "QUEUE_OPERATOR_ROLE_ADMIN" - */ - function testPublicGetQueueOperatorRoleAdmin_succeedsGivenCorrectAdmin() - public - { - bytes32 operatorRoleAdmin_ = queue.getQueueOperatorRoleAdmin(); - assertTrue( - operatorRoleAdmin_ == "QUEUE_OPERATOR_ROLE_ADMIN", - "Queue operator role admin should be the queue address" - ); - } - /* Test: Function _calculateProtocolFeeAmount() └── Given collectProtocolFee flag is set to false └── When the function _calculateProtocolFeeAmount() is called └── Then it should return 0 for the fee and total amount as net amount */ - function testInternalCalculateProtocolFeeAmount_worksGivenCollectProtocolFeeFlagIsFalse( - uint totalAmount_, - uint projectFee_ - ) public { - // Setup - vm.assume(projectFee_ > 0); - bool collectProtocolFee_ = false; - - // Test function call - (uint feeAmount, uint netAmount, address treasury) = queue - .exposed_calculateProtocolFeeAmount( - totalAmount_, - PROCESS_PAYMENTS_FUNCTION_SELECTOR, - collectProtocolFee_, - projectFee_ - ); - - // Post-assertions - assertEq(feeAmount, 0, "Fee amount should be 0"); - assertEq(netAmount, totalAmount_, "Net amount should be correct"); - assertEq(treasury, address(0), "Treasury should be 0 address"); - } - - /* Test: Function _calculateProtocolFeeAmount() - └── Given collectProtocolFee flag is set to true - └── And the protocol fee == 0 - └── When the function _calculateProtocolFeeAmount() is called - └── Then it should return 0 for the fee and total amount as net amount - */ - function testInternalCalculateProtocolFeeAmount_worksGivenCollectProtocolFeeFlagIsTrueAndProtocolFeeIsZero( - uint totalAmount_, - uint projectFee_ - ) public { - // Setup - projectFee_ = 0; - bool collectProtocolFee_ = true; - - // Set protocol fee to 0 - feeManager.setCollateralWorkflowFee( - address(_orchestrator), - address(queue), - PROCESS_PAYMENTS_FUNCTION_SELECTOR, - true, - 0 // protocol fee is 0 - ); - // Calculate expected values - address expectedTreasury = - feeManager.getWorkflowTreasuries(address(_orchestrator)); - - // Test function call - (uint feeAmount, uint netAmount, address treasury) = queue - .exposed_calculateProtocolFeeAmount( - totalAmount_, - PROCESS_PAYMENTS_FUNCTION_SELECTOR, - collectProtocolFee_, - projectFee_ - ); - - // Post-assertions - assertEq(feeAmount, 0, "Fee amount should be 0"); - assertEq(netAmount, totalAmount_, "Net amount should be correct"); - assertEq(treasury, expectedTreasury, "Treasury should be correct"); - } - - /* Test: Function _calculateProtocolFeeAmount() - └── Given collectProtocolFee flag is set to true - ├── And the protocol fee is > 0 - └── And the project fee == 0 - └── When the function _calculateProtocolFeeAmount() is called - └── Then it should return the correct fee amount and treasury address - */ - function testInternalCalculateProtocolFeeAmount_worksGivenCollectProtocolFeeFlagIsTrueAndProtocolFeeIsGreaterThanZeroAndProjectFeeIsZero( + function testInternalGetProtocolFeeAmountAndTreasury_worksGivenCorrectFeeDetailsRetrieved( uint protocolFee_, uint totalAmount_, uint projectFee_ @@ -3964,14 +3864,14 @@ contract PP_Queue_v1_Test is ModuleTest { abi.encodeWithSignature( "Module__PP_Queue_InvalidStateTransition(uint256,uint8,uint8)", 1, - uint8(IPP_Queue_v1.RedemptionState.PROCESSED), - uint8(IPP_Queue_v1.RedemptionState.PENDING) + uint8(IPP_Queue_v2.RedemptionState.PROCESSED), + uint8(IPP_Queue_v2.RedemptionState.PENDING) ) ); queue.exposed_validStateTransition( 1, - IPP_Queue_v1.RedemptionState.PROCESSED, - IPP_Queue_v1.RedemptionState.PENDING + IPP_Queue_v2.RedemptionState.PROCESSED, + IPP_Queue_v2.RedemptionState.PENDING ); } @@ -3987,22 +3887,22 @@ contract PP_Queue_v1_Test is ModuleTest { // PENDING -> PROCESSED queue.exposed_validStateTransition( 1, - IPP_Queue_v1.RedemptionState.PENDING, - IPP_Queue_v1.RedemptionState.PROCESSED + IPP_Queue_v2.RedemptionState.PENDING, + IPP_Queue_v2.RedemptionState.PROCESSED ); // PENDING -> CANCELLED queue.exposed_validStateTransition( 1, - IPP_Queue_v1.RedemptionState.PENDING, - IPP_Queue_v1.RedemptionState.CANCELLED + IPP_Queue_v2.RedemptionState.PENDING, + IPP_Queue_v2.RedemptionState.CANCELLED ); // PENDING -> FAILED queue.exposed_validStateTransition( 1, - IPP_Queue_v1.RedemptionState.PENDING, - IPP_Queue_v1.RedemptionState.FAILED + IPP_Queue_v2.RedemptionState.PENDING, + IPP_Queue_v2.RedemptionState.FAILED ); } @@ -4077,8 +3977,8 @@ contract PP_Queue_v1_Test is ModuleTest { (bytes32 flags, bytes32[] memory data) = helper__encodePaymentOrderData(1, 0); - IERC20PaymentClientBase_v2.PaymentOrder memory invalidOrder = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory invalidOrder = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, amount: amount, paymentToken: address(_token), @@ -4103,7 +4003,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient = makeAddr("recipient"); uint96 amount = 100; - IERC20PaymentClientBase_v2.PaymentOrder memory validOrder = + IERC20PaymentClientBase_v3.PaymentOrder memory validOrder = helper_createTestPaymentOrder(recipient, amount, 1, address(_token)); assertTrue( @@ -4183,10 +4083,10 @@ contract PP_Queue_v1_Test is ModuleTest { uint96 amount_, uint orderNum_, address token_ - ) internal view returns (IERC20PaymentClientBase_v2.PaymentOrder memory) { + ) internal view returns (IERC20PaymentClientBase_v3.PaymentOrder memory) { (bytes32 flags, bytes32[] memory data) = helper__encodePaymentOrderData(orderNum_, 0); - return IERC20PaymentClientBase_v2.PaymentOrder({ + return IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient_, amount: amount_, paymentToken: address(token_), @@ -4198,7 +4098,7 @@ contract PP_Queue_v1_Test is ModuleTest { } function helper_addPaymentOrderToQueue( - IERC20PaymentClientBase_v2.PaymentOrder memory order_, + IERC20PaymentClientBase_v3.PaymentOrder memory order_, address client_ ) internal returns (uint queueId_) { queueId_ = queue.exposed_addPaymentOrderToQueue(order_, client_); @@ -4221,10 +4121,10 @@ contract PP_Queue_v1_Test is ModuleTest { address client_, address expectedRecipient_, uint96 expectedAmount_, - IPP_Queue_v1.RedemptionState expectedState_ + IPP_Queue_v2.RedemptionState expectedState_ ) internal { - IPP_Queue_v1.QueuedOrder memory queuedOrder = - queue.getOrder(orderId_, IERC20PaymentClientBase_v2(client_)); + IPP_Queue_v2.QueuedOrder memory queuedOrder = + queue.getOrder(orderId_, IERC20PaymentClientBase_v3(client_)); assertEq( queuedOrder.order_.recipient, expectedRecipient_, "Wrong recipient" ); @@ -4279,7 +4179,7 @@ contract PP_Queue_v1_Test is ModuleTest { address recipient_ = makeAddr(string.concat("recipient", vm.toString(i))); // Create payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order_ = + IERC20PaymentClientBase_v3.PaymentOrder memory order_ = helper_createTestPaymentOrder( recipient_, sellAmountForEachOrder, queueId, address(_token) ); diff --git a/test/unit/modules/paymentProcessor/PP_Simple_v2.t.sol b/test/unit/modules/paymentProcessor/PP_Simple_v3.t.sol similarity index 88% rename from test/unit/modules/paymentProcessor/PP_Simple_v2.t.sol rename to test/unit/modules/paymentProcessor/PP_Simple_v3.t.sol index 3a30044db..bf3809d76 100644 --- a/test/unit/modules/paymentProcessor/PP_Simple_v2.t.sol +++ b/test/unit/modules/paymentProcessor/PP_Simple_v3.t.sol @@ -8,36 +8,36 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // SuT -import {PP_Simple_v2_Exposed} from - "@mocks/modules/paymentProcessor/PP_Simple_v2_Exposed.sol"; +import {PP_Simple_v3_Exposed} from + "@mocks/modules/paymentProcessor/PP_Simple_v3_Exposed.sol"; import { - PP_Simple_v2, - IPaymentProcessor_v2 -} from "src/modules/paymentProcessor/PP_Simple_v2.sol"; + PP_Simple_v3, + IPaymentProcessor_v3 +} from "src/modules/paymentProcessor/PP_Simple_v3.sol"; // Mocks import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock, + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock, ERC20Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; -contract PP_SimpleV2Test is ModuleTest { +contract PP_Simple_v2_Test is ModuleTest { // SuT - PP_Simple_v2_Exposed paymentProcessor; + PP_Simple_v3_Exposed paymentProcessor; // Mocks - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; //-------------------------------------------------------------------------- // Events @@ -53,8 +53,8 @@ contract PP_SimpleV2Test is ModuleTest { ); function setUp() public { - address impl = address(new PP_Simple_v2_Exposed()); - paymentProcessor = PP_Simple_v2_Exposed(Clones.clone(impl)); + address impl = address(new PP_Simple_v3_Exposed()); + paymentProcessor = PP_Simple_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(paymentProcessor); @@ -62,8 +62,8 @@ contract PP_SimpleV2Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, bytes("")); - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); @@ -83,19 +83,19 @@ contract PP_SimpleV2Test is ModuleTest { ); } - function testSupportsInterface() public { + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init(_orchestrator, _METADATA, bytes("")); + } + + function testSupportsInterface() public override(ModuleTest) { assertTrue( paymentProcessor.supportsInterface( - type(IPaymentProcessor_v2).interfaceId + type(IPaymentProcessor_v3).interfaceId ) ); } - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init(_orchestrator, _METADATA, bytes("")); - } - //-------------------------------------------------------------------------- // Test: Payment Processing @@ -111,7 +111,7 @@ contract PP_SimpleV2Test is ModuleTest { // Add payment order to client. paymentClient.exposed_addPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, paymentToken: address(_token), amount: amount, @@ -131,7 +131,7 @@ contract PP_SimpleV2Test is ModuleTest { } vm.expectEmit(true, true, true, true); - emit IPaymentProcessor_v2.PaymentOrderProcessed( + emit IPaymentProcessor_v3.PaymentOrderProcessed( address(paymentClient), recipient, address(_token), @@ -189,7 +189,7 @@ contract PP_SimpleV2Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); @@ -197,7 +197,7 @@ contract PP_SimpleV2Test is ModuleTest { vm.prank(nonModule); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__OnlyCallableByModule .selector ) @@ -211,18 +211,18 @@ contract PP_SimpleV2Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); - ERC20PaymentClientBaseV2Mock otherERC20PaymentClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock otherERC20PaymentClient = + new ERC20PaymentClientBase_v3_Mock(); vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__CannotCallOnOtherClientsOrders .selector ) @@ -236,7 +236,7 @@ contract PP_SimpleV2Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); @@ -244,7 +244,7 @@ contract PP_SimpleV2Test is ModuleTest { vm.prank(nonModule); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__OnlyCallableByModule .selector ) @@ -258,18 +258,18 @@ contract PP_SimpleV2Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); - ERC20PaymentClientBaseV2Mock otherERC20PaymentClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock otherERC20PaymentClient = + new ERC20PaymentClientBase_v3_Mock(); vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__CannotCallOnOtherClientsOrders .selector ) @@ -305,7 +305,7 @@ contract PP_SimpleV2Test is ModuleTest { data[1] = bytes32(block.timestamp); paymentClient.exposed_addPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipients[i], paymentToken: address(_token), amount: 1, @@ -369,7 +369,7 @@ contract PP_SimpleV2Test is ModuleTest { function testClaimPreviouslyUnclaimableFailsIfNothingToClaim() public { vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__NothingToClaim .selector, address(paymentClient), @@ -382,7 +382,7 @@ contract PP_SimpleV2Test is ModuleTest { } function test_ValidPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order, + IERC20PaymentClientBase_v3.PaymentOrder memory order, address sender ) public { // The randomToken can't be the address of the Create2Deployer diff --git a/test/unit/modules/paymentProcessor/PP_Streaming_v2.t.sol b/test/unit/modules/paymentProcessor/PP_Streaming_v3.t.sol similarity index 94% rename from test/unit/modules/paymentProcessor/PP_Streaming_v2.t.sol rename to test/unit/modules/paymentProcessor/PP_Streaming_v3.t.sol index 04efe6428..1601d282f 100644 --- a/test/unit/modules/paymentProcessor/PP_Streaming_v2.t.sol +++ b/test/unit/modules/paymentProcessor/PP_Streaming_v3.t.sol @@ -8,34 +8,34 @@ import {IERC165} from "@oz/utils/introspection/IERC165.sol"; import { ModuleTest, - IModule_v1, - IOrchestrator_v1 + IModule_v2, + IOrchestrator_v2 } from "@unitTest/modules/ModuleTest.sol"; // SuT -import {IPaymentProcessor_v2} from - "src/modules/paymentProcessor/IPaymentProcessor_v2.sol"; +import {IPaymentProcessor_v3} from + "src/modules/paymentProcessor/IPaymentProcessor_v3.sol"; import { - PP_Streaming_v2, - IPP_Streaming_v2 -} from "src/modules/paymentProcessor/PP_Streaming_v2.sol"; + PP_Streaming_v3, + IPP_Streaming_v3 +} from "src/modules/paymentProcessor/PP_Streaming_v3.sol"; // Mocks -import {PP_Streaming_v2_Exposed} from - "@mocks/modules/paymentProcessor/PP_Streaming_v2_Exposed.sol"; +import {PP_Streaming_v3_Exposed} from + "@mocks/modules/paymentProcessor/PP_Streaming_v3_Exposed.sol"; import { - IERC20PaymentClientBase_v2, - ERC20PaymentClientBaseV2Mock, + IERC20PaymentClientBase_v3, + ERC20PaymentClientBase_v3_Mock, ERC20Mock -} from "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +} from "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; -contract PP_StreamingV1Test is ModuleTest { +contract PP_Streaming_v3_Test is ModuleTest { bytes32 internal constant _START_END_CLIFF_FLAG = 0x000000000000000000000000000000000000000000000000000000000000000e; uint internal constant defaultStart = 69; @@ -43,10 +43,10 @@ contract PP_StreamingV1Test is ModuleTest { uint internal constant defaultEnd = 420; // SuT - PP_Streaming_v2_Exposed paymentProcessor; + PP_Streaming_v3_Exposed paymentProcessor; // Mocks - ERC20PaymentClientBaseV2Mock paymentClient; + ERC20PaymentClientBase_v3_Mock paymentClient; //-------------------------------------------------------------------------- // Events @@ -90,8 +90,8 @@ contract PP_StreamingV1Test is ModuleTest { ); function setUp() public { - address impl = address(new PP_Streaming_v2_Exposed()); - paymentProcessor = PP_Streaming_v2_Exposed(Clones.clone(impl)); + address impl = address(new PP_Streaming_v3_Exposed()); + paymentProcessor = PP_Streaming_v3_Exposed(Clones.clone(impl)); _setUpOrchestrator(paymentProcessor); @@ -100,11 +100,12 @@ contract PP_StreamingV1Test is ModuleTest { paymentProcessor.init(_orchestrator, _METADATA, configData); - _authorizer.setIsAuthorized(address(this), true); + // Turn on all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(true); // Set up PaymentClient Correctöy - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); _orchestrator.initiateAddModuleWithTimelock(address(paymentClient)); vm.warp(block.timestamp + _orchestrator.MODULE_UPDATE_TIMELOCK()); @@ -124,19 +125,19 @@ contract PP_StreamingV1Test is ModuleTest { ); } - function testSupportsInterface() public { + function testReinitFails() public override(ModuleTest) { + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + paymentProcessor.init(_orchestrator, _METADATA, bytes("")); + } + + function testSupportsInterface() public override(ModuleTest) { assertTrue( paymentProcessor.supportsInterface( - type(IPP_Streaming_v2).interfaceId + type(IPP_Streaming_v3).interfaceId ) ); } - function testReinitFails() public override(ModuleTest) { - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - paymentProcessor.init(_orchestrator, _METADATA, bytes("")); - } - //-------------------------------------------------------------------------- // Test: Payment Processing @@ -197,7 +198,7 @@ contract PP_StreamingV1Test is ModuleTest { 0, block.timestamp + durations[i] ); - emit IPaymentProcessor_v2.PaymentOrderProcessed( + emit IPaymentProcessor_v3.PaymentOrderProcessed( address(paymentClient), recipients[i], address(_token), @@ -583,7 +584,7 @@ contract PP_StreamingV1Test is ModuleTest { ); } - IERC20PaymentClientBase_v2.PaymentOrder[] memory orders = + IERC20PaymentClientBase_v3.PaymentOrder[] memory orders = paymentClient.paymentOrders(); // Call processPayments @@ -592,7 +593,7 @@ contract PP_StreamingV1Test is ModuleTest { for (uint i; i < length; i++) { address recipient = recipients[i]; - IERC20PaymentClientBase_v2.PaymentOrder memory order = orders[i]; + IERC20PaymentClientBase_v3.PaymentOrder memory order = orders[i]; uint end = uint(order.data[2]); @@ -752,7 +753,7 @@ contract PP_StreamingV1Test is ModuleTest { // Now, let's check whether all streaming informations exist or not // checking for paymentReceiver2 - IPP_Streaming_v2.Stream[] memory paymentReceiverStreams; + IPP_Streaming_v3.Stream[] memory paymentReceiverStreams; paymentReceiverStreams = paymentProcessor.viewAllPaymentOrders( address(paymentClient), paymentReceiver2 ); @@ -807,6 +808,22 @@ contract PP_StreamingV1Test is ModuleTest { ); } + function testRemoveAllPaymentReceiverPayments_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + paymentProcessor.removeAllPaymentReceiverPayments( + address(0), address(0) + ); + } + uint initialNumWallets; uint initialPaymentReceiverBalance; uint initialStreamIdAtIndex1; @@ -876,7 +893,7 @@ contract PP_StreamingV1Test is ModuleTest { // This means, that when we call removePaymentForSpecificStream, that should increase the balance of the // paymentReceiver by 1/2 of the vested token amount - IPP_Streaming_v2.Stream[] memory paymentReceiverStreams = + IPP_Streaming_v3.Stream[] memory paymentReceiverStreams = paymentProcessor.viewAllPaymentOrders( address(paymentClient), paymentReceiver1 ); @@ -991,7 +1008,7 @@ contract PP_StreamingV1Test is ModuleTest { // Let's note down the current balance of the paymentReceiver1 initialPaymentReceiverBalance = _token.balanceOf(paymentReceiver1); - IPP_Streaming_v2.Stream[] memory paymentReceiverStreams = + IPP_Streaming_v3.Stream[] memory paymentReceiverStreams = paymentProcessor.viewAllPaymentOrders( address(paymentClient), paymentReceiver1 ); @@ -1073,12 +1090,28 @@ contract PP_StreamingV1Test is ModuleTest { ); } + function testRemovePaymentFromSpecificStream_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + paymentProcessor.removePaymentForSpecificStream( + address(0), address(0), 0 + ); + } + function test_processPayments_failsWhenCalledByNonModule(address nonModule) public { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_authorizer)); @@ -1087,7 +1120,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(nonModule); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__OnlyCallableByModule .selector ) @@ -1101,18 +1134,18 @@ contract PP_StreamingV1Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); - ERC20PaymentClientBaseV2Mock otherERC20PaymentClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock otherERC20PaymentClient = + new ERC20PaymentClientBase_v3_Mock(); vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__CannotCallOnOtherClientsOrders .selector ) @@ -1141,7 +1174,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -1193,7 +1226,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -1221,7 +1254,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -1249,7 +1282,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -1277,7 +1310,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IERC20PaymentClientBase_v2 + IERC20PaymentClientBase_v3 .Module__ERC20PaymentClientBase__InvalidPaymentOrder .selector ) @@ -1327,7 +1360,7 @@ contract PP_StreamingV1Test is ModuleTest { 0, duration + block.timestamp ); - emit IPaymentProcessor_v2.PaymentOrderProcessed( + emit IPaymentProcessor_v3.PaymentOrderProcessed( address(paymentClient), recipients[i], address(_token), @@ -1404,7 +1437,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); @@ -1412,7 +1445,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.prank(nonModule); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__OnlyCallableByModule .selector ) @@ -1426,18 +1459,18 @@ contract PP_StreamingV1Test is ModuleTest { vm.assume(nonModule != address(paymentProcessor)); vm.assume(nonModule != address(paymentClient)); vm.assume(nonModule != address(_authorizer)); - // PaymentProcessorV1Mock gets deployed and initialized in ModuleTest, + // PaymentProcessor_v3_Mock gets deployed and initialized in ModuleTest, // if deployed address is same as nonModule, this test will fail. vm.assume(nonModule != address(_paymentProcessor)); vm.assume(nonModule != address(_fundingManager)); - ERC20PaymentClientBaseV2Mock otherERC20PaymentClient = - new ERC20PaymentClientBaseV2Mock(); + ERC20PaymentClientBase_v3_Mock otherERC20PaymentClient = + new ERC20PaymentClientBase_v3_Mock(); vm.prank(address(paymentClient)); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__CannotCallOnOtherClientsOrders .selector ) @@ -1511,7 +1544,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.startPrank(recipient); vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__NothingToClaim .selector, address(paymentClient), @@ -1690,7 +1723,7 @@ contract PP_StreamingV1Test is ModuleTest { function testClaimPreviouslyUnclaimableFailsIfNothingToClaim() public { vm.expectRevert( abi.encodeWithSelector( - IPaymentProcessor_v2 + IPaymentProcessor_v3 .Module__PaymentProcessor__NothingToClaim .selector, address(paymentClient), @@ -1742,7 +1775,7 @@ contract PP_StreamingV1Test is ModuleTest { ); } - // No funds left in the ERC20PaymentClientBase_v2 + // No funds left in the ERC20PaymentClientBase_v3 assertEq(_token.balanceOf(address(paymentClient)), 0); // Invariant: Payment processor does not hold funds. @@ -1996,7 +2029,7 @@ contract PP_StreamingV1Test is ModuleTest { } function test_ValidPaymentOrder( - IERC20PaymentClientBase_v2.PaymentOrder memory order, + IERC20PaymentClientBase_v3.PaymentOrder memory order, address sender, uint start, uint cliff, @@ -2193,7 +2226,7 @@ contract PP_StreamingV1Test is ModuleTest { vm.expectRevert( abi.encodeWithSelector( - IPP_Streaming_v2 + IPP_Streaming_v3 .Module__PP_Streaming__InvalidDefaultTimes .selector, defaultStart_, @@ -2206,6 +2239,20 @@ contract PP_StreamingV1Test is ModuleTest { ); } + function testSetStreamingDefaults_ModifierInPosition() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + _authorizer.setAllAuthorized(false); + vm.expectRevert( + abi.encodeWithSelector( + IModule_v2.Module__CallerNotPermissioned.selector + ) + ); + vm.prank(address(0xB0B)); + paymentProcessor.setStreamingDefaults(0, 0, 0); + } + function test_getProcessorFlags() public { bytes32 flags = paymentProcessor.getProcessorFlags(); assertEq(flags, _START_END_CLIFF_FLAG); @@ -2277,7 +2324,7 @@ contract PP_StreamingV1Test is ModuleTest { ) internal view - returns (IERC20PaymentClientBase_v2.PaymentOrder memory paymentOrder) + returns (IERC20PaymentClientBase_v3.PaymentOrder memory paymentOrder) { bytes32 flagsBytes = 0x000000000000000000000000000000000000000000000000000000000000000e; @@ -2286,7 +2333,7 @@ contract PP_StreamingV1Test is ModuleTest { data[1] = bytes32(cliff); data[2] = bytes32(end); - paymentOrder = IERC20PaymentClientBase_v2.PaymentOrder({ + paymentOrder = IERC20PaymentClientBase_v3.PaymentOrder({ recipient: recipient, paymentToken: paymentToken, amount: amount, diff --git a/test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.t.sol b/test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.t.sol similarity index 93% rename from test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.t.sol rename to test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.t.sol index 448283180..68fab48b4 100644 --- a/test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1.t.sol +++ b/test/unit/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; // Internal imports -import {PP_CrossChainBase_v1} from "@pp/abstracts/PP_CrossChainBase_v1.sol"; -import {IPP_CrossChainBase_v1} from "@pp/interfaces/IPP_CrossChainBase_v1.sol"; +import {PP_CrossChainBase_v2} from "@pp/abstracts/PP_CrossChainBase_v2.sol"; +import {IPP_CrossChainBase_v2} from "@pp/interfaces/IPP_CrossChainBase_v2.sol"; import {IPaymentProcessor_v2} from "@pp/IPaymentProcessor_v2.sol"; -import {IERC20PaymentClientBase_v2} from - "@lm/interfaces/IERC20PaymentClientBase_v2.sol"; +import {IERC20PaymentClientBase_v3} from + "@lm/interfaces/IERC20PaymentClientBase_v3.sol"; // External imports import {Clones} from "@oz/proxy/Clones.sol"; @@ -14,37 +14,38 @@ import {OZErrors} from "@testUtilities/OZErrors.sol"; // Tests and Mocks import {ModuleTest} from "@unitTest/modules/ModuleTest.sol"; -import {ERC20PaymentClientBaseV2Mock} from - "@mocks/modules/paymentClient/ERC20PaymentClientBaseV2Mock.sol"; +import {ERC20PaymentClientBase_v3_Mock} from + "@mocks/modules/paymentClient/ERC20PaymentClientBase_v3_Mock.sol"; import {FundingManagerV1Mock} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {PaymentProcessor_v3_Mock} from + "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; // SuT -import {PP_CrossChainBase_v1_Exposed} from - "@mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v1_Exposed.sol"; +import {PP_CrossChainBase_v2_Exposed} from + "@mocks/modules/paymentProcessor/abstracts/PP_CrossChainBase_v2_Exposed.sol"; -contract PP_CrossChainBase_v1_Test is ModuleTest { +contract PP_CrossChainBase_v2_Test is ModuleTest { // ======================================================================== // State - PP_CrossChainBase_v1_Exposed public crossChainPaymentProcessorBase; - ERC20PaymentClientBaseV2Mock public paymentClient; + PP_CrossChainBase_v2_Exposed public crossChainPaymentProcessorBase; + ERC20PaymentClientBase_v3_Mock public paymentClient; // ======================================================================== // Setup function setUp() public { // Deploy and init the SUT - address impl = address(new PP_CrossChainBase_v1_Exposed()); + address impl = address(new PP_CrossChainBase_v2_Exposed()); crossChainPaymentProcessorBase = - PP_CrossChainBase_v1_Exposed(Clones.clone(impl)); + PP_CrossChainBase_v2_Exposed(Clones.clone(impl)); // Deploy and setup the payment client for testing SUT - impl = address(new ERC20PaymentClientBaseV2Mock()); - paymentClient = ERC20PaymentClientBaseV2Mock(Clones.clone(impl)); + impl = address(new ERC20PaymentClientBase_v3_Mock()); + paymentClient = ERC20PaymentClientBase_v3_Mock(Clones.clone(impl)); // Setup the mock workflow contracts and token _setUpOrchestrator(paymentClient); @@ -77,10 +78,10 @@ contract PP_CrossChainBase_v1_Test is ModuleTest { ); } - function testSupportsInterface() public { + function testSupportsInterface() public override { assertTrue( crossChainPaymentProcessorBase.supportsInterface( - type(IPP_CrossChainBase_v1).interfaceId + type(IPP_CrossChainBase_v2).interfaceId ) ); assertTrue( @@ -319,11 +320,11 @@ contract PP_CrossChainBase_v1_Test is ModuleTest { .selector ); crossChainPaymentProcessorBase.cancelRunningPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); // Test 3: Deploy module which is not registered and call cancelRunningPayments() - address paymentProcessor = address(new PaymentProcessorV1Mock()); + address paymentProcessor = address(new PaymentProcessor_v3_Mock()); // Test function call vm.prank(paymentProcessor); @@ -333,11 +334,11 @@ contract PP_CrossChainBase_v1_Test is ModuleTest { .selector ); crossChainPaymentProcessorBase.cancelRunningPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); // Test 3: Deploy module which is not registered and call cancelRunningPayments() - address authorizer = address(new AuthorizerV1Mock()); + address authorizer = address(new Authorizer_v2_Mock()); // Test function call vm.prank(authorizer); @@ -347,7 +348,7 @@ contract PP_CrossChainBase_v1_Test is ModuleTest { .selector ); crossChainPaymentProcessorBase.cancelRunningPayments( - IERC20PaymentClientBase_v2(address(paymentClient)) + IERC20PaymentClientBase_v3(address(paymentClient)) ); } @@ -397,8 +398,8 @@ contract PP_CrossChainBase_v1_Test is ModuleTest { public { // Create mock payment order - IERC20PaymentClientBase_v2.PaymentOrder memory order = - IERC20PaymentClientBase_v2.PaymentOrder({ + IERC20PaymentClientBase_v3.PaymentOrder memory order = + IERC20PaymentClientBase_v3.PaymentOrder({ recipient: address(0), paymentToken: address(0), amount: 0 ether, diff --git a/test/unit/orchestrator/Orchestrator_v1.t.sol b/test/unit/orchestrator/Orchestrator_v1.t.sol deleted file mode 100644 index 47add9f34..000000000 --- a/test/unit/orchestrator/Orchestrator_v1.t.sol +++ /dev/null @@ -1,754 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; - -// External Libraries -import {Clones} from "@oz/proxy/Clones.sol"; - -// External Interfaces -import {IERC20} from "@oz/token/ERC20/IERC20.sol"; - -// Internal Dependencies -import {Orchestrator_v1} from "src/orchestrator/Orchestrator_v1.sol"; -import {IModule_v1} from "src/modules/base/IModule_v1.sol"; - -// Internal Interfaces -import { - IOrchestrator_v1, - IAuthorizer_v1, - IPaymentProcessor_v2 -} from "src/orchestrator/interfaces/IOrchestrator_v1.sol"; - -import {TransactionForwarder_v1} from - "src/external/forwarder/TransactionForwarder_v1.sol"; - -// Mocks -import { - FundingManagerV1Mock, - IFundingManager_v1 -} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; -import {PaymentProcessorV1Mock} from - "@mocks/modules/paymentProcessor/PaymentProcessorV1Mock.sol"; -import {GovernorV1Mock} from "@mocks/external/governance/GovernorV1Mock.sol"; -import {ModuleFactoryV1Mock} from "@mocks/factories/ModuleFactoryV1Mock.sol"; -import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; - -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; -// Errors -import {OZErrors} from "@testUtilities/OZErrors.sol"; - -// Helper -import {TypeSanityHelper} from "@testUtilities/TypeSanityHelper.sol"; - -contract OrchestratorV1Test is Test { - // SuT - Orchestrator_v1 orchestrator; - - // Helper - TypeSanityHelper types; - - // Mocks - FundingManagerV1Mock fundingManager; - AuthorizerV1Mock authorizer; - PaymentProcessorV1Mock paymentProcessor; - GovernorV1Mock governor; - ModuleFactoryV1Mock moduleFactory; - ERC20Mock token; - TransactionForwarder_v1 forwarder; - - event AuthorizerUpdated(address indexed _address); - event FundingManagerUpdated(address indexed _address); - event PaymentProcessorUpdated(address indexed _address); - event OrchestratorInitialized( - uint indexed orchestratorId_, - address fundingManager, - address authorizer, - address paymentProcessor, - address[] modules, - address governor - ); - - function setUp() public { - fundingManager = new FundingManagerV1Mock(); - authorizer = new AuthorizerV1Mock(); - paymentProcessor = new PaymentProcessorV1Mock(); - governor = new GovernorV1Mock(); - moduleFactory = new ModuleFactoryV1Mock(); - forwarder = new TransactionForwarder_v1(); - token = new ERC20Mock("TestToken", "TST", 18); - - address impl = address(new Orchestrator_v1(address(forwarder))); - orchestrator = Orchestrator_v1(Clones.clone(impl)); - - types = new TypeSanityHelper(address(orchestrator)); - } - - //-------------------------------------------------------------------------- - // Tests: Initialization - - function testInit(uint orchestratorId, uint moduleAmount) public { - types.assumeValidOrchestratorId(orchestratorId); - - address[] memory modules = createModules(moduleAmount); - - address wrongModule = address(new ModuleV1Mock()); - - // We expect reverts when trying to set the wrong module as any of the privileged modules - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - wrongModule - ) - ); - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - IFundingManager_v1(wrongModule), - authorizer, - paymentProcessor, - governor - ); - - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - wrongModule - ) - ); - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - fundingManager, - IAuthorizer_v1(wrongModule), - paymentProcessor, - governor - ); - - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - wrongModule - ) - ); - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - fundingManager, - authorizer, - IPaymentProcessor_v2(wrongModule), - governor - ); - - // Now we test correct initialization - vm.expectEmit(true, true, true, false); - emit OrchestratorInitialized( - orchestratorId, - address(fundingManager), - address(authorizer), - address(paymentProcessor), - modules, - address(governor) - ); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - // Check that orchestrator's storage correctly initialized. - assertEq(orchestrator.orchestratorId(), orchestratorId); - assertEq(address(orchestrator.authorizer()), address(authorizer)); - assertEq( - address(orchestrator.paymentProcessor()), address(paymentProcessor) - ); - - assertEq( - address(orchestrator.fundingManager()), address(fundingManager) - ); - assertTrue(orchestrator.isTrustedForwarder(address(forwarder))); - } - - function testReinitFails(uint orchestratorId, uint moduleAmount) public { - address[] memory modules = createModules(moduleAmount); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - vm.expectRevert(OZErrors.Initializable__InvalidInitialization); - orchestrator.init( - orchestratorId, - address(moduleFactory), - modules, - fundingManager, - authorizer, - paymentProcessor, - governor - ); - } - - //-------------------------------------------------------------------------- - // Tests: Replacing the three base modules: authorizer, funding manager, - // payment processor - - function testInitiateAndExecuteSetAuthorizer( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new authorizer module - AuthorizerV1Mock newAuthorizer = new AuthorizerV1Mock(); - - newAuthorizer.mockInit(abi.encode(address(0xA11CE))); - - orchestrator.initiateSetAuthorizerWithTimelock(newAuthorizer); - vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); - - // set the new authorizer module - vm.expectEmit(true, true, true, true); - emit AuthorizerUpdated(address(newAuthorizer)); - orchestrator.executeSetAuthorizer(newAuthorizer); - - assertTrue(orchestrator.authorizer() == newAuthorizer); - - // verify whether the init value is set and not the value from the old - // authorizer, to check whether the replacement is successful - bytes32 adminRole = orchestrator.authorizer().getAdminRole(); - assertFalse(orchestrator.authorizer().hasRole(adminRole, address(this))); - assertTrue( - orchestrator.authorizer().hasRole(adminRole, address(0xA11CE)) - ); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testExecuteSetAuthorizer_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new authorizer module - address newAuthorizer = address(0x8888); - - // set the new payment processor module. First the verification function reverts, then the setter. - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newAuthorizer - ) - ); - orchestrator.executeSetAuthorizer(IAuthorizer_v1(newAuthorizer)); - assertTrue(orchestrator.authorizer() == authorizer); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testInitiateSetAuthorizerWithTimelock_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new authorizer module - address newAuthorizer = address(0x8888); - - // set the new payment processor module. First the verification function reverts, then the setter. - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newAuthorizer - ) - ); - orchestrator.initiateSetAuthorizerWithTimelock( - IAuthorizer_v1(newAuthorizer) - ); - assertTrue(orchestrator.authorizer() == authorizer); - } - - function testInitiateAndExecuteSetFundingManager( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( - IERC20(address(0xA11CE)) - ); - - // Create new funding manager module - FundingManagerV1Mock newFundingManager = new FundingManagerV1Mock(); - newFundingManager.setToken(IERC20(address(0xA11CE))); - - orchestrator.initiateSetFundingManagerWithTimelock(newFundingManager); - vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); - - // set the new funding manager module - vm.expectEmit(true, true, true, true); - emit FundingManagerUpdated(address(newFundingManager)); - orchestrator.executeSetFundingManager(newFundingManager); - assertTrue(orchestrator.fundingManager() == newFundingManager); - assertTrue( - address((orchestrator.fundingManager()).token()) == address(0xA11CE) - ); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testInitiateSetFundingManagerWithTimelock_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( - IERC20(address(0xA11CE)) - ); - - // Create new funding manager module - address newFundingManager = address(0x8888); - - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newFundingManager - ) - ); - orchestrator.initiateSetFundingManagerWithTimelock( - IFundingManager_v1(newFundingManager) - ); - assertTrue(orchestrator.fundingManager() == fundingManager); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testExecuteSetFundingManager_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( - IERC20(address(0xA11CE)) - ); - - // Create new funding manager module - address newFundingManager = address(0x8888); - - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newFundingManager - ) - ); - orchestrator.executeSetFundingManager( - IFundingManager_v1(newFundingManager) - ); - assertTrue(orchestrator.fundingManager() == fundingManager); - } - - function testInitiateAndExecuteSetFundingManager_failsIfMismatchedTokens( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( - IERC20(address(0xA11CE)) - ); - - // Create new funding manager module - FundingManagerV1Mock newFundingManager = new FundingManagerV1Mock(); - newFundingManager.setToken(IERC20(address(0xB0B))); - - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1 - .Orchestrator__MismatchedTokenForFundingManager - .selector, - orchestrator.fundingManager().token(), - newFundingManager.token() - ) - ); - orchestrator.initiateSetFundingManagerWithTimelock(newFundingManager); - } - - function testInitiateAndExecuteSetPaymentProcessor( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new payment processor module - PaymentProcessorV1Mock newPaymentProcessor = - new PaymentProcessorV1Mock(); - - orchestrator.initiateSetPaymentProcessorWithTimelock( - newPaymentProcessor - ); - vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); - - // set the new payment processor module - vm.expectEmit(true, true, true, true); - emit PaymentProcessorUpdated(address(newPaymentProcessor)); - orchestrator.executeSetPaymentProcessor(newPaymentProcessor); - assertTrue(orchestrator.paymentProcessor() == newPaymentProcessor); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testInitiateSetPaymentProcessorWithTimelock_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new payment processor module - address newPaymentProcessor = address(0x8888); - - // set the new payment processor module. First the verification function reverts, then the setter. - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newPaymentProcessor - ) - ); - orchestrator.initiateSetPaymentProcessorWithTimelock( - IPaymentProcessor_v2(newPaymentProcessor) - ); - - assertTrue(orchestrator.paymentProcessor() == paymentProcessor); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testExecuteSetPaymentProcessor_FailsIfWrongModuleType( - uint orchestratorId, - uint moduleAmount - ) public { - types.assumeValidOrchestratorId(orchestratorId); - // Initialize orchestrator. - orchestrator.init( - orchestratorId, - address(moduleFactory), - createModules(moduleAmount), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - - // Create new payment processor module - address newPaymentProcessor = address(0x8888); - - // set the new payment processor module. First the verification function reverts, then the setter. - vm.expectRevert(); - vm.expectRevert( - abi.encodeWithSelector( - IOrchestrator_v1.Orchestrator__InvalidModuleType.selector, - newPaymentProcessor - ) - ); - orchestrator.executeSetPaymentProcessor( - IPaymentProcessor_v2(newPaymentProcessor) - ); - - assertTrue(orchestrator.paymentProcessor() == paymentProcessor); - } - - /* Test function initiateRemoveModuleWithTimelock - ├── Given the module address to be removed is the current authorizer - │ └── When the function initiateRemoveModuleWithTimelock() gets called - │ └── Then the function should revert - ├── Given the module address to be removed is the current funding manager - │ └── When the function initiateRemoveModuleWithTimelock() gets called - │ └── Then the function should revert - └── Given the module address to be removed is the current payment processor - └── When the function initiateRemoveModuleWithTimelock() gets called - └── Then the function should revert - */ - - function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentAuthorizer( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - address currentAuthorizer = address(orchestrator.authorizer()); - - vm.expectRevert( - IOrchestrator_v1.Orchestrator__InvalidRemovalOfAuthorizer.selector - ); - orchestrator.initiateRemoveModuleWithTimelock(currentAuthorizer); - } - - function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentFundingManager( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - address currentFundingManager = address(orchestrator.fundingManager()); - - vm.expectRevert( - IOrchestrator_v1 - .Orchestrator__InvalidRemovalOfFundingManager - .selector - ); - orchestrator.initiateRemoveModuleWithTimelock(currentFundingManager); - } - - function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentPaymentProcessor( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - address currentPaymentProcessor = - address(orchestrator.paymentProcessor()); - - vm.expectRevert( - IOrchestrator_v1 - .Orchestrator__InvalidRemovalOfPaymentProcessor - .selector - ); - orchestrator.initiateRemoveModuleWithTimelock(currentPaymentProcessor); - } - /* Test function executeRemoveModule - ├── Given the module address to be removed is the current authorizer - │ └── When the function executeRemoveModule() gets called - │ └── Then the function should revert - ├── Given the module address to be removed is the current funding manager - │ └── When the function executeRemoveModule() gets called - │ └── Then the function should revert - └── Given the module address to be removed is the current payment processor - └── When the function executeRemoveModule() gets called - └── Then the function should revert - */ - - function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentAuthorizer( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - - authorizer.setIsAuthorized(address(this), true); - address currentAuthorizer = address(orchestrator.authorizer()); - - vm.expectRevert( - IOrchestrator_v1.Orchestrator__InvalidRemovalOfAuthorizer.selector - ); - orchestrator.executeRemoveModule(currentAuthorizer); - } - - function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentFundingManager( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - address currentFundingManager = address(orchestrator.fundingManager()); - - vm.expectRevert( - IOrchestrator_v1 - .Orchestrator__InvalidRemovalOfFundingManager - .selector - ); - orchestrator.executeRemoveModule(currentFundingManager); - } - - function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentPaymentProcessor( - ) public { - orchestrator.init( - 1, - address(moduleFactory), - new address[](0), - fundingManager, - authorizer, - paymentProcessor, - governor - ); - address currentPaymentProcessor = - address(orchestrator.paymentProcessor()); - - vm.expectRevert( - IOrchestrator_v1 - .Orchestrator__InvalidRemovalOfPaymentProcessor - .selector - ); - orchestrator.executeRemoveModule(currentPaymentProcessor); - } - - //-------------------------------------------------------------------------- - // Helper Functions - - function createModules(uint amount) - internal - returns (address[] memory modules) - { - if (amount > 50) { - amount = 50; - } - - modules = new address[](amount); - for (uint i = 0; i < amount; i++) { - modules[i] = address(new ModuleV1Mock()); - } - } -} diff --git a/test/unit/orchestrator/Orchestrator_v2.t.sol b/test/unit/orchestrator/Orchestrator_v2.t.sol new file mode 100644 index 000000000..2ac79d40b --- /dev/null +++ b/test/unit/orchestrator/Orchestrator_v2.t.sol @@ -0,0 +1,1465 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// External Libraries +import {Clones} from "@oz/proxy/Clones.sol"; + +// External Interfaces +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; + +// Internal Dependencies +import {Orchestrator_v2_Exposed} from + "@mocks/orchestrator/Orchestrator_v2_Exposed.sol"; +import {IModule_v1} from "src/modules/base/IModule_v1.sol"; +import {IModule_v2} from "src/modules/base/IModule_v2.sol"; + +// Internal Interfaces +import { + IOrchestrator_v2, + IAuthorizer_v2, + IPaymentProcessor_v3 +} from "src/orchestrator/interfaces/IOrchestrator_v2.sol"; + +import {TransactionForwarder_v1} from + "src/external/forwarder/TransactionForwarder_v1.sol"; + +// Mocks +import { + FundingManagerV1Mock, + IFundingManager_v1 +} from "@mocks/modules/fundingManager/FundingManagerV1Mock.sol"; +import { + Authorizer_v1_Mock, + IAuthorizer_v1 +} from "@mocks/modules/authorizer/Authorizer_v1_Mock.sol"; +import { + Authorizer_v2_Mock, + IAuthorizer_v2 +} from "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import { + PaymentProcessor_v1_Mock, + IPaymentProcessor_v1 +} from "@mocks/modules/paymentProcessor/PaymentProcessor_v1_Mock.sol"; +import { + PaymentProcessor_v2_Mock, + IPaymentProcessor_v2 +} from "@mocks/modules/paymentProcessor/PaymentProcessor_v2_Mock.sol"; +import { + PaymentProcessor_v3_Mock, + IPaymentProcessor_v3 +} from "@mocks/modules/paymentProcessor/PaymentProcessor_v3_Mock.sol"; +import {GovernorV1Mock} from "@mocks/external/governance/GovernorV1Mock.sol"; +import {ModuleFactoryV1Mock} from "@mocks/factories/ModuleFactoryV1Mock.sol"; +import {ERC20Mock} from "@mocks/external/token/ERC20Mock.sol"; + +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; +// Errors +import {OZErrors} from "@testUtilities/OZErrors.sol"; + +// Helper +import {TypeSanityHelper} from "@testUtilities/TypeSanityHelper.sol"; +import {ERC165InterfaceMock} from "@testUtilities/ERC165InterfaceMock.sol"; + +contract OrchestratorV1Test is Test { + // SuT + Orchestrator_v2_Exposed orchestrator; + + // Helper + TypeSanityHelper types; + + // Mocks + FundingManagerV1Mock fundingManager; + Authorizer_v2_Mock authorizer; + PaymentProcessor_v3_Mock paymentProcessor; + GovernorV1Mock governor; + ModuleFactoryV1Mock moduleFactory; + ERC20Mock token; + TransactionForwarder_v1 forwarder; + + function setUp() public { + fundingManager = new FundingManagerV1Mock(); + authorizer = new Authorizer_v2_Mock(); + paymentProcessor = new PaymentProcessor_v3_Mock(); + governor = new GovernorV1Mock(); + moduleFactory = new ModuleFactoryV1Mock(); + forwarder = new TransactionForwarder_v1(); + token = new ERC20Mock("TestToken", "TST", 18); + + address impl = address(new Orchestrator_v2_Exposed(address(forwarder))); + orchestrator = Orchestrator_v2_Exposed(Clones.clone(impl)); + + types = new TypeSanityHelper(address(orchestrator)); + + // Actually link the Authorizer to the Orchestrator + orchestrator.setup_authorizer(address(authorizer)); + + // Set the default admin to this contract + authorizer.setDefaultAdmin(address(this)); + + // Every caller has permission for every permissioned function + authorizer.setAllAuthorized(true); + } + + //-------------------------------------------------------------------------- + // Tests: Initialization + + function testInit(uint orchestratorId, uint moduleAmount) public { + types.assumeValidOrchestratorId(orchestratorId); + + address[] memory modules = createModules(moduleAmount); + + address wrongModule = address(new Module_v2_Mock()); + + // We expect reverts when trying to set the wrong module as any of the privileged modules + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + wrongModule + ) + ); + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + IFundingManager_v1(wrongModule), + authorizer, + paymentProcessor, + governor + ); + + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + wrongModule + ) + ); + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + fundingManager, + IAuthorizer_v2(wrongModule), + paymentProcessor, + governor + ); + + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + wrongModule + ) + ); + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + fundingManager, + authorizer, + IPaymentProcessor_v3(wrongModule), + governor + ); + + // Now we test correct initialization + vm.expectEmit(true, true, true, false); + emit IOrchestrator_v2.OrchestratorInitialized( + orchestratorId, + address(fundingManager), + address(authorizer), + address(paymentProcessor), + modules, + address(governor) + ); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Check that orchestrator's storage correctly initialized. + assertEq(orchestrator.orchestratorId(), orchestratorId); + assertEq(address(orchestrator.authorizer()), address(authorizer)); + assertEq( + address(orchestrator.paymentProcessor()), address(paymentProcessor) + ); + + assertEq( + address(orchestrator.fundingManager()), address(fundingManager) + ); + assertTrue(orchestrator.isTrustedForwarder(address(forwarder))); + } + + function testReinitFails(uint orchestratorId, uint moduleAmount) public { + address[] memory modules = createModules(moduleAmount); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + vm.expectRevert(OZErrors.Initializable__InvalidInitialization); + orchestrator.init( + orchestratorId, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + } + + //-------------------------------------------------------------------------- + // Tests: Modifiers + + /* + Test: permissioned + ├── Given: modifierPermissionedCheck is executed via call with a valid selector, but random data + ├── And: The call sender is randomised + └── And: The Caller is permissioned to call the function + └── When: The function modifierPermissionedCheck is called + └── Then: the function should not revert, because the sender and only the function selector were correctly passed + */ + function testPermissioned_modifier(address caller_, bytes memory data_) + public + { + // Assume that the calldata is at least 4 bytes long + vm.assume(data_.length >= 4); + + bytes4 targetSelector = + Orchestrator_v2_Exposed.modifierPermissionedCheck.selector; + + // Proof + authorizer.setHasPermission( + caller_, address(orchestrator), targetSelector, true + ); + + // Replace the msg.data function selector with the correct one + for (uint i = 0; i < 4; i++) { + data_[i] = targetSelector[i]; + } + + // Expect no revert + vm.prank(caller_); + address(orchestrator).call(data_); + } + + //-------------------------------------------------------------------------- + // Tests: Replacing the three base modules: authorizer, funding manager, + // payment processor + + /* + Test: initiateSetAuthorizerWithTimelock Modifier Checks + └── Given: caller is not permissioned + └── When: initiateSetAuthorizerWithTimelock is called + └── Then: it should revert (modifier in position check) + */ + function testInitiateSetAuthorizerWithTimelock_ModifierInPositionChecks() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + + vm.prank(address(0xB0B)); + orchestrator.initiateSetAuthorizerWithTimelock(address(0)); + } + + /* + Test: executeSetAuthorizer Modifier Checks + └── Given: caller is not permissioned + └── When: executeSetAuthorizer is called + └── Then: it should revert (modifier in position check) + */ + function testExecuteSetAuthorizer_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.executeSetAuthorizer(address(0)); + } + + /* + Test: cancelAuthorizerUpdate Modifier Checks + └── Given: caller is not permissioned + └── When: cancelAuthorizerUpdate is called + └── Then: it should revert (modifier in position check) + */ + function testCancelAuthorizerUpdate_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.cancelAuthorizerUpdate(address(0)); + } + + function testInitiateSetAuthorizerWithTimelock_SupportsIAuthorizer_v1() + public + { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + Authorizer_v1_Mock newAuthorizer_v1 = new Authorizer_v1_Mock(); + + orchestrator.initiateSetAuthorizerWithTimelock( + address(newAuthorizer_v1) + ); + } + + function testInitiateSetAuthorizerWithTimelock_SupportsIAuthorizer_v2() + public + { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + Authorizer_v2_Mock newAuthorizer_v2 = new Authorizer_v2_Mock(); + + orchestrator.initiateSetAuthorizerWithTimelock( + address(newAuthorizer_v2) + ); + } + + function testExecuteSetAuthorizerWithTimelock_SupportsIAuthorizer_v1() + public + { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + Authorizer_v1_Mock newAuthorizer_v1 = new Authorizer_v1_Mock(); + + orchestrator.initiateSetAuthorizerWithTimelock( + address(newAuthorizer_v1) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetAuthorizer(address(newAuthorizer_v1)); + } + + function testExecuteSetAuthorizerWithTimelock_SupportsIAuthorizer_v2() + public + { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + Authorizer_v2_Mock newAuthorizer_v2 = new Authorizer_v2_Mock(); + + orchestrator.initiateSetAuthorizerWithTimelock( + address(newAuthorizer_v2) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetAuthorizer(address(newAuthorizer_v2)); + } + + function testInitiateAndExecuteSetAuthorizer( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + Authorizer_v2_Mock newAuthorizer = new Authorizer_v2_Mock(); + + newAuthorizer.mockInit(abi.encode(address(0xA11CE))); + + orchestrator.initiateSetAuthorizerWithTimelock(address(newAuthorizer)); + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + // set the new authorizer module + vm.expectEmit(true, true, true, true); + emit IOrchestrator_v2.AuthorizerUpdated(address(newAuthorizer)); + orchestrator.executeSetAuthorizer(address(newAuthorizer)); + + assertTrue(orchestrator.authorizer() == newAuthorizer); + + // verify whether the init value is set and not the value from the old + // authorizer, to check whether the replacement is successful + bytes32 adminRole = orchestrator.authorizer().getAdminRole(); + assertFalse(orchestrator.authorizer().hasRole(adminRole, address(this))); + assertTrue( + orchestrator.authorizer().hasRole(adminRole, address(0xA11CE)) + ); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testExecuteSetAuthorizer_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + address newAuthorizer = address(0x8888); + + // set the new payment processor module. First the verification function reverts, then the setter. + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newAuthorizer + ) + ); + orchestrator.executeSetAuthorizer(newAuthorizer); + assertTrue(orchestrator.authorizer() == authorizer); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testInitiateSetAuthorizerWithTimelock_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new authorizer module + address newAuthorizer = address(0x8888); + + // set the new payment processor module. First the verification function reverts, then the setter. + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newAuthorizer + ) + ); + orchestrator.initiateSetAuthorizerWithTimelock(newAuthorizer); + assertTrue(orchestrator.authorizer() == authorizer); + } + + /* + Test: initiateSetFundingManagerWithTimelock Modifier Checks + └── Given: caller is not permissioned + └── When: initiateSetFundingManagerWithTimelock is called + └── Then: it should revert (modifier in position check) + */ + function testInitiateSetFundingManagerWithTimelock_ModifierInPositionChecks( + ) public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + + vm.prank(address(0xB0B)); + orchestrator.initiateSetFundingManagerWithTimelock(address(0)); + } + + /* + Test: executeSetFundingManager Modifier Checks + └── Given: caller is not permissioned + └── When: executeSetFundingManager is called + └── Then: it should revert (modifier in position check) + */ + function testExecuteSetFundingManager_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.executeSetFundingManager(address(0)); + } + + /* + Test: cancelFundingManagerUpdate Modifier Checks + └── Given: caller is not permissioned + └── When: cancelFundingManagerUpdate is called + └── Then: it should revert (modifier in position check) + */ + function testCancelFundingManagerUpdate_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.cancelFundingManagerUpdate(address(0)); + } + + function testInitiateSetFundingmanagerWithTimelock_SupportsIFundingManager_v1( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new funding manager module + FundingManagerV1Mock newFundingManager_v1 = new FundingManagerV1Mock(); + + orchestrator.initiateSetFundingManagerWithTimelock( + address(newFundingManager_v1) + ); + } + + function testExecuteSetFundingmanagerWithTimelock_SupportsIFundingManager_v1( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new funding manager module + FundingManagerV1Mock newFundingManager_v1 = new FundingManagerV1Mock(); + + orchestrator.initiateSetFundingManagerWithTimelock( + address(newFundingManager_v1) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetFundingManager(address(newFundingManager_v1)); + } + + function testInitiateAndExecuteSetFundingManager( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( + IERC20(address(0xA11CE)) + ); + + // Create new funding manager module + FundingManagerV1Mock newFundingManager = new FundingManagerV1Mock(); + newFundingManager.setToken(IERC20(address(0xA11CE))); + + orchestrator.initiateSetFundingManagerWithTimelock( + address(newFundingManager) + ); + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + // set the new funding manager module + vm.expectEmit(true, true, true, true); + emit IOrchestrator_v2.FundingManagerUpdated(address(newFundingManager)); + orchestrator.executeSetFundingManager(address(newFundingManager)); + assertTrue(orchestrator.fundingManager() == newFundingManager); + assertTrue( + address((orchestrator.fundingManager()).token()) == address(0xA11CE) + ); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testInitiateSetFundingManagerWithTimelock_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( + IERC20(address(0xA11CE)) + ); + + // Create new funding manager module + address newFundingManager = address(0x8888); + + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newFundingManager + ) + ); + orchestrator.initiateSetFundingManagerWithTimelock(newFundingManager); + assertTrue(orchestrator.fundingManager() == fundingManager); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testExecuteSetFundingManager_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( + IERC20(address(0xA11CE)) + ); + + // Create new funding manager module + address newFundingManager = address(0x8888); + + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newFundingManager + ) + ); + orchestrator.executeSetFundingManager(newFundingManager); + assertTrue(orchestrator.fundingManager() == fundingManager); + } + + function testInitiateAndExecuteSetFundingManager_failsIfMismatchedTokens( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + FundingManagerV1Mock(address(orchestrator.fundingManager())).setToken( + IERC20(address(0xA11CE)) + ); + + // Create new funding manager module + FundingManagerV1Mock newFundingManager = new FundingManagerV1Mock(); + newFundingManager.setToken(IERC20(address(0xB0B))); + + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2 + .Orchestrator__MismatchedTokenForFundingManager + .selector, + orchestrator.fundingManager().token(), + newFundingManager.token() + ) + ); + orchestrator.initiateSetFundingManagerWithTimelock( + address(newFundingManager) + ); + } + + /* + Test: initiateSetPaymentProcessorWithTimelock Modifier Checks + └── Given: caller is not permissioned + └── When: initiateSetPaymentProcessorWithTimelock is called + └── Then: it should revert (modifier in position check) + */ + function testInitiateSetPaymentProcessorWithTimelock_ModifierInPositionChecks( + ) public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + + vm.prank(address(0xB0B)); + orchestrator.initiateSetPaymentProcessorWithTimelock(address(0)); + } + + /* + Test: executeSetPaymentProcessor Modifier Checks + └── Given: caller is not permissioned + └── When: executeSetPaymentProcessor is called + └── Then: it should revert (modifier in position check) + */ + function testExecuteSetPaymentProcessor_ModifierInPositionChecks() public { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.executeSetPaymentProcessor(address(0)); + } + + /* + Test: cancelPaymentProcessorUpdate Modifier Checks + └── Given: caller is not permissioned + └── When: cancelPaymentProcessorUpdate is called + └── Then: it should revert (modifier in position check) + */ + function testCancelPaymentProcessorUpdate_ModifierInPositionChecks() + public + { + // permissioned + + // Turn off all adresses are permissioned to call all functions + authorizer.setAllAuthorized(false); + vm.expectRevert(IOrchestrator_v2.Orchestrator__NotPermissioned.selector); + vm.prank(address(0xB0B)); + orchestrator.cancelPaymentProcessorUpdate(address(0)); + } + + function testInitiateSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v1( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v1 = address(new PaymentProcessor_v1_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v1) + ); + } + + function testInitiateSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v2( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v2 = address(new PaymentProcessor_v2_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v2) + ); + } + + function testInitiateSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v3( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v3 = address(new PaymentProcessor_v3_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v3) + ); + } + + function testExecuteSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v1( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v1 = address(new PaymentProcessor_v1_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v1) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetPaymentProcessor(address(newPaymentProcessor_v1)); + } + + function testExecuteSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v2( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v2 = address(new PaymentProcessor_v2_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v2) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetPaymentProcessor(address(newPaymentProcessor_v2)); + } + + function testExecuteSetpaymentProcessorWithTimelock_SupportsIPaymentProcessor_v3( + ) public { + address[] memory modules; + + // Initialize orchestrator. + orchestrator.init( + 1, + address(moduleFactory), + modules, + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor_v3 = address(new PaymentProcessor_v3_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + address(newPaymentProcessor_v3) + ); + + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + orchestrator.executeSetPaymentProcessor(address(newPaymentProcessor_v3)); + } + + function testInitiateAndExecuteSetPaymentProcessor( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor = address(new PaymentProcessor_v3_Mock()); + + orchestrator.initiateSetPaymentProcessorWithTimelock( + newPaymentProcessor + ); + vm.warp(block.timestamp + orchestrator.MODULE_UPDATE_TIMELOCK()); + + // set the new payment processor module + vm.expectEmit(true, true, true, true); + emit IOrchestrator_v2.PaymentProcessorUpdated(newPaymentProcessor); + orchestrator.executeSetPaymentProcessor(newPaymentProcessor); + assertTrue( + address(orchestrator.paymentProcessor()) == newPaymentProcessor + ); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testInitiateSetPaymentProcessorWithTimelock_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor = address(0x8888); + + // set the new payment processor module. First the verification function reverts, then the setter. + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newPaymentProcessor + ) + ); + orchestrator.initiateSetPaymentProcessorWithTimelock( + newPaymentProcessor + ); + + assertTrue(orchestrator.paymentProcessor() == paymentProcessor); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testExecuteSetPaymentProcessor_FailsIfWrongModuleType( + uint orchestratorId, + uint moduleAmount + ) public { + types.assumeValidOrchestratorId(orchestratorId); + // Initialize orchestrator. + orchestrator.init( + orchestratorId, + address(moduleFactory), + createModules(moduleAmount), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + // Create new payment processor module + address newPaymentProcessor = address(0x8888); + + // set the new payment processor module. First the verification function reverts, then the setter. + vm.expectRevert(); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + newPaymentProcessor + ) + ); + orchestrator.executeSetPaymentProcessor(newPaymentProcessor); + + assertTrue(orchestrator.paymentProcessor() == paymentProcessor); + } + + /* Test function initiateRemoveModuleWithTimelock + ├── Given the module address to be removed is the current authorizer + │ └── When the function initiateRemoveModuleWithTimelock() gets called + │ └── Then the function should revert + ├── Given the module address to be removed is the current funding manager + │ └── When the function initiateRemoveModuleWithTimelock() gets called + │ └── Then the function should revert + └── Given the module address to be removed is the current payment processor + └── When the function initiateRemoveModuleWithTimelock() gets called + └── Then the function should revert + */ + + function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentAuthorizer( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + address currentAuthorizer = address(orchestrator.authorizer()); + + vm.expectRevert( + IOrchestrator_v2.Orchestrator__InvalidRemovalOfAuthorizer.selector + ); + orchestrator.initiateRemoveModuleWithTimelock(currentAuthorizer); + } + + function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentFundingManager( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + address currentFundingManager = address(orchestrator.fundingManager()); + + vm.expectRevert( + IOrchestrator_v2 + .Orchestrator__InvalidRemovalOfFundingManager + .selector + ); + orchestrator.initiateRemoveModuleWithTimelock(currentFundingManager); + } + + function testInitiateRemoveModuleWithTimelock_failsGivenModuleAddressIsCurrentPaymentProcessor( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + address currentPaymentProcessor = + address(orchestrator.paymentProcessor()); + + vm.expectRevert( + IOrchestrator_v2 + .Orchestrator__InvalidRemovalOfPaymentProcessor + .selector + ); + orchestrator.initiateRemoveModuleWithTimelock(currentPaymentProcessor); + } + /* Test function executeRemoveModule + ├── Given the module address to be removed is the current authorizer + │ └── When the function executeRemoveModule() gets called + │ └── Then the function should revert + ├── Given the module address to be removed is the current funding manager + │ └── When the function executeRemoveModule() gets called + │ └── Then the function should revert + └── Given the module address to be removed is the current payment processor + └── When the function executeRemoveModule() gets called + └── Then the function should revert + */ + + function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentAuthorizer( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + + address currentAuthorizer = address(orchestrator.authorizer()); + + vm.expectRevert( + IOrchestrator_v2.Orchestrator__InvalidRemovalOfAuthorizer.selector + ); + orchestrator.executeRemoveModule(currentAuthorizer); + } + + function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentFundingManager( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + address currentFundingManager = address(orchestrator.fundingManager()); + + vm.expectRevert( + IOrchestrator_v2 + .Orchestrator__InvalidRemovalOfFundingManager + .selector + ); + orchestrator.executeRemoveModule(currentFundingManager); + } + + function testExecuteRemoveModule_failsGivenModuleAddressIsCurrentPaymentProcessor( + ) public { + orchestrator.init( + 1, + address(moduleFactory), + new address[](0), + fundingManager, + authorizer, + paymentProcessor, + governor + ); + address currentPaymentProcessor = + address(orchestrator.paymentProcessor()); + + vm.expectRevert( + IOrchestrator_v2 + .Orchestrator__InvalidRemovalOfPaymentProcessor + .selector + ); + orchestrator.executeRemoveModule(currentPaymentProcessor); + } + + // ======================================================================== + // Internal Functions + + // ------------------------------------------------------------------------ + // Internal - Enforce Module Interface Check + + function test_enforcePrivilegedModuleInterfaceCheck_failsIfContractIsNotAModule( + ) public { + // Create a mock that mocks ERC165 + ERC165InterfaceMock iMock = new ERC165InterfaceMock(); + address contractAddr = address(iMock); + + // if the contract address is not a module, it should revert + { + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforcePrivilegedModuleInterfaceCheck_exposed( + contractAddr, new bytes4[](0) + ); + } + } + + function test_enforcePrivilegedModuleInterfaceCheck_succeedsIfContractIsEitherModule_v1Or_v2( + ) public { + // Create a mock that mocks ERC165 + ERC165InterfaceMock iMock = new ERC165InterfaceMock(); + address contractAddr = address(iMock); + + // We need to check for a single interface for the call to be successful + bytes4[] memory privilegedInterfaceIds = new bytes4[](1); + privilegedInterfaceIds[0] = bytes4(0); + iMock.registerInterface(bytes4(0)); + + // if the contract address implements IModule_v1 it should not revert + { + iMock.registerInterface(type(IModule_v1).interfaceId); + orchestrator._enforcePrivilegedModuleInterfaceCheck_exposed( + contractAddr, privilegedInterfaceIds + ); + iMock.unregisterInterface(type(IModule_v1).interfaceId); + } + // if the contract address implements IModule_v2 it should not revert + { + iMock.registerInterface(type(IModule_v2).interfaceId); + orchestrator._enforcePrivilegedModuleInterfaceCheck_exposed( + contractAddr, privilegedInterfaceIds + ); + } + } + + function test_enforceNonPrivilegedModuleInterfaceCheck_failsGivenModuleIsPrivileged( + ) public { + // Create a mock that mocks ERC165 + ERC165InterfaceMock iMock = new ERC165InterfaceMock(); + address contractAddr = address(iMock); + + // if the contract address is not a module, it should revert + { + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + } + + // If the contract address is Module_v1, it should not revert + { + iMock.registerInterface(type(IModule_v1).interfaceId); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IModule_v1).interfaceId); + } + + // If the contract address is Module_v2, it should not revert + { + iMock.registerInterface(type(IModule_v2).interfaceId); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IModule_v2).interfaceId); + } + + // All the following tests will revert automatically if the contract address is not a module + iMock.registerInterface(type(IModule_v2).interfaceId); + + // If the contract address is IAuthorizer_v1, it should revert + { + iMock.registerInterface(type(IAuthorizer_v1).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IAuthorizer_v1).interfaceId); + } + + // If the contract address is IAuthorizer_v2, it should revert + { + iMock.registerInterface(type(IAuthorizer_v2).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IAuthorizer_v2).interfaceId); + } + + // If the contract address is IFundingManager_v1, it should revert + { + iMock.registerInterface(type(IFundingManager_v1).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IFundingManager_v1).interfaceId); + } + + // If the contract address is IPaymentProcessor_v1, it should revert + { + iMock.registerInterface(type(IPaymentProcessor_v1).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IPaymentProcessor_v1).interfaceId); + } + + // If the contract address is IPaymentProcessor_v2, it should revert + { + iMock.registerInterface(type(IPaymentProcessor_v2).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + iMock.unregisterInterface(type(IPaymentProcessor_v2).interfaceId); + } + + // If the contract address is IPaymentProcessor_v3, it should revert + { + iMock.registerInterface(type(IPaymentProcessor_v3).interfaceId); + vm.expectRevert( + abi.encodeWithSelector( + IOrchestrator_v2.Orchestrator__InvalidModuleType.selector, + contractAddr + ) + ); + orchestrator._enforceNonPrivilegedModuleInterfaceCheck_exposed( + contractAddr + ); + } + } + + // ------------------------------------------------------------------------ + // Internal - Authorization + + /* + Test: _checkAuthorization_ + └── Given: Authorizer hasPermission() is mocked + ├── When: _checkAuthorization_ is called + └── And: Authorizer hasPermission() returns false + ├── Then: It should forward the function selector properly + └── And: The function should revert + */ + function test_checkAuthorization_hasPermissionMocked( + bool hasPermission_, + address caller_, + bytes calldata data_ + ) public { + vm.assume(data_.length >= 4); + // Assume that caller is not the module as it is the default admin + vm.assume(caller_ != address(this)); + + // Turn off that every caller has permission for every permissioned function + authorizer.setAllAuthorized(false); + + authorizer.setHasPermission( + caller_, address(orchestrator), bytes4(data_[0:4]), hasPermission_ + ); + + if (!hasPermission_) { + vm.expectRevert( + IOrchestrator_v2.Orchestrator__NotPermissioned.selector + ); + } + + orchestrator._checkAuthorization_exposed(caller_, data_); + } + + //-------------------------------------------------------------------------- + // Helper Functions + + function createModules(uint amount) + internal + returns (address[] memory modules) + { + if (amount > 50) { + amount = 50; + } + + modules = new address[](amount); + for (uint i = 0; i < amount; i++) { + modules[i] = address(new Module_v2_Mock()); + } + } +} diff --git a/test/unit/orchestrator/abstracts/ModuleManagerBase_v1.t.sol b/test/unit/orchestrator/abstracts/ModuleManagerBase_v1.t.sol index 3283808cb..06d6841f0 100644 --- a/test/unit/orchestrator/abstracts/ModuleManagerBase_v1.t.sol +++ b/test/unit/orchestrator/abstracts/ModuleManagerBase_v1.t.sol @@ -10,8 +10,9 @@ import { } from "@mocks/orchestrator/abstracts/ModuleManagerBaseV1Mock.sol"; // Mocks -import {AuthorizerV1Mock} from "@mocks/modules/authorizer/AuthorizerV1Mock.sol"; -import {ModuleV1Mock} from "@mocks/modules/base/ModuleV1Mock.sol"; +import {Authorizer_v2_Mock} from + "@mocks/modules/authorizer/Authorizer_v2_Mock.sol"; +import {Module_v2_Mock} from "@mocks/modules/base/Module_v2_Mock.sol"; // Errors import {OZErrors} from "@testUtilities/OZErrors.sol"; @@ -117,7 +118,7 @@ contract ModuleManagerBaseV1Test is Test { moduleManager = new ModuleManagerBaseV1Mock(address(0)); types = new TypeSanityHelper(address(moduleManager)); - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); address[] memory modules = new address[](2); modules[0] = module; @@ -205,7 +206,7 @@ contract ModuleManagerBaseV1Test is Test { { vm.assume(timePassed < timelock - 1); - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); moduleManager.call_initiateAddModuleWithTimelock(module); (, uint timelockUntil) = moduleManager.moduleAddressToTimelock(module); @@ -226,7 +227,7 @@ contract ModuleManagerBaseV1Test is Test { } function testExecuteAddModule_revertGivenTimelockStillActive() public { - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); moduleManager.call_initiateAddModuleWithTimelock(module); // Cancel setting module @@ -242,42 +243,13 @@ contract ModuleManagerBaseV1Test is Test { moduleManager.call_executeAddModule(module); } - function testInitiateAddModuleWithTimelock_FailsIfCallerNotAuthorized() - public - { - address module = address(new ModuleV1Mock()); - - moduleManager.__ModuleManager_setIsAuthorized(address(this), false); - - vm.expectRevert( - IModuleManagerBase_v1 - .ModuleManagerBase__CallerNotAuthorized - .selector - ); - moduleManager.call_initiateAddModuleWithTimelock(module); - } - - function testExecuteAddModule_FailsIfCallerNotAuthorized() public { - address module = address(new ModuleV1Mock()); - moduleManager.call_initiateAddModuleWithTimelock(module); - - moduleManager.__ModuleManager_setIsAuthorized(address(this), false); - - vm.expectRevert( - IModuleManagerBase_v1 - .ModuleManagerBase__CallerNotAuthorized - .selector - ); - moduleManager.call_executeAddModule(module); - } - function testExecuteAddModule_FailsIfModuleLimitIsExceeded() public { uint modulesUntilLimit = MAX_MODULES - moduleManager.modulesSize(); address[] memory modules = new address[](modulesUntilLimit + 1); // Create MAX_MODULES amount of modules + 1 for (uint i = 0; i < modulesUntilLimit + 1; i++) { - modules[i] = address(new ModuleV1Mock()); + modules[i] = address(new Module_v2_Mock()); moduleManager.call_initiateAddModuleWithTimelock(modules[i]); } @@ -325,7 +297,7 @@ contract ModuleManagerBaseV1Test is Test { } function testInitiateAddModuleWithTimelock_FailsIfAlreadyAdded() public { - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); moduleManager.call_initiateAddModuleWithTimelock(module); vm.warp(block.timestamp + timelock); @@ -360,7 +332,7 @@ contract ModuleManagerBaseV1Test is Test { ) public { vm.assume(timePassed < timelock - 1); - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); // Setup add module moduleManager.call_initiateAddModuleWithTimelock(module); @@ -388,7 +360,7 @@ contract ModuleManagerBaseV1Test is Test { } function testExecuteRemoveModule_revertGivenTimelockStillActive() public { - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); // Setup add module moduleManager.call_initiateAddModuleWithTimelock(module); vm.warp(block.timestamp + timelock); @@ -464,44 +436,8 @@ contract ModuleManagerBaseV1Test is Test { assertEq(moduleManager.listModules().length, 0); } - function testInitiateRemoveModuleWithTimelock_FailsIfCallerNotAuthorized() - public - { - address module = address(new ModuleV1Mock()); - - moduleManager.call_initiateAddModuleWithTimelock(module); - vm.warp(block.timestamp + timelock); - moduleManager.call_executeAddModule(module); - - moduleManager.__ModuleManager_setIsAuthorized(address(this), false); - - vm.expectRevert( - IModuleManagerBase_v1 - .ModuleManagerBase__CallerNotAuthorized - .selector - ); - moduleManager.call_initiateRemoveModuleWithTimelock(module); - } - - function testExecuteRemoveModule_FailsIfCallerNotAuthorized() public { - address module = address(new ModuleV1Mock()); - - moduleManager.call_initiateAddModuleWithTimelock(module); - vm.warp(block.timestamp + timelock); - moduleManager.call_executeAddModule(module); - - moduleManager.__ModuleManager_setIsAuthorized(address(this), false); - - vm.expectRevert( - IModuleManagerBase_v1 - .ModuleManagerBase__CallerNotAuthorized - .selector - ); - moduleManager.call_initiateRemoveModuleWithTimelock(module); - } - function testInitiateRemoveModuleWithTimelock_FailsIfNotModule() public { - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); vm.expectRevert( IModuleManagerBase_v1.ModuleManagerBase__IsNotModule.selector @@ -513,33 +449,19 @@ contract ModuleManagerBaseV1Test is Test { // Tests: cancelModuleUpdate() /* Test cancelModuleUpdate() function - ├── Given the caller of the function is not authorized - │ └── When the function cancelModuleUpdate() gets called - │ └── Then it should revert ├── Given no update has been initated for the module │ └── When the function cancelModuleUpdate() gets called │ └── Then it should revert - └── Given caller is authorized & module update has been initiated + └── Given module update has been initiated └── When the function cancelModuleUpdate() gets called └── Then it should cancel the update └── And it should emit an event */ - function testCancelModuleUpdate_failsGivenCallerNotAuthorized() public { - address module = address(new ModuleV1Mock()); - moduleManager.__ModuleManager_setIsAuthorized(address(this), false); - - vm.expectRevert( - IModuleManagerBase_v1 - .ModuleManagerBase__CallerNotAuthorized - .selector - ); - moduleManager.call_cancelModuleUpdate(module); - } function testCancelModuleUpdate_failsGivenModuleUpdateNotInitated() public { - address module = address(new ModuleV1Mock()); + address module = address(new Module_v2_Mock()); vm.expectRevert( IModuleManagerBase_v1 .ModuleManagerBase__ModuleUpdateAlreadyStarted @@ -594,7 +516,7 @@ contract ModuleManagerBaseV1Test is Test { modules = new address[](amount); for (uint i = 0; i < amount; i++) { - modules[i] = address(new ModuleV1Mock()); + modules[i] = address(new Module_v2_Mock()); } } } diff --git a/test/unit/proxies/InverterProxyAdmin.sol b/test/unit/proxies/InverterProxyAdmin.t.sol similarity index 100% rename from test/unit/proxies/InverterProxyAdmin.sol rename to test/unit/proxies/InverterProxyAdmin.t.sol