diff --git a/README.md b/README.md index eb3f668c..244bf459 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -
- Smart contracts built for the MetaDock platform. + Smart contracts built for the Werk platform.
- - + + - - + + @@ -20,23 +20,23 @@
-MetaDock aims to offer a decentralised platform for freelancers and small companies to run all their operations -leveraging the power of web3 as an organisation. +Werk aims to offer a decentralised platform for freelancers and small companies to run all their operations leveraging +the power of web3 as an organisation. ## Architecture Multiple Containers can be deployed, allowing ERC-20 and native tokens (ETH) to be deposited, acting as a user vault. At -the same time, a Container can execute arbitrary code on an unlimited number of Modules. This ability enhances the -MetaDock protocol with a modular architecture, providing an opportunity to create an open market of Modules where -external players can create and integrate their own use-cases into the protocol. +the same time, a Container can execute arbitrary code on an unlimited number of Modules. This ability enhances the Werk +protocol with a modular architecture, providing an opportunity to create an open market of Modules where external +players can create and integrate their own use-cases into the protocol. -A module must first be allowlisted through the `ModuleKeeper` before being made publicly available to MetaDock users. -Currently, due to the high-security risks, only the MetaDock team can add modules to or remove them from the allowlist. +A module must first be allowlisted through the `ModuleKeeper` before being made publicly available to Werk users. +Currently, due to the high-security risks, only the Werk team can add modules to or remove them from the allowlist. Once a module is allowlisted, it can be enabled via the `enableModule()` method available on any `Container`. @@ -54,7 +54,7 @@ creation and management. ## Contribute Anyone is welcomed to contribute either by creating & proposing a new module or simply -[opening](https://github.com/metadock/contracts/issues/new) an issue, starting a discussion or submitting a PR. +[opening](https://github.com/werk/contracts/issues/new) an issue, starting a discussion or submitting a PR. If you want to propose a new module, fork this repository and create a new folder inside the `src/modules/[module-name]` folder following the `src/modules/invoice-module` module structure. Then, create a new PR with the module logic and a @@ -62,5 +62,5 @@ detailed description of its capabilities. ## License -The primary license for MetaDock contracts is the GNU General Public License v3.0 (`GPL-3.0-or-later`), -see [LICENSE](https://github.com/metadock/contracts//blob/main/LICENSE). However, many files in `test/` are unlicensed. +The primary license for Werk contracts is the GNU General Public License v3.0 (`GPL-3.0-or-later`), +see [LICENSE](https://github.com/werk/contracts//blob/main/LICENSE). However, many files in `test/` are unlicensed. diff --git a/docs/images/metadock_banner.jpeg b/docs/images/metadock_banner.jpeg deleted file mode 100644 index 5455034c..00000000 Binary files a/docs/images/metadock_banner.jpeg and /dev/null differ diff --git a/docs/images/werk_banner.png b/docs/images/werk_banner.png new file mode 100644 index 00000000..4c7ac0b8 Binary files /dev/null and b/docs/images/werk_banner.png differ diff --git a/script/DeployContainer.s.sol b/script/DeployContainer.s.sol deleted file mode 100644 index 96fbcce0..00000000 --- a/script/DeployContainer.s.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.26; - -import { BaseScript } from "./Base.s.sol"; -import { Container } from "../src/Container.sol"; -import { DockRegistry } from "./../src/DockRegistry.sol"; - -/// @notice Deploys an instance of {Container} and enables initial module(s) -contract DeployContainer is BaseScript { - function run( - address initialAdmin, - DockRegistry dockRegistry, - uint256 dockId, - address[] memory initialModules - ) public virtual broadcast returns (Container container) { - // Get the number of total accounts created by the `initialAdmin` deployer - uint256 totalAccountsOfAdmin = dockRegistry.totalAccountsOfSigner(initialAdmin); - - // Construct the ABI-encoded data to be passed to the `createAccount` method - bytes memory data = abi.encode(totalAccountsOfAdmin, dockId, initialModules); - - // Deploy a new {Container} smart account through the {DockRegistry} account factory - container = Container(payable(dockRegistry.createAccount(initialAdmin, data))); - } -} diff --git a/script/DeployDeterministicDockRegistry.s.sol b/script/DeployDeterministicStationRegistry.s.sol similarity index 66% rename from script/DeployDeterministicDockRegistry.s.sol rename to script/DeployDeterministicStationRegistry.s.sol index 9535b0ff..25deec1d 100644 --- a/script/DeployDeterministicDockRegistry.s.sol +++ b/script/DeployDeterministicStationRegistry.s.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.26; import { BaseScript } from "./Base.s.sol"; -import { DockRegistry } from "./../src/DockRegistry.sol"; +import { StationRegistry } from "./../src/StationRegistry.sol"; import { ModuleKeeper } from "./../src/ModuleKeeper.sol"; import { EntryPoint } from "@thirdweb/contracts/prebuilts/account/utils/Entrypoint.sol"; -/// @notice Deploys at deterministic addresses across chains an instance of {DockRegistry} +/// @notice Deploys at deterministic addresses across chains an instance of {StationRegistry} /// @dev Reverts if any contract has already been deployed -contract DeployDeterministicDockRegistry is BaseScript { +contract DeployDeterministicStationRegistry is BaseScript { /// @dev By using a salt, Forge will deploy the contract via a deterministic CREATE2 factory /// https://book.getfoundry.sh/tutorials/create2-tutorial?highlight=deter#deterministic-deployment-using-create2 function run( @@ -16,10 +16,10 @@ contract DeployDeterministicDockRegistry is BaseScript { address initialAdmin, EntryPoint entrypoint, ModuleKeeper moduleKeeper - ) public virtual broadcast returns (DockRegistry dockRegistry) { + ) public virtual broadcast returns (StationRegistry stationRegistry) { bytes32 salt = bytes32(abi.encodePacked(create2Salt)); - // Deterministically deploy the {DockRegistry} smart account factory - dockRegistry = new DockRegistry{ salt: salt }(initialAdmin, entrypoint, moduleKeeper); + // Deterministically deploy the {StationRegistry} smart account factory + stationRegistry = new StationRegistry{ salt: salt }(initialAdmin, entrypoint, moduleKeeper); } } diff --git a/script/DeploySpace.sol b/script/DeploySpace.sol new file mode 100644 index 00000000..80a3cc8f --- /dev/null +++ b/script/DeploySpace.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.26; + +import { BaseScript } from "./Base.s.sol"; +import { Space } from "../src/Space.sol"; +import { StationRegistry } from "./../src/StationRegistry.sol"; + +/// @notice Deploys an instance of {Space} and enables initial module(s) +contract DeploySpace is BaseScript { + function run( + address initialAdmin, + StationRegistry stationRegistry, + uint256 stationId, + address[] memory initialModules + ) public virtual broadcast returns (Space space) { + // Get the number of total accounts created by the `initialAdmin` deployer + uint256 totalAccountsOfAdmin = stationRegistry.totalAccountsOfSigner(initialAdmin); + + // Construct the ABI-encoded data to be passed to the `createAccount` method + bytes memory data = abi.encode(totalAccountsOfAdmin, stationId, initialModules); + + // Deploy a new {Space} smart account through the {StationRegistry} account factory + space = Space(payable(stationRegistry.createAccount(initialAdmin, data))); + } +} diff --git a/src/DockRegistry.sol b/src/DockRegistry.sol deleted file mode 100644 index b386fe2e..00000000 --- a/src/DockRegistry.sol +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.26; - -import { BaseAccountFactory } from "@thirdweb/contracts/prebuilts/account/utils/BaseAccountFactory.sol"; -import { IEntryPoint } from "@thirdweb/contracts/prebuilts/account/interface/IEntrypoint.sol"; -import { PermissionsEnumerable } from "@thirdweb/contracts/extension/PermissionsEnumerable.sol"; -import { EnumerableSet } from "@thirdweb/contracts/external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; - -import { IDockRegistry } from "./interfaces/IDockRegistry.sol"; -import { Container } from "./Container.sol"; -import { ModuleKeeper } from "./ModuleKeeper.sol"; -import { Errors } from "./libraries/Errors.sol"; - -/// @title DockRegistry -/// @notice See the documentation in {IDockRegistry} - -contract DockRegistry is IDockRegistry, BaseAccountFactory, PermissionsEnumerable { - using EnumerableSet for EnumerableSet.AddressSet; - - /*////////////////////////////////////////////////////////////////////////// - STORAGE - //////////////////////////////////////////////////////////////////////////*/ - - /// @inheritdoc IDockRegistry - ModuleKeeper public override moduleKeeper; - - /// @inheritdoc IDockRegistry - mapping(uint256 dockId => address owner) public override ownerOfDock; - - /// @inheritdoc IDockRegistry - mapping(address container => uint256 dockId) public override dockIdOfContainer; - - /// @inheritdoc IDockRegistry - mapping(address container => address owner) public override ownerOfContainer; - - /// @dev Counter to keep track of the next dock ID - uint256 private _dockNextId; - - /*////////////////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Initializes the {Container} implementation, the Entrypoint, registry admin and sets first dock ID to 1 - constructor( - address _initialAdmin, - IEntryPoint _entrypoint, - ModuleKeeper _moduleKeeper - ) BaseAccountFactory(address(new Container(_entrypoint, address(this))), address(_entrypoint)) { - _setupRole(DEFAULT_ADMIN_ROLE, _initialAdmin); - - _dockNextId = 1; - moduleKeeper = _moduleKeeper; - } - - /*////////////////////////////////////////////////////////////////////////// - NON-CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - function createAccount(address _admin, bytes calldata _data) public override returns (address) { - // Get the dock ID and initial modules array from the calldata - // Note: calldata contains a salt (usually the number of accounts created by an admin), - // dock ID and an array with the initial enabled modules on the account - (, uint256 dockId, address[] memory initialModules) = abi.decode(_data, (uint256, uint256, address[])); - - // Checks: a new dock must be created first - if (dockId == 0) { - // Store the ID of the next dock - dockId = _dockNextId; - - // Effects: set the owner of the freshly created dock - ownerOfDock[dockId] = msg.sender; - - // Effects: increment the next dock ID - // Use unchecked because the dock ID cannot realistically overflow - unchecked { - _dockNextId++; - } - } else { - // Checks: `msg.sender` is the dock owner - if (ownerOfDock[dockId] != msg.sender) { - revert Errors.CallerNotDockOwner(); - } - } - - // Interactions: deploy a new {Container} smart account - address container = super.createAccount(_admin, _data); - - // Assign the ID of the dock to which the new container belongs - dockIdOfContainer[container] = dockId; - - // Assign the owner of the container - ownerOfContainer[container] = _admin; - - // Log the {Container} creation - emit ContainerCreated(_admin, dockId, container, initialModules); - - // Return {Container} smart account address - return container; - } - - /// @inheritdoc IDockRegistry - function transferContainerOwnership(address container, address newOwner) external { - // Checks: `msg.sender` is the current owner of the {Container} - address currentOwner = ownerOfContainer[container]; - if (msg.sender != currentOwner) { - revert Errors.CallerNotContainerOwner(); - } - - // Checks: the new owner is not the zero address - if (newOwner == address(0)) { - revert Errors.InvalidOwnerZeroAddress(); - } - - // Effects: update container's ownership - ownerOfContainer[container] = newOwner; - - // Log the ownership transfer - emit ContainerOwnershipTransferred({ container: container, oldOwner: currentOwner, newOwner: newOwner }); - } - - /// @inheritdoc IDockRegistry - function transferDockOwnership(uint256 dockId, address newOwner) external { - // Checks: `msg.sender` is the current owner of the dock - address currentOwner = ownerOfDock[dockId]; - if (msg.sender != currentOwner) { - revert Errors.CallerNotDockOwner(); - } - - // Effects: update dock's ownership - ownerOfDock[dockId] = newOwner; - - // Log the ownership transfer - emit DockOwnershipTransferred({ dockId: dockId, oldOwner: currentOwner, newOwner: newOwner }); - } - - /// @inheritdoc IDockRegistry - function updateModuleKeeper(ModuleKeeper newModuleKeeper) external onlyRole(DEFAULT_ADMIN_ROLE) { - // Effects: update the {ModuleKeeper} address - moduleKeeper = newModuleKeeper; - - // Log the update - emit ModuleKeeperUpdated(newModuleKeeper); - } - - /*////////////////////////////////////////////////////////////////////////// - CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Returns the total number of accounts created by the `signer` address - function totalAccountsOfSigner(address signer) public view returns (uint256) { - return accountsOfSigner[signer].length(); - } - - /*////////////////////////////////////////////////////////////////////////// - INTERNAL FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { - Container(payable(_account)).initialize(_admin, _data); - } -} diff --git a/src/Container.sol b/src/Space.sol similarity index 90% rename from src/Container.sol rename to src/Space.sol index 0b4ee9ee..ca5c1b1f 100644 --- a/src/Container.sol +++ b/src/Space.sol @@ -16,16 +16,16 @@ import { ERC1271 } from "@thirdweb/contracts/eip/ERC1271.sol"; import { EnumerableSet } from "@thirdweb/contracts/external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; import { AccountCoreStorage } from "@thirdweb/contracts/prebuilts/account/utils/AccountCoreStorage.sol"; -import { IContainer } from "./interfaces/IContainer.sol"; +import { ISpace } from "./interfaces/ISpace.sol"; import { ModuleManager } from "./abstracts/ModuleManager.sol"; import { IModuleManager } from "./interfaces/IModuleManager.sol"; import { Errors } from "./libraries/Errors.sol"; -import { DockRegistry } from "./DockRegistry.sol"; +import { StationRegistry } from "./StationRegistry.sol"; import { ModuleKeeper } from "./ModuleKeeper.sol"; -/// @title Container -/// @notice See the documentation in {IContainer} -contract Container is IContainer, AccountCore, ERC1271, ModuleManager { +/// @title Space +/// @notice See the documentation in {ISpace} +contract Space is ISpace, AccountCore, ERC1271, ModuleManager { using ECDSA for bytes32; using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; @@ -39,15 +39,15 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { /// @dev Initializes the address of the EIP 4337 factory and EntryPoint contract constructor(IEntryPoint _entrypoint, address _factory) AccountCore(_entrypoint, _factory) { } - /// @notice Initializes the {ModuleKeeper}, enables initial modules and configures the {Container} smart account + /// @notice Initializes the {ModuleKeeper}, enables initial modules and configures the {Space} smart account function initialize(address _defaultAdmin, bytes calldata _data) public override { (,, address[] memory initialModules) = abi.decode(_data, (uint256, uint256, address[])); // Enable the initial module(s) - ModuleKeeper moduleKeeper = DockRegistry(factory).moduleKeeper(); + ModuleKeeper moduleKeeper = StationRegistry(factory).moduleKeeper(); _initializeModuleManager(moduleKeeper, initialModules); - // Initialize the {Container} smart contract + // Initialize the {Space} smart contract super.initialize(_defaultAdmin, _data); _registerOnFactory(); @@ -57,7 +57,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { RECEIVE & FALLBACK //////////////////////////////////////////////////////////////////////////*/ - /// @dev Allow container to receive native token (ETH) + /// @dev Allow space to receive native token (ETH) receive() external payable { // Log the successful native token deposit emit NativeReceived({ from: msg.sender, amount: msg.value }); @@ -82,7 +82,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc IContainer + /// @inheritdoc ISpace function execute( address module, uint256 value, @@ -95,7 +95,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { success = _call(module, value, data); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function executeBatch( address[] calldata modules, uint256[] calldata values, @@ -117,9 +117,9 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { } } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC20(IERC20 asset, uint256 amount) public onlyAdminOrEntrypoint { - // Checks: the available ERC20 balance of the container is greater enough to support the withdrawal + // Checks: the available ERC20 balance of the space is greater enough to support the withdrawal if (amount > asset.balanceOf(address(this))) revert Errors.InsufficientERC20ToWithdraw(); // Interactions: withdraw by transferring the amount to the sender @@ -129,9 +129,9 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { emit AssetWithdrawn({ to: msg.sender, asset: address(asset), amount: amount }); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC721(IERC721 collection, uint256 tokenId) public onlyAdminOrEntrypoint { - // Checks, Effects, Interactions: withdraw by transferring the token to the container owner + // Checks, Effects, Interactions: withdraw by transferring the token to the space owner // Notes: // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account // therefore the `onERC721Received` hook must be implemented @@ -141,13 +141,13 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { emit ERC721Withdrawn({ to: msg.sender, collection: address(collection), tokenId: tokenId }); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC1155( IERC1155 collection, uint256[] memory ids, uint256[] memory amounts ) public onlyAdminOrEntrypoint { - // Checks, Effects, Interactions: withdraw by transferring the tokens to the container owner + // Checks, Effects, Interactions: withdraw by transferring the tokens to the space owner // Notes: // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account // therefore the `onERC1155Received` hook must be implemented @@ -162,9 +162,9 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { emit ERC1155Withdrawn(msg.sender, address(collection), ids, amounts); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawNative(uint256 amount) public onlyAdminOrEntrypoint { - // Checks: the native balance of the container minus the amount locked for operations is greater than the requested amount + // Checks: the native balance of the space minus the amount locked for operations is greater than the requested amount if (amount > address(this).balance) revert Errors.InsufficientNativeToWithdraw(); // Interactions: withdraw by transferring the amount to the sender @@ -179,7 +179,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { /// @inheritdoc IModuleManager function enableModule(address module) public override onlyAdminOrEntrypoint { // Retrieve the address of the {ModuleKeeper} - ModuleKeeper moduleKeeper = DockRegistry(factory).moduleKeeper(); + ModuleKeeper moduleKeeper = StationRegistry(factory).moduleKeeper(); // Checks, Effects: enable the module _enableModule(moduleKeeper, module); @@ -223,7 +223,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { } } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function getMessageHash(bytes32 _hash) public view returns (bytes32) { bytes32 messageHash = keccak256(abi.encode(_hash)); bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash)); @@ -232,7 +232,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) public pure returns (bool) { - return interfaceId == type(IContainer).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId + return interfaceId == type(ISpace).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC165).interfaceId; } @@ -292,7 +292,7 @@ contract Container is IContainer, AccountCore, ERC1271, ModuleManager { /// @dev Registers the account on the factory if it hasn't been registered yet function _registerOnFactory() internal { // Get the address of the factory contract - DockRegistry factoryContract = DockRegistry(factory); + StationRegistry factoryContract = StationRegistry(factory); // Checks: the smart account is registered on the factory contract if (!factoryContract.isRegistered(address(this))) { diff --git a/src/StationRegistry.sol b/src/StationRegistry.sol new file mode 100644 index 00000000..d7315d44 --- /dev/null +++ b/src/StationRegistry.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.26; + +import { BaseAccountFactory } from "@thirdweb/contracts/prebuilts/account/utils/BaseAccountFactory.sol"; +import { IEntryPoint } from "@thirdweb/contracts/prebuilts/account/interface/IEntrypoint.sol"; +import { PermissionsEnumerable } from "@thirdweb/contracts/extension/PermissionsEnumerable.sol"; +import { EnumerableSet } from "@thirdweb/contracts/external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; + +import { IStationRegistry } from "./interfaces/IStationRegistry.sol"; +import { Space } from "./Space.sol"; +import { ModuleKeeper } from "./ModuleKeeper.sol"; +import { Errors } from "./libraries/Errors.sol"; + +/// @title StationRegistry +/// @notice See the documentation in {IStationRegistry} +contract StationRegistry is IStationRegistry, BaseAccountFactory, PermissionsEnumerable { + using EnumerableSet for EnumerableSet.AddressSet; + + /*////////////////////////////////////////////////////////////////////////// + STORAGE + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc IStationRegistry + ModuleKeeper public override moduleKeeper; + + /// @inheritdoc IStationRegistry + mapping(uint256 stationId => address owner) public override ownerOfStation; + + /// @inheritdoc IStationRegistry + mapping(address space => uint256 stationId) public override stationIdOfSpace; + + /// @dev Counter to keep track of the next station ID + uint256 private _stationNextId; + + /*////////////////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Initializes the {Space} implementation, the Entrypoint, registry admin and sets first station ID to 1 + constructor( + address _initialAdmin, + IEntryPoint _entrypoint, + ModuleKeeper _moduleKeeper + ) BaseAccountFactory(address(new Space(_entrypoint, address(this))), address(_entrypoint)) { + _setupRole(DEFAULT_ADMIN_ROLE, _initialAdmin); + + _stationNextId = 1; + moduleKeeper = _moduleKeeper; + } + + /*////////////////////////////////////////////////////////////////////////// + NON-CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc IStationRegistry + function createAccount( + address _admin, + bytes calldata _data + ) public override(BaseAccountFactory, IStationRegistry) returns (address) { + // Get the station ID and initial modules array from the calldata + // Note: calldata contains a salt (usually the number of accounts created by an admin), + // station ID and an array with the initial enabled modules on the account + (, uint256 stationId, address[] memory initialModules) = abi.decode(_data, (uint256, uint256, address[])); + + // Checks: a new station must be created first + if (stationId == 0) { + // Store the ID of the next station + stationId = _stationNextId; + + // Effects: set the owner of the freshly created station + ownerOfStation[stationId] = msg.sender; + + // Effects: increment the next station ID + // Use unchecked because the station ID cannot realistically overflow + unchecked { + _stationNextId++; + } + } else { + // Checks: `msg.sender` is the station owner + if (ownerOfStation[stationId] != msg.sender) { + revert Errors.CallerNotStationOwner(); + } + } + + // Interactions: deploy a new {Space} smart account + address space = super.createAccount(_admin, _data); + + // Assign the ID of the station to which the new space belongs + stationIdOfSpace[space] = stationId; + + // Log the {Space} creation + emit SpaceCreated(_admin, stationId, space, initialModules); + + // Return {Space} smart account address + return space; + } + + /// @inheritdoc IStationRegistry + function transferStationOwnership(uint256 stationId, address newOwner) external { + // Checks: `msg.sender` is the current owner of the station + address currentOwner = ownerOfStation[stationId]; + if (msg.sender != currentOwner) { + revert Errors.CallerNotStationOwner(); + } + + // Effects: update station's ownership + ownerOfStation[stationId] = newOwner; + + // Log the ownership transfer + emit StationOwnershipTransferred({ stationId: stationId, oldOwner: currentOwner, newOwner: newOwner }); + } + + /// @inheritdoc IStationRegistry + function updateModuleKeeper(ModuleKeeper newModuleKeeper) external onlyRole(DEFAULT_ADMIN_ROLE) { + // Effects: update the {ModuleKeeper} address + moduleKeeper = newModuleKeeper; + + // Log the update + emit ModuleKeeperUpdated(newModuleKeeper); + } + + /*////////////////////////////////////////////////////////////////////////// + CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc IStationRegistry + function totalAccountsOfSigner(address signer) public view returns (uint256) { + return accountsOfSigner[signer].length(); + } + + /*////////////////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. + function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { + Space(payable(_account)).initialize(_admin, _data); + } +} diff --git a/src/abstracts/ModuleManager.sol b/src/abstracts/ModuleManager.sol index e0f56617..9eaac859 100644 --- a/src/abstracts/ModuleManager.sol +++ b/src/abstracts/ModuleManager.sol @@ -19,7 +19,7 @@ abstract contract ModuleManager is IModuleManager { INITIALIZER //////////////////////////////////////////////////////////////////////////*/ - /// @dev Initializes the initial module(s) enabled on the container + /// @dev Initializes the initial module(s) enabled on the space function _initializeModuleManager(ModuleKeeper moduleKeeper, address[] memory _initialModules) internal { _enableBatchModules(moduleKeeper, _initialModules); } @@ -38,7 +38,7 @@ abstract contract ModuleManager is IModuleManager { INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Reverts if the `module` module is not enabled on the container + /// @dev Reverts if the `module` module is not enabled on the space function _checkIfModuleIsEnabled(address module) internal view { if (!isModuleEnabled[module]) { revert Errors.ModuleNotEnabled(module); diff --git a/src/interfaces/IDockRegistry.sol b/src/interfaces/IDockRegistry.sol deleted file mode 100644 index e1cbde32..00000000 --- a/src/interfaces/IDockRegistry.sol +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.26; - -import { Container } from "./../Container.sol"; -import { IModuleKeeper } from "./IModuleKeeper.sol"; -import { ModuleKeeper } from "./../ModuleKeeper.sol"; - -/// @title IDockRegistry -/// @notice Contract that provides functionalities to create docks and deploy {Container}s from a single place -interface IDockRegistry { - /*////////////////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Emitted when a new {Container} contract gets deployed - /// @param owner The address of the owner - /// @param dockId The ID of the dock to which this {Container} belongs - /// @param container The address of the {Container} - /// @param initialModules Array of initially enabled modules - event ContainerCreated(address indexed owner, uint256 indexed dockId, address container, address[] initialModules); - - /// @notice Emitted when the ownership of a {Container} is transferred to a new owner - /// @param container The address of the {Container} - /// @param oldOwner The address of the current owner - /// @param newOwner The address of the new owner - event ContainerOwnershipTransferred(address indexed container, address oldOwner, address newOwner); - - /// @notice Emitted when the ownership of a {Dock} is transferred to a new owner - /// @param dockId The address of the {Dock} - /// @param oldOwner The address of the current owner - /// @param newOwner The address of the new owner - event DockOwnershipTransferred(uint256 indexed dockId, address oldOwner, address newOwner); - - /// @notice Emitted when the {ModuleKeeper} address is updated - /// @param newModuleKeeper The new address of the {ModuleKeeper} - event ModuleKeeperUpdated(IModuleKeeper newModuleKeeper); - - /*////////////////////////////////////////////////////////////////////////// - CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Returns the address of the {ModuleKeeper} contract - function moduleKeeper() external view returns (ModuleKeeper); - - /// @notice Retrieves the owner of the given dock ID - function ownerOfDock(uint256 dockId) external view returns (address); - - /// @notice Retrieves the dock ID of the given container address - function dockIdOfContainer(address container) external view returns (uint256); - - /// @notice Retrieves the owner address of the {Container}'s address - function ownerOfContainer(address container) external view returns (address); - - /*////////////////////////////////////////////////////////////////////////// - NON-CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Creates a new {Container} contract and attaches it to a dock - /// - /// Notes: - /// - if `dockId` equal zero, a new dock will be created - /// - /// Requirements: - /// - `msg.sender` MUST be the dock owner if a new container is to be attached to an existing dock - /// - /// @param dockId The ID of the dock to attach the {Container} to - /// @param initialModules Array of initially enabled modules - /* function createContainer(uint256 dockId, address[] memory initialModules) external returns (address container); - */ - /// @notice Transfers the ownership of the `container` container - /// - /// Requirements: - /// - reverts if `msg.sender` is not the current {Container} owner - /// - reverts if `newOwner` is the zero-address - /// - /// @param container The address of the {Container} instance whose ownership is to be transferred - /// @param newOwner The address of the new owner - function transferContainerOwnership(address container, address newOwner) external; - - /// @notice Transfers the ownership of the `dockId` dock - /// - /// Notes: - /// - does not check for zero-address; ownership will be renounced if `newOwner` is the zero-address - /// - /// Requirements: - /// - `msg.sender` MUST be the current dock owner - /// - /// @param dockId The ID of the dock of whose ownership is to be transferred - /// @param newOwner The address of the new owner - function transferDockOwnership(uint256 dockId, address newOwner) external; - - /// @notice Updates the address of the {ModuleKeeper} - /// - /// Notes: - /// - does not check for zero-address; - /// - /// Requirements: - /// - reverts if `msg.sender` is not the {DockRegistry} owner - /// - /// @param newModuleKeeper The new address of the {ModuleKeeper} - function updateModuleKeeper(ModuleKeeper newModuleKeeper) external; -} diff --git a/src/interfaces/IModuleKeeper.sol b/src/interfaces/IModuleKeeper.sol index b82b44d7..eb80c976 100644 --- a/src/interfaces/IModuleKeeper.sol +++ b/src/interfaces/IModuleKeeper.sol @@ -22,7 +22,7 @@ interface IModuleKeeper { CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Checks if the `module` module is allowlisted to be used by a {Container} + /// @notice Checks if the `module` module is allowlisted to be used by a {Space} /// @param module The address of the module contract function isAllowlisted(address module) external view returns (bool allowlisted); diff --git a/src/interfaces/IModuleManager.sol b/src/interfaces/IModuleManager.sol index f7c9ac79..05736bc4 100644 --- a/src/interfaces/IModuleManager.sol +++ b/src/interfaces/IModuleManager.sol @@ -1,20 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.26; -import { DockRegistry } from "./../DockRegistry.sol"; - /// @title IModuleManager -/// @notice Contract that provides functionalities to manage multiple modules within a {Container} contract +/// @notice Contract that provides functionalities to manage multiple modules within a {Space} contract interface IModuleManager { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a module is enabled on the container + /// @notice Emitted when a module is enabled on the space /// @param module The address of the enabled module event ModuleEnabled(address indexed module, address indexed owner); - /// @notice Emitted when a module is disabled on the container + /// @notice Emitted when a module is disabled on the space /// @param module The address of the disabled module event ModuleDisabled(address indexed module, address indexed owner); @@ -22,10 +20,7 @@ interface IModuleManager { CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Returns the address of the {DockRegistry} contract - /* function dockRegistry() external view returns (DockRegistry); - */ - /// @notice Checks whether the `module` module is enabled on the container + /// @notice Checks whether the `module` module is enabled on the space function isModuleEnabled(address module) external view returns (bool isEnabled); /*////////////////////////////////////////////////////////////////////////// diff --git a/src/interfaces/IContainer.sol b/src/interfaces/ISpace.sol similarity index 92% rename from src/interfaces/IContainer.sol rename to src/interfaces/ISpace.sol index 81b122d5..c2ff98fa 100644 --- a/src/interfaces/IContainer.sol +++ b/src/interfaces/ISpace.sol @@ -8,43 +8,43 @@ import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -/// @title IContainer +/// @title ISpace /// @notice Contract that provides functionalities to store native token (ETH) value and any ERC-20 tokens, allowing /// external modules to be executed by extending its core functionalities -interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { +interface ISpace is IERC165, IERC721Receiver, IERC1155Receiver { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when an `amount` amount of `asset` native tokens (ETH) is deposited on the container + /// @notice Emitted when an `amount` amount of `asset` native tokens (ETH) is deposited on the space /// @param from The address of the depositor /// @param amount The amount of the deposited ERC-20 token event NativeReceived(address indexed from, uint256 amount); - /// @notice Emitted when an ERC-721 token is received by the container + /// @notice Emitted when an ERC-721 token is received by the space /// @param from The address of the depositor /// @param tokenId The ID of the received token event ERC721Received(address indexed from, uint256 indexed tokenId); - /// @notice Emitted when an ERC-1155 token is received by the container + /// @notice Emitted when an ERC-1155 token is received by the space /// @param from The address of the depositor /// @param id The ID of the received token /// @param value The amount of tokens received event ERC1155Received(address indexed from, uint256 indexed id, uint256 value); - /// @notice Emitted when an `amount` amount of `asset` ERC-20 asset or native ETH is withdrawn from the container + /// @notice Emitted when an `amount` amount of `asset` ERC-20 asset or native ETH is withdrawn from the space /// @param to The address to which the tokens were transferred /// @param asset The address of the ERC-20 token or zero-address for native ETH /// @param amount The withdrawn amount event AssetWithdrawn(address indexed to, address indexed asset, uint256 amount); - /// @notice Emitted when an ERC-721 token is withdrawn from the container + /// @notice Emitted when an ERC-721 token is withdrawn from the space /// @param to The address to which the token was transferred /// @param collection The address of the ERC-721 collection /// @param tokenId The ID of the token event ERC721Withdrawn(address indexed to, address indexed collection, uint256 tokenId); - /// @notice Emitted when a `value` amount of ERC-1155 `id` tokens are withdrawn from the container + /// @notice Emitted when a `value` amount of ERC-1155 `id` tokens are withdrawn from the space /// @param to The address to which the tokens were transferred /// @param collection The address of the ERC-1155 collection /// @param ids The IDs of the tokens @@ -79,13 +79,13 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @notice Executes multiple calls to one or more `modules` modules /// @param modules The addesses of the modules to call /// @param values THe amout of wei to provide to each call - /// @param data The ABI-encoded definition of the method and inputs + /// @param data The ABI-encoded definition of the method and inputs function executeBatch(address[] calldata modules, uint256[] calldata values, bytes[] calldata data) external; /// @notice Withdraws an `amount` amount of `asset` ERC-20 token /// /// Requirements: - /// - `msg.sender` must be the owner of the container + /// - `msg.sender` must be the owner of the space /// /// @param asset The address of the ERC-20 token to withdraw /// @param amount The amount of the ERC-20 token to withdraw @@ -94,7 +94,7 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @notice Withdraws the `tokenId` token of the ERC-721 `collection` collection /// /// Requirements: - /// - `msg.sender` must be the owner of the container + /// - `msg.sender` must be the owner of the space /// /// @param collection The address of the ERC-721 collection /// @param tokenId The ID of the token to withdraw @@ -103,7 +103,7 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @notice Withdraws an `amount` amount of the ERC-1155 `id` token /// /// Requirements: - /// - `msg.sender` must be the owner of the container + /// - `msg.sender` must be the owner of the space /// /// @param collection The address of the ERC-1155 collection /// @param ids The IDs of tokens to withdraw @@ -113,7 +113,7 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @notice Withdraws an `amount` amount of native token (ETH) /// /// Requirements: - /// - `msg.sender` must be the owner of the container + /// - `msg.sender` must be the owner of the space /// /// @param amount The amount of the native token to withdraw function withdrawNative(uint256 amount) external; diff --git a/src/interfaces/IStationRegistry.sol b/src/interfaces/IStationRegistry.sol new file mode 100644 index 00000000..858e1da1 --- /dev/null +++ b/src/interfaces/IStationRegistry.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.26; + +import { Space } from "./../Space.sol"; +import { IModuleKeeper } from "./IModuleKeeper.sol"; +import { ModuleKeeper } from "./../ModuleKeeper.sol"; + +/// @title IStationRegistry +/// @notice Contract that provides functionalities to create stations and deploy {Space}s from a single place +interface IStationRegistry { + /*////////////////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Emitted when a new {Space} contract gets deployed + /// @param owner The address of the owner + /// @param stationId The ID of the station to which this {Space} belongs + /// @param space The address of the {Space} + /// @param initialModules Array of initially enabled modules + event SpaceCreated(address indexed owner, uint256 indexed stationId, address space, address[] initialModules); + + /// @notice Emitted when the ownership of a {Station} is transferred to a new owner + /// @param stationId The address of the {Station} + /// @param oldOwner The address of the current owner + /// @param newOwner The address of the new owner + event StationOwnershipTransferred(uint256 indexed stationId, address oldOwner, address newOwner); + + /// @notice Emitted when the {ModuleKeeper} address is updated + /// @param newModuleKeeper The new address of the {ModuleKeeper} + event ModuleKeeperUpdated(IModuleKeeper newModuleKeeper); + + /*////////////////////////////////////////////////////////////////////////// + CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Returns the address of the {ModuleKeeper} contract + function moduleKeeper() external view returns (ModuleKeeper); + + /// @notice Retrieves the owner of the given station ID + function ownerOfStation(uint256 stationId) external view returns (address); + + /// @notice Retrieves the station ID of the given space address + function stationIdOfSpace(address space) external view returns (uint256); + + /// @notice Retrieves the total number of accounts created by the `signer` address + function totalAccountsOfSigner(address signer) external view returns (uint256); + + /*////////////////////////////////////////////////////////////////////////// + NON-CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Creates a new {Space} contract and attaches it to a station + /// + /// Notes: + /// - if `stationId` equal zero, a new station will be created + /// + /// Requirements: + /// - `msg.sender` MUST be the station owner if a new space is to be attached to an existing station + /// + /// @param _admin The ID of the station to attach the {Space} to + /// @param _data Array of initially enabled modules + function createAccount(address _admin, bytes calldata _data) external returns (address space); + + /// @notice Transfers the ownership of the `stationId` station + /// + /// Notes: + /// - does not check for zero-address; ownership will be renounced if `newOwner` is the zero-address + /// + /// Requirements: + /// - `msg.sender` MUST be the current station owner + /// + /// @param stationId The ID of the station of whose ownership is to be transferred + /// @param newOwner The address of the new owner + function transferStationOwnership(uint256 stationId, address newOwner) external; + + /// @notice Updates the address of the {ModuleKeeper} + /// + /// Notes: + /// - does not check for zero-address; + /// + /// Requirements: + /// - reverts if `msg.sender` is not the {StationRegistry} owner + /// + /// @param newModuleKeeper The new address of the {ModuleKeeper} + function updateModuleKeeper(ModuleKeeper newModuleKeeper) external; +} diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 2dd01425..799ce8ba 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -5,18 +5,18 @@ pragma solidity ^0.8.26; /// @notice Library containing all custom errors the protocol may revert with library Errors { /*////////////////////////////////////////////////////////////////////////// - DOCK-REGISTRY + STATION-REGISTRY //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when `msg.sender` is not the dock owner - error CallerNotDockOwner(); + /// @notice Thrown when `msg.sender` is not the station owner + error CallerNotStationOwner(); /*////////////////////////////////////////////////////////////////////////// - CONTAINER + SPACE //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when `msg.sender` is not the {Container} contract owner - error CallerNotContainerOwner(); + /// @notice Thrown when `msg.sender` is not the {Space} contract owner + error CallerNotSpaceOwner(); /// @notice Thrown when a native token (ETH) withdrawal fails error NativeWithdrawFailed(); @@ -45,10 +45,10 @@ library Errors { MODULE-MANAGER //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when a {Container} tries to execute a method on a non-enabled module + /// @notice Thrown when a {Space} tries to execute a method on a non-enabled module error ModuleNotEnabled(address module); - /// @notice Thrown when an attempt is made to enable a non-allowlisted module on a {Container} + /// @notice Thrown when an attempt is made to enable a non-allowlisted module on a {Space} error ModuleNotAllowlisted(); /*////////////////////////////////////////////////////////////////////////// diff --git a/src/modules/invoice-module/InvoiceModule.sol b/src/modules/invoice-module/InvoiceModule.sol index d0679a73..174db916 100644 --- a/src/modules/invoice-module/InvoiceModule.sol +++ b/src/modules/invoice-module/InvoiceModule.sol @@ -11,7 +11,7 @@ import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISabli import { Types } from "./libraries/Types.sol"; import { Errors } from "./libraries/Errors.sol"; import { IInvoiceModule } from "./interfaces/IInvoiceModule.sol"; -import { IContainer } from "./../../interfaces/IContainer.sol"; +import { ISpace } from "./../../interfaces/ISpace.sol"; import { StreamManager } from "./sablier-v2/StreamManager.sol"; import { Helpers } from "./libraries/Helpers.sol"; @@ -46,7 +46,7 @@ contract InvoiceModule is IInvoiceModule, StreamManager, ERC721 { string memory _URI ) StreamManager(_sablierLockupLinear, _sablierLockupTranched, _brokerAdmin) - ERC721("Metadock Invoice NFT", "MD-INVOICES") + ERC721("Metastation Invoice NFT", "MD-INVOICES") { // Start the invoice IDs from 1 _nextInvoiceId = 1; @@ -59,16 +59,16 @@ contract InvoiceModule is IInvoiceModule, StreamManager, ERC721 { MODIFIERS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Allow only calls from contracts implementing the {IContainer} interface - modifier onlyContainer() { + /// @dev Allow only calls from contracts implementing the {ISpace} interface + modifier onlySpace() { // Checks: the sender is a valid non-zero code size contract if (msg.sender.code.length == 0) { - revert Errors.ContainerZeroCodeSize(); + revert Errors.SpaceZeroCodeSize(); } - // Checks: the sender implements the ERC-165 interface required by {IContainer} - bytes4 interfaceId = type(IContainer).interfaceId; - if (!IContainer(msg.sender).supportsInterface(interfaceId)) revert Errors.ContainerUnsupportedInterface(); + // Checks: the sender implements the ERC-165 interface required by {ISpace} + bytes4 interfaceId = type(ISpace).interfaceId; + if (!ISpace(msg.sender).supportsInterface(interfaceId)) revert Errors.SpaceUnsupportedInterface(); _; } @@ -86,7 +86,7 @@ contract InvoiceModule is IInvoiceModule, StreamManager, ERC721 { //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IInvoiceModule - function createInvoice(Types.Invoice calldata invoice) external onlyContainer returns (uint256 invoiceId) { + function createInvoice(Types.Invoice calldata invoice) external onlySpace returns (uint256 invoiceId) { // Checks: the amount is non-zero if (invoice.payment.amount == 0) { revert Errors.ZeroPaymentAmount(); @@ -166,7 +166,7 @@ contract InvoiceModule is IInvoiceModule, StreamManager, ERC721 { ++_nextInvoiceId; } - // Effects: mint the invoice NFT to the recipient container + // Effects: mint the invoice NFT to the recipient space _mint({ to: msg.sender, tokenId: invoiceId }); // Log the invoice creation diff --git a/src/modules/invoice-module/interfaces/IInvoiceModule.sol b/src/modules/invoice-module/interfaces/IInvoiceModule.sol index 14a8b921..ca83343c 100644 --- a/src/modules/invoice-module/interfaces/IInvoiceModule.sol +++ b/src/modules/invoice-module/interfaces/IInvoiceModule.sol @@ -52,10 +52,10 @@ interface IInvoiceModule { /// @notice Creates a new invoice /// /// Requirements: - /// - `msg.sender` must be a contract implementing the {IContainer} interface + /// - `msg.sender` must be a contract implementing the {ISpace} interface /// /// Notes: - /// - `recipient` is not checked because the call is enforced to be made through a {Container} contract + /// - `recipient` is not checked because the call is enforced to be made through a {Space} contract /// /// @param invoice The details of the invoice following the {Invoice} struct format /// @return id The on-chain ID of the invoice diff --git a/src/modules/invoice-module/libraries/Errors.sol b/src/modules/invoice-module/libraries/Errors.sol index 7655807d..e9f0c88f 100644 --- a/src/modules/invoice-module/libraries/Errors.sol +++ b/src/modules/invoice-module/libraries/Errors.sol @@ -9,10 +9,10 @@ library Errors { //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when the caller is an invalid zero code contract or EOA - error ContainerZeroCodeSize(); + error SpaceZeroCodeSize(); - /// @notice Thrown when the caller is a contract that does not implement the {IContainer} interface - error ContainerUnsupportedInterface(); + /// @notice Thrown when the caller is a contract that does not implement the {ISpace} interface + error SpaceUnsupportedInterface(); /// @notice Thrown when the end time of an invoice is in the past error EndTimeInThePast(); diff --git a/test/Base.t.sol b/test/Base.t.sol index 95bddbb1..88349347 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -5,16 +5,16 @@ import { Events } from "./utils/Events.sol"; import { Users } from "./utils/Types.sol"; import { Test } from "forge-std/Test.sol"; import { MockERC20NoReturn } from "./mocks/MockERC20NoReturn.sol"; -import { MockNonCompliantContainer } from "./mocks/MockNonCompliantContainer.sol"; +import { MockNonCompliantSpace } from "./mocks/MockNonCompliantSpace.sol"; import { MockModule } from "./mocks/MockModule.sol"; import { MockBadReceiver } from "./mocks/MockBadReceiver.sol"; -import { Container } from "./../src/Container.sol"; +import { Space } from "./../src/Space.sol"; import { ModuleKeeper } from "./../src/ModuleKeeper.sol"; -import { DockRegistry } from "./../src/DockRegistry.sol"; +import { StationRegistry } from "./../src/StationRegistry.sol"; import { EntryPoint } from "@thirdweb/contracts/prebuilts/account/utils/Entrypoint.sol"; import { MockERC721Collection } from "./mocks/MockERC721Collection.sol"; import { MockERC1155Collection } from "./mocks/MockERC1155Collection.sol"; -import { MockBadContainer } from "./mocks/MockBadContainer.sol"; +import { MockBadSpace } from "./mocks/MockBadSpace.sol"; import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; abstract contract Base_Test is Test, Events { @@ -29,12 +29,12 @@ abstract contract Base_Test is Test, Events { //////////////////////////////////////////////////////////////////////////*/ EntryPoint internal entrypoint; - DockRegistry internal dockRegistry; - Container internal container; + StationRegistry internal stationRegistry; + Space internal space; ModuleKeeper internal moduleKeeper; MockERC20NoReturn internal usdt; MockModule internal mockModule; - MockNonCompliantContainer internal mockNonCompliantContainer; + MockNonCompliantSpace internal mockNonCompliantSpace; MockBadReceiver internal mockBadReceiver; MockERC721Collection internal mockERC721; MockERC1155Collection internal mockERC1155; @@ -61,11 +61,11 @@ abstract contract Base_Test is Test, Events { entrypoint = new EntryPoint(); moduleKeeper = new ModuleKeeper({ _initialOwner: users.admin }); - dockRegistry = new DockRegistry(users.admin, entrypoint, moduleKeeper); - containerImplementation = address(new Container(entrypoint, address(dockRegistry))); + stationRegistry = new StationRegistry(users.admin, entrypoint, moduleKeeper); + containerImplementation = address(new Space(entrypoint, address(stationRegistry))); mockModule = new MockModule(); - mockNonCompliantContainer = new MockNonCompliantContainer({ _owner: users.admin }); + mockNonCompliantSpace = new MockNonCompliantSpace({ _owner: users.admin }); mockBadReceiver = new MockBadReceiver(); mockERC721 = new MockERC721Collection("MockERC721Collection", "MC"); mockERC1155 = new MockERC1155Collection("https://nft.com/0x1.json"); @@ -74,24 +74,24 @@ abstract contract Base_Test is Test, Events { mockModules.push(address(mockModule)); // Label the test contracts so we can easily track them - vm.label({ account: address(dockRegistry), newLabel: "DockRegistry" }); + vm.label({ account: address(stationRegistry), newLabel: "StationRegistry" }); vm.label({ account: address(entrypoint), newLabel: "EntryPoint" }); vm.label({ account: address(moduleKeeper), newLabel: "ModuleKeeper" }); vm.label({ account: address(usdt), newLabel: "USDT" }); vm.label({ account: address(mockModule), newLabel: "MockModule" }); - vm.label({ account: address(mockNonCompliantContainer), newLabel: "MockNonCompliantContainer" }); + vm.label({ account: address(mockNonCompliantSpace), newLabel: "MockNonCompliantSpace" }); } /*////////////////////////////////////////////////////////////////////////// DEPLOYMENT-RELATED FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Deploys a new {Container} smart account based on the provided `owner`, `moduleKeeper` and `initialModules` input params - function deployContainer( + /// @dev Deploys a new {Space} smart account based on the provided `owner`, `moduleKeeper` and `initialModules` input params + function deploySpace( address _owner, - uint256 _dockId, + uint256 _spaceId, address[] memory _initialModules - ) internal returns (Container _container) { + ) internal returns (Space _container) { vm.startPrank({ msgSender: users.admin }); for (uint256 i; i < _initialModules.length; ++i) { allowlistModule(_initialModules[i]); @@ -99,19 +99,19 @@ abstract contract Base_Test is Test, Events { vm.stopPrank(); bytes memory data = - computeCreateAccountCalldata({ deployer: _owner, dockId: _dockId, initialModules: _initialModules }); + computeCreateAccountCalldata({ deployer: _owner, stationId: _spaceId, initialModules: _initialModules }); vm.prank({ msgSender: _owner }); - _container = Container(payable(dockRegistry.createAccount({ _admin: _owner, _data: data }))); + _container = Space(payable(stationRegistry.createAccount({ _admin: _owner, _data: data }))); vm.stopPrank(); } - /// @dev Deploys a new {MockBadContainer} smart account based on the provided `owner`, `moduleKeeper` and `initialModules` input params - function deployBadContainer( + /// @dev Deploys a new {MockBadSpace} smart account based on the provided `owner`, `moduleKeeper` and `initialModules` input params + function deployBadSpace( address _owner, - uint256 _dockId, + uint256 _spaceId, address[] memory _initialModules - ) internal returns (MockBadContainer _badContainer) { + ) internal returns (MockBadSpace _badSpace) { vm.startPrank({ msgSender: users.admin }); for (uint256 i; i < _initialModules.length; ++i) { allowlistModule(_initialModules[i]); @@ -119,10 +119,10 @@ abstract contract Base_Test is Test, Events { vm.stopPrank(); bytes memory data = - computeCreateAccountCalldata({ deployer: _owner, dockId: _dockId, initialModules: _initialModules }); + computeCreateAccountCalldata({ deployer: _owner, stationId: _spaceId, initialModules: _initialModules }); vm.prank({ msgSender: _owner }); - _badContainer = MockBadContainer(payable(dockRegistry.createAccount({ _admin: _owner, _data: data }))); + _badSpace = MockBadSpace(payable(stationRegistry.createAccount({ _admin: _owner, _data: data }))); vm.stopPrank(); } @@ -147,31 +147,31 @@ abstract contract Base_Test is Test, Events { /// and constructs the calldata to be used to create the new smart account function computeDeploymentAddressAndCalldata( address deployer, - uint256 dockId, + uint256 stationId, address[] memory initialModules ) internal view returns (address expectedAddress, bytes memory data) { - data = computeCreateAccountCalldata(deployer, dockId, initialModules); + data = computeCreateAccountCalldata(deployer, stationId, initialModules); // Compute the final salt made by the deployer address and initialization data bytes32 salt = keccak256(abi.encode(deployer, data)); // Use {Clones} library to predict the smart account address based on the smart account implementation, salt and account factory expectedAddress = - Clones.predictDeterministicAddress(dockRegistry.accountImplementation(), salt, address(dockRegistry)); + Clones.predictDeterministicAddress(stationRegistry.accountImplementation(), salt, address(stationRegistry)); } - /// @dev Constructs the calldata passed to the {DockRegistry}.createAccount method + /// @dev Constructs the calldata passed to the {StationRegistry}.createAccount method function computeCreateAccountCalldata( address deployer, - uint256 dockId, + uint256 stationId, address[] memory initialModules ) internal view returns (bytes memory data) { // Get the total account deployed by `deployer` and use it as a unique salt field // because a signer must be able to deploy multiple smart accounts within one - // dock with the same initial modules - uint256 totalAccountsOfDeployer = dockRegistry.totalAccountsOfSigner(deployer); + // station with the same initial modules + uint256 totalAccountsOfDeployer = stationRegistry.totalAccountsOfSigner(deployer); - // Construct the calldata to be used to initialize the {Container} smart account - data = abi.encode(totalAccountsOfDeployer, dockId, initialModules); + // Construct the calldata to be used to initialize the {Space} smart account + data = abi.encode(totalAccountsOfDeployer, stationId, initialModules); } } diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 25159832..275252f8 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -7,8 +7,8 @@ import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinea import { SablierV2LockupTranched } from "@sablier/v2-core/src/SablierV2LockupTranched.sol"; import { NFTDescriptorMock } from "@sablier/v2-core/test/mocks/NFTDescriptorMock.sol"; import { MockStreamManager } from "../mocks/MockStreamManager.sol"; -import { MockBadContainer } from "../mocks/MockBadContainer.sol"; -import { Container } from "./../../src/Container.sol"; +import { MockBadSpace } from "../mocks/MockBadSpace.sol"; +import { Space } from "./../../src/Space.sol"; abstract contract Integration_Test is Base_Test { /*////////////////////////////////////////////////////////////////////////// @@ -21,7 +21,7 @@ abstract contract Integration_Test is Base_Test { SablierV2LockupLinear internal sablierV2LockupLinear; SablierV2LockupTranched internal sablierV2LockupTranched; MockStreamManager internal mockStreamManager; - MockBadContainer internal badContainer; + MockBadSpace internal badSpace; /*////////////////////////////////////////////////////////////////////////// SET-UP FUNCTION @@ -33,15 +33,15 @@ abstract contract Integration_Test is Base_Test { // Deploy the {InvoiceModule} modul deployInvoiceModule(); - // Setup the initial {InvoiceModule} module to be initialized on the {Container} + // Setup the initial {InvoiceModule} module to be initialized on the {Space} address[] memory modules = new address[](1); modules[0] = address(invoiceModule); - // Deploy the {Container} contract with the {InvoiceModule} enabled by default - container = deployContainer({ _owner: users.eve, _dockId: 0, _initialModules: modules }); + // Deploy the {Space} contract with the {InvoiceModule} enabled by default + space = deploySpace({ _owner: users.eve, _spaceId: 0, _initialModules: modules }); - // Deploy a "bad" {Container} with the `mockBadReceiver` as the owner - badContainer = deployBadContainer({ _owner: address(mockBadReceiver), _dockId: 0, _initialModules: modules }); + // Deploy a "bad" {Space} with the `mockBadReceiver` as the owner + badSpace = deployBadSpace({ _owner: address(mockBadReceiver), _spaceId: 0, _initialModules: modules }); // Deploy the mock {StreamManager} mockStreamManager = new MockStreamManager(sablierV2LockupLinear, sablierV2LockupTranched, users.admin); @@ -50,8 +50,8 @@ abstract contract Integration_Test is Base_Test { vm.label({ account: address(invoiceModule), newLabel: "InvoiceModule" }); vm.label({ account: address(sablierV2LockupLinear), newLabel: "SablierV2LockupLinear" }); vm.label({ account: address(sablierV2LockupTranched), newLabel: "SablierV2LockupTranched" }); - vm.label({ account: address(container), newLabel: "Eve's Container" }); - vm.label({ account: address(badContainer), newLabel: "Bad receiver's Container" }); + vm.label({ account: address(space), newLabel: "Eve's Space" }); + vm.label({ account: address(badSpace), newLabel: "Bad receiver's Space" }); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol b/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol index 78838a23..460e3ea1 100644 --- a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol +++ b/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol @@ -35,8 +35,8 @@ contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Sha // Set the one-off ETH transfer invoice as current one uint256 invoiceId = 2; - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Cancel the invoice first invoiceModule.cancelInvoice({ id: invoiceId }); @@ -77,8 +77,8 @@ contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Sha // Set the one-off ETH transfer invoice as current one uint256 invoiceId = 2; - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Expect the {InvoiceCanceled} event to be emitted vm.expectEmit(); @@ -123,8 +123,8 @@ contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Sha // Set current invoice as a linear stream-based one uint256 invoiceId = 5; - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Expect the {InvoiceCanceled} event to be emitted vm.expectEmit(); @@ -232,8 +232,8 @@ contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Sha // Set current invoice as a tranched stream-based one uint256 invoiceId = 5; - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Expect the {InvoiceCanceled} event to be emitted vm.expectEmit(); diff --git a/test/integration/concrete/invoice-module/create-invoice/createInvoice.t.sol b/test/integration/concrete/invoice-module/create-invoice/createInvoice.t.sol index 22d735a3..6dd4c516 100644 --- a/test/integration/concrete/invoice-module/create-invoice/createInvoice.t.sol +++ b/test/integration/concrete/invoice-module/create-invoice/createInvoice.t.sol @@ -17,8 +17,8 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha // Make Bob the caller in this test suite which is an EOA vm.startPrank({ msgSender: users.bob }); - // Expect the call to revert with the {ContainerZeroCodeSize} error - vm.expectRevert(Errors.ContainerZeroCodeSize.selector); + // Expect the call to revert with the {SpaceZeroCodeSize} error + vm.expectRevert(Errors.SpaceZeroCodeSize.selector); // Create an one-off transfer invoice invoice = createInvoiceWithOneOffTransfer({ asset: address(usdt) }); @@ -27,8 +27,8 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha invoiceModule.createInvoice(invoice); } - function test_RevertWhen_NonCompliantContainer() external whenCallerContract { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + function test_RevertWhen_NonCompliantSpace() external whenCallerContract { + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create an one-off transfer invoice @@ -39,15 +39,15 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha "createInvoice((uint8,uint40,uint40,(uint8,uint8,uint40,address,uint128,uint256)))", invoice ); - // Expect the call to revert with the {ContainerUnsupportedInterface} error - vm.expectRevert(Errors.ContainerUnsupportedInterface.selector); + // Expect the call to revert with the {SpaceUnsupportedInterface} error + vm.expectRevert(Errors.SpaceUnsupportedInterface.selector); // Run the test - mockNonCompliantContainer.execute({ module: address(invoiceModule), value: 0, data: data }); + mockNonCompliantSpace.execute({ module: address(invoiceModule), value: 0, data: data }); } - function test_RevertWhen_ZeroPaymentAmount() external whenCallerContract whenCompliantContainer { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + function test_RevertWhen_ZeroPaymentAmount() external whenCallerContract whenCompliantSpace { + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create an one-off transfer invoice @@ -65,16 +65,16 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectRevert(Errors.ZeroPaymentAmount.selector); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_RevertWhen_StartTimeGreaterThanEndTime() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create an one-off transfer invoice @@ -93,17 +93,17 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectRevert(Errors.StartTimeGreaterThanEndTime.selector); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_RevertWhen_EndTimeInThePast() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create an one-off transfer invoice @@ -126,19 +126,19 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectRevert(Errors.EndTimeInThePast.selector); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_CreateInvoice_PaymentMethodOneOffTransfer() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodOneOffTransfer { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a recurring transfer invoice that must be paid on a monthly basis @@ -154,25 +154,25 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectEmit(); emit Events.InvoiceCreated({ id: 1, - recipient: address(container), + recipient: address(space), status: Types.Status.Pending, startTime: invoice.startTime, endTime: invoice.endTime, payment: invoice.payment }); - // Expect the {Container} contract to emit a {ModuleExecutionSucceded} event + // Expect the {Space} contract to emit a {ModuleExecutionSucceded} event vm.expectEmit(); emit Events.ModuleExecutionSucceded({ module: address(invoiceModule), value: 0, data: data }); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Assert the actual and expected invoice state Types.Invoice memory actualInvoice = invoiceModule.getInvoice({ id: 1 }); address expectedRecipient = invoiceModule.ownerOf(1); - assertEq(expectedRecipient, address(container)); + assertEq(expectedRecipient, address(space)); assertEq(uint8(actualInvoice.status), uint8(Types.Status.Pending)); assertEq(actualInvoice.startTime, invoice.startTime); assertEq(actualInvoice.endTime, invoice.endTime); @@ -187,13 +187,13 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha function test_RevertWhen_PaymentMethodRecurringTransfer_PaymentIntervalTooShortForSelectedRecurrence() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodRecurringTransfer { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a recurring transfer invoice that must be paid on a monthly basis @@ -212,20 +212,20 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectRevert(Errors.PaymentIntervalTooShortForSelectedRecurrence.selector); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_CreateInvoice_RecurringTransfer() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodRecurringTransfer whenPaymentIntervalLongEnough { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a recurring transfer invoice that must be paid on weekly basis @@ -240,25 +240,25 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectEmit(); emit Events.InvoiceCreated({ id: 1, - recipient: address(container), + recipient: address(space), status: Types.Status.Pending, startTime: invoice.startTime, endTime: invoice.endTime, payment: invoice.payment }); - // Expect the {Container} contract to emit a {ModuleExecutionSucceded} event + // Expect the {Space} contract to emit a {ModuleExecutionSucceded} event vm.expectEmit(); emit Events.ModuleExecutionSucceded({ module: address(invoiceModule), value: 0, data: data }); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Assert the actual and expected invoice state Types.Invoice memory actualInvoice = invoiceModule.getInvoice({ id: 1 }); address expectedRecipient = invoiceModule.ownerOf(1); - assertEq(expectedRecipient, address(container)); + assertEq(expectedRecipient, address(space)); assertEq(uint8(actualInvoice.status), uint8(Types.Status.Pending)); assertEq(actualInvoice.startTime, invoice.startTime); assertEq(actualInvoice.endTime, invoice.endTime); @@ -273,13 +273,13 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha function test_RevertWhen_PaymentMethodTranchedStream_RecurrenceSetToOneOff() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodTranchedStream { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a tranched stream payment @@ -297,20 +297,20 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha ); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_RevertWhen_PaymentMethodTranchedStream_PaymentIntervalTooShortForSelectedRecurrence() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodTranchedStream whenTranchedStreamWithGoodRecurring { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a tranched stream payment @@ -328,13 +328,13 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha ); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_RevertWhen_PaymentMethodTranchedStream_PaymentAssetNativeToken() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture @@ -342,7 +342,7 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha whenTranchedStreamWithGoodRecurring whenPaymentIntervalLongEnough { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a linear stream payment @@ -360,20 +360,20 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha ); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_CreateInvoice_Tranched() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodTranchedStream whenPaymentAssetNotNativeToken { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a tranched stream payment @@ -388,25 +388,25 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectEmit(); emit Events.InvoiceCreated({ id: 1, - recipient: address(container), + recipient: address(space), status: Types.Status.Pending, startTime: invoice.startTime, endTime: invoice.endTime, payment: invoice.payment }); - // Expect the {Container} contract to emit a {ModuleExecutionSucceded} event + // Expect the {Space} contract to emit a {ModuleExecutionSucceded} event vm.expectEmit(); emit Events.ModuleExecutionSucceded({ module: address(invoiceModule), value: 0, data: data }); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Assert the actual and expected invoice state Types.Invoice memory actualInvoice = invoiceModule.getInvoice({ id: 1 }); address expectedRecipient = invoiceModule.ownerOf(1); - assertEq(expectedRecipient, address(container)); + assertEq(expectedRecipient, address(space)); assertEq(uint8(actualInvoice.status), uint8(Types.Status.Pending)); assertEq(actualInvoice.startTime, invoice.startTime); assertEq(actualInvoice.endTime, invoice.endTime); @@ -421,13 +421,13 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha function test_RevertWhen_PaymentMethodLinearStream_PaymentAssetNativeToken() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodLinearStream { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a linear stream payment @@ -445,20 +445,20 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha ); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); } function test_CreateInvoice_LinearStream() external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture givenPaymentMethodLinearStream whenPaymentAssetNotNativeToken { - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); // Create a new invoice with a linear stream payment @@ -473,25 +473,25 @@ contract CreateInvoice_Integration_Concret_Test is CreateInvoice_Integration_Sha vm.expectEmit(); emit Events.InvoiceCreated({ id: 1, - recipient: address(container), + recipient: address(space), status: Types.Status.Pending, startTime: invoice.startTime, endTime: invoice.endTime, payment: invoice.payment }); - // Expect the {Container} contract to emit a {ModuleExecutionSucceded} event + // Expect the {Space} contract to emit a {ModuleExecutionSucceded} event vm.expectEmit(); emit Events.ModuleExecutionSucceded({ module: address(invoiceModule), value: 0, data: data }); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Assert the actual and expected invoice state Types.Invoice memory actualInvoice = invoiceModule.getInvoice({ id: 1 }); address expectedRecipient = invoiceModule.ownerOf(1); - assertEq(expectedRecipient, address(container)); + assertEq(expectedRecipient, address(space)); assertEq(uint8(actualInvoice.status), uint8(Types.Status.Pending)); assertEq(actualInvoice.startTime, invoice.startTime); assertEq(actualInvoice.endTime, invoice.endTime); diff --git a/test/integration/concrete/invoice-module/create-invoice/createInvoice.tree b/test/integration/concrete/invoice-module/create-invoice/createInvoice.tree index e38cdb84..23a5184e 100644 --- a/test/integration/concrete/invoice-module/create-invoice/createInvoice.tree +++ b/test/integration/concrete/invoice-module/create-invoice/createInvoice.tree @@ -1,10 +1,10 @@ createInvoice.t.sol ├── when the caller IS NOT a contract -│ └── it should revert with the {ContainerZeroCodeSize} error +│ └── it should revert with the {SpaceZeroCodeSize} error └── when the caller IS a contract - ├── when the caller contract DOES NOT implement the ERC-165 {IContainer} interface - │ └── it should revert with the {ContainerUnsupportedInterface} error - └── when the caller contract DOES implement the ERC-165 {IContainer} interface + ├── when the caller contract DOES NOT implement the ERC-165 {ISpace} interface + │ └── it should revert with the {SpaceUnsupportedInterface} error + └── when the caller contract DOES implement the ERC-165 {ISpace} interface ├── when the payment amount IS zero │ └── it should revert with the {ZeroPaymentAmount} error └── when the payment amount IS greater than zero diff --git a/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol b/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol index 854ab34c..5fb24732 100644 --- a/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol +++ b/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol @@ -45,8 +45,8 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Set the one-off USDT transfer invoice as current one uint256 invoiceId = 1; - // Make Eve's container the caller in this test suite as his container is the owner of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller in this test suite as his space is the owner of the invoice + vm.startPrank({ msgSender: address(space) }); // Cancel the invoice first invoiceModule.cancelInvoice({ id: invoiceId }); @@ -95,20 +95,20 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te givenPaymentAmountInNativeToken whenPaymentAmountEqualToInvoiceValue { - // Create a mock invoice with a one-off ETH transfer from the Eve's container + // Create a mock invoice with a one-off ETH transfer from the Eve's space Types.Invoice memory invoice = createInvoiceWithOneOffTransfer({ asset: address(0) }); executeCreateInvoice({ invoice: invoice, user: users.eve }); uint256 invoiceId = _nextInvoiceId; - // Make Eve's container the caller for the next call to approve & transfer the invoice NFT to a bad receiver - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller for the next call to approve & transfer the invoice NFT to a bad receiver + vm.startPrank({ msgSender: address(space) }); // Approve the {InvoiceModule} to transfer the token invoiceModule.approve({ to: address(invoiceModule), tokenId: invoiceId }); // Transfer the invoice to a bad receiver so we can test against `NativeTokenPaymentFailed` - invoiceModule.transferFrom({ from: address(container), to: address(mockBadReceiver), tokenId: invoiceId }); + invoiceModule.transferFrom({ from: address(space), to: address(mockBadReceiver), tokenId: invoiceId }); // Make Bob the payer for this invoice vm.startPrank({ msgSender: users.bob }); @@ -138,7 +138,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Store the ETH balances of Bob and recipient before paying the invoice uint256 balanceOfBobBefore = address(users.bob).balance; - uint256 balanceOfRecipientBefore = address(container).balance; + uint256 balanceOfRecipientBefore = address(space).balance; // Expect the {InvoicePaid} event to be emitted vm.expectEmit(); @@ -166,7 +166,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the balances of payer and recipient assertEq(address(users.bob).balance, balanceOfBobBefore - invoices[invoiceId].payment.amount); - assertEq(address(container).balance, balanceOfRecipientBefore + invoices[invoiceId].payment.amount); + assertEq(address(space).balance, balanceOfRecipientBefore + invoices[invoiceId].payment.amount); } function test_PayInvoice_PaymentMethodTransfer_ERC20Token_Recurring() @@ -186,7 +186,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Store the USDT balances of Bob and recipient before paying the invoice uint256 balanceOfBobBefore = usdt.balanceOf(users.bob); - uint256 balanceOfRecipientBefore = usdt.balanceOf(address(container)); + uint256 balanceOfRecipientBefore = usdt.balanceOf(address(space)); // Approve the {InvoiceModule} to transfer the ERC-20 tokens on Bob's behalf usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount }); @@ -217,7 +217,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the balances of payer and recipient assertEq(usdt.balanceOf(users.bob), balanceOfBobBefore - invoices[invoiceId].payment.amount); - assertEq(usdt.balanceOf(address(container)), balanceOfRecipientBefore + invoices[invoiceId].payment.amount); + assertEq(usdt.balanceOf(address(space)), balanceOfRecipientBefore + invoices[invoiceId].payment.amount); } function test_PayInvoice_PaymentMethodLinearStream() @@ -266,7 +266,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the actual and the expected state of the Sablier v2 linear stream LockupLinear.StreamLL memory stream = invoiceModule.getLinearStream({ streamId: 1 }); assertEq(stream.sender, address(invoiceModule)); - assertEq(stream.recipient, address(container)); + assertEq(stream.recipient, address(space)); assertEq(address(stream.asset), address(usdt)); assertEq(stream.startTime, invoice.startTime); assertEq(stream.endTime, invoice.endTime); @@ -318,7 +318,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the actual and the expected state of the Sablier v2 tranched stream LockupTranched.StreamLT memory stream = invoiceModule.getTranchedStream({ streamId: 1 }); assertEq(stream.sender, address(invoiceModule)); - assertEq(stream.recipient, address(container)); + assertEq(stream.recipient, address(space)); assertEq(address(stream.asset), address(usdt)); assertEq(stream.startTime, invoice.startTime); assertEq(stream.endTime, invoice.endTime); diff --git a/test/integration/concrete/invoice-module/transfer-from/transferFrom.t.sol b/test/integration/concrete/invoice-module/transfer-from/transferFrom.t.sol index e4def1d6..8486d277 100644 --- a/test/integration/concrete/invoice-module/transfer-from/transferFrom.t.sol +++ b/test/integration/concrete/invoice-module/transfer-from/transferFrom.t.sol @@ -12,14 +12,14 @@ contract TransferFrom_Integration_Concret_Test is TransferFrom_Integration_Share } function test_RevertWhen_TokenDoesNotExist() external { - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Expect the call to revert with the {ERC721NonexistentToken} error vm.expectRevert(abi.encodeWithSelector(Errors.ERC721NonexistentToken.selector, 99)); // Run the test - invoiceModule.transferFrom({ from: address(container), to: users.eve, tokenId: 99 }); + invoiceModule.transferFrom({ from: address(space), to: users.eve, tokenId: 99 }); } function test_TransferFrom_PaymentMethodStream() external whenTokenExists { @@ -38,24 +38,24 @@ contract TransferFrom_Integration_Concret_Test is TransferFrom_Integration_Share // Simulate the passage of time so that the maximum withdrawable amount is non-zero vm.warp(block.timestamp + 5 weeks); - // Store Eve's container balance before withdrawing the USDT tokens - uint256 balanceOfBefore = usdt.balanceOf(address(container)); + // Store Eve's space balance before withdrawing the USDT tokens + uint256 balanceOfBefore = usdt.balanceOf(address(space)); // Get the maximum withdrawable amount from the stream before transferring the stream NFT uint128 maxWithdrawableAmount = invoiceModule.withdrawableAmountOf({ streamType: Types.Method.LinearStream, streamId: streamId }); - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); - // Approve the {InvoiceModule} to transfer the `streamId` stream on behalf of the Eve's container + // Approve the {InvoiceModule} to transfer the `streamId` stream on behalf of the Eve's space sablierV2LockupLinear.approve({ to: address(invoiceModule), tokenId: streamId }); // Run the test - invoiceModule.transferFrom({ from: address(container), to: users.eve, tokenId: invoiceId }); + invoiceModule.transferFrom({ from: address(space), to: users.eve, tokenId: invoiceId }); - // Assert the current and expected Eve's container USDT balance - assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(container))); + // Assert the current and expected Eve's space USDT balance + assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(space))); // Assert the current and expected owner of the invoice NFT assertEq(invoiceModule.ownerOf({ tokenId: invoiceId }), users.eve); @@ -67,11 +67,11 @@ contract TransferFrom_Integration_Concret_Test is TransferFrom_Integration_Share function test_TransferFrom_PaymentTransfer() external whenTokenExists { uint256 invoiceId = 1; - // Make Eve's container the caller which is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller which is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Run the test - invoiceModule.transferFrom({ from: address(container), to: users.eve, tokenId: invoiceId }); + invoiceModule.transferFrom({ from: address(space), to: users.eve, tokenId: invoiceId }); // Assert the current and expected owner of the invoice NFT assertEq(invoiceModule.ownerOf({ tokenId: invoiceId }), users.eve); diff --git a/test/integration/concrete/invoice-module/withdraw-invoice-stream/withdrawStream.t.sol b/test/integration/concrete/invoice-module/withdraw-invoice-stream/withdrawStream.t.sol index e7a96871..5002a087 100644 --- a/test/integration/concrete/invoice-module/withdraw-invoice-stream/withdrawStream.t.sol +++ b/test/integration/concrete/invoice-module/withdraw-invoice-stream/withdrawStream.t.sol @@ -27,21 +27,21 @@ contract WithdrawLinearStream_Integration_Concret_Test is WithdrawLinearStream_I // Advance the timestamp by 5 weeks to simulate the withdrawal vm.warp(block.timestamp + 5 weeks); - // Store Eve's container balance before withdrawing the USDT tokens - uint256 balanceOfBefore = usdt.balanceOf(address(container)); + // Store Eve's space balance before withdrawing the USDT tokens + uint256 balanceOfBefore = usdt.balanceOf(address(space)); // Get the maximum withdrawable amount from the stream uint128 maxWithdrawableAmount = invoiceModule.withdrawableAmountOf({ streamType: Types.Method.LinearStream, streamId: streamId }); - // Make Eve's container the caller in this test suite as his container is the recipient of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller in this test suite as his space is the recipient of the invoice + vm.startPrank({ msgSender: address(space) }); // Run the test invoiceModule.withdrawInvoiceStream(invoiceId); // Assert the current and expected USDT balance of Eve - assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(container))); + assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(space))); } function test_WithdrawStream_TranchedStream() external givenPaymentMethodTranchedStream givenInvoiceStatusOngoing { @@ -62,20 +62,20 @@ contract WithdrawLinearStream_Integration_Concret_Test is WithdrawLinearStream_I // Advance the timestamp by 5 weeks to simulate the withdrawal vm.warp(block.timestamp + 5 weeks); - // Store Eve's container balance before withdrawing the USDT tokens - uint256 balanceOfBefore = usdt.balanceOf(address(container)); + // Store Eve's space balance before withdrawing the USDT tokens + uint256 balanceOfBefore = usdt.balanceOf(address(space)); // Get the maximum withdrawable amount from the stream uint128 maxWithdrawableAmount = invoiceModule.withdrawableAmountOf({ streamType: Types.Method.TranchedStream, streamId: streamId }); - // Make Eve's container the caller in this test suite as her container is the owner of the invoice - vm.startPrank({ msgSender: address(container) }); + // Make Eve's space the caller in this test suite as her space is the owner of the invoice + vm.startPrank({ msgSender: address(space) }); // Run the test invoiceModule.withdrawInvoiceStream(invoiceId); - // Assert the current and expected USDT balance of Eve's container - assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(container))); + // Assert the current and expected USDT balance of Eve's space + assertEq(balanceOfBefore + maxWithdrawableAmount, usdt.balanceOf(address(space))); } } diff --git a/test/integration/fuzz/createInvoice.t.sol b/test/integration/fuzz/createInvoice.t.sol index 11911b10..d5692321 100644 --- a/test/integration/fuzz/createInvoice.t.sol +++ b/test/integration/fuzz/createInvoice.t.sol @@ -12,7 +12,7 @@ contract CreateInvoice_Integration_Fuzz_Test is CreateInvoice_Integration_Shared function setUp() public virtual override { CreateInvoice_Integration_Shared_Test.setUp(); - // Make Eve the caller in this test suite as she's the owner of the {Container} contract + // Make Eve the caller in this test suite as she's the owner of the {Space} contract vm.startPrank({ msgSender: users.eve }); } @@ -25,7 +25,7 @@ contract CreateInvoice_Integration_Fuzz_Test is CreateInvoice_Integration_Shared ) external whenCallerContract - whenCompliantContainer + whenCompliantSpace whenNonZeroPaymentAmount whenStartTimeLowerThanEndTime whenEndTimeInTheFuture @@ -68,25 +68,25 @@ contract CreateInvoice_Integration_Fuzz_Test is CreateInvoice_Integration_Shared vm.expectEmit(); emit Events.InvoiceCreated({ id: 1, - recipient: address(container), + recipient: address(space), status: Types.Status.Pending, startTime: invoice.startTime, endTime: invoice.endTime, payment: invoice.payment }); - // Expect the {Container} contract to emit a {ModuleExecutionSucceded} event + // Expect the {Space} contract to emit a {ModuleExecutionSucceded} event vm.expectEmit(); emit Events.ModuleExecutionSucceded({ module: address(invoiceModule), value: 0, data: data }); // Run the test - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Assert the actual and expected invoice state Types.Invoice memory actualInvoice = invoiceModule.getInvoice({ id: 1 }); address actualRecipient = invoiceModule.ownerOf(1); - assertEq(actualRecipient, address(container)); + assertEq(actualRecipient, address(space)); assertEq(uint8(actualInvoice.status), uint8(Types.Status.Pending)); assertEq(actualInvoice.startTime, invoice.startTime); assertEq(actualInvoice.endTime, invoice.endTime); diff --git a/test/integration/fuzz/payInvoice.t.sol b/test/integration/fuzz/payInvoice.t.sol index 5aefd327..2b231243 100644 --- a/test/integration/fuzz/payInvoice.t.sol +++ b/test/integration/fuzz/payInvoice.t.sol @@ -68,7 +68,7 @@ contract PayInvoice_Integration_Fuzz_Test is PayInvoice_Integration_Shared_Test vm.startPrank({ msgSender: users.eve }); // Create the fuzzed invoice - container.execute({ module: address(invoiceModule), value: 0, data: data }); + space.execute({ module: address(invoiceModule), value: 0, data: data }); // Mint enough USDT to the payer's address to be able to pay the invoice deal({ token: address(usdt), to: users.bob, give: invoice.payment.amount }); @@ -81,7 +81,7 @@ contract PayInvoice_Integration_Fuzz_Test is PayInvoice_Integration_Shared_Test // Store the USDT balances of the payer and recipient before paying the invoice uint256 balanceOfPayerBefore = usdt.balanceOf(users.bob); - uint256 balanceOfRecipientBefore = usdt.balanceOf(address(container)); + uint256 balanceOfRecipientBefore = usdt.balanceOf(address(space)); uint256 streamId = paymentMethod == 0 ? 0 : 1; numberOfPayments = numberOfPayments > 0 ? numberOfPayments - 1 : 0; @@ -117,9 +117,9 @@ contract PayInvoice_Integration_Fuzz_Test is PayInvoice_Integration_Shared_Test // Assert the actual and expected balances of the payer and recipient assertEq(usdt.balanceOf(users.bob), balanceOfPayerBefore - invoice.payment.amount); if (invoice.payment.method == Types.Method.Transfer) { - assertEq(usdt.balanceOf(address(container)), balanceOfRecipientBefore + invoice.payment.amount); + assertEq(usdt.balanceOf(address(space)), balanceOfRecipientBefore + invoice.payment.amount); } else { - assertEq(usdt.balanceOf(address(container)), balanceOfRecipientBefore); + assertEq(usdt.balanceOf(address(space)), balanceOfRecipientBefore); } } } diff --git a/test/integration/shared/createInvoice.t.sol b/test/integration/shared/createInvoice.t.sol index 2168df18..db174680 100644 --- a/test/integration/shared/createInvoice.t.sol +++ b/test/integration/shared/createInvoice.t.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.26; import { Integration_Test } from "../Integration.t.sol"; import { Types } from "./../../../src/modules/invoice-module/libraries/Types.sol"; -import { Container } from "./../../../src/Container.sol"; -import { MockBadContainer } from "../../mocks/MockBadContainer.sol"; +import { Space } from "./../../../src/Space.sol"; +import { MockBadSpace } from "../../mocks/MockBadSpace.sol"; abstract contract CreateInvoice_Integration_Shared_Test is Integration_Test { mapping(uint256 invoiceId => Types.Invoice) invoices; @@ -47,7 +47,7 @@ abstract contract CreateInvoice_Integration_Shared_Test is Integration_Test { _; } - modifier whenCompliantContainer() { + modifier whenCompliantSpace() { _; } @@ -176,7 +176,7 @@ abstract contract CreateInvoice_Integration_Shared_Test is Integration_Test { } function executeCreateInvoice(Types.Invoice memory invoice, address user) public { - // Make the `user` account the caller who must be the owner of the {Container} contract + // Make the `user` account the caller who must be the owner of the {Space} contract vm.startPrank({ msgSender: user }); // Create the invoice @@ -184,12 +184,12 @@ abstract contract CreateInvoice_Integration_Shared_Test is Integration_Test { "createInvoice((uint8,uint40,uint40,(uint8,uint8,uint40,address,uint128,uint256)))", invoice ); - // Select the according {Container} of the user + // Select the according {Space} of the user if (user == users.eve) { - Container(container).execute({ module: address(invoiceModule), value: 0, data: data }); + Space(space).execute({ module: address(invoiceModule), value: 0, data: data }); } else { - MockBadContainer(badContainer).execute({ module: address(invoiceModule), value: 0, data: data }); + MockBadSpace(badSpace).execute({ module: address(invoiceModule), value: 0, data: data }); } // Stop the active prank diff --git a/test/mocks/MockBadContainer.sol b/test/mocks/MockBadSpace.sol similarity index 89% rename from test/mocks/MockBadContainer.sol rename to test/mocks/MockBadSpace.sol index ac68a48a..34c1c706 100644 --- a/test/mocks/MockBadContainer.sol +++ b/test/mocks/MockBadSpace.sol @@ -16,16 +16,16 @@ import { AccountCore } from "@thirdweb/contracts/prebuilts/account/utils/Account import { AccountCoreStorage } from "@thirdweb/contracts/prebuilts/account/utils/AccountCoreStorage.sol"; import { EnumerableSet } from "@thirdweb/contracts/external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; -import { IContainer } from "./../../src/interfaces/IContainer.sol"; +import { ISpace } from "./../../src/interfaces/ISpace.sol"; import { ModuleManager } from "./../../src/abstracts/ModuleManager.sol"; import { IModuleManager } from "./../../src/interfaces/IModuleManager.sol"; import { Errors } from "./../../src/libraries/Errors.sol"; import { ModuleKeeper } from "./../../src/ModuleKeeper.sol"; -import { DockRegistry } from "./../../src/DockRegistry.sol"; +import { StationRegistry } from "./../../src/StationRegistry.sol"; -/// @title MockBadContainer -/// @notice Container that reverts when receiving native tokens (ETH) -contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { +/// @title MockBadSpace +/// @notice Space that reverts when receiving native tokens (ETH) +contract MockBadSpace is ISpace, AccountCore, ERC1271, ModuleManager { using ECDSA for bytes32; using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; @@ -36,14 +36,14 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ - /// @dev Initializes the address of the {Container} owner, {ModuleKeeper} and enables the initial module(s) + /// @dev Initializes the address of the {Space} owner, {ModuleKeeper} and enables the initial module(s) constructor(IEntryPoint _entrypoint, address _factory) AccountCore(_entrypoint, _factory) { } /*////////////////////////////////////////////////////////////////////////// RECEIVE & FALLBACK //////////////////////////////////////////////////////////////////////////*/ - /// @dev Allow container to receive native token (ETH) + /// @dev Allow space to receive native token (ETH) receive() external payable { // Log the successful native token deposit emit NativeReceived({ from: msg.sender, amount: msg.value }); @@ -68,13 +68,13 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc IContainer + /// @inheritdoc ISpace function execute( address module, uint256 value, bytes calldata data ) public onlyAdminOrEntrypoint returns (bool success) { - // Check and register the smart account on the {DockRegistry} factory if it is not registered yet + // Check and register the smart account on the {StationRegistry} factory if it is not registered yet _registerOnFactory(); // Checks: the `module` module is enabled on the smart account @@ -84,13 +84,13 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { success = _call(module, value, data); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function executeBatch( address[] calldata modules, uint256[] calldata values, bytes[] calldata data ) external onlyAdminOrEntrypoint { - // Check and register the smart account on the {DockRegistry} factory if it is not registered yet + // Check and register the smart account on the {StationRegistry} factory if it is not registered yet _registerOnFactory(); // Cache the length of the modules array @@ -109,9 +109,9 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { } } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC20(IERC20 asset, uint256 amount) public onlyAdminOrEntrypoint { - // Checks: the available ERC20 balance of the container is greater enough to support the withdrawal + // Checks: the available ERC20 balance of the space is greater enough to support the withdrawal if (amount > asset.balanceOf(address(this))) revert Errors.InsufficientERC20ToWithdraw(); // Interactions: withdraw by transferring the amount to the sender @@ -121,9 +121,9 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { emit AssetWithdrawn({ to: msg.sender, asset: address(asset), amount: amount }); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC721(IERC721 collection, uint256 tokenId) public onlyAdminOrEntrypoint { - // Checks, Effects, Interactions: withdraw by transferring the token to the container owner + // Checks, Effects, Interactions: withdraw by transferring the token to the space owner // Notes: // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account // therefore the `onERC721Received` hook must be implemented @@ -133,13 +133,13 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { emit ERC721Withdrawn({ to: msg.sender, collection: address(collection), tokenId: tokenId }); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawERC1155( IERC1155 collection, uint256[] memory ids, uint256[] memory amounts ) public onlyAdminOrEntrypoint { - // Checks, Effects, Interactions: withdraw by transferring the tokens to the container owner + // Checks, Effects, Interactions: withdraw by transferring the tokens to the space owner // Notes: // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account // therefore the `onERC1155Received` hook must be implemented @@ -154,9 +154,9 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { emit ERC1155Withdrawn(msg.sender, address(collection), ids, amounts); } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function withdrawNative(uint256 amount) public onlyAdminOrEntrypoint { - // Checks: the native balance of the container minus the amount locked for operations is greater than the requested amount + // Checks: the native balance of the space minus the amount locked for operations is greater than the requested amount if (amount > address(this).balance) revert Errors.InsufficientNativeToWithdraw(); // Interactions: withdraw by transferring the amount to the sender @@ -170,7 +170,7 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { /// @inheritdoc IModuleManager function enableModule(address module) public override onlyAdminOrEntrypoint { - ModuleKeeper moduleKeeper = DockRegistry(factory).moduleKeeper(); + ModuleKeeper moduleKeeper = StationRegistry(factory).moduleKeeper(); _enableModule(moduleKeeper, module); } @@ -212,7 +212,7 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { } } - /// @inheritdoc IContainer + /// @inheritdoc ISpace function getMessageHash(bytes32 _hash) public view returns (bytes32) { bytes32 messageHash = keccak256(abi.encode(_hash)); bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash)); @@ -221,7 +221,7 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) public pure returns (bool) { - return interfaceId == type(IContainer).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId + return interfaceId == type(ISpace).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC165).interfaceId; } @@ -281,7 +281,7 @@ contract MockBadContainer is IContainer, AccountCore, ERC1271, ModuleManager { /// @dev Registers the account on the factory if it hasn't been registered yet function _registerOnFactory() internal virtual { // Get the address of the factory contract - DockRegistry factoryContract = DockRegistry(factory); + StationRegistry factoryContract = StationRegistry(factory); // Check if this smart account is registered in the factory contract if (!factoryContract.isRegistered(address(this))) { diff --git a/test/mocks/MockDockRegistryV2.sol b/test/mocks/MockDockRegistryV2.sol deleted file mode 100644 index 6116099d..00000000 --- a/test/mocks/MockDockRegistryV2.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.26; - -import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { IDockRegistry } from "./../../src/interfaces/IDockRegistry.sol"; -import { Container } from "./../../src/Container.sol"; -import { ModuleKeeper } from "./../../src/ModuleKeeper.sol"; - -/// @title MockDockRegistryV2 -/// @notice See the documentation in {IDockRegistry} -contract MockDockRegistryV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable { - /// @dev Version identifier for the current implementation of the contract - string public constant VERSION = "2.0.0"; - - /*////////////////////////////////////////////////////////////////////////// - STORAGE - //////////////////////////////////////////////////////////////////////////*/ - - ModuleKeeper public moduleKeeper; - - mapping(uint256 dockId => address owner) public ownerOfDock; - - mapping(address container => uint256 dockId) public dockIdOfContainer; - - mapping(address container => address owner) public ownerOfContainer; - - /// @dev Counter to keep track of the next dock ID - uint256 private _dockNextId; - - /*////////////////////////////////////////////////////////////////////////// - RESERVED STORAGE - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev This empty reserved space is put in place to allow future versions to add new - /// variables without shifting down storage in the inheritance chain. - /// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - uint256[45] private __gap; - - /*////////////////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Lock the implementation contract - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - /// @dev Initializes the address of the {ModuleKeeper} contract, registry owner and sets the next dock ID to start from 1 - function initialize(address _initialOwner, ModuleKeeper _moduleKeeper) public initializer { - __Ownable_init(_initialOwner); - __UUPSUpgradeable_init(); - - _dockNextId = 1; - moduleKeeper = _moduleKeeper; - } - - /// @dev Allows only the owner to upgrade the contract - function _authorizeUpgrade(address newImplementation) internal override onlyOwner { } -} diff --git a/test/mocks/MockModule.sol b/test/mocks/MockModule.sol index afafc66e..78e22cf2 100644 --- a/test/mocks/MockModule.sol +++ b/test/mocks/MockModule.sol @@ -2,32 +2,32 @@ pragma solidity ^0.8.26; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import { IContainer } from "./../../src/interfaces/IContainer.sol"; +import { ISpace } from "./../../src/interfaces/ISpace.sol"; import { Errors } from "./../../src/modules/invoice-module/libraries/Errors.sol"; /// @notice A mock implementation of a boilerplate module that creates multiple items and -/// associates them with the corresponding {Container} contract +/// associates them with the corresponding {Space} contract contract MockModule { - mapping(address container => uint256[]) public itemsOf; + mapping(address space => uint256[]) public itemsOf; uint256 private _nextItemIf; event ModuleItemCreated(uint256 indexed id); - /// @dev Allow only calls from contracts implementing the {IContainer} interface - modifier onlyContainer() { + /// @dev Allow only calls from contracts implementing the {ISpace} interface + modifier onlySpace() { // Checks: the sender is a valid non-zero code size contract if (msg.sender.code.length == 0) { - revert Errors.ContainerZeroCodeSize(); + revert Errors.SpaceZeroCodeSize(); } - // Checks: the sender implements the ERC-165 interface required by {IContainer} - bytes4 interfaceId = type(IContainer).interfaceId; - if (!IERC165(msg.sender).supportsInterface(interfaceId)) revert Errors.ContainerUnsupportedInterface(); + // Checks: the sender implements the ERC-165 interface required by {ISpace} + bytes4 interfaceId = type(ISpace).interfaceId; + if (!IERC165(msg.sender).supportsInterface(interfaceId)) revert Errors.SpaceUnsupportedInterface(); _; } - function createModuleItem() external onlyContainer returns (uint256 id) { + function createModuleItem() external onlySpace returns (uint256 id) { // Get the next module item ID id = _nextItemIf; diff --git a/test/mocks/MockNonCompliantContainer.sol b/test/mocks/MockNonCompliantSpace.sol similarity index 86% rename from test/mocks/MockNonCompliantContainer.sol rename to test/mocks/MockNonCompliantSpace.sol index f8db9ae5..e56cc6d9 100644 --- a/test/mocks/MockNonCompliantContainer.sol +++ b/test/mocks/MockNonCompliantSpace.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.26; import { ExcessivelySafeCall } from "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -/// @title Container -/// @notice A mock non-compliant container contract that do not support the {IContainer} interface -contract MockNonCompliantContainer is IERC165 { +/// @title MockNonCompliantSpace +/// @notice A mock non-compliant {Space} contract that do not support the {ISpace} interface +contract MockNonCompliantSpace is IERC165 { using ExcessivelySafeCall for address; address public owner; @@ -38,7 +38,9 @@ contract MockNonCompliantContainer is IERC165 { revert(add(result, 0x20), 4) } // Log the execution success - } else emit ModuleExecutionSucceded(module, value, data); + } else { + emit ModuleExecutionSucceded(module, value, data); + } } /// @inheritdoc IERC165 diff --git a/test/unit/concrete/dock-registry/create-container/createAccount.t.sol b/test/unit/concrete/dock-registry/create-container/createAccount.t.sol deleted file mode 100644 index c129a9d5..00000000 --- a/test/unit/concrete/dock-registry/create-container/createAccount.t.sol +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.26; - -import { DockRegistry_Unit_Concrete_Test } from "../DockRegistry.t.sol"; -import { Container } from "./../../../../../src/Container.sol"; -import { Errors } from "../../../../utils/Errors.sol"; -import { Events } from "../../../../utils/Events.sol"; - -contract CreateAccount_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Test { - function setUp() public virtual override { - DockRegistry_Unit_Concrete_Test.setUp(); - } - - modifier whenDockIdZero() { - _; - } - - function test_CreateAccount_DockIdZero() external whenDockIdZero { - // The {DockRegistry} contract deploys each new {Container} contract. - // Therefore, we need to calculate the current nonce of the {DockRegistry} - // to pre-compute the address of the new {Container} before deployment. - (address expectedContainer, bytes memory data) = - computeDeploymentAddressAndCalldata({ deployer: users.bob, dockId: 0, initialModules: mockModules }); - - // Allowlist the mock modules on the {ModuleKeeper} contract from the admin account - vm.startPrank({ msgSender: users.admin }); - for (uint256 i; i < mockModules.length; ++i) { - allowlistModule(mockModules[i]); - } - vm.stopPrank(); - - // Expect the {ContainerCreated} to be emitted - vm.expectEmit(); - emit Events.ContainerCreated({ - owner: users.bob, - dockId: 1, - container: Container(payable(expectedContainer)), - initialModules: mockModules - }); - - // Make Bob the caller in this test suite - vm.prank({ msgSender: users.bob }); - - // Run the test - dockRegistry.createAccount({ _admin: users.bob, _data: data }); - - // Assert the expected and actual owner of the dock - address actualOwnerOfDock = dockRegistry.ownerOfDock({ dockId: 1 }); - assertEq(users.bob, actualOwnerOfDock); - - // Assert the expected and actual owner of the {Container} - address actualOwnerOfContainer = dockRegistry.ownerOfContainer({ container: expectedContainer }); - assertEq(users.bob, actualOwnerOfContainer); - - // Assert the expected and actual dock ID of the {Container} - uint256 actualDockIdOfContainer = dockRegistry.dockIdOfContainer({ container: expectedContainer }); - assertEq(1, actualDockIdOfContainer); - } - - modifier whenDockIdNonZero() { - // Create & deploy a new container with Eve as the owner - container = deployContainer({ _owner: users.bob, _dockId: 0, _initialModules: mockModules }); - _; - } - - function test_RevertWhen_CallerNotDockOwner() external whenDockIdNonZero { - // Construct the calldata to be used to initialize the {Container} smart account - bytes memory data = - computeCreateAccountCalldata({ deployer: users.eve, dockId: 1, initialModules: mockModules }); - - // Make Eve the caller in this test suite - vm.prank({ msgSender: users.eve }); - - // Expect the {CallerNotDockOwner} to be emitted - vm.expectRevert(Errors.CallerNotDockOwner.selector); - - // Run the test - dockRegistry.createAccount({ _admin: users.bob, _data: data }); - } - - modifier whenCallerDockOwner() { - _; - } - - function test_CreateAccount_DockIdNonZero() external whenDockIdNonZero whenCallerDockOwner { - // The {DockRegistry} contract deploys each new {Container} contract. - // Therefore, we need to calculate the current nonce of the {DockRegistry} - // to pre-compute the address of the new {Container} before deployment. - (address expectedContainer, bytes memory data) = - computeDeploymentAddressAndCalldata({ deployer: users.bob, dockId: 1, initialModules: mockModules }); - - // Allowlist the mock modules on the {ModuleKeeper} contract from the admin account - vm.startPrank({ msgSender: users.admin }); - for (uint256 i; i < mockModules.length; ++i) { - allowlistModule(mockModules[i]); - } - vm.stopPrank(); - - // Expect the {ContainerCreated} event to be emitted - vm.expectEmit(); - emit Events.ContainerCreated({ - owner: users.bob, - dockId: 1, - container: Container(payable(expectedContainer)), - initialModules: mockModules - }); - - // Make Bob the caller in this test suite - vm.prank({ msgSender: users.bob }); - - // Run the test - dockRegistry.createAccount({ _admin: users.bob, _data: data }); - - // Assert if the freshly deployed smart account is registered on the factory - bool isRegisteredOnFactory = dockRegistry.isRegistered(expectedContainer); - assertTrue(isRegisteredOnFactory); - - // Assert if the initial modules has been enabled on the {Container} smart account instance - bool isModuleEnabled = Container(payable(expectedContainer)).isModuleEnabled(mockModules[0]); - assertTrue(isModuleEnabled); - - // Assert the expected and actual owner of the dock - address actualOwnerOfDock = dockRegistry.ownerOfDock({ dockId: 1 }); - assertEq(users.bob, actualOwnerOfDock); - - // Assert the expected and actual owner of the {Container} - address actualOwnerOfContainer = dockRegistry.ownerOfContainer({ container: expectedContainer }); - assertEq(users.bob, actualOwnerOfContainer); - - // Assert the expected and actual dock ID of the {Container} - uint256 actualDockIdOfContainer = dockRegistry.dockIdOfContainer({ container: expectedContainer }); - assertEq(1, actualDockIdOfContainer); - } -} diff --git a/test/unit/concrete/dock-registry/create-container/createAccount.tree b/test/unit/concrete/dock-registry/create-container/createAccount.tree deleted file mode 100644 index 348f8c20..00000000 --- a/test/unit/concrete/dock-registry/create-container/createAccount.tree +++ /dev/null @@ -1,12 +0,0 @@ -createContainer.t.sol -├── when dock ID is zero -│ └── it should create a new dock with the caller address as the owner -└── when dock ID is non-zero - ├── when the caller IS NOT the owner of the dock - │ └── it should revert with the {CallerNotDockOwner} error - └── when the IS the owner of the dock - ├── it should deploy a new {Container} - ├── it should enabled the initial modules on the {Container} - ├── it should register the {Container} smart account on the {DockRegistry} factory - ├── it should set the dock ID to which the new deployed {Container} belongs - └── it should emit a {ContainerCreated} event diff --git a/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.t.sol b/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.t.sol deleted file mode 100644 index fff5b0e8..00000000 --- a/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.t.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.26; - -import { DockRegistry_Unit_Concrete_Test } from "../DockRegistry.t.sol"; -import { MockModule } from "../../../../mocks/MockModule.sol"; -import { Container } from "./../../../../../src/Container.sol"; -import { Events } from "../../../../utils/Events.sol"; -import { Errors } from "../../../../utils/Errors.sol"; - -contract TransferContainerOwnership_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Test { - function setUp() public virtual override { - DockRegistry_Unit_Concrete_Test.setUp(); - } - - modifier givenContainerCreated() { - // Create & deploy a new container with Eve as the owner - address[] memory modules = new address[](1); - modules[0] = address(mockModule); - - container = deployContainer({ _owner: users.eve, _dockId: 0, _initialModules: modules }); - _; - } - - function test_RevertWhen_CallerNotOwner() external givenContainerCreated { - // Make Bob the caller for this test suite who is not the owner of the container - vm.startPrank({ msgSender: users.bob }); - - // Expect the next call to revert with the {CallerNotContainerOwner} error - vm.expectRevert(Errors.CallerNotContainerOwner.selector); - - // Run the test - dockRegistry.transferContainerOwnership({ container: address(container), newOwner: users.eve }); - } - - modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container - vm.startPrank({ msgSender: users.eve }); - _; - } - - function test_RevertWhen_InvalidOwnerZeroAddress() external givenContainerCreated whenCallerOwner { - // Expect the next call to revert with the {InvalidOwnerZeroAddress} - vm.expectRevert(Errors.InvalidOwnerZeroAddress.selector); - - // Run the test - dockRegistry.transferContainerOwnership({ container: address(container), newOwner: address(0) }); - } - - modifier whenNonZeroOwnerAddress() { - _; - } - - function test_TransferContainerOwnership() external givenContainerCreated whenCallerOwner whenNonZeroOwnerAddress { - // Expect the {ContainerOwnershipTransferred} to be emitted - vm.expectEmit(); - emit Events.ContainerOwnershipTransferred({ container: container, oldOwner: users.eve, newOwner: users.bob }); - - // Run the test - dockRegistry.transferContainerOwnership({ container: address(container), newOwner: users.bob }); - - // Assert the actual and expected owner - address actualOwner = dockRegistry.ownerOfContainer(address(container)); - assertEq(actualOwner, users.bob); - } -} diff --git a/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.tree b/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.tree deleted file mode 100644 index 7530cf9d..00000000 --- a/test/unit/concrete/dock-registry/transfer-container-ownership/transferContainerOwnership.tree +++ /dev/null @@ -1,10 +0,0 @@ -transferContainerOwnership.t.sol -└── given container created - ├── when the caller IS NOT the container owner - │ └── it should revert with the {CallerNotContainerOwner} error - └── when the caller IS the container owner - ├── when the new owner address IS the zero address - │ └── it should revert with the {InvalidOwnerZeroAddress} error - └── when the new owner address IS NOT the zero address - ├── it should update the owner - └── it should emit a {ContainerOwnershipTransferred} event diff --git a/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.t.sol b/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.t.sol deleted file mode 100644 index 5d969192..00000000 --- a/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.t.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.26; - -import { DockRegistry_Unit_Concrete_Test } from "../DockRegistry.t.sol"; -import { MockModule } from "../../../../mocks/MockModule.sol"; -import { Container } from "./../../../../../src/Container.sol"; -import { Events } from "../../../../utils/Events.sol"; -import { Errors } from "../../../../utils/Errors.sol"; - -contract TransferDockOwnership_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Test { - function setUp() public virtual override { - DockRegistry_Unit_Concrete_Test.setUp(); - } - - modifier givenDockCreated() { - // Create a new dock by creating & deploying a new container - address[] memory modules = new address[](1); - modules[0] = address(mockModule); - - container = deployContainer({ _owner: users.eve, _dockId: 0, _initialModules: modules }); - _; - } - - function test_RevertWhen_CallerNotOwner() external givenDockCreated { - // Make Bob the caller for this test suite who is not the owner of the dock - vm.startPrank({ msgSender: users.bob }); - - // Expect the next call to revert with the {CallerNotDockOwner} error - vm.expectRevert(Errors.CallerNotDockOwner.selector); - - // Run the test - dockRegistry.transferDockOwnership({ dockId: 1, newOwner: users.bob }); - } - - modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the dock - vm.startPrank({ msgSender: users.eve }); - _; - } - - function test_TransferDockOwnership() external givenDockCreated whenCallerOwner { - // Expect the {DockOwnershipTransferred} to be emitted - vm.expectEmit(); - emit Events.DockOwnershipTransferred({ dockId: 1, oldOwner: users.eve, newOwner: users.bob }); - - // Run the test - dockRegistry.transferDockOwnership({ dockId: 1, newOwner: users.bob }); - - // Assert the actual and expected owner - address actualOwner = dockRegistry.ownerOfDock({ dockId: 1 }); - assertEq(actualOwner, users.bob); - } -} diff --git a/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.tree b/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.tree deleted file mode 100644 index 524bca72..00000000 --- a/test/unit/concrete/dock-registry/transfer-dock-ownership/transferDockOwnership.tree +++ /dev/null @@ -1,7 +0,0 @@ -transferDockOwnership.t.sol -└── given dock created - ├── when the caller IS NOT the dock owner - │ └── it should revert with the {CallerNotDockOwner} error - └── when the caller IS the dock owner - ├── it should update the owner - └── it should emit a {DockOwnershipTransferred} event diff --git a/test/unit/concrete/module-keeper/add-to-allowlist/addToAllowlist.sol b/test/unit/concrete/module-keeper/add-to-allowlist/addToAllowlist.sol index fbfaed04..e6bf0b8a 100644 --- a/test/unit/concrete/module-keeper/add-to-allowlist/addToAllowlist.sol +++ b/test/unit/concrete/module-keeper/add-to-allowlist/addToAllowlist.sol @@ -12,7 +12,7 @@ contract AddToAllowlist_Unit_Concrete_Test is ModuleKeeper_Unit_Concrete_Test { } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the {Unauthorized} error diff --git a/test/unit/concrete/module-keeper/remove-from-allowlist/removeFromAllowlist.sol b/test/unit/concrete/module-keeper/remove-from-allowlist/removeFromAllowlist.sol index dd335a84..7aee4c69 100644 --- a/test/unit/concrete/module-keeper/remove-from-allowlist/removeFromAllowlist.sol +++ b/test/unit/concrete/module-keeper/remove-from-allowlist/removeFromAllowlist.sol @@ -12,7 +12,7 @@ contract RemoveFromAllowlist_Unit_Concrete_Test is ModuleKeeper_Unit_Concrete_Te } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the {Unauthorized} error diff --git a/test/unit/concrete/container/Container.t.sol b/test/unit/concrete/space/Space.t.sol similarity index 66% rename from test/unit/concrete/container/Container.t.sol rename to test/unit/concrete/space/Space.t.sol index c900f577..131e5fc7 100644 --- a/test/unit/concrete/container/Container.t.sol +++ b/test/unit/concrete/space/Space.t.sol @@ -3,13 +3,13 @@ pragma solidity ^0.8.26; import { Base_Test } from "../../../Base.t.sol"; -contract Container_Unit_Concrete_Test is Base_Test { +contract Space_Unit_Concrete_Test is Base_Test { function setUp() public virtual override { Base_Test.setUp(); address[] memory modules = new address[](1); modules[0] = address(mockModule); - container = deployContainer({ _owner: users.eve, _dockId: 0, _initialModules: modules }); + space = deploySpace({ _owner: users.eve, _spaceId: 0, _initialModules: modules }); } } diff --git a/test/unit/concrete/container/disable-module/disableModule.t.sol b/test/unit/concrete/space/disable-module/disableModule.t.sol similarity index 71% rename from test/unit/concrete/container/disable-module/disableModule.t.sol rename to test/unit/concrete/space/disable-module/disableModule.t.sol index a35aab3d..7959ae25 100644 --- a/test/unit/concrete/container/disable-module/disableModule.t.sol +++ b/test/unit/concrete/space/disable-module/disableModule.t.sol @@ -1,35 +1,35 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { MockModule } from "../../../../mocks/MockModule.sol"; import { Events } from "../../../../utils/Events.sol"; -contract DisableModule_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract DisableModule_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.disableModule({ module: address(0x1) }); + space.disableModule({ module: address(0x1) }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } modifier givenModuleEnabled() { // Enable the {MockModule} first - container.enableModule({ module: address(mockModule) }); + space.enableModule({ module: address(mockModule) }); _; } @@ -42,10 +42,10 @@ contract DisableModule_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.ModuleDisabled({ module: address(mockModule), owner: users.eve }); // Run the test - container.disableModule({ module: address(mockModule) }); + space.disableModule({ module: address(mockModule) }); // Assert the module enablement state - bool isModuleEnabled = container.isModuleEnabled({ module: address(mockModule) }); + bool isModuleEnabled = space.isModuleEnabled({ module: address(mockModule) }); assertFalse(isModuleEnabled); } } diff --git a/test/unit/concrete/container/disable-module/disableModule.tree b/test/unit/concrete/space/disable-module/disableModule.tree similarity index 71% rename from test/unit/concrete/container/disable-module/disableModule.tree rename to test/unit/concrete/space/disable-module/disableModule.tree index d1b592b8..0c10c209 100644 --- a/test/unit/concrete/container/disable-module/disableModule.tree +++ b/test/unit/concrete/space/disable-module/disableModule.tree @@ -1,7 +1,7 @@ disableModule.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner └── given module enabled ├── it should mark the module as disabled └── it should emit a {ModuleDisabled} event diff --git a/test/unit/concrete/container/enable-module/enableModule.t.sol b/test/unit/concrete/space/enable-module/enableModule.t.sol similarity index 74% rename from test/unit/concrete/container/enable-module/enableModule.t.sol rename to test/unit/concrete/space/enable-module/enableModule.t.sol index 3fdc0529..3397a8d9 100644 --- a/test/unit/concrete/container/enable-module/enableModule.t.sol +++ b/test/unit/concrete/space/enable-module/enableModule.t.sol @@ -1,29 +1,29 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { MockModule } from "../../../../mocks/MockModule.sol"; import { Events } from "../../../../utils/Events.sol"; import { Errors } from "../../../../utils/Errors.sol"; -contract EnableModule_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract EnableModule_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.enableModule({ module: address(0x1) }); + space.enableModule({ module: address(0x1) }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -33,7 +33,7 @@ contract EnableModule_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(Errors.ModuleNotAllowlisted.selector); // Run the test - container.enableModule({ module: address(0x1) }); + space.enableModule({ module: address(0x1) }); } modifier whenNonZeroCodeModule() { @@ -46,10 +46,10 @@ contract EnableModule_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.ModuleEnabled({ module: address(mockModule), owner: users.eve }); // Run the test - container.enableModule({ module: address(mockModule) }); + space.enableModule({ module: address(mockModule) }); // Assert the module enablement state - bool isModuleEnabled = container.isModuleEnabled({ module: address(mockModule) }); + bool isModuleEnabled = space.isModuleEnabled({ module: address(mockModule) }); assertTrue(isModuleEnabled); } } diff --git a/test/unit/concrete/container/enable-module/enableModule.tree b/test/unit/concrete/space/enable-module/enableModule.tree similarity index 79% rename from test/unit/concrete/container/enable-module/enableModule.tree rename to test/unit/concrete/space/enable-module/enableModule.tree index 93989d1b..8a24ac9d 100644 --- a/test/unit/concrete/container/enable-module/enableModule.tree +++ b/test/unit/concrete/space/enable-module/enableModule.tree @@ -1,7 +1,7 @@ enableModule.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner ├── when the module IS NOT allowlisted │ └── it should revert with the {ModuleNotAllowlisted} error └── when the module IS allowlisted diff --git a/test/unit/concrete/container/execute-batch/executeBatch.t.sol b/test/unit/concrete/space/execute-batch/executeBatch.t.sol similarity index 86% rename from test/unit/concrete/container/execute-batch/executeBatch.t.sol rename to test/unit/concrete/space/execute-batch/executeBatch.t.sol index e348ef61..7a322dbe 100644 --- a/test/unit/concrete/container/execute-batch/executeBatch.t.sol +++ b/test/unit/concrete/space/execute-batch/executeBatch.t.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { MockModule } from "../../../../mocks/MockModule.sol"; -contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract ExecuteBatch_Unit_Concrete_Test is Space_Unit_Concrete_Test { address[] modules; uint256[] values; bytes[] data; function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); // Construct the mock params to be used in this test suite modules = new address[](1); @@ -25,18 +25,18 @@ contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.executeBatch(modules, values, data); + space.executeBatch(modules, values, data); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -50,7 +50,7 @@ contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(Errors.WrongArrayLengths.selector); // Run the test - container.executeBatch(modules, values, data); + space.executeBatch(modules, values, data); } modifier whenCorrectArrayLengths() { @@ -65,7 +65,7 @@ contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(abi.encodeWithSelector(Errors.ModuleNotEnabled.selector, address(0x1))); // Run the test - container.executeBatch(modules, values, data); + space.executeBatch(modules, values, data); } modifier whenModuleEnabled() { @@ -89,7 +89,7 @@ contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit MockModule.ModuleItemCreated({ id: 1 }); // Run the test - container.executeBatch(modules, values, data); + space.executeBatch(modules, values, data); // Assert the actual and expected items stored for Eve due to the batch execution uint256[] memory itemsOf = mockModule.getItemsOf(users.eve); @@ -104,6 +104,6 @@ contract ExecuteBatch_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(); // Run the test - container.executeBatch(modules, values, data); + space.executeBatch(modules, values, data); } } diff --git a/test/unit/concrete/container/execute-batch/executeBatch.tree b/test/unit/concrete/space/execute-batch/executeBatch.tree similarity index 86% rename from test/unit/concrete/container/execute-batch/executeBatch.tree rename to test/unit/concrete/space/execute-batch/executeBatch.tree index 5d983c42..e108863e 100644 --- a/test/unit/concrete/container/execute-batch/executeBatch.tree +++ b/test/unit/concrete/space/execute-batch/executeBatch.tree @@ -1,7 +1,7 @@ executeBatch.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner ├── when one array have a different length than the others │ └── it should revert with the {WrongArrayLengths} error └── when all arrays have the same length diff --git a/test/unit/concrete/container/execute/execute.t.sol b/test/unit/concrete/space/execute/execute.t.sol similarity index 77% rename from test/unit/concrete/container/execute/execute.t.sol rename to test/unit/concrete/space/execute/execute.t.sol index 36531f21..27491bc6 100644 --- a/test/unit/concrete/container/execute/execute.t.sol +++ b/test/unit/concrete/space/execute/execute.t.sol @@ -1,28 +1,28 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { MockModule } from "../../../../mocks/MockModule.sol"; -contract Execute_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract Execute_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.execute({ module: address(mockModule), value: 0, data: "" }); + space.execute({ module: address(mockModule), value: 0, data: "" }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -32,7 +32,7 @@ contract Execute_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(abi.encodeWithSelector(Errors.ModuleNotEnabled.selector, address(0x1))); // Run the test by trying to execute a module at `0x0000000000000000000000000000000000000001` address - container.execute({ module: address(0x1), value: 0, data: "" }); + space.execute({ module: address(0x1), value: 0, data: "" }); } modifier whenModuleEnabled() { @@ -48,7 +48,7 @@ contract Execute_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit MockModule.ModuleItemCreated({ id: 0 }); // Run the test - container.execute({ module: address(mockModule), value: 0, data: data }); + space.execute({ module: address(mockModule), value: 0, data: data }); // Alter the `createModuleItem` method signature by adding an invalid `uint256` field bytes memory wrongData = abi.encodeWithSignature("createModuleItem(uint256)", 1); @@ -57,6 +57,6 @@ contract Execute_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(); // Run the test - container.execute({ module: address(mockModule), value: 0, data: wrongData }); + space.execute({ module: address(mockModule), value: 0, data: wrongData }); } } diff --git a/test/unit/concrete/container/execute/execute.tree b/test/unit/concrete/space/execute/execute.tree similarity index 81% rename from test/unit/concrete/container/execute/execute.tree rename to test/unit/concrete/space/execute/execute.tree index 4527e437..ebc428ee 100644 --- a/test/unit/concrete/container/execute/execute.tree +++ b/test/unit/concrete/space/execute/execute.tree @@ -1,7 +1,7 @@ execute.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner ├── when the module IS NOT enabled │ └── it should revert with the {ModuleNotEnabled} error └── when the module IS enabled diff --git a/test/unit/concrete/container/fallback/fallback.t.sol b/test/unit/concrete/space/fallback/fallback.t.sol similarity index 60% rename from test/unit/concrete/container/fallback/fallback.t.sol rename to test/unit/concrete/space/fallback/fallback.t.sol index 07705e31..49d321f0 100644 --- a/test/unit/concrete/container/fallback/fallback.t.sol +++ b/test/unit/concrete/space/fallback/fallback.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Events } from "../../../../utils/Events.sol"; -contract Fallback_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract Fallback_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_Fallback() external { @@ -18,10 +18,10 @@ contract Fallback_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.NativeReceived({ from: users.bob, amount: 1 ether }); // Run the test - (bool success,) = address(container).call{ value: 1 ether }("test"); + (bool success,) = address(space).call{ value: 1 ether }("test"); if (!success) revert(); - // Assert the {Container} contract balance - assertEq(address(container).balance, 1 ether); + // Assert the {Space} contract balance + assertEq(address(space).balance, 1 ether); } } diff --git a/test/unit/concrete/container/fallback/fallback.tree b/test/unit/concrete/space/fallback/fallback.tree similarity index 100% rename from test/unit/concrete/container/fallback/fallback.tree rename to test/unit/concrete/space/fallback/fallback.tree diff --git a/test/unit/concrete/container/receive/receive.t.sol b/test/unit/concrete/space/receive/receive.t.sol similarity index 60% rename from test/unit/concrete/container/receive/receive.t.sol rename to test/unit/concrete/space/receive/receive.t.sol index f1525ba4..9d74f390 100644 --- a/test/unit/concrete/container/receive/receive.t.sol +++ b/test/unit/concrete/space/receive/receive.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Events } from "../../../../utils/Events.sol"; -contract Receive_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract Receive_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_Receive() external { @@ -18,10 +18,10 @@ contract Receive_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.NativeReceived({ from: users.bob, amount: 1 ether }); // Run the test - (bool success,) = address(container).call{ value: 1 ether }(""); + (bool success,) = address(space).call{ value: 1 ether }(""); if (!success) revert(); - // Assert the {Container} contract balance - assertEq(address(container).balance, 1 ether); + // Assert the {Space} contract balance + assertEq(address(space).balance, 1 ether); } } diff --git a/test/unit/concrete/container/receive/receive.tree b/test/unit/concrete/space/receive/receive.tree similarity index 100% rename from test/unit/concrete/container/receive/receive.tree rename to test/unit/concrete/space/receive/receive.tree diff --git a/test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.t.sol b/test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.t.sol similarity index 75% rename from test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.t.sol rename to test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.t.sol index 42422753..5b718169 100644 --- a/test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.t.sol +++ b/test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.t.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { Events } from "../../../../utils/Events.sol"; import { IERC1155 } from "@openzeppelin/contracts/interfaces/IERC1155.sol"; -contract WithdrawERC1155_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract WithdrawERC1155_Unit_Concrete_Test is Space_Unit_Concrete_Test { uint256[] ids; uint256[] amounts; function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); ids = new uint256[](2); amounts = new uint256[](2); @@ -23,18 +23,18 @@ contract WithdrawERC1155_Unit_Concrete_Test is Container_Unit_Concrete_Test { } function test_RevertWhen_CallerNotAdminOrEntryPoint() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.withdrawERC1155({ collection: IERC1155(address(0x0)), ids: ids, amounts: amounts }); + space.withdrawERC1155({ collection: IERC1155(address(0x0)), ids: ids, amounts: amounts }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -42,18 +42,16 @@ contract WithdrawERC1155_Unit_Concrete_Test is Container_Unit_Concrete_Test { function test_RevertWhen_InsufficientERC1155Balance() external whenCallerOwner { // Expect the next call to revert with the {ERC1155InsufficientBalance} error vm.expectRevert( - abi.encodeWithSelector( - Errors.ERC1155InsufficientBalance.selector, address(container), 0, amounts[0], ids[0] - ) + abi.encodeWithSelector(Errors.ERC1155InsufficientBalance.selector, address(space), 0, amounts[0], ids[0]) ); // Run the test by attempting to withdraw a nonexistent ERC1155 token - container.withdrawERC1155({ collection: mockERC1155, ids: ids, amounts: amounts }); + space.withdrawERC1155({ collection: mockERC1155, ids: ids, amounts: amounts }); } modifier whenExistingERC1155Token() { - // Mint 100 ERC1155 tokens to the container contract - mockERC1155.mintBatch({ to: address(container), amounts: amounts }); + // Mint 100 ERC1155 tokens to the space contract + mockERC1155.mintBatch({ to: address(space), amounts: amounts }); _; } @@ -73,7 +71,7 @@ contract WithdrawERC1155_Unit_Concrete_Test is Container_Unit_Concrete_Test { }); // Run the test - container.withdrawERC1155({ collection: mockERC1155, ids: idsToWithdraw, amounts: amountsToWithdraw }); + space.withdrawERC1155({ collection: mockERC1155, ids: idsToWithdraw, amounts: amountsToWithdraw }); // Assert the actual and expected token type 1 ERC1155 balance of Eve uint256 actualBalanceOfEve = mockERC1155.balanceOf(users.eve, idsToWithdraw[0]); @@ -86,7 +84,7 @@ contract WithdrawERC1155_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.ERC1155Withdrawn({ to: users.eve, collection: address(mockERC1155), ids: ids, amounts: amounts }); // Run the test - container.withdrawERC1155({ collection: mockERC1155, ids: ids, amounts: amounts }); + space.withdrawERC1155({ collection: mockERC1155, ids: ids, amounts: amounts }); // Assert the actual and expected balance of any ERC1155 tokens uint256 numberOfTokens = ids.length; diff --git a/test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.tree b/test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.tree similarity index 81% rename from test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.tree rename to test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.tree index 0d2a3c8c..6f111a5f 100644 --- a/test/unit/concrete/container/withdraw-erc1155/withdrawERC1155.tree +++ b/test/unit/concrete/space/withdraw-erc1155/withdrawERC1155.tree @@ -1,7 +1,7 @@ withdrawERC1155.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner ├── when there the ERC-1155 balance IS NOT sufficient │ └── it should revert with the {ERC1155InsufficientBalance} error └── when there the ERC-1155 balance IS sufficient diff --git a/test/unit/concrete/container/withdraw-erc20/withdrawERC20.t.sol b/test/unit/concrete/space/withdraw-erc20/withdrawERC20.t.sol similarity index 64% rename from test/unit/concrete/container/withdraw-erc20/withdrawERC20.t.sol rename to test/unit/concrete/space/withdraw-erc20/withdrawERC20.t.sol index c1c4e1f0..e94a2df3 100644 --- a/test/unit/concrete/container/withdraw-erc20/withdrawERC20.t.sol +++ b/test/unit/concrete/space/withdraw-erc20/withdrawERC20.t.sol @@ -1,29 +1,29 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { Events } from "../../../../utils/Events.sol"; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; -contract WithdrawERC20_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract WithdrawERC20_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.withdrawERC20({ asset: IERC20(address(0x0)), amount: 100e6 }); + space.withdrawERC20({ asset: IERC20(address(0x0)), amount: 100e6 }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -33,15 +33,15 @@ contract WithdrawERC20_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(Errors.InsufficientERC20ToWithdraw.selector); // Run the test - container.withdrawERC20({ asset: IERC20(address(usdt)), amount: 100e6 }); + space.withdrawERC20({ asset: IERC20(address(usdt)), amount: 100e6 }); } modifier whenSufficientERC20ToWithdraw() { - // Approve the {Container} contract to spend USDT tokens on behalf of Eve - usdt.approve({ spender: address(container), amount: 100e6 }); + // Approve the {Space} contract to spend USDT tokens on behalf of Eve + usdt.approve({ spender: address(space), amount: 100e6 }); - // Deposit enough ERC-20 tokens into the container to enable the withdrawal - usdt.transfer({ recipient: address(container), amount: 100e6 }); + // Deposit enough ERC-20 tokens into the space to enable the withdrawal + usdt.transfer({ recipient: address(space), amount: 100e6 }); _; } @@ -54,11 +54,11 @@ contract WithdrawERC20_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.AssetWithdrawn({ to: users.eve, asset: address(usdt), amount: 10e6 }); // Run the test - container.withdrawERC20({ asset: IERC20(address(usdt)), amount: 10e6 }); + space.withdrawERC20({ asset: IERC20(address(usdt)), amount: 10e6 }); - // Assert the USDT balance of the {Container} contract - uint256 actualBalanceOfContainer = usdt.balanceOf(address(container)); - assertEq(actualBalanceOfContainer, 90e6); + // Assert the USDT balance of the {Space} contract + uint256 actualBalanceOfSpace = usdt.balanceOf(address(space)); + assertEq(actualBalanceOfSpace, 90e6); // Assert the USDT balance of Eve uint256 actualBalanceOfEve = usdt.balanceOf(users.eve); diff --git a/test/unit/concrete/container/withdraw-erc20/withdrawERC20.tree b/test/unit/concrete/space/withdraw-erc20/withdrawERC20.tree similarity index 51% rename from test/unit/concrete/container/withdraw-erc20/withdrawERC20.tree rename to test/unit/concrete/space/withdraw-erc20/withdrawERC20.tree index 48bb6c67..f88f8cf3 100644 --- a/test/unit/concrete/container/withdraw-erc20/withdrawERC20.tree +++ b/test/unit/concrete/space/withdraw-erc20/withdrawERC20.tree @@ -1,9 +1,9 @@ withdrawERC20.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner - ├── when container ERC-20 token balance IS INSUFFICIENT to support the withdrawal +└── when the caller IS the space owner + ├── when space ERC-20 token balance IS INSUFFICIENT to support the withdrawal │ └── it should revert with the {InsufficientERC20ToWithdraw} error - └── when container ERC-20 token balance IS SUFFICIENT to support the withdrawal + └── when space ERC-20 token balance IS SUFFICIENT to support the withdrawal ├── it should transfer the tokens to the caller └── it should emit an {AssetWithdrawn} event diff --git a/test/unit/concrete/container/withdraw-erc721/withdrawERC721.t.sol b/test/unit/concrete/space/withdraw-erc721/withdrawERC721.t.sol similarity index 74% rename from test/unit/concrete/container/withdraw-erc721/withdrawERC721.t.sol rename to test/unit/concrete/space/withdraw-erc721/withdrawERC721.t.sol index 40fc97fd..80849b4d 100644 --- a/test/unit/concrete/container/withdraw-erc721/withdrawERC721.t.sol +++ b/test/unit/concrete/space/withdraw-erc721/withdrawERC721.t.sol @@ -1,29 +1,29 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { Events } from "../../../../utils/Events.sol"; import { IERC721 } from "@openzeppelin/contracts/interfaces/IERC721.sol"; -contract WithdrawERC721_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract WithdrawERC721_Unit_Concrete_Test is Space_Unit_Concrete_Test { function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.withdrawERC721({ collection: IERC721(address(0x0)), tokenId: 1 }); + space.withdrawERC721({ collection: IERC721(address(0x0)), tokenId: 1 }); } modifier whenCallerOwner() { - // Make Eve the caller for the next test suite as she's the owner of the container + // Make Eve the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: users.eve }); _; } @@ -33,12 +33,12 @@ contract WithdrawERC721_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(abi.encodeWithSelector(Errors.ERC721NonexistentToken.selector, 1)); // Run the test by attempting to withdraw a nonexistent ERC721 token - container.withdrawERC721({ collection: mockERC721, tokenId: 1 }); + space.withdrawERC721({ collection: mockERC721, tokenId: 1 }); } modifier whenExistingERC721Token() { - // Mint an ERC721 token to the container contract - mockERC721.mint({ to: address(container) }); + // Mint an ERC721 token to the space contract + mockERC721.mint({ to: address(space) }); _; } @@ -48,7 +48,7 @@ contract WithdrawERC721_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.ERC721Withdrawn({ to: users.eve, collection: address(mockERC721), tokenId: 1 }); // Run the test - container.withdrawERC721({ collection: mockERC721, tokenId: 1 }); + space.withdrawERC721({ collection: mockERC721, tokenId: 1 }); // Assert the actual and expected owner of the ERC721 token address actualOwner = mockERC721.ownerOf(1); diff --git a/test/unit/concrete/container/withdraw-erc721/withdrawERC721.tree b/test/unit/concrete/space/withdraw-erc721/withdrawERC721.tree similarity index 81% rename from test/unit/concrete/container/withdraw-erc721/withdrawERC721.tree rename to test/unit/concrete/space/withdraw-erc721/withdrawERC721.tree index 5686e0f3..49945ec7 100644 --- a/test/unit/concrete/container/withdraw-erc721/withdrawERC721.tree +++ b/test/unit/concrete/space/withdraw-erc721/withdrawERC721.tree @@ -1,7 +1,7 @@ withdrawERC721.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner +└── when the caller IS the space owner ├── when there is no existing ERC-721 token to be transferred │ └── it should revert with the {ERC721WithdrawalFailed} error └── when there is an existing ERC-721 token to be transferred diff --git a/test/unit/concrete/container/withdraw-native/withdrawNative.t.sol b/test/unit/concrete/space/withdraw-native/withdrawNative.t.sol similarity index 62% rename from test/unit/concrete/container/withdraw-native/withdrawNative.t.sol rename to test/unit/concrete/space/withdraw-native/withdrawNative.t.sol index e92c5d82..d92f006c 100644 --- a/test/unit/concrete/container/withdraw-native/withdrawNative.t.sol +++ b/test/unit/concrete/space/withdraw-native/withdrawNative.t.sol @@ -2,41 +2,41 @@ pragma solidity ^0.8.26; import { MockBadReceiver } from "../../../../mocks/MockBadReceiver.sol"; -import { Container_Unit_Concrete_Test } from "../Container.t.sol"; -import { Container } from "./../../../../../src/Container.sol"; +import { Space_Unit_Concrete_Test } from "../Space.t.sol"; +import { Space } from "./../../../../../src/Space.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { Events } from "../../../../utils/Events.sol"; -contract WithdrawNative_Unit_Concrete_Test is Container_Unit_Concrete_Test { +contract WithdrawNative_Unit_Concrete_Test is Space_Unit_Concrete_Test { address badReceiver; - Container badContainer; + Space badSpace; function setUp() public virtual override { - Container_Unit_Concrete_Test.setUp(); + Space_Unit_Concrete_Test.setUp(); - // Create a bad receiver contract as the owner of the `badContainer` to test for the `NativeWithdrawFailed` error + // Create a bad receiver contract as the owner of the `badSpace` to test for the `NativeWithdrawFailed` error badReceiver = address(new MockBadReceiver()); vm.deal({ account: badReceiver, newBalance: 100 ether }); - // Deploy the `badContainer` container + // Deploy the `badSpace` space address[] memory modules = new address[](1); modules[0] = address(mockModule); - badContainer = deployContainer({ _owner: address(badReceiver), _dockId: 0, _initialModules: modules }); + badSpace = deploySpace({ _owner: address(badReceiver), _spaceId: 0, _initialModules: modules }); } function test_RevertWhen_CallerNotOwner() external { - // Make Bob the caller for this test suite who is not the owner of the container + // Make Bob the caller for this test suite who is not the owner of the space vm.startPrank({ msgSender: users.bob }); // Expect the next call to revert with the "Account: not admin or EntryPoint." error vm.expectRevert("Account: not admin or EntryPoint."); // Run the test - container.withdrawNative({ amount: 2 ether }); + space.withdrawNative({ amount: 2 ether }); } modifier whenCallerOwner(address caller) { - // Make `caller` the caller for the next test suite as she's the owner of the container + // Make `caller` the caller for the next test suite as she's the owner of the space vm.startPrank({ msgSender: caller }); _; } @@ -46,12 +46,12 @@ contract WithdrawNative_Unit_Concrete_Test is Container_Unit_Concrete_Test { vm.expectRevert(Errors.InsufficientNativeToWithdraw.selector); // Run the test - container.withdrawNative({ amount: 2 ether }); + space.withdrawNative({ amount: 2 ether }); } - modifier whenSufficientNativeToWithdraw(Container container) { - // Deposit sufficient native tokens (ETH) into the container to enable the withdrawal - (bool success,) = payable(container).call{ value: 2 ether }(""); + modifier whenSufficientNativeToWithdraw(Space space) { + // Deposit sufficient native tokens (ETH) into the space to enable the withdrawal + (bool success,) = payable(space).call{ value: 2 ether }(""); if (!success) revert(); _; } @@ -59,13 +59,13 @@ contract WithdrawNative_Unit_Concrete_Test is Container_Unit_Concrete_Test { function test_RevertWhen_NativeWithdrawFailed() external whenCallerOwner(badReceiver) - whenSufficientNativeToWithdraw(badContainer) + whenSufficientNativeToWithdraw(badSpace) { // Expect the next call to revert with the {NativeWithdrawFailed} error vm.expectRevert(Errors.NativeWithdrawFailed.selector); // Run the test - badContainer.withdrawNative({ amount: 1 ether }); + badSpace.withdrawNative({ amount: 1 ether }); } modifier whenNativeWithdrawSucceeds() { @@ -75,11 +75,11 @@ contract WithdrawNative_Unit_Concrete_Test is Container_Unit_Concrete_Test { function test_WithdrawNative() external whenCallerOwner(users.eve) - whenSufficientNativeToWithdraw(container) + whenSufficientNativeToWithdraw(space) whenNativeWithdrawSucceeds { - // Store the ETH balance of Eve and {Container} contract before withdrawal - uint256 balanceOfContainerBefore = address(container).balance; + // Store the ETH balance of Eve and {Space} contract before withdrawal + uint256 balanceOfSpaceBefore = address(space).balance; uint256 balanceOfEveBefore = address(users.eve).balance; uint256 ethToWithdraw = 1 ether; @@ -88,11 +88,11 @@ contract WithdrawNative_Unit_Concrete_Test is Container_Unit_Concrete_Test { emit Events.AssetWithdrawn({ to: users.eve, asset: address(0x0), amount: ethToWithdraw }); // Run the test - container.withdrawNative({ amount: ethToWithdraw }); + space.withdrawNative({ amount: ethToWithdraw }); - // Assert the ETH balance of the {Container} contract - uint256 actualBalanceOfContainer = address(container).balance; - assertEq(actualBalanceOfContainer, balanceOfContainerBefore - ethToWithdraw); + // Assert the ETH balance of the {Space} contract + uint256 actualBalanceOfSpace = address(space).balance; + assertEq(actualBalanceOfSpace, balanceOfSpaceBefore - ethToWithdraw); // Assert the ETH balance of Eve uint256 actualBalanceOfEve = address(users.eve).balance; diff --git a/test/unit/concrete/container/withdraw-native/withdrawNative.tree b/test/unit/concrete/space/withdraw-native/withdrawNative.tree similarity index 63% rename from test/unit/concrete/container/withdraw-native/withdrawNative.tree rename to test/unit/concrete/space/withdraw-native/withdrawNative.tree index 92952124..aeab8a5f 100644 --- a/test/unit/concrete/container/withdraw-native/withdrawNative.tree +++ b/test/unit/concrete/space/withdraw-native/withdrawNative.tree @@ -1,10 +1,10 @@ withdrawNative.t.sol -├── when the caller IS NOT the container owner +├── when the caller IS NOT the space owner │ └── it should revert with the "Account: not admin or EntryPoint." error -└── when the caller IS the container owner - ├── when container native token (ETH) balance IS INSUFFICIENT to support the withdrawal +└── when the caller IS the space owner + ├── when space native token (ETH) balance IS INSUFFICIENT to support the withdrawal │ └── it should revert with the {InsufficientERC20ToWithdraw} error - └── when container native token (ETH) balance IS SUFFICIENT to support the withdrawal + └── when space native token (ETH) balance IS SUFFICIENT to support the withdrawal ├── when native token transfer fails │ └── it should revert with the {NativeWithdrawFailed} error └── when native token transfer succeeds diff --git a/test/unit/concrete/dock-registry/DockRegistry.t.sol b/test/unit/concrete/station-registry/StationRegistry.t.sol similarity index 76% rename from test/unit/concrete/dock-registry/DockRegistry.t.sol rename to test/unit/concrete/station-registry/StationRegistry.t.sol index f5ce8b9c..c974d331 100644 --- a/test/unit/concrete/dock-registry/DockRegistry.t.sol +++ b/test/unit/concrete/station-registry/StationRegistry.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.26; import { Base_Test } from "../../../Base.t.sol"; -contract DockRegistry_Unit_Concrete_Test is Base_Test { +contract StationRegistry_Unit_Concrete_Test is Base_Test { function setUp() public virtual override { Base_Test.setUp(); } diff --git a/test/unit/concrete/dock-registry/constructor.t.sol b/test/unit/concrete/station-registry/constructor.t.sol similarity index 58% rename from test/unit/concrete/dock-registry/constructor.t.sol rename to test/unit/concrete/station-registry/constructor.t.sol index ba3acbbb..b7891150 100644 --- a/test/unit/concrete/dock-registry/constructor.t.sol +++ b/test/unit/concrete/station-registry/constructor.t.sol @@ -1,29 +1,29 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { DockRegistry } from "./../../../../src/DockRegistry.sol"; +import { StationRegistry } from "./../../../../src/StationRegistry.sol"; import { Base_Test } from "../../../Base.t.sol"; import { Constants } from "../../../utils/Constants.sol"; -contract Constructor_DockRegistry_Test is Base_Test { +contract Constructor_StationRegistry_Test is Base_Test { function setUp() public virtual override { Base_Test.setUp(); } function test_Constructor() external { // Run the test - new DockRegistry({ _initialAdmin: users.admin, _entrypoint: entrypoint, _moduleKeeper: moduleKeeper }); + new StationRegistry({ _initialAdmin: users.admin, _entrypoint: entrypoint, _moduleKeeper: moduleKeeper }); // Assert the actual and expected {ModuleKeeper} address - address actualModuleKeeper = address(dockRegistry.moduleKeeper()); + address actualModuleKeeper = address(stationRegistry.moduleKeeper()); assertEq(actualModuleKeeper, address(moduleKeeper)); // Assert the actual and expected {DEFAULT_ADMIN_ROLE} user - address actualInitialAdmin = dockRegistry.getRoleMember(Constants.DEFAULT_ADMIN_ROLE, 0); + address actualInitialAdmin = stationRegistry.getRoleMember(Constants.DEFAULT_ADMIN_ROLE, 0); assertEq(actualInitialAdmin, users.admin); // Assert the actual and expected {Entrypoint} address - address actualEntrypoint = dockRegistry.entrypoint(); + address actualEntrypoint = stationRegistry.entrypoint(); assertEq(actualEntrypoint, address(entrypoint)); } } diff --git a/test/unit/concrete/station-registry/create-account/createAccount.t.sol b/test/unit/concrete/station-registry/create-account/createAccount.t.sol new file mode 100644 index 00000000..e3a0dc9f --- /dev/null +++ b/test/unit/concrete/station-registry/create-account/createAccount.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import { StationRegistry_Unit_Concrete_Test } from "../StationRegistry.t.sol"; +import { Space } from "./../../../../../src/Space.sol"; +import { Errors } from "../../../../utils/Errors.sol"; +import { Events } from "../../../../utils/Events.sol"; + +contract CreateAccount_Unit_Concrete_Test is StationRegistry_Unit_Concrete_Test { + function setUp() public virtual override { + StationRegistry_Unit_Concrete_Test.setUp(); + } + + modifier whenStationIdZero() { + _; + } + + function test_CreateAccount_StationIdZero() external whenStationIdZero { + // The {StationRegistry} contract deploys each new {Space} contract. + // Therefore, we need to calculate the current nonce of the {StationRegistry} + // to pre-compute the address of the new {Space} before deployment. + (address expectedSpace, bytes memory data) = + computeDeploymentAddressAndCalldata({ deployer: users.bob, stationId: 0, initialModules: mockModules }); + + // Allowlist the mock modules on the {ModuleKeeper} contract from the admin account + vm.startPrank({ msgSender: users.admin }); + for (uint256 i; i < mockModules.length; ++i) { + allowlistModule(mockModules[i]); + } + vm.stopPrank(); + + // Expect the {SpaceCreated} to be emitted + vm.expectEmit(); + emit Events.SpaceCreated({ + owner: users.bob, + stationId: 1, + space: Space(payable(expectedSpace)), + initialModules: mockModules + }); + + // Make Bob the caller in this test suite + vm.prank({ msgSender: users.bob }); + + // Run the test + stationRegistry.createAccount({ _admin: users.bob, _data: data }); + + // Assert the expected and actual owner of the station + address actualOwnerOfStation = stationRegistry.ownerOfStation({ stationId: 1 }); + assertEq(users.bob, actualOwnerOfStation); + + // Assert the expected and actual station ID of the {Space} + uint256 actualStationIdOfSpace = stationRegistry.stationIdOfSpace({ space: expectedSpace }); + assertEq(1, actualStationIdOfSpace); + } + + modifier whenStationIdNonZero() { + // Create & deploy a new space with Eve as the owner + space = deploySpace({ _owner: users.bob, _spaceId: 0, _initialModules: mockModules }); + _; + } + + function test_RevertWhen_CallerNotStationOwner() external whenStationIdNonZero { + // Construct the calldata to be used to initialize the {Space} smart account + bytes memory data = + computeCreateAccountCalldata({ deployer: users.eve, stationId: 1, initialModules: mockModules }); + + // Make Eve the caller in this test suite + vm.prank({ msgSender: users.eve }); + + // Expect the {CallerNotStationOwner} to be emitted + vm.expectRevert(Errors.CallerNotStationOwner.selector); + + // Run the test + stationRegistry.createAccount({ _admin: users.bob, _data: data }); + } + + modifier whenCallerStationOwner() { + _; + } + + function test_CreateAccount_StationIdNonZero() external whenStationIdNonZero whenCallerStationOwner { + // The {StationRegistry} contract deploys each new {Space} contract. + // Therefore, we need to calculate the current nonce of the {StationRegistry} + // to pre-compute the address of the new {Space} before deployment. + (address expectedSpace, bytes memory data) = + computeDeploymentAddressAndCalldata({ deployer: users.bob, stationId: 1, initialModules: mockModules }); + + // Allowlist the mock modules on the {ModuleKeeper} contract from the admin account + vm.startPrank({ msgSender: users.admin }); + for (uint256 i; i < mockModules.length; ++i) { + allowlistModule(mockModules[i]); + } + vm.stopPrank(); + + // Expect the {SpaceCreated} event to be emitted + vm.expectEmit(); + emit Events.SpaceCreated({ + owner: users.bob, + stationId: 1, + space: Space(payable(expectedSpace)), + initialModules: mockModules + }); + + // Make Bob the caller in this test suite + vm.prank({ msgSender: users.bob }); + + // Run the test + stationRegistry.createAccount({ _admin: users.bob, _data: data }); + + // Assert if the freshly deployed smart account is registered on the factory + bool isRegisteredOnFactory = stationRegistry.isRegistered(expectedSpace); + assertTrue(isRegisteredOnFactory); + + // Assert if the initial modules has been enabled on the {Space} smart account instance + bool isModuleEnabled = Space(payable(expectedSpace)).isModuleEnabled(mockModules[0]); + assertTrue(isModuleEnabled); + + // Assert the expected and actual owner of the station + address actualOwnerOfStation = stationRegistry.ownerOfStation({ stationId: 1 }); + assertEq(users.bob, actualOwnerOfStation); + + // Assert the expected and actual station ID of the {Space} + uint256 actualStationIdOfSpace = stationRegistry.stationIdOfSpace({ space: expectedSpace }); + assertEq(1, actualStationIdOfSpace); + } +} diff --git a/test/unit/concrete/station-registry/create-account/createAccount.tree b/test/unit/concrete/station-registry/create-account/createAccount.tree new file mode 100644 index 00000000..482355a5 --- /dev/null +++ b/test/unit/concrete/station-registry/create-account/createAccount.tree @@ -0,0 +1,12 @@ +createSpace.t.sol +├── when station ID is zero +│ └── it should create a new station with the caller address as the owner +└── when station ID is non-zero + ├── when the caller IS NOT the owner of the station + │ └── it should revert with the {CallerNotStationOwner} error + └── when the IS the owner of the station + ├── it should deploy a new {Space} + ├── it should enabled the initial modules on the {Space} + ├── it should register the {Space} smart account on the {StationRegistry} factory + ├── it should set the station ID to which the new deployed {Space} belongs + └── it should emit a {SpaceCreated} event diff --git a/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.t.sol b/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.t.sol new file mode 100644 index 00000000..6b09da1b --- /dev/null +++ b/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.t.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import { StationRegistry_Unit_Concrete_Test } from "../StationRegistry.t.sol"; +import { MockModule } from "../../../../mocks/MockModule.sol"; +import { Space } from "./../../../../../src/Space.sol"; +import { Events } from "../../../../utils/Events.sol"; +import { Errors } from "../../../../utils/Errors.sol"; + +contract TransferStationOwnership_Unit_Concrete_Test is StationRegistry_Unit_Concrete_Test { + function setUp() public virtual override { + StationRegistry_Unit_Concrete_Test.setUp(); + } + + modifier givenStationCreated() { + // Create a new station by creating & deploying a new space + address[] memory modules = new address[](1); + modules[0] = address(mockModule); + + space = deploySpace({ _owner: users.eve, _spaceId: 0, _initialModules: modules }); + _; + } + + function test_RevertWhen_CallerNotOwner() external givenStationCreated { + // Make Bob the caller for this test suite who is not the owner of the station + vm.startPrank({ msgSender: users.bob }); + + // Expect the next call to revert with the {CallerNotStationOwner} error + vm.expectRevert(Errors.CallerNotStationOwner.selector); + + // Run the test + stationRegistry.transferStationOwnership({ stationId: 1, newOwner: users.bob }); + } + + modifier whenCallerOwner() { + // Make Eve the caller for the next test suite as she's the owner of the station + vm.startPrank({ msgSender: users.eve }); + _; + } + + function test_TransferStationOwnership() external givenStationCreated whenCallerOwner { + // Expect the {StationOwnershipTransferred} to be emitted + vm.expectEmit(); + emit Events.StationOwnershipTransferred({ stationId: 1, oldOwner: users.eve, newOwner: users.bob }); + + // Run the test + stationRegistry.transferStationOwnership({ stationId: 1, newOwner: users.bob }); + + // Assert the actual and expected owner + address actualOwner = stationRegistry.ownerOfStation({ stationId: 1 }); + assertEq(actualOwner, users.bob); + } +} diff --git a/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.tree b/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.tree new file mode 100644 index 00000000..c2d1c04f --- /dev/null +++ b/test/unit/concrete/station-registry/transfer-station-ownership/transferDockOwnership.tree @@ -0,0 +1,7 @@ +transferStationOwnership.t.sol +└── given station created + ├── when the caller IS NOT the station owner + │ └── it should revert with the {CallerNotStationOwner} error + └── when the caller IS the station owner + ├── it should update the owner + └── it should emit a {StationOwnershipTransferred} event diff --git a/test/unit/concrete/dock-registry/update-module-keeper/updateModuleKeeper.t.sol b/test/unit/concrete/station-registry/update-module-keeper/updateModuleKeeper.t.sol similarity index 76% rename from test/unit/concrete/dock-registry/update-module-keeper/updateModuleKeeper.t.sol rename to test/unit/concrete/station-registry/update-module-keeper/updateModuleKeeper.t.sol index 69030b23..75ab7f58 100644 --- a/test/unit/concrete/dock-registry/update-module-keeper/updateModuleKeeper.t.sol +++ b/test/unit/concrete/station-registry/update-module-keeper/updateModuleKeeper.t.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; -import { DockRegistry_Unit_Concrete_Test } from "../DockRegistry.t.sol"; +import { StationRegistry_Unit_Concrete_Test } from "../StationRegistry.t.sol"; import { ModuleKeeper } from "./../../../../../src/ModuleKeeper.sol"; import { Events } from "../../../../utils/Events.sol"; import { Errors } from "../../../../utils/Errors.sol"; import { Constants } from "../../../../utils/Constants.sol"; -contract UpdateModuleKeeper_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Test { +contract UpdateModuleKeeper_Unit_Concrete_Test is StationRegistry_Unit_Concrete_Test { function setUp() public virtual override { - DockRegistry_Unit_Concrete_Test.setUp(); + StationRegistry_Unit_Concrete_Test.setUp(); } function test_RevertWhen_CallerNotRegistryOwner() external { @@ -24,7 +24,7 @@ contract UpdateModuleKeeper_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Tes ); // Run the test - dockRegistry.updateModuleKeeper({ newModuleKeeper: ModuleKeeper(address(0x1)) }); + stationRegistry.updateModuleKeeper({ newModuleKeeper: ModuleKeeper(address(0x1)) }); } modifier whenCallerRegistryOwner() { @@ -41,10 +41,10 @@ contract UpdateModuleKeeper_Unit_Concrete_Test is DockRegistry_Unit_Concrete_Tes emit Events.ModuleKeeperUpdated(newModuleKeeper); // Run the test - dockRegistry.updateModuleKeeper(newModuleKeeper); + stationRegistry.updateModuleKeeper(newModuleKeeper); // Assert the actual and expected module keeper address - address actualModuleKeeper = address(dockRegistry.moduleKeeper()); + address actualModuleKeeper = address(stationRegistry.moduleKeeper()); assertEq(actualModuleKeeper, address(newModuleKeeper)); } } diff --git a/test/unit/concrete/dock-registry/update-module-keeper/updateModuleKeeper.tree b/test/unit/concrete/station-registry/update-module-keeper/updateModuleKeeper.tree similarity index 100% rename from test/unit/concrete/dock-registry/update-module-keeper/updateModuleKeeper.tree rename to test/unit/concrete/station-registry/update-module-keeper/updateModuleKeeper.tree diff --git a/test/utils/Errors.sol b/test/utils/Errors.sol index 1e963e99..3d9865f2 100644 --- a/test/utils/Errors.sol +++ b/test/utils/Errors.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.26; library Errors { /*////////////////////////////////////////////////////////////////////////// - DOCK-REGISTRY + STATION-REGISTRY //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when `msg.sender` is not the dock owner - error CallerNotDockOwner(); + /// @notice Thrown when `msg.sender` is not the station owner + error CallerNotStationOwner(); /*////////////////////////////////////////////////////////////////////////// CONTAINER //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when `msg.sender` is not the {Container} contract owner - error CallerNotContainerOwner(); + /// @notice Thrown when `msg.sender` is not the {Space} contract owner + error CallerNotSpaceOwner(); /// @notice Thrown when a native token (ETH) withdrawal fails error NativeWithdrawFailed(); @@ -46,10 +46,10 @@ library Errors { MODULE-MANAGER //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when a {Container} tries to execute a method on a non-enabled module + /// @notice Thrown when a {Space} tries to execute a method on a non-enabled module error ModuleNotEnabled(address module); - /// @notice Thrown when an attempt is made to enable a non-allowlisted module on a {Container} + /// @notice Thrown when an attempt is made to enable a non-allowlisted module on a {Space} error ModuleNotAllowlisted(); /*////////////////////////////////////////////////////////////////////////// @@ -64,10 +64,10 @@ library Errors { //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when the caller is an invalid zero code contract or EOA - error ContainerZeroCodeSize(); + error SpaceZeroCodeSize(); - /// @notice Thrown when the caller is a contract that does not implement the {IContainer} interface - error ContainerUnsupportedInterface(); + /// @notice Thrown when the caller is a contract that does not implement the {ISpace} interface + error SpaceUnsupportedInterface(); /// @notice Thrown when the end time of an invoice is in the past error EndTimeInThePast(); diff --git a/test/utils/Events.sol b/test/utils/Events.sol index b397c30e..46263377 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -2,36 +2,28 @@ pragma solidity ^0.8.26; import { Types } from "./../../src/modules/invoice-module/libraries/Types.sol"; -import { Container } from "./../../src/Container.sol"; +import { Space } from "./../../src/Space.sol"; import { ModuleKeeper } from "./../../src/ModuleKeeper.sol"; import { UD60x18 } from "@prb/math/src/UD60x18.sol"; /// @notice Abstract contract to store all the events emitted in the tested contracts abstract contract Events { /*////////////////////////////////////////////////////////////////////////// - DOCK-REGISTRY + STATION-REGISTRY //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a new {Container} contract gets deployed + /// @notice Emitted when a new {Space} contract gets deployed /// @param owner The address of the owner - /// @param dockId The ID of the dock to which this {Container} belongs - /// @param container The address of the {Container} + /// @param stationId The ID of the station to which this {Space} belongs + /// @param space The address of the {Space} /// @param initialModules Array of initially enabled modules - event ContainerCreated( - address indexed owner, uint256 indexed dockId, Container container, address[] initialModules - ); - - /// @notice Emitted when the ownership of a {Container} is transferred to a new owner - /// @param container The address of the {Container} - /// @param oldOwner The address of the current owner - /// @param newOwner The address of the new owner - event ContainerOwnershipTransferred(Container indexed container, address oldOwner, address newOwner); + event SpaceCreated(address indexed owner, uint256 indexed stationId, Space space, address[] initialModules); - /// @notice Emitted when the ownership of a {Dock} is transferred to a new owner - /// @param dockId The address of the {Dock} + /// @notice Emitted when the ownership of a {Station} is transferred to a new owner + /// @param stationId The address of the {Station} /// @param oldOwner The address of the current owner /// @param newOwner The address of the new owner - event DockOwnershipTransferred(uint256 indexed dockId, address oldOwner, address newOwner); + event StationOwnershipTransferred(uint256 indexed stationId, address oldOwner, address newOwner); /// @notice Emitted when the {ModuleKeeper} address is updated /// @param newModuleKeeper The new address of the {ModuleKeeper} @@ -47,35 +39,35 @@ abstract contract Events { CONTAINER //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when an `amount` amount of `asset` native tokens (ETH) is deposited on the container + /// @notice Emitted when an `amount` amount of `asset` native tokens (ETH) is deposited on the space /// @param from The address of the depositor /// @param amount The amount of the deposited ERC-20 token event NativeReceived(address indexed from, uint256 amount); - /// @notice Emitted when an ERC-721 token is received by the container + /// @notice Emitted when an ERC-721 token is received by the space /// @param from The address of the depositor /// @param tokenId The ID of the received token event ERC721Received(address indexed from, uint256 indexed tokenId); - /// @notice Emitted when an ERC-1155 token is received by the container + /// @notice Emitted when an ERC-1155 token is received by the space /// @param from The address of the depositor /// @param id The ID of the received token /// @param value The amount of tokens received event ERC1155Received(address indexed from, uint256 indexed id, uint256 value); - /// @notice Emitted when an `amount` amount of `asset` ERC-20 asset or native ETH is withdrawn from the container + /// @notice Emitted when an `amount` amount of `asset` ERC-20 asset or native ETH is withdrawn from the space /// @param to The address to which the tokens were transferred /// @param asset The address of the ERC-20 token or zero-address for native ETH /// @param amount The withdrawn amount event AssetWithdrawn(address indexed to, address indexed asset, uint256 amount); - /// @notice Emitted when an ERC-721 token is withdrawn from the container + /// @notice Emitted when an ERC-721 token is withdrawn from the space /// @param to The address to which the token was transferred /// @param collection The address of the ERC-721 collection /// @param tokenId The ID of the token event ERC721Withdrawn(address indexed to, address indexed collection, uint256 tokenId); - /// @notice Emitted when an ERC-1155 token is withdrawn from the container + /// @notice Emitted when an ERC-1155 token is withdrawn from the space /// @param to The address to which the tokens were transferred /// @param ids The IDs of the tokens /// @param amounts The amounts of the tokens @@ -91,11 +83,11 @@ abstract contract Events { MODULE-MANAGER //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a module is enabled on the container + /// @notice Emitted when a module is enabled on the space /// @param module The address of the enabled module event ModuleEnabled(address indexed module, address indexed owner); - /// @notice Emitted when a module is disabled on the container + /// @notice Emitted when a module is disabled on the space /// @param module The address of the disabled module event ModuleDisabled(address indexed module, address indexed owner);